• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

open-source-parsers / jsoncpp / 12683272582

09 Jan 2025 03:47AM UTC coverage: 95.296%. First build
12683272582

Pull #1591

github

web-flow
Merge b4e88e35b into dca8a24cf
Pull Request #1591:

5308 of 5570 relevant lines covered (95.3%)

11930.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.03
/src/lib_json/json_writer.cpp
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 <algorithm>
11
#include <cassert>
12
#include <cctype>
13
#include <cmath>
14
#include <cstdio>
15
#include <cstring>
16
#include <iomanip>
17
#include <memory>
18
#include <set>
19
#include <sstream>
20
#include <utility>
21

22
#if defined(_MSC_VER)
23
// Disable warning about strdup being deprecated.
24
#pragma warning(disable : 4996)
25
#endif
26

27
namespace Json {
28

29
using StreamWriterPtr = std::unique_ptr<StreamWriter>;
30

31
String valueToString(LargestInt value) {
157,433✔
32
  UIntToStringBuffer buffer;
33
  char* current = buffer + sizeof(buffer);
34
  if (value == Value::minLargestInt) {
157,433✔
35
    uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
36
    *--current = '-';
37✔
37
  } else if (value < 0) {
157,396✔
38
    uintToString(LargestUInt(-value), current);
220✔
39
    *--current = '-';
220✔
40
  } else {
41
    uintToString(LargestUInt(value), current);
157,176✔
42
  }
43
  assert(current >= buffer);
157,433✔
44
  return current;
157,433✔
45
}
46

47
String valueToString(LargestUInt value) {
278✔
48
  UIntToStringBuffer buffer;
49
  char* current = buffer + sizeof(buffer);
50
  uintToString(value, current);
51
  assert(current >= buffer);
278✔
52
  return current;
278✔
53
}
54

55
#if defined(JSON_HAS_INT64)
56

57
String valueToString(Int value) { return valueToString(LargestInt(value)); }
×
58

59
String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
×
60

61
#endif // # if defined(JSON_HAS_INT64)
62

63
namespace {
64
String valueToString(double value, bool useSpecialFloats,
186✔
65
                     unsigned int precision, PrecisionType precisionType) {
66
  // Print into the buffer. We need not request the alternative representation
67
  // that always has a decimal point because JSON doesn't distinguish the
68
  // concepts of reals and integers.
69
  if (!std::isfinite(value)) {
186✔
70
    if (std::isnan(value))
27✔
71
      return useSpecialFloats ? "NaN" : "null";
1✔
72
    if (value < 0)
26✔
73
      return useSpecialFloats ? "-Infinity" : "-1e+9999";
25✔
74
    return useSpecialFloats ? "Infinity" : "1e+9999";
25✔
75
  }
76

77
  String buffer(size_t(36), '\0');
78
  while (true) {
79
    int len = jsoncpp_snprintf(
159✔
80
        &*buffer.begin(), buffer.size(),
81
        (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
82
        precision, value);
83
    assert(len >= 0);
159✔
84
    auto wouldPrint = static_cast<size_t>(len);
159✔
85
    if (wouldPrint >= buffer.size()) {
159✔
86
      buffer.resize(wouldPrint + 1);
×
87
      continue;
88
    }
89
    buffer.resize(wouldPrint);
90
    break;
91
  }
×
92

93
  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
159✔
94

95
  // try to ensure we preserve the fact that this was given to us as a double on
96
  // input
97
  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
159✔
98
    buffer += ".0";
99
  }
100

101
  // strip the zero padding from the right
102
  if (precisionType == PrecisionType::decimalPlaces) {
159✔
103
    buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
7✔
104
                 buffer.end());
105
  }
106

107
  return buffer;
108
}
109
} // namespace
110

111
String valueToString(double value, unsigned int precision,
120✔
112
                     PrecisionType precisionType) {
113
  return valueToString(value, false, precision, precisionType);
120✔
114
}
115

116
String valueToString(bool value) { return value ? "true" : "false"; }
84✔
117

118
static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
1,637✔
119
  assert(s || !n);
1,637✔
120

121
  return std::any_of(s, s + n, [](unsigned char c) {
1,637✔
122
    return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
2,853✔
123
  });
1,637✔
124
}
125

126
static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
23,544✔
127
  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
128

129
  unsigned int firstByte = static_cast<unsigned char>(*s);
23,544✔
130

131
  if (firstByte < 0x80)
23,544✔
132
    return firstByte;
133

134
  if (firstByte < 0xE0) {
157✔
135
    if (e - s < 2)
123✔
136
      return REPLACEMENT_CHARACTER;
137

138
    unsigned int calculated =
139
        ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
123✔
140
    s += 1;
123✔
141
    // oversized encoded characters are invalid
142
    return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
246✔
143
  }
144

145
  if (firstByte < 0xF0) {
34✔
146
    if (e - s < 3)
17✔
147
      return REPLACEMENT_CHARACTER;
148

149
    unsigned int calculated = ((firstByte & 0x0F) << 12) |
17✔
150
                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
17✔
151
                              (static_cast<unsigned int>(s[2]) & 0x3F);
17✔
152
    s += 2;
17✔
153
    // surrogates aren't valid codepoints itself
154
    // shouldn't be UTF-8 encoded
155
    if (calculated >= 0xD800 && calculated <= 0xDFFF)
17✔
156
      return REPLACEMENT_CHARACTER;
157
    // oversized encoded characters are invalid
158
    return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
34✔
159
  }
160

161
  if (firstByte < 0xF8) {
17✔
162
    if (e - s < 4)
17✔
163
      return REPLACEMENT_CHARACTER;
164

165
    unsigned int calculated = ((firstByte & 0x07) << 18) |
17✔
166
                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
17✔
167
                              ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
17✔
168
                              (static_cast<unsigned int>(s[3]) & 0x3F);
17✔
169
    s += 3;
17✔
170
    // oversized encoded characters are invalid
171
    return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
34✔
172
  }
173

174
  return REPLACEMENT_CHARACTER;
175
}
176

177
static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
178
                           "101112131415161718191a1b1c1d1e1f"
179
                           "202122232425262728292a2b2c2d2e2f"
180
                           "303132333435363738393a3b3c3d3e3f"
181
                           "404142434445464748494a4b4c4d4e4f"
182
                           "505152535455565758595a5b5c5d5e5f"
183
                           "606162636465666768696a6b6c6d6e6f"
184
                           "707172737475767778797a7b7c7d7e7f"
185
                           "808182838485868788898a8b8c8d8e8f"
186
                           "909192939495969798999a9b9c9d9e9f"
187
                           "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
188
                           "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
189
                           "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
190
                           "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
191
                           "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
192
                           "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
193

194
static String toHex16Bit(unsigned int x) {
230✔
195
  const unsigned int hi = (x >> 8) & 0xff;
230✔
196
  const unsigned int lo = x & 0xff;
230✔
197
  String result(4, ' ');
198
  result[0] = hex2[2 * hi];
230✔
199
  result[1] = hex2[2 * hi + 1];
230✔
200
  result[2] = hex2[2 * lo];
230✔
201
  result[3] = hex2[2 * lo + 1];
230✔
202
  return result;
230✔
203
}
204

205
static void appendRaw(String& result, unsigned ch) {
206
  result += static_cast<char>(ch);
23,358✔
207
}
23,500✔
208

209
static void appendHex(String& result, unsigned ch) {
230✔
210
  result.append("\\u").append(toHex16Bit(ch));
230✔
211
}
230✔
212

213
static String valueToQuotedStringN(const char* value, size_t length,
1,637✔
214
                                   bool emitUTF8 = false) {
215
  if (value == nullptr)
1,637✔
216
    return "";
×
217

218
  if (!doesAnyCharRequireEscaping(value, length))
1,637✔
219
    return String("\"") + value + "\"";
2,660✔
220
  // We have to walk value and escape any special characters.
221
  // Appending to String is not efficient, but this should be rare.
222
  // (Note: forward slashes are *not* rare, but I am not escaping them.)
223
  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
307✔
224
  String result;
225
  result.reserve(maxsize); // to avoid lots of mallocs
307✔
226
  result += "\"";
227
  char const* end = value + length;
307✔
228
  for (const char* c = value; c != end; ++c) {
25,055✔
229
    switch (*c) {
24,748✔
230
    case '\"':
231
      result += "\\\"";
232
      break;
233
    case '\\':
234
      result += "\\\\";
235
      break;
236
    case '\b':
237
      result += "\\b";
238
      break;
239
    case '\f':
240
      result += "\\f";
241
      break;
242
    case '\n':
243
      result += "\\n";
244
      break;
245
    case '\r':
246
      result += "\\r";
247
      break;
248
    case '\t':
249
      result += "\\t";
250
      break;
251
    // case '/':
252
    // Even though \/ is considered a legal escape in JSON, a bare
253
    // slash is also legal, so I see no reason to escape it.
254
    // (I hope I am not misunderstanding something.)
255
    // blep notes: actually escaping \/ may be useful in javascript to avoid </
256
    // sequence.
257
    // Should add a flag to allow this compatibility mode and prevent this
258
    // sequence from occurring.
259
    default: {
23,713✔
260
      if (emitUTF8) {
23,713✔
261
        unsigned codepoint = static_cast<unsigned char>(*c);
169✔
262
        if (codepoint < 0x20) {
169✔
263
          appendHex(result, codepoint);
27✔
264
        } else {
265
          appendRaw(result, codepoint);
266
        }
267
      } else {
268
        unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
23,544✔
269
        if (codepoint < 0x20) {
23,544✔
270
          appendHex(result, codepoint);
29✔
271
        } else if (codepoint < 0x80) {
23,515✔
272
          appendRaw(result, codepoint);
273
        } else if (codepoint < 0x10000) {
157✔
274
          // Basic Multilingual Plane
275
          appendHex(result, codepoint);
140✔
276
        } else {
277
          // Extended Unicode. Encode 20 bits as a surrogate pair.
278
          codepoint -= 0x10000;
17✔
279
          appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
17✔
280
          appendHex(result, 0xdc00 + (codepoint & 0x3ff));
17✔
281
        }
282
      }
283
    } break;
284
    }
285
  }
286
  result += "\"";
287
  return result;
288
}
289

290
String valueToQuotedString(const char* value) {
×
291
  return valueToQuotedStringN(value, strlen(value));
×
292
}
293

294
String valueToQuotedString(const char* value, size_t length) {
292✔
295
  return valueToQuotedStringN(value, length);
292✔
296
}
297

298
// Class Writer
299
// //////////////////////////////////////////////////////////////////
300
Writer::~Writer() = default;
490✔
301

302
// Class FastWriter
303
// //////////////////////////////////////////////////////////////////
304

305
FastWriter::FastWriter()
8✔
306

307
    = default;
308

309
void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
1✔
310

311
void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
1✔
312

313
void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
1✔
314

315
String FastWriter::write(const Value& root) {
13✔
316
  document_.clear();
13✔
317
  writeValue(root);
13✔
318
  if (!omitEndingLineFeed_)
13✔
319
    document_ += '\n';
320
  return document_;
13✔
321
}
322

323
void FastWriter::writeValue(const Value& value) {
55✔
324
  switch (value.type()) {
55✔
325
  case nullValue:
7✔
326
    if (!dropNullPlaceholders_)
7✔
327
      document_ += "null";
6✔
328
    break;
329
  case intValue:
7✔
330
    document_ += valueToString(value.asLargestInt());
7✔
331
    break;
7✔
332
  case uintValue:
1✔
333
    document_ += valueToString(value.asLargestUInt());
1✔
334
    break;
1✔
335
  case realValue:
2✔
336
    document_ += valueToString(value.asDouble());
2✔
337
    break;
2✔
338
  case stringValue: {
17✔
339
    // Is NULL possible for value.string_? No.
340
    char const* str;
341
    char const* end;
342
    bool ok = value.getString(&str, &end);
17✔
343
    if (ok)
17✔
344
      document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
34✔
345
    break;
346
  }
347
  case booleanValue:
2✔
348
    document_ += valueToString(value.asBool());
2✔
349
    break;
2✔
350
  case arrayValue: {
6✔
351
    document_ += '[';
6✔
352
    ArrayIndex size = value.size();
6✔
353
    for (ArrayIndex index = 0; index < size; ++index) {
24✔
354
      if (index > 0)
18✔
355
        document_ += ',';
356
      writeValue(value[index]);
18✔
357
    }
358
    document_ += ']';
359
  } break;
360
  case objectValue: {
13✔
361
    Value::Members members(value.getMemberNames());
13✔
362
    document_ += '{';
13✔
363
    for (auto it = members.begin(); it != members.end(); ++it) {
37✔
364
      const String& name = *it;
365
      if (it != members.begin())
24✔
366
        document_ += ',';
367
      document_ += valueToQuotedStringN(name.data(), name.length());
24✔
368
      document_ += yamlCompatibilityEnabled_ ? ": " : ":";
24✔
369
      writeValue(value[name]);
24✔
370
    }
371
    document_ += '}';
372
  } break;
13✔
373
  }
374
}
55✔
375

376
// Class StyledWriter
377
// //////////////////////////////////////////////////////////////////
378

379
StyledWriter::StyledWriter() = default;
237✔
380

381
String StyledWriter::write(const Value& root) {
240✔
382
  document_.clear();
240✔
383
  addChildValues_ = false;
240✔
384
  indentString_.clear();
385
  writeCommentBeforeValue(root);
240✔
386
  writeValue(root);
240✔
387
  writeCommentAfterValueOnSameLine(root);
240✔
388
  document_ += '\n';
389
  return document_;
240✔
390
}
391

392
void StyledWriter::writeValue(const Value& value) {
17,893✔
393
  switch (value.type()) {
17,893✔
394
  case nullValue:
395
    pushValue("null");
17✔
396
    break;
17✔
397
  case intValue:
17,491✔
398
    pushValue(valueToString(value.asLargestInt()));
17,491✔
399
    break;
17,491✔
400
  case uintValue:
46✔
401
    pushValue(valueToString(value.asLargestUInt()));
46✔
402
    break;
46✔
403
  case realValue:
50✔
404
    pushValue(valueToString(value.asDouble()));
50✔
405
    break;
50✔
406
  case stringValue: {
122✔
407
    // Is NULL possible for value.string_? No.
408
    char const* str;
409
    char const* end;
410
    bool ok = value.getString(&str, &end);
122✔
411
    if (ok)
122✔
412
      pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
244✔
413
    else
414
      pushValue("");
×
415
    break;
416
  }
417
  case booleanValue:
18✔
418
    pushValue(valueToString(value.asBool()));
18✔
419
    break;
18✔
420
  case arrayValue:
66✔
421
    writeArrayValue(value);
66✔
422
    break;
66✔
423
  case objectValue: {
83✔
424
    Value::Members members(value.getMemberNames());
83✔
425
    if (members.empty())
83✔
426
      pushValue("{}");
10✔
427
    else {
428
      writeWithIndent("{");
78✔
429
      indent();
78✔
430
      auto it = members.begin();
431
      for (;;) {
432
        const String& name = *it;
433
        const Value& childValue = value[name];
146✔
434
        writeCommentBeforeValue(childValue);
146✔
435
        writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
146✔
436
        document_ += " : ";
146✔
437
        writeValue(childValue);
146✔
438
        if (++it == members.end()) {
146✔
439
          writeCommentAfterValueOnSameLine(childValue);
78✔
440
          break;
441
        }
442
        document_ += ',';
443
        writeCommentAfterValueOnSameLine(childValue);
68✔
444
      }
445
      unindent();
78✔
446
      writeWithIndent("}");
156✔
447
    }
448
  } break;
83✔
449
  }
450
}
17,893✔
451

452
void StyledWriter::writeArrayValue(const Value& value) {
66✔
453
  size_t size = value.size();
66✔
454
  if (size == 0)
66✔
455
    pushValue("[]");
10✔
456
  else {
457
    bool isArrayMultiLine = isMultilineArray(value);
61✔
458
    if (isArrayMultiLine) {
61✔
459
      writeWithIndent("[");
37✔
460
      indent();
37✔
461
      bool hasChildValue = !childValues_.empty();
462
      ArrayIndex index = 0;
463
      for (;;) {
464
        const Value& childValue = value[index];
17,437✔
465
        writeCommentBeforeValue(childValue);
17,437✔
466
        if (hasChildValue)
17,437✔
467
          writeWithIndent(childValues_[index]);
41✔
468
        else {
469
          writeIndent();
17,396✔
470
          writeValue(childValue);
17,396✔
471
        }
472
        if (++index == size) {
17,437✔
473
          writeCommentAfterValueOnSameLine(childValue);
37✔
474
          break;
475
        }
476
        document_ += ',';
17,400✔
477
        writeCommentAfterValueOnSameLine(childValue);
17,400✔
478
      }
17,400✔
479
      unindent();
37✔
480
      writeWithIndent("]");
74✔
481
    } else // output on a single line
482
    {
483
      assert(childValues_.size() == size);
24✔
484
      document_ += "[ ";
24✔
485
      for (size_t index = 0; index < size; ++index) {
94✔
486
        if (index > 0)
70✔
487
          document_ += ", ";
488
        document_ += childValues_[index];
70✔
489
      }
490
      document_ += " ]";
491
    }
492
  }
493
}
66✔
494

495
bool StyledWriter::isMultilineArray(const Value& value) {
61✔
496
  ArrayIndex const size = value.size();
61✔
497
  bool isMultiLine = size * 3 >= rightMargin_;
61✔
498
  childValues_.clear();
61✔
499
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
204✔
500
    const Value& childValue = value[index];
143✔
501
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
159✔
502
                   !childValue.empty());
16✔
503
  }
504
  if (!isMultiLine) // check if line length > max line length
61✔
505
  {
506
    childValues_.reserve(size);
33✔
507
    addChildValues_ = true;
33✔
508
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
33✔
509
    for (ArrayIndex index = 0; index < size; ++index) {
144✔
510
      if (hasCommentForValue(value[index])) {
111✔
511
        isMultiLine = true;
512
      }
513
      writeValue(value[index]);
111✔
514
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
111✔
515
    }
516
    addChildValues_ = false;
33✔
517
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
33✔
518
  }
519
  return isMultiLine;
61✔
520
}
521

522
void StyledWriter::pushValue(const String& value) {
17,754✔
523
  if (addChildValues_)
17,754✔
524
    childValues_.push_back(value);
111✔
525
  else
526
    document_ += value;
17,643✔
527
}
17,754✔
528

529
void StyledWriter::writeIndent() {
17,982✔
530
  if (!document_.empty()) {
17,982✔
531
    char last = document_[document_.length() - 1];
17,936✔
532
    if (last == ' ') // already indented
17,936✔
533
      return;
534
    if (last != '\n') // Comments may add new-line
17,875✔
535
      document_ += '\n';
17,646✔
536
  }
537
  document_ += indentString_;
17,921✔
538
}
539

540
void StyledWriter::writeWithIndent(const String& value) {
417✔
541
  writeIndent();
417✔
542
  document_ += value;
417✔
543
}
417✔
544

545
void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
230✔
546

547
void StyledWriter::unindent() {
115✔
548
  assert(indentString_.size() >= indentSize_);
115✔
549
  indentString_.resize(indentString_.size() - indentSize_);
115✔
550
}
115✔
551

552
void StyledWriter::writeCommentBeforeValue(const Value& root) {
17,823✔
553
  if (!root.hasComment(commentBefore))
17,823✔
554
    return;
17,690✔
555

556
  document_ += '\n';
133✔
557
  writeIndent();
133✔
558
  const String& comment = root.getComment(commentBefore);
133✔
559
  String::const_iterator iter = comment.begin();
560
  while (iter != comment.end()) {
6,481✔
561
    document_ += *iter;
6,348✔
562
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,348✔
563
      writeIndent();
36✔
564
    ++iter;
565
  }
566

567
  // Comments are stripped of trailing newlines, so add one here
568
  document_ += '\n';
569
}
570

571
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
17,823✔
572
  if (root.hasComment(commentAfterOnSameLine))
17,823✔
573
    document_ += " " + root.getComment(commentAfterOnSameLine);
34✔
574

575
  if (root.hasComment(commentAfter)) {
17,823✔
576
    document_ += '\n';
5✔
577
    document_ += root.getComment(commentAfter);
10✔
578
    document_ += '\n';
579
  }
580
}
17,823✔
581

582
bool StyledWriter::hasCommentForValue(const Value& value) {
111✔
583
  return value.hasComment(commentBefore) ||
218✔
584
         value.hasComment(commentAfterOnSameLine) ||
218✔
585
         value.hasComment(commentAfter);
107✔
586
}
587

588
// Class StyledStreamWriter
589
// //////////////////////////////////////////////////////////////////
590

591
StyledStreamWriter::StyledStreamWriter(String indentation)
238✔
592
    : document_(nullptr), indentation_(std::move(indentation)),
238✔
593
      addChildValues_(), indented_(false) {}
238✔
594

595
void StyledStreamWriter::write(OStream& out, const Value& root) {
240✔
596
  document_ = &out;
240✔
597
  addChildValues_ = false;
240✔
598
  indentString_.clear();
599
  indented_ = true;
240✔
600
  writeCommentBeforeValue(root);
240✔
601
  if (!indented_)
240✔
602
    writeIndent();
81✔
603
  indented_ = true;
240✔
604
  writeValue(root);
240✔
605
  writeCommentAfterValueOnSameLine(root);
240✔
606
  *document_ << "\n";
240✔
607
  document_ = nullptr; // Forget the stream, for safety.
240✔
608
}
240✔
609

610
void StyledStreamWriter::writeValue(const Value& value) {
17,893✔
611
  switch (value.type()) {
17,893✔
612
  case nullValue:
613
    pushValue("null");
17✔
614
    break;
17✔
615
  case intValue:
17,491✔
616
    pushValue(valueToString(value.asLargestInt()));
17,491✔
617
    break;
17,491✔
618
  case uintValue:
46✔
619
    pushValue(valueToString(value.asLargestUInt()));
46✔
620
    break;
46✔
621
  case realValue:
50✔
622
    pushValue(valueToString(value.asDouble()));
50✔
623
    break;
50✔
624
  case stringValue: {
122✔
625
    // Is NULL possible for value.string_? No.
626
    char const* str;
627
    char const* end;
628
    bool ok = value.getString(&str, &end);
122✔
629
    if (ok)
122✔
630
      pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
244✔
631
    else
632
      pushValue("");
×
633
    break;
634
  }
635
  case booleanValue:
18✔
636
    pushValue(valueToString(value.asBool()));
18✔
637
    break;
18✔
638
  case arrayValue:
66✔
639
    writeArrayValue(value);
66✔
640
    break;
66✔
641
  case objectValue: {
83✔
642
    Value::Members members(value.getMemberNames());
83✔
643
    if (members.empty())
83✔
644
      pushValue("{}");
10✔
645
    else {
646
      writeWithIndent("{");
78✔
647
      indent();
78✔
648
      auto it = members.begin();
649
      for (;;) {
650
        const String& name = *it;
651
        const Value& childValue = value[name];
146✔
652
        writeCommentBeforeValue(childValue);
146✔
653
        writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
146✔
654
        *document_ << " : ";
146✔
655
        writeValue(childValue);
146✔
656
        if (++it == members.end()) {
146✔
657
          writeCommentAfterValueOnSameLine(childValue);
78✔
658
          break;
659
        }
660
        *document_ << ",";
68✔
661
        writeCommentAfterValueOnSameLine(childValue);
68✔
662
      }
663
      unindent();
78✔
664
      writeWithIndent("}");
156✔
665
    }
666
  } break;
83✔
667
  }
668
}
17,893✔
669

670
void StyledStreamWriter::writeArrayValue(const Value& value) {
66✔
671
  unsigned size = value.size();
66✔
672
  if (size == 0)
66✔
673
    pushValue("[]");
10✔
674
  else {
675
    bool isArrayMultiLine = isMultilineArray(value);
61✔
676
    if (isArrayMultiLine) {
61✔
677
      writeWithIndent("[");
37✔
678
      indent();
37✔
679
      bool hasChildValue = !childValues_.empty();
680
      unsigned index = 0;
681
      for (;;) {
682
        const Value& childValue = value[index];
17,437✔
683
        writeCommentBeforeValue(childValue);
17,437✔
684
        if (hasChildValue)
17,437✔
685
          writeWithIndent(childValues_[index]);
41✔
686
        else {
687
          if (!indented_)
17,396✔
688
            writeIndent();
17,396✔
689
          indented_ = true;
17,396✔
690
          writeValue(childValue);
17,396✔
691
          indented_ = false;
17,396✔
692
        }
693
        if (++index == size) {
17,437✔
694
          writeCommentAfterValueOnSameLine(childValue);
37✔
695
          break;
696
        }
697
        *document_ << ",";
17,400✔
698
        writeCommentAfterValueOnSameLine(childValue);
17,400✔
699
      }
17,400✔
700
      unindent();
37✔
701
      writeWithIndent("]");
74✔
702
    } else // output on a single line
703
    {
704
      assert(childValues_.size() == size);
24✔
705
      *document_ << "[ ";
24✔
706
      for (unsigned index = 0; index < size; ++index) {
94✔
707
        if (index > 0)
70✔
708
          *document_ << ", ";
46✔
709
        *document_ << childValues_[index];
70✔
710
      }
711
      *document_ << " ]";
24✔
712
    }
713
  }
714
}
66✔
715

716
bool StyledStreamWriter::isMultilineArray(const Value& value) {
61✔
717
  ArrayIndex const size = value.size();
61✔
718
  bool isMultiLine = size * 3 >= rightMargin_;
61✔
719
  childValues_.clear();
61✔
720
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
204✔
721
    const Value& childValue = value[index];
143✔
722
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
159✔
723
                   !childValue.empty());
16✔
724
  }
725
  if (!isMultiLine) // check if line length > max line length
61✔
726
  {
727
    childValues_.reserve(size);
33✔
728
    addChildValues_ = true;
33✔
729
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
33✔
730
    for (ArrayIndex index = 0; index < size; ++index) {
144✔
731
      if (hasCommentForValue(value[index])) {
111✔
732
        isMultiLine = true;
733
      }
734
      writeValue(value[index]);
111✔
735
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
111✔
736
    }
737
    addChildValues_ = false;
33✔
738
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
33✔
739
  }
740
  return isMultiLine;
61✔
741
}
742

743
void StyledStreamWriter::pushValue(const String& value) {
17,754✔
744
  if (addChildValues_)
17,754✔
745
    childValues_.push_back(value);
111✔
746
  else
747
    *document_ << value;
17,643✔
748
}
17,754✔
749

750
void StyledStreamWriter::writeIndent() {
17,873✔
751
  // blep intended this to look at the so-far-written string
752
  // to determine whether we are already indented, but
753
  // with a stream we cannot do that. So we rely on some saved state.
754
  // The caller checks indented_.
755
  *document_ << '\n' << indentString_;
17,873✔
756
}
17,873✔
757

758
void StyledStreamWriter::writeWithIndent(const String& value) {
417✔
759
  if (!indented_)
417✔
760
    writeIndent();
339✔
761
  *document_ << value;
417✔
762
  indented_ = false;
417✔
763
}
417✔
764

765
void StyledStreamWriter::indent() { indentString_ += indentation_; }
115✔
766

767
void StyledStreamWriter::unindent() {
115✔
768
  assert(indentString_.size() >= indentation_.size());
115✔
769
  indentString_.resize(indentString_.size() - indentation_.size());
115✔
770
}
115✔
771

772
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
17,823✔
773
  if (!root.hasComment(commentBefore))
17,823✔
774
    return;
17,690✔
775

776
  if (!indented_)
133✔
777
    writeIndent();
52✔
778
  const String& comment = root.getComment(commentBefore);
133✔
779
  String::const_iterator iter = comment.begin();
780
  while (iter != comment.end()) {
6,481✔
781
    *document_ << *iter;
6,348✔
782
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,348✔
783
      // writeIndent();  // would include newline
784
      *document_ << indentString_;
36✔
785
    ++iter;
786
  }
787
  indented_ = false;
133✔
788
}
789

790
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
17,823✔
791
  if (root.hasComment(commentAfterOnSameLine))
17,823✔
792
    *document_ << ' ' << root.getComment(commentAfterOnSameLine);
34✔
793

794
  if (root.hasComment(commentAfter)) {
17,823✔
795
    writeIndent();
5✔
796
    *document_ << root.getComment(commentAfter);
10✔
797
  }
798
  indented_ = false;
17,823✔
799
}
17,823✔
800

801
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
111✔
802
  return value.hasComment(commentBefore) ||
218✔
803
         value.hasComment(commentAfterOnSameLine) ||
218✔
804
         value.hasComment(commentAfter);
107✔
805
}
806

807
//////////////////////////
808
// BuiltStyledStreamWriter
809

810
/// Scoped enums are not available until C++11.
811
struct CommentStyle {
812
  /// Decide whether to write comments.
813
  enum Enum {
814
    None, ///< Drop all comments.
815
    Most, ///< Recover odd behavior of previous versions (not implemented yet).
816
    All   ///< Keep all comments.
817
  };
818
};
819

820
struct BuiltStyledStreamWriter : public StreamWriter {
821
  BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
822
                          String colonSymbol, String nullSymbol,
823
                          String endingLineFeedSymbol, bool useSpecialFloats,
824
                          bool emitUTF8, unsigned int precision,
825
                          PrecisionType precisionType);
826
  int write(Value const& root, OStream* sout) override;
827

828
private:
829
  void writeValue(Value const& value);
830
  void writeArrayValue(Value const& value);
831
  bool isMultilineArray(Value const& value);
832
  void pushValue(String const& value);
833
  void writeIndent();
834
  void writeWithIndent(String const& value);
835
  void indent();
836
  void unindent();
837
  void writeCommentBeforeValue(Value const& root);
838
  void writeCommentAfterValueOnSameLine(Value const& root);
839
  static bool hasCommentForValue(const Value& value);
840

841
  using ChildValues = std::vector<String>;
842

843
  ChildValues childValues_;
844
  String indentString_;
845
  unsigned int rightMargin_;
846
  String indentation_;
847
  CommentStyle::Enum cs_;
848
  String colonSymbol_;
849
  String nullSymbol_;
850
  String endingLineFeedSymbol_;
851
  bool addChildValues_ : 1;
852
  bool indented_ : 1;
853
  bool useSpecialFloats_ : 1;
854
  bool emitUTF8_ : 1;
855
  unsigned int precision_;
856
  PrecisionType precisionType_;
857
};
858
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
660✔
859
    String indentation, CommentStyle::Enum cs, String colonSymbol,
860
    String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
861
    bool emitUTF8, unsigned int precision, PrecisionType precisionType)
660✔
862
    : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
1,320✔
863
      colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
864
      endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
865
      addChildValues_(false), indented_(false),
660✔
866
      useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
660✔
867
      precision_(precision), precisionType_(precisionType) {}
660✔
868
int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
660✔
869
  sout_ = sout;
660✔
870
  addChildValues_ = false;
660✔
871
  indented_ = true;
660✔
872
  indentString_.clear();
873
  writeCommentBeforeValue(root);
660✔
874
  if (!indented_)
660✔
875
    writeIndent();
84✔
876
  indented_ = true;
660✔
877
  writeValue(root);
660✔
878
  writeCommentAfterValueOnSameLine(root);
660✔
879
  *sout_ << endingLineFeedSymbol_;
660✔
880
  sout_ = nullptr;
660✔
881
  return 0;
660✔
882
}
883
void BuiltStyledStreamWriter::writeValue(Value const& value) {
18,706✔
884
  switch (value.type()) {
18,706✔
885
  case nullValue:
22✔
886
    pushValue(nullSymbol_);
22✔
887
    break;
22✔
888
  case intValue:
17,491✔
889
    pushValue(valueToString(value.asLargestInt()));
17,491✔
890
    break;
17,491✔
891
  case uintValue:
46✔
892
    pushValue(valueToString(value.asLargestUInt()));
46✔
893
    break;
46✔
894
  case realValue:
66✔
895
    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
66✔
896
                            precisionType_));
897
    break;
66✔
898
  case stringValue: {
521✔
899
    // Is NULL is possible for value.string_? No.
900
    char const* str;
901
    char const* end;
902
    bool ok = value.getString(&str, &end);
521✔
903
    if (ok)
521✔
904
      pushValue(
521✔
905
          valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
1,042✔
906
    else
907
      pushValue("");
×
908
    break;
909
  }
910
  case booleanValue:
18✔
911
    pushValue(valueToString(value.asBool()));
18✔
912
    break;
18✔
913
  case arrayValue:
66✔
914
    writeArrayValue(value);
66✔
915
    break;
66✔
916
  case objectValue: {
476✔
917
    Value::Members members(value.getMemberNames());
476✔
918
    if (members.empty())
476✔
919
      pushValue("{}");
10✔
920
    else {
921
      writeWithIndent("{");
471✔
922
      indent();
471✔
923
      auto it = members.begin();
924
      for (;;) {
925
        String const& name = *it;
926
        Value const& childValue = value[name];
539✔
927
        writeCommentBeforeValue(childValue);
539✔
928
        writeWithIndent(
539✔
929
            valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
539✔
930
        *sout_ << colonSymbol_;
539✔
931
        writeValue(childValue);
539✔
932
        if (++it == members.end()) {
539✔
933
          writeCommentAfterValueOnSameLine(childValue);
471✔
934
          break;
935
        }
936
        *sout_ << ",";
68✔
937
        writeCommentAfterValueOnSameLine(childValue);
68✔
938
      }
939
      unindent();
471✔
940
      writeWithIndent("}");
942✔
941
    }
942
  } break;
476✔
943
  }
944
}
18,706✔
945

946
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
66✔
947
  unsigned size = value.size();
66✔
948
  if (size == 0)
66✔
949
    pushValue("[]");
10✔
950
  else {
951
    bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
61✔
952
    if (isMultiLine) {
953
      writeWithIndent("[");
60✔
954
      indent();
60✔
955
      bool hasChildValue = !childValues_.empty();
956
      unsigned index = 0;
957
      for (;;) {
958
        Value const& childValue = value[index];
17,497✔
959
        writeCommentBeforeValue(childValue);
17,497✔
960
        if (hasChildValue)
17,497✔
961
          writeWithIndent(childValues_[index]);
21✔
962
        else {
963
          if (!indented_)
17,476✔
964
            writeIndent();
17,476✔
965
          indented_ = true;
17,476✔
966
          writeValue(childValue);
17,476✔
967
          indented_ = false;
17,476✔
968
        }
969
        if (++index == size) {
17,497✔
970
          writeCommentAfterValueOnSameLine(childValue);
60✔
971
          break;
972
        }
973
        *sout_ << ",";
17,437✔
974
        writeCommentAfterValueOnSameLine(childValue);
17,437✔
975
      }
17,437✔
976
      unindent();
60✔
977
      writeWithIndent("]");
120✔
978
    } else // output on a single line
979
    {
980
      assert(childValues_.size() == size);
1✔
981
      *sout_ << "[";
1✔
982
      if (!indentation_.empty())
1✔
983
        *sout_ << " ";
1✔
984
      for (unsigned index = 0; index < size; ++index) {
11✔
985
        if (index > 0)
10✔
986
          *sout_ << ((!indentation_.empty()) ? ", " : ",");
9✔
987
        *sout_ << childValues_[index];
10✔
988
      }
989
      if (!indentation_.empty())
1✔
990
        *sout_ << " ";
1✔
991
      *sout_ << "]";
1✔
992
    }
993
  }
994
}
66✔
995

996
bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
2✔
997
  ArrayIndex const size = value.size();
2✔
998
  bool isMultiLine = size * 3 >= rightMargin_;
2✔
999
  childValues_.clear();
2✔
1000
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
33✔
1001
    Value const& childValue = value[index];
31✔
1002
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
31✔
1003
                   !childValue.empty());
×
1004
  }
1005
  if (!isMultiLine) // check if line length > max line length
2✔
1006
  {
1007
    childValues_.reserve(size);
2✔
1008
    addChildValues_ = true;
2✔
1009
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
2✔
1010
    for (ArrayIndex index = 0; index < size; ++index) {
33✔
1011
      if (hasCommentForValue(value[index])) {
31✔
1012
        isMultiLine = true;
1013
      }
1014
      writeValue(value[index]);
31✔
1015
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
31✔
1016
    }
1017
    addChildValues_ = false;
2✔
1018
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
2✔
1019
  }
1020
  return isMultiLine;
2✔
1021
}
1022

1023
void BuiltStyledStreamWriter::pushValue(String const& value) {
18,174✔
1024
  if (addChildValues_)
18,174✔
1025
    childValues_.push_back(value);
31✔
1026
  else
1027
    *sout_ << value;
18,143✔
1028
}
18,174✔
1029

1030
void BuiltStyledStreamWriter::writeIndent() {
18,749✔
1031
  // blep intended this to look at the so-far-written string
1032
  // to determine whether we are already indented, but
1033
  // with a stream we cannot do that. So we rely on some saved state.
1034
  // The caller checks indented_.
1035

1036
  if (!indentation_.empty()) {
18,749✔
1037
    // In this case, drop newlines too.
1038
    *sout_ << '\n' << indentString_;
18,741✔
1039
  }
1040
}
18,749✔
1041

1042
void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1,622✔
1043
  if (!indented_)
1,622✔
1044
    writeIndent();
1,133✔
1045
  *sout_ << value;
1,622✔
1046
  indented_ = false;
1,622✔
1047
}
1,622✔
1048

1049
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
531✔
1050

1051
void BuiltStyledStreamWriter::unindent() {
531✔
1052
  assert(indentString_.size() >= indentation_.size());
531✔
1053
  indentString_.resize(indentString_.size() - indentation_.size());
531✔
1054
}
531✔
1055

1056
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
18,696✔
1057
  if (cs_ == CommentStyle::None)
18,696✔
1058
    return;
18,560✔
1059
  if (!root.hasComment(commentBefore))
18,673✔
1060
    return;
1061

1062
  if (!indented_)
136✔
1063
    writeIndent();
52✔
1064
  const String& comment = root.getComment(commentBefore);
136✔
1065
  String::const_iterator iter = comment.begin();
1066
  while (iter != comment.end()) {
6,608✔
1067
    *sout_ << *iter;
6,472✔
1068
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,472✔
1069
      // writeIndent();  // would write extra newline
1070
      *sout_ << indentString_;
36✔
1071
    ++iter;
1072
  }
1073
  indented_ = false;
136✔
1074
}
1075

1076
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
18,696✔
1077
    Value const& root) {
1078
  if (cs_ == CommentStyle::None)
18,696✔
1079
    return;
1080
  if (root.hasComment(commentAfterOnSameLine))
18,673✔
1081
    *sout_ << " " + root.getComment(commentAfterOnSameLine);
32✔
1082

1083
  if (root.hasComment(commentAfter)) {
18,673✔
1084
    writeIndent();
4✔
1085
    *sout_ << root.getComment(commentAfter);
8✔
1086
  }
1087
}
1088

1089
// static
1090
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
31✔
1091
  return value.hasComment(commentBefore) ||
62✔
1092
         value.hasComment(commentAfterOnSameLine) ||
62✔
1093
         value.hasComment(commentAfter);
31✔
1094
}
1095

1096
///////////////
1097
// StreamWriter
1098

1099
StreamWriter::StreamWriter() : sout_(nullptr) {}
660✔
1100
StreamWriter::~StreamWriter() = default;
1,320✔
1101
StreamWriter::Factory::~Factory() = default;
510✔
1102
StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
255✔
1103
StreamWriterBuilder::~StreamWriterBuilder() = default;
510✔
1104
StreamWriter* StreamWriterBuilder::newStreamWriter() const {
660✔
1105
  const String indentation = settings_["indentation"].asString();
660✔
1106
  const String cs_str = settings_["commentStyle"].asString();
660✔
1107
  const String pt_str = settings_["precisionType"].asString();
660✔
1108
  const bool eyc = settings_["enableYAMLCompatibility"].asBool();
660✔
1109
  const bool dnp = settings_["dropNullPlaceholders"].asBool();
660✔
1110
  const bool usf = settings_["useSpecialFloats"].asBool();
660✔
1111
  const bool emitUTF8 = settings_["emitUTF8"].asBool();
660✔
1112
  unsigned int pre = settings_["precision"].asUInt();
660✔
1113
  CommentStyle::Enum cs = CommentStyle::All;
1114
  if (cs_str == "All") {
660✔
1115
    cs = CommentStyle::All;
1116
  } else if (cs_str == "None") {
2✔
1117
    cs = CommentStyle::None;
1118
  } else {
1119
    throwRuntimeError("commentStyle must be 'All' or 'None'");
×
1120
  }
1121
  PrecisionType precisionType(significantDigits);
1122
  if (pt_str == "significant") {
660✔
1123
    precisionType = PrecisionType::significantDigits;
1124
  } else if (pt_str == "decimal") {
7✔
1125
    precisionType = PrecisionType::decimalPlaces;
1126
  } else {
1127
    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
×
1128
  }
1129
  String colonSymbol = " : ";
660✔
1130
  if (eyc) {
660✔
1131
    colonSymbol = ": ";
1132
  } else if (indentation.empty()) {
659✔
1133
    colonSymbol = ":";
1134
  }
1135
  String nullSymbol = "null";
660✔
1136
  if (dnp) {
660✔
1137
    nullSymbol.clear();
1138
  }
1139
  if (pre > 17)
1140
    pre = 17;
1141
  String endingLineFeedSymbol;
1142
  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1143
                                     endingLineFeedSymbol, usf, emitUTF8, pre,
1144
                                     precisionType);
1,980✔
1145
}
1146

1147
bool StreamWriterBuilder::validate(Json::Value* invalid) const {
2✔
1148
  static const auto& valid_keys = *new std::set<String>{
1149
      "indentation",
1150
      "commentStyle",
1151
      "enableYAMLCompatibility",
1152
      "dropNullPlaceholders",
1153
      "useSpecialFloats",
1154
      "emitUTF8",
1155
      "precision",
1156
      "precisionType",
1157
  };
10✔
1158
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
38✔
1159
    auto key = si.name();
17✔
1160
    if (valid_keys.count(key))
17✔
1161
      continue;
1162
    if (invalid)
1✔
1163
      (*invalid)[key] = *si;
1✔
1164
    else
1165
      return false;
1166
  }
1167
  return invalid ? invalid->empty() : true;
2✔
1168
}
1169

1170
Value& StreamWriterBuilder::operator[](const String& key) {
1✔
1171
  return settings_[key];
1✔
1172
}
1173
// static
1174
void StreamWriterBuilder::setDefaults(Json::Value* settings) {
255✔
1175
  //! [StreamWriterBuilderDefaults]
1176
  (*settings)["commentStyle"] = "All";
255✔
1177
  (*settings)["indentation"] = "\t";
255✔
1178
  (*settings)["enableYAMLCompatibility"] = false;
255✔
1179
  (*settings)["dropNullPlaceholders"] = false;
255✔
1180
  (*settings)["useSpecialFloats"] = false;
255✔
1181
  (*settings)["emitUTF8"] = false;
255✔
1182
  (*settings)["precision"] = 17;
255✔
1183
  (*settings)["precisionType"] = "significant";
255✔
1184
  //! [StreamWriterBuilderDefaults]
1185
}
255✔
1186

1187
String writeString(StreamWriter::Factory const& factory, Value const& root) {
657✔
1188
  OStringStream sout;
657✔
1189
  StreamWriterPtr const writer(factory.newStreamWriter());
657✔
1190
  writer->write(root, &sout);
657✔
1191
  return std::move(sout).str();
657✔
1192
}
657✔
1193

1194
OStream& operator<<(OStream& sout, Value const& root) {
3✔
1195
  StreamWriterBuilder builder;
3✔
1196
  StreamWriterPtr const writer(builder.newStreamWriter());
3✔
1197
  writer->write(root, &sout);
3✔
1198
  return sout;
3✔
1199
}
3✔
1200

1201
} // namespace Json
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc