mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
This change refactors the codebase to use the "east const convention", and adds a clang-format rule to follow this convention.
737 lines
18 KiB
C++
737 lines
18 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
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 <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/json/json_forwards.h>
|
|
#include <xrpl/json/json_value.h>
|
|
#include <xrpl/json/json_writer.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <ios>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
namespace Json {
|
|
|
|
static bool
|
|
isControlCharacter(char ch)
|
|
{
|
|
return ch > 0 && ch <= 0x1F;
|
|
}
|
|
|
|
static bool
|
|
containsControlCharacter(char const* str)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (isControlCharacter(*(str++)))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
static void
|
|
uintToString(unsigned int value, char*& current)
|
|
{
|
|
*--current = 0;
|
|
|
|
do
|
|
{
|
|
*--current = (value % 10) + '0';
|
|
value /= 10;
|
|
} while (value != 0);
|
|
}
|
|
|
|
std::string
|
|
valueToString(Int value)
|
|
{
|
|
char buffer[32];
|
|
char* current = buffer + sizeof(buffer);
|
|
bool isNegative = value < 0;
|
|
|
|
if (isNegative)
|
|
value = -value;
|
|
|
|
uintToString(UInt(value), current);
|
|
|
|
if (isNegative)
|
|
*--current = '-';
|
|
|
|
XRPL_ASSERT(current >= buffer, "Json::valueToString(Int) : buffer check");
|
|
return current;
|
|
}
|
|
|
|
std::string
|
|
valueToString(UInt value)
|
|
{
|
|
char buffer[32];
|
|
char* current = buffer + sizeof(buffer);
|
|
uintToString(value, current);
|
|
XRPL_ASSERT(current >= buffer, "Json::valueToString(UInt) : buffer check");
|
|
return current;
|
|
}
|
|
|
|
std::string
|
|
valueToString(double value)
|
|
{
|
|
// Allocate a buffer that is more than large enough to store the 16 digits
|
|
// of precision requested below.
|
|
char buffer[32];
|
|
// Print into the buffer. We need not request the alternative representation
|
|
// that always has a decimal point because JSON doesn't distingish the
|
|
// concepts of reals and integers.
|
|
#if defined(_MSC_VER) && \
|
|
defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005
|
|
// to avoid warning.
|
|
sprintf_s(buffer, sizeof(buffer), "%.16g", value);
|
|
#else
|
|
snprintf(buffer, sizeof(buffer), "%.16g", value);
|
|
#endif
|
|
return buffer;
|
|
}
|
|
|
|
std::string
|
|
valueToString(bool value)
|
|
{
|
|
return value ? "true" : "false";
|
|
}
|
|
|
|
std::string
|
|
valueToQuotedString(char const* value)
|
|
{
|
|
// Not sure how to handle unicode...
|
|
if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr &&
|
|
!containsControlCharacter(value))
|
|
return std::string("\"") + value + "\"";
|
|
|
|
// We have to walk value and escape any special characters.
|
|
// Appending to std::string is not efficient, but this should be rare.
|
|
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
|
unsigned maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL
|
|
std::string result;
|
|
result.reserve(maxsize); // to avoid lots of mallocs
|
|
result += "\"";
|
|
|
|
for (char const* c = value; *c != 0; ++c)
|
|
{
|
|
switch (*c)
|
|
{
|
|
case '\"':
|
|
result += "\\\"";
|
|
break;
|
|
|
|
case '\\':
|
|
result += "\\\\";
|
|
break;
|
|
|
|
case '\b':
|
|
result += "\\b";
|
|
break;
|
|
|
|
case '\f':
|
|
result += "\\f";
|
|
break;
|
|
|
|
case '\n':
|
|
result += "\\n";
|
|
break;
|
|
|
|
case '\r':
|
|
result += "\\r";
|
|
break;
|
|
|
|
case '\t':
|
|
result += "\\t";
|
|
break;
|
|
|
|
// case '/':
|
|
// Even though \/ is considered a legal escape in JSON, a bare
|
|
// slash is also legal, so I see no reason to escape it.
|
|
// (I hope I am not misunderstanding something.
|
|
// blep notes: actually escaping \/ may be useful in javascript
|
|
// to avoid </ sequence. Should add a flag to allow this
|
|
// compatibility mode and prevent this sequence from occurring.
|
|
default:
|
|
if (isControlCharacter(*c))
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "\\u" << std::hex << std::uppercase
|
|
<< std::setfill('0') << std::setw(4)
|
|
<< static_cast<int>(*c);
|
|
result += oss.str();
|
|
}
|
|
else
|
|
{
|
|
result += *c;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
result += "\"";
|
|
return result;
|
|
}
|
|
|
|
// Class FastWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
std::string
|
|
FastWriter::write(Value const& root)
|
|
{
|
|
document_ = "";
|
|
writeValue(root);
|
|
return std::move(document_);
|
|
}
|
|
|
|
void
|
|
FastWriter::writeValue(Value const& value)
|
|
{
|
|
switch (value.type())
|
|
{
|
|
case nullValue:
|
|
document_ += "null";
|
|
break;
|
|
|
|
case intValue:
|
|
document_ += valueToString(value.asInt());
|
|
break;
|
|
|
|
case uintValue:
|
|
document_ += valueToString(value.asUInt());
|
|
break;
|
|
|
|
case realValue:
|
|
document_ += valueToString(value.asDouble());
|
|
break;
|
|
|
|
case stringValue:
|
|
document_ += valueToQuotedString(value.asCString());
|
|
break;
|
|
|
|
case booleanValue:
|
|
document_ += valueToString(value.asBool());
|
|
break;
|
|
|
|
case arrayValue: {
|
|
document_ += "[";
|
|
int size = value.size();
|
|
|
|
for (int index = 0; index < size; ++index)
|
|
{
|
|
if (index > 0)
|
|
document_ += ",";
|
|
|
|
writeValue(value[index]);
|
|
}
|
|
|
|
document_ += "]";
|
|
}
|
|
break;
|
|
|
|
case objectValue: {
|
|
Value::Members members(value.getMemberNames());
|
|
document_ += "{";
|
|
|
|
for (Value::Members::iterator it = members.begin();
|
|
it != members.end();
|
|
++it)
|
|
{
|
|
std::string const& name = *it;
|
|
|
|
if (it != members.begin())
|
|
document_ += ",";
|
|
|
|
document_ += valueToQuotedString(name.c_str());
|
|
document_ += ":";
|
|
writeValue(value[name]);
|
|
}
|
|
|
|
document_ += "}";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Class StyledWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3)
|
|
{
|
|
}
|
|
|
|
std::string
|
|
StyledWriter::write(Value const& root)
|
|
{
|
|
document_ = "";
|
|
addChildValues_ = false;
|
|
indentString_ = "";
|
|
writeValue(root);
|
|
document_ += "\n";
|
|
return document_;
|
|
}
|
|
|
|
void
|
|
StyledWriter::writeValue(Value const& value)
|
|
{
|
|
switch (value.type())
|
|
{
|
|
case nullValue:
|
|
pushValue("null");
|
|
break;
|
|
|
|
case intValue:
|
|
pushValue(valueToString(value.asInt()));
|
|
break;
|
|
|
|
case uintValue:
|
|
pushValue(valueToString(value.asUInt()));
|
|
break;
|
|
|
|
case realValue:
|
|
pushValue(valueToString(value.asDouble()));
|
|
break;
|
|
|
|
case stringValue:
|
|
pushValue(valueToQuotedString(value.asCString()));
|
|
break;
|
|
|
|
case booleanValue:
|
|
pushValue(valueToString(value.asBool()));
|
|
break;
|
|
|
|
case arrayValue:
|
|
writeArrayValue(value);
|
|
break;
|
|
|
|
case objectValue: {
|
|
Value::Members members(value.getMemberNames());
|
|
|
|
if (members.empty())
|
|
pushValue("{}");
|
|
else
|
|
{
|
|
writeWithIndent("{");
|
|
indent();
|
|
Value::Members::iterator it = members.begin();
|
|
|
|
while (true)
|
|
{
|
|
std::string const& name = *it;
|
|
Value const& childValue = value[name];
|
|
writeWithIndent(valueToQuotedString(name.c_str()));
|
|
document_ += " : ";
|
|
writeValue(childValue);
|
|
|
|
if (++it == members.end())
|
|
break;
|
|
|
|
document_ += ",";
|
|
}
|
|
|
|
unindent();
|
|
writeWithIndent("}");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
StyledWriter::writeArrayValue(Value const& value)
|
|
{
|
|
unsigned size = value.size();
|
|
|
|
if (size == 0)
|
|
pushValue("[]");
|
|
else
|
|
{
|
|
bool isArrayMultiLine = isMultineArray(value);
|
|
|
|
if (isArrayMultiLine)
|
|
{
|
|
writeWithIndent("[");
|
|
indent();
|
|
bool hasChildValue = !childValues_.empty();
|
|
unsigned index = 0;
|
|
|
|
while (true)
|
|
{
|
|
Value const& childValue = value[index];
|
|
|
|
if (hasChildValue)
|
|
writeWithIndent(childValues_[index]);
|
|
else
|
|
{
|
|
writeIndent();
|
|
writeValue(childValue);
|
|
}
|
|
|
|
if (++index == size)
|
|
break;
|
|
|
|
document_ += ",";
|
|
}
|
|
|
|
unindent();
|
|
writeWithIndent("]");
|
|
}
|
|
else // output on a single line
|
|
{
|
|
XRPL_ASSERT(
|
|
childValues_.size() == size,
|
|
"Json::StyledWriter::writeArrayValue : child size match");
|
|
document_ += "[ ";
|
|
|
|
for (unsigned index = 0; index < size; ++index)
|
|
{
|
|
if (index > 0)
|
|
document_ += ", ";
|
|
|
|
document_ += childValues_[index];
|
|
}
|
|
|
|
document_ += " ]";
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
StyledWriter::isMultineArray(Value const& value)
|
|
{
|
|
int size = value.size();
|
|
bool isMultiLine = size * 3 >= rightMargin_;
|
|
childValues_.clear();
|
|
|
|
for (int index = 0; index < size && !isMultiLine; ++index)
|
|
{
|
|
Value const& childValue = value[index];
|
|
isMultiLine = isMultiLine ||
|
|
((childValue.isArray() || childValue.isObject()) &&
|
|
childValue.size() > 0);
|
|
}
|
|
|
|
if (!isMultiLine) // check if line length > max line length
|
|
{
|
|
childValues_.reserve(size);
|
|
addChildValues_ = true;
|
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for (int index = 0; index < size; ++index)
|
|
{
|
|
writeValue(value[index]);
|
|
lineLength += int(childValues_[index].length());
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
}
|
|
|
|
return isMultiLine;
|
|
}
|
|
|
|
void
|
|
StyledWriter::pushValue(std::string const& value)
|
|
{
|
|
if (addChildValues_)
|
|
childValues_.push_back(value);
|
|
else
|
|
document_ += value;
|
|
}
|
|
|
|
void
|
|
StyledWriter::writeIndent()
|
|
{
|
|
if (!document_.empty())
|
|
{
|
|
char last = document_[document_.length() - 1];
|
|
|
|
if (last == ' ') // already indented
|
|
return;
|
|
|
|
if (last != '\n') // Comments may add new-line
|
|
document_ += '\n';
|
|
}
|
|
|
|
document_ += indentString_;
|
|
}
|
|
|
|
void
|
|
StyledWriter::writeWithIndent(std::string const& value)
|
|
{
|
|
writeIndent();
|
|
document_ += value;
|
|
}
|
|
|
|
void
|
|
StyledWriter::indent()
|
|
{
|
|
indentString_ += std::string(indentSize_, ' ');
|
|
}
|
|
|
|
void
|
|
StyledWriter::unindent()
|
|
{
|
|
XRPL_ASSERT(
|
|
int(indentString_.size()) >= indentSize_,
|
|
"Json::StyledWriter::unindent : maximum indent size");
|
|
indentString_.resize(indentString_.size() - indentSize_);
|
|
}
|
|
|
|
// Class StyledStreamWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
StyledStreamWriter::StyledStreamWriter(std::string indentation)
|
|
: document_(nullptr), rightMargin_(74), indentation_(indentation)
|
|
{
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::write(std::ostream& out, Value const& root)
|
|
{
|
|
document_ = &out;
|
|
addChildValues_ = false;
|
|
indentString_ = "";
|
|
writeValue(root);
|
|
*document_ << "\n";
|
|
document_ = nullptr; // Forget the stream, for safety.
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::writeValue(Value const& value)
|
|
{
|
|
switch (value.type())
|
|
{
|
|
case nullValue:
|
|
pushValue("null");
|
|
break;
|
|
|
|
case intValue:
|
|
pushValue(valueToString(value.asInt()));
|
|
break;
|
|
|
|
case uintValue:
|
|
pushValue(valueToString(value.asUInt()));
|
|
break;
|
|
|
|
case realValue:
|
|
pushValue(valueToString(value.asDouble()));
|
|
break;
|
|
|
|
case stringValue:
|
|
pushValue(valueToQuotedString(value.asCString()));
|
|
break;
|
|
|
|
case booleanValue:
|
|
pushValue(valueToString(value.asBool()));
|
|
break;
|
|
|
|
case arrayValue:
|
|
writeArrayValue(value);
|
|
break;
|
|
|
|
case objectValue: {
|
|
Value::Members members(value.getMemberNames());
|
|
|
|
if (members.empty())
|
|
pushValue("{}");
|
|
else
|
|
{
|
|
writeWithIndent("{");
|
|
indent();
|
|
Value::Members::iterator it = members.begin();
|
|
|
|
while (true)
|
|
{
|
|
std::string const& name = *it;
|
|
Value const& childValue = value[name];
|
|
writeWithIndent(valueToQuotedString(name.c_str()));
|
|
*document_ << " : ";
|
|
writeValue(childValue);
|
|
|
|
if (++it == members.end())
|
|
break;
|
|
|
|
*document_ << ",";
|
|
}
|
|
|
|
unindent();
|
|
writeWithIndent("}");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::writeArrayValue(Value const& value)
|
|
{
|
|
unsigned size = value.size();
|
|
|
|
if (size == 0)
|
|
pushValue("[]");
|
|
else
|
|
{
|
|
bool isArrayMultiLine = isMultineArray(value);
|
|
|
|
if (isArrayMultiLine)
|
|
{
|
|
writeWithIndent("[");
|
|
indent();
|
|
bool hasChildValue = !childValues_.empty();
|
|
unsigned index = 0;
|
|
|
|
while (true)
|
|
{
|
|
Value const& childValue = value[index];
|
|
|
|
if (hasChildValue)
|
|
writeWithIndent(childValues_[index]);
|
|
else
|
|
{
|
|
writeIndent();
|
|
writeValue(childValue);
|
|
}
|
|
|
|
if (++index == size)
|
|
break;
|
|
|
|
*document_ << ",";
|
|
}
|
|
|
|
unindent();
|
|
writeWithIndent("]");
|
|
}
|
|
else // output on a single line
|
|
{
|
|
XRPL_ASSERT(
|
|
childValues_.size() == size,
|
|
"Json::StyledStreamWriter::writeArrayValue : child size match");
|
|
*document_ << "[ ";
|
|
|
|
for (unsigned index = 0; index < size; ++index)
|
|
{
|
|
if (index > 0)
|
|
*document_ << ", ";
|
|
|
|
*document_ << childValues_[index];
|
|
}
|
|
|
|
*document_ << " ]";
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
StyledStreamWriter::isMultineArray(Value const& value)
|
|
{
|
|
int size = value.size();
|
|
bool isMultiLine = size * 3 >= rightMargin_;
|
|
childValues_.clear();
|
|
|
|
for (int index = 0; index < size && !isMultiLine; ++index)
|
|
{
|
|
Value const& childValue = value[index];
|
|
isMultiLine = isMultiLine ||
|
|
((childValue.isArray() || childValue.isObject()) &&
|
|
childValue.size() > 0);
|
|
}
|
|
|
|
if (!isMultiLine) // check if line length > max line length
|
|
{
|
|
childValues_.reserve(size);
|
|
addChildValues_ = true;
|
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for (int index = 0; index < size; ++index)
|
|
{
|
|
writeValue(value[index]);
|
|
lineLength += int(childValues_[index].length());
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
}
|
|
|
|
return isMultiLine;
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::pushValue(std::string const& value)
|
|
{
|
|
if (addChildValues_)
|
|
childValues_.push_back(value);
|
|
else
|
|
*document_ << value;
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::writeIndent()
|
|
{
|
|
/*
|
|
Some comments in this method would have been nice. ;-)
|
|
|
|
if ( !document_.empty() )
|
|
{
|
|
char last = document_[document_.length()-1];
|
|
if ( last == ' ' ) // already indented
|
|
return;
|
|
if ( last != '\n' ) // Comments may add new-line
|
|
*document_ << '\n';
|
|
}
|
|
*/
|
|
*document_ << '\n' << indentString_;
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::writeWithIndent(std::string const& value)
|
|
{
|
|
writeIndent();
|
|
*document_ << value;
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::indent()
|
|
{
|
|
indentString_ += indentation_;
|
|
}
|
|
|
|
void
|
|
StyledStreamWriter::unindent()
|
|
{
|
|
XRPL_ASSERT(
|
|
indentString_.size() >= indentation_.size(),
|
|
"Json::StyledStreamWriter::unindent : maximum indent size");
|
|
indentString_.resize(indentString_.size() - indentation_.size());
|
|
}
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream& sout, Value const& root)
|
|
{
|
|
Json::StyledStreamWriter writer;
|
|
writer.write(sout, root);
|
|
return sout;
|
|
}
|
|
|
|
} // namespace Json
|