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