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

open-source-parsers / jsoncpp / 27651904452

16 Jun 2026 10:19PM UTC coverage: 89.914% (-0.05%) from 89.959%
27651904452

Pull #1695

github

web-flow
Merge branch 'master' into fix/1427-subnormal-roundtrip
Pull Request #1695: fix: accept subnormal and underflowing doubles when parsing (#1427)

2213 of 2630 branches covered (84.14%)

Branch coverage included in aggregate %.

3 of 4 new or added lines in 1 file covered. (75.0%)

37 existing lines in 1 file now uncovered.

2610 of 2734 relevant lines covered (95.46%)

23796.29 hits per line

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

89.91
/src/lib_json/json_reader.cpp
1
// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
2
// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
3
// Distributed under MIT license, or public domain if desired and
4
// recognized in your jurisdiction.
5
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
6

7
#if !defined(JSON_IS_AMALGAMATION)
8
#include "json_tool.h"
9
#include <json/assertions.h>
10
#include <json/reader.h>
11
#include <json/value.h>
12
#endif // if !defined(JSON_IS_AMALGAMATION)
13
#include <algorithm>
14
#include <cassert>
15
#include <cerrno>
16
#include <cmath>
17
#include <cstring>
18
#include <iostream>
19
#include <istream>
20
#include <iterator>
21
#include <limits>
22
#include <memory>
23
#include <set>
24
#include <sstream>
25
#include <utility>
26

27
#include <cstdio>
28

29
#if defined(_MSC_VER)
30
#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
31
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
32
#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
33
#endif //_MSC_VER
34

35
#if defined(_MSC_VER)
36
// Disable warning about strdup being deprecated.
37
#pragma warning(disable : 4996)
38
#endif
39

40
// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
41
// time to change the stack limit
42
#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
43
#define JSONCPP_DEPRECATED_STACK_LIMIT 256
44
#endif
45

46
static size_t const stackLimit_g =
47
    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
48

49
namespace Json {
50

51
using CharReaderPtr = std::unique_ptr<CharReader>;
52

53
// Implementation of class Features
54
// ////////////////////////////////
55

56
Features::Features() = default;
614✔
57

58
Features Features::all() { return {}; }
16✔
59

60
Features Features::strictMode() {
100✔
61
  Features features;
100✔
62
  features.allowComments_ = false;
100✔
63
  features.strictRoot_ = true;
100✔
64
  features.allowDroppedNullPlaceholders_ = false;
100✔
65
  features.allowNumericKeys_ = false;
100✔
66
  return features;
100✔
67
}
68

69
// Implementation of class Reader
70
// ////////////////////////////////
71

72
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
184✔
73
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
347!
74
}
75

76
// Class Reader
77
// //////////////////////////////////////////////////////////////////
78

79
Reader::Reader() : features_(Features::all()) {}
32✔
80

81
Reader::Reader(const Features& features) : features_(features) {}
1,374✔
82

83
bool Reader::parse(const std::string& document, Value& root,
26✔
84
                   bool collectComments) {
85
  document_.assign(document.begin(), document.end());
26✔
86
  const char* begin = document_.c_str();
87
  const char* end = begin + document_.length();
26✔
88
  return parse(begin, end, root, collectComments);
26✔
89
}
90

91
bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
1✔
92
  document_.assign(std::istreambuf_iterator<char>(is),
1✔
93
                   std::istreambuf_iterator<char>());
94
  return parse(document_.data(), document_.data() + document_.size(), root,
1✔
95
               collectComments);
1✔
96
}
97

98
bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
711✔
99
                   bool collectComments) {
100
  if (!features_.allowComments_) {
711✔
101
    collectComments = false;
102
  }
103

104
  begin_ = beginDoc;
711✔
105
  end_ = endDoc;
711✔
106
  collectComments_ = collectComments;
711✔
107
  current_ = begin_;
711✔
108
  lastValueEnd_ = nullptr;
711✔
109
  lastValue_ = nullptr;
711✔
110
  commentsBefore_.clear();
111
  errors_.clear();
711✔
112
  while (!nodes_.empty())
722✔
113
    nodes_.pop();
11✔
114
  nodes_.push(&root);
711!
115

116
  bool successful = readValue();
711✔
117
  Token token;
118
  readTokenSkippingComments(token);
711✔
119
  if (collectComments_ && !commentsBefore_.empty())
711✔
120
    root.setComment(commentsBefore_, commentAfter);
26✔
121
  if (features_.strictRoot_) {
711✔
122
    if (!root.isArray() && !root.isObject()) {
1!
123
      // Set error location to start of doc, ideally should be first token found
124
      // in doc
125
      token.type_ = tokenError;
1✔
126
      token.start_ = beginDoc;
1✔
127
      token.end_ = endDoc;
1✔
128
      addError(
1✔
129
          "A valid JSON document must be either an array or an object value.",
130
          token);
131
      return false;
1✔
132
    }
133
  }
134
  return successful;
135
}
136

137
bool Reader::readValue() {
53,564!
138
  // readValue() may call itself only if it calls readObject() or ReadArray().
139
  // These methods execute nodes_.push() just before and nodes_.pop)() just
140
  // after calling readValue(). parse() executes one nodes_.push(), so > instead
141
  // of >=.
142
  if (nodes_.size() > stackLimit_g)
53,564!
143
#if JSON_USE_EXCEPTION
UNCOV
144
    throwRuntimeError("Exceeded stackLimit in readValue().");
×
145
#else
146
    // throwRuntimeError aborts. Don't abort here.
147
    return false;
148
#endif
149

150
  Token token;
151
  readTokenSkippingComments(token);
53,564✔
152
  bool successful = true;
153

154
  if (collectComments_ && !commentsBefore_.empty()) {
53,564✔
155
    currentValue().setComment(commentsBefore_, commentBefore);
1,191✔
156
    commentsBefore_.clear();
157
  }
158

159
  switch (token.type_) {
53,564✔
160
  case tokenObjectBegin:
244✔
161
    successful = readObject(token);
244✔
162
    currentValue().setOffsetLimit(current_ - begin_);
244✔
163
    break;
164
  case tokenArrayBegin:
190✔
165
    successful = readArray(token);
190✔
166
    currentValue().setOffsetLimit(current_ - begin_);
190✔
167
    break;
168
  case tokenNumber:
52,650✔
169
    successful = decodeNumber(token);
52,650✔
170
    break;
171
  case tokenString:
374✔
172
    successful = decodeString(token);
374✔
173
    break;
174
  case tokenTrue: {
26✔
175
    Value v(true);
26✔
176
    currentValue().swapPayload(v);
26✔
177
    currentValue().setOffsetStart(token.start_ - begin_);
26✔
178
    currentValue().setOffsetLimit(token.end_ - begin_);
26✔
179
  } break;
26✔
180
  case tokenFalse: {
25✔
181
    Value v(false);
25✔
182
    currentValue().swapPayload(v);
25✔
183
    currentValue().setOffsetStart(token.start_ - begin_);
25✔
184
    currentValue().setOffsetLimit(token.end_ - begin_);
25✔
185
  } break;
25✔
186
  case tokenNull: {
49✔
187
    Value v;
49✔
188
    currentValue().swapPayload(v);
49✔
189
    currentValue().setOffsetStart(token.start_ - begin_);
49✔
190
    currentValue().setOffsetLimit(token.end_ - begin_);
49✔
191
  } break;
49✔
192
  case tokenArraySeparator:
1✔
193
  case tokenObjectEnd:
194
  case tokenArrayEnd:
195
    if (features_.allowDroppedNullPlaceholders_) {
1!
196
      // "Un-read" the current token and mark the current value as a null
197
      // token.
198
      current_--;
1✔
199
      Value v;
1✔
200
      currentValue().swapPayload(v);
1✔
201
      currentValue().setOffsetStart(current_ - begin_ - 1);
1✔
202
      currentValue().setOffsetLimit(current_ - begin_);
1✔
203
      break;
204
    } // Else, fall through...
1✔
205
  default:
206
    currentValue().setOffsetStart(token.start_ - begin_);
5✔
207
    currentValue().setOffsetLimit(token.end_ - begin_);
5✔
208
    return addError("Syntax error: value, object or array expected.", token);
10✔
209
  }
210

211
  if (collectComments_) {
53,559✔
212
    lastValueEnd_ = current_;
53,558✔
213
    lastValue_ = &currentValue();
53,558✔
214
  }
215

216
  return successful;
217
}
218

219
bool Reader::readTokenSkippingComments(Token& token) {
107,548✔
220
  bool success = readToken(token);
107,548✔
221
  if (features_.allowComments_) {
107,548✔
222
    while (success && token.type_ == tokenComment) {
108,115✔
223
      success = readToken(token);
569✔
224
    }
225
  }
226
  return success;
107,548✔
227
}
228

229
bool Reader::readToken(Token& token) {
108,571✔
230
  skipSpaces();
108,571✔
231
  token.start_ = current_;
108,571✔
232
  Char c = getNextChar();
108,571✔
233
  bool ok = true;
234
  switch (c) {
108,571✔
235
  case '{':
244✔
236
    token.type_ = tokenObjectBegin;
244✔
237
    break;
238
  case '}':
242✔
239
    token.type_ = tokenObjectEnd;
242✔
240
    break;
241
  case '[':
190✔
242
    token.type_ = tokenArrayBegin;
190✔
243
    break;
244
  case ']':
189✔
245
    token.type_ = tokenArrayEnd;
189✔
246
    break;
247
  case '"':
792✔
248
    token.type_ = tokenString;
792✔
249
    ok = readString();
792✔
250
    break;
792✔
251
  case '/':
569✔
252
    token.type_ = tokenComment;
569✔
253
    ok = readComment();
569✔
254
    break;
569✔
255
  case '0':
52,651✔
256
  case '1':
257
  case '2':
258
  case '3':
259
  case '4':
260
  case '5':
261
  case '6':
262
  case '7':
263
  case '8':
264
  case '9':
265
  case '-':
266
    token.type_ = tokenNumber;
52,651✔
267
    readNumber();
52,651✔
268
    break;
269
  case 't':
26✔
270
    token.type_ = tokenTrue;
26✔
271
    ok = match("rue", 3);
26✔
272
    break;
26✔
273
  case 'f':
27✔
274
    token.type_ = tokenFalse;
27✔
275
    ok = match("alse", 4);
27✔
276
    break;
27✔
277
  case 'n':
52✔
278
    token.type_ = tokenNull;
52✔
279
    ok = match("ull", 3);
52✔
280
    break;
52✔
281
  case ',':
52,446✔
282
    token.type_ = tokenArraySeparator;
52,446✔
283
    break;
284
  case ':':
417✔
285
    token.type_ = tokenMemberSeparator;
417✔
286
    break;
287
  case 0:
718✔
288
    token.type_ = tokenEndOfStream;
718✔
289
    break;
290
  default:
291
    ok = false;
292
    break;
293
  }
294
  if (!ok)
1,466✔
295
    token.type_ = tokenError;
13✔
296
  token.end_ = current_;
108,571✔
297
  return ok;
108,571✔
298
}
299

300
void Reader::skipSpaces() {
108,761✔
301
  while (current_ != end_) {
226,621✔
302
    Char c = *current_;
225,903✔
303
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
225,903✔
304
      ++current_;
117,860✔
305
    else
306
      break;
307
  }
308
}
108,761✔
309

310
bool Reader::match(const Char* pattern, int patternLength) {
105✔
311
  if (end_ - current_ < patternLength)
105✔
312
    return false;
313
  int index = patternLength;
314
  while (index--)
429✔
315
    if (current_[index] != pattern[index])
329✔
316
      return false;
317
  current_ += patternLength;
100✔
318
  return true;
100✔
319
}
320

321
bool Reader::readComment() {
569✔
322
  Location commentBegin = current_ - 1;
569✔
323
  Char c = getNextChar();
569✔
324
  bool successful = false;
325
  if (c == '*')
569✔
326
    successful = readCStyleComment();
73✔
327
  else if (c == '/')
496!
328
    successful = readCppStyleComment();
496✔
329
  if (!successful)
569!
UNCOV
330
    return false;
×
331

332
  if (collectComments_) {
569!
333
    CommentPlacement placement = commentBefore;
334
    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
569✔
335
      if (c != '*' || !containsNewLine(commentBegin, current_))
50!
336
        placement = commentAfterOnSameLine;
337
    }
338

339
    addComment(commentBegin, current_, placement);
569✔
340
  }
341
  return true;
342
}
343

344
String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
569✔
345
  String normalized;
346
  normalized.reserve(static_cast<size_t>(end - begin));
569✔
347
  Reader::Location current = begin;
348
  while (current != end) {
21,596✔
349
    char c = *current++;
21,027✔
350
    if (c == '\r') {
21,027✔
351
      if (current != end && *current == '\n')
2!
352
        // convert dos EOL
353
        ++current;
1✔
354
      // convert Mac EOL
355
      normalized += '\n';
356
    } else {
357
      normalized += c;
358
    }
359
  }
360
  return normalized;
569✔
361
}
362

363
void Reader::addComment(Location begin, Location end,
569✔
364
                        CommentPlacement placement) {
365
  assert(collectComments_);
569!
366
  const String& normalized = normalizeEOL(begin, end);
569✔
367
  if (placement == commentAfterOnSameLine) {
569✔
368
    assert(lastValue_ != nullptr);
50!
369
    lastValue_->setComment(normalized, placement);
100✔
370
  } else {
371
    commentsBefore_ += normalized;
519✔
372
  }
373
}
569✔
374

375
bool Reader::readCStyleComment() {
73✔
376
  while ((current_ + 1) < end_) {
2,419!
377
    Char c = getNextChar();
2,419✔
378
    if (c == '*' && *current_ == '/')
2,419!
379
      break;
380
  }
381
  return getNextChar() == '/';
73✔
382
}
383

384
bool Reader::readCppStyleComment() {
496✔
385
  while (current_ != end_) {
17,397!
386
    Char c = getNextChar();
17,397✔
387
    if (c == '\n')
17,397✔
388
      break;
389
    if (c == '\r') {
16,903✔
390
      // Consume DOS EOL. It will be normalized in addComment.
391
      if (current_ != end_ && *current_ == '\n')
2!
392
        getNextChar();
1✔
393
      // Break on Moc OS 9 EOL.
394
      break;
395
    }
396
  }
397
  return true;
496✔
398
}
399

400
void Reader::readNumber() {
52,651✔
401
  Location p = current_;
52,651✔
402
  char c = '0'; // stopgap for already consumed character
403
  // integral part
404
  while (c >= '0' && c <= '9')
234,911✔
405
    c = (current_ = p) < end_ ? *p++ : '\0';
182,260✔
406
  // fractional part
407
  if (c == '.') {
52,651✔
408
    c = (current_ = p) < end_ ? *p++ : '\0';
91!
409
    while (c >= '0' && c <= '9')
674✔
410
      c = (current_ = p) < end_ ? *p++ : '\0';
583✔
411
  }
412
  // exponential part
413
  if (c == 'e' || c == 'E') {
52,651✔
414
    c = (current_ = p) < end_ ? *p++ : '\0';
67!
415
    if (c == '+' || c == '-')
67✔
416
      c = (current_ = p) < end_ ? *p++ : '\0';
55!
417
    while (c >= '0' && c <= '9')
225✔
418
      c = (current_ = p) < end_ ? *p++ : '\0';
158✔
419
  }
420
}
52,651✔
421

422
bool Reader::readString() {
792✔
423
  Char c = '\0';
424
  while (current_ != end_) {
30,369!
425
    c = getNextChar();
30,369✔
426
    if (c == '\\')
30,369✔
427
      getNextChar();
1,152✔
428
    else if (c == '"')
29,217✔
429
      break;
430
  }
431
  return c == '"';
792✔
432
}
433

434
bool Reader::readObject(Token& token) {
244✔
435
  Token tokenName;
436
  String name;
437
  Value init(objectValue);
244✔
438
  currentValue().swapPayload(init);
244✔
439
  currentValue().setOffsetStart(token.start_ - begin_);
244✔
440
  while (readTokenSkippingComments(tokenName)) {
429!
441
    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
429!
442
      return true;
243✔
443
    name.clear();
444
    if (tokenName.type_ == tokenString) {
417✔
445
      if (!decodeString(tokenName, name))
415!
UNCOV
446
        return recoverFromError(tokenObjectEnd);
×
447
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
2!
448
      Value numberName;
1✔
449
      if (!decodeNumber(tokenName, numberName))
1!
UNCOV
450
        return recoverFromError(tokenObjectEnd);
×
451
      name = numberName.asString();
1✔
452
    } else {
1✔
453
      break;
454
    }
455

456
    Token colon;
457
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
416!
458
      return addErrorAndRecover("Missing ':' after object member name", colon,
2✔
459
                                tokenObjectEnd);
460
    }
461
    Value& value = currentValue()[name];
415✔
462
    nodes_.push(&value);
415!
463
    bool ok = readValue();
415✔
464
    nodes_.pop();
415✔
465
    if (!ok) // error already set
415✔
466
      return recoverFromError(tokenObjectEnd);
5✔
467

468
    Token comma;
469
    if (!readTokenSkippingComments(comma) ||
410!
470
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
410✔
471
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
2✔
472
                                comma, tokenObjectEnd);
473
    }
474
    if (comma.type_ == tokenObjectEnd)
409✔
475
      return true;
476
  }
477
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
2✔
478
                            tokenObjectEnd);
479
}
244✔
480

481
bool Reader::readArray(Token& token) {
190✔
482
  Value init(arrayValue);
190✔
483
  currentValue().swapPayload(init);
190✔
484
  currentValue().setOffsetStart(token.start_ - begin_);
190✔
485
  skipSpaces();
190✔
486
  if (current_ != end_ && *current_ == ']') // empty array
190!
487
  {
488
    Token endArray;
489
    readToken(endArray);
12✔
490
    return true;
491
  }
492
  int index = 0;
493
  for (;;) {
494
    Value& value = currentValue()[index++];
52,438✔
495
    nodes_.push(&value);
52,438!
496
    bool ok = readValue();
52,438✔
497
    nodes_.pop();
52,438✔
498
    if (!ok) // error already set
52,438✔
499
      return recoverFromError(tokenArrayEnd);
6✔
500

501
    Token currentToken;
502
    // Accept Comment after last item in the array.
503
    ok = readTokenSkippingComments(currentToken);
52,434✔
504
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,434✔
505
                         currentToken.type_ != tokenArrayEnd);
52,434✔
506
    if (!ok || badTokenType) {
52,434✔
507
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
4✔
508
                                currentToken, tokenArrayEnd);
509
    }
510
    if (currentToken.type_ == tokenArrayEnd)
52,432✔
511
      break;
512
  }
52,260✔
513
  return true;
172✔
514
}
190✔
515

516
bool Reader::decodeNumber(Token& token) {
52,650✔
517
  Value decoded;
52,650✔
518
  if (!decodeNumber(token, decoded))
52,650!
519
    return false;
520
  currentValue().swapPayload(decoded);
52,650✔
521
  currentValue().setOffsetStart(token.start_ - begin_);
52,650✔
522
  currentValue().setOffsetLimit(token.end_ - begin_);
52,650✔
523
  return true;
524
}
52,650✔
525

526
bool Reader::decodeNumber(Token& token, Value& decoded) {
52,651✔
527
  // Attempts to parse the number as an integer. If the number is
528
  // larger than the maximum supported value of an integer then
529
  // we decode the number as a double.
530
  Location current = token.start_;
52,651✔
531
  bool isNegative = *current == '-';
52,651✔
532
  if (isNegative)
52,651✔
533
    ++current;
133✔
534
  // TODO: Help the compiler do the div and mod at compile time or get rid of
535
  // them.
536
  Value::LargestUInt maxIntegerValue =
537
      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
538
                 : Value::maxLargestUInt;
539
  Value::LargestUInt threshold = maxIntegerValue / 10;
52,651✔
540
  Value::LargestUInt value = 0;
541
  while (current < token.end_) {
234,760✔
542
    Char c = *current++;
182,254✔
543
    if (c < '0' || c > '9')
182,254✔
544
      return decodeDouble(token, decoded);
127✔
545
    auto digit(static_cast<Value::UInt>(c - '0'));
182,127✔
546
    if (value >= threshold) {
182,127✔
547
      // We've hit or exceeded the max value divided by 10 (rounded down). If
548
      // a) we've only just touched the limit, b) this is the last digit, and
549
      // c) it's small enough to fit in that rounding delta, we're okay.
550
      // Otherwise treat this number as a double to avoid overflow.
551
      if (value > threshold || current != token.end_ ||
42!
552
          digit > maxIntegerValue % 10) {
30✔
553
        return decodeDouble(token, decoded);
18✔
554
      }
555
    }
556
    value = value * 10 + digit;
182,109✔
557
  }
558
  if (isNegative && value == maxIntegerValue)
52,506✔
559
    decoded = Value::minLargestInt;
12✔
560
  else if (isNegative)
52,494✔
561
    decoded = -Value::LargestInt(value);
72✔
562
  else if (value <= Value::LargestUInt(Value::maxInt))
52,422✔
563
    decoded = Value::LargestInt(value);
52,362✔
564
  else
565
    decoded = value;
60✔
566
  return true;
567
}
568

569
bool Reader::decodeDouble(Token& token) {
×
570
  Value decoded;
×
UNCOV
571
  if (!decodeDouble(token, decoded))
×
572
    return false;
573
  currentValue().swapPayload(decoded);
×
574
  currentValue().setOffsetStart(token.start_ - begin_);
×
UNCOV
575
  currentValue().setOffsetLimit(token.end_ - begin_);
×
576
  return true;
UNCOV
577
}
×
578

579
bool Reader::decodeDouble(Token& token, Value& decoded) {
145✔
580
  double value = 0;
145✔
581
  IStringStream is(String(token.start_, token.end_));
145✔
582
  is.imbue(std::locale::classic());
145✔
583
  errno = 0;
145✔
584
  if (!(is >> value)) {
145✔
585
    if (value == std::numeric_limits<double>::max())
24✔
586
      value = std::numeric_limits<double>::infinity();
12✔
587
    else if (value == std::numeric_limits<double>::lowest())
12!
588
      value = -std::numeric_limits<double>::infinity();
12✔
589
    // A subnormal or otherwise out-of-range value (e.g. "3.2114e-312" or
590
    // "1e-400") makes operator>> set failbit even though it parsed a usable
591
    // result; the stream signals this via errno == ERANGE. Keep that value
592
    // (the correctly-rounded subnormal, zero, or infinity) instead of
593
    // rejecting it, so such numbers round-trip (issue #1427). Genuine junk
594
    // leaves errno unset and is still rejected.
NEW
595
    else if (!std::isinf(value) && errno != ERANGE)
×
UNCOV
596
      return addError(
×
UNCOV
597
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
598
  }
599
  decoded = value;
145✔
600
  return true;
145✔
601
}
145✔
602

603
bool Reader::decodeString(Token& token) {
374✔
604
  String decoded_string;
605
  if (!decodeString(token, decoded_string))
374✔
606
    return false;
607
  Value decoded(decoded_string);
369✔
608
  currentValue().swapPayload(decoded);
369✔
609
  currentValue().setOffsetStart(token.start_ - begin_);
369✔
610
  currentValue().setOffsetLimit(token.end_ - begin_);
369✔
611
  return true;
612
}
369✔
613

614
bool Reader::decodeString(Token& token, String& decoded) {
789✔
615
  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
789✔
616
  Location current = token.start_ + 1; // skip '"'
789✔
617
  Location end = token.end_ - 1;       // do not include '"'
789✔
618
  while (current != end) {
29,861✔
619
    Char c = *current++;
29,077✔
620
    if (c == '"')
29,077!
621
      break;
622
    if (c == '\\') {
29,077✔
623
      if (current == end)
1,138!
UNCOV
624
        return addError("Empty escape sequence in string", token, current);
×
625
      Char escape = *current++;
1,138✔
626
      switch (escape) {
1,138✔
627
      case '"':
628
        decoded += '"';
629
        break;
630
      case '/':
631
        decoded += '/';
632
        break;
633
      case '\\':
634
        decoded += '\\';
635
        break;
636
      case 'b':
637
        decoded += '\b';
638
        break;
639
      case 'f':
640
        decoded += '\f';
641
        break;
642
      case 'n':
643
        decoded += '\n';
644
        break;
645
      case 'r':
646
        decoded += '\r';
647
        break;
648
      case 't':
649
        decoded += '\t';
650
        break;
651
      case 'u': {
103✔
652
        unsigned int unicode;
653
        if (!decodeUnicodeCodePoint(token, current, end, unicode))
103✔
654
          return false;
4✔
655
        decoded += codePointToUTF8(unicode);
99✔
656
      } break;
99✔
657
      default:
1✔
658
        return addError("Bad escape sequence in string", token, current);
2✔
659
      }
660
    } else {
661
      if (static_cast<unsigned char>(c) < 0x20)
27,939!
UNCOV
662
        return addError("Control character in string", token, current - 1);
×
663
      decoded += c;
664
    }
665
  }
666
  return true;
667
}
668

669
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
670
                                    Location end, unsigned int& unicode) {
671

672
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
103✔
673
    return false;
674
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
101✔
675
    // surrogate pairs
676
    if (end - current < 6)
15✔
677
      return addError(
2✔
678
          "additional six characters expected to parse unicode surrogate pair.",
679
          token, current);
680
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
681
      unsigned int surrogatePair;
682
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
683
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
684
      } else
UNCOV
685
        return false;
×
686
    } else
687
      return addError("expecting another \\u token to begin the second half of "
2✔
688
                      "a unicode surrogate pair",
689
                      token, current);
690
  }
691
  return true;
692
}
693

694
bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
116✔
695
                                         Location end,
696
                                         unsigned int& ret_unicode) {
697
  if (end - current < 4)
116✔
698
    return addError(
2✔
699
        "Bad unicode escape sequence in string: four digits expected.", token,
700
        current);
701
  int unicode = 0;
702
  for (int index = 0; index < 4; ++index) {
573✔
703
    Char c = *current++;
459✔
704
    unicode *= 16;
459✔
705
    if (c >= '0' && c <= '9')
459✔
706
      unicode += c - '0';
342✔
707
    else if (c >= 'a' && c <= 'f')
117✔
708
      unicode += c - 'a' + 10;
74✔
709
    else if (c >= 'A' && c <= 'F')
43✔
710
      unicode += c - 'A' + 10;
42✔
711
    else
712
      return addError(
2✔
713
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
714
          token, current);
715
  }
716
  ret_unicode = static_cast<unsigned int>(unicode);
114✔
717
  return true;
114✔
718
}
719

720
bool Reader::addError(const String& message, Token& token, Location extra) {
16✔
721
  ErrorInfo info;
722
  info.token_ = token;
16✔
723
  info.message_ = message;
724
  info.extra_ = extra;
16✔
725
  errors_.push_back(info);
16✔
726
  return false;
16✔
727
}
728

729
bool Reader::recoverFromError(TokenType skipUntilToken) {
14✔
730
  size_t const errorCount = errors_.size();
731
  Token skip;
732
  for (;;) {
733
    if (!readToken(skip))
26✔
734
      errors_.resize(errorCount); // discard errors caused by recovery
10✔
735
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
26✔
736
      break;
737
  }
738
  errors_.resize(errorCount);
14✔
739
  return false;
14✔
740
}
741

742
bool Reader::addErrorAndRecover(const String& message, Token& token,
5✔
743
                                TokenType skipUntilToken) {
744
  addError(message, token);
5✔
745
  return recoverFromError(skipUntilToken);
5✔
746
}
747

748
Value& Reader::currentValue() { return *(nodes_.top()); }
267,480✔
749

750
Reader::Char Reader::getNextChar() {
160,551✔
751
  if (current_ == end_)
160,551✔
752
    return 0;
753
  return *current_++;
159,833✔
754
}
755

756
void Reader::getLocationLineAndColumn(Location location, int& line,
24✔
757
                                      int& column) const {
758
  Location current = begin_;
24✔
759
  Location lastLineStart = current;
760
  line = 0;
24✔
761
  while (current < location && current != end_) {
263!
762
    Char c = *current++;
239✔
763
    if (c == '\r') {
239!
UNCOV
764
      if (current != end_ && *current == '\n')
×
765
        ++current;
×
766
      lastLineStart = current;
UNCOV
767
      ++line;
×
768
    } else if (c == '\n') {
239!
769
      lastLineStart = current;
UNCOV
770
      ++line;
×
771
    }
772
  }
773
  // column & line start at 1
774
  column = int(location - lastLineStart) + 1;
24✔
775
  ++line;
24✔
776
}
24✔
777

778
String Reader::getLocationLineAndColumn(Location location) const {
24✔
779
  int line, column;
780
  getLocationLineAndColumn(location, line, column);
24✔
781
  char buffer[18 + 16 + 16 + 1];
782
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
24✔
783
  return buffer;
24✔
784
}
785

786
// Deprecated. Preserved for backward compatibility
UNCOV
787
String Reader::getFormatedErrorMessages() const {
×
UNCOV
788
  return getFormattedErrorMessages();
×
789
}
790

791
String Reader::getFormattedErrorMessages() const {
19✔
792
  String formattedMessage;
793
  for (const auto& error : errors_) {
37✔
794
    formattedMessage +=
795
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
18✔
796
    formattedMessage += "  " + error.message_ + "\n";
18✔
797
    if (error.extra_)
18✔
798
      formattedMessage +=
799
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
12✔
800
  }
801
  return formattedMessage;
19✔
802
}
803

804
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
17✔
805
  std::vector<Reader::StructuredError> allErrors;
806
  for (const auto& error : errors_) {
33✔
807
    Reader::StructuredError structured;
808
    structured.offset_start = error.token_.start_ - begin_;
16✔
809
    structured.offset_limit = error.token_.end_ - begin_;
16✔
810
    structured.message = error.message_;
16✔
811
    allErrors.push_back(structured);
16✔
812
  }
813
  return allErrors;
17✔
UNCOV
814
}
×
815

816
bool Reader::pushError(const Value& value, const String& message) {
1✔
817
  ptrdiff_t const length = end_ - begin_;
1✔
818
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
1!
UNCOV
819
    return false;
×
820
  Token token;
821
  token.type_ = tokenError;
822
  token.start_ = begin_ + value.getOffsetStart();
1✔
823
  token.end_ = begin_ + value.getOffsetLimit();
1✔
824
  ErrorInfo info;
825
  info.token_ = token;
1✔
826
  info.message_ = message;
827
  info.extra_ = nullptr;
1✔
828
  errors_.push_back(info);
1✔
829
  return true;
830
}
831

832
bool Reader::pushError(const Value& value, const String& message,
1✔
833
                       const Value& extra) {
834
  ptrdiff_t const length = end_ - begin_;
1✔
835
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
2!
836
      extra.getOffsetLimit() > length)
1✔
UNCOV
837
    return false;
×
838
  Token token;
839
  token.type_ = tokenError;
840
  token.start_ = begin_ + value.getOffsetStart();
1✔
841
  token.end_ = begin_ + value.getOffsetLimit();
1✔
842
  ErrorInfo info;
843
  info.token_ = token;
1✔
844
  info.message_ = message;
845
  info.extra_ = begin_ + extra.getOffsetStart();
1✔
846
  errors_.push_back(info);
1✔
847
  return true;
848
}
849

UNCOV
850
bool Reader::good() const { return errors_.empty(); }
×
851

852
// Originally copied from the Features class (now deprecated), used internally
853
// for features implementation.
854
class OurFeatures {
855
public:
856
  static OurFeatures all();
857
  bool allowComments_;
858
  bool allowTrailingCommas_;
859
  bool strictRoot_;
860
  bool allowDroppedNullPlaceholders_;
861
  bool allowNumericKeys_;
862
  bool allowSingleQuotes_;
863
  bool failIfExtra_;
864
  bool rejectDupKeys_;
865
  bool allowSpecialFloats_;
866
  bool skipBom_;
867
  size_t stackLimit_;
868
}; // OurFeatures
869

870
OurFeatures OurFeatures::all() { return {}; }
903✔
871

872
// Implementation of class Reader
873
// ////////////////////////////////
874

875
// Originally copied from the Reader class (now deprecated), used internally
876
// for implementing JSON reading.
877
class OurReader {
878
public:
879
  using Char = char;
880
  using Location = const Char*;
881

882
  explicit OurReader(OurFeatures const& features);
883
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
884
             bool collectComments = true);
885
  String getFormattedErrorMessages() const;
886
  std::vector<CharReader::StructuredError> getStructuredErrors() const;
887

888
private:
889
  OurReader(OurReader const&);      // no impl
890
  void operator=(OurReader const&); // no impl
891

892
  enum TokenType {
893
    tokenEndOfStream = 0,
894
    tokenObjectBegin,
895
    tokenObjectEnd,
896
    tokenArrayBegin,
897
    tokenArrayEnd,
898
    tokenString,
899
    tokenNumber,
900
    tokenTrue,
901
    tokenFalse,
902
    tokenNull,
903
    tokenNaN,
904
    tokenPosInf,
905
    tokenNegInf,
906
    tokenArraySeparator,
907
    tokenMemberSeparator,
908
    tokenComment,
909
    tokenError
910
  };
911

912
  class Token {
913
  public:
914
    TokenType type_;
915
    Location start_;
916
    Location end_;
917
  };
918

919
  class ErrorInfo {
163✔
920
  public:
921
    Token token_;
922
    String message_;
923
    Location extra_;
924
  };
925

926
  using Errors = std::deque<ErrorInfo>;
927

928
  bool readToken(Token& token);
929
  bool readTokenSkippingComments(Token& token);
930
  void skipSpaces();
931
  void skipBom(bool skipBom);
932
  bool match(const Char* pattern, int patternLength);
933
  bool readComment();
934
  bool readCStyleComment(bool* containsNewLineResult);
935
  bool readCppStyleComment();
936
  bool readString();
937
  bool readStringSingleQuote();
938
  bool readNumber(bool checkInf);
939
  bool readValue();
940
  bool readObject(Token& token);
941
  bool readArray(Token& token);
942
  bool decodeNumber(Token& token);
943
  bool decodeNumber(Token& token, Value& decoded);
944
  bool decodeString(Token& token);
945
  bool decodeString(Token& token, String& decoded);
946
  bool decodeDouble(Token& token);
947
  bool decodeDouble(Token& token, Value& decoded);
948
  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
949
                              unsigned int& unicode);
950
  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
951
                                   Location end, unsigned int& unicode);
952
  bool addError(const String& message, Token& token, Location extra = nullptr);
953
  bool recoverFromError(TokenType skipUntilToken);
954
  bool addErrorAndRecover(const String& message, Token& token,
955
                          TokenType skipUntilToken);
956
  void skipUntilSpace();
957
  Value& currentValue();
958
  Char getNextChar();
959
  void getLocationLineAndColumn(Location location, int& line,
960
                                int& column) const;
961
  String getLocationLineAndColumn(Location location) const;
962
  void addComment(Location begin, Location end, CommentPlacement placement);
963

964
  static String normalizeEOL(Location begin, Location end);
965
  static bool containsNewLine(Location begin, Location end);
966

967
  using Nodes = std::stack<Value*>;
968

969
  Nodes nodes_{};
970
  Errors errors_{};
971
  String document_{};
972
  Location begin_ = nullptr;
973
  Location end_ = nullptr;
974
  Location current_ = nullptr;
975
  Location lastValueEnd_ = nullptr;
976
  Value* lastValue_ = nullptr;
977
  bool lastValueHasAComment_ = false;
978
  String commentsBefore_{};
979

980
  OurFeatures const features_;
981
  bool collectComments_ = false;
982
}; // OurReader
983

984
// complete copy of Read impl, for OurReader
985

986
// Test-only instrumentation: total bytes examined by
987
// OurReader::containsNewLine, so unit tests can assert that comment handling
988
// stays linear in the input rather than quadratic in the comment count (see
989
// CharReaderTest/parseCommentsAfterValueScansLinearly). thread_local so it
990
// never races during concurrent parsing; the increment is negligible and only
991
// runs while parsing comments. Not part of the supported public API.
992
JSON_API size_t& newlineScanByteCountForTesting() {
164✔
993
  static thread_local size_t count = 0;
994
  return count;
164✔
995
}
996

997
bool OurReader::containsNewLine(OurReader::Location begin,
162✔
998
                                OurReader::Location end) {
999
  newlineScanByteCountForTesting() += static_cast<size_t>(end - begin);
162✔
1000
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
202!
1001
}
1002

1003
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,806✔
1004

1005
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
940✔
1006
                      bool collectComments) {
1007
  if (!features_.allowComments_) {
940✔
1008
    collectComments = false;
1009
  }
1010

1011
  begin_ = beginDoc;
940✔
1012
  end_ = endDoc;
940✔
1013
  collectComments_ = collectComments;
940✔
1014
  current_ = begin_;
940✔
1015
  lastValueEnd_ = nullptr;
940✔
1016
  lastValue_ = nullptr;
940✔
1017
  commentsBefore_.clear();
1018
  errors_.clear();
940✔
1019
  while (!nodes_.empty())
940!
UNCOV
1020
    nodes_.pop();
×
1021
  nodes_.push(&root);
940!
1022

1023
  // skip byte order mark if it exists at the beginning of the UTF-8 text.
1024
  skipBom(features_.skipBom_);
940✔
1025
  bool successful = readValue();
940✔
1026
  nodes_.pop();
932✔
1027
  Token token;
1028
  readTokenSkippingComments(token);
932✔
1029
  if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
932✔
1030
    addError("Extra non-whitespace after JSON value.", token);
5✔
1031
    return false;
5✔
1032
  }
1033
  if (collectComments_ && !commentsBefore_.empty())
927✔
1034
    root.setComment(commentsBefore_, commentAfter);
48✔
1035
  if (features_.strictRoot_) {
927✔
1036
    if (!root.isArray() && !root.isObject()) {
101✔
1037
      // Set error location to start of doc, ideally should be first token found
1038
      // in doc
1039
      token.type_ = tokenError;
4✔
1040
      token.start_ = beginDoc;
4✔
1041
      token.end_ = endDoc;
4✔
1042
      addError(
4✔
1043
          "A valid JSON document must be either an array or an object value.",
1044
          token);
1045
      return false;
4✔
1046
    }
1047
  }
1048
  return successful;
1049
}
1050

1051
bool OurReader::readValue() {
56,107✔
1052
  //  To preserve the old behaviour we cast size_t to int.
1053
  if (nodes_.size() > features_.stackLimit_)
56,107✔
1054
    throwRuntimeError("Exceeded stackLimit in readValue().");
8✔
1055
  Token token;
1056
  readTokenSkippingComments(token);
56,099✔
1057
  bool successful = true;
1058

1059
  if (collectComments_ && !commentsBefore_.empty()) {
56,099✔
1060
    currentValue().setComment(commentsBefore_, commentBefore);
1,203✔
1061
    commentsBefore_.clear();
1062
  }
1063

1064
  switch (token.type_) {
56,099✔
1065
  case tokenObjectBegin:
367✔
1066
    successful = readObject(token);
367✔
1067
    currentValue().setOffsetLimit(current_ - begin_);
366✔
1068
    break;
1069
  case tokenArrayBegin:
2,157✔
1070
    successful = readArray(token);
2,157✔
1071
    currentValue().setOffsetLimit(current_ - begin_);
365✔
1072
    break;
1073
  case tokenNumber:
52,841✔
1074
    successful = decodeNumber(token);
52,841✔
1075
    break;
1076
  case tokenString:
519✔
1077
    successful = decodeString(token);
519✔
1078
    break;
1079
  case tokenTrue: {
48✔
1080
    Value v(true);
48✔
1081
    currentValue().swapPayload(v);
48✔
1082
    currentValue().setOffsetStart(token.start_ - begin_);
48✔
1083
    currentValue().setOffsetLimit(token.end_ - begin_);
48✔
1084
  } break;
48✔
1085
  case tokenFalse: {
31✔
1086
    Value v(false);
31✔
1087
    currentValue().swapPayload(v);
31✔
1088
    currentValue().setOffsetStart(token.start_ - begin_);
31✔
1089
    currentValue().setOffsetLimit(token.end_ - begin_);
31✔
1090
  } break;
31✔
1091
  case tokenNull: {
61✔
1092
    Value v;
61✔
1093
    currentValue().swapPayload(v);
61✔
1094
    currentValue().setOffsetStart(token.start_ - begin_);
61✔
1095
    currentValue().setOffsetLimit(token.end_ - begin_);
61✔
1096
  } break;
61✔
1097
  case tokenNaN: {
1✔
1098
    Value v(std::numeric_limits<double>::quiet_NaN());
1✔
1099
    currentValue().swapPayload(v);
1✔
1100
    currentValue().setOffsetStart(token.start_ - begin_);
1✔
1101
    currentValue().setOffsetLimit(token.end_ - begin_);
1✔
1102
  } break;
1✔
1103
  case tokenPosInf: {
5✔
1104
    Value v(std::numeric_limits<double>::infinity());
5✔
1105
    currentValue().swapPayload(v);
5✔
1106
    currentValue().setOffsetStart(token.start_ - begin_);
5✔
1107
    currentValue().setOffsetLimit(token.end_ - begin_);
5✔
1108
  } break;
5✔
1109
  case tokenNegInf: {
3✔
1110
    Value v(-std::numeric_limits<double>::infinity());
3✔
1111
    currentValue().swapPayload(v);
3✔
1112
    currentValue().setOffsetStart(token.start_ - begin_);
3✔
1113
    currentValue().setOffsetLimit(token.end_ - begin_);
3✔
1114
  } break;
3✔
1115
  case tokenArraySeparator:
41✔
1116
  case tokenObjectEnd:
1117
  case tokenArrayEnd:
1118
    if (features_.allowDroppedNullPlaceholders_) {
41✔
1119
      // "Un-read" the current token and mark the current value as a null
1120
      // token.
1121
      current_--;
29✔
1122
      Value v;
29✔
1123
      currentValue().swapPayload(v);
29✔
1124
      currentValue().setOffsetStart(current_ - begin_ - 1);
29✔
1125
      currentValue().setOffsetLimit(current_ - begin_);
29✔
1126
      break;
1127
    } // else, fall through ...
29✔
1128
  default:
1129
    currentValue().setOffsetStart(token.start_ - begin_);
37✔
1130
    currentValue().setOffsetLimit(token.end_ - begin_);
37✔
1131
    return addError("Syntax error: value, object or array expected.", token);
74✔
1132
  }
1133

1134
  if (collectComments_) {
54,269✔
1135
    lastValueEnd_ = current_;
53,808✔
1136
    lastValueHasAComment_ = false;
53,808✔
1137
    lastValue_ = &currentValue();
53,808✔
1138
  }
1139

1140
  return successful;
1141
}
1142

1143
bool OurReader::readTokenSkippingComments(Token& token) {
111,028✔
1144
  bool success = readToken(token);
111,028✔
1145
  if (features_.allowComments_) {
111,028✔
1146
    while (success && token.type_ == tokenComment) {
111,529✔
1147
      success = readToken(token);
1,591✔
1148
    }
1149
  }
1150
  return success;
111,028✔
1151
}
1152

1153
bool OurReader::readToken(Token& token) {
113,699✔
1154
  skipSpaces();
113,699✔
1155
  token.start_ = current_;
113,699✔
1156
  Char c = getNextChar();
113,699✔
1157
  bool ok = true;
1158
  switch (c) {
113,699✔
1159
  case '{':
373✔
1160
    token.type_ = tokenObjectBegin;
373✔
1161
    break;
1162
  case '}':
359✔
1163
    token.type_ = tokenObjectEnd;
359✔
1164
    break;
1165
  case '[':
2,157✔
1166
    token.type_ = tokenArrayBegin;
2,157✔
1167
    break;
1168
  case ']':
366✔
1169
    token.type_ = tokenArrayEnd;
366✔
1170
    break;
1171
  case '"':
1,174✔
1172
    token.type_ = tokenString;
1,174✔
1173
    ok = readString();
1,174✔
1174
    break;
1,174✔
1175
  case '\'':
23✔
1176
    if (features_.allowSingleQuotes_) {
23✔
1177
      token.type_ = tokenString;
11✔
1178
      ok = readStringSingleQuote();
11✔
1179
    } else {
1180
      // If we don't allow single quotes, this is a failure case.
1181
      ok = false;
1182
    }
1183
    break;
1184
  case '/':
1,616✔
1185
    token.type_ = tokenComment;
1,616✔
1186
    ok = readComment();
1,616✔
1187
    break;
1,616✔
1188
  case '0':
52,727✔
1189
  case '1':
1190
  case '2':
1191
  case '3':
1192
  case '4':
1193
  case '5':
1194
  case '6':
1195
  case '7':
1196
  case '8':
1197
  case '9':
1198
    token.type_ = tokenNumber;
52,727✔
1199
    readNumber(false);
52,727✔
1200
    break;
1201
  case '-':
148✔
1202
    if (readNumber(true)) {
148✔
1203
      token.type_ = tokenNumber;
145✔
1204
    } else {
1205
      token.type_ = tokenNegInf;
3✔
1206
      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
3!
1207
    }
1208
    break;
1209
  case '+':
7✔
1210
    if (readNumber(true)) {
7✔
1211
      token.type_ = tokenNumber;
4✔
1212
    } else {
1213
      token.type_ = tokenPosInf;
3✔
1214
      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
3!
1215
    }
1216
    break;
1217
  case 't':
72✔
1218
    token.type_ = tokenTrue;
72✔
1219
    ok = match("rue", 3);
72✔
1220
    break;
72✔
1221
  case 'f':
43✔
1222
    token.type_ = tokenFalse;
43✔
1223
    ok = match("alse", 4);
43✔
1224
    break;
43✔
1225
  case 'n':
97✔
1226
    token.type_ = tokenNull;
97✔
1227
    ok = match("ull", 3);
97✔
1228
    break;
97✔
1229
  case 'N':
3✔
1230
    if (features_.allowSpecialFloats_) {
3✔
1231
      token.type_ = tokenNaN;
1✔
1232
      ok = match("aN", 2);
1✔
1233
    } else {
1234
      ok = false;
1235
    }
1236
    break;
1237
  case 'I':
8✔
1238
    if (features_.allowSpecialFloats_) {
8✔
1239
      token.type_ = tokenPosInf;
7✔
1240
      ok = match("nfinity", 7);
7✔
1241
    } else {
1242
      ok = false;
1243
    }
1244
    break;
1245
  case ',':
52,771✔
1246
    token.type_ = tokenArraySeparator;
52,771✔
1247
    break;
1248
  case ':':
663✔
1249
    token.type_ = tokenMemberSeparator;
663✔
1250
    break;
1251
  case 0:
951✔
1252
    token.type_ = tokenEndOfStream;
951✔
1253
    break;
1254
  default:
1255
    ok = false;
1256
    break;
1257
  }
1258
  if (!ok)
3,021✔
1259
    token.type_ = tokenError;
216✔
1260
  token.end_ = current_;
113,699✔
1261
  return ok;
113,699✔
1262
}
1263

1264
void OurReader::skipSpaces() {
168,262✔
1265
  while (current_ != end_) {
287,989✔
1266
    Char c = *current_;
287,038✔
1267
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
287,038✔
1268
      ++current_;
119,727✔
1269
    else
1270
      break;
1271
  }
1272
}
168,262✔
1273

1274
void OurReader::skipBom(bool skipBom) {
940✔
1275
  // The default behavior is to skip BOM.
1276
  if (skipBom) {
940✔
1277
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
939✔
1278
      begin_ += 3;
1✔
1279
      current_ = begin_;
1✔
1280
    }
1281
  }
1282
}
940✔
1283

1284
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1285
  if (end_ - current_ < patternLength)
226✔
1286
    return false;
1287
  int index = patternLength;
1288
  while (index--)
797✔
1289
    if (current_[index] != pattern[index])
631✔
1290
      return false;
1291
  current_ += patternLength;
166✔
1292
  return true;
166✔
1293
}
1294

1295
bool OurReader::readComment() {
1,616✔
1296
  const Location commentBegin = current_ - 1;
1,616✔
1297
  const Char c = getNextChar();
1,616✔
1298
  bool successful = false;
1299
  bool cStyleWithEmbeddedNewline = false;
1,616✔
1300

1301
  const bool isCStyleComment = (c == '*');
1302
  const bool isCppStyleComment = (c == '/');
1303
  if (isCStyleComment) {
1,616✔
1304
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1,086✔
1305
  } else if (isCppStyleComment) {
530!
1306
    successful = readCppStyleComment();
530✔
1307
  }
1308

1309
  if (!successful)
1,616!
UNCOV
1310
    return false;
×
1311

1312
  if (collectComments_) {
1,616✔
1313
    CommentPlacement placement = commentBefore;
1314

1315
    if (!lastValueHasAComment_) {
1,598✔
1316
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
434✔
1317
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
66✔
1318
          placement = commentAfterOnSameLine;
1319
        }
1320
      }
1321
      // The gap between the last value and this comment only grows as more
1322
      // comments are consumed, so a later comment can never be on the same
1323
      // line as that value. Mark it handled to avoid re-scanning the same
1324
      // growing prefix for every following comment (quadratic behavior).
1325
      lastValueHasAComment_ = true;
434✔
1326
    }
1327

1328
    addComment(commentBegin, current_, placement);
1,598✔
1329
  }
1330
  return true;
1331
}
1332

1333
String OurReader::normalizeEOL(OurReader::Location begin,
1,598✔
1334
                               OurReader::Location end) {
1335
  String normalized;
1336
  normalized.reserve(static_cast<size_t>(end - begin));
1,598✔
1337
  OurReader::Location current = begin;
1338
  while (current != end) {
28,206✔
1339
    char c = *current++;
26,608✔
1340
    if (c == '\r') {
26,608✔
1341
      if (current != end && *current == '\n')
3!
1342
        // convert dos EOL
1343
        ++current;
1✔
1344
      // convert Mac EOL
1345
      normalized += '\n';
1346
    } else {
1347
      normalized += c;
1348
    }
1349
  }
1350
  return normalized;
1,598✔
1351
}
1352

1353
void OurReader::addComment(Location begin, Location end,
1,598✔
1354
                           CommentPlacement placement) {
1355
  assert(collectComments_);
1,598!
1356
  const String& normalized = normalizeEOL(begin, end);
1,598✔
1357
  if (placement == commentAfterOnSameLine) {
1,598✔
1358
    assert(lastValue_ != nullptr);
64!
1359
    lastValue_->setComment(normalized, placement);
128✔
1360
  } else {
1361
    commentsBefore_ += normalized;
1,534✔
1362
  }
1363
}
1,598✔
1364

1365
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,086✔
1366
  *containsNewLineResult = false;
1,086✔
1367

1368
  while ((current_ + 1) < end_) {
6,013!
1369
    Char c = getNextChar();
4,927✔
1370
    if (c == '*' && *current_ == '/')
4,927!
1371
      break;
1372
    if (c == '\n')
3,841✔
1373
      *containsNewLineResult = true;
98✔
1374
  }
1375

1376
  return getNextChar() == '/';
1,086✔
1377
}
1378

1379
bool OurReader::readCppStyleComment() {
530✔
1380
  while (current_ != end_) {
18,083✔
1381
    Char c = getNextChar();
18,077✔
1382
    if (c == '\n')
18,077✔
1383
      break;
1384
    if (c == '\r') {
17,556✔
1385
      // Consume DOS EOL. It will be normalized in addComment.
1386
      if (current_ != end_ && *current_ == '\n')
3!
1387
        getNextChar();
1✔
1388
      // Break on Moc OS 9 EOL.
1389
      break;
1390
    }
1391
  }
1392
  return true;
530✔
1393
}
1394

1395
bool OurReader::readNumber(bool checkInf) {
52,882✔
1396
  Location p = current_;
52,882✔
1397
  if (checkInf && p != end_ && *p == 'I') {
52,882!
1398
    current_ = ++p;
6✔
1399
    return false;
6✔
1400
  }
1401
  char c = '0'; // stopgap for already consumed character
1402
  // integral part
1403
  while (c >= '0' && c <= '9')
235,531✔
1404
    c = (current_ = p) < end_ ? *p++ : '\0';
182,655✔
1405
  // fractional part
1406
  if (c == '.') {
52,876✔
1407
    c = (current_ = p) < end_ ? *p++ : '\0';
116!
1408
    while (c >= '0' && c <= '9')
809✔
1409
      c = (current_ = p) < end_ ? *p++ : '\0';
693✔
1410
  }
1411
  // exponential part
1412
  if (c == 'e' || c == 'E') {
52,876✔
1413
    c = (current_ = p) < end_ ? *p++ : '\0';
108!
1414
    if (c == '+' || c == '-')
108✔
1415
      c = (current_ = p) < end_ ? *p++ : '\0';
81!
1416
    while (c >= '0' && c <= '9')
326✔
1417
      c = (current_ = p) < end_ ? *p++ : '\0';
218✔
1418
  }
1419
  return true;
1420
}
1421
bool OurReader::readString() {
1,174✔
1422
  Char c = 0;
1423
  while (current_ != end_) {
34,521!
1424
    c = getNextChar();
34,521✔
1425
    if (c == '\\')
34,521✔
1426
      getNextChar();
1,275✔
1427
    else if (c == '"')
33,246✔
1428
      break;
1429
  }
1430
  return c == '"';
1,174✔
1431
}
1432

1433
bool OurReader::readStringSingleQuote() {
11✔
1434
  Char c = 0;
1435
  while (current_ != end_) {
26!
1436
    c = getNextChar();
26✔
1437
    if (c == '\\')
26✔
1438
      getNextChar();
2✔
1439
    else if (c == '\'')
24✔
1440
      break;
1441
  }
1442
  return c == '\'';
11✔
1443
}
1444

1445
bool OurReader::readObject(Token& token) {
367✔
1446
  Token tokenName;
1447
  String name;
1448
  Value init(objectValue);
367✔
1449
  currentValue().swapPayload(init);
367✔
1450
  currentValue().setOffsetStart(token.start_ - begin_);
367✔
1451
  while (readTokenSkippingComments(tokenName)) {
691✔
1452
    if (tokenName.type_ == tokenObjectEnd &&
682✔
1453
        (name.empty() ||
6✔
1454
         features_.allowTrailingCommas_)) // empty object or trailing comma
6!
1455
      return true;
340✔
1456
    name.clear();
1457
    if (tokenName.type_ == tokenString) {
657✔
1458
      if (!decodeString(tokenName, name))
637!
UNCOV
1459
        return recoverFromError(tokenObjectEnd);
×
1460
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
20✔
1461
      Value numberName;
3✔
1462
      if (!decodeNumber(tokenName, numberName))
3!
UNCOV
1463
        return recoverFromError(tokenObjectEnd);
×
1464
      name = numberName.asString();
3✔
1465
    } else {
3✔
1466
      break;
1467
    }
1468
    if (name.length() >= (1U << 30))
640!
UNCOV
1469
      throwRuntimeError("keylength >= 2^30");
×
1470
    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
640✔
1471
      String msg = "Duplicate key: '" + name + "'";
1✔
1472
      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1✔
1473
    }
1474

1475
    Token colon;
1476
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
639!
1477
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1478
                                tokenObjectEnd);
1479
    }
1480
    Value& value = currentValue()[name];
632✔
1481
    nodes_.push(&value);
632!
1482
    bool ok = readValue();
632✔
1483
    nodes_.pop();
631✔
1484
    if (!ok) // error already set
631✔
1485
      return recoverFromError(tokenObjectEnd);
22✔
1486

1487
    Token comma;
1488
    if (!readTokenSkippingComments(comma) ||
609✔
1489
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
601✔
1490
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1491
                                comma, tokenObjectEnd);
1492
    }
1493
    if (comma.type_ == tokenObjectEnd)
582✔
1494
      return true;
1495
  }
1496
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1497
                            tokenObjectEnd);
1498
}
367✔
1499

1500
bool OurReader::readArray(Token& token) {
2,157✔
1501
  Value init(arrayValue);
2,157✔
1502
  currentValue().swapPayload(init);
2,157✔
1503
  currentValue().setOffsetStart(token.start_ - begin_);
2,157✔
1504
  int index = 0;
1505
  for (;;) {
1506
    skipSpaces();
54,563✔
1507
    if (current_ != end_ && *current_ == ']' &&
54,563!
1508
        (index == 0 ||
14✔
1509
         (features_.allowTrailingCommas_ &&
14!
1510
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
14✔
1511
                                                      // comma
1512
    {
1513
      Token endArray;
1514
      readToken(endArray);
28✔
1515
      return true;
1516
    }
1517
    Value& value = currentValue()[index++];
54,535✔
1518
    nodes_.push(&value);
54,535✔
1519
    bool ok = readValue();
54,535✔
1520
    nodes_.pop();
52,743✔
1521
    if (!ok) // error already set
52,743✔
1522
      return recoverFromError(tokenArrayEnd);
46✔
1523

1524
    Token currentToken;
1525
    // Accept Comment after last item in the array.
1526
    ok = readTokenSkippingComments(currentToken);
52,697✔
1527
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,697✔
1528
                         currentToken.type_ != tokenArrayEnd);
52,697✔
1529
    if (!ok || badTokenType) {
52,697✔
1530
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
48✔
1531
                                currentToken, tokenArrayEnd);
1532
    }
1533
    if (currentToken.type_ == tokenArrayEnd)
52,673✔
1534
      break;
1535
  }
52,406✔
1536
  return true;
267✔
1537
}
2,157✔
1538

1539
bool OurReader::decodeNumber(Token& token) {
52,841✔
1540
  Value decoded;
52,841✔
1541
  if (!decodeNumber(token, decoded))
52,841✔
1542
    return false;
1543
  currentValue().swapPayload(decoded);
52,832✔
1544
  currentValue().setOffsetStart(token.start_ - begin_);
52,832✔
1545
  currentValue().setOffsetLimit(token.end_ - begin_);
52,832✔
1546
  return true;
1547
}
52,841✔
1548

1549
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,844✔
1550
  // Attempts to parse the number as an integer. If the number is
1551
  // larger than the maximum supported value of an integer then
1552
  // we decode the number as a double.
1553
  Location current = token.start_;
52,844✔
1554
  const bool isNegative = *current == '-';
52,844✔
1555
  if (isNegative) {
52,844✔
1556
    ++current;
142✔
1557
  }
1558

1559
  // We assume we can represent the largest and smallest integer types as
1560
  // unsigned integers with separate sign. This is only true if they can fit
1561
  // into an unsigned integer.
1562
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1563
                "Int must be smaller than UInt");
1564

1565
  // We need to convert minLargestInt into a positive number. The easiest way
1566
  // to do this conversion is to assume our "threshold" value of minLargestInt
1567
  // divided by 10 can fit in maxLargestInt when absolute valued. This should
1568
  // be a safe assumption.
1569
  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1570
                "The absolute value of minLargestInt must be greater than or "
1571
                "equal to maxLargestInt");
1572
  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1573
                "The absolute value of minLargestInt must be only 1 magnitude "
1574
                "larger than maxLargest Int");
1575

1576
  static constexpr Value::LargestUInt positive_threshold =
1577
      Value::maxLargestUInt / 10;
1578
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1579

1580
  // For the negative values, we have to be more careful. Since typically
1581
  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1582
  // then take the inverse. This assumes that minLargestInt is only a single
1583
  // power of 10 different in magnitude, which we check above. For the last
1584
  // digit, we take the modulus before negating for the same reason.
1585
  static constexpr auto negative_threshold =
1586
      Value::LargestUInt(-(Value::minLargestInt / 10));
1587
  static constexpr auto negative_last_digit =
1588
      Value::UInt(-(Value::minLargestInt % 10));
1589

1590
  const Value::LargestUInt threshold =
1591
      isNegative ? negative_threshold : positive_threshold;
1592
  const Value::UInt max_last_digit =
1593
      isNegative ? negative_last_digit : positive_last_digit;
1594

1595
  Value::LargestUInt value = 0;
1596
  while (current < token.end_) {
235,296✔
1597
    Char c = *current++;
182,653✔
1598
    if (c < '0' || c > '9')
182,653✔
1599
      return decodeDouble(token, decoded);
182✔
1600

1601
    const auto digit(static_cast<Value::UInt>(c - '0'));
182,471✔
1602
    if (value >= threshold) {
182,471✔
1603
      // We've hit or exceeded the max value divided by 10 (rounded down). If
1604
      // a) we've only just touched the limit, meaning value == threshold,
1605
      // b) this is the last digit, or
1606
      // c) it's small enough to fit in that rounding delta, we're okay.
1607
      // Otherwise treat this number as a double to avoid overflow.
1608
      if (value > threshold || current != token.end_ ||
43!
1609
          digit > max_last_digit) {
1610
        return decodeDouble(token, decoded);
19✔
1611
      }
1612
    }
1613
    value = value * 10 + digit;
182,452✔
1614
  }
1615

1616
  if (isNegative) {
52,643✔
1617
    // We use the same magnitude assumption here, just in case.
1618
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1619
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1620
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,554✔
1621
    decoded = Value::LargestInt(value);
52,530✔
1622
  } else {
1623
    decoded = value;
24✔
1624
  }
1625

1626
  return true;
1627
}
1628

1629
bool OurReader::decodeDouble(Token& token) {
×
UNCOV
1630
  Value decoded;
×
1631
  if (!decodeDouble(token, decoded))
×
1632
    return false;
1633
  currentValue().swapPayload(decoded);
×
UNCOV
1634
  currentValue().setOffsetStart(token.start_ - begin_);
×
1635
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1636
  return true;
UNCOV
1637
}
×
1638

1639
bool OurReader::decodeDouble(Token& token, Value& decoded) {
201✔
1640
  double value = 0;
201✔
1641
  IStringStream is(String(token.start_, token.end_));
201✔
1642
  is.imbue(std::locale::classic());
201✔
1643
  errno = 0;
201✔
1644
  if (!(is >> value)) {
201✔
1645
    if (value == std::numeric_limits<double>::max())
33✔
1646
      value = std::numeric_limits<double>::infinity();
12✔
1647
    else if (value == std::numeric_limits<double>::lowest())
21✔
1648
      value = -std::numeric_limits<double>::infinity();
12✔
1649
    // A subnormal or otherwise out-of-range value (e.g. "3.2114e-312" or
1650
    // "1e-400") makes operator>> set failbit even though it parsed a usable
1651
    // result; the stream signals this via errno == ERANGE. Keep that value
1652
    // (the correctly-rounded subnormal, zero, or infinity) instead of
1653
    // rejecting it, so such numbers round-trip (issue #1427). Genuine junk
1654
    // leaves errno unset and is still rejected.
1655
    else if (!std::isinf(value) && errno != ERANGE)
9!
1656
      return addError(
9✔
1657
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
18✔
1658
  }
1659
  decoded = value;
192✔
1660
  return true;
192✔
1661
}
201✔
1662

1663
bool OurReader::decodeString(Token& token) {
519✔
1664
  String decoded_string;
1665
  if (!decodeString(token, decoded_string))
519✔
1666
    return false;
1667
  Value decoded(decoded_string);
496✔
1668
  currentValue().swapPayload(decoded);
496✔
1669
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1670
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1671
  return true;
1672
}
496✔
1673

1674
bool OurReader::decodeString(Token& token, String& decoded) {
1,156✔
1675
  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1,156✔
1676
  Location current = token.start_ + 1; // skip '"'
1,156✔
1677
  Location end = token.end_ - 1;       // do not include '"'
1,156✔
1678
  while (current != end) {
33,563✔
1679
    Char c = *current++;
32,430✔
1680
    if (c == '"')
32,430!
1681
      break;
1682
    if (c == '\\') {
32,430✔
1683
      if (current == end)
1,254!
UNCOV
1684
        return addError("Empty escape sequence in string", token, current);
×
1685
      Char escape = *current++;
1,254✔
1686
      switch (escape) {
1,254✔
1687
      case '"':
1688
        decoded += '"';
1689
        break;
1690
      case '/':
1691
        decoded += '/';
1692
        break;
1693
      case '\\':
1694
        decoded += '\\';
1695
        break;
1696
      case 'b':
1697
        decoded += '\b';
1698
        break;
1699
      case 'f':
1700
        decoded += '\f';
1701
        break;
1702
      case 'n':
1703
        decoded += '\n';
1704
        break;
1705
      case 'r':
1706
        decoded += '\r';
1707
        break;
1708
      case 't':
1709
        decoded += '\t';
1710
        break;
1711
      case 'u': {
145✔
1712
        unsigned int unicode;
1713
        if (!decodeUnicodeCodePoint(token, current, end, unicode))
145✔
1714
          return false;
4✔
1715
        decoded += codePointToUTF8(unicode);
141✔
1716
      } break;
141✔
1717
      default:
13✔
1718
        return addError("Bad escape sequence in string", token, current);
26✔
1719
      }
1720
    } else {
1721
      if (static_cast<unsigned char>(c) < 0x20)
31,176✔
1722
        return addError("Control character in string", token, current - 1);
12✔
1723
      decoded += c;
1724
    }
1725
  }
1726
  return true;
1727
}
1728

1729
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1730
                                       Location end, unsigned int& unicode) {
1731

1732
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1733
    return false;
1734
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1735
    // surrogate pairs
1736
    if (end - current < 6)
15✔
1737
      return addError(
2✔
1738
          "additional six characters expected to parse unicode surrogate pair.",
1739
          token, current);
1740
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1741
      unsigned int surrogatePair;
1742
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1743
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1744
      } else
UNCOV
1745
        return false;
×
1746
    } else
1747
      return addError("expecting another \\u token to begin the second half of "
2✔
1748
                      "a unicode surrogate pair",
1749
                      token, current);
1750
  }
1751
  return true;
1752
}
1753

1754
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
158✔
1755
                                            Location end,
1756
                                            unsigned int& ret_unicode) {
1757
  if (end - current < 4)
158✔
1758
    return addError(
2✔
1759
        "Bad unicode escape sequence in string: four digits expected.", token,
1760
        current);
1761
  int unicode = 0;
1762
  for (int index = 0; index < 4; ++index) {
783✔
1763
    Char c = *current++;
627✔
1764
    unicode *= 16;
627✔
1765
    if (c >= '0' && c <= '9')
627✔
1766
      unicode += c - '0';
400✔
1767
    else if (c >= 'a' && c <= 'f')
227✔
1768
      unicode += c - 'a' + 10;
115✔
1769
    else if (c >= 'A' && c <= 'F')
112✔
1770
      unicode += c - 'A' + 10;
111✔
1771
    else
1772
      return addError(
2✔
1773
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1774
          token, current);
1775
  }
1776
  ret_unicode = static_cast<unsigned int>(unicode);
156✔
1777
  return true;
156✔
1778
}
1779

1780
bool OurReader::addError(const String& message, Token& token, Location extra) {
163✔
1781
  ErrorInfo info;
1782
  info.token_ = token;
163✔
1783
  info.message_ = message;
1784
  info.extra_ = extra;
163✔
1785
  errors_.push_back(info);
163✔
1786
  return false;
163✔
1787
}
1788

1789
bool OurReader::recoverFromError(TokenType skipUntilToken) {
153✔
1790
  size_t errorCount = errors_.size();
1791
  Token skip;
1792
  for (;;) {
1793
    if (!readToken(skip))
413✔
1794
      errors_.resize(errorCount); // discard errors caused by recovery
175✔
1795
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
413✔
1796
      break;
1797
  }
1798
  errors_.resize(errorCount);
153✔
1799
  return false;
153✔
1800
}
1801

1802
bool OurReader::addErrorAndRecover(const String& message, Token& token,
85✔
1803
                                   TokenType skipUntilToken) {
1804
  addError(message, token);
85✔
1805
  return recoverFromError(skipUntilToken);
85✔
1806
}
1807

1808
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,750✔
1809

1810
OurReader::Char OurReader::getNextChar() {
175,230✔
1811
  if (current_ == end_)
175,230✔
1812
    return 0;
1813
  return *current_++;
174,279✔
1814
}
1815

1816
void OurReader::getLocationLineAndColumn(Location location, int& line,
185✔
1817
                                         int& column) const {
1818
  Location current = begin_;
185✔
1819
  Location lastLineStart = current;
1820
  line = 0;
185✔
1821
  while (current < location && current != end_) {
2,274!
1822
    Char c = *current++;
2,089✔
1823
    if (c == '\r') {
2,089✔
1824
      if (current != end_ && *current == '\n')
1!
UNCOV
1825
        ++current;
×
1826
      lastLineStart = current;
1827
      ++line;
1✔
1828
    } else if (c == '\n') {
2,088✔
1829
      lastLineStart = current;
1830
      ++line;
28✔
1831
    }
1832
  }
1833
  // column & line start at 1
1834
  column = int(location - lastLineStart) + 1;
185✔
1835
  ++line;
185✔
1836
}
185✔
1837

1838
String OurReader::getLocationLineAndColumn(Location location) const {
185✔
1839
  int line, column;
1840
  getLocationLineAndColumn(location, line, column);
185✔
1841
  char buffer[18 + 16 + 16 + 1];
1842
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
185✔
1843
  return buffer;
185✔
1844
}
1845

1846
String OurReader::getFormattedErrorMessages() const {
930✔
1847
  String formattedMessage;
1848
  for (const auto& error : errors_) {
1,092✔
1849
    formattedMessage +=
1850
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
162✔
1851
    formattedMessage += "  " + error.message_ + "\n";
162✔
1852
    if (error.extra_)
162✔
1853
      formattedMessage +=
1854
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
46✔
1855
  }
1856
  return formattedMessage;
930✔
1857
}
1858

1859
std::vector<CharReader::StructuredError>
1860
OurReader::getStructuredErrors() const {
2✔
1861
  std::vector<CharReader::StructuredError> allErrors;
1862
  for (const auto& error : errors_) {
3✔
1863
    CharReader::StructuredError structured;
1864
    structured.offset_start = error.token_.start_ - begin_;
1✔
1865
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1866
    structured.message = error.message_;
1✔
1867
    allErrors.push_back(structured);
1✔
1868
  }
1869
  return allErrors;
2✔
UNCOV
1870
}
×
1871

1872
class OurCharReader : public CharReader {
1873

1874
public:
1875
  OurCharReader(bool collectComments, OurFeatures const& features)
903✔
1876
      : CharReader(
903✔
1877
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,806✔
1878

1879
protected:
1880
  class OurImpl : public Impl {
1881
  public:
1882
    OurImpl(bool collectComments, OurFeatures const& features)
1883
        : collectComments_(collectComments), reader_(features) {}
903✔
1884

1885
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
940✔
1886
               String* errs) override {
1887
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
940✔
1888
      if (errs) {
932✔
1889
        *errs = reader_.getFormattedErrorMessages();
1,860✔
1890
      }
1891
      return ok;
932✔
1892
    }
1893

1894
    std::vector<CharReader::StructuredError>
1895
    getStructuredErrors() const override {
2✔
1896
      return reader_.getStructuredErrors();
2✔
1897
    }
1898

1899
  private:
1900
    bool const collectComments_;
1901
    OurReader reader_;
1902
  };
1903
};
1904

1905
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
900✔
1906
CharReaderBuilder::~CharReaderBuilder() = default;
900✔
1907
CharReader* CharReaderBuilder::newCharReader() const {
903✔
1908
  bool collectComments = settings_["collectComments"].asBool();
903✔
1909
  OurFeatures features = OurFeatures::all();
903✔
1910
  features.allowComments_ = settings_["allowComments"].asBool();
903✔
1911
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
903✔
1912
  features.strictRoot_ = settings_["strictRoot"].asBool();
903✔
1913
  features.allowDroppedNullPlaceholders_ =
903✔
1914
      settings_["allowDroppedNullPlaceholders"].asBool();
903✔
1915
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
903✔
1916
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
903✔
1917

1918
  // Stack limit is always a size_t, so we get this as an unsigned int
1919
  // regardless of it we have 64-bit integer support enabled.
1920
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
903✔
1921
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
903✔
1922
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
903✔
1923
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
903✔
1924
  features.skipBom_ = settings_["skipBom"].asBool();
903✔
1925
  return new OurCharReader(collectComments, features);
903✔
1926
}
1927

1928
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1929
  static const auto& valid_keys = *new std::set<String>{
1930
      "collectComments",
1931
      "allowComments",
1932
      "allowTrailingCommas",
1933
      "strictRoot",
1934
      "allowDroppedNullPlaceholders",
1935
      "allowNumericKeys",
1936
      "allowSingleQuotes",
1937
      "stackLimit",
1938
      "failIfExtra",
1939
      "rejectDupKeys",
1940
      "allowSpecialFloats",
1941
      "skipBom",
1942
  };
15!
1943
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
54✔
1944
    auto key = si.name();
25✔
1945
    if (valid_keys.count(key))
25✔
1946
      continue;
1947
    if (invalid)
1!
1948
      (*invalid)[key] = *si;
1✔
1949
    else
1950
      return false;
1951
  }
1952
  return invalid ? invalid->empty() : true;
2!
1953
}
2!
1954

1955
Value& CharReaderBuilder::operator[](const String& key) {
1✔
1956
  return settings_[key];
1✔
1957
}
1958
// static
1959
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1960
  //! [CharReaderBuilderStrictMode]
1961
  (*settings)["allowComments"] = false;
3✔
1962
  (*settings)["allowTrailingCommas"] = false;
3✔
1963
  (*settings)["strictRoot"] = true;
3✔
1964
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1965
  (*settings)["allowNumericKeys"] = false;
3✔
1966
  (*settings)["allowSingleQuotes"] = false;
3✔
1967
  (*settings)["stackLimit"] = 256;
3✔
1968
  (*settings)["failIfExtra"] = true;
3✔
1969
  (*settings)["rejectDupKeys"] = true;
3✔
1970
  (*settings)["allowSpecialFloats"] = false;
3✔
1971
  (*settings)["skipBom"] = true;
3✔
1972
  //! [CharReaderBuilderStrictMode]
1973
}
3✔
1974
// static
1975
void CharReaderBuilder::setDefaults(Json::Value* settings) {
900✔
1976
  //! [CharReaderBuilderDefaults]
1977
  (*settings)["collectComments"] = true;
900✔
1978
  (*settings)["allowComments"] = true;
900✔
1979
  (*settings)["allowTrailingCommas"] = true;
900✔
1980
  (*settings)["strictRoot"] = false;
900✔
1981
  (*settings)["allowDroppedNullPlaceholders"] = false;
900✔
1982
  (*settings)["allowNumericKeys"] = false;
900✔
1983
  (*settings)["allowSingleQuotes"] = false;
900✔
1984
  (*settings)["stackLimit"] = 256;
900✔
1985
  (*settings)["failIfExtra"] = false;
900✔
1986
  (*settings)["rejectDupKeys"] = false;
900✔
1987
  (*settings)["allowSpecialFloats"] = false;
900✔
1988
  (*settings)["skipBom"] = true;
900✔
1989
  //! [CharReaderBuilderDefaults]
1990
}
900✔
1991
// static
1992
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
1993
  //! [CharReaderBuilderECMA404Mode]
1994
  (*settings)["allowComments"] = false;
×
1995
  (*settings)["allowTrailingCommas"] = false;
×
1996
  (*settings)["strictRoot"] = false;
×
1997
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
1998
  (*settings)["allowNumericKeys"] = false;
×
1999
  (*settings)["allowSingleQuotes"] = false;
×
2000
  (*settings)["stackLimit"] = 256;
×
2001
  (*settings)["failIfExtra"] = true;
×
UNCOV
2002
  (*settings)["rejectDupKeys"] = false;
×
2003
  (*settings)["allowSpecialFloats"] = false;
×
UNCOV
2004
  (*settings)["skipBom"] = false;
×
2005
  //! [CharReaderBuilderECMA404Mode]
UNCOV
2006
}
×
2007

2008
std::vector<CharReader::StructuredError>
2009
CharReader::getStructuredErrors() const {
2✔
2010
  return _impl->getStructuredErrors();
2✔
2011
}
2012

2013
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
940✔
2014
                       String* errs) {
2015
  return _impl->parse(beginDoc, endDoc, root, errs);
940✔
2016
}
2017

2018
//////////////////////////////////
2019
// global functions
2020

2021
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
2022
                     String* errs) {
2023
  OStringStream ssin;
4✔
2024
  ssin << sin.rdbuf();
4✔
2025
  String doc = std::move(ssin).str();
2026
  char const* begin = doc.data();
2027
  char const* end = begin + doc.size();
4✔
2028
  // Note that we do not actually need a null-terminator.
2029
  CharReaderPtr const reader(fact.newCharReader());
4✔
2030
  return reader->parse(begin, end, root, errs);
8✔
2031
}
4✔
2032

2033
IStream& operator>>(IStream& sin, Value& root) {
1✔
2034
  CharReaderBuilder b;
1✔
2035
  String errs;
2036
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2037
  if (!ok) {
1!
UNCOV
2038
    throwRuntimeError(errs);
×
2039
  }
2040
  return sin;
1✔
2041
}
1✔
2042

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

© 2026 Coveralls, Inc