1 // Copyright 2011 Baptiste Lepilleur
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/writer.h>
8 #include "json_tool.h"
9 #endif // if !defined(JSON_IS_AMALGAMATION)
10 #include <utility>
11 #include <assert.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sstream>
15 #include <iomanip>
16 #include <math.h>
17 
18 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
19 #include <float.h>
20 #define isfinite _finite
21 #define snprintf _snprintf
22 #endif
23 
24 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
25 // Disable warning about strdup being deprecated.
26 #pragma warning(disable : 4996)
27 #endif
28 
29 namespace Json {
30 
containsControlCharacter(const char * str)31 static bool containsControlCharacter(const char* str) {
32   while (*str) {
33     if (isControlCharacter(*(str++)))
34       return true;
35   }
36   return false;
37 }
38 
valueToString(LargestInt value)39 std::string valueToString(LargestInt value) {
40   UIntToStringBuffer buffer;
41   char* current = buffer + sizeof(buffer);
42   bool isNegative = value < 0;
43   if (isNegative)
44     value = -value;
45   uintToString(LargestUInt(value), current);
46   if (isNegative)
47     *--current = '-';
48   assert(current >= buffer);
49   return current;
50 }
51 
valueToString(LargestUInt value)52 std::string valueToString(LargestUInt value) {
53   UIntToStringBuffer buffer;
54   char* current = buffer + sizeof(buffer);
55   uintToString(value, current);
56   assert(current >= buffer);
57   return current;
58 }
59 
60 #if defined(JSON_HAS_INT64)
61 
valueToString(Int value)62 std::string valueToString(Int value) {
63   return valueToString(LargestInt(value));
64 }
65 
valueToString(UInt value)66 std::string valueToString(UInt value) {
67   return valueToString(LargestUInt(value));
68 }
69 
70 #endif // # if defined(JSON_HAS_INT64)
71 
valueToString(double value)72 std::string valueToString(double value) {
73   // Allocate a buffer that is more than large enough to store the 16 digits of
74   // precision requested below.
75   char buffer[32];
76   int len = -1;
77 
78 // Print into the buffer. We need not request the alternative representation
79 // that always has a decimal point because JSON doesn't distingish the
80 // concepts of reals and integers.
81 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with
82                                                       // visual studio 2005 to
83                                                       // avoid warning.
84 #if defined(WINCE)
85   len = _snprintf(buffer, sizeof(buffer), "%.16g", value);
86 #else
87   len = sprintf_s(buffer, sizeof(buffer), "%.16g", value);
88 #endif
89 #else
90   if (isfinite(value)) {
91     len = snprintf(buffer, sizeof(buffer), "%.16g", value);
92   } else {
93     // IEEE standard states that NaN values will not compare to themselves
94     if (value != value) {
95       len = snprintf(buffer, sizeof(buffer), "null");
96     } else if (value < 0) {
97       len = snprintf(buffer, sizeof(buffer), "-1e+9999");
98     } else {
99       len = snprintf(buffer, sizeof(buffer), "1e+9999");
100     }
101     // For those, we do not need to call fixNumLoc, but it is fast.
102   }
103 #endif
104   assert(len >= 0);
105   fixNumericLocale(buffer, buffer + len);
106   return buffer;
107 }
108 
valueToString(bool value)109 std::string valueToString(bool value) { return value ? "true" : "false"; }
110 
valueToQuotedString(const char * value)111 std::string valueToQuotedString(const char* value) {
112   if (value == NULL)
113     return "";
114   // Not sure how to handle unicode...
115   if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
116       !containsControlCharacter(value))
117     return std::string("\"") + value + "\"";
118   // We have to walk value and escape any special characters.
119   // Appending to std::string is not efficient, but this should be rare.
120   // (Note: forward slashes are *not* rare, but I am not escaping them.)
121   std::string::size_type maxsize =
122       strlen(value) * 2 + 3; // allescaped+quotes+NULL
123   std::string result;
124   result.reserve(maxsize); // to avoid lots of mallocs
125   result += "\"";
126   for (const char* c = value; *c != 0; ++c) {
127     switch (*c) {
128     case '\"':
129       result += "\\\"";
130       break;
131     case '\\':
132       result += "\\\\";
133       break;
134     case '\b':
135       result += "\\b";
136       break;
137     case '\f':
138       result += "\\f";
139       break;
140     case '\n':
141       result += "\\n";
142       break;
143     case '\r':
144       result += "\\r";
145       break;
146     case '\t':
147       result += "\\t";
148       break;
149     // case '/':
150     // Even though \/ is considered a legal escape in JSON, a bare
151     // slash is also legal, so I see no reason to escape it.
152     // (I hope I am not misunderstanding something.
153     // blep notes: actually escaping \/ may be useful in javascript to avoid </
154     // sequence.
155     // Should add a flag to allow this compatibility mode and prevent this
156     // sequence from occurring.
157     default:
158       if (isControlCharacter(*c)) {
159         std::ostringstream oss;
160         oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
161             << std::setw(4) << static_cast<int>(*c);
162         result += oss.str();
163       } else {
164         result += *c;
165       }
166       break;
167     }
168   }
169   result += "\"";
170   return result;
171 }
172 
173 // Class Writer
174 // //////////////////////////////////////////////////////////////////
~Writer()175 Writer::~Writer() {}
176 
177 // Class FastWriter
178 // //////////////////////////////////////////////////////////////////
179 
FastWriter()180 FastWriter::FastWriter()
181     : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
182       omitEndingLineFeed_(false) {}
183 
enableYAMLCompatibility()184 void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
185 
dropNullPlaceholders()186 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
187 
omitEndingLineFeed()188 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
189 
write(const Value & root)190 std::string FastWriter::write(const Value& root) {
191   document_ = "";
192   writeValue(root);
193   if (!omitEndingLineFeed_)
194     document_ += "\n";
195   return document_;
196 }
197 
writeValue(const Value & value)198 void FastWriter::writeValue(const Value& value) {
199   switch (value.type()) {
200   case nullValue:
201     if (!dropNullPlaceholders_)
202       document_ += "null";
203     break;
204   case intValue:
205     document_ += valueToString(value.asLargestInt());
206     break;
207   case uintValue:
208     document_ += valueToString(value.asLargestUInt());
209     break;
210   case realValue:
211     document_ += valueToString(value.asDouble());
212     break;
213   case stringValue:
214     document_ += valueToQuotedString(value.asCString());
215     break;
216   case booleanValue:
217     document_ += valueToString(value.asBool());
218     break;
219   case arrayValue: {
220     document_ += '[';
221     int size = value.size();
222     for (int index = 0; index < size; ++index) {
223       if (index > 0)
224         document_ += ',';
225       writeValue(value[index]);
226     }
227     document_ += ']';
228   } break;
229   case objectValue: {
230     Value::Members members(value.getMemberNames());
231     document_ += '{';
232     for (Value::Members::iterator it = members.begin(); it != members.end();
233          ++it) {
234       const std::string& name = *it;
235       if (it != members.begin())
236         document_ += ',';
237       document_ += valueToQuotedString(name.c_str());
238       document_ += yamlCompatiblityEnabled_ ? ": " : ":";
239       writeValue(value[name]);
240     }
241     document_ += '}';
242   } break;
243   }
244 }
245 
246 // Class StyledWriter
247 // //////////////////////////////////////////////////////////////////
248 
StyledWriter()249 StyledWriter::StyledWriter()
250     : rightMargin_(74), indentSize_(3), addChildValues_() {}
251 
write(const Value & root)252 std::string StyledWriter::write(const Value& root) {
253   document_ = "";
254   addChildValues_ = false;
255   indentString_ = "";
256   writeCommentBeforeValue(root);
257   writeValue(root);
258   writeCommentAfterValueOnSameLine(root);
259   document_ += "\n";
260   return document_;
261 }
262 
writeValue(const Value & value)263 void StyledWriter::writeValue(const Value& value) {
264   switch (value.type()) {
265   case nullValue:
266     pushValue("null");
267     break;
268   case intValue:
269     pushValue(valueToString(value.asLargestInt()));
270     break;
271   case uintValue:
272     pushValue(valueToString(value.asLargestUInt()));
273     break;
274   case realValue:
275     pushValue(valueToString(value.asDouble()));
276     break;
277   case stringValue:
278     pushValue(valueToQuotedString(value.asCString()));
279     break;
280   case booleanValue:
281     pushValue(valueToString(value.asBool()));
282     break;
283   case arrayValue:
284     writeArrayValue(value);
285     break;
286   case objectValue: {
287     Value::Members members(value.getMemberNames());
288     if (members.empty())
289       pushValue("{}");
290     else {
291       writeWithIndent("{");
292       indent();
293       Value::Members::iterator it = members.begin();
294       for (;;) {
295         const std::string& name = *it;
296         const Value& childValue = value[name];
297         writeCommentBeforeValue(childValue);
298         writeWithIndent(valueToQuotedString(name.c_str()));
299         document_ += " : ";
300         writeValue(childValue);
301         if (++it == members.end()) {
302           writeCommentAfterValueOnSameLine(childValue);
303           break;
304         }
305         document_ += ',';
306         writeCommentAfterValueOnSameLine(childValue);
307       }
308       unindent();
309       writeWithIndent("}");
310     }
311   } break;
312   }
313 }
314 
writeArrayValue(const Value & value)315 void StyledWriter::writeArrayValue(const Value& value) {
316   unsigned size = value.size();
317   if (size == 0)
318     pushValue("[]");
319   else {
320     bool isArrayMultiLine = isMultineArray(value);
321     if (isArrayMultiLine) {
322       writeWithIndent("[");
323       indent();
324       bool hasChildValue = !childValues_.empty();
325       unsigned index = 0;
326       for (;;) {
327         const Value& childValue = value[index];
328         writeCommentBeforeValue(childValue);
329         if (hasChildValue)
330           writeWithIndent(childValues_[index]);
331         else {
332           writeIndent();
333           writeValue(childValue);
334         }
335         if (++index == size) {
336           writeCommentAfterValueOnSameLine(childValue);
337           break;
338         }
339         document_ += ',';
340         writeCommentAfterValueOnSameLine(childValue);
341       }
342       unindent();
343       writeWithIndent("]");
344     } else // output on a single line
345     {
346       assert(childValues_.size() == size);
347       document_ += "[ ";
348       for (unsigned index = 0; index < size; ++index) {
349         if (index > 0)
350           document_ += ", ";
351         document_ += childValues_[index];
352       }
353       document_ += " ]";
354     }
355   }
356 }
357 
isMultineArray(const Value & value)358 bool StyledWriter::isMultineArray(const Value& value) {
359   int size = value.size();
360   bool isMultiLine = size * 3 >= rightMargin_;
361   childValues_.clear();
362   for (int index = 0; index < size && !isMultiLine; ++index) {
363     const Value& childValue = value[index];
364     isMultiLine =
365         isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
366                         childValue.size() > 0);
367   }
368   if (!isMultiLine) // check if line length > max line length
369   {
370     childValues_.reserve(size);
371     addChildValues_ = true;
372     int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
373     for (int index = 0; index < size; ++index) {
374       writeValue(value[index]);
375       lineLength += int(childValues_[index].length());
376     }
377     addChildValues_ = false;
378     isMultiLine = isMultiLine || lineLength >= rightMargin_;
379   }
380   return isMultiLine;
381 }
382 
pushValue(const std::string & value)383 void StyledWriter::pushValue(const std::string& value) {
384   if (addChildValues_)
385     childValues_.push_back(value);
386   else
387     document_ += value;
388 }
389 
writeIndent()390 void StyledWriter::writeIndent() {
391   if (!document_.empty()) {
392     char last = document_[document_.length() - 1];
393     if (last == ' ') // already indented
394       return;
395     if (last != '\n') // Comments may add new-line
396       document_ += '\n';
397   }
398   document_ += indentString_;
399 }
400 
writeWithIndent(const std::string & value)401 void StyledWriter::writeWithIndent(const std::string& value) {
402   writeIndent();
403   document_ += value;
404 }
405 
indent()406 void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
407 
unindent()408 void StyledWriter::unindent() {
409   assert(int(indentString_.size()) >= indentSize_);
410   indentString_.resize(indentString_.size() - indentSize_);
411 }
412 
writeCommentBeforeValue(const Value & root)413 void StyledWriter::writeCommentBeforeValue(const Value& root) {
414   if (!root.hasComment(commentBefore))
415     return;
416 
417   document_ += "\n";
418   writeIndent();
419   std::string normalizedComment = normalizeEOL(root.getComment(commentBefore));
420   std::string::const_iterator iter = normalizedComment.begin();
421   while (iter != normalizedComment.end()) {
422     document_ += *iter;
423     if (*iter == '\n' && *(iter + 1) == '/')
424       writeIndent();
425     ++iter;
426   }
427 
428   // Comments are stripped of newlines, so add one here
429   document_ += "\n";
430 }
431 
writeCommentAfterValueOnSameLine(const Value & root)432 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
433   if (root.hasComment(commentAfterOnSameLine))
434     document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
435 
436   if (root.hasComment(commentAfter)) {
437     document_ += "\n";
438     document_ += normalizeEOL(root.getComment(commentAfter));
439     document_ += "\n";
440   }
441 }
442 
hasCommentForValue(const Value & value)443 bool StyledWriter::hasCommentForValue(const Value& value) {
444   return value.hasComment(commentBefore) ||
445          value.hasComment(commentAfterOnSameLine) ||
446          value.hasComment(commentAfter);
447 }
448 
normalizeEOL(const std::string & text)449 std::string StyledWriter::normalizeEOL(const std::string& text) {
450   std::string normalized;
451   normalized.reserve(text.length());
452   const char* begin = text.c_str();
453   const char* end = begin + text.length();
454   const char* current = begin;
455   while (current != end) {
456     char c = *current++;
457     if (c == '\r') // mac or dos EOL
458     {
459       if (*current == '\n') // convert dos EOL
460         ++current;
461       normalized += '\n';
462     } else // handle unix EOL & other char
463       normalized += c;
464   }
465   return normalized;
466 }
467 
468 // Class StyledStreamWriter
469 // //////////////////////////////////////////////////////////////////
470 
StyledStreamWriter(std::string indentation)471 StyledStreamWriter::StyledStreamWriter(std::string indentation)
472     : document_(NULL), rightMargin_(74), indentation_(indentation),
473       addChildValues_() {}
474 
write(std::ostream & out,const Value & root)475 void StyledStreamWriter::write(std::ostream& out, const Value& root) {
476   document_ = &out;
477   addChildValues_ = false;
478   indentString_ = "";
479   writeCommentBeforeValue(root);
480   writeValue(root);
481   writeCommentAfterValueOnSameLine(root);
482   *document_ << "\n";
483   document_ = NULL; // Forget the stream, for safety.
484 }
485 
writeValue(const Value & value)486 void StyledStreamWriter::writeValue(const Value& value) {
487   switch (value.type()) {
488   case nullValue:
489     pushValue("null");
490     break;
491   case intValue:
492     pushValue(valueToString(value.asLargestInt()));
493     break;
494   case uintValue:
495     pushValue(valueToString(value.asLargestUInt()));
496     break;
497   case realValue:
498     pushValue(valueToString(value.asDouble()));
499     break;
500   case stringValue:
501     pushValue(valueToQuotedString(value.asCString()));
502     break;
503   case booleanValue:
504     pushValue(valueToString(value.asBool()));
505     break;
506   case arrayValue:
507     writeArrayValue(value);
508     break;
509   case objectValue: {
510     Value::Members members(value.getMemberNames());
511     if (members.empty())
512       pushValue("{}");
513     else {
514       writeWithIndent("{");
515       indent();
516       Value::Members::iterator it = members.begin();
517       for (;;) {
518         const std::string& name = *it;
519         const Value& childValue = value[name];
520         writeCommentBeforeValue(childValue);
521         writeWithIndent(valueToQuotedString(name.c_str()));
522         *document_ << " : ";
523         writeValue(childValue);
524         if (++it == members.end()) {
525           writeCommentAfterValueOnSameLine(childValue);
526           break;
527         }
528         *document_ << ",";
529         writeCommentAfterValueOnSameLine(childValue);
530       }
531       unindent();
532       writeWithIndent("}");
533     }
534   } break;
535   }
536 }
537 
writeArrayValue(const Value & value)538 void StyledStreamWriter::writeArrayValue(const Value& value) {
539   unsigned size = value.size();
540   if (size == 0)
541     pushValue("[]");
542   else {
543     bool isArrayMultiLine = isMultineArray(value);
544     if (isArrayMultiLine) {
545       writeWithIndent("[");
546       indent();
547       bool hasChildValue = !childValues_.empty();
548       unsigned index = 0;
549       for (;;) {
550         const Value& childValue = value[index];
551         writeCommentBeforeValue(childValue);
552         if (hasChildValue)
553           writeWithIndent(childValues_[index]);
554         else {
555           writeIndent();
556           writeValue(childValue);
557         }
558         if (++index == size) {
559           writeCommentAfterValueOnSameLine(childValue);
560           break;
561         }
562         *document_ << ",";
563         writeCommentAfterValueOnSameLine(childValue);
564       }
565       unindent();
566       writeWithIndent("]");
567     } else // output on a single line
568     {
569       assert(childValues_.size() == size);
570       *document_ << "[ ";
571       for (unsigned index = 0; index < size; ++index) {
572         if (index > 0)
573           *document_ << ", ";
574         *document_ << childValues_[index];
575       }
576       *document_ << " ]";
577     }
578   }
579 }
580 
isMultineArray(const Value & value)581 bool StyledStreamWriter::isMultineArray(const Value& value) {
582   int size = value.size();
583   bool isMultiLine = size * 3 >= rightMargin_;
584   childValues_.clear();
585   for (int index = 0; index < size && !isMultiLine; ++index) {
586     const Value& childValue = value[index];
587     isMultiLine =
588         isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
589                         childValue.size() > 0);
590   }
591   if (!isMultiLine) // check if line length > max line length
592   {
593     childValues_.reserve(size);
594     addChildValues_ = true;
595     int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
596     for (int index = 0; index < size; ++index) {
597       writeValue(value[index]);
598       lineLength += int(childValues_[index].length());
599     }
600     addChildValues_ = false;
601     isMultiLine = isMultiLine || lineLength >= rightMargin_;
602   }
603   return isMultiLine;
604 }
605 
pushValue(const std::string & value)606 void StyledStreamWriter::pushValue(const std::string& value) {
607   if (addChildValues_)
608     childValues_.push_back(value);
609   else
610     *document_ << value;
611 }
612 
writeIndent()613 void StyledStreamWriter::writeIndent() {
614   /*
615     Some comments in this method would have been nice. ;-)
616 
617    if ( !document_.empty() )
618    {
619       char last = document_[document_.length()-1];
620       if ( last == ' ' )     // already indented
621          return;
622       if ( last != '\n' )    // Comments may add new-line
623          *document_ << '\n';
624    }
625   */
626   *document_ << '\n' << indentString_;
627 }
628 
writeWithIndent(const std::string & value)629 void StyledStreamWriter::writeWithIndent(const std::string& value) {
630   writeIndent();
631   *document_ << value;
632 }
633 
indent()634 void StyledStreamWriter::indent() { indentString_ += indentation_; }
635 
unindent()636 void StyledStreamWriter::unindent() {
637   assert(indentString_.size() >= indentation_.size());
638   indentString_.resize(indentString_.size() - indentation_.size());
639 }
640 
writeCommentBeforeValue(const Value & root)641 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
642   if (!root.hasComment(commentBefore))
643     return;
644   *document_ << normalizeEOL(root.getComment(commentBefore));
645   *document_ << "\n";
646 }
647 
writeCommentAfterValueOnSameLine(const Value & root)648 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
649   if (root.hasComment(commentAfterOnSameLine))
650     *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine));
651 
652   if (root.hasComment(commentAfter)) {
653     *document_ << "\n";
654     *document_ << normalizeEOL(root.getComment(commentAfter));
655     *document_ << "\n";
656   }
657 }
658 
hasCommentForValue(const Value & value)659 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
660   return value.hasComment(commentBefore) ||
661          value.hasComment(commentAfterOnSameLine) ||
662          value.hasComment(commentAfter);
663 }
664 
normalizeEOL(const std::string & text)665 std::string StyledStreamWriter::normalizeEOL(const std::string& text) {
666   std::string normalized;
667   normalized.reserve(text.length());
668   const char* begin = text.c_str();
669   const char* end = begin + text.length();
670   const char* current = begin;
671   while (current != end) {
672     char c = *current++;
673     if (c == '\r') // mac or dos EOL
674     {
675       if (*current == '\n') // convert dos EOL
676         ++current;
677       normalized += '\n';
678     } else // handle unix EOL & other char
679       normalized += c;
680   }
681   return normalized;
682 }
683 
operator <<(std::ostream & sout,const Value & root)684 std::ostream& operator<<(std::ostream& sout, const Value& root) {
685   Json::StyledStreamWriter writer;
686   writer.write(sout, root);
687   return sout;
688 }
689 
690 } // namespace Json
691