JsonCpp - JSON data format manipulation library

JsonCpp project page | JsonCpp home page

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