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

open-source-parsers / jsoncpp / 10821526037

11 Sep 2024 11:59PM CUT coverage: 95.295%. Remained the same
10821526037

Pull #1566

github

baylesj
Merge branch '1.9.7' of github.com:open-source-parsers/jsoncpp into 1.9.7
Pull Request #1566: Release 1.9.6 and move versions to 1.9.7

5306 of 5568 relevant lines covered (95.29%)

11935.1 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 <cstring>
14
#include <iomanip>
15
#include <memory>
16
#include <set>
17
#include <sstream>
18
#include <utility>
19

20
#if __cplusplus >= 201103L
21
#include <cmath>
22
#include <cstdio>
23

24
#if !defined(isnan)
25
#define isnan std::isnan
26
#endif
27

28
#if !defined(isfinite)
29
#define isfinite std::isfinite
30
#endif
31

32
#else
33
#include <cmath>
34
#include <cstdio>
35

36
#if defined(_MSC_VER)
37
#if !defined(isnan)
38
#include <float.h>
39
#define isnan _isnan
40
#endif
41

42
#if !defined(isfinite)
43
#include <float.h>
44
#define isfinite _finite
45
#endif
46

47
#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
48
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
49
#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
50

51
#endif //_MSC_VER
52

53
#if defined(__sun) && defined(__SVR4) // Solaris
54
#if !defined(isfinite)
55
#include <ieeefp.h>
56
#define isfinite finite
57
#endif
58
#endif
59

60
#if defined(__hpux)
61
#if !defined(isfinite)
62
#if defined(__ia64) && !defined(finite)
63
#define isfinite(x)                                                            \
64
  ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
65
#endif
66
#endif
67
#endif
68

69
#if !defined(isnan)
70
// IEEE standard states that NaN values will not compare to themselves
71
#define isnan(x) ((x) != (x))
72
#endif
73

74
#if !defined(__APPLE__)
75
#if !defined(isfinite)
76
#define isfinite finite
77
#endif
78
#endif
79
#endif
80

81
#if defined(_MSC_VER)
82
// Disable warning about strdup being deprecated.
83
#pragma warning(disable : 4996)
84
#endif
85

86
namespace Json {
87

88
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
89
using StreamWriterPtr = std::unique_ptr<StreamWriter>;
90
#else
91
using StreamWriterPtr = std::auto_ptr<StreamWriter>;
92
#endif
93

94
String valueToString(LargestInt value) {
157,433✔
95
  UIntToStringBuffer buffer;
96
  char* current = buffer + sizeof(buffer);
97
  if (value == Value::minLargestInt) {
157,433✔
98
    uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
99
    *--current = '-';
37✔
100
  } else if (value < 0) {
157,396✔
101
    uintToString(LargestUInt(-value), current);
220✔
102
    *--current = '-';
220✔
103
  } else {
104
    uintToString(LargestUInt(value), current);
157,176✔
105
  }
106
  assert(current >= buffer);
157,433✔
107
  return current;
157,433✔
108
}
109

110
String valueToString(LargestUInt value) {
278✔
111
  UIntToStringBuffer buffer;
112
  char* current = buffer + sizeof(buffer);
113
  uintToString(value, current);
114
  assert(current >= buffer);
278✔
115
  return current;
278✔
116
}
117

118
#if defined(JSON_HAS_INT64)
119

120
String valueToString(Int value) { return valueToString(LargestInt(value)); }
×
121

122
String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
×
123

124
#endif // # if defined(JSON_HAS_INT64)
125

126
namespace {
127
String valueToString(double value, bool useSpecialFloats,
186✔
128
                     unsigned int precision, PrecisionType precisionType) {
129
  // Print into the buffer. We need not request the alternative representation
130
  // that always has a decimal point because JSON doesn't distinguish the
131
  // concepts of reals and integers.
132
  if (!isfinite(value)) {
186✔
133
    static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
134
                                           {"null", "-1e+9999", "1e+9999"}};
135
    return reps[useSpecialFloats ? 0 : 1][isnan(value)  ? 0
51✔
136
                                          : (value < 0) ? 1
26✔
137
                                                        : 2];
27✔
138
  }
139

140
  String buffer(size_t(36), '\0');
141
  while (true) {
142
    int len = jsoncpp_snprintf(
159✔
143
        &*buffer.begin(), buffer.size(),
144
        (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
145
        precision, value);
146
    assert(len >= 0);
159✔
147
    auto wouldPrint = static_cast<size_t>(len);
159✔
148
    if (wouldPrint >= buffer.size()) {
159✔
149
      buffer.resize(wouldPrint + 1);
×
150
      continue;
151
    }
152
    buffer.resize(wouldPrint);
153
    break;
154
  }
×
155

156
  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
159✔
157

158
  // try to ensure we preserve the fact that this was given to us as a double on
159
  // input
160
  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
159✔
161
    buffer += ".0";
162
  }
163

164
  // strip the zero padding from the right
165
  if (precisionType == PrecisionType::decimalPlaces) {
159✔
166
    buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
7✔
167
                 buffer.end());
168
  }
169

170
  return buffer;
171
}
172
} // namespace
173

174
String valueToString(double value, unsigned int precision,
120✔
175
                     PrecisionType precisionType) {
176
  return valueToString(value, false, precision, precisionType);
120✔
177
}
178

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

181
static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
1,637✔
182
  assert(s || !n);
1,637✔
183

184
  return std::any_of(s, s + n, [](unsigned char c) {
1,637✔
185
    return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
2,853✔
186
  });
1,637✔
187
}
188

189
static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
23,544✔
190
  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
191

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

194
  if (firstByte < 0x80)
23,544✔
195
    return firstByte;
196

197
  if (firstByte < 0xE0) {
157✔
198
    if (e - s < 2)
123✔
199
      return REPLACEMENT_CHARACTER;
200

201
    unsigned int calculated =
202
        ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
123✔
203
    s += 1;
123✔
204
    // oversized encoded characters are invalid
205
    return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
246✔
206
  }
207

208
  if (firstByte < 0xF0) {
34✔
209
    if (e - s < 3)
17✔
210
      return REPLACEMENT_CHARACTER;
211

212
    unsigned int calculated = ((firstByte & 0x0F) << 12) |
17✔
213
                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
17✔
214
                              (static_cast<unsigned int>(s[2]) & 0x3F);
17✔
215
    s += 2;
17✔
216
    // surrogates aren't valid codepoints itself
217
    // shouldn't be UTF-8 encoded
218
    if (calculated >= 0xD800 && calculated <= 0xDFFF)
17✔
219
      return REPLACEMENT_CHARACTER;
220
    // oversized encoded characters are invalid
221
    return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
34✔
222
  }
223

224
  if (firstByte < 0xF8) {
17✔
225
    if (e - s < 4)
17✔
226
      return REPLACEMENT_CHARACTER;
227

228
    unsigned int calculated = ((firstByte & 0x07) << 18) |
17✔
229
                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
17✔
230
                              ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
17✔
231
                              (static_cast<unsigned int>(s[3]) & 0x3F);
17✔
232
    s += 3;
17✔
233
    // oversized encoded characters are invalid
234
    return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
34✔
235
  }
236

237
  return REPLACEMENT_CHARACTER;
238
}
239

240
static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
241
                           "101112131415161718191a1b1c1d1e1f"
242
                           "202122232425262728292a2b2c2d2e2f"
243
                           "303132333435363738393a3b3c3d3e3f"
244
                           "404142434445464748494a4b4c4d4e4f"
245
                           "505152535455565758595a5b5c5d5e5f"
246
                           "606162636465666768696a6b6c6d6e6f"
247
                           "707172737475767778797a7b7c7d7e7f"
248
                           "808182838485868788898a8b8c8d8e8f"
249
                           "909192939495969798999a9b9c9d9e9f"
250
                           "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
251
                           "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
252
                           "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
253
                           "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
254
                           "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
255
                           "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
256

257
static String toHex16Bit(unsigned int x) {
230✔
258
  const unsigned int hi = (x >> 8) & 0xff;
230✔
259
  const unsigned int lo = x & 0xff;
230✔
260
  String result(4, ' ');
261
  result[0] = hex2[2 * hi];
230✔
262
  result[1] = hex2[2 * hi + 1];
230✔
263
  result[2] = hex2[2 * lo];
230✔
264
  result[3] = hex2[2 * lo + 1];
230✔
265
  return result;
230✔
266
}
267

268
static void appendRaw(String& result, unsigned ch) {
269
  result += static_cast<char>(ch);
23,358✔
270
}
23,500✔
271

272
static void appendHex(String& result, unsigned ch) {
230✔
273
  result.append("\\u").append(toHex16Bit(ch));
230✔
274
}
230✔
275

276
static String valueToQuotedStringN(const char* value, size_t length,
1,637✔
277
                                   bool emitUTF8 = false) {
278
  if (value == nullptr)
1,637✔
279
    return "";
×
280

281
  if (!doesAnyCharRequireEscaping(value, length))
1,637✔
282
    return String("\"") + value + "\"";
2,660✔
283
  // We have to walk value and escape any special characters.
284
  // Appending to String is not efficient, but this should be rare.
285
  // (Note: forward slashes are *not* rare, but I am not escaping them.)
286
  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
307✔
287
  String result;
288
  result.reserve(maxsize); // to avoid lots of mallocs
307✔
289
  result += "\"";
290
  char const* end = value + length;
307✔
291
  for (const char* c = value; c != end; ++c) {
25,055✔
292
    switch (*c) {
24,748✔
293
    case '\"':
294
      result += "\\\"";
295
      break;
296
    case '\\':
297
      result += "\\\\";
298
      break;
299
    case '\b':
300
      result += "\\b";
301
      break;
302
    case '\f':
303
      result += "\\f";
304
      break;
305
    case '\n':
306
      result += "\\n";
307
      break;
308
    case '\r':
309
      result += "\\r";
310
      break;
311
    case '\t':
312
      result += "\\t";
313
      break;
314
    // case '/':
315
    // Even though \/ is considered a legal escape in JSON, a bare
316
    // slash is also legal, so I see no reason to escape it.
317
    // (I hope I am not misunderstanding something.)
318
    // blep notes: actually escaping \/ may be useful in javascript to avoid </
319
    // sequence.
320
    // Should add a flag to allow this compatibility mode and prevent this
321
    // sequence from occurring.
322
    default: {
23,713✔
323
      if (emitUTF8) {
23,713✔
324
        unsigned codepoint = static_cast<unsigned char>(*c);
169✔
325
        if (codepoint < 0x20) {
169✔
326
          appendHex(result, codepoint);
27✔
327
        } else {
328
          appendRaw(result, codepoint);
329
        }
330
      } else {
331
        unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
23,544✔
332
        if (codepoint < 0x20) {
23,544✔
333
          appendHex(result, codepoint);
29✔
334
        } else if (codepoint < 0x80) {
23,515✔
335
          appendRaw(result, codepoint);
336
        } else if (codepoint < 0x10000) {
157✔
337
          // Basic Multilingual Plane
338
          appendHex(result, codepoint);
140✔
339
        } else {
340
          // Extended Unicode. Encode 20 bits as a surrogate pair.
341
          codepoint -= 0x10000;
17✔
342
          appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
17✔
343
          appendHex(result, 0xdc00 + (codepoint & 0x3ff));
17✔
344
        }
345
      }
346
    } break;
347
    }
348
  }
349
  result += "\"";
350
  return result;
351
}
352

353
String valueToQuotedString(const char* value) {
×
354
  return valueToQuotedStringN(value, strlen(value));
×
355
}
356

357
String valueToQuotedString(const char* value, size_t length) {
292✔
358
  return valueToQuotedStringN(value, length);
292✔
359
}
360

361
// Class Writer
362
// //////////////////////////////////////////////////////////////////
363
Writer::~Writer() = default;
490✔
364

365
// Class FastWriter
366
// //////////////////////////////////////////////////////////////////
367

368
FastWriter::FastWriter()
8✔
369

370
    = default;
371

372
void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
1✔
373

374
void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
1✔
375

376
void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
1✔
377

378
String FastWriter::write(const Value& root) {
13✔
379
  document_.clear();
13✔
380
  writeValue(root);
13✔
381
  if (!omitEndingLineFeed_)
13✔
382
    document_ += '\n';
383
  return document_;
13✔
384
}
385

386
void FastWriter::writeValue(const Value& value) {
55✔
387
  switch (value.type()) {
55✔
388
  case nullValue:
7✔
389
    if (!dropNullPlaceholders_)
7✔
390
      document_ += "null";
6✔
391
    break;
392
  case intValue:
7✔
393
    document_ += valueToString(value.asLargestInt());
7✔
394
    break;
7✔
395
  case uintValue:
1✔
396
    document_ += valueToString(value.asLargestUInt());
1✔
397
    break;
1✔
398
  case realValue:
2✔
399
    document_ += valueToString(value.asDouble());
2✔
400
    break;
2✔
401
  case stringValue: {
17✔
402
    // Is NULL possible for value.string_? No.
403
    char const* str;
404
    char const* end;
405
    bool ok = value.getString(&str, &end);
17✔
406
    if (ok)
17✔
407
      document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
34✔
408
    break;
409
  }
410
  case booleanValue:
2✔
411
    document_ += valueToString(value.asBool());
2✔
412
    break;
2✔
413
  case arrayValue: {
6✔
414
    document_ += '[';
6✔
415
    ArrayIndex size = value.size();
6✔
416
    for (ArrayIndex index = 0; index < size; ++index) {
24✔
417
      if (index > 0)
18✔
418
        document_ += ',';
419
      writeValue(value[index]);
18✔
420
    }
421
    document_ += ']';
422
  } break;
423
  case objectValue: {
13✔
424
    Value::Members members(value.getMemberNames());
13✔
425
    document_ += '{';
13✔
426
    for (auto it = members.begin(); it != members.end(); ++it) {
37✔
427
      const String& name = *it;
428
      if (it != members.begin())
24✔
429
        document_ += ',';
430
      document_ += valueToQuotedStringN(name.data(), name.length());
24✔
431
      document_ += yamlCompatibilityEnabled_ ? ": " : ":";
24✔
432
      writeValue(value[name]);
24✔
433
    }
434
    document_ += '}';
435
  } break;
13✔
436
  }
437
}
55✔
438

439
// Class StyledWriter
440
// //////////////////////////////////////////////////////////////////
441

442
StyledWriter::StyledWriter() = default;
237✔
443

444
String StyledWriter::write(const Value& root) {
240✔
445
  document_.clear();
240✔
446
  addChildValues_ = false;
240✔
447
  indentString_.clear();
448
  writeCommentBeforeValue(root);
240✔
449
  writeValue(root);
240✔
450
  writeCommentAfterValueOnSameLine(root);
240✔
451
  document_ += '\n';
452
  return document_;
240✔
453
}
454

455
void StyledWriter::writeValue(const Value& value) {
17,893✔
456
  switch (value.type()) {
17,893✔
457
  case nullValue:
458
    pushValue("null");
17✔
459
    break;
17✔
460
  case intValue:
17,491✔
461
    pushValue(valueToString(value.asLargestInt()));
17,491✔
462
    break;
17,491✔
463
  case uintValue:
46✔
464
    pushValue(valueToString(value.asLargestUInt()));
46✔
465
    break;
46✔
466
  case realValue:
50✔
467
    pushValue(valueToString(value.asDouble()));
50✔
468
    break;
50✔
469
  case stringValue: {
122✔
470
    // Is NULL possible for value.string_? No.
471
    char const* str;
472
    char const* end;
473
    bool ok = value.getString(&str, &end);
122✔
474
    if (ok)
122✔
475
      pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
244✔
476
    else
477
      pushValue("");
×
478
    break;
479
  }
480
  case booleanValue:
18✔
481
    pushValue(valueToString(value.asBool()));
18✔
482
    break;
18✔
483
  case arrayValue:
66✔
484
    writeArrayValue(value);
66✔
485
    break;
66✔
486
  case objectValue: {
83✔
487
    Value::Members members(value.getMemberNames());
83✔
488
    if (members.empty())
83✔
489
      pushValue("{}");
10✔
490
    else {
491
      writeWithIndent("{");
78✔
492
      indent();
78✔
493
      auto it = members.begin();
494
      for (;;) {
495
        const String& name = *it;
496
        const Value& childValue = value[name];
146✔
497
        writeCommentBeforeValue(childValue);
146✔
498
        writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
146✔
499
        document_ += " : ";
146✔
500
        writeValue(childValue);
146✔
501
        if (++it == members.end()) {
146✔
502
          writeCommentAfterValueOnSameLine(childValue);
78✔
503
          break;
504
        }
505
        document_ += ',';
506
        writeCommentAfterValueOnSameLine(childValue);
68✔
507
      }
508
      unindent();
78✔
509
      writeWithIndent("}");
156✔
510
    }
511
  } break;
83✔
512
  }
513
}
17,893✔
514

515
void StyledWriter::writeArrayValue(const Value& value) {
66✔
516
  size_t size = value.size();
66✔
517
  if (size == 0)
66✔
518
    pushValue("[]");
10✔
519
  else {
520
    bool isArrayMultiLine = isMultilineArray(value);
61✔
521
    if (isArrayMultiLine) {
61✔
522
      writeWithIndent("[");
37✔
523
      indent();
37✔
524
      bool hasChildValue = !childValues_.empty();
525
      ArrayIndex index = 0;
526
      for (;;) {
527
        const Value& childValue = value[index];
17,437✔
528
        writeCommentBeforeValue(childValue);
17,437✔
529
        if (hasChildValue)
17,437✔
530
          writeWithIndent(childValues_[index]);
41✔
531
        else {
532
          writeIndent();
17,396✔
533
          writeValue(childValue);
17,396✔
534
        }
535
        if (++index == size) {
17,437✔
536
          writeCommentAfterValueOnSameLine(childValue);
37✔
537
          break;
538
        }
539
        document_ += ',';
17,400✔
540
        writeCommentAfterValueOnSameLine(childValue);
17,400✔
541
      }
17,400✔
542
      unindent();
37✔
543
      writeWithIndent("]");
74✔
544
    } else // output on a single line
545
    {
546
      assert(childValues_.size() == size);
24✔
547
      document_ += "[ ";
24✔
548
      for (size_t index = 0; index < size; ++index) {
94✔
549
        if (index > 0)
70✔
550
          document_ += ", ";
551
        document_ += childValues_[index];
70✔
552
      }
553
      document_ += " ]";
554
    }
555
  }
556
}
66✔
557

558
bool StyledWriter::isMultilineArray(const Value& value) {
61✔
559
  ArrayIndex const size = value.size();
61✔
560
  bool isMultiLine = size * 3 >= rightMargin_;
61✔
561
  childValues_.clear();
61✔
562
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
204✔
563
    const Value& childValue = value[index];
143✔
564
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
159✔
565
                   !childValue.empty());
16✔
566
  }
567
  if (!isMultiLine) // check if line length > max line length
61✔
568
  {
569
    childValues_.reserve(size);
33✔
570
    addChildValues_ = true;
33✔
571
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
33✔
572
    for (ArrayIndex index = 0; index < size; ++index) {
144✔
573
      if (hasCommentForValue(value[index])) {
111✔
574
        isMultiLine = true;
575
      }
576
      writeValue(value[index]);
111✔
577
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
111✔
578
    }
579
    addChildValues_ = false;
33✔
580
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
33✔
581
  }
582
  return isMultiLine;
61✔
583
}
584

585
void StyledWriter::pushValue(const String& value) {
17,754✔
586
  if (addChildValues_)
17,754✔
587
    childValues_.push_back(value);
111✔
588
  else
589
    document_ += value;
17,643✔
590
}
17,754✔
591

592
void StyledWriter::writeIndent() {
17,982✔
593
  if (!document_.empty()) {
17,982✔
594
    char last = document_[document_.length() - 1];
17,936✔
595
    if (last == ' ') // already indented
17,936✔
596
      return;
597
    if (last != '\n') // Comments may add new-line
17,875✔
598
      document_ += '\n';
17,646✔
599
  }
600
  document_ += indentString_;
17,921✔
601
}
602

603
void StyledWriter::writeWithIndent(const String& value) {
417✔
604
  writeIndent();
417✔
605
  document_ += value;
417✔
606
}
417✔
607

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

610
void StyledWriter::unindent() {
115✔
611
  assert(indentString_.size() >= indentSize_);
115✔
612
  indentString_.resize(indentString_.size() - indentSize_);
115✔
613
}
115✔
614

615
void StyledWriter::writeCommentBeforeValue(const Value& root) {
17,823✔
616
  if (!root.hasComment(commentBefore))
17,823✔
617
    return;
17,690✔
618

619
  document_ += '\n';
133✔
620
  writeIndent();
133✔
621
  const String& comment = root.getComment(commentBefore);
133✔
622
  String::const_iterator iter = comment.begin();
623
  while (iter != comment.end()) {
6,481✔
624
    document_ += *iter;
6,348✔
625
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,348✔
626
      writeIndent();
36✔
627
    ++iter;
628
  }
629

630
  // Comments are stripped of trailing newlines, so add one here
631
  document_ += '\n';
632
}
633

634
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
17,823✔
635
  if (root.hasComment(commentAfterOnSameLine))
17,823✔
636
    document_ += " " + root.getComment(commentAfterOnSameLine);
34✔
637

638
  if (root.hasComment(commentAfter)) {
17,823✔
639
    document_ += '\n';
5✔
640
    document_ += root.getComment(commentAfter);
10✔
641
    document_ += '\n';
642
  }
643
}
17,823✔
644

645
bool StyledWriter::hasCommentForValue(const Value& value) {
111✔
646
  return value.hasComment(commentBefore) ||
218✔
647
         value.hasComment(commentAfterOnSameLine) ||
218✔
648
         value.hasComment(commentAfter);
107✔
649
}
650

651
// Class StyledStreamWriter
652
// //////////////////////////////////////////////////////////////////
653

654
StyledStreamWriter::StyledStreamWriter(String indentation)
238✔
655
    : document_(nullptr), indentation_(std::move(indentation)),
238✔
656
      addChildValues_(), indented_(false) {}
238✔
657

658
void StyledStreamWriter::write(OStream& out, const Value& root) {
240✔
659
  document_ = &out;
240✔
660
  addChildValues_ = false;
240✔
661
  indentString_.clear();
662
  indented_ = true;
240✔
663
  writeCommentBeforeValue(root);
240✔
664
  if (!indented_)
240✔
665
    writeIndent();
81✔
666
  indented_ = true;
240✔
667
  writeValue(root);
240✔
668
  writeCommentAfterValueOnSameLine(root);
240✔
669
  *document_ << "\n";
240✔
670
  document_ = nullptr; // Forget the stream, for safety.
240✔
671
}
240✔
672

673
void StyledStreamWriter::writeValue(const Value& value) {
17,893✔
674
  switch (value.type()) {
17,893✔
675
  case nullValue:
676
    pushValue("null");
17✔
677
    break;
17✔
678
  case intValue:
17,491✔
679
    pushValue(valueToString(value.asLargestInt()));
17,491✔
680
    break;
17,491✔
681
  case uintValue:
46✔
682
    pushValue(valueToString(value.asLargestUInt()));
46✔
683
    break;
46✔
684
  case realValue:
50✔
685
    pushValue(valueToString(value.asDouble()));
50✔
686
    break;
50✔
687
  case stringValue: {
122✔
688
    // Is NULL possible for value.string_? No.
689
    char const* str;
690
    char const* end;
691
    bool ok = value.getString(&str, &end);
122✔
692
    if (ok)
122✔
693
      pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
244✔
694
    else
695
      pushValue("");
×
696
    break;
697
  }
698
  case booleanValue:
18✔
699
    pushValue(valueToString(value.asBool()));
18✔
700
    break;
18✔
701
  case arrayValue:
66✔
702
    writeArrayValue(value);
66✔
703
    break;
66✔
704
  case objectValue: {
83✔
705
    Value::Members members(value.getMemberNames());
83✔
706
    if (members.empty())
83✔
707
      pushValue("{}");
10✔
708
    else {
709
      writeWithIndent("{");
78✔
710
      indent();
78✔
711
      auto it = members.begin();
712
      for (;;) {
713
        const String& name = *it;
714
        const Value& childValue = value[name];
146✔
715
        writeCommentBeforeValue(childValue);
146✔
716
        writeWithIndent(valueToQuotedString(name.c_str(), name.size()));
146✔
717
        *document_ << " : ";
146✔
718
        writeValue(childValue);
146✔
719
        if (++it == members.end()) {
146✔
720
          writeCommentAfterValueOnSameLine(childValue);
78✔
721
          break;
722
        }
723
        *document_ << ",";
68✔
724
        writeCommentAfterValueOnSameLine(childValue);
68✔
725
      }
726
      unindent();
78✔
727
      writeWithIndent("}");
156✔
728
    }
729
  } break;
83✔
730
  }
731
}
17,893✔
732

733
void StyledStreamWriter::writeArrayValue(const Value& value) {
66✔
734
  unsigned size = value.size();
66✔
735
  if (size == 0)
66✔
736
    pushValue("[]");
10✔
737
  else {
738
    bool isArrayMultiLine = isMultilineArray(value);
61✔
739
    if (isArrayMultiLine) {
61✔
740
      writeWithIndent("[");
37✔
741
      indent();
37✔
742
      bool hasChildValue = !childValues_.empty();
743
      unsigned index = 0;
744
      for (;;) {
745
        const Value& childValue = value[index];
17,437✔
746
        writeCommentBeforeValue(childValue);
17,437✔
747
        if (hasChildValue)
17,437✔
748
          writeWithIndent(childValues_[index]);
41✔
749
        else {
750
          if (!indented_)
17,396✔
751
            writeIndent();
17,396✔
752
          indented_ = true;
17,396✔
753
          writeValue(childValue);
17,396✔
754
          indented_ = false;
17,396✔
755
        }
756
        if (++index == size) {
17,437✔
757
          writeCommentAfterValueOnSameLine(childValue);
37✔
758
          break;
759
        }
760
        *document_ << ",";
17,400✔
761
        writeCommentAfterValueOnSameLine(childValue);
17,400✔
762
      }
17,400✔
763
      unindent();
37✔
764
      writeWithIndent("]");
74✔
765
    } else // output on a single line
766
    {
767
      assert(childValues_.size() == size);
24✔
768
      *document_ << "[ ";
24✔
769
      for (unsigned index = 0; index < size; ++index) {
94✔
770
        if (index > 0)
70✔
771
          *document_ << ", ";
46✔
772
        *document_ << childValues_[index];
70✔
773
      }
774
      *document_ << " ]";
24✔
775
    }
776
  }
777
}
66✔
778

779
bool StyledStreamWriter::isMultilineArray(const Value& value) {
61✔
780
  ArrayIndex const size = value.size();
61✔
781
  bool isMultiLine = size * 3 >= rightMargin_;
61✔
782
  childValues_.clear();
61✔
783
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
204✔
784
    const Value& childValue = value[index];
143✔
785
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
159✔
786
                   !childValue.empty());
16✔
787
  }
788
  if (!isMultiLine) // check if line length > max line length
61✔
789
  {
790
    childValues_.reserve(size);
33✔
791
    addChildValues_ = true;
33✔
792
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
33✔
793
    for (ArrayIndex index = 0; index < size; ++index) {
144✔
794
      if (hasCommentForValue(value[index])) {
111✔
795
        isMultiLine = true;
796
      }
797
      writeValue(value[index]);
111✔
798
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
111✔
799
    }
800
    addChildValues_ = false;
33✔
801
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
33✔
802
  }
803
  return isMultiLine;
61✔
804
}
805

806
void StyledStreamWriter::pushValue(const String& value) {
17,754✔
807
  if (addChildValues_)
17,754✔
808
    childValues_.push_back(value);
111✔
809
  else
810
    *document_ << value;
17,643✔
811
}
17,754✔
812

813
void StyledStreamWriter::writeIndent() {
17,873✔
814
  // blep intended this to look at the so-far-written string
815
  // to determine whether we are already indented, but
816
  // with a stream we cannot do that. So we rely on some saved state.
817
  // The caller checks indented_.
818
  *document_ << '\n' << indentString_;
17,873✔
819
}
17,873✔
820

821
void StyledStreamWriter::writeWithIndent(const String& value) {
417✔
822
  if (!indented_)
417✔
823
    writeIndent();
339✔
824
  *document_ << value;
417✔
825
  indented_ = false;
417✔
826
}
417✔
827

828
void StyledStreamWriter::indent() { indentString_ += indentation_; }
115✔
829

830
void StyledStreamWriter::unindent() {
115✔
831
  assert(indentString_.size() >= indentation_.size());
115✔
832
  indentString_.resize(indentString_.size() - indentation_.size());
115✔
833
}
115✔
834

835
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
17,823✔
836
  if (!root.hasComment(commentBefore))
17,823✔
837
    return;
17,690✔
838

839
  if (!indented_)
133✔
840
    writeIndent();
52✔
841
  const String& comment = root.getComment(commentBefore);
133✔
842
  String::const_iterator iter = comment.begin();
843
  while (iter != comment.end()) {
6,481✔
844
    *document_ << *iter;
6,348✔
845
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,348✔
846
      // writeIndent();  // would include newline
847
      *document_ << indentString_;
36✔
848
    ++iter;
849
  }
850
  indented_ = false;
133✔
851
}
852

853
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
17,823✔
854
  if (root.hasComment(commentAfterOnSameLine))
17,823✔
855
    *document_ << ' ' << root.getComment(commentAfterOnSameLine);
34✔
856

857
  if (root.hasComment(commentAfter)) {
17,823✔
858
    writeIndent();
5✔
859
    *document_ << root.getComment(commentAfter);
10✔
860
  }
861
  indented_ = false;
17,823✔
862
}
17,823✔
863

864
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
111✔
865
  return value.hasComment(commentBefore) ||
218✔
866
         value.hasComment(commentAfterOnSameLine) ||
218✔
867
         value.hasComment(commentAfter);
107✔
868
}
869

870
//////////////////////////
871
// BuiltStyledStreamWriter
872

873
/// Scoped enums are not available until C++11.
874
struct CommentStyle {
875
  /// Decide whether to write comments.
876
  enum Enum {
877
    None, ///< Drop all comments.
878
    Most, ///< Recover odd behavior of previous versions (not implemented yet).
879
    All   ///< Keep all comments.
880
  };
881
};
882

883
struct BuiltStyledStreamWriter : public StreamWriter {
884
  BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
885
                          String colonSymbol, String nullSymbol,
886
                          String endingLineFeedSymbol, bool useSpecialFloats,
887
                          bool emitUTF8, unsigned int precision,
888
                          PrecisionType precisionType);
889
  int write(Value const& root, OStream* sout) override;
890

891
private:
892
  void writeValue(Value const& value);
893
  void writeArrayValue(Value const& value);
894
  bool isMultilineArray(Value const& value);
895
  void pushValue(String const& value);
896
  void writeIndent();
897
  void writeWithIndent(String const& value);
898
  void indent();
899
  void unindent();
900
  void writeCommentBeforeValue(Value const& root);
901
  void writeCommentAfterValueOnSameLine(Value const& root);
902
  static bool hasCommentForValue(const Value& value);
903

904
  using ChildValues = std::vector<String>;
905

906
  ChildValues childValues_;
907
  String indentString_;
908
  unsigned int rightMargin_;
909
  String indentation_;
910
  CommentStyle::Enum cs_;
911
  String colonSymbol_;
912
  String nullSymbol_;
913
  String endingLineFeedSymbol_;
914
  bool addChildValues_ : 1;
915
  bool indented_ : 1;
916
  bool useSpecialFloats_ : 1;
917
  bool emitUTF8_ : 1;
918
  unsigned int precision_;
919
  PrecisionType precisionType_;
920
};
921
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
660✔
922
    String indentation, CommentStyle::Enum cs, String colonSymbol,
923
    String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
924
    bool emitUTF8, unsigned int precision, PrecisionType precisionType)
660✔
925
    : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
1,320✔
926
      colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
927
      endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
928
      addChildValues_(false), indented_(false),
660✔
929
      useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
660✔
930
      precision_(precision), precisionType_(precisionType) {}
660✔
931
int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
660✔
932
  sout_ = sout;
660✔
933
  addChildValues_ = false;
660✔
934
  indented_ = true;
660✔
935
  indentString_.clear();
936
  writeCommentBeforeValue(root);
660✔
937
  if (!indented_)
660✔
938
    writeIndent();
84✔
939
  indented_ = true;
660✔
940
  writeValue(root);
660✔
941
  writeCommentAfterValueOnSameLine(root);
660✔
942
  *sout_ << endingLineFeedSymbol_;
660✔
943
  sout_ = nullptr;
660✔
944
  return 0;
660✔
945
}
946
void BuiltStyledStreamWriter::writeValue(Value const& value) {
18,706✔
947
  switch (value.type()) {
18,706✔
948
  case nullValue:
22✔
949
    pushValue(nullSymbol_);
22✔
950
    break;
22✔
951
  case intValue:
17,491✔
952
    pushValue(valueToString(value.asLargestInt()));
17,491✔
953
    break;
17,491✔
954
  case uintValue:
46✔
955
    pushValue(valueToString(value.asLargestUInt()));
46✔
956
    break;
46✔
957
  case realValue:
66✔
958
    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
66✔
959
                            precisionType_));
960
    break;
66✔
961
  case stringValue: {
521✔
962
    // Is NULL is possible for value.string_? No.
963
    char const* str;
964
    char const* end;
965
    bool ok = value.getString(&str, &end);
521✔
966
    if (ok)
521✔
967
      pushValue(
521✔
968
          valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
1,042✔
969
    else
970
      pushValue("");
×
971
    break;
972
  }
973
  case booleanValue:
18✔
974
    pushValue(valueToString(value.asBool()));
18✔
975
    break;
18✔
976
  case arrayValue:
66✔
977
    writeArrayValue(value);
66✔
978
    break;
66✔
979
  case objectValue: {
476✔
980
    Value::Members members(value.getMemberNames());
476✔
981
    if (members.empty())
476✔
982
      pushValue("{}");
10✔
983
    else {
984
      writeWithIndent("{");
471✔
985
      indent();
471✔
986
      auto it = members.begin();
987
      for (;;) {
988
        String const& name = *it;
989
        Value const& childValue = value[name];
539✔
990
        writeCommentBeforeValue(childValue);
539✔
991
        writeWithIndent(
539✔
992
            valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
539✔
993
        *sout_ << colonSymbol_;
539✔
994
        writeValue(childValue);
539✔
995
        if (++it == members.end()) {
539✔
996
          writeCommentAfterValueOnSameLine(childValue);
471✔
997
          break;
998
        }
999
        *sout_ << ",";
68✔
1000
        writeCommentAfterValueOnSameLine(childValue);
68✔
1001
      }
1002
      unindent();
471✔
1003
      writeWithIndent("}");
942✔
1004
    }
1005
  } break;
476✔
1006
  }
1007
}
18,706✔
1008

1009
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
66✔
1010
  unsigned size = value.size();
66✔
1011
  if (size == 0)
66✔
1012
    pushValue("[]");
10✔
1013
  else {
1014
    bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
61✔
1015
    if (isMultiLine) {
1016
      writeWithIndent("[");
60✔
1017
      indent();
60✔
1018
      bool hasChildValue = !childValues_.empty();
1019
      unsigned index = 0;
1020
      for (;;) {
1021
        Value const& childValue = value[index];
17,497✔
1022
        writeCommentBeforeValue(childValue);
17,497✔
1023
        if (hasChildValue)
17,497✔
1024
          writeWithIndent(childValues_[index]);
21✔
1025
        else {
1026
          if (!indented_)
17,476✔
1027
            writeIndent();
17,476✔
1028
          indented_ = true;
17,476✔
1029
          writeValue(childValue);
17,476✔
1030
          indented_ = false;
17,476✔
1031
        }
1032
        if (++index == size) {
17,497✔
1033
          writeCommentAfterValueOnSameLine(childValue);
60✔
1034
          break;
1035
        }
1036
        *sout_ << ",";
17,437✔
1037
        writeCommentAfterValueOnSameLine(childValue);
17,437✔
1038
      }
17,437✔
1039
      unindent();
60✔
1040
      writeWithIndent("]");
120✔
1041
    } else // output on a single line
1042
    {
1043
      assert(childValues_.size() == size);
1✔
1044
      *sout_ << "[";
1✔
1045
      if (!indentation_.empty())
1✔
1046
        *sout_ << " ";
1✔
1047
      for (unsigned index = 0; index < size; ++index) {
11✔
1048
        if (index > 0)
10✔
1049
          *sout_ << ((!indentation_.empty()) ? ", " : ",");
9✔
1050
        *sout_ << childValues_[index];
10✔
1051
      }
1052
      if (!indentation_.empty())
1✔
1053
        *sout_ << " ";
1✔
1054
      *sout_ << "]";
1✔
1055
    }
1056
  }
1057
}
66✔
1058

1059
bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
2✔
1060
  ArrayIndex const size = value.size();
2✔
1061
  bool isMultiLine = size * 3 >= rightMargin_;
2✔
1062
  childValues_.clear();
2✔
1063
  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
33✔
1064
    Value const& childValue = value[index];
31✔
1065
    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
31✔
1066
                   !childValue.empty());
×
1067
  }
1068
  if (!isMultiLine) // check if line length > max line length
2✔
1069
  {
1070
    childValues_.reserve(size);
2✔
1071
    addChildValues_ = true;
2✔
1072
    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
2✔
1073
    for (ArrayIndex index = 0; index < size; ++index) {
33✔
1074
      if (hasCommentForValue(value[index])) {
31✔
1075
        isMultiLine = true;
1076
      }
1077
      writeValue(value[index]);
31✔
1078
      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
31✔
1079
    }
1080
    addChildValues_ = false;
2✔
1081
    isMultiLine = isMultiLine || lineLength >= rightMargin_;
2✔
1082
  }
1083
  return isMultiLine;
2✔
1084
}
1085

1086
void BuiltStyledStreamWriter::pushValue(String const& value) {
18,174✔
1087
  if (addChildValues_)
18,174✔
1088
    childValues_.push_back(value);
31✔
1089
  else
1090
    *sout_ << value;
18,143✔
1091
}
18,174✔
1092

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

1099
  if (!indentation_.empty()) {
18,749✔
1100
    // In this case, drop newlines too.
1101
    *sout_ << '\n' << indentString_;
18,741✔
1102
  }
1103
}
18,749✔
1104

1105
void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
1,622✔
1106
  if (!indented_)
1,622✔
1107
    writeIndent();
1,133✔
1108
  *sout_ << value;
1,622✔
1109
  indented_ = false;
1,622✔
1110
}
1,622✔
1111

1112
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
531✔
1113

1114
void BuiltStyledStreamWriter::unindent() {
531✔
1115
  assert(indentString_.size() >= indentation_.size());
531✔
1116
  indentString_.resize(indentString_.size() - indentation_.size());
531✔
1117
}
531✔
1118

1119
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
18,696✔
1120
  if (cs_ == CommentStyle::None)
18,696✔
1121
    return;
18,560✔
1122
  if (!root.hasComment(commentBefore))
18,673✔
1123
    return;
1124

1125
  if (!indented_)
136✔
1126
    writeIndent();
52✔
1127
  const String& comment = root.getComment(commentBefore);
136✔
1128
  String::const_iterator iter = comment.begin();
1129
  while (iter != comment.end()) {
6,608✔
1130
    *sout_ << *iter;
6,472✔
1131
    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
6,472✔
1132
      // writeIndent();  // would write extra newline
1133
      *sout_ << indentString_;
36✔
1134
    ++iter;
1135
  }
1136
  indented_ = false;
136✔
1137
}
1138

1139
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
18,696✔
1140
    Value const& root) {
1141
  if (cs_ == CommentStyle::None)
18,696✔
1142
    return;
1143
  if (root.hasComment(commentAfterOnSameLine))
18,673✔
1144
    *sout_ << " " + root.getComment(commentAfterOnSameLine);
32✔
1145

1146
  if (root.hasComment(commentAfter)) {
18,673✔
1147
    writeIndent();
4✔
1148
    *sout_ << root.getComment(commentAfter);
8✔
1149
  }
1150
}
1151

1152
// static
1153
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
31✔
1154
  return value.hasComment(commentBefore) ||
62✔
1155
         value.hasComment(commentAfterOnSameLine) ||
62✔
1156
         value.hasComment(commentAfter);
31✔
1157
}
1158

1159
///////////////
1160
// StreamWriter
1161

1162
StreamWriter::StreamWriter() : sout_(nullptr) {}
660✔
1163
StreamWriter::~StreamWriter() = default;
1,320✔
1164
StreamWriter::Factory::~Factory() = default;
510✔
1165
StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
255✔
1166
StreamWriterBuilder::~StreamWriterBuilder() = default;
510✔
1167
StreamWriter* StreamWriterBuilder::newStreamWriter() const {
660✔
1168
  const String indentation = settings_["indentation"].asString();
660✔
1169
  const String cs_str = settings_["commentStyle"].asString();
660✔
1170
  const String pt_str = settings_["precisionType"].asString();
660✔
1171
  const bool eyc = settings_["enableYAMLCompatibility"].asBool();
660✔
1172
  const bool dnp = settings_["dropNullPlaceholders"].asBool();
660✔
1173
  const bool usf = settings_["useSpecialFloats"].asBool();
660✔
1174
  const bool emitUTF8 = settings_["emitUTF8"].asBool();
660✔
1175
  unsigned int pre = settings_["precision"].asUInt();
660✔
1176
  CommentStyle::Enum cs = CommentStyle::All;
1177
  if (cs_str == "All") {
660✔
1178
    cs = CommentStyle::All;
1179
  } else if (cs_str == "None") {
2✔
1180
    cs = CommentStyle::None;
1181
  } else {
1182
    throwRuntimeError("commentStyle must be 'All' or 'None'");
×
1183
  }
1184
  PrecisionType precisionType(significantDigits);
1185
  if (pt_str == "significant") {
660✔
1186
    precisionType = PrecisionType::significantDigits;
1187
  } else if (pt_str == "decimal") {
7✔
1188
    precisionType = PrecisionType::decimalPlaces;
1189
  } else {
1190
    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
×
1191
  }
1192
  String colonSymbol = " : ";
660✔
1193
  if (eyc) {
660✔
1194
    colonSymbol = ": ";
1195
  } else if (indentation.empty()) {
659✔
1196
    colonSymbol = ":";
1197
  }
1198
  String nullSymbol = "null";
660✔
1199
  if (dnp) {
660✔
1200
    nullSymbol.clear();
1201
  }
1202
  if (pre > 17)
1203
    pre = 17;
1204
  String endingLineFeedSymbol;
1205
  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
1206
                                     endingLineFeedSymbol, usf, emitUTF8, pre,
1207
                                     precisionType);
1,980✔
1208
}
1209

1210
bool StreamWriterBuilder::validate(Json::Value* invalid) const {
2✔
1211
  static const auto& valid_keys = *new std::set<String>{
1212
      "indentation",
1213
      "commentStyle",
1214
      "enableYAMLCompatibility",
1215
      "dropNullPlaceholders",
1216
      "useSpecialFloats",
1217
      "emitUTF8",
1218
      "precision",
1219
      "precisionType",
1220
  };
10✔
1221
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
38✔
1222
    auto key = si.name();
17✔
1223
    if (valid_keys.count(key))
17✔
1224
      continue;
1225
    if (invalid)
1✔
1226
      (*invalid)[key] = *si;
1✔
1227
    else
1228
      return false;
1229
  }
1230
  return invalid ? invalid->empty() : true;
2✔
1231
}
1232

1233
Value& StreamWriterBuilder::operator[](const String& key) {
1✔
1234
  return settings_[key];
1✔
1235
}
1236
// static
1237
void StreamWriterBuilder::setDefaults(Json::Value* settings) {
255✔
1238
  //! [StreamWriterBuilderDefaults]
1239
  (*settings)["commentStyle"] = "All";
255✔
1240
  (*settings)["indentation"] = "\t";
255✔
1241
  (*settings)["enableYAMLCompatibility"] = false;
255✔
1242
  (*settings)["dropNullPlaceholders"] = false;
255✔
1243
  (*settings)["useSpecialFloats"] = false;
255✔
1244
  (*settings)["emitUTF8"] = false;
255✔
1245
  (*settings)["precision"] = 17;
255✔
1246
  (*settings)["precisionType"] = "significant";
255✔
1247
  //! [StreamWriterBuilderDefaults]
1248
}
255✔
1249

1250
String writeString(StreamWriter::Factory const& factory, Value const& root) {
657✔
1251
  OStringStream sout;
657✔
1252
  StreamWriterPtr const writer(factory.newStreamWriter());
657✔
1253
  writer->write(root, &sout);
657✔
1254
  return std::move(sout).str();
657✔
1255
}
657✔
1256

1257
OStream& operator<<(OStream& sout, Value const& root) {
3✔
1258
  StreamWriterBuilder builder;
3✔
1259
  StreamWriterPtr const writer(builder.newStreamWriter());
3✔
1260
  writer->write(root, &sout);
3✔
1261
  return sout;
3✔
1262
}
3✔
1263

1264
} // 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