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

open-source-parsers / jsoncpp / 28695298703

04 Jul 2026 04:45AM UTC coverage: 89.917% (+0.01%) from 89.907%
28695298703

push

github

web-flow
Merge fc1cef1c8 into edc01ab10

2236 of 2656 branches covered (84.19%)

Branch coverage included in aggregate %.

9 of 10 new or added lines in 1 file covered. (90.0%)

1 existing line in 1 file now uncovered.

2624 of 2749 relevant lines covered (95.45%)

23868.53 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 <cmath>
16
#include <cstring>
17
#include <iostream>
18
#include <istream>
19
#include <iterator>
20
#include <limits>
21
#include <memory>
22
#include <set>
23
#include <sstream>
24
#include <utility>
25

26
#include <cstdio>
27

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

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

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

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

48
namespace Json {
49

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

52
// Implementation of class Features
53
// ////////////////////////////////
54

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

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

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

68
// Implementation of class Reader
69
// ////////////////////////////////
70

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

75
// Class Reader
76
// //////////////////////////////////////////////////////////////////
77

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

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

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

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

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

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

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

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

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

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

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

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

215
  return successful;
216
}
217

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

578
bool Reader::decodeDouble(Token& token, Value& decoded) {
145✔
579
  double value = 0;
145✔
580
  IStringStream is(String(token.start_, token.end_));
145✔
581
  is.imbue(std::locale::classic());
145✔
582
  if (!(is >> value)) {
145✔
583
    if (value == std::numeric_limits<double>::max())
24✔
584
      value = std::numeric_limits<double>::infinity();
12✔
585
    else if (value == std::numeric_limits<double>::lowest())
12!
586
      value = -std::numeric_limits<double>::infinity();
12✔
587
    // operator>> sets failbit for a subnormal result (underflow) even though
588
    // it produced the correctly-rounded value, which made such numbers fail to
589
    // parse back after jsoncpp serialized them. Keep a subnormal value instead
590
    // of rejecting it. See issue #1427. Other failures -- malformed numbers
591
    // like "0e" or "0e+", or non-numbers -- leave the value at zero/non-finite
592
    // and are still rejected.
593
    else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
×
594
      return addError(
×
595
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
596
  }
597
  decoded = value;
145✔
598
  return true;
145✔
599
}
145✔
600

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

848
bool Reader::good() const { return errors_.empty(); }
×
849

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

869
OurFeatures OurFeatures::all() { return {}; }
905✔
870

871
// Implementation of class Reader
872
// ////////////////////////////////
873

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

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

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

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

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

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

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

927
  bool readToken(Token& token);
928
  bool readTokenSkippingComments(Token& token);
929
  void skipSpaces();
930
  void skipCommentTokens();
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() {
169✔
993
  static thread_local size_t count = 0;
994
  return count;
169✔
995
}
996

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

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

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

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

1023
  // skip byte order mark if it exists at the beginning of the UTF-8 text.
1024
  skipBom(features_.skipBom_);
950✔
1025
  bool successful = readValue();
950✔
1026
  nodes_.pop();
942✔
1027
  Token token;
1028
  readTokenSkippingComments(token);
942✔
1029
  if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
942✔
1030
    addError("Extra non-whitespace after JSON value.", token);
5✔
1031
    return false;
5✔
1032
  }
1033
  if (collectComments_ && !commentsBefore_.empty())
937✔
1034
    root.setComment(commentsBefore_, commentAfter);
56✔
1035
  if (features_.strictRoot_) {
937✔
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,129✔
1052
  //  To preserve the old behaviour we cast size_t to int.
1053
  if (nodes_.size() > features_.stackLimit_)
56,129✔
1054
    throwRuntimeError("Exceeded stackLimit in readValue().");
8✔
1055
  Token token;
1056
  readTokenSkippingComments(token);
56,121✔
1057
  bool successful = true;
1058

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

1064
  switch (token.type_) {
56,121✔
1065
  case tokenObjectBegin:
369✔
1066
    successful = readObject(token);
369✔
1067
    currentValue().setOffsetLimit(current_ - begin_);
368✔
1068
    break;
1069
  case tokenArrayBegin:
2,166✔
1070
    successful = readArray(token);
2,166✔
1071
    currentValue().setOffsetLimit(current_ - begin_);
374✔
1072
    break;
1073
  case tokenNumber:
52,849✔
1074
    successful = decodeNumber(token);
52,849✔
1075
    break;
1076
  case tokenString:
522✔
1077
    successful = decodeString(token);
522✔
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,291✔
1135
    lastValueEnd_ = current_;
53,830✔
1136
    lastValueHasAComment_ = false;
53,830✔
1137
    lastValue_ = &currentValue();
53,830✔
1138
  }
1139

1140
  return successful;
1141
}
1142

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

1153
bool OurReader::readToken(Token& token) {
113,757✔
1154
  skipSpaces();
113,757✔
1155
  token.start_ = current_;
113,757✔
1156
  Char c = getNextChar();
113,757✔
1157
  bool ok = true;
1158
  switch (c) {
113,757✔
1159
  case '{':
375✔
1160
    token.type_ = tokenObjectBegin;
375✔
1161
    break;
1162
  case '}':
361✔
1163
    token.type_ = tokenObjectEnd;
361✔
1164
    break;
1165
  case '[':
2,166✔
1166
    token.type_ = tokenArrayBegin;
2,166✔
1167
    break;
1168
  case ']':
375✔
1169
    token.type_ = tokenArrayEnd;
375✔
1170
    break;
1171
  case '"':
1,178✔
1172
    token.type_ = tokenString;
1,178✔
1173
    ok = readString();
1,178✔
1174
    break;
1,178✔
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,622✔
1185
    token.type_ = tokenComment;
1,622✔
1186
    ok = readComment();
1,622✔
1187
    break;
1,622✔
1188
  case '0':
52,735✔
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,735✔
1199
    readNumber(false);
52,735✔
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,778✔
1246
    token.type_ = tokenArraySeparator;
52,778✔
1247
    break;
1248
  case ':':
664✔
1249
    token.type_ = tokenMemberSeparator;
664✔
1250
    break;
1251
  case 0:
961✔
1252
    token.type_ = tokenEndOfStream;
961✔
1253
    break;
1254
  default:
1255
    ok = false;
1256
    break;
1257
  }
1258
  if (!ok)
3,031✔
1259
    token.type_ = tokenError;
216✔
1260
  token.end_ = current_;
113,757✔
1261
  return ok;
113,757✔
1262
}
1263

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

1274
// Skip whitespace and any comments, leaving current_ at the next significant
1275
// character. Consumed comments are recorded (commentsBefore_) so the next value
1276
// still receives them; if none follows they are simply not attached. This lets
1277
// callers peek for a delimiter that is preceded by comments (e.g. a ']' after a
1278
// trailing comma -- see readArray and issue #1500).
1279
void OurReader::skipCommentTokens() {
54,578✔
1280
  skipSpaces();
54,578✔
1281
  if (!features_.allowComments_)
54,578✔
1282
    return;
1283
  while (current_ != end_ && *current_ == '/' && (current_ + 1) != end_ &&
54,396!
1284
         (current_[1] == '/' || current_[1] == '*')) {
55!
1285
    Token comment;
1286
    if (!readToken(comment))
55!
1287
      return;
×
1288
    skipSpaces();
55✔
1289
  }
1290
}
1291

1292
void OurReader::skipBom(bool skipBom) {
950✔
1293
  // The default behavior is to skip BOM.
1294
  if (skipBom) {
950✔
1295
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
949✔
1296
      begin_ += 3;
1✔
1297
      current_ = begin_;
1✔
1298
    }
1299
  }
1300
}
950✔
1301

1302
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1303
  if (end_ - current_ < patternLength)
226✔
1304
    return false;
1305
  int index = patternLength;
1306
  while (index--)
797✔
1307
    if (current_[index] != pattern[index])
631✔
1308
      return false;
1309
  current_ += patternLength;
166✔
1310
  return true;
166✔
1311
}
1312

1313
bool OurReader::readComment() {
1,622✔
1314
  const Location commentBegin = current_ - 1;
1,622✔
1315
  const Char c = getNextChar();
1,622✔
1316
  bool successful = false;
1317
  bool cStyleWithEmbeddedNewline = false;
1,622✔
1318

1319
  const bool isCStyleComment = (c == '*');
1320
  const bool isCppStyleComment = (c == '/');
1321
  if (isCStyleComment) {
1,622✔
1322
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1,087✔
1323
  } else if (isCppStyleComment) {
535!
1324
    successful = readCppStyleComment();
535✔
1325
  }
1326

1327
  if (!successful)
1,622!
1328
    return false;
×
1329

1330
  if (collectComments_) {
1,622✔
1331
    CommentPlacement placement = commentBefore;
1332

1333
    if (!lastValueHasAComment_) {
1,604✔
1334
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
440✔
1335
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
67✔
1336
          placement = commentAfterOnSameLine;
1337
        }
1338
      }
1339
      // The gap between the last value and this comment only grows as more
1340
      // comments are consumed, so a later comment can never be on the same
1341
      // line as that value. Mark it handled to avoid re-scanning the same
1342
      // growing prefix for every following comment (quadratic behavior).
1343
      lastValueHasAComment_ = true;
440✔
1344
    }
1345

1346
    addComment(commentBegin, current_, placement);
1,604✔
1347
  }
1348
  return true;
1349
}
1350

1351
String OurReader::normalizeEOL(OurReader::Location begin,
1,604✔
1352
                               OurReader::Location end) {
1353
  String normalized;
1354
  normalized.reserve(static_cast<size_t>(end - begin));
1,604✔
1355
  OurReader::Location current = begin;
1356
  while (current != end) {
28,294✔
1357
    char c = *current++;
26,690✔
1358
    if (c == '\r') {
26,690✔
1359
      if (current != end && *current == '\n')
3!
1360
        // convert dos EOL
1361
        ++current;
1✔
1362
      // convert Mac EOL
1363
      normalized += '\n';
1364
    } else {
1365
      normalized += c;
1366
    }
1367
  }
1368
  return normalized;
1,604✔
1369
}
1370

1371
void OurReader::addComment(Location begin, Location end,
1,604✔
1372
                           CommentPlacement placement) {
1373
  assert(collectComments_);
1,604!
1374
  const String& normalized = normalizeEOL(begin, end);
1,604✔
1375
  if (placement == commentAfterOnSameLine) {
1,604✔
1376
    assert(lastValue_ != nullptr);
65!
1377
    lastValue_->setComment(normalized, placement);
130✔
1378
  } else {
1379
    commentsBefore_ += normalized;
1,539✔
1380
  }
1381
}
1,604✔
1382

1383
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,087✔
1384
  *containsNewLineResult = false;
1,087✔
1385

1386
  while ((current_ + 1) < end_) {
6,025!
1387
    Char c = getNextChar();
4,938✔
1388
    if (c == '*' && *current_ == '/')
4,938!
1389
      break;
1390
    if (c == '\n')
3,851✔
1391
      *containsNewLineResult = true;
98✔
1392
  }
1393

1394
  return getNextChar() == '/';
1,087✔
1395
}
1396

1397
bool OurReader::readCppStyleComment() {
535✔
1398
  while (current_ != end_) {
18,141✔
1399
    Char c = getNextChar();
18,135✔
1400
    if (c == '\n')
18,135✔
1401
      break;
1402
    if (c == '\r') {
17,609✔
1403
      // Consume DOS EOL. It will be normalized in addComment.
1404
      if (current_ != end_ && *current_ == '\n')
3!
1405
        getNextChar();
1✔
1406
      // Break on Moc OS 9 EOL.
1407
      break;
1408
    }
1409
  }
1410
  return true;
535✔
1411
}
1412

1413
bool OurReader::readNumber(bool checkInf) {
52,890✔
1414
  Location p = current_;
52,890✔
1415
  if (checkInf && p != end_ && *p == 'I') {
52,890!
1416
    current_ = ++p;
6✔
1417
    return false;
6✔
1418
  }
1419
  char c = '0'; // stopgap for already consumed character
1420
  // integral part
1421
  while (c >= '0' && c <= '9')
235,547✔
1422
    c = (current_ = p) < end_ ? *p++ : '\0';
182,663✔
1423
  // fractional part
1424
  if (c == '.') {
52,884✔
1425
    c = (current_ = p) < end_ ? *p++ : '\0';
116!
1426
    while (c >= '0' && c <= '9')
809✔
1427
      c = (current_ = p) < end_ ? *p++ : '\0';
693✔
1428
  }
1429
  // exponential part
1430
  if (c == 'e' || c == 'E') {
52,884✔
1431
    c = (current_ = p) < end_ ? *p++ : '\0';
109!
1432
    if (c == '+' || c == '-')
109✔
1433
      c = (current_ = p) < end_ ? *p++ : '\0';
81!
1434
    while (c >= '0' && c <= '9')
324✔
1435
      c = (current_ = p) < end_ ? *p++ : '\0';
215✔
1436
  }
1437
  return true;
1438
}
1439
bool OurReader::readString() {
1,178✔
1440
  Char c = 0;
1441
  while (current_ != end_) {
34,546!
1442
    c = getNextChar();
34,546✔
1443
    if (c == '\\')
34,546✔
1444
      getNextChar();
1,279✔
1445
    else if (c == '"')
33,267✔
1446
      break;
1447
  }
1448
  return c == '"';
1,178✔
1449
}
1450

1451
bool OurReader::readStringSingleQuote() {
11✔
1452
  Char c = 0;
1453
  while (current_ != end_) {
26!
1454
    c = getNextChar();
26✔
1455
    if (c == '\\')
26✔
1456
      getNextChar();
2✔
1457
    else if (c == '\'')
24✔
1458
      break;
1459
  }
1460
  return c == '\'';
11✔
1461
}
1462

1463
bool OurReader::readObject(Token& token) {
369✔
1464
  Token tokenName;
1465
  String name;
1466
  Value init(objectValue);
369✔
1467
  currentValue().swapPayload(init);
369✔
1468
  currentValue().setOffsetStart(token.start_ - begin_);
369✔
1469
  while (readTokenSkippingComments(tokenName)) {
694✔
1470
    if (tokenName.type_ == tokenObjectEnd &&
685✔
1471
        (name.empty() ||
7✔
1472
         features_.allowTrailingCommas_)) // empty object or trailing comma
7!
1473
      return true;
342✔
1474
    name.clear();
1475
    if (tokenName.type_ == tokenString) {
658✔
1476
      if (!decodeString(tokenName, name))
638!
1477
        return recoverFromError(tokenObjectEnd);
×
1478
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
20✔
1479
      Value numberName;
3✔
1480
      if (!decodeNumber(tokenName, numberName))
3!
1481
        return recoverFromError(tokenObjectEnd);
×
1482
      name = numberName.asString();
3✔
1483
    } else {
3✔
1484
      break;
1485
    }
1486
    if (name.length() >= (1U << 30))
641!
1487
      throwRuntimeError("keylength >= 2^30");
×
1488
    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
641✔
1489
      String msg = "Duplicate key: '" + name + "'";
1✔
1490
      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1✔
1491
    }
1492

1493
    Token colon;
1494
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
640!
1495
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1496
                                tokenObjectEnd);
1497
    }
1498
    Value& value = currentValue()[name];
633✔
1499
    nodes_.push(&value);
633!
1500
    bool ok = readValue();
633✔
1501
    nodes_.pop();
632✔
1502
    if (!ok) // error already set
632✔
1503
      return recoverFromError(tokenObjectEnd);
22✔
1504

1505
    Token comma;
1506
    if (!readTokenSkippingComments(comma) ||
610✔
1507
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
602✔
1508
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1509
                                comma, tokenObjectEnd);
1510
    }
1511
    if (comma.type_ == tokenObjectEnd)
583✔
1512
      return true;
1513
  }
1514
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1515
                            tokenObjectEnd);
1516
}
369✔
1517

1518
bool OurReader::readArray(Token& token) {
2,166✔
1519
  Value init(arrayValue);
2,166✔
1520
  currentValue().swapPayload(init);
2,166✔
1521
  currentValue().setOffsetStart(token.start_ - begin_);
2,166✔
1522
  int index = 0;
1523
  for (;;) {
1524
    // Skip comments too, so a ']' that follows a trailing comma (or comments in
1525
    // an otherwise empty array) is recognized rather than mistaken for the
1526
    // start of another value. See issue #1500.
1527
    skipCommentTokens();
54,578✔
1528
    if (current_ != end_ && *current_ == ']' &&
54,578!
1529
        (index == 0 ||
17✔
1530
         (features_.allowTrailingCommas_ &&
17!
1531
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
17✔
1532
                                                      // comma
1533
    {
1534
      Token endArray;
1535
      readToken(endArray);
32✔
1536
      return true;
1537
    }
1538
    Value& value = currentValue()[index++];
54,546✔
1539
    nodes_.push(&value);
54,546✔
1540
    bool ok = readValue();
54,546✔
1541
    nodes_.pop();
52,754✔
1542
    if (!ok) // error already set
52,754✔
1543
      return recoverFromError(tokenArrayEnd);
50✔
1544

1545
    Token currentToken;
1546
    // Accept Comment after last item in the array.
1547
    ok = readTokenSkippingComments(currentToken);
52,704✔
1548
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,704✔
1549
                         currentToken.type_ != tokenArrayEnd);
52,704✔
1550
    if (!ok || badTokenType) {
52,704✔
1551
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
48✔
1552
                                currentToken, tokenArrayEnd);
1553
    }
1554
    if (currentToken.type_ == tokenArrayEnd)
52,680✔
1555
      break;
1556
  }
52,412✔
1557
  return true;
268✔
1558
}
2,166✔
1559

1560
bool OurReader::decodeNumber(Token& token) {
52,849✔
1561
  Value decoded;
52,849✔
1562
  if (!decodeNumber(token, decoded))
52,849✔
1563
    return false;
1564
  currentValue().swapPayload(decoded);
52,838✔
1565
  currentValue().setOffsetStart(token.start_ - begin_);
52,838✔
1566
  currentValue().setOffsetLimit(token.end_ - begin_);
52,838✔
1567
  return true;
1568
}
52,849✔
1569

1570
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,852✔
1571
  // Attempts to parse the number as an integer. If the number is
1572
  // larger than the maximum supported value of an integer then
1573
  // we decode the number as a double.
1574
  Location current = token.start_;
52,852✔
1575
  const bool isNegative = *current == '-';
52,852✔
1576
  if (isNegative) {
52,852✔
1577
    ++current;
142✔
1578
  }
1579

1580
  // We assume we can represent the largest and smallest integer types as
1581
  // unsigned integers with separate sign. This is only true if they can fit
1582
  // into an unsigned integer.
1583
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1584
                "Int must be smaller than UInt");
1585

1586
  // We need to convert minLargestInt into a positive number. The easiest way
1587
  // to do this conversion is to assume our "threshold" value of minLargestInt
1588
  // divided by 10 can fit in maxLargestInt when absolute valued. This should
1589
  // be a safe assumption.
1590
  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1591
                "The absolute value of minLargestInt must be greater than or "
1592
                "equal to maxLargestInt");
1593
  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1594
                "The absolute value of minLargestInt must be only 1 magnitude "
1595
                "larger than maxLargest Int");
1596

1597
  static constexpr Value::LargestUInt positive_threshold =
1598
      Value::maxLargestUInt / 10;
1599
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1600

1601
  // For the negative values, we have to be more careful. Since typically
1602
  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1603
  // then take the inverse. This assumes that minLargestInt is only a single
1604
  // power of 10 different in magnitude, which we check above. For the last
1605
  // digit, we take the modulus before negating for the same reason.
1606
  static constexpr auto negative_threshold =
1607
      Value::LargestUInt(-(Value::minLargestInt / 10));
1608
  static constexpr auto negative_last_digit =
1609
      Value::UInt(-(Value::minLargestInt % 10));
1610

1611
  const Value::LargestUInt threshold =
1612
      isNegative ? negative_threshold : positive_threshold;
1613
  const Value::UInt max_last_digit =
1614
      isNegative ? negative_last_digit : positive_last_digit;
1615

1616
  Value::LargestUInt value = 0;
1617
  while (current < token.end_) {
235,312✔
1618
    Char c = *current++;
182,662✔
1619
    if (c < '0' || c > '9')
182,662✔
1620
      return decodeDouble(token, decoded);
183✔
1621

1622
    const auto digit(static_cast<Value::UInt>(c - '0'));
182,479✔
1623
    if (value >= threshold) {
182,479✔
1624
      // We've hit or exceeded the max value divided by 10 (rounded down). If
1625
      // a) we've only just touched the limit, meaning value == threshold,
1626
      // b) this is the last digit, or
1627
      // c) it's small enough to fit in that rounding delta, we're okay.
1628
      // Otherwise treat this number as a double to avoid overflow.
1629
      if (value > threshold || current != token.end_ ||
43!
1630
          digit > max_last_digit) {
1631
        return decodeDouble(token, decoded);
19✔
1632
      }
1633
    }
1634
    value = value * 10 + digit;
182,460✔
1635
  }
1636

1637
  if (isNegative) {
52,650✔
1638
    // We use the same magnitude assumption here, just in case.
1639
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1640
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1641
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,561✔
1642
    decoded = Value::LargestInt(value);
52,537✔
1643
  } else {
1644
    decoded = value;
24✔
1645
  }
1646

1647
  return true;
1648
}
1649

1650
bool OurReader::decodeDouble(Token& token) {
×
1651
  Value decoded;
×
1652
  if (!decodeDouble(token, decoded))
×
1653
    return false;
1654
  currentValue().swapPayload(decoded);
×
1655
  currentValue().setOffsetStart(token.start_ - begin_);
×
1656
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1657
  return true;
1658
}
×
1659

1660
bool OurReader::decodeDouble(Token& token, Value& decoded) {
202✔
1661
  double value = 0;
202✔
1662
  IStringStream is(String(token.start_, token.end_));
202✔
1663
  is.imbue(std::locale::classic());
202✔
1664
  if (!(is >> value)) {
202✔
1665
    if (value == std::numeric_limits<double>::max())
35✔
1666
      value = std::numeric_limits<double>::infinity();
12✔
1667
    else if (value == std::numeric_limits<double>::lowest())
23✔
1668
      value = -std::numeric_limits<double>::infinity();
12✔
1669
    // operator>> sets failbit for a subnormal result (underflow) even though
1670
    // it produced the correctly-rounded value, which made such numbers fail to
1671
    // parse back after jsoncpp serialized them. Keep a subnormal value instead
1672
    // of rejecting it. See issue #1427. Other failures -- malformed numbers
1673
    // like "0e" or "0e+", or non-numbers -- leave the value at zero/non-finite
1674
    // and are still rejected.
1675
    else if (!std::isinf(value) && std::fpclassify(value) != FP_SUBNORMAL)
11!
1676
      return addError(
11✔
1677
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
22✔
1678
  }
1679
  decoded = value;
191✔
1680
  return true;
191✔
1681
}
202✔
1682

1683
bool OurReader::decodeString(Token& token) {
522✔
1684
  String decoded_string;
1685
  if (!decodeString(token, decoded_string))
522✔
1686
    return false;
1687
  Value decoded(decoded_string);
497✔
1688
  currentValue().swapPayload(decoded);
497✔
1689
  currentValue().setOffsetStart(token.start_ - begin_);
497✔
1690
  currentValue().setOffsetLimit(token.end_ - begin_);
497✔
1691
  return true;
1692
}
497✔
1693

1694
bool OurReader::decodeString(Token& token, String& decoded) {
1,160✔
1695
  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1,160✔
1696
  Location current = token.start_ + 1; // skip '"'
1,160✔
1697
  Location end = token.end_ - 1;       // do not include '"'
1,160✔
1698
  while (current != end) {
33,569✔
1699
    Char c = *current++;
32,434✔
1700
    if (c == '"')
32,434!
1701
      break;
1702
    if (c == '\\') {
32,434✔
1703
      if (current == end)
1,257!
1704
        return addError("Empty escape sequence in string", token, current);
×
1705
      Char escape = *current++;
1,257✔
1706
      switch (escape) {
1,257✔
1707
      case '"':
1708
        decoded += '"';
1709
        break;
1710
      case '/':
1711
        decoded += '/';
1712
        break;
1713
      case '\\':
1714
        decoded += '\\';
1715
        break;
1716
      case 'b':
1717
        decoded += '\b';
1718
        break;
1719
      case 'f':
1720
        decoded += '\f';
1721
        break;
1722
      case 'n':
1723
        decoded += '\n';
1724
        break;
1725
      case 'r':
1726
        decoded += '\r';
1727
        break;
1728
      case 't':
1729
        decoded += '\t';
1730
        break;
1731
      case 'u': {
148✔
1732
        unsigned int unicode;
1733
        if (!decodeUnicodeCodePoint(token, current, end, unicode))
148✔
1734
          return false;
6✔
1735
        decoded += codePointToUTF8(unicode);
142✔
1736
      } break;
142✔
1737
      default:
13✔
1738
        return addError("Bad escape sequence in string", token, current);
26✔
1739
      }
1740
    } else {
1741
      if (static_cast<unsigned char>(c) < 0x20)
31,177✔
1742
        return addError("Control character in string", token, current - 1);
12✔
1743
      decoded += c;
1744
    }
1745
  }
1746
  return true;
1747
}
1748

1749
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
148✔
1750
                                       Location end, unsigned int& unicode) {
1751

1752
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
148✔
1753
    return false;
1754
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
146✔
1755
    // surrogate pairs
1756
    if (end - current < 6)
16✔
1757
      return addError(
2✔
1758
          "additional six characters expected to parse unicode surrogate pair.",
1759
          token, current);
1760
    if (*(current++) == '\\' && *(current++) == 'u') {
15!
1761
      unsigned int surrogatePair;
1762
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
14!
1763
        if (features_.rejectInvalidSurrogates_ &&
14!
1764
            (surrogatePair < 0xDC00 || surrogatePair > 0xDFFF))
14✔
1765
          return addError("expecting a low surrogate (DC00-DFFF) to complete "
2✔
1766
                          "the unicode surrogate pair",
1767
                          token, current);
1768
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1769
      } else
1770
        return false;
1771
    } else
1772
      return addError("expecting another \\u token to begin the second half of "
2✔
1773
                      "a unicode surrogate pair",
1774
                      token, current);
1775
  } else if (features_.rejectInvalidSurrogates_ && unicode >= 0xDC00 &&
130✔
1776
             unicode <= 0xDFFF) {
1777
    return addError("unexpected low surrogate (DC00-DFFF); a high surrogate "
2✔
1778
                    "(D800-DBFF) must come first",
1779
                    token, current);
1780
  }
1781
  return true;
1782
}
1783

1784
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
162✔
1785
                                            Location end,
1786
                                            unsigned int& ret_unicode) {
1787
  if (end - current < 4)
162✔
1788
    return addError(
2✔
1789
        "Bad unicode escape sequence in string: four digits expected.", token,
1790
        current);
1791
  int unicode = 0;
1792
  for (int index = 0; index < 4; ++index) {
803✔
1793
    Char c = *current++;
643✔
1794
    unicode *= 16;
643✔
1795
    if (c >= '0' && c <= '9')
643✔
1796
      unicode += c - '0';
411✔
1797
    else if (c >= 'a' && c <= 'f')
232✔
1798
      unicode += c - 'a' + 10;
115✔
1799
    else if (c >= 'A' && c <= 'F')
117✔
1800
      unicode += c - 'A' + 10;
116✔
1801
    else
1802
      return addError(
2✔
1803
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1804
          token, current);
1805
  }
1806
  ret_unicode = static_cast<unsigned int>(unicode);
160✔
1807
  return true;
160✔
1808
}
1809

1810
bool OurReader::addError(const String& message, Token& token, Location extra) {
167✔
1811
  ErrorInfo info;
1812
  info.token_ = token;
167✔
1813
  info.message_ = message;
1814
  info.extra_ = extra;
167✔
1815
  errors_.push_back(info);
167✔
1816
  return false;
167✔
1817
}
1818

1819
bool OurReader::recoverFromError(TokenType skipUntilToken) {
157✔
1820
  size_t errorCount = errors_.size();
1821
  Token skip;
1822
  for (;;) {
1823
    if (!readToken(skip))
417✔
1824
      errors_.resize(errorCount); // discard errors caused by recovery
175✔
1825
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
417✔
1826
      break;
1827
  }
1828
  errors_.resize(errorCount);
157✔
1829
  return false;
157✔
1830
}
1831

1832
bool OurReader::addErrorAndRecover(const String& message, Token& token,
85✔
1833
                                   TokenType skipUntilToken) {
1834
  addError(message, token);
85✔
1835
  return recoverFromError(skipUntilToken);
85✔
1836
}
1837

1838
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,839✔
1839

1840
OurReader::Char OurReader::getNextChar() {
175,393✔
1841
  if (current_ == end_)
175,393✔
1842
    return 0;
1843
  return *current_++;
174,432✔
1844
}
1845

1846
void OurReader::getLocationLineAndColumn(Location location, int& line,
191✔
1847
                                         int& column) const {
1848
  Location current = begin_;
191✔
1849
  Location lastLineStart = current;
1850
  line = 0;
191✔
1851
  while (current < location && current != end_) {
2,310!
1852
    Char c = *current++;
2,119✔
1853
    if (c == '\r') {
2,119✔
1854
      if (current != end_ && *current == '\n')
1!
1855
        ++current;
×
1856
      lastLineStart = current;
1857
      ++line;
1✔
1858
    } else if (c == '\n') {
2,118✔
1859
      lastLineStart = current;
1860
      ++line;
28✔
1861
    }
1862
  }
1863
  // column & line start at 1
1864
  column = int(location - lastLineStart) + 1;
191✔
1865
  ++line;
191✔
1866
}
191✔
1867

1868
String OurReader::getLocationLineAndColumn(Location location) const {
191✔
1869
  int line, column;
1870
  getLocationLineAndColumn(location, line, column);
191✔
1871
  char buffer[18 + 16 + 16 + 1];
1872
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
191✔
1873
  return buffer;
191✔
1874
}
1875

1876
String OurReader::getFormattedErrorMessages() const {
940✔
1877
  String formattedMessage;
1878
  for (const auto& error : errors_) {
1,106✔
1879
    formattedMessage +=
1880
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
166✔
1881
    formattedMessage += "  " + error.message_ + "\n";
166✔
1882
    if (error.extra_)
166✔
1883
      formattedMessage +=
1884
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
50✔
1885
  }
1886
  return formattedMessage;
940✔
1887
}
1888

1889
std::vector<CharReader::StructuredError>
1890
OurReader::getStructuredErrors() const {
2✔
1891
  std::vector<CharReader::StructuredError> allErrors;
1892
  for (const auto& error : errors_) {
3✔
1893
    CharReader::StructuredError structured;
1894
    structured.offset_start = error.token_.start_ - begin_;
1✔
1895
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1896
    structured.message = error.message_;
1✔
1897
    allErrors.push_back(structured);
1✔
1898
  }
1899
  return allErrors;
2✔
1900
}
×
1901

1902
class OurCharReader : public CharReader {
1903

1904
public:
1905
  OurCharReader(bool collectComments, OurFeatures const& features)
905✔
1906
      : CharReader(
905✔
1907
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,810✔
1908

1909
protected:
1910
  class OurImpl : public Impl {
1911
  public:
1912
    OurImpl(bool collectComments, OurFeatures const& features)
1913
        : collectComments_(collectComments), reader_(features) {}
905✔
1914

1915
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
950✔
1916
               String* errs) override {
1917
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
950✔
1918
      if (errs) {
942✔
1919
        *errs = reader_.getFormattedErrorMessages();
1,880✔
1920
      }
1921
      return ok;
942✔
1922
    }
1923

1924
    std::vector<CharReader::StructuredError>
1925
    getStructuredErrors() const override {
2✔
1926
      return reader_.getStructuredErrors();
2✔
1927
    }
1928

1929
  private:
1930
    bool const collectComments_;
1931
    OurReader reader_;
1932
  };
1933
};
1934

1935
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
902✔
1936
CharReaderBuilder::~CharReaderBuilder() = default;
902✔
1937
CharReader* CharReaderBuilder::newCharReader() const {
905✔
1938
  bool collectComments = settings_["collectComments"].asBool();
905✔
1939
  OurFeatures features = OurFeatures::all();
905✔
1940
  features.allowComments_ = settings_["allowComments"].asBool();
905✔
1941
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
905✔
1942
  features.strictRoot_ = settings_["strictRoot"].asBool();
905✔
1943
  features.allowDroppedNullPlaceholders_ =
905✔
1944
      settings_["allowDroppedNullPlaceholders"].asBool();
905✔
1945
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
905✔
1946
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
905✔
1947

1948
  // Stack limit is always a size_t, so we get this as an unsigned int
1949
  // regardless of it we have 64-bit integer support enabled.
1950
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
905✔
1951
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
905✔
1952
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
905✔
1953
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
905✔
1954
  features.rejectInvalidSurrogates_ =
905✔
1955
      settings_["rejectInvalidSurrogates"].asBool();
905✔
1956
  features.skipBom_ = settings_["skipBom"].asBool();
905✔
1957
  return new OurCharReader(collectComments, features);
905✔
1958
}
1959

1960
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1961
  static const auto& valid_keys = *new std::set<String>{
1962
      "collectComments",
1963
      "allowComments",
1964
      "allowTrailingCommas",
1965
      "strictRoot",
1966
      "allowDroppedNullPlaceholders",
1967
      "allowNumericKeys",
1968
      "allowSingleQuotes",
1969
      "stackLimit",
1970
      "failIfExtra",
1971
      "rejectDupKeys",
1972
      "allowSpecialFloats",
1973
      "rejectInvalidSurrogates",
1974
      "skipBom",
1975
  };
16!
1976
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
58✔
1977
    auto key = si.name();
27✔
1978
    if (valid_keys.count(key))
27✔
1979
      continue;
1980
    if (invalid)
1!
1981
      (*invalid)[key] = *si;
1✔
1982
    else
1983
      return false;
1984
  }
1985
  return invalid ? invalid->empty() : true;
2!
1986
}
2!
1987

1988
Value& CharReaderBuilder::operator[](const String& key) {
2✔
1989
  return settings_[key];
2✔
1990
}
1991
// static
1992
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1993
  //! [CharReaderBuilderStrictMode]
1994
  (*settings)["allowComments"] = false;
3✔
1995
  (*settings)["allowTrailingCommas"] = false;
3✔
1996
  (*settings)["strictRoot"] = true;
3✔
1997
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1998
  (*settings)["allowNumericKeys"] = false;
3✔
1999
  (*settings)["allowSingleQuotes"] = false;
3✔
2000
  (*settings)["stackLimit"] = 256;
3✔
2001
  (*settings)["failIfExtra"] = true;
3✔
2002
  (*settings)["rejectDupKeys"] = true;
3✔
2003
  (*settings)["allowSpecialFloats"] = false;
3✔
2004
  (*settings)["rejectInvalidSurrogates"] = true;
3✔
2005
  (*settings)["skipBom"] = true;
3✔
2006
  //! [CharReaderBuilderStrictMode]
2007
}
3✔
2008
// static
2009
void CharReaderBuilder::setDefaults(Json::Value* settings) {
902✔
2010
  //! [CharReaderBuilderDefaults]
2011
  (*settings)["collectComments"] = true;
902✔
2012
  (*settings)["allowComments"] = true;
902✔
2013
  (*settings)["allowTrailingCommas"] = true;
902✔
2014
  (*settings)["strictRoot"] = false;
902✔
2015
  (*settings)["allowDroppedNullPlaceholders"] = false;
902✔
2016
  (*settings)["allowNumericKeys"] = false;
902✔
2017
  (*settings)["allowSingleQuotes"] = false;
902✔
2018
  (*settings)["stackLimit"] = 256;
902✔
2019
  (*settings)["failIfExtra"] = false;
902✔
2020
  (*settings)["rejectDupKeys"] = false;
902✔
2021
  (*settings)["allowSpecialFloats"] = false;
902✔
2022
  (*settings)["rejectInvalidSurrogates"] = true;
902✔
2023
  (*settings)["skipBom"] = true;
902✔
2024
  //! [CharReaderBuilderDefaults]
2025
}
902✔
2026
// static
2027
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
2028
  //! [CharReaderBuilderECMA404Mode]
2029
  (*settings)["allowComments"] = false;
×
2030
  (*settings)["allowTrailingCommas"] = false;
×
2031
  (*settings)["strictRoot"] = false;
×
2032
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
2033
  (*settings)["allowNumericKeys"] = false;
×
2034
  (*settings)["allowSingleQuotes"] = false;
×
2035
  (*settings)["stackLimit"] = 256;
×
2036
  (*settings)["failIfExtra"] = true;
×
2037
  (*settings)["rejectDupKeys"] = false;
×
2038
  (*settings)["allowSpecialFloats"] = false;
×
NEW
2039
  (*settings)["rejectInvalidSurrogates"] = false;
×
UNCOV
2040
  (*settings)["skipBom"] = false;
×
2041
  //! [CharReaderBuilderECMA404Mode]
2042
}
×
2043

2044
std::vector<CharReader::StructuredError>
2045
CharReader::getStructuredErrors() const {
2✔
2046
  return _impl->getStructuredErrors();
2✔
2047
}
2048

2049
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
950✔
2050
                       String* errs) {
2051
  return _impl->parse(beginDoc, endDoc, root, errs);
950✔
2052
}
2053

2054
//////////////////////////////////
2055
// global functions
2056

2057
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
2058
                     String* errs) {
2059
  OStringStream ssin;
4✔
2060
  ssin << sin.rdbuf();
4✔
2061
  String doc = std::move(ssin).str();
2062
  char const* begin = doc.data();
2063
  char const* end = begin + doc.size();
4✔
2064
  // Note that we do not actually need a null-terminator.
2065
  CharReaderPtr const reader(fact.newCharReader());
4✔
2066
  return reader->parse(begin, end, root, errs);
8✔
2067
}
4✔
2068

2069
IStream& operator>>(IStream& sin, Value& root) {
1✔
2070
  CharReaderBuilder b;
1✔
2071
  String errs;
2072
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2073
  if (!ok) {
1!
2074
    throwRuntimeError(errs);
×
2075
  }
2076
  return sin;
1✔
2077
}
1✔
2078

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