Better interoperation between Json::Value and JsonObject.

* Generic functions to add entries to both object models.
* Add Json::Value into JsonObjects.
* Write Json::Value to string incrementally.
* Get rid of ripple::RPC::New namespace
This commit is contained in:
Tom Ritchford
2014-12-01 20:35:09 -05:00
committed by Vinnie Falco
parent 7cfac1a91a
commit 8053598069
16 changed files with 567 additions and 106 deletions

View File

@@ -2812,6 +2812,8 @@
</ClInclude> </ClInclude>
<None Include="..\..\src\ripple\resource\README.md"> <None Include="..\..\src\ripple\resource\README.md">
</None> </None>
<ClInclude Include="..\..\src\ripple\rpc\Coroutine.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\ErrorCodes.h"> <ClInclude Include="..\..\src\ripple\rpc\ErrorCodes.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\handlers\AccountCurrencies.cpp"> <ClCompile Include="..\..\src\ripple\rpc\handlers\AccountCurrencies.cpp">
@@ -3008,6 +3010,12 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\impl\Context.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\Context.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Coroutine.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Coroutine.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\ErrorCodes.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\ErrorCodes.cpp">
@@ -3028,7 +3036,7 @@
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\JsonObject.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\JsonObject.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonObject_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonObject.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.cpp">
@@ -3036,7 +3044,7 @@
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\JsonWriter.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\JsonWriter.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\LegacyPathFind.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\LegacyPathFind.cpp">
@@ -3063,7 +3071,7 @@
<ClCompile Include="..\..\src\ripple\rpc\impl\Status.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Status.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Status_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Status.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\TestOutputSuite.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\TestOutputSuite.h">
@@ -3075,10 +3083,15 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\impl\Tuning.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\Tuning.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\WriteJson.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield_test.cpp"> <ClInclude Include="..\..\src\ripple\rpc\impl\WriteJson.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\WriteJson.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\InternalHandler.h"> <ClInclude Include="..\..\src\ripple\rpc\InternalHandler.h">

View File

@@ -3906,6 +3906,9 @@
<None Include="..\..\src\ripple\resource\README.md"> <None Include="..\..\src\ripple\resource\README.md">
<Filter>ripple\resource</Filter> <Filter>ripple\resource</Filter>
</None> </None>
<ClInclude Include="..\..\src\ripple\rpc\Coroutine.h">
<Filter>ripple\rpc</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\ErrorCodes.h"> <ClInclude Include="..\..\src\ripple\rpc\ErrorCodes.h">
<Filter>ripple\rpc</Filter> <Filter>ripple\rpc</Filter>
</ClInclude> </ClInclude>
@@ -4107,6 +4110,12 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\Context.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\Context.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Coroutine.cpp">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Coroutine.test.cpp">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\DoPrint.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
@@ -4131,7 +4140,7 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\JsonObject.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\JsonObject.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonObject_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonObject.test.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.cpp">
@@ -4140,7 +4149,7 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\JsonWriter.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\JsonWriter.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\JsonWriter.test.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\LegacyPathFind.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\LegacyPathFind.cpp">
@@ -4170,7 +4179,7 @@
<ClCompile Include="..\..\src\ripple\rpc\impl\Status.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Status.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Status_test.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\Status.test.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\TestOutputSuite.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\TestOutputSuite.h">
@@ -4185,10 +4194,16 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\Tuning.h"> <ClInclude Include="..\..\src\ripple\rpc\impl\Tuning.h">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield.cpp"> <ClCompile Include="..\..\src\ripple\rpc\impl\WriteJson.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield_test.cpp"> <ClInclude Include="..\..\src\ripple\rpc\impl\WriteJson.h">
<Filter>ripple\rpc\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\WriteJson.test.cpp">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\rpc\impl\Yield.cpp">
<Filter>ripple\rpc\impl</Filter> <Filter>ripple\rpc\impl</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\InternalHandler.h"> <ClInclude Include="..\..\src\ripple\rpc\InternalHandler.h">

View File

@@ -25,7 +25,6 @@
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
/** Status represents the results of an operation that might fail. /** Status represents the results of an operation that might fail.
@@ -114,7 +113,6 @@ private:
Strings messages_; Strings messages_;
}; };
} // namespace New
} // namespace RPC } // namespace RPC
} // ripple } // ripple

View File

@@ -18,11 +18,9 @@
//============================================================================== //==============================================================================
#include <ripple/rpc/impl/JsonObject.h> #include <ripple/rpc/impl/JsonObject.h>
#include <ripple/rpc/impl/JsonWriter.h>
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
Collection::Collection (Collection* parent, Writer* writer) Collection::Collection (Collection* parent, Writer* writer)
: parent_ (parent), writer_ (writer), enabled_ (true) : parent_ (parent), writer_ (writer), enabled_ (true)
@@ -71,31 +69,9 @@ void Collection::checkWritable (std::string const& label)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template <typename Scalar>
Array& Array::append (Scalar value)
{
checkWritable ("append");
if (writer_)
writer_->append (value);
return *this;
}
//------------------------------------------------------------------------------
Object::Root::Root (Writer& w) : Object (nullptr, &w) Object::Root::Root (Writer& w) : Object (nullptr, &w)
{ {
writer_->startRoot (Writer::object); // writer_ can't be null. writer_->startRoot (Writer::object);
}
//------------------------------------------------------------------------------
template <typename Scalar>
Object& Object::set (std::string const& key, Scalar value)
{
checkWritable ("set");
if (writer_)
writer_->set (key, value);
return *this;
} }
Object Object::makeObject (std::string const& key) Object Object::makeObject (std::string const& key)
@@ -113,6 +89,8 @@ Array Object::makeArray (std::string const& key) {
return Array (this, writer_); return Array (this, writer_);
} }
//------------------------------------------------------------------------------
Object Array::makeObject () Object Array::makeObject ()
{ {
checkWritable ("Array::makeObject"); checkWritable ("Array::makeObject");
@@ -139,9 +117,44 @@ Object::Proxy::Proxy (Object& object, std::string const& key)
Object::Proxy Object::operator[] (std::string const& key) Object::Proxy Object::operator[] (std::string const& key)
{ {
return {*this, key}; return Proxy (*this, key);
}
Object::Proxy Object::operator[] (Json::StaticString const& key)
{
return Proxy (*this, std::string (key));
}
namespace {
template <class Object>
void doCopyFrom (Object& to, Json::Value const& from)
{
auto members = from.getMemberNames();
for (auto& m: members)
to[m] = from[m];
}
}
void copyFrom (Json::Value& to, Json::Value const& from)
{
if (to.empty()) // Short circuit this very common case.
to = from;
else
doCopyFrom (to, from);
}
void copyFrom (Object& to, Json::Value const& from)
{
doCopyFrom (to, from);
}
WriterObject stringWriterObject (std::string& s)
{
return WriterObject (stringOutput (s));
} }
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -20,11 +20,10 @@
#ifndef RIPPLED_RIPPLE_RPC_IMPL_JSONCOLLECTIONS_H #ifndef RIPPLED_RIPPLE_RPC_IMPL_JSONCOLLECTIONS_H
#define RIPPLED_RIPPLE_RPC_IMPL_JSONCOLLECTIONS_H #define RIPPLED_RIPPLE_RPC_IMPL_JSONCOLLECTIONS_H
#include <ripple/rpc/impl/JsonWriter.h>
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
class Writer;
/** /**
Collection is a base class for Array and Object, classes which provide the Collection is a base class for Array and Object, classes which provide the
@@ -32,8 +31,8 @@ class Writer;
heap memory and only a very small amount of stack. heap memory and only a very small amount of stack.
From http://json.org, JSON has two types of collection: array, and object. From http://json.org, JSON has two types of collection: array, and object.
Everything else is a *scalar* - a number, a string, a boolean or the special Everything else is a *scalar* - a number, a string, a boolean, the special
value null. value null, or a legacy Json::Value.
Collections must write JSON "as-it-goes" in order to get the strong Collections must write JSON "as-it-goes" in order to get the strong
performance guarantees. This puts restrictions upon API users: performance guarantees. This puts restrictions upon API users:
@@ -182,7 +181,8 @@ public:
/** Set a scalar value in the Object for a key. /** Set a scalar value in the Object for a key.
A JSON scalar is a single value - a number, string, boolean or null. A JSON scalar is a single value - a number, string, boolean, nullptr or
a Json::Value.
`set()` throws an exception if this object is disabled (which means that `set()` throws an exception if this object is disabled (which means that
one of its children is enabled). one of its children is enabled).
@@ -193,11 +193,13 @@ public:
An operator[] is provided to allow writing `object["key"] = scalar;`. An operator[] is provided to allow writing `object["key"] = scalar;`.
*/ */
template <typename Scalar> template <typename Scalar>
Object& set (std::string const& key, Scalar); Object& set (std::string const& key, Scalar const&);
// Detail class and method used to implement operator[]. // Detail class and method used to implement operator[].
class Proxy; class Proxy;
Proxy operator[] (std::string const& key); Proxy operator[] (std::string const& key);
Proxy operator[] (Json::StaticString const& key);
/** Make a new Object at a key and return it. /** Make a new Object at a key and return it.
@@ -218,8 +220,6 @@ protected:
Object (Collection* parent, Writer* w) : Collection (parent, w) {} Object (Collection* parent, Writer* w) : Collection (parent, w) {}
}; };
//------------------------------------------------------------------------------
class Object::Root : public Object class Object::Root : public Object
{ {
public: public:
@@ -239,7 +239,7 @@ public:
its sub-collections is enabled). its sub-collections is enabled).
*/ */
template <typename Scalar> template <typename Scalar>
Array& append (Scalar); Array& append (const Scalar&);
/** Append a new Object and return it. /** Append a new Object and return it.
@@ -262,12 +262,71 @@ public:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Generic accessor functions to allow Json::Value and Collection to
// interoperate.
/** Add a new subarray at a named key in a Json object. */
Json::Value& addArray (Json::Value&, Json::StaticString const& key);
/** Add a new subarray at a named key in a Json object. */
Array addArray (Object&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Json::Value& addObject (Json::Value&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Object addObject (Object&, Json::StaticString const& key);
/** Copy all the keys and values from one object into another. */
void copyFrom (Json::Value& to, Json::Value const& from);
/** Copy all the keys and values from one object into another. */
void copyFrom (Object& to, Json::Value const& from);
/** An Object that contains its own Writer. */
class WriterObject
{
public:
WriterObject (Output const& output)
: writer_ (std::make_unique<Writer> (output)),
object_ (std::make_unique<Object::Root> (*writer_))
{
}
#ifdef _MSC_VER
WriterObject (WriterObject&& other)
: writer_ (std::move (other.writer_)),
object_ (std::move (other.object_))
{
}
#endif
Object* operator->()
{
return object_.get();
}
Object& operator*()
{
return *object_;
}
private:
std::unique_ptr <Writer> writer_;
std::unique_ptr<Object::Root> object_;
};
WriterObject stringWriterObject (std::string&);
//------------------------------------------------------------------------------
// Implementation details.
// Detail class for Object::operator[]. // Detail class for Object::operator[].
class Object::Proxy class Object::Proxy
{ {
private: private:
Object& object_; Object& object_;
std::string const& key_; std::string const key_;
public: public:
Proxy (Object& object, std::string const& key); Proxy (Object& object, std::string const& key);
@@ -280,7 +339,50 @@ public:
} }
}; };
} // New //------------------------------------------------------------------------------
template <typename Scalar>
Array& Array::append (Scalar const& value)
{
checkWritable ("append");
if (writer_)
writer_->append (value);
return *this;
}
template <typename Scalar>
Object& Object::set (std::string const& key, Scalar const& value)
{
checkWritable ("set");
if (writer_)
writer_->set (key, value);
return *this;
}
inline
Json::Value& addArray (Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::arrayValue);
}
inline
Array addArray (Object& json, Json::StaticString const& key)
{
return json.makeArray (std::string (key));
}
inline
Json::Value& addObject (Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::objectValue);
}
inline
Object addObject (Object& object, Json::StaticString const& key)
{
return object.makeObject (std::string (key));
}
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -23,17 +23,37 @@
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
class JsonObject_test : public TestOutputSuite class JsonObject_test : public TestOutputSuite
{ {
void setup (std::string const& testName)
{
testcase (testName);
output_.clear ();
}
std::unique_ptr<WriterObject> writerObject_;
Object& makeRoot()
{
writerObject_ = std::make_unique<WriterObject> (
stringWriterObject (output_));
return **writerObject_;
}
void expectResult (std::string const& expected)
{
writerObject_.reset();
TestOutputSuite::expectResult (expected);
}
public: public:
void testTrivial () void testTrivial ()
{ {
setup ("trivial"); setup ("trivial");
{ {
Object::Root root (*writer_); auto& root = makeRoot();
(void) root; (void) root;
} }
expectResult ("{}"); expectResult ("{}");
@@ -43,7 +63,7 @@ public:
{ {
setup ("simple"); setup ("simple");
{ {
Object::Root root (*writer_); auto& root = makeRoot();
root["hello"] = "world"; root["hello"] = "world";
root["skidoo"] = 23; root["skidoo"] = 23;
root["awake"] = false; root["awake"] = false;
@@ -60,7 +80,7 @@ public:
void testSimpleShort () void testSimpleShort ()
{ {
setup ("simpleShort"); setup ("simpleShort");
Object::Root (*writer_) makeRoot()
.set ("hello", "world") .set ("hello", "world")
.set ("skidoo", 23) .set ("skidoo", 23)
.set ("awake", false) .set ("awake", false)
@@ -77,7 +97,7 @@ public:
{ {
setup ("oneSub"); setup ("oneSub");
{ {
Object::Root root (*writer_); auto& root = makeRoot();
root.makeArray ("ar"); root.makeArray ("ar");
} }
expectResult ("{\"ar\":[]}"); expectResult ("{\"ar\":[]}");
@@ -87,7 +107,7 @@ public:
{ {
setup ("subs"); setup ("subs");
{ {
Object::Root root (*writer_); auto& root = makeRoot();
{ {
// Add an array with three entries. // Add an array with three entries.
@@ -122,7 +142,7 @@ public:
setup ("subsShort"); setup ("subsShort");
{ {
Object::Root root (*writer_); auto& root = makeRoot();
// Add an array with three entries. // Add an array with three entries.
root.makeArray ("ar") root.makeArray ("ar")
@@ -163,19 +183,19 @@ public:
{ {
{ {
setup ("object failure assign"); setup ("object failure assign");
Object::Root root (*writer_); auto& root = makeRoot();
auto obj = root.makeObject ("o1"); auto obj = root.makeObject ("o1");
expectException ([&]() { root["fail"] = "complete"; }); expectException ([&]() { root["fail"] = "complete"; });
} }
{ {
setup ("object failure object"); setup ("object failure object");
Object::Root root (*writer_); auto& root = makeRoot();
auto obj = root.makeObject ("o1"); auto obj = root.makeObject ("o1");
expectException ([&] () { root.makeObject ("o2"); }); expectException ([&] () { root.makeObject ("o2"); });
} }
{ {
setup ("object failure Array"); setup ("object failure Array");
Object::Root root (*writer_); auto& root = makeRoot();
auto obj = root.makeArray ("o1"); auto obj = root.makeArray ("o1");
expectException ([&] () { root.makeArray ("o2"); }); expectException ([&] () { root.makeArray ("o2"); });
} }
@@ -185,7 +205,7 @@ public:
{ {
{ {
setup ("array failure append"); setup ("array failure append");
Object::Root root (*writer_); auto& root = makeRoot();
auto array = root.makeArray ("array"); auto array = root.makeArray ("array");
auto subarray = array.makeArray (); auto subarray = array.makeArray ();
auto fail = [&]() { array.append ("fail"); }; auto fail = [&]() { array.append ("fail"); };
@@ -193,7 +213,7 @@ public:
} }
{ {
setup ("array failure makeArray"); setup ("array failure makeArray");
Object::Root root (*writer_); auto& root = makeRoot();
auto array = root.makeArray ("array"); auto array = root.makeArray ("array");
auto subarray = array.makeArray (); auto subarray = array.makeArray ();
auto fail = [&]() { array.makeArray (); }; auto fail = [&]() { array.makeArray (); };
@@ -201,7 +221,7 @@ public:
} }
{ {
setup ("array failure makeObject"); setup ("array failure makeObject");
Object::Root root (*writer_); auto& root = makeRoot();
auto array = root.makeArray ("array"); auto array = root.makeArray ("array");
auto subarray = array.makeArray (); auto subarray = array.makeArray ();
auto fail = [&]() { array.makeObject (); }; auto fail = [&]() { array.makeObject (); };
@@ -213,7 +233,7 @@ public:
{ {
#ifdef DEBUG #ifdef DEBUG
setup ("repeating keys"); setup ("repeating keys");
Object::Root root(*writer_); auto& root = makeRoot();
root.set ("foo", "bar") root.set ("foo", "bar")
.set ("baz", 0); .set ("baz", 0);
auto fail = [&]() { root.set ("foo", "bar"); }; auto fail = [&]() { root.set ("foo", "bar"); };
@@ -223,6 +243,7 @@ public:
void run () override void run () override
{ {
testTrivial ();
testSimple (); testSimple ();
testSimpleShort (); testSimpleShort ();
@@ -238,6 +259,5 @@ public:
BEAST_DEFINE_TESTSUITE(JsonObject, ripple_basics, ripple); BEAST_DEFINE_TESTSUITE(JsonObject, ripple_basics, ripple);
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -18,11 +18,11 @@
//============================================================================== //==============================================================================
#include <ripple/rpc/impl/JsonWriter.h> #include <ripple/rpc/impl/JsonWriter.h>
#include <ripple/rpc/impl/WriteJson.h>
#include <beast/unit_test/suite.h> #include <beast/unit_test/suite.h>
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
namespace { namespace {
@@ -37,7 +37,7 @@ std::map <char, const char*> jsonSpecialCharacterEscape = {
{'\t', "\\t"} {'\t', "\\t"}
}; };
static int const jsonEscapeLength = 2; static size_t const jsonEscapeLength = 2;
// All other JSON punctuation. // All other JSON punctuation.
const char closeBrace = '}'; const char closeBrace = '}';
@@ -50,12 +50,24 @@ const char quote = '"';
const std::string none; const std::string none;
static auto const integralFloatsBecomeInts = false;
size_t lengthWithoutTrailingZeros (std::string const& s) size_t lengthWithoutTrailingZeros (std::string const& s)
{ {
if (s.find ('.') == std::string::npos) auto dotPos = s.find ('.');
if (dotPos == std::string::npos)
return s.size(); return s.size();
return s.find_last_not_of ('0') + 1; auto lastNonZero = s.find_last_not_of ('0');
auto hasDecimals = dotPos != lastNonZero;
if (hasDecimals)
return lastNonZero + 1;
if (integralFloatsBecomeInts || lastNonZero + 2 > s.size())
return lastNonZero;
return lastNonZero + 2;
} }
} // namespace } // namespace
@@ -63,7 +75,8 @@ size_t lengthWithoutTrailingZeros (std::string const& s)
class Writer::Impl class Writer::Impl
{ {
public: public:
Impl (Output output) : output_(output) {} Impl (Output const& output) : output_(output) {}
~Impl() = default;
Impl(Impl&&) = delete; Impl(Impl&&) = delete;
Impl& operator=(Impl&&) = delete; Impl& operator=(Impl&&) = delete;
@@ -168,6 +181,8 @@ public:
} }
} }
Output const& getOutput() const { return output_; }
private: private:
// JSON collections are either arrrays, or objects. // JSON collections are either arrrays, or objects.
struct Collection struct Collection
@@ -193,12 +208,14 @@ private:
bool isStarted_ = false; bool isStarted_ = false;
}; };
Writer::Writer (Output output) : impl_(std::make_unique <Impl> (output)) Writer::Writer (Output const &output)
: impl_(std::make_unique <Impl> (output))
{ {
} }
Writer::~Writer() Writer::~Writer()
{ {
if (impl_)
impl_->finishAll (); impl_->finishAll ();
} }
@@ -223,6 +240,12 @@ void Writer::output (std::string const& s)
impl_->stringOutput (s); impl_->stringOutput (s);
} }
void Writer::output (Json::Value const& value)
{
impl_->markStarted();
writeJson (value, impl_->getOutput());
}
template <> template <>
void Writer::output (float f) void Writer::output (float f)
{ {
@@ -243,37 +266,32 @@ void Writer::output (std::nullptr_t)
impl_->output ("null"); impl_->output ("null");
} }
template <typename Type> void Writer::implOutput (std::string const& s)
void Writer::output (Type t)
{ {
impl_->output (to_string (t)); impl_->output (s);
} }
void Writer::finishAll () void Writer::finishAll ()
{ {
if (impl_)
impl_->finishAll (); impl_->finishAll ();
} }
template <typename Type> void Writer::rawAppend()
void Writer::append (Type t)
{ {
impl_->nextCollectionEntry (array, "append"); impl_->nextCollectionEntry (array, "append");
output (t);
} }
template <typename Type> void Writer::rawSet (std::string const& tag)
void Writer::set (std::string const& tag, Type t)
{ {
check (!tag.empty(), "Tag can't be empty"); check (!tag.empty(), "Tag can't be empty");
impl_->nextCollectionEntry (object, "set"); impl_->nextCollectionEntry (object, "set");
impl_->writeObjectTag (tag); impl_->writeObjectTag (tag);
output (t);
} }
void Writer::startRoot (CollectionType type) void Writer::startRoot (CollectionType type)
{ {
check (impl_->empty(), "stack_ not empty() in start");
impl_->start (type); impl_->start (type);
} }
@@ -292,9 +310,9 @@ void Writer::startSet (CollectionType type, std::string const& key)
void Writer::finish () void Writer::finish ()
{ {
if (impl_)
impl_->finish (); impl_->finish ();
} }
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -22,10 +22,10 @@
#include <ripple/basics/ToString.h> #include <ripple/basics/ToString.h>
#include <ripple/rpc/Output.h> #include <ripple/rpc/Output.h>
#include <ripple/rpc/ErrorCodes.h>
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
/** /**
* Writer implements an O(1)-space, O(1)-granular output JSON writer. * Writer implements an O(1)-space, O(1)-granular output JSON writer.
@@ -127,13 +127,13 @@ class Writer
public: public:
enum CollectionType {array, object}; enum CollectionType {array, object};
explicit Writer (Output output); explicit Writer (Output const& output);
Writer(Writer&&); Writer(Writer&&);
Writer& operator=(Writer&&); Writer& operator=(Writer&&);
~Writer(); ~Writer();
/** Start a new collection at the root level. May only be called once. */ /** Start a new collection at the root level. */
void startRoot (CollectionType); void startRoot (CollectionType);
/** Start a new collection inside an array. */ /** Start a new collection inside an array. */
@@ -152,10 +152,18 @@ public:
/** Append a value to an array. /** Append a value to an array.
* *
* Scalar must be a scalar - that is, a number, boolean, string, string * Scalar must be a scalar - that is, a number, boolean, string, string
* literal, or nullptr. * literal, nullptr or Json::Value
*/ */
template <typename Scalar> template <typename Scalar>
void append (Scalar); void append (Scalar t)
{
rawAppend();
output (t);
}
/** Add a comma before this next item if not the first item in an array.
Useful if you are writing the actual array yourself. */
void rawAppend();
/** Add a key, value assignment to an object. /** Add a key, value assignment to an object.
* *
@@ -168,8 +176,16 @@ public:
* If CHECK_JSON_WRITER is defined, this function throws an exception if if * If CHECK_JSON_WRITER is defined, this function throws an exception if if
* the tag you use has already been used in this object. * the tag you use has already been used in this object.
*/ */
template <typename Scalar> template <typename Type>
void set (std::string const& key, Scalar value); void set (std::string const& tag, Type t)
{
rawSet (tag);
output (t);
}
/** Emit just "tag": as part of an object. Useful if you are writing the
actual value data yourself. */
void rawSet (std::string const& key);
// You won't need to call anything below here until you are writing single // You won't need to call anything below here until you are writing single
// items (numbers, strings, bools, null) to a JSON stream. // items (numbers, strings, bools, null) to a JSON stream.
@@ -180,13 +196,32 @@ public:
/*** Output a literal constant or C string. */ /*** Output a literal constant or C string. */
void output (char const*); void output (char const*);
/*** Output a literal constant or C string. */
void output (Json::Value const&);
/** Output numbers, booleans, or nullptr. */ /** Output numbers, booleans, or nullptr. */
template <typename Scalar> template <typename Type>
void output (Scalar t); void output (Type t)
{
implOutput (to_string (t));
}
/** Output an error code. */
void output (error_code_i t)
{
output (int(t));
}
void output (Json::StaticString const& t)
{
output (t.c_str());
}
private: private:
class Impl; class Impl;
std::unique_ptr <Impl> impl_; std::unique_ptr <Impl> impl_;
void implOutput (std::string const&);
}; };
class JsonException : public std::exception class JsonException : public std::exception
@@ -208,7 +243,6 @@ inline void check (bool condition, std::string const& message)
throw JsonException (message); throw JsonException (message);
} }
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -17,13 +17,14 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/json/json_writer.h>
#include <ripple/rpc/impl/JsonWriter.h> #include <ripple/rpc/impl/JsonWriter.h>
#include <ripple/rpc/impl/TestOutputSuite.h> #include <ripple/rpc/impl/TestOutputSuite.h>
#include <beast/unit_test/suite.h> #include <beast/unit_test/suite.h>
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
class JsonWriter_test : public TestOutputSuite class JsonWriter_test : public TestOutputSuite
{ {
@@ -32,6 +33,13 @@ public:
{ {
setup ("trivial"); setup ("trivial");
expect (output_.empty ()); expect (output_.empty ());
expectResult("");
}
void testNearTrivial ()
{
setup ("near trivial");
expect (output_.empty ());
writer_->output (0); writer_->output (0);
expectResult("0"); expectResult("0");
} }
@@ -50,6 +58,10 @@ public:
writer_->output (23); writer_->output (23);
expectResult ("23"); expectResult ("23");
setup ("23.0");
writer_->output (23.0);
expectResult ("23.0");
setup ("23.5"); setup ("23.5");
writer_->output (23.5); writer_->output (23.5);
expectResult ("23.5"); expectResult ("23.5");
@@ -161,9 +173,22 @@ public:
"\"subarray\":[23.5]}]]}"); "\"subarray\":[23.5]}]]}");
} }
void testJson ()
{
setup ("object");
Json::Value value (Json::objectValue);
value["foo"] = 23;
writer_->startRoot (Writer::object);
writer_->set ("hello", value);
writer_->finish ();
expectResult ("{\"hello\":{\"foo\":23}}");
}
void run () override void run () override
{ {
testTrivial (); testTrivial ();
testNearTrivial ();
testPrimitives (); testPrimitives ();
testEmpty (); testEmpty ();
testEscaping (); testEscaping ();
@@ -172,11 +197,11 @@ public:
testEmbeddedArraySimple (); testEmbeddedArraySimple ();
testObject (); testObject ();
testComplexObject (); testComplexObject ();
testJson();
} }
}; };
BEAST_DEFINE_TESTSUITE(JsonWriter, ripple_basics, ripple); BEAST_DEFINE_TESTSUITE(JsonWriter, ripple_basics, ripple);
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -21,7 +21,6 @@
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
std::string Status::codeString () const std::string Status::codeString () const
{ {
@@ -72,6 +71,5 @@ void Status::fillJson (Json::Value& value)
} }
} }
} // namespace New
} // namespace RPC } // namespace RPC
} // ripple } // ripple

View File

@@ -22,7 +22,6 @@
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
class codeString_test : public beast::unit_test::suite class codeString_test : public beast::unit_test::suite
{ {
@@ -209,6 +208,5 @@ public:
BEAST_DEFINE_TESTSUITE (fillJson, Status, RPC); BEAST_DEFINE_TESTSUITE (fillJson, Status, RPC);
} // namespace New
} // namespace RPC } // namespace RPC
} // ripple } // ripple

View File

@@ -26,13 +26,11 @@
namespace ripple { namespace ripple {
namespace RPC { namespace RPC {
namespace New {
class TestOutputSuite : public beast::unit_test::suite class TestOutputSuite : public beast::unit_test::suite
{ {
protected: protected:
std::string output_; std::string output_;
std::unique_ptr <Writer> writer_; std::unique_ptr <Writer> writer_;
void setup (std::string const& testName) void setup (std::string const& testName)
@@ -46,6 +44,7 @@ protected:
void expectResult (std::string const& expected) void expectResult (std::string const& expected)
{ {
writer_.reset (); writer_.reset ();
expectEquals (output_, expected); expectEquals (output_, expected);
} }
@@ -58,7 +57,6 @@ protected:
} }
}; };
} // New
} // RPC } // RPC
} // ripple } // ripple

View File

@@ -0,0 +1,112 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/rpc/impl/WriteJson.h>
#include <ripple/rpc/impl/JsonWriter.h>
namespace ripple {
namespace RPC {
namespace {
void writeJson (Json::Value const& value, Writer& writer)
{
switch (value.type())
{
case Json::nullValue:
{
writer.output (nullptr);
break;
}
case Json::intValue:
{
writer.output (value.asInt());
break;
}
case Json::uintValue:
{
writer.output (value.asUInt());
break;
}
case Json::realValue:
{
writer.output (value.asDouble());
break;
}
case Json::stringValue:
{
writer.output (value.asString());
break;
}
case Json::booleanValue:
{
writer.output (value.asBool());
break;
}
case Json::arrayValue:
{
writer.startRoot (Writer::array);
for (auto const& i: value)
{
writer.rawAppend();
writeJson (i, writer);
}
writer.finish();
break;
}
case Json::objectValue:
{
writer.startRoot (Writer::object);
auto members = value.getMemberNames ();
for (auto const& tag: members)
{
writer.rawSet (tag);
writeJson (value[tag], writer);
}
writer.finish();
break;
}
} // switch
}
} // namespace
void writeJson (Json::Value const& value, Output const& out)
{
Writer writer (out);
writeJson (value, writer);
}
std::string jsonAsString (Json::Value const& value)
{
std::string s;
Writer writer (stringOutput (s));
writeJson (value, writer);
return s;
}
} // RPC
} // ripple

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLED_RIPPLE_RPC_IMPL_WRITELEGACYJSON_H
#define RIPPLED_RIPPLE_RPC_IMPL_WRITELEGACYJSON_H
namespace ripple {
namespace RPC {
/** Writes a minimal representation of a Json value to an Output in O(n) time.
Data is streamed right to the output, so only a marginal amount of memory is
used. This can be very important for a very large Json::Value.
*/
void writeJson (Json::Value const&, Output const&);
/** Return the minimal string representation of a Json::Value in O(n) time.
This requires a memory allocation for the full size of the output.
If possible, use write().
*/
std::string jsonAsString (Json::Value const&);
} // RPC
} // ripple
#endif

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/rpc/impl/WriteJson.h>
#include <ripple/rpc/impl/TestOutputSuite.h>
namespace ripple {
namespace RPC {
struct WriteJson_test : TestOutputSuite
{
void runTest (std::string const& name, std::string const& valueDesc)
{
setup (name);
Json::Value value;
expect (Json::Reader().parse (valueDesc, value));
auto out = stringOutput (output_);
writeJson (value, out);
// Compare with the original version.
auto expected = Json::FastWriter().write (value);
expected.resize (expected.size() - 1);
// For some reason, the FastWriter puts a carriage return on the end of
// every piece of Json it outputs.
expectResult (expected);
expectResult (valueDesc);
expectResult (jsonAsString (value));
}
void runTest (std::string const& name)
{
runTest (name, name);
}
void run () override
{
runTest ("null");
runTest ("true");
runTest ("0");
runTest ("23.5");
runTest ("string", "\"a string\"");
runTest ("empty dict", "{}");
runTest ("empty array", "[]");
runTest ("array", "[23,4.25,true,null,\"string\"]");
runTest ("dict", "{\"hello\":\"world\"}");
runTest ("array dict", "[{}]");
runTest ("array array", "[[]]");
runTest ("more complex",
"{\"array\":[{\"12\":23},{},null,false,0.5]}");
}
};
BEAST_DEFINE_TESTSUITE(WriteJson, ripple_basics, ripple);
} // RPC
} // ripple

View File

@@ -30,14 +30,15 @@
#include <ripple/overlay/Overlay.h> #include <ripple/overlay/Overlay.h>
#include <tuple> #include <tuple>
#include <ripple/rpc/impl/Coroutine.cpp>
#include <ripple/rpc/impl/ErrorCodes.cpp> #include <ripple/rpc/impl/ErrorCodes.cpp>
#include <ripple/rpc/impl/JsonObject.cpp> #include <ripple/rpc/impl/JsonObject.cpp>
#include <ripple/rpc/impl/JsonWriter.cpp> #include <ripple/rpc/impl/JsonWriter.cpp>
#include <ripple/rpc/impl/WriteJson.cpp>
#include <ripple/rpc/impl/Manager.cpp> #include <ripple/rpc/impl/Manager.cpp>
#include <ripple/rpc/impl/RPCHandler.cpp> #include <ripple/rpc/impl/RPCHandler.cpp>
#include <ripple/rpc/impl/Status.cpp> #include <ripple/rpc/impl/Status.cpp>
#include <ripple/rpc/impl/Yield.cpp> #include <ripple/rpc/impl/Yield.cpp>
#include <ripple/rpc/impl/Status_test.cpp>
#include <ripple/rpc/handlers/Handlers.h> #include <ripple/rpc/handlers/Handlers.h>
#include <ripple/rpc/handlers/AccountCurrencies.cpp> #include <ripple/rpc/handlers/AccountCurrencies.cpp>