Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 */
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 }
212 void appendString(const SkString& value) {
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)
#define DEFINE_NAMED_APPEND(function, type)
int32_t SkUnichar
Definition SkTypes.h:175
#define N
Definition beziers.cpp:19
void appendS32(int32_t value)
void appendNString(char const (&value)[N])
SkJSONWriter(SkWStream *stream, Mode mode=Mode::kFast)
void appendFloatDigits(float value, int digits)
void appendString(const T &value)
void beginArray(const char *name=nullptr, bool multiline=true)
void appendDouble(double value)
void appendNString(const char *name, char const (&value)[N])
void appendString(const char *name, const SkString &value)
void appendU32(uint32_t value)
void appendDoubleDigits(const char *name, double value, int digits)
void beginObject(const char *name=nullptr, bool multiline=true)
void appendU64(uint64_t value)
void appendFloat(float value)
void appendPointer(const void *value)
void appendS64(int64_t value)
void appendCString(const char *name, const char *value)
void appendBool(bool value)
void appendName(const char *name)
void appendCString(const char *value)
void appendString(const SkString &value)
void appendString(const char *name, const T &value)
void appendHexU64(uint64_t value)
void appendString(const char *value, size_t size)
void appendDoubleDigits(double value, int digits)
void appendHexU32(uint32_t value)
void appendFloatDigits(const char *name, float value, int digits)
void appendString(const char *name, const char *value, size_t size)
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:194
int size() const
Definition SkTArray.h:416
struct MyStruct s
glong glong end
uint8_t value
const char * name
Definition fuchsia.cc:50
size_t length
SK_SPI SkUnichar NextUTF8(const char **ptr, const char *end)
Definition SkUTF.cpp:118
@ kNone
Definition layer.h:52
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
#define T