1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2017 Google Inc. 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #ifndef SkJSONWriter_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker #define SkJSONWriter_DEFINED 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkNoncopyable.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUTF.h" 17*c8dee2aaSAndroid Build Coastguard Worker 18*c8dee2aaSAndroid Build Coastguard Worker #include <cstring> 19*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint> 20*c8dee2aaSAndroid Build Coastguard Worker #include <string> 21*c8dee2aaSAndroid Build Coastguard Worker #include <type_traits> 22*c8dee2aaSAndroid Build Coastguard Worker 23*c8dee2aaSAndroid Build Coastguard Worker /** 24*c8dee2aaSAndroid Build Coastguard Worker * Lightweight class for writing properly structured JSON data. No random-access, everything must 25*c8dee2aaSAndroid Build Coastguard Worker * be generated in-order. The resulting JSON is written directly to the SkWStream supplied at 26*c8dee2aaSAndroid Build Coastguard Worker * construction time. Output is buffered, so writing to disk (via an SkFILEWStream) is ideal. 27*c8dee2aaSAndroid Build Coastguard Worker * 28*c8dee2aaSAndroid Build Coastguard Worker * There is a basic state machine to ensure that JSON is structured correctly, and to allow for 29*c8dee2aaSAndroid Build Coastguard Worker * (optional) pretty formatting. 30*c8dee2aaSAndroid Build Coastguard Worker * 31*c8dee2aaSAndroid Build Coastguard Worker * This class adheres to the RFC-4627 usage of JSON (not ECMA-404). In other words, all JSON 32*c8dee2aaSAndroid Build Coastguard Worker * created with this class must have a top-level object or array. Free-floating values of other 33*c8dee2aaSAndroid Build Coastguard Worker * types are not considered valid. 34*c8dee2aaSAndroid Build Coastguard Worker * 35*c8dee2aaSAndroid Build Coastguard Worker * Note that all error checking is in the form of asserts - invalid usage in a non-debug build 36*c8dee2aaSAndroid Build Coastguard Worker * will simply produce invalid JSON. 37*c8dee2aaSAndroid Build Coastguard Worker */ 38*c8dee2aaSAndroid Build Coastguard Worker class SkJSONWriter : SkNoncopyable { 39*c8dee2aaSAndroid Build Coastguard Worker public: 40*c8dee2aaSAndroid Build Coastguard Worker enum class Mode { 41*c8dee2aaSAndroid Build Coastguard Worker /** 42*c8dee2aaSAndroid Build Coastguard Worker * Output the minimal amount of text. No additional whitespace (including newlines) is 43*c8dee2aaSAndroid Build Coastguard Worker * generated. The resulting JSON is suitable for fast parsing and machine consumption. 44*c8dee2aaSAndroid Build Coastguard Worker */ 45*c8dee2aaSAndroid Build Coastguard Worker kFast, 46*c8dee2aaSAndroid Build Coastguard Worker 47*c8dee2aaSAndroid Build Coastguard Worker /** 48*c8dee2aaSAndroid Build Coastguard Worker * Output human-readable JSON, with indented objects and arrays, and one value per line. 49*c8dee2aaSAndroid Build Coastguard Worker * Slightly slower than kFast, and produces data that is somewhat larger. 50*c8dee2aaSAndroid Build Coastguard Worker */ 51*c8dee2aaSAndroid Build Coastguard Worker kPretty 52*c8dee2aaSAndroid Build Coastguard Worker }; 53*c8dee2aaSAndroid Build Coastguard Worker 54*c8dee2aaSAndroid Build Coastguard Worker /** 55*c8dee2aaSAndroid Build Coastguard Worker * Construct a JSON writer that will serialize all the generated JSON to 'stream'. 56*c8dee2aaSAndroid Build Coastguard Worker */ 57*c8dee2aaSAndroid Build Coastguard Worker SkJSONWriter(SkWStream* stream, Mode mode = Mode::kFast) fBlock(new char[kBlockSize])58*c8dee2aaSAndroid Build Coastguard Worker : fBlock(new char[kBlockSize]) 59*c8dee2aaSAndroid Build Coastguard Worker , fWrite(fBlock) 60*c8dee2aaSAndroid Build Coastguard Worker , fBlockEnd(fBlock + kBlockSize) 61*c8dee2aaSAndroid Build Coastguard Worker , fStream(stream) 62*c8dee2aaSAndroid Build Coastguard Worker , fMode(mode) 63*c8dee2aaSAndroid Build Coastguard Worker , fState(State::kStart) { 64*c8dee2aaSAndroid Build Coastguard Worker fScopeStack.push_back(Scope::kNone); 65*c8dee2aaSAndroid Build Coastguard Worker fNewlineStack.push_back(true); 66*c8dee2aaSAndroid Build Coastguard Worker } 67*c8dee2aaSAndroid Build Coastguard Worker ~SkJSONWriter()68*c8dee2aaSAndroid Build Coastguard Worker ~SkJSONWriter() { 69*c8dee2aaSAndroid Build Coastguard Worker this->flush(); 70*c8dee2aaSAndroid Build Coastguard Worker delete[] fBlock; 71*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fScopeStack.size() == 1); 72*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fNewlineStack.size() == 1); 73*c8dee2aaSAndroid Build Coastguard Worker } 74*c8dee2aaSAndroid Build Coastguard Worker 75*c8dee2aaSAndroid Build Coastguard Worker /** 76*c8dee2aaSAndroid Build Coastguard Worker * Force all buffered output to be flushed to the underlying stream. 77*c8dee2aaSAndroid Build Coastguard Worker */ flush()78*c8dee2aaSAndroid Build Coastguard Worker void flush() { 79*c8dee2aaSAndroid Build Coastguard Worker if (fWrite != fBlock) { 80*c8dee2aaSAndroid Build Coastguard Worker fStream->write(fBlock, fWrite - fBlock); 81*c8dee2aaSAndroid Build Coastguard Worker fWrite = fBlock; 82*c8dee2aaSAndroid Build Coastguard Worker } 83*c8dee2aaSAndroid Build Coastguard Worker } 84*c8dee2aaSAndroid Build Coastguard Worker 85*c8dee2aaSAndroid Build Coastguard Worker /** 86*c8dee2aaSAndroid Build Coastguard Worker * Append the name (key) portion of an object member. Must be called between beginObject() and 87*c8dee2aaSAndroid Build Coastguard Worker * endObject(). If you have both the name and value of an object member, you can simply call 88*c8dee2aaSAndroid Build Coastguard Worker * the two argument versions of the other append functions. 89*c8dee2aaSAndroid Build Coastguard Worker */ appendName(const char * name)90*c8dee2aaSAndroid Build Coastguard Worker void appendName(const char* name) { 91*c8dee2aaSAndroid Build Coastguard Worker if (!name) { 92*c8dee2aaSAndroid Build Coastguard Worker return; 93*c8dee2aaSAndroid Build Coastguard Worker } 94*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(Scope::kObject == this->scope()); 95*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(State::kObjectBegin == fState || State::kObjectValue == fState); 96*c8dee2aaSAndroid Build Coastguard Worker if (State::kObjectValue == fState) { 97*c8dee2aaSAndroid Build Coastguard Worker this->write(",", 1); 98*c8dee2aaSAndroid Build Coastguard Worker } 99*c8dee2aaSAndroid Build Coastguard Worker this->separator(this->multiline()); 100*c8dee2aaSAndroid Build Coastguard Worker this->write("\"", 1); 101*c8dee2aaSAndroid Build Coastguard Worker this->write(name, strlen(name)); 102*c8dee2aaSAndroid Build Coastguard Worker this->write("\":", 2); 103*c8dee2aaSAndroid Build Coastguard Worker fState = State::kObjectName; 104*c8dee2aaSAndroid Build Coastguard Worker } 105*c8dee2aaSAndroid Build Coastguard Worker 106*c8dee2aaSAndroid Build Coastguard Worker /** 107*c8dee2aaSAndroid Build Coastguard Worker * Adds a new object. A name must be supplied when called between beginObject() and 108*c8dee2aaSAndroid Build Coastguard Worker * endObject(). Calls to beginObject() must be balanced by corresponding calls to endObject(). 109*c8dee2aaSAndroid Build Coastguard Worker * By default, objects are written out with one named value per line (when in kPretty mode). 110*c8dee2aaSAndroid Build Coastguard Worker * This can be overridden for a particular object by passing false for multiline, this will 111*c8dee2aaSAndroid Build Coastguard Worker * keep the entire object on a single line. This can help with readability in some situations. 112*c8dee2aaSAndroid Build Coastguard Worker * In kFast mode, this parameter is ignored. 113*c8dee2aaSAndroid Build Coastguard Worker */ 114*c8dee2aaSAndroid Build Coastguard Worker void beginObject(const char* name = nullptr, bool multiline = true) { 115*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 116*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(true); 117*c8dee2aaSAndroid Build Coastguard Worker this->write("{", 1); 118*c8dee2aaSAndroid Build Coastguard Worker fScopeStack.push_back(Scope::kObject); 119*c8dee2aaSAndroid Build Coastguard Worker fNewlineStack.push_back(multiline); 120*c8dee2aaSAndroid Build Coastguard Worker fState = State::kObjectBegin; 121*c8dee2aaSAndroid Build Coastguard Worker } 122*c8dee2aaSAndroid Build Coastguard Worker 123*c8dee2aaSAndroid Build Coastguard Worker /** 124*c8dee2aaSAndroid Build Coastguard Worker * Ends an object that was previously started with beginObject(). 125*c8dee2aaSAndroid Build Coastguard Worker */ endObject()126*c8dee2aaSAndroid Build Coastguard Worker void endObject() { 127*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(Scope::kObject == this->scope()); 128*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(State::kObjectBegin == fState || State::kObjectValue == fState); 129*c8dee2aaSAndroid Build Coastguard Worker bool emptyObject = State::kObjectBegin == fState; 130*c8dee2aaSAndroid Build Coastguard Worker bool wasMultiline = this->multiline(); 131*c8dee2aaSAndroid Build Coastguard Worker this->popScope(); 132*c8dee2aaSAndroid Build Coastguard Worker if (!emptyObject) { 133*c8dee2aaSAndroid Build Coastguard Worker this->separator(wasMultiline); 134*c8dee2aaSAndroid Build Coastguard Worker } 135*c8dee2aaSAndroid Build Coastguard Worker this->write("}", 1); 136*c8dee2aaSAndroid Build Coastguard Worker } 137*c8dee2aaSAndroid Build Coastguard Worker 138*c8dee2aaSAndroid Build Coastguard Worker /** 139*c8dee2aaSAndroid Build Coastguard Worker * Adds a new array. A name must be supplied when called between beginObject() and 140*c8dee2aaSAndroid Build Coastguard Worker * endObject(). Calls to beginArray() must be balanced by corresponding calls to endArray(). 141*c8dee2aaSAndroid Build Coastguard Worker * By default, arrays are written out with one value per line (when in kPretty mode). 142*c8dee2aaSAndroid Build Coastguard Worker * This can be overridden for a particular array by passing false for multiline, this will 143*c8dee2aaSAndroid Build Coastguard Worker * keep the entire array on a single line. This can help with readability in some situations. 144*c8dee2aaSAndroid Build Coastguard Worker * In kFast mode, this parameter is ignored. 145*c8dee2aaSAndroid Build Coastguard Worker */ 146*c8dee2aaSAndroid Build Coastguard Worker void beginArray(const char* name = nullptr, bool multiline = true) { 147*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 148*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(true); 149*c8dee2aaSAndroid Build Coastguard Worker this->write("[", 1); 150*c8dee2aaSAndroid Build Coastguard Worker fScopeStack.push_back(Scope::kArray); 151*c8dee2aaSAndroid Build Coastguard Worker fNewlineStack.push_back(multiline); 152*c8dee2aaSAndroid Build Coastguard Worker fState = State::kArrayBegin; 153*c8dee2aaSAndroid Build Coastguard Worker } 154*c8dee2aaSAndroid Build Coastguard Worker 155*c8dee2aaSAndroid Build Coastguard Worker /** 156*c8dee2aaSAndroid Build Coastguard Worker * Ends an array that was previous started with beginArray(). 157*c8dee2aaSAndroid Build Coastguard Worker */ endArray()158*c8dee2aaSAndroid Build Coastguard Worker void endArray() { 159*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(Scope::kArray == this->scope()); 160*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(State::kArrayBegin == fState || State::kArrayValue == fState); 161*c8dee2aaSAndroid Build Coastguard Worker bool emptyArray = State::kArrayBegin == fState; 162*c8dee2aaSAndroid Build Coastguard Worker bool wasMultiline = this->multiline(); 163*c8dee2aaSAndroid Build Coastguard Worker this->popScope(); 164*c8dee2aaSAndroid Build Coastguard Worker if (!emptyArray) { 165*c8dee2aaSAndroid Build Coastguard Worker this->separator(wasMultiline); 166*c8dee2aaSAndroid Build Coastguard Worker } 167*c8dee2aaSAndroid Build Coastguard Worker this->write("]", 1); 168*c8dee2aaSAndroid Build Coastguard Worker } 169*c8dee2aaSAndroid Build Coastguard Worker 170*c8dee2aaSAndroid Build Coastguard Worker /** 171*c8dee2aaSAndroid Build Coastguard Worker * Functions for adding values of various types. The single argument versions add un-named 172*c8dee2aaSAndroid Build Coastguard Worker * values, so must be called either 173*c8dee2aaSAndroid Build Coastguard Worker * - Between beginArray() and endArray() -or- 174*c8dee2aaSAndroid Build Coastguard Worker * - Between beginObject() and endObject(), after calling appendName() 175*c8dee2aaSAndroid Build Coastguard Worker */ appendString(const char * value,size_t size)176*c8dee2aaSAndroid Build Coastguard Worker void appendString(const char* value, size_t size) { 177*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(); 178*c8dee2aaSAndroid Build Coastguard Worker this->write("\"", 1); 179*c8dee2aaSAndroid Build Coastguard Worker if (value) { 180*c8dee2aaSAndroid Build Coastguard Worker char const * const end = value + size; 181*c8dee2aaSAndroid Build Coastguard Worker while (value < end) { 182*c8dee2aaSAndroid Build Coastguard Worker char const * next = value; 183*c8dee2aaSAndroid Build Coastguard Worker SkUnichar u = SkUTF::NextUTF8(&next, end); 184*c8dee2aaSAndroid Build Coastguard Worker switch (u) { 185*c8dee2aaSAndroid Build Coastguard Worker case '"': this->write("\\\"", 2); break; 186*c8dee2aaSAndroid Build Coastguard Worker case '\\': this->write("\\\\", 2); break; 187*c8dee2aaSAndroid Build Coastguard Worker case '\b': this->write("\\b", 2); break; 188*c8dee2aaSAndroid Build Coastguard Worker case '\f': this->write("\\f", 2); break; 189*c8dee2aaSAndroid Build Coastguard Worker case '\n': this->write("\\n", 2); break; 190*c8dee2aaSAndroid Build Coastguard Worker case '\r': this->write("\\r", 2); break; 191*c8dee2aaSAndroid Build Coastguard Worker case '\t': this->write("\\t", 2); break; 192*c8dee2aaSAndroid Build Coastguard Worker default: { 193*c8dee2aaSAndroid Build Coastguard Worker if (u < 0) { 194*c8dee2aaSAndroid Build Coastguard Worker next = value + 1; 195*c8dee2aaSAndroid Build Coastguard Worker SkString s("\\u"); 196*c8dee2aaSAndroid Build Coastguard Worker s.appendHex((unsigned char)*value, 4); 197*c8dee2aaSAndroid Build Coastguard Worker this->write(s.c_str(), s.size()); 198*c8dee2aaSAndroid Build Coastguard Worker } else if (u < 0x20) { 199*c8dee2aaSAndroid Build Coastguard Worker SkString s("\\u"); 200*c8dee2aaSAndroid Build Coastguard Worker s.appendHex(u, 4); 201*c8dee2aaSAndroid Build Coastguard Worker this->write(s.c_str(), s.size()); 202*c8dee2aaSAndroid Build Coastguard Worker } else { 203*c8dee2aaSAndroid Build Coastguard Worker this->write(value, next - value); 204*c8dee2aaSAndroid Build Coastguard Worker } 205*c8dee2aaSAndroid Build Coastguard Worker } break; 206*c8dee2aaSAndroid Build Coastguard Worker } 207*c8dee2aaSAndroid Build Coastguard Worker value = next; 208*c8dee2aaSAndroid Build Coastguard Worker } 209*c8dee2aaSAndroid Build Coastguard Worker } 210*c8dee2aaSAndroid Build Coastguard Worker this->write("\"", 1); 211*c8dee2aaSAndroid Build Coastguard Worker } appendString(const SkString & value)212*c8dee2aaSAndroid Build Coastguard Worker void appendString(const SkString& value) { 213*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value.c_str(), value.size()); 214*c8dee2aaSAndroid Build Coastguard Worker } 215*c8dee2aaSAndroid Build Coastguard Worker // Avoid the non-explicit converting constructor from char* 216*c8dee2aaSAndroid Build Coastguard Worker template <class T, std::enable_if_t<std::is_same_v<T,std::string>,bool> = false> appendString(const T & value)217*c8dee2aaSAndroid Build Coastguard Worker void appendString(const T& value) { 218*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value.data(), value.size()); 219*c8dee2aaSAndroid Build Coastguard Worker } appendNString(char const (& value)[N])220*c8dee2aaSAndroid Build Coastguard Worker template <size_t N> inline void appendNString(char const (&value)[N]) { 221*c8dee2aaSAndroid Build Coastguard Worker static_assert(N > 0); 222*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value, N-1); 223*c8dee2aaSAndroid Build Coastguard Worker } appendCString(const char * value)224*c8dee2aaSAndroid Build Coastguard Worker void appendCString(const char* value) { 225*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value, value ? strlen(value) : 0); 226*c8dee2aaSAndroid Build Coastguard Worker } 227*c8dee2aaSAndroid Build Coastguard Worker appendPointer(const void * value)228*c8dee2aaSAndroid Build Coastguard Worker void appendPointer(const void* value) { this->beginValue(); this->appendf("\"%p\"", value); } appendBool(bool value)229*c8dee2aaSAndroid Build Coastguard Worker void appendBool(bool value) { 230*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(); 231*c8dee2aaSAndroid Build Coastguard Worker if (value) { 232*c8dee2aaSAndroid Build Coastguard Worker this->write("true", 4); 233*c8dee2aaSAndroid Build Coastguard Worker } else { 234*c8dee2aaSAndroid Build Coastguard Worker this->write("false", 5); 235*c8dee2aaSAndroid Build Coastguard Worker } 236*c8dee2aaSAndroid Build Coastguard Worker } appendS32(int32_t value)237*c8dee2aaSAndroid Build Coastguard Worker void appendS32(int32_t value) { this->beginValue(); this->appendf("%d", value); } 238*c8dee2aaSAndroid Build Coastguard Worker void appendS64(int64_t value); appendU32(uint32_t value)239*c8dee2aaSAndroid Build Coastguard Worker void appendU32(uint32_t value) { this->beginValue(); this->appendf("%u", value); } 240*c8dee2aaSAndroid Build Coastguard Worker void appendU64(uint64_t value); appendFloat(float value)241*c8dee2aaSAndroid Build Coastguard Worker void appendFloat(float value) { this->beginValue(); this->appendf("%g", value); } appendDouble(double value)242*c8dee2aaSAndroid Build Coastguard Worker void appendDouble(double value) { this->beginValue(); this->appendf("%g", value); } appendFloatDigits(float value,int digits)243*c8dee2aaSAndroid Build Coastguard Worker void appendFloatDigits(float value, int digits) { 244*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(); 245*c8dee2aaSAndroid Build Coastguard Worker this->appendf("%.*g", digits, value); 246*c8dee2aaSAndroid Build Coastguard Worker } appendDoubleDigits(double value,int digits)247*c8dee2aaSAndroid Build Coastguard Worker void appendDoubleDigits(double value, int digits) { 248*c8dee2aaSAndroid Build Coastguard Worker this->beginValue(); 249*c8dee2aaSAndroid Build Coastguard Worker this->appendf("%.*g", digits, value); 250*c8dee2aaSAndroid Build Coastguard Worker } appendHexU32(uint32_t value)251*c8dee2aaSAndroid Build Coastguard Worker void appendHexU32(uint32_t value) { this->beginValue(); this->appendf("\"0x%x\"", value); } 252*c8dee2aaSAndroid Build Coastguard Worker void appendHexU64(uint64_t value); 253*c8dee2aaSAndroid Build Coastguard Worker appendString(const char * name,const char * value,size_t size)254*c8dee2aaSAndroid Build Coastguard Worker void appendString(const char* name, const char* value, size_t size) { 255*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 256*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value, size); 257*c8dee2aaSAndroid Build Coastguard Worker } appendString(const char * name,const SkString & value)258*c8dee2aaSAndroid Build Coastguard Worker void appendString(const char* name, const SkString& value) { 259*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 260*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value.c_str(), value.size()); 261*c8dee2aaSAndroid Build Coastguard Worker } 262*c8dee2aaSAndroid Build Coastguard Worker // Avoid the non-explicit converting constructor from char* 263*c8dee2aaSAndroid Build Coastguard Worker template <class T, std::enable_if_t<std::is_same_v<T,std::string>,bool> = false> appendString(const char * name,const T & value)264*c8dee2aaSAndroid Build Coastguard Worker void appendString(const char* name, const T& value) { 265*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 266*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value.data(), value.size()); 267*c8dee2aaSAndroid Build Coastguard Worker } appendNString(const char * name,char const (& value)[N])268*c8dee2aaSAndroid Build Coastguard Worker template <size_t N> inline void appendNString(const char* name, char const (&value)[N]) { 269*c8dee2aaSAndroid Build Coastguard Worker static_assert(N > 0); 270*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 271*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value, N-1); 272*c8dee2aaSAndroid Build Coastguard Worker } appendCString(const char * name,const char * value)273*c8dee2aaSAndroid Build Coastguard Worker void appendCString(const char* name, const char* value) { 274*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 275*c8dee2aaSAndroid Build Coastguard Worker this->appendString(value, value ? strlen(value) : 0); 276*c8dee2aaSAndroid Build Coastguard Worker } 277*c8dee2aaSAndroid Build Coastguard Worker #define DEFINE_NAMED_APPEND(function, type) \ 278*c8dee2aaSAndroid Build Coastguard Worker void function(const char* name, type value) { this->appendName(name); this->function(value); } 279*c8dee2aaSAndroid Build Coastguard Worker 280*c8dee2aaSAndroid Build Coastguard Worker /** 281*c8dee2aaSAndroid Build Coastguard Worker * Functions for adding named values of various types. These add a name field, so must be 282*c8dee2aaSAndroid Build Coastguard Worker * called between beginObject() and endObject(). 283*c8dee2aaSAndroid Build Coastguard Worker */ DEFINE_NAMED_APPEND(appendPointer,const void *)284*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendPointer, const void *) 285*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendBool, bool) 286*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendS32, int32_t) 287*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendS64, int64_t) 288*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendU32, uint32_t) 289*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendU64, uint64_t) 290*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendFloat, float) 291*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendDouble, double) 292*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendHexU32, uint32_t) 293*c8dee2aaSAndroid Build Coastguard Worker DEFINE_NAMED_APPEND(appendHexU64, uint64_t) 294*c8dee2aaSAndroid Build Coastguard Worker 295*c8dee2aaSAndroid Build Coastguard Worker #undef DEFINE_NAMED_APPEND 296*c8dee2aaSAndroid Build Coastguard Worker 297*c8dee2aaSAndroid Build Coastguard Worker void appendFloatDigits(const char* name, float value, int digits) { 298*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 299*c8dee2aaSAndroid Build Coastguard Worker this->appendFloatDigits(value, digits); 300*c8dee2aaSAndroid Build Coastguard Worker } appendDoubleDigits(const char * name,double value,int digits)301*c8dee2aaSAndroid Build Coastguard Worker void appendDoubleDigits(const char* name, double value, int digits) { 302*c8dee2aaSAndroid Build Coastguard Worker this->appendName(name); 303*c8dee2aaSAndroid Build Coastguard Worker this->appendDoubleDigits(value, digits); 304*c8dee2aaSAndroid Build Coastguard Worker } 305*c8dee2aaSAndroid Build Coastguard Worker 306*c8dee2aaSAndroid Build Coastguard Worker private: 307*c8dee2aaSAndroid Build Coastguard Worker enum { 308*c8dee2aaSAndroid Build Coastguard Worker // Using a 32k scratch block gives big performance wins, but we diminishing returns going 309*c8dee2aaSAndroid Build Coastguard Worker // any larger. Even with a 1MB block, time to write a large (~300 MB) JSON file only drops 310*c8dee2aaSAndroid Build Coastguard Worker // another ~10%. 311*c8dee2aaSAndroid Build Coastguard Worker kBlockSize = 32 * 1024, 312*c8dee2aaSAndroid Build Coastguard Worker }; 313*c8dee2aaSAndroid Build Coastguard Worker 314*c8dee2aaSAndroid Build Coastguard Worker enum class Scope { 315*c8dee2aaSAndroid Build Coastguard Worker kNone, 316*c8dee2aaSAndroid Build Coastguard Worker kObject, 317*c8dee2aaSAndroid Build Coastguard Worker kArray 318*c8dee2aaSAndroid Build Coastguard Worker }; 319*c8dee2aaSAndroid Build Coastguard Worker 320*c8dee2aaSAndroid Build Coastguard Worker enum class State { 321*c8dee2aaSAndroid Build Coastguard Worker kStart, 322*c8dee2aaSAndroid Build Coastguard Worker kEnd, 323*c8dee2aaSAndroid Build Coastguard Worker kObjectBegin, 324*c8dee2aaSAndroid Build Coastguard Worker kObjectName, 325*c8dee2aaSAndroid Build Coastguard Worker kObjectValue, 326*c8dee2aaSAndroid Build Coastguard Worker kArrayBegin, 327*c8dee2aaSAndroid Build Coastguard Worker kArrayValue, 328*c8dee2aaSAndroid Build Coastguard Worker }; 329*c8dee2aaSAndroid Build Coastguard Worker 330*c8dee2aaSAndroid Build Coastguard Worker void appendf(const char* fmt, ...) SK_PRINTF_LIKE(2, 3); 331*c8dee2aaSAndroid Build Coastguard Worker 332*c8dee2aaSAndroid Build Coastguard Worker void beginValue(bool structure = false) { 333*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(State::kObjectName == fState || 334*c8dee2aaSAndroid Build Coastguard Worker State::kArrayBegin == fState || 335*c8dee2aaSAndroid Build Coastguard Worker State::kArrayValue == fState || 336*c8dee2aaSAndroid Build Coastguard Worker (structure && State::kStart == fState)); 337*c8dee2aaSAndroid Build Coastguard Worker if (State::kArrayValue == fState) { 338*c8dee2aaSAndroid Build Coastguard Worker this->write(",", 1); 339*c8dee2aaSAndroid Build Coastguard Worker } 340*c8dee2aaSAndroid Build Coastguard Worker if (Scope::kArray == this->scope()) { 341*c8dee2aaSAndroid Build Coastguard Worker this->separator(this->multiline()); 342*c8dee2aaSAndroid Build Coastguard Worker } else if (Scope::kObject == this->scope() && Mode::kPretty == fMode) { 343*c8dee2aaSAndroid Build Coastguard Worker this->write(" ", 1); 344*c8dee2aaSAndroid Build Coastguard Worker } 345*c8dee2aaSAndroid Build Coastguard Worker // We haven't added the value yet, but all (non-structure) callers emit something 346*c8dee2aaSAndroid Build Coastguard Worker // immediately, so transition state, to simplify the calling code. 347*c8dee2aaSAndroid Build Coastguard Worker if (!structure) { 348*c8dee2aaSAndroid Build Coastguard Worker fState = Scope::kArray == this->scope() ? State::kArrayValue : State::kObjectValue; 349*c8dee2aaSAndroid Build Coastguard Worker } 350*c8dee2aaSAndroid Build Coastguard Worker } 351*c8dee2aaSAndroid Build Coastguard Worker separator(bool multiline)352*c8dee2aaSAndroid Build Coastguard Worker void separator(bool multiline) { 353*c8dee2aaSAndroid Build Coastguard Worker if (Mode::kPretty == fMode) { 354*c8dee2aaSAndroid Build Coastguard Worker if (multiline) { 355*c8dee2aaSAndroid Build Coastguard Worker this->write("\n", 1); 356*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fScopeStack.size() - 1; ++i) { 357*c8dee2aaSAndroid Build Coastguard Worker this->write(" ", 3); 358*c8dee2aaSAndroid Build Coastguard Worker } 359*c8dee2aaSAndroid Build Coastguard Worker } else { 360*c8dee2aaSAndroid Build Coastguard Worker this->write(" ", 1); 361*c8dee2aaSAndroid Build Coastguard Worker } 362*c8dee2aaSAndroid Build Coastguard Worker } 363*c8dee2aaSAndroid Build Coastguard Worker } 364*c8dee2aaSAndroid Build Coastguard Worker write(const char * buf,size_t length)365*c8dee2aaSAndroid Build Coastguard Worker void write(const char* buf, size_t length) { 366*c8dee2aaSAndroid Build Coastguard Worker if (static_cast<size_t>(fBlockEnd - fWrite) < length) { 367*c8dee2aaSAndroid Build Coastguard Worker // Don't worry about splitting writes that overflow our block. 368*c8dee2aaSAndroid Build Coastguard Worker this->flush(); 369*c8dee2aaSAndroid Build Coastguard Worker } 370*c8dee2aaSAndroid Build Coastguard Worker if (length > kBlockSize) { 371*c8dee2aaSAndroid Build Coastguard Worker // Send particularly large writes straight through to the stream (unbuffered). 372*c8dee2aaSAndroid Build Coastguard Worker fStream->write(buf, length); 373*c8dee2aaSAndroid Build Coastguard Worker } else { 374*c8dee2aaSAndroid Build Coastguard Worker memcpy(fWrite, buf, length); 375*c8dee2aaSAndroid Build Coastguard Worker fWrite += length; 376*c8dee2aaSAndroid Build Coastguard Worker } 377*c8dee2aaSAndroid Build Coastguard Worker } 378*c8dee2aaSAndroid Build Coastguard Worker scope()379*c8dee2aaSAndroid Build Coastguard Worker Scope scope() const { 380*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fScopeStack.empty()); 381*c8dee2aaSAndroid Build Coastguard Worker return fScopeStack.back(); 382*c8dee2aaSAndroid Build Coastguard Worker } 383*c8dee2aaSAndroid Build Coastguard Worker multiline()384*c8dee2aaSAndroid Build Coastguard Worker bool multiline() const { 385*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fNewlineStack.empty()); 386*c8dee2aaSAndroid Build Coastguard Worker return fNewlineStack.back(); 387*c8dee2aaSAndroid Build Coastguard Worker } 388*c8dee2aaSAndroid Build Coastguard Worker popScope()389*c8dee2aaSAndroid Build Coastguard Worker void popScope() { 390*c8dee2aaSAndroid Build Coastguard Worker fScopeStack.pop_back(); 391*c8dee2aaSAndroid Build Coastguard Worker fNewlineStack.pop_back(); 392*c8dee2aaSAndroid Build Coastguard Worker switch (this->scope()) { 393*c8dee2aaSAndroid Build Coastguard Worker case Scope::kNone: 394*c8dee2aaSAndroid Build Coastguard Worker fState = State::kEnd; 395*c8dee2aaSAndroid Build Coastguard Worker break; 396*c8dee2aaSAndroid Build Coastguard Worker case Scope::kObject: 397*c8dee2aaSAndroid Build Coastguard Worker fState = State::kObjectValue; 398*c8dee2aaSAndroid Build Coastguard Worker break; 399*c8dee2aaSAndroid Build Coastguard Worker case Scope::kArray: 400*c8dee2aaSAndroid Build Coastguard Worker fState = State::kArrayValue; 401*c8dee2aaSAndroid Build Coastguard Worker break; 402*c8dee2aaSAndroid Build Coastguard Worker default: 403*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAIL("Invalid scope"); 404*c8dee2aaSAndroid Build Coastguard Worker break; 405*c8dee2aaSAndroid Build Coastguard Worker } 406*c8dee2aaSAndroid Build Coastguard Worker } 407*c8dee2aaSAndroid Build Coastguard Worker 408*c8dee2aaSAndroid Build Coastguard Worker char* fBlock; 409*c8dee2aaSAndroid Build Coastguard Worker char* fWrite; 410*c8dee2aaSAndroid Build Coastguard Worker char* fBlockEnd; 411*c8dee2aaSAndroid Build Coastguard Worker 412*c8dee2aaSAndroid Build Coastguard Worker SkWStream* fStream; 413*c8dee2aaSAndroid Build Coastguard Worker Mode fMode; 414*c8dee2aaSAndroid Build Coastguard Worker State fState; 415*c8dee2aaSAndroid Build Coastguard Worker skia_private::STArray<16, Scope, true> fScopeStack; 416*c8dee2aaSAndroid Build Coastguard Worker skia_private::STArray<16, bool, true> fNewlineStack; 417*c8dee2aaSAndroid Build Coastguard Worker }; 418*c8dee2aaSAndroid Build Coastguard Worker 419*c8dee2aaSAndroid Build Coastguard Worker #endif 420