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 11 #include "include/core/SkStream.h" 12 #include "include/core/SkString.h" 13 #include "include/core/SkTypes.h" 14 #include "include/private/base/SkNoncopyable.h" 15 #include "include/private/base/SkTArray.h" 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 */ 38 class SkJSONWriter : SkNoncopyable { 39 public: 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 */ 57 SkJSONWriter(SkWStream* stream, Mode mode = Mode::kFast) fBlock(new char[kBlockSize])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 ~SkJSONWriter()68 ~SkJSONWriter() { 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 */ flush()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 */ appendName(const char * name)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 */ endObject()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 */ endArray()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 */ appendString(const char * value,size_t size)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; 183 SkUnichar u = SkUTF::NextUTF8(&next, end); 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 } appendString(const SkString & value)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> appendString(const T & value)217 void appendString(const T& value) { 218 this->appendString(value.data(), value.size()); 219 } appendNString(char const (& value)[N])220 template <size_t N> inline void appendNString(char const (&value)[N]) { 221 static_assert(N > 0); 222 this->appendString(value, N-1); 223 } appendCString(const char * value)224 void appendCString(const char* value) { 225 this->appendString(value, value ? strlen(value) : 0); 226 } 227 appendPointer(const void * value)228 void appendPointer(const void* value) { this->beginValue(); this->appendf("\"%p\"", value); } appendBool(bool 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 } appendS32(int32_t value)237 void appendS32(int32_t value) { this->beginValue(); this->appendf("%d", value); } 238 void appendS64(int64_t value); appendU32(uint32_t value)239 void appendU32(uint32_t value) { this->beginValue(); this->appendf("%u", value); } 240 void appendU64(uint64_t value); appendFloat(float value)241 void appendFloat(float value) { this->beginValue(); this->appendf("%g", value); } appendDouble(double value)242 void appendDouble(double value) { this->beginValue(); this->appendf("%g", value); } appendFloatDigits(float value,int digits)243 void appendFloatDigits(float value, int digits) { 244 this->beginValue(); 245 this->appendf("%.*g", digits, value); 246 } appendDoubleDigits(double value,int digits)247 void appendDoubleDigits(double value, int digits) { 248 this->beginValue(); 249 this->appendf("%.*g", digits, value); 250 } appendHexU32(uint32_t value)251 void appendHexU32(uint32_t value) { this->beginValue(); this->appendf("\"0x%x\"", value); } 252 void appendHexU64(uint64_t value); 253 appendString(const char * name,const char * value,size_t size)254 void appendString(const char* name, const char* value, size_t size) { 255 this->appendName(name); 256 this->appendString(value, size); 257 } appendString(const char * name,const SkString & value)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> appendString(const char * name,const T & value)264 void appendString(const char* name, const T& value) { 265 this->appendName(name); 266 this->appendString(value.data(), value.size()); 267 } appendNString(const char * name,char const (& value)[N])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 } appendCString(const char * name,const char * value)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 */ DEFINE_NAMED_APPEND(appendPointer,const void *)284 DEFINE_NAMED_APPEND(appendPointer, const void *) 285 DEFINE_NAMED_APPEND(appendBool, bool) 286 DEFINE_NAMED_APPEND(appendS32, int32_t) 287 DEFINE_NAMED_APPEND(appendS64, int64_t) 288 DEFINE_NAMED_APPEND(appendU32, uint32_t) 289 DEFINE_NAMED_APPEND(appendU64, uint64_t) 290 DEFINE_NAMED_APPEND(appendFloat, float) 291 DEFINE_NAMED_APPEND(appendDouble, double) 292 DEFINE_NAMED_APPEND(appendHexU32, uint32_t) 293 DEFINE_NAMED_APPEND(appendHexU64, uint64_t) 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 } appendDoubleDigits(const char * name,double value,int digits)301 void appendDoubleDigits(const char* name, double value, int digits) { 302 this->appendName(name); 303 this->appendDoubleDigits(value, digits); 304 } 305 306 private: 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 separator(bool multiline)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 write(const char * buf,size_t length)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 scope()379 Scope scope() const { 380 SkASSERT(!fScopeStack.empty()); 381 return fScopeStack.back(); 382 } 383 multiline()384 bool multiline() const { 385 SkASSERT(!fNewlineStack.empty()); 386 return fNewlineStack.back(); 387 } 388 popScope()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; 415 SkSTArray<16, Scope, true> fScopeStack; 416 SkSTArray<16, bool, true> fNewlineStack; 417 }; 418 419 #endif 420