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

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) // Writer appears to need about 16 bytes per nested object level (with 64bit size_t)
using StackAllocator = FixedLinearAllocator<2048>; using StackAllocator = FixedLinearAllocator<2048>;
constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t)); constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t));
using JsonWriter = using JsonWriterBase =
rapidjson::Writer<DirectStringBuffer, UTF8, UTF8, StackAllocator, rapidjson::kWriteNoFlags>; 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>; using JsonDocumentBase = rapidjson::GenericDocument<UTF8, PoolAllocator, StackAllocator>;
class JsonDocument : public JsonDocumentBase { class JsonDocument : public JsonDocumentBase {
public: public: