Wrap json writer the same way I did reader. More RAII for json writing so I'm less likely to mess it up again.

This commit is contained in:
Chris Marsh 2017-07-28 13:42:58 -07:00
parent e69f9fbf71
commit 98852fba82
2 changed files with 117 additions and 110 deletions

View file

@ -2,8 +2,6 @@
#include "connection.h"
#include "discord-rpc.h"
MallocAllocator MallocAllocatorInst;
// it's ever so slightly faster to not have to strlen the key
template <typename T>
void WriteKey(JsonWriter& w, T& k)
@ -11,6 +9,35 @@ void WriteKey(JsonWriter& w, T& k)
w.Key(k, sizeof(T) - 1);
}
struct WriteObject {
JsonWriter& writer;
WriteObject(JsonWriter& w)
: writer(w)
{
writer.StartObject();
}
template <typename T>
WriteObject(JsonWriter& w, T& name)
: writer(w)
{
WriteKey(writer, name);
writer.StartObject();
}
~WriteObject() { writer.EndObject(); }
};
struct WriteArray {
JsonWriter& writer;
template <typename T>
WriteArray(JsonWriter& w, T& name)
: writer(w)
{
WriteKey(writer, name);
writer.StartArray();
}
~WriteArray() { writer.EndArray(); }
};
template <typename T>
void WriteOptionalString(JsonWriter& w, T& k, const char* value)
{
@ -28,150 +55,115 @@ void JsonWriteNonce(JsonWriter& writer, int nonce)
writer.String(nonceBuffer);
}
void JsonWriteCommandStart(JsonWriter& writer, int nonce, const char* cmd)
{
writer.StartObject();
JsonWriteNonce(writer, nonce);
WriteKey(writer, "cmd");
writer.String(cmd);
WriteKey(writer, "args");
writer.StartObject();
}
void JsonWriteCommandEnd(JsonWriter& writer)
{
writer.EndObject(); // args
writer.EndObject(); // top level
}
size_t JsonWriteRichPresenceObj(char* dest,
size_t maxLen,
int nonce,
int pid,
const DiscordRichPresence* presence)
{
DirectStringBuffer sb(dest, maxLen);
StackAllocator wa;
JsonWriter writer(sb, &wa, WriterNestingLevels);
JsonWriter writer(dest, maxLen);
JsonWriteCommandStart(writer, nonce, "SET_ACTIVITY");
{
WriteObject top(writer);
WriteKey(writer, "pid");
writer.Int(pid);
JsonWriteNonce(writer, nonce);
WriteKey(writer, "activity");
writer.StartObject();
WriteKey(writer, "cmd");
writer.String("SET_ACTIVITY");
WriteOptionalString(writer, "state", presence->state);
WriteOptionalString(writer, "details", presence->details);
{
WriteObject args(writer, "args");
if (presence->startTimestamp || presence->endTimestamp) {
WriteKey(writer, "timestamps");
writer.StartObject();
WriteKey(writer, "pid");
writer.Int(pid);
if (presence->startTimestamp) {
WriteKey(writer, "start");
writer.Int64(presence->startTimestamp);
}
{
WriteObject activity(writer, "activity");
if (presence->endTimestamp) {
WriteKey(writer, "end");
writer.Int64(presence->endTimestamp);
}
WriteOptionalString(writer, "state", presence->state);
WriteOptionalString(writer, "details", presence->details);
writer.EndObject();
}
if (presence->startTimestamp || presence->endTimestamp) {
WriteObject timestamps(writer, "timestamps");
if (presence->largeImageKey || presence->largeImageText || presence->smallImageKey ||
presence->smallImageText) {
WriteKey(writer, "assets");
writer.StartObject();
if (presence->startTimestamp) {
WriteKey(writer, "start");
writer.Int64(presence->startTimestamp);
}
WriteOptionalString(writer, "large_image", presence->largeImageKey);
WriteOptionalString(writer, "large_text", presence->largeImageText);
WriteOptionalString(writer, "small_image", presence->smallImageKey);
WriteOptionalString(writer, "small_text", presence->smallImageText);
if (presence->endTimestamp) {
WriteKey(writer, "end");
writer.Int64(presence->endTimestamp);
}
}
writer.EndObject();
}
if (presence->largeImageKey || presence->largeImageText ||
presence->smallImageKey || presence->smallImageText) {
WriteObject assets(writer, "assets");
WriteOptionalString(writer, "large_image", presence->largeImageKey);
WriteOptionalString(writer, "large_text", presence->largeImageText);
WriteOptionalString(writer, "small_image", presence->smallImageKey);
WriteOptionalString(writer, "small_text", presence->smallImageText);
}
if (presence->partyId || presence->partySize || presence->partyMax) {
WriteKey(writer, "party");
writer.StartObject();
if (presence->partyId || presence->partySize || presence->partyMax) {
WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize) {
WriteArray size(writer, "size");
writer.Int(presence->partySize);
if (0 < presence->partyMax) {
writer.Int(presence->partyMax);
}
}
}
WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize) {
WriteKey(writer, "size");
writer.StartArray();
if (presence->matchSecret || presence->joinSecret || presence->spectateSecret) {
WriteObject secrets(writer, "secrets");
WriteOptionalString(writer, "match", presence->matchSecret);
WriteOptionalString(writer, "join", presence->joinSecret);
WriteOptionalString(writer, "spectate", presence->spectateSecret);
}
writer.Int(presence->partySize);
if (0 < presence->partyMax) {
writer.Int(presence->partyMax);
writer.Key("instance");
writer.Bool(presence->instance != 0);
}
writer.EndArray();
}
writer.EndObject();
}
if (presence->matchSecret || presence->joinSecret || presence->spectateSecret) {
WriteKey(writer, "secrets");
writer.StartObject();
WriteOptionalString(writer, "match", presence->matchSecret);
WriteOptionalString(writer, "join", presence->joinSecret);
WriteOptionalString(writer, "spectate", presence->spectateSecret);
writer.EndObject();
}
writer.Key("instance");
writer.Bool(presence->instance != 0);
writer.EndObject(); // activity
JsonWriteCommandEnd(writer);
return sb.GetSize();
return writer.Size();
}
size_t JsonWriteHandshakeObj(char* dest, size_t maxLen, int version, const char* applicationId)
{
DirectStringBuffer sb(dest, maxLen);
StackAllocator wa;
JsonWriter writer(sb, &wa, WriterNestingLevels);
JsonWriter writer(dest, maxLen);
writer.StartObject();
WriteKey(writer, "v");
writer.Int(version);
WriteKey(writer, "client_id");
writer.String(applicationId);
writer.EndObject();
{
WriteObject obj(writer);
WriteKey(writer, "v");
writer.Int(version);
WriteKey(writer, "client_id");
writer.String(applicationId);
}
return sb.GetSize();
return writer.Size();
}
size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName)
{
DirectStringBuffer sb(dest, maxLen);
StackAllocator wa;
JsonWriter writer(sb, &wa, WriterNestingLevels);
JsonWriter writer(dest, maxLen);
writer.StartObject();
{
WriteObject obj(writer);
JsonWriteNonce(writer, nonce);
JsonWriteNonce(writer, nonce);
WriteKey(writer, "cmd");
writer.String("SUBSCRIBE");
WriteKey(writer, "cmd");
writer.String("SUBSCRIBE");
WriteKey(writer, "evt");
writer.String(evtName);
WriteKey(writer, "evt");
writer.String(evtName);
}
writer.EndObject();
return sb.GetSize();
return writer.Size();
}

View file

@ -120,8 +120,23 @@ using UTF8 = rapidjson::UTF8<char>;
// Writer appears to need about 16 bytes per nested object level (with 64bit size_t)
using StackAllocator = FixedLinearAllocator<2048>;
constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t));
using JsonWriter =
using JsonWriterBase =
rapidjson::Writer<DirectStringBuffer, UTF8, UTF8, StackAllocator, rapidjson::kWriteNoFlags>;
class JsonWriter : public JsonWriterBase {
public:
DirectStringBuffer stringBuffer_;
StackAllocator stackAlloc_;
JsonWriter(char* dest, size_t maxLen)
: JsonWriterBase(stringBuffer_, &stackAlloc_, WriterNestingLevels)
, stringBuffer_(dest, maxLen)
, stackAlloc_()
{
}
size_t Size() const { return stringBuffer_.GetSize(); }
};
using JsonDocumentBase = rapidjson::GenericDocument<UTF8, PoolAllocator, StackAllocator>;
class JsonDocument : public JsonDocumentBase {
public: