• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include "json_tool.h"
8 #include <json/writer.h>
9 #endif // if !defined(JSON_IS_AMALGAMATION)
10 #include <cassert>
11 #include <cstring>
12 #include <iomanip>
13 #include <memory>
14 #include <set>
15 #include <sstream>
16 #include <utility>
17 
18 #if __cplusplus >= 201103L
19 #include <cmath>
20 #include <cstdio>
21 
22 #if !defined(isnan)
23 #define isnan std::isnan
24 #endif
25 
26 #if !defined(isfinite)
27 #define isfinite std::isfinite
28 #endif
29 
30 #else
31 #include <cmath>
32 #include <cstdio>
33 
34 #if defined(_MSC_VER)
35 #if !defined(isnan)
36 #include <float.h>
37 #define isnan _isnan
38 #endif
39 
40 #if !defined(isfinite)
41 #include <float.h>
42 #define isfinite _finite
43 #endif
44 
45 #if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
46 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
47 #endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
48 
49 #endif //_MSC_VER
50 
51 #if defined(__sun) && defined(__SVR4) // Solaris
52 #if !defined(isfinite)
53 #include <ieeefp.h>
54 #define isfinite finite
55 #endif
56 #endif
57 
58 #if defined(__hpux)
59 #if !defined(isfinite)
60 #if defined(__ia64) && !defined(finite)
61 #define isfinite(x)                                                            \
62   ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
63 #endif
64 #endif
65 #endif
66 
67 #if !defined(isnan)
68 // IEEE standard states that NaN values will not compare to themselves
69 #define isnan(x) (x != x)
70 #endif
71 
72 #if !defined(__APPLE__)
73 #if !defined(isfinite)
74 #define isfinite finite
75 #endif
76 #endif
77 #endif
78 
79 #if defined(_MSC_VER)
80 // Disable warning about strdup being deprecated.
81 #pragma warning(disable : 4996)
82 #endif
83 
84 namespace Json {
85 
86 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
87 typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
88 #else
89 typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
90 #endif
91 
valueToString(LargestInt value)92 String valueToString(LargestInt value) {
93   UIntToStringBuffer buffer;
94   char* current = buffer + sizeof(buffer);
95   if (value == Value::minLargestInt) {
96     uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
97     *--current = '-';
98   } else if (value < 0) {
99     uintToString(LargestUInt(-value), current);
100     *--current = '-';
101   } else {
102     uintToString(LargestUInt(value), current);
103   }
104   assert(current >= buffer);
105   return current;
106 }
107 
valueToString(LargestUInt value)108 String valueToString(LargestUInt value) {
109   UIntToStringBuffer buffer;
110   char* current = buffer + sizeof(buffer);
111   uintToString(value, current);
112   assert(current >= buffer);
113   return current;
114 }
115 
116 #if defined(JSON_HAS_INT64)
117 
valueToString(Int value)118 String valueToString(Int value) { return valueToString(LargestInt(value)); }
119 
valueToString(UInt value)120 String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
121 
122 #endif // # if defined(JSON_HAS_INT64)
123 
124 namespace {
valueToString(double value,bool useSpecialFloats,unsigned int precision,PrecisionType precisionType)125 String valueToString(double value,
126                      bool useSpecialFloats,
127                      unsigned int precision,
128                      PrecisionType precisionType) {
129   // Print into the buffer. We need not request the alternative representation
130   // that always has a decimal point because JSON doesn't distinguish the
131   // concepts of reals and integers.
132   if (!isfinite(value)) {
133     static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
134                                            {"null", "-1e+9999", "1e+9999"}};
135     return reps[useSpecialFloats ? 0 : 1]
136                [isnan(value) ? 0 : (value < 0) ? 1 : 2];
137   }
138 
139   String buffer(size_t(36), '\0');
140   while (true) {
141     int len = jsoncpp_snprintf(
142         &*buffer.begin(), buffer.size(),
143         (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
144         precision, value);
145     assert(len >= 0);
146     auto wouldPrint = static_cast<size_t>(len);
147     if (wouldPrint >= buffer.size()) {
148       buffer.resize(wouldPrint + 1);
149       continue;
150     }
151     buffer.resize(wouldPrint);
152     break;
153   }
154 
155   buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
156 
157   // strip the zero padding from the right
158   if (precisionType == PrecisionType::decimalPlaces) {
159     buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
160   }
161 
162   // try to ensure we preserve the fact that this was given to us as a double on
163   // input
164   if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
165     buffer += ".0";
166   }
167   return buffer;
168 }
169 } // namespace
170 
valueToString(double value,unsigned int precision,PrecisionType precisionType)171 String valueToString(double value,
172                      unsigned int precision,
173                      PrecisionType precisionType) {
174   return valueToString(value, false, precision, precisionType);
175 }
176 
valueToString(bool value)177 String valueToString(bool value) { return value ? "true" : "false"; }
178 
isAnyCharRequiredQuoting(char const * s,size_t n)179 static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
180   assert(s || !n);
181 
182   char const* const end = s + n;
183   for (char const* cur = s; cur < end; ++cur) {
184     if (*cur == '\\' || *cur == '\"' || *cur < ' ' ||
185         static_cast<unsigned char>(*cur) < 0x80)
186       return true;
187   }
188   return false;
189 }
190 
utf8ToCodepoint(const char * & s,const char * e)191 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
192   const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
193 
194   unsigned int firstByte = static_cast<unsigned char>(*s);
195 
196   if (firstByte < 0x80)
197     return firstByte;
198 
199   if (firstByte < 0xE0) {
200     if (e - s < 2)
201       return REPLACEMENT_CHARACTER;
202 
203     unsigned int calculated =
204         ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
205     s += 1;
206     // oversized encoded characters are invalid
207     return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
208   }
209 
210   if (firstByte < 0xF0) {
211     if (e - s < 3)
212       return REPLACEMENT_CHARACTER;
213 
214     unsigned int calculated = ((firstByte & 0x0F) << 12) |
215                               ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
216                               (static_cast<unsigned int>(s[2]) & 0x3F);
217     s += 2;
218     // surrogates aren't valid codepoints itself
219     // shouldn't be UTF-8 encoded
220     if (calculated >= 0xD800 && calculated <= 0xDFFF)
221       return REPLACEMENT_CHARACTER;
222     // oversized encoded characters are invalid
223     return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
224   }
225 
226   if (firstByte < 0xF8) {
227     if (e - s < 4)
228       return REPLACEMENT_CHARACTER;
229 
230     unsigned int calculated = ((firstByte & 0x07) << 18) |
231                               ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
232                               ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
233                               (static_cast<unsigned int>(s[3]) & 0x3F);
234     s += 3;
235     // oversized encoded characters are invalid
236     return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
237   }
238 
239   return REPLACEMENT_CHARACTER;
240 }
241 
242 static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
243                            "101112131415161718191a1b1c1d1e1f"
244                            "202122232425262728292a2b2c2d2e2f"
245                            "303132333435363738393a3b3c3d3e3f"
246                            "404142434445464748494a4b4c4d4e4f"
247                            "505152535455565758595a5b5c5d5e5f"
248                            "606162636465666768696a6b6c6d6e6f"
249                            "707172737475767778797a7b7c7d7e7f"
250                            "808182838485868788898a8b8c8d8e8f"
251                            "909192939495969798999a9b9c9d9e9f"
252                            "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
253                            "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
254                            "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
255                            "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
256                            "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
257                            "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
258 
toHex16Bit(unsigned int x)259 static String toHex16Bit(unsigned int x) {
260   const unsigned int hi = (x >> 8) & 0xff;
261   const unsigned int lo = x & 0xff;
262   String result(4, ' ');
263   result[0] = hex2[2 * hi];
264   result[1] = hex2[2 * hi + 1];
265   result[2] = hex2[2 * lo];
266   result[3] = hex2[2 * lo + 1];
267   return result;
268 }
269 
valueToQuotedStringN(const char * value,unsigned length)270 static String valueToQuotedStringN(const char* value, unsigned length) {
271   if (value == nullptr)
272     return "";
273 
274   if (!isAnyCharRequiredQuoting(value, length))
275     return String("\"") + value + "\"";
276   // We have to walk value and escape any special characters.
277   // Appending to String is not efficient, but this should be rare.
278   // (Note: forward slashes are *not* rare, but I am not escaping them.)
279   String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
280   String result;
281   result.reserve(maxsize); // to avoid lots of mallocs
282   result += "\"";
283   char const* end = value + length;
284   for (const char* c = value; c != end; ++c) {
285     switch (*c) {
286     case '\"':
287       result += "\\\"";
288       break;
289     case '\\':
290       result += "\\\\";
291       break;
292     case '\b':
293       result += "\\b";
294       break;
295     case '\f':
296       result += "\\f";
297       break;
298     case '\n':
299       result += "\\n";
300       break;
301     case '\r':
302       result += "\\r";
303       break;
304     case '\t':
305       result += "\\t";
306       break;
307     // case '/':
308     // Even though \/ is considered a legal escape in JSON, a bare
309     // slash is also legal, so I see no reason to escape it.
310     // (I hope I am not misunderstanding something.)
311     // blep notes: actually escaping \/ may be useful in javascript to avoid </
312     // sequence.
313     // Should add a flag to allow this compatibility mode and prevent this
314     // sequence from occurring.
315     default: {
316       unsigned int cp = utf8ToCodepoint(c, end);
317       // don't escape non-control characters
318       // (short escape sequence are applied above)
319       if (cp < 0x80 && cp >= 0x20)
320         result += static_cast<char>(cp);
321       else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
322         result += "\\u";
323         result += toHex16Bit(cp);
324       } else { // codepoint is not in Basic Multilingual Plane
325                // convert to surrogate pair first
326         cp -= 0x10000;
327         result += "\\u";
328         result += toHex16Bit((cp >> 10) + 0xD800);
329         result += "\\u";
330         result += toHex16Bit((cp & 0x3FF) + 0xDC00);
331       }
332     } break;
333     }
334   }
335   result += "\"";
336   return result;
337 }
338 
valueToQuotedString(const char * value)339 String valueToQuotedString(const char* value) {
340   return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
341 }
342 
343 // Class Writer
344 // //////////////////////////////////////////////////////////////////
345 Writer::~Writer() = default;
346 
347 // Class FastWriter
348 // //////////////////////////////////////////////////////////////////
349 
350 FastWriter::FastWriter()
351 
352     = default;
353 
enableYAMLCompatibility()354 void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
355 
dropNullPlaceholders()356 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
357 
omitEndingLineFeed()358 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
359 
write(const Value & root)360 String FastWriter::write(const Value& root) {
361   document_.clear();
362   writeValue(root);
363   if (!omitEndingLineFeed_)
364     document_ += '\n';
365   return document_;
366 }
367 
writeValue(const Value & value)368 void FastWriter::writeValue(const Value& value) {
369   switch (value.type()) {
370   case nullValue:
371     if (!dropNullPlaceholders_)
372       document_ += "null";
373     break;
374   case intValue:
375     document_ += valueToString(value.asLargestInt());
376     break;
377   case uintValue:
378     document_ += valueToString(value.asLargestUInt());
379     break;
380   case realValue:
381     document_ += valueToString(value.asDouble());
382     break;
383   case stringValue: {
384     // Is NULL possible for value.string_? No.
385     char const* str;
386     char const* end;
387     bool ok = value.getString(&str, &end);
388     if (ok)
389       document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
390     break;
391   }
392   case booleanValue:
393     document_ += valueToString(value.asBool());
394     break;
395   case arrayValue: {
396     document_ += '[';
397     ArrayIndex size = value.size();
398     for (ArrayIndex index = 0; index < size; ++index) {
399       if (index > 0)
400         document_ += ',';
401       writeValue(value[index]);
402     }
403     document_ += ']';
404   } break;
405   case objectValue: {
406     Value::Members members(value.getMemberNames());
407     document_ += '{';
408     for (auto it = members.begin(); it != members.end(); ++it) {
409       const String& name = *it;
410       if (it != members.begin())
411         document_ += ',';
412       document_ += valueToQuotedStringN(name.data(),
413                                         static_cast<unsigned>(name.length()));
414       document_ += yamlCompatibilityEnabled_ ? ": " : ":";
415       writeValue(value[name]);
416     }
417     document_ += '}';
418   } break;
419   }
420 }
421 
422 // Class StyledWriter
423 // //////////////////////////////////////////////////////////////////
424 
425 StyledWriter::StyledWriter() = default;
426 
write(const Value & root)427 String StyledWriter::write(const Value& root) {
428   document_.clear();
429   addChildValues_ = false;
430   indentString_.clear();
431   writeCommentBeforeValue(root);
432   writeValue(root);
433   writeCommentAfterValueOnSameLine(root);
434   document_ += '\n';
435   return document_;
436 }
437 
writeValue(const Value & value)438 void StyledWriter::writeValue(const Value& value) {
439   switch (value.type()) {
440   case nullValue:
441     pushValue("null");
442     break;
443   case intValue:
444     pushValue(valueToString(value.asLargestInt()));
445     break;
446   case uintValue:
447     pushValue(valueToString(value.asLargestUInt()));
448     break;
449   case realValue:
450     pushValue(valueToString(value.asDouble()));
451     break;
452   case stringValue: {
453     // Is NULL possible for value.string_? No.
454     char const* str;
455     char const* end;
456     bool ok = value.getString(&str, &end);
457     if (ok)
458       pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
459     else
460       pushValue("");
461     break;
462   }
463   case booleanValue:
464     pushValue(valueToString(value.asBool()));
465     break;
466   case arrayValue:
467     writeArrayValue(value);
468     break;
469   case objectValue: {
470     Value::Members members(value.getMemberNames());
471     if (members.empty())
472       pushValue("{}");
473     else {
474       writeWithIndent("{");
475       indent();
476       auto it = members.begin();
477       for (;;) {
478         const String& name = *it;
479         const Value& childValue = value[name];
480         writeCommentBeforeValue(childValue);
481         writeWithIndent(valueToQuotedString(name.c_str()));
482         document_ += " : ";
483         writeValue(childValue);
484         if (++it == members.end()) {
485           writeCommentAfterValueOnSameLine(childValue);
486           break;
487         }
488         document_ += ',';
489         writeCommentAfterValueOnSameLine(childValue);
490       }
491       unindent();
492       writeWithIndent("}");
493     }
494   } break;
495   }
496 }
497 
writeArrayValue(const Value & value)498 void StyledWriter::writeArrayValue(const Value& value) {
499   unsigned size = value.size();
500   if (size == 0)
501     pushValue("[]");
502   else {
503     bool isArrayMultiLine = isMultilineArray(value);
504     if (isArrayMultiLine) {
505       writeWithIndent("[");
506       indent();
507       bool hasChildValue = !childValues_.empty();
508       unsigned index = 0;
509       for (;;) {
510         const Value& childValue = value[index];
511         writeCommentBeforeValue(childValue);
512         if (hasChildValue)
513           writeWithIndent(childValues_[index]);
514         else {
515           writeIndent();
516           writeValue(childValue);
517         }
518         if (++index == size) {
519           writeCommentAfterValueOnSameLine(childValue);
520           break;
521         }
522         document_ += ',';
523         writeCommentAfterValueOnSameLine(childValue);
524       }
525       unindent();
526       writeWithIndent("]");
527     } else // output on a single line
528     {
529       assert(childValues_.size() == size);
530       document_ += "[ ";
531       for (unsigned index = 0; index < size; ++index) {
532         if (index > 0)
533           document_ += ", ";
534         document_ += childValues_[index];
535       }
536       document_ += " ]";
537     }
538   }
539 }
540 
isMultilineArray(const Value & value)541 bool StyledWriter::isMultilineArray(const Value& value) {
542   ArrayIndex const size = value.size();
543   bool isMultiLine = size * 3 >= rightMargin_;
544   childValues_.clear();
545   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
546     const Value& childValue = value[index];
547     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
548                    !childValue.empty());
549   }
550   if (!isMultiLine) // check if line length > max line length
551   {
552     childValues_.reserve(size);
553     addChildValues_ = true;
554     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
555     for (ArrayIndex index = 0; index < size; ++index) {
556       if (hasCommentForValue(value[index])) {
557         isMultiLine = true;
558       }
559       writeValue(value[index]);
560       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
561     }
562     addChildValues_ = false;
563     isMultiLine = isMultiLine || lineLength >= rightMargin_;
564   }
565   return isMultiLine;
566 }
567 
pushValue(const String & value)568 void StyledWriter::pushValue(const String& value) {
569   if (addChildValues_)
570     childValues_.push_back(value);
571   else
572     document_ += value;
573 }
574 
writeIndent()575 void StyledWriter::writeIndent() {
576   if (!document_.empty()) {
577     char last = document_[document_.length() - 1];
578     if (last == ' ') // already indented
579       return;
580     if (last != '\n') // Comments may add new-line
581       document_ += '\n';
582   }
583   document_ += indentString_;
584 }
585 
writeWithIndent(const String & value)586 void StyledWriter::writeWithIndent(const String& value) {
587   writeIndent();
588   document_ += value;
589 }
590 
indent()591 void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
592 
unindent()593 void StyledWriter::unindent() {
594   assert(indentString_.size() >= indentSize_);
595   indentString_.resize(indentString_.size() - indentSize_);
596 }
597 
writeCommentBeforeValue(const Value & root)598 void StyledWriter::writeCommentBeforeValue(const Value& root) {
599   if (!root.hasComment(commentBefore))
600     return;
601 
602   document_ += '\n';
603   writeIndent();
604   const String& comment = root.getComment(commentBefore);
605   String::const_iterator iter = comment.begin();
606   while (iter != comment.end()) {
607     document_ += *iter;
608     if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
609       writeIndent();
610     ++iter;
611   }
612 
613   // Comments are stripped of trailing newlines, so add one here
614   document_ += '\n';
615 }
616 
writeCommentAfterValueOnSameLine(const Value & root)617 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
618   if (root.hasComment(commentAfterOnSameLine))
619     document_ += " " + root.getComment(commentAfterOnSameLine);
620 
621   if (root.hasComment(commentAfter)) {
622     document_ += '\n';
623     document_ += root.getComment(commentAfter);
624     document_ += '\n';
625   }
626 }
627 
hasCommentForValue(const Value & value)628 bool StyledWriter::hasCommentForValue(const Value& value) {
629   return value.hasComment(commentBefore) ||
630          value.hasComment(commentAfterOnSameLine) ||
631          value.hasComment(commentAfter);
632 }
633 
634 // Class StyledStreamWriter
635 // //////////////////////////////////////////////////////////////////
636 
StyledStreamWriter(String indentation)637 StyledStreamWriter::StyledStreamWriter(String indentation)
638     : document_(nullptr), indentation_(std::move(indentation)),
639       addChildValues_(), indented_(false) {}
640 
write(OStream & out,const Value & root)641 void StyledStreamWriter::write(OStream& out, const Value& root) {
642   document_ = &out;
643   addChildValues_ = false;
644   indentString_.clear();
645   indented_ = true;
646   writeCommentBeforeValue(root);
647   if (!indented_)
648     writeIndent();
649   indented_ = true;
650   writeValue(root);
651   writeCommentAfterValueOnSameLine(root);
652   *document_ << "\n";
653   document_ = nullptr; // Forget the stream, for safety.
654 }
655 
writeValue(const Value & value)656 void StyledStreamWriter::writeValue(const Value& value) {
657   switch (value.type()) {
658   case nullValue:
659     pushValue("null");
660     break;
661   case intValue:
662     pushValue(valueToString(value.asLargestInt()));
663     break;
664   case uintValue:
665     pushValue(valueToString(value.asLargestUInt()));
666     break;
667   case realValue:
668     pushValue(valueToString(value.asDouble()));
669     break;
670   case stringValue: {
671     // Is NULL possible for value.string_? No.
672     char const* str;
673     char const* end;
674     bool ok = value.getString(&str, &end);
675     if (ok)
676       pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
677     else
678       pushValue("");
679     break;
680   }
681   case booleanValue:
682     pushValue(valueToString(value.asBool()));
683     break;
684   case arrayValue:
685     writeArrayValue(value);
686     break;
687   case objectValue: {
688     Value::Members members(value.getMemberNames());
689     if (members.empty())
690       pushValue("{}");
691     else {
692       writeWithIndent("{");
693       indent();
694       auto it = members.begin();
695       for (;;) {
696         const String& name = *it;
697         const Value& childValue = value[name];
698         writeCommentBeforeValue(childValue);
699         writeWithIndent(valueToQuotedString(name.c_str()));
700         *document_ << " : ";
701         writeValue(childValue);
702         if (++it == members.end()) {
703           writeCommentAfterValueOnSameLine(childValue);
704           break;
705         }
706         *document_ << ",";
707         writeCommentAfterValueOnSameLine(childValue);
708       }
709       unindent();
710       writeWithIndent("}");
711     }
712   } break;
713   }
714 }
715 
writeArrayValue(const Value & value)716 void StyledStreamWriter::writeArrayValue(const Value& value) {
717   unsigned size = value.size();
718   if (size == 0)
719     pushValue("[]");
720   else {
721     bool isArrayMultiLine = isMultilineArray(value);
722     if (isArrayMultiLine) {
723       writeWithIndent("[");
724       indent();
725       bool hasChildValue = !childValues_.empty();
726       unsigned index = 0;
727       for (;;) {
728         const Value& childValue = value[index];
729         writeCommentBeforeValue(childValue);
730         if (hasChildValue)
731           writeWithIndent(childValues_[index]);
732         else {
733           if (!indented_)
734             writeIndent();
735           indented_ = true;
736           writeValue(childValue);
737           indented_ = false;
738         }
739         if (++index == size) {
740           writeCommentAfterValueOnSameLine(childValue);
741           break;
742         }
743         *document_ << ",";
744         writeCommentAfterValueOnSameLine(childValue);
745       }
746       unindent();
747       writeWithIndent("]");
748     } else // output on a single line
749     {
750       assert(childValues_.size() == size);
751       *document_ << "[ ";
752       for (unsigned index = 0; index < size; ++index) {
753         if (index > 0)
754           *document_ << ", ";
755         *document_ << childValues_[index];
756       }
757       *document_ << " ]";
758     }
759   }
760 }
761 
isMultilineArray(const Value & value)762 bool StyledStreamWriter::isMultilineArray(const Value& value) {
763   ArrayIndex const size = value.size();
764   bool isMultiLine = size * 3 >= rightMargin_;
765   childValues_.clear();
766   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
767     const Value& childValue = value[index];
768     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
769                    !childValue.empty());
770   }
771   if (!isMultiLine) // check if line length > max line length
772   {
773     childValues_.reserve(size);
774     addChildValues_ = true;
775     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
776     for (ArrayIndex index = 0; index < size; ++index) {
777       if (hasCommentForValue(value[index])) {
778         isMultiLine = true;
779       }
780       writeValue(value[index]);
781       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
782     }
783     addChildValues_ = false;
784     isMultiLine = isMultiLine || lineLength >= rightMargin_;
785   }
786   return isMultiLine;
787 }
788 
pushValue(const String & value)789 void StyledStreamWriter::pushValue(const String& value) {
790   if (addChildValues_)
791     childValues_.push_back(value);
792   else
793     *document_ << value;
794 }
795 
writeIndent()796 void StyledStreamWriter::writeIndent() {
797   // blep intended this to look at the so-far-written string
798   // to determine whether we are already indented, but
799   // with a stream we cannot do that. So we rely on some saved state.
800   // The caller checks indented_.
801   *document_ << '\n' << indentString_;
802 }
803 
writeWithIndent(const String & value)804 void StyledStreamWriter::writeWithIndent(const String& value) {
805   if (!indented_)
806     writeIndent();
807   *document_ << value;
808   indented_ = false;
809 }
810 
indent()811 void StyledStreamWriter::indent() { indentString_ += indentation_; }
812 
unindent()813 void StyledStreamWriter::unindent() {
814   assert(indentString_.size() >= indentation_.size());
815   indentString_.resize(indentString_.size() - indentation_.size());
816 }
817 
writeCommentBeforeValue(const Value & root)818 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
819   if (!root.hasComment(commentBefore))
820     return;
821 
822   if (!indented_)
823     writeIndent();
824   const String& comment = root.getComment(commentBefore);
825   String::const_iterator iter = comment.begin();
826   while (iter != comment.end()) {
827     *document_ << *iter;
828     if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
829       // writeIndent();  // would include newline
830       *document_ << indentString_;
831     ++iter;
832   }
833   indented_ = false;
834 }
835 
writeCommentAfterValueOnSameLine(const Value & root)836 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
837   if (root.hasComment(commentAfterOnSameLine))
838     *document_ << ' ' << root.getComment(commentAfterOnSameLine);
839 
840   if (root.hasComment(commentAfter)) {
841     writeIndent();
842     *document_ << root.getComment(commentAfter);
843   }
844   indented_ = false;
845 }
846 
hasCommentForValue(const Value & value)847 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
848   return value.hasComment(commentBefore) ||
849          value.hasComment(commentAfterOnSameLine) ||
850          value.hasComment(commentAfter);
851 }
852 
853 //////////////////////////
854 // BuiltStyledStreamWriter
855 
856 /// Scoped enums are not available until C++11.
857 struct CommentStyle {
858   /// Decide whether to write comments.
859   enum Enum {
860     None, ///< Drop all comments.
861     Most, ///< Recover odd behavior of previous versions (not implemented yet).
862     All   ///< Keep all comments.
863   };
864 };
865 
866 struct BuiltStyledStreamWriter : public StreamWriter {
867   BuiltStyledStreamWriter(String indentation,
868                           CommentStyle::Enum cs,
869                           String colonSymbol,
870                           String nullSymbol,
871                           String endingLineFeedSymbol,
872                           bool useSpecialFloats,
873                           unsigned int precision,
874                           PrecisionType precisionType);
875   int write(Value const& root, OStream* sout) override;
876 
877 private:
878   void writeValue(Value const& value);
879   void writeArrayValue(Value const& value);
880   bool isMultilineArray(Value const& value);
881   void pushValue(String const& value);
882   void writeIndent();
883   void writeWithIndent(String const& value);
884   void indent();
885   void unindent();
886   void writeCommentBeforeValue(Value const& root);
887   void writeCommentAfterValueOnSameLine(Value const& root);
888   static bool hasCommentForValue(const Value& value);
889 
890   typedef std::vector<String> ChildValues;
891 
892   ChildValues childValues_;
893   String indentString_;
894   unsigned int rightMargin_;
895   String indentation_;
896   CommentStyle::Enum cs_;
897   String colonSymbol_;
898   String nullSymbol_;
899   String endingLineFeedSymbol_;
900   bool addChildValues_ : 1;
901   bool indented_ : 1;
902   bool useSpecialFloats_ : 1;
903   unsigned int precision_;
904   PrecisionType precisionType_;
905 };
BuiltStyledStreamWriter(String indentation,CommentStyle::Enum cs,String colonSymbol,String nullSymbol,String endingLineFeedSymbol,bool useSpecialFloats,unsigned int precision,PrecisionType precisionType)906 BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation,
907                                                  CommentStyle::Enum cs,
908                                                  String colonSymbol,
909                                                  String nullSymbol,
910                                                  String endingLineFeedSymbol,
911                                                  bool useSpecialFloats,
912                                                  unsigned int precision,
913                                                  PrecisionType precisionType)
914     : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
915       colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
916       endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
917       addChildValues_(false), indented_(false),
918       useSpecialFloats_(useSpecialFloats), precision_(precision),
919       precisionType_(precisionType) {}
write(Value const & root,OStream * sout)920 int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
921   sout_ = sout;
922   addChildValues_ = false;
923   indented_ = true;
924   indentString_.clear();
925   writeCommentBeforeValue(root);
926   if (!indented_)
927     writeIndent();
928   indented_ = true;
929   writeValue(root);
930   writeCommentAfterValueOnSameLine(root);
931   *sout_ << endingLineFeedSymbol_;
932   sout_ = nullptr;
933   return 0;
934 }
writeValue(Value const & value)935 void BuiltStyledStreamWriter::writeValue(Value const& value) {
936   switch (value.type()) {
937   case nullValue:
938     pushValue(nullSymbol_);
939     break;
940   case intValue:
941     pushValue(valueToString(value.asLargestInt()));
942     break;
943   case uintValue:
944     pushValue(valueToString(value.asLargestUInt()));
945     break;
946   case realValue:
947     pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
948                             precisionType_));
949     break;
950   case stringValue: {
951     // Is NULL is possible for value.string_? No.
952     char const* str;
953     char const* end;
954     bool ok = value.getString(&str, &end);
955     if (ok)
956       pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
957     else
958       pushValue("");
959     break;
960   }
961   case booleanValue:
962     pushValue(valueToString(value.asBool()));
963     break;
964   case arrayValue:
965     writeArrayValue(value);
966     break;
967   case objectValue: {
968     Value::Members members(value.getMemberNames());
969     if (members.empty())
970       pushValue("{}");
971     else {
972       writeWithIndent("{");
973       indent();
974       auto it = members.begin();
975       for (;;) {
976         String const& name = *it;
977         Value const& childValue = value[name];
978         writeCommentBeforeValue(childValue);
979         writeWithIndent(valueToQuotedStringN(
980             name.data(), static_cast<unsigned>(name.length())));
981         *sout_ << colonSymbol_;
982         writeValue(childValue);
983         if (++it == members.end()) {
984           writeCommentAfterValueOnSameLine(childValue);
985           break;
986         }
987         *sout_ << ",";
988         writeCommentAfterValueOnSameLine(childValue);
989       }
990       unindent();
991       writeWithIndent("}");
992     }
993   } break;
994   }
995 }
996 
writeArrayValue(Value const & value)997 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
998   unsigned size = value.size();
999   if (size == 0)
1000     pushValue("[]");
1001   else {
1002     bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
1003     if (isMultiLine) {
1004       writeWithIndent("[");
1005       indent();
1006       bool hasChildValue = !childValues_.empty();
1007       unsigned index = 0;
1008       for (;;) {
1009         Value const& childValue = value[index];
1010         writeCommentBeforeValue(childValue);
1011         if (hasChildValue)
1012           writeWithIndent(childValues_[index]);
1013         else {
1014           if (!indented_)
1015             writeIndent();
1016           indented_ = true;
1017           writeValue(childValue);
1018           indented_ = false;
1019         }
1020         if (++index == size) {
1021           writeCommentAfterValueOnSameLine(childValue);
1022           break;
1023         }
1024         *sout_ << ",";
1025         writeCommentAfterValueOnSameLine(childValue);
1026       }
1027       unindent();
1028       writeWithIndent("]");
1029     } else // output on a single line
1030     {
1031       assert(childValues_.size() == size);
1032       *sout_ << "[";
1033       if (!indentation_.empty())
1034         *sout_ << " ";
1035       for (unsigned index = 0; index < size; ++index) {
1036         if (index > 0)
1037           *sout_ << ((!indentation_.empty()) ? ", " : ",");
1038         *sout_ << childValues_[index];
1039       }
1040       if (!indentation_.empty())
1041         *sout_ << " ";
1042       *sout_ << "]";
1043     }
1044   }
1045 }
1046 
isMultilineArray(Value const & value)1047 bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1048   ArrayIndex const size = value.size();
1049   bool isMultiLine = size * 3 >= rightMargin_;
1050   childValues_.clear();
1051   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1052     Value const& childValue = value[index];
1053     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1054                    !childValue.empty());
1055   }
1056   if (!isMultiLine) // check if line length > max line length
1057   {
1058     childValues_.reserve(size);
1059     addChildValues_ = true;
1060     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1061     for (ArrayIndex index = 0; index < size; ++index) {
1062       if (hasCommentForValue(value[index])) {
1063         isMultiLine = true;
1064       }
1065       writeValue(value[index]);
1066       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1067     }
1068     addChildValues_ = false;
1069     isMultiLine = isMultiLine || lineLength >= rightMargin_;
1070   }
1071   return isMultiLine;
1072 }
1073 
pushValue(String const & value)1074 void BuiltStyledStreamWriter::pushValue(String const& value) {
1075   if (addChildValues_)
1076     childValues_.push_back(value);
1077   else
1078     *sout_ << value;
1079 }
1080 
writeIndent()1081 void BuiltStyledStreamWriter::writeIndent() {
1082   // blep intended this to look at the so-far-written string
1083   // to determine whether we are already indented, but
1084   // with a stream we cannot do that. So we rely on some saved state.
1085   // The caller checks indented_.
1086 
1087   if (!indentation_.empty()) {
1088     // In this case, drop newlines too.
1089     *sout_ << '\n' << indentString_;
1090   }
1091 }
1092 
writeWithIndent(String const & value)1093 void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1094   if (!indented_)
1095     writeIndent();
1096   *sout_ << value;
1097   indented_ = false;
1098 }
1099 
indent()1100 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1101 
unindent()1102 void BuiltStyledStreamWriter::unindent() {
1103   assert(indentString_.size() >= indentation_.size());
1104   indentString_.resize(indentString_.size() - indentation_.size());
1105 }
1106 
writeCommentBeforeValue(Value const & root)1107 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1108   if (cs_ == CommentStyle::None)
1109     return;
1110   if (!root.hasComment(commentBefore))
1111     return;
1112 
1113   if (!indented_)
1114     writeIndent();
1115   const String& comment = root.getComment(commentBefore);
1116   String::const_iterator iter = comment.begin();
1117   while (iter != comment.end()) {
1118     *sout_ << *iter;
1119     if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
1120       // writeIndent();  // would write extra newline
1121       *sout_ << indentString_;
1122     ++iter;
1123   }
1124   indented_ = false;
1125 }
1126 
writeCommentAfterValueOnSameLine(Value const & root)1127 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
1128     Value const& root) {
1129   if (cs_ == CommentStyle::None)
1130     return;
1131   if (root.hasComment(commentAfterOnSameLine))
1132     *sout_ << " " + root.getComment(commentAfterOnSameLine);
1133 
1134   if (root.hasComment(commentAfter)) {
1135     writeIndent();
1136     *sout_ << root.getComment(commentAfter);
1137   }
1138 }
1139 
1140 // static
hasCommentForValue(const Value & value)1141 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1142   return value.hasComment(commentBefore) ||
1143          value.hasComment(commentAfterOnSameLine) ||
1144          value.hasComment(commentAfter);
1145 }
1146 
1147 ///////////////
1148 // StreamWriter
1149 
StreamWriter()1150 StreamWriter::StreamWriter() : sout_(nullptr) {}
1151 StreamWriter::~StreamWriter() = default;
1152 StreamWriter::Factory::~Factory() = default;
StreamWriterBuilder()1153 StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
1154 StreamWriterBuilder::~StreamWriterBuilder() = default;
newStreamWriter() const1155 StreamWriter* StreamWriterBuilder::newStreamWriter() const {
1156   String indentation = settings_["indentation"].asString();
1157   String cs_str = settings_["commentStyle"].asString();
1158   String pt_str = settings_["precisionType"].asString();
1159   bool eyc = settings_["enableYAMLCompatibility"].asBool();
1160   bool dnp = settings_["dropNullPlaceholders"].asBool();
1161   bool usf = settings_["useSpecialFloats"].asBool();
1162   unsigned int pre = settings_["precision"].asUInt();
1163   CommentStyle::Enum cs = CommentStyle::All;
1164   if (cs_str == "All") {
1165     cs = CommentStyle::All;
1166   } else if (cs_str == "None") {
1167     cs = CommentStyle::None;
1168   } else {
1169     throwRuntimeError("commentStyle must be 'All' or 'None'");
1170   }
1171   PrecisionType precisionType(significantDigits);
1172   if (pt_str == "significant") {
1173     precisionType = PrecisionType::significantDigits;
1174   } else if (pt_str == "decimal") {
1175     precisionType = PrecisionType::decimalPlaces;
1176   } else {
1177     throwRuntimeError("precisionType must be 'significant' or 'decimal'");
1178   }
1179   String colonSymbol = " : ";
1180   if (eyc) {
1181     colonSymbol = ": ";
1182   } else if (indentation.empty()) {
1183     colonSymbol = ":";
1184   }
1185   String nullSymbol = "null";
1186   if (dnp) {
1187     nullSymbol.clear();
1188   }
1189   if (pre > 17)
1190     pre = 17;
1191   String endingLineFeedSymbol;
1192   return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1193                                      endingLineFeedSymbol, usf, pre,
1194                                      precisionType);
1195 }
getValidWriterKeys(std::set<String> * valid_keys)1196 static void getValidWriterKeys(std::set<String>* valid_keys) {
1197   valid_keys->clear();
1198   valid_keys->insert("indentation");
1199   valid_keys->insert("commentStyle");
1200   valid_keys->insert("enableYAMLCompatibility");
1201   valid_keys->insert("dropNullPlaceholders");
1202   valid_keys->insert("useSpecialFloats");
1203   valid_keys->insert("precision");
1204   valid_keys->insert("precisionType");
1205 }
validate(Json::Value * invalid) const1206 bool StreamWriterBuilder::validate(Json::Value* invalid) const {
1207   Json::Value my_invalid;
1208   if (!invalid)
1209     invalid = &my_invalid; // so we do not need to test for NULL
1210   Json::Value& inv = *invalid;
1211   std::set<String> valid_keys;
1212   getValidWriterKeys(&valid_keys);
1213   Value::Members keys = settings_.getMemberNames();
1214   size_t n = keys.size();
1215   for (size_t i = 0; i < n; ++i) {
1216     String const& key = keys[i];
1217     if (valid_keys.find(key) == valid_keys.end()) {
1218       inv[key] = settings_[key];
1219     }
1220   }
1221   return inv.empty();
1222 }
operator [](const String & key)1223 Value& StreamWriterBuilder::operator[](const String& key) {
1224   return settings_[key];
1225 }
1226 // static
setDefaults(Json::Value * settings)1227 void StreamWriterBuilder::setDefaults(Json::Value* settings) {
1228   //! [StreamWriterBuilderDefaults]
1229   (*settings)["commentStyle"] = "All";
1230   (*settings)["indentation"] = "\t";
1231   (*settings)["enableYAMLCompatibility"] = false;
1232   (*settings)["dropNullPlaceholders"] = false;
1233   (*settings)["useSpecialFloats"] = false;
1234   (*settings)["precision"] = 17;
1235   (*settings)["precisionType"] = "significant";
1236   //! [StreamWriterBuilderDefaults]
1237 }
1238 
writeString(StreamWriter::Factory const & factory,Value const & root)1239 String writeString(StreamWriter::Factory const& factory, Value const& root) {
1240   OStringStream sout;
1241   StreamWriterPtr const writer(factory.newStreamWriter());
1242   writer->write(root, &sout);
1243   return sout.str();
1244 }
1245 
operator <<(OStream & sout,Value const & root)1246 OStream& operator<<(OStream& sout, Value const& root) {
1247   StreamWriterBuilder builder;
1248   StreamWriterPtr const writer(builder.newStreamWriter());
1249   writer->write(root, &sout);
1250   return sout;
1251 }
1252 
1253 } // namespace Json
1254