xref: /aosp_15_r20/external/skia/src/utils/SkJSONWriter.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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