236 lines
4.1 KiB
C++
236 lines
4.1 KiB
C++
#include "JsonWriter.h"
|
|
|
|
#include <iomanip>
|
|
#include <stdexcept>
|
|
|
|
namespace RenderCadenceCompositor
|
|
{
|
|
namespace
|
|
{
|
|
constexpr int kMaxDepth = 32;
|
|
|
|
void AppendHexEscape(std::ostringstream& stream, unsigned char value)
|
|
{
|
|
stream << "\\u"
|
|
<< std::hex << std::uppercase << std::setw(4) << std::setfill('0')
|
|
<< static_cast<int>(value)
|
|
<< std::dec << std::nouppercase << std::setfill(' ');
|
|
}
|
|
}
|
|
|
|
void JsonWriter::BeginObject()
|
|
{
|
|
BeginValue();
|
|
mStream << "{";
|
|
PushScope(ScopeKind::Object);
|
|
}
|
|
|
|
void JsonWriter::EndObject()
|
|
{
|
|
PopScope(ScopeKind::Object);
|
|
mStream << "}";
|
|
}
|
|
|
|
void JsonWriter::BeginArray()
|
|
{
|
|
BeginValue();
|
|
mStream << "[";
|
|
PushScope(ScopeKind::Array);
|
|
}
|
|
|
|
void JsonWriter::EndArray()
|
|
{
|
|
PopScope(ScopeKind::Array);
|
|
mStream << "]";
|
|
}
|
|
|
|
void JsonWriter::Key(const std::string& name)
|
|
{
|
|
BeginKey();
|
|
mStream << "\"" << EscapeString(name) << "\":";
|
|
}
|
|
|
|
void JsonWriter::String(const std::string& value)
|
|
{
|
|
BeginValue();
|
|
mStream << "\"" << EscapeString(value) << "\"";
|
|
}
|
|
|
|
void JsonWriter::Bool(bool value)
|
|
{
|
|
BeginValue();
|
|
mStream << (value ? "true" : "false");
|
|
}
|
|
|
|
void JsonWriter::Null()
|
|
{
|
|
BeginValue();
|
|
mStream << "null";
|
|
}
|
|
|
|
void JsonWriter::Int(int64_t value)
|
|
{
|
|
BeginValue();
|
|
mStream << value;
|
|
}
|
|
|
|
void JsonWriter::UInt(uint64_t value)
|
|
{
|
|
BeginValue();
|
|
mStream << value;
|
|
}
|
|
|
|
void JsonWriter::Double(double value)
|
|
{
|
|
BeginValue();
|
|
mStream << std::setprecision(15) << value;
|
|
}
|
|
|
|
void JsonWriter::KeyString(const std::string& name, const std::string& value)
|
|
{
|
|
Key(name);
|
|
String(value);
|
|
}
|
|
|
|
void JsonWriter::KeyBool(const std::string& name, bool value)
|
|
{
|
|
Key(name);
|
|
Bool(value);
|
|
}
|
|
|
|
void JsonWriter::KeyNull(const std::string& name)
|
|
{
|
|
Key(name);
|
|
Null();
|
|
}
|
|
|
|
void JsonWriter::KeyInt(const std::string& name, int64_t value)
|
|
{
|
|
Key(name);
|
|
Int(value);
|
|
}
|
|
|
|
void JsonWriter::KeyUInt(const std::string& name, uint64_t value)
|
|
{
|
|
Key(name);
|
|
UInt(value);
|
|
}
|
|
|
|
void JsonWriter::KeyDouble(const std::string& name, double value)
|
|
{
|
|
Key(name);
|
|
Double(value);
|
|
}
|
|
|
|
std::string JsonWriter::StringValue() const
|
|
{
|
|
if (mScopeDepth != 0)
|
|
throw std::logic_error("JSON document has unclosed scopes.");
|
|
return mStream.str();
|
|
}
|
|
|
|
void JsonWriter::Reset()
|
|
{
|
|
mStream.str(std::string());
|
|
mStream.clear();
|
|
mScopeDepth = 0;
|
|
}
|
|
|
|
std::string JsonWriter::EscapeString(const std::string& value)
|
|
{
|
|
std::ostringstream stream;
|
|
for (unsigned char character : value)
|
|
{
|
|
switch (character)
|
|
{
|
|
case '"':
|
|
stream << "\\\"";
|
|
break;
|
|
case '\\':
|
|
stream << "\\\\";
|
|
break;
|
|
case '\b':
|
|
stream << "\\b";
|
|
break;
|
|
case '\f':
|
|
stream << "\\f";
|
|
break;
|
|
case '\n':
|
|
stream << "\\n";
|
|
break;
|
|
case '\r':
|
|
stream << "\\r";
|
|
break;
|
|
case '\t':
|
|
stream << "\\t";
|
|
break;
|
|
default:
|
|
if (character < 0x20)
|
|
AppendHexEscape(stream, character);
|
|
else
|
|
stream << character;
|
|
break;
|
|
}
|
|
}
|
|
return stream.str();
|
|
}
|
|
|
|
void JsonWriter::BeginValue()
|
|
{
|
|
if (mScopeDepth == 0)
|
|
return;
|
|
|
|
Scope& scope = mScopes[mScopeDepth - 1];
|
|
if (scope.kind == ScopeKind::Object)
|
|
{
|
|
if (!scope.expectingValue)
|
|
throw std::logic_error("JSON object value must follow a key.");
|
|
scope.expectingValue = false;
|
|
return;
|
|
}
|
|
|
|
if (!scope.first)
|
|
mStream << ",";
|
|
scope.first = false;
|
|
}
|
|
|
|
void JsonWriter::BeginKey()
|
|
{
|
|
if (mScopeDepth == 0)
|
|
throw std::logic_error("JSON key cannot be written outside an object.");
|
|
|
|
Scope& scope = mScopes[mScopeDepth - 1];
|
|
if (scope.kind != ScopeKind::Object)
|
|
throw std::logic_error("JSON key cannot be written inside an array.");
|
|
if (scope.expectingValue)
|
|
throw std::logic_error("JSON object key cannot be written before its previous value.");
|
|
|
|
if (!scope.first)
|
|
mStream << ",";
|
|
scope.first = false;
|
|
scope.expectingValue = true;
|
|
}
|
|
|
|
void JsonWriter::PushScope(ScopeKind kind)
|
|
{
|
|
if (mScopeDepth >= kMaxDepth)
|
|
throw std::logic_error("JSON nesting is too deep.");
|
|
|
|
mScopes[mScopeDepth++] = Scope{ kind, true, false };
|
|
}
|
|
|
|
void JsonWriter::PopScope(ScopeKind kind)
|
|
{
|
|
if (mScopeDepth == 0)
|
|
throw std::logic_error("JSON scope underflow.");
|
|
|
|
Scope& scope = mScopes[mScopeDepth - 1];
|
|
if (scope.kind != kind)
|
|
throw std::logic_error("JSON scope kind mismatch.");
|
|
if (scope.expectingValue)
|
|
throw std::logic_error("JSON object key is missing a value.");
|
|
|
|
--mScopeDepth;
|
|
}
|
|
}
|