#include #include #include #include #include #include #include #include #include #include #include #include namespace Json { static bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } static bool containsControlCharacter(char const* str) { while (*str != 0) { 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 distinguish 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; // all-escaped+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 (*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; 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 = isMultilineArray(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::isMultilineArray(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 = isMultilineArray(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::isMultilineArray(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