all repos — mgba @ 59d2e58bbbd42746d2bc781fe8d8adfe1ff3588f

mGBA Game Boy Advance Emulator

src/third-party/discord-rpc/src/serialization.h (view raw)

  1#pragma once
  2
  3#include <stdint.h>
  4
  5#ifndef __MINGW32__
  6#pragma warning(push)
  7
  8#pragma warning(disable : 4061) // enum is not explicitly handled by a case label
  9#pragma warning(disable : 4365) // signed/unsigned mismatch
 10#pragma warning(disable : 4464) // relative include path contains
 11#pragma warning(disable : 4668) // is not defined as a preprocessor macro
 12#pragma warning(disable : 6313) // Incorrect operator
 13#endif                          // __MINGW32__
 14
 15#include "rapidjson/document.h"
 16#include "rapidjson/stringbuffer.h"
 17#include "rapidjson/writer.h"
 18
 19#ifndef __MINGW32__
 20#pragma warning(pop)
 21#endif // __MINGW32__
 22
 23// if only there was a standard library function for this
 24template <size_t Len>
 25inline size_t StringCopy(char (&dest)[Len], const char* src)
 26{
 27    if (!src || !Len) {
 28        return 0;
 29    }
 30    size_t copied;
 31    char* out = dest;
 32    for (copied = 1; *src && copied < Len; ++copied) {
 33        *out++ = *src++;
 34    }
 35    *out = 0;
 36    return copied - 1;
 37}
 38
 39size_t JsonWriteHandshakeObj(char* dest, size_t maxLen, int version, const char* applicationId);
 40
 41// Commands
 42struct DiscordRichPresence;
 43size_t JsonWriteRichPresenceObj(char* dest,
 44                                size_t maxLen,
 45                                int nonce,
 46                                int pid,
 47                                const DiscordRichPresence* presence);
 48size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName);
 49
 50size_t JsonWriteUnsubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName);
 51
 52size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce);
 53
 54// I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
 55// to supply some of your own allocators for stuff rather than use the defaults
 56
 57class LinearAllocator {
 58public:
 59    char* buffer_;
 60    char* end_;
 61    LinearAllocator()
 62    {
 63        assert(0); // needed for some default case in rapidjson, should not use
 64    }
 65    LinearAllocator(char* buffer, size_t size)
 66      : buffer_(buffer)
 67      , end_(buffer + size)
 68    {
 69    }
 70    static const bool kNeedFree = false;
 71    void* Malloc(size_t size)
 72    {
 73        char* res = buffer_;
 74        buffer_ += size;
 75        if (buffer_ > end_) {
 76            buffer_ = res;
 77            return nullptr;
 78        }
 79        return res;
 80    }
 81    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
 82    {
 83        if (newSize == 0) {
 84            return nullptr;
 85        }
 86        // allocate how much you need in the first place
 87        assert(!originalPtr && !originalSize);
 88        // unused parameter warning
 89        (void)(originalPtr);
 90        (void)(originalSize);
 91        return Malloc(newSize);
 92    }
 93    static void Free(void* ptr)
 94    {
 95        /* shrug */
 96        (void)ptr;
 97    }
 98};
 99
100template <size_t Size>
101class FixedLinearAllocator : public LinearAllocator {
102public:
103    char fixedBuffer_[Size];
104    FixedLinearAllocator()
105      : LinearAllocator(fixedBuffer_, Size)
106    {
107    }
108    static const bool kNeedFree = false;
109};
110
111// wonder why this isn't a thing already, maybe I missed it
112class DirectStringBuffer {
113public:
114    using Ch = char;
115    char* buffer_;
116    char* end_;
117    char* current_;
118
119    DirectStringBuffer(char* buffer, size_t maxLen)
120      : buffer_(buffer)
121      , end_(buffer + maxLen)
122      , current_(buffer)
123    {
124    }
125
126    void Put(char c)
127    {
128        if (current_ < end_) {
129            *current_++ = c;
130        }
131    }
132    void Flush() {}
133    size_t GetSize() const { return (size_t)(current_ - buffer_); }
134};
135
136using MallocAllocator = rapidjson::CrtAllocator;
137using PoolAllocator = rapidjson::MemoryPoolAllocator<MallocAllocator>;
138using UTF8 = rapidjson::UTF8<char>;
139// Writer appears to need about 16 bytes per nested object level (with 64bit size_t)
140using StackAllocator = FixedLinearAllocator<2048>;
141constexpr size_t WriterNestingLevels = 2048 / (2 * sizeof(size_t));
142using JsonWriterBase =
143  rapidjson::Writer<DirectStringBuffer, UTF8, UTF8, StackAllocator, rapidjson::kWriteNoFlags>;
144class JsonWriter : public JsonWriterBase {
145public:
146    DirectStringBuffer stringBuffer_;
147    StackAllocator stackAlloc_;
148
149    JsonWriter(char* dest, size_t maxLen)
150      : JsonWriterBase(stringBuffer_, &stackAlloc_, WriterNestingLevels)
151      , stringBuffer_(dest, maxLen)
152      , stackAlloc_()
153    {
154    }
155
156    size_t Size() const { return stringBuffer_.GetSize(); }
157};
158
159using JsonDocumentBase = rapidjson::GenericDocument<UTF8, PoolAllocator, StackAllocator>;
160class JsonDocument : public JsonDocumentBase {
161public:
162    static const int kDefaultChunkCapacity = 32 * 1024;
163    // json parser will use this buffer first, then allocate more if needed; I seriously doubt we
164    // send any messages that would use all of this, though.
165    char parseBuffer_[32 * 1024];
166    MallocAllocator mallocAllocator_;
167    PoolAllocator poolAllocator_;
168    StackAllocator stackAllocator_;
169    JsonDocument()
170      : JsonDocumentBase(rapidjson::kObjectType,
171                         &poolAllocator_,
172                         sizeof(stackAllocator_.fixedBuffer_),
173                         &stackAllocator_)
174      , poolAllocator_(parseBuffer_, sizeof(parseBuffer_), kDefaultChunkCapacity, &mallocAllocator_)
175      , stackAllocator_()
176    {
177    }
178};
179
180using JsonValue = rapidjson::GenericValue<UTF8, PoolAllocator>;
181
182inline JsonValue* GetObjMember(JsonValue* obj, const char* name)
183{
184    if (obj) {
185        auto member = obj->FindMember(name);
186        if (member != obj->MemberEnd() && member->value.IsObject()) {
187            return &member->value;
188        }
189    }
190    return nullptr;
191}
192
193inline int GetIntMember(JsonValue* obj, const char* name, int notFoundDefault = 0)
194{
195    if (obj) {
196        auto member = obj->FindMember(name);
197        if (member != obj->MemberEnd() && member->value.IsInt()) {
198            return member->value.GetInt();
199        }
200    }
201    return notFoundDefault;
202}
203
204inline const char* GetStrMember(JsonValue* obj,
205                                const char* name,
206                                const char* notFoundDefault = nullptr)
207{
208    if (obj) {
209        auto member = obj->FindMember(name);
210        if (member != obj->MemberEnd() && member->value.IsString()) {
211            return member->value.GetString();
212        }
213    }
214    return notFoundDefault;
215}