Move streaming Json objects to ripple/json.

This commit is contained in:
Tom Ritchford
2015-01-27 12:10:42 -05:00
parent c5d673c426
commit e9b7003cf5
32 changed files with 173 additions and 184 deletions

View File

@@ -85,11 +85,11 @@ class HandlerTable {
assert (table_.find(HandlerImpl::name()) == table_.end());
Handler h;
h.name_ = HandlerImpl::name(),
h.valueMethod_ = &handle<Json::Value, HandlerImpl>,
h.role_ = HandlerImpl::role(),
h.condition_ = HandlerImpl::condition(),
h.objectMethod_ = &handle<Object, HandlerImpl>;
h.name_ = HandlerImpl::name();
h.valueMethod_ = &handle<Json::Value, HandlerImpl>;
h.role_ = HandlerImpl::role();
h.condition_ = HandlerImpl::condition();
h.objectMethod_ = &handle<Json::Object, HandlerImpl>;
table_[HandlerImpl::name()] = h;
};

View File

@@ -24,11 +24,13 @@
#include <ripple/rpc/RPCHandler.h>
#include <ripple/rpc/Status.h>
namespace Json {
class Object;
}
namespace ripple {
namespace RPC {
class Object;
// Under what condition can we call this RPC?
enum Condition {
NO_CONDITION = 0,
@@ -46,7 +48,7 @@ struct Handler
Method<Json::Value> valueMethod_;
Role role_;
RPC::Condition condition_;
Method<Object> objectMethod_;
Method<Json::Object> objectMethod_;
};
const Handler* getHandler (std::string const&);

View File

@@ -1,161 +0,0 @@
//------------------------------------------------------------------------------
/*
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 <BeastConfig.h>
#include <ripple/rpc/impl/JsonObject.h>
namespace ripple {
namespace RPC {
Collection::Collection (Collection* parent, Writer* writer)
: parent_ (parent), writer_ (writer), enabled_ (true)
{
checkWritable ("Collection::Collection()");
if (parent_)
{
check (parent_->enabled_, "Parent not enabled in constructor");
parent_->enabled_ = false;
}
}
Collection::~Collection ()
{
if (writer_)
writer_->finish ();
if (parent_)
parent_->enabled_ = true;
}
Collection& Collection::operator= (Collection&& that) noexcept
{
parent_ = that.parent_;
writer_ = that.writer_;
enabled_ = that.enabled_;
that.parent_ = nullptr;
that.writer_ = nullptr;
that.enabled_ = false;
return *this;
}
Collection::Collection (Collection&& that) noexcept
{
*this = std::move (that);
}
void Collection::checkWritable (std::string const& label)
{
if (!enabled_)
throw JsonException (label + ": not enabled");
if (!writer_)
throw JsonException (label + ": not writable");
}
//------------------------------------------------------------------------------
Object::Root::Root (Writer& w) : Object (nullptr, &w)
{
writer_->startRoot (Writer::object);
}
Object Object::setObject (std::string const& key)
{
checkWritable ("Object::setObject");
if (writer_)
writer_->startSet (Writer::object, key);
return Object (this, writer_);
}
Array Object::setArray (std::string const& key) {
checkWritable ("Object::setArray");
if (writer_)
writer_->startSet (Writer::array, key);
return Array (this, writer_);
}
//------------------------------------------------------------------------------
Object Array::appendObject ()
{
checkWritable ("Array::appendObject");
if (writer_)
writer_->startAppend (Writer::object);
return Object (this, writer_);
}
Array Array::appendArray ()
{
checkWritable ("Array::makeArray");
if (writer_)
writer_->startAppend (Writer::array);
return Array (this, writer_);
}
//------------------------------------------------------------------------------
Object::Proxy::Proxy (Object& object, std::string const& key)
: object_ (object)
, key_ (key)
{
}
Object::Proxy Object::operator[] (std::string const& 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)
{
assert (from.isObject());
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));
}
} // RPC
} // ripple

View File

@@ -1,495 +0,0 @@
//------------------------------------------------------------------------------
/*
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 RIPPLE_RPC_JSONOBJECT_H_INCLUDED
#define RIPPLE_RPC_JSONOBJECT_H_INCLUDED
#include <ripple/rpc/impl/JsonWriter.h>
namespace ripple {
namespace RPC {
/**
Collection is a base class for Array and Object, classes which provide the
facade of JSON collections for the O(1) JSON writer, while still using no
heap memory and only a very small amount of stack.
From http://json.org, JSON has two types of collection: array, and object.
Everything else is a *scalar* - a number, a string, a boolean, the special
value null, or a legacy Json::Value.
Collections must write JSON "as-it-goes" in order to get the strong
performance guarantees. This puts restrictions upon API users:
1. Only one collection can be open for change at any one time.
This condition is enforced automatically and a JsonException thrown if it
is violated.
2. A tag may only be used once in an Object.
Some objects have many tags, so this condition might be a little
expensive. Enforcement of this condition is turned on in debug builds and
a JsonException is thrown when the tag is added for a second time.
Code samples:
Writer writer;
// An empty object.
{
Object::Root (writer);
}
// Outputs {}
// An object with one scalar value.
{
Object::Root root (writer);
write["hello"] = "world";
}
// Outputs {"hello":"world"}
// Same, using chaining.
{
Object::Root (writer)["hello"] = "world";
}
// Output is the same.
// Add several scalars, with chaining.
{
Object::Root (writer)
.set ("hello", "world")
.set ("flag", false)
.set ("x", 42);
}
// Outputs {"hello":"world","flag":false,"x":42}
// Add an array.
{
Object::Root root (writer);
{
auto array = root.setArray ("hands");
array.append ("left");
array.append ("right");
}
}
// Outputs {"hands":["left", "right"]}
// Same, using chaining.
{
Object::Root (writer)
.setArray ("hands")
.append ("left")
.append ("right");
}
// Output is the same.
// Add an object.
{
Object::Root root (writer);
{
auto object = root.setObject ("hands");
object["left"] = false;
object["right"] = true;
}
}
// Outputs {"hands":{"left":false,"right":true}}
// Same, using chaining.
{
Object::Root (writer)
.setObject ("hands")
.set ("left", false)
.set ("right", true);
}
}
// Outputs {"hands":{"left":false,"right":true}}
Typical ways to make mistakes and get a JsonException:
Writer writer;
Object::Root root (writer);
// Repeat a tag.
{
root ["hello"] = "world";
root ["hello"] = "there"; // THROWS! in a debug build.
}
// Open a subcollection, then set something else.
{
auto object = root.setObject ("foo");
root ["hello"] = "world"; // THROWS!
}
// Open two subcollections at a time.
{
auto object = root.setObject ("foo");
auto array = root.setArray ("bar"); // THROWS!!
}
For more examples, check the unit tests.
*/
class Collection
{
public:
Collection (Collection&& c) noexcept;
Collection& operator= (Collection&& c) noexcept;
Collection() = delete;
~Collection();
protected:
// A null parent means "no parent at all".
// Writers cannot be null.
Collection (Collection* parent, Writer*);
void checkWritable (std::string const& label);
Collection* parent_;
Writer* writer_;
bool enabled_;
};
class Array;
//------------------------------------------------------------------------------
/** Represents a JSON object being written to a Writer. */
class Object : protected Collection
{
public:
/** Object::Root is the only Collection that has a public constructor. */
class Root;
/** Set a scalar value in the Object for a key.
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
one of its children is enabled).
In a debug build, `set()` also throws an exception if the key has
already been set() before.
An operator[] is provided to allow writing `object["key"] = scalar;`.
*/
template <typename Scalar>
void set (std::string const& key, Scalar const&);
void set (std::string const& key, Json::Value const&);
// Detail class and method used to implement operator[].
class Proxy;
Proxy operator[] (std::string const& key);
Proxy operator[] (Json::StaticString const& key);
/** Make a new Object at a key and return it.
This Object is disabled until that sub-object is destroyed.
Throws an exception if this Object was already disabled.
*/
Object setObject (std::string const& key);
/** Make a new Array at a key and return it.
This Object is disabled until that sub-array is destroyed.
Throws an exception if this Object was already disabled.
*/
Array setArray (std::string const& key);
protected:
friend class Array;
Object (Collection* parent, Writer* w) : Collection (parent, w) {}
};
class Object::Root : public Object
{
public:
/** Each Object::Root must be constructed with its own unique Writer. */
Root (Writer&);
};
//------------------------------------------------------------------------------
/** Represents a JSON array being written to a Writer. */
class Array : private Collection
{
public:
/** Append a scalar to the Arrary.
Throws an exception if this array is disabled (which means that one of
its sub-collections is enabled).
*/
template <typename Scalar>
void append (Scalar const&);
/**
Appends a Json::Value to an array.
Throws an exception if this Array was disabled.
*/
void append (Json::Value const&);
/** Append a new Object and return it.
This Array is disabled until that sub-object is destroyed.
Throws an exception if this Array was disabled.
*/
Object appendObject ();
/** Append a new Array and return it.
This Array is disabled until that sub-array is destroyed.
Throws an exception if this Array was already disabled.
*/
Array appendArray ();
protected:
friend class Object;
Array (Collection* parent, Writer* w) : Collection (parent, w) {}
};
//------------------------------------------------------------------------------
// 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& setArray (Json::Value&, Json::StaticString const& key);
/** Add a new subarray at a named key in a Json object. */
Array setArray (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);
/** Append a new subarray to a Json array. */
Json::Value& appendArray (Json::Value&);
/** Append a new subarray to a Json array. */
Array appendArray (Array&);
/** Append a new subobject to a Json object. */
Json::Value& appendObject (Json::Value&);
/** Append a new subobject to a Json object. */
Object appendObject (Array&);
/** 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) noexcept
: 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[].
class Object::Proxy
{
private:
Object& object_;
std::string const key_;
public:
Proxy (Object& object, std::string const& key);
template <class T>
void operator= (T const& t)
{
object_.set (key_, t);
}
};
//------------------------------------------------------------------------------
template <typename Scalar>
void Array::append (Scalar const& value)
{
checkWritable ("append");
if (writer_)
writer_->append (value);
}
inline void Array::append (Json::Value const& v)
{
auto t = v.type();
switch (t)
{
case Json::nullValue: return append (nullptr);
case Json::intValue: return append (v.asInt());
case Json::uintValue: return append (v.asUInt());
case Json::realValue: return append (v.asDouble());
case Json::stringValue: return append (v.asString());
case Json::booleanValue: return append (v.asBool());
case Json::objectValue:
{
auto object = appendObject ();
copyFrom (object, v);
return;
}
case Json::arrayValue:
{
auto array = appendArray ();
for (auto& item: v)
array.append (item);
return;
}
}
assert (false); // Can't get here.
}
template <typename Scalar>
void Object::set (std::string const& key, Scalar const& value)
{
checkWritable ("set");
if (writer_)
writer_->set (key, value);
}
inline void Object::set (std::string const& k, Json::Value const& v)
{
auto t = v.type();
switch (t)
{
case Json::nullValue: return set (k, nullptr);
case Json::intValue: return set (k, v.asInt());
case Json::uintValue: return set (k, v.asUInt());
case Json::realValue: return set (k, v.asDouble());
case Json::stringValue: return set (k, v.asString());
case Json::booleanValue: return set (k, v.asBool());
case Json::objectValue:
{
auto object = setObject (k);
copyFrom (object, v);
return;
}
case Json::arrayValue:
{
auto array = setArray (k);
for (auto& item: v)
array.append (item);
return;
}
}
assert (false); // Can't get here.
}
inline
Json::Value& setArray (Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::arrayValue);
}
inline
Array setArray (Object& json, Json::StaticString const& key)
{
return json.setArray (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.setObject (std::string (key));
}
inline
Json::Value& appendArray (Json::Value& json)
{
return json.append (Json::arrayValue);
}
inline
Array appendArray (Array& json)
{
return json.appendArray ();
}
inline
Json::Value& appendObject (Json::Value& json)
{
return json.append (Json::objectValue);
}
inline
Object appendObject (Array& json)
{
return json.appendObject ();
}
} // RPC
} // ripple
#endif

View File

@@ -1,319 +0,0 @@
//------------------------------------------------------------------------------
/*
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 <BeastConfig.h>
#include <ripple/rpc/impl/JsonWriter.h>
#include <ripple/rpc/impl/WriteJson.h>
#include <beast/unit_test/suite.h>
namespace ripple {
namespace RPC {
namespace {
std::map <char, const char*> jsonSpecialCharacterEscape = {
{'"', "\\\""},
{'\\', "\\\\"},
{'/', "\\/"},
{'\b', "\\b"},
{'\f', "\\f"},
{'\n', "\\n"},
{'\r', "\\r"},
{'\t', "\\t"}
};
static size_t const jsonEscapeLength = 2;
// All other JSON punctuation.
const char closeBrace = '}';
const char closeBracket = ']';
const char colon = ':';
const char comma = ',';
const char openBrace = '{';
const char openBracket = '[';
const char quote = '"';
const std::string none;
static auto const integralFloatsBecomeInts = false;
size_t lengthWithoutTrailingZeros (std::string const& s)
{
auto dotPos = s.find ('.');
if (dotPos == std::string::npos)
return s.size();
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
class Writer::Impl
{
public:
Impl (Output const& output) : output_(output) {}
~Impl() = default;
Impl(Impl&&) = delete;
Impl& operator=(Impl&&) = delete;
bool empty() const { return stack_.empty (); }
void start (CollectionType ct)
{
char ch = (ct == array) ? openBracket : openBrace;
output ({&ch, 1});
stack_.push (Collection());
stack_.top().type = ct;
}
void output (boost::string_ref const& bytes)
{
markStarted ();
output_ (bytes);
}
void stringOutput (boost::string_ref const& bytes)
{
markStarted ();
std::size_t position = 0, writtenUntil = 0;
output_ ({&quote, 1});
auto data = bytes.data();
for (; position < bytes.size(); ++position)
{
auto i = jsonSpecialCharacterEscape.find (data[position]);
if (i != jsonSpecialCharacterEscape.end ())
{
if (writtenUntil < position)
{
output_ ({data + writtenUntil, position - writtenUntil});
}
output_ ({i->second, jsonEscapeLength});
writtenUntil = position + 1;
};
}
if (writtenUntil < position)
output_ ({data + writtenUntil, position - writtenUntil});
output_ ({&quote, 1});
}
void markStarted ()
{
check (!isFinished(), "isFinished() in output.");
isStarted_ = true;
}
void nextCollectionEntry (CollectionType type, std::string const& message)
{
check (!empty() , "empty () in " + message);
auto t = stack_.top ().type;
if (t != type)
{
check (false, "Not an " +
((type == array ? "array: " : "object: ") + message));
}
if (stack_.top ().isFirst)
stack_.top ().isFirst = false;
else
output_ ({&comma, 1});
}
void writeObjectTag (std::string const& tag)
{
#ifdef DEBUG
// Make sure we haven't already seen this tag.
auto& tags = stack_.top ().tags;
check (tags.find (tag) == tags.end (), "Already seen tag " + tag);
tags.insert (tag);
#endif
stringOutput (tag);
output_ ({&colon, 1});
}
bool isFinished() const
{
return isStarted_ && empty();
}
void finish ()
{
check (!empty(), "Empty stack in finish()");
auto isArray = stack_.top().type == array;
auto ch = isArray ? closeBracket : closeBrace;
output_ ({&ch, 1});
stack_.pop();
}
void finishAll ()
{
if (isStarted_)
{
while (!isFinished())
finish();
}
}
Output const& getOutput() const { return output_; }
private:
// JSON collections are either arrrays, or objects.
struct Collection
{
/** What type of collection are we in? */
Writer::CollectionType type;
/** Is this the first entry in a collection?
* If false, we have to emit a , before we write the next entry. */
bool isFirst = true;
#ifdef DEBUG
/** What tags have we already seen in this collection? */
std::set <std::string> tags;
#endif
};
using Stack = std::stack <Collection, std::vector<Collection>>;
Output output_;
Stack stack_;
bool isStarted_ = false;
};
Writer::Writer (Output const &output)
: impl_(std::make_unique <Impl> (output))
{
}
Writer::~Writer()
{
if (impl_)
impl_->finishAll ();
}
Writer::Writer(Writer&& w) noexcept
{
impl_ = std::move (w.impl_);
}
Writer& Writer::operator=(Writer&& w) noexcept
{
impl_ = std::move (w.impl_);
return *this;
}
void Writer::output (char const* s)
{
impl_->stringOutput (s);
}
void Writer::output (std::string const& s)
{
impl_->stringOutput (s);
}
void Writer::output (Json::Value const& value)
{
impl_->markStarted();
writeJson (value, impl_->getOutput());
}
template <>
void Writer::output (float f)
{
auto s = to_string (f);
impl_->output ({s.data (), lengthWithoutTrailingZeros (s)});
}
template <>
void Writer::output (double f)
{
auto s = to_string (f);
impl_->output ({s.data (), lengthWithoutTrailingZeros (s)});
}
template <>
void Writer::output (std::nullptr_t)
{
impl_->output ("null");
}
void Writer::implOutput (std::string const& s)
{
impl_->output (s);
}
void Writer::finishAll ()
{
if (impl_)
impl_->finishAll ();
}
void Writer::rawAppend()
{
impl_->nextCollectionEntry (array, "append");
}
void Writer::rawSet (std::string const& tag)
{
check (!tag.empty(), "Tag can't be empty");
impl_->nextCollectionEntry (object, "set");
impl_->writeObjectTag (tag);
}
void Writer::startRoot (CollectionType type)
{
impl_->start (type);
}
void Writer::startAppend (CollectionType type)
{
impl_->nextCollectionEntry (array, "startAppend");
impl_->start (type);
}
void Writer::startSet (CollectionType type, std::string const& key)
{
impl_->nextCollectionEntry (object, "startSet");
impl_->writeObjectTag (key);
impl_->start (type);
}
void Writer::finish ()
{
if (impl_)
impl_->finish ();
}
} // RPC
} // ripple

View File

@@ -1,250 +0,0 @@
//------------------------------------------------------------------------------
/*
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 RIPPLE_RPC_JSONWRITER_H_INCLUDED
#define RIPPLE_RPC_JSONWRITER_H_INCLUDED
#include <ripple/basics/ToString.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/rpc/Output.h>
#include <ripple/protocol/ErrorCodes.h>
namespace ripple {
namespace RPC {
/**
* Writer implements an O(1)-space, O(1)-granular output JSON writer.
*
* O(1)-space means that it uses a fixed amount of memory, and that there are
* no heap allocations at each step of the way.
*
* O(1)-granular output means the writer only outputs in small segments of a
* bounded size, using a bounded number of CPU cycles in doing so. This is
* very helpful in scheduling long jobs.
*
* The tradeoff is that you have to fill items in the JSON tree as you go,
* and you can never go backward.
*
* Writer can write single JSON tokens, but the typical use is to write out an
* entire JSON object. For example:
*
* {
* Writer w (out);
*
* w.startObject (); // Start the root object.
* w.set ("hello", "world");
* w.set ("goodbye", 23);
* w.finishObject (); // Finish the root object.
* }
*
* which outputs the string
*
* {"hello":"world","goodbye":23}
*
* There can be an object inside an object:
*
* {
* Writer w (out);
*
* w.startObject (); // Start the root object.
* w.set ("hello", "world");
*
* w.startObjectSet ("subobject"); // Start a sub-object.
* w.set ("goodbye", 23); // Add a key, value assignment.
* w.finishObject (); // Finish the sub-object.
*
* w.finishObject (); // Finish the root-object.
* }
*
* which outputs the string
*
* {"hello":"world","subobject":{"goodbye":23}}.
*
* Arrays work similarly
*
* {
* Writer w (out);
* w.startObject (); // Start the root object.
*
* w.startArraySet ("hello"); // Start an array.
* w.append (23) // Append some items.
* w.append ("skidoo")
* w.finishArray (); // Finish the array.
*
* w.finishObject (); // Finish the root object.
* }
*
* which outputs the string
*
* {"hello":[23,"skidoo"]}.
*
*
* If you've reached the end of a long object, you can just use finishAll()
* which finishes all arrays and objects that you have started.
*
* {
* Writer w (out);
* w.startObject (); // Start the root object.
*
* w.startArraySet ("hello"); // Start an array.
* w.append (23) // Append an item.
*
* w.startArrayAppend () // Start a sub-array.
* w.append ("one");
* w.append ("two");
*
* w.startObjectAppend (); // Append a sub-object.
* w.finishAll (); // Finish everything.
* }
*
* which outputs the string
*
* {"hello":[23,["one","two",{}]]}.
*
* For convenience, the destructor of Writer calls w.finishAll() which makes
* sure that all arrays and objects are closed. This means that you can throw
* an exception, or have a coroutine simply clean up the stack, and be sure
* that you do in fact generate a complete JSON object.
*/
class Writer
{
public:
enum CollectionType {array, object};
explicit Writer (Output const& output);
Writer(Writer&&) noexcept;
Writer& operator=(Writer&&) noexcept;
~Writer();
/** Start a new collection at the root level. */
void startRoot (CollectionType);
/** Start a new collection inside an array. */
void startAppend (CollectionType);
/** Start a new collection inside an object. */
void startSet (CollectionType, std::string const& key);
/** Finish the collection most recently started. */
void finish ();
/** Finish all objects and arrays. After finishArray() has been called, no
* more operations can be performed. */
void finishAll ();
/** Append a value to an array.
*
* Scalar must be a scalar - that is, a number, boolean, string, string
* literal, nullptr or Json::Value
*/
template <typename 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.
*
* Scalar must be a scalar - that is, a number, boolean, string, string
* literal, or nullptr.
*
* While the JSON spec doesn't explicitly disallow this, you should avoid
* calling this method twice with the same tag for the same object.
*
* If CHECK_JSON_WRITER is defined, this function throws an exception if if
* the tag you use has already been used in this object.
*/
template <typename Type>
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
// items (numbers, strings, bools, null) to a JSON stream.
/*** Output a string. */
void output (std::string const&);
/*** Output a literal constant or C string. */
void output (char const*);
/*** Output a Json::Value. */
void output (Json::Value const&);
/** Output numbers or booleans. */
template <typename Type>
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:
class Impl;
std::unique_ptr <Impl> impl_;
void implOutput (std::string const&);
};
class JsonException : public std::exception
{
public:
explicit JsonException (std::string const& name) : name_(name) {}
const char* what() const throw() override
{
return name_.c_str();
}
private:
std::string const name_;
};
inline void check (bool condition, std::string const& message)
{
if (!condition)
throw JsonException (message);
}
} // RPC
} // ripple
#endif

View File

@@ -22,12 +22,12 @@
#include <ripple/rpc/Yield.h>
#include <ripple/rpc/impl/Tuning.h>
#include <ripple/rpc/impl/Handler.h>
#include <ripple/rpc/impl/WriteJson.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/core/JobQueue.h>
#include <ripple/json/Object.h>
#include <ripple/json/to_string.h>
#include <ripple/net/InfoSub.h>
#include <ripple/net/RPCErr.h>
@@ -190,7 +190,7 @@ template <class Method, class Object>
void getResult (
Context& context, Method method, Object& object, std::string const& name)
{
auto&& result = addObject (object, jss::result);
auto&& result = Json::addObject (object, jss::result);
if (auto status = callMethod (context, method, name, result))
{
WriteLog (lsDEBUG, RPCErr) << "rpcError: " << status.toString();
@@ -228,13 +228,13 @@ void executeRPC (
boost::optional <Handler const&> handler;
if (auto error = fillHandler (context, handler))
{
auto wo = stringWriterObject (output);
auto&& sub = addObject (*wo, jss::result);
auto wo = Json::stringWriterObject (output);
auto&& sub = Json::addObject (*wo, jss::result);
inject_error (error, sub);
}
else if (auto method = handler->objectMethod_)
{
auto wo = stringWriterObject (output);
auto wo = Json::stringWriterObject (output);
getResult (context, method, *wo, handler->name_);
}
else if (auto method = handler->valueMethod_)
@@ -250,7 +250,7 @@ void executeRPC (
{
// Can't ever get here.
assert (false);
throw RPC::JsonException ("RPC handler with no method");
throw Json::JsonException ("RPC handler with no method");
}
}

View File

@@ -1,113 +0,0 @@
//------------------------------------------------------------------------------
/*
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 <BeastConfig.h>
#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

@@ -1,43 +0,0 @@
//------------------------------------------------------------------------------
/*
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 RIPPLE_RPC_WRITEJSON_H_INCLUDED
#define RIPPLE_RPC_WRITEJSON_H_INCLUDED
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

@@ -24,8 +24,8 @@
namespace ripple {
namespace RPC {
Output chunkedYieldingOutput (
Output const& output, Yield const& yield, std::size_t chunkSize)
Json::Output chunkedYieldingOutput (
Json::Output const& output, Yield const& yield, std::size_t chunkSize)
{
auto count = std::make_shared <std::size_t> (0);
return [chunkSize, count, output, yield] (boost::string_ref const& bytes)