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

open-source-parsers / jsoncpp / 27673874228

17 Jun 2026 07:46AM UTC coverage: 89.903% (-0.06%) from 89.959%
27673874228

Pull #1696

github

web-flow
Merge branch 'master' into fix/1500-trailing-comma-comment
Pull Request #1696: fix: allow a comment between a trailing comma and ']' (#1500)

2220 of 2638 branches covered (84.15%)

Branch coverage included in aggregate %.

8 of 9 new or added lines in 1 file covered. (88.89%)

2615 of 2740 relevant lines covered (95.44%)

23827.05 hits per line

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

89.88
/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 skipBom_;
865
  size_t stackLimit_;
866
}; // OurFeatures
867

868
OurFeatures OurFeatures::all() { return {}; }
904✔
869

870
// Implementation of class Reader
871
// ////////////////////////////////
872

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1002
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,808✔
1003

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

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

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

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

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

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

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

1139
  return successful;
1140
}
1141

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1646
  return true;
1647
}
1648

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

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

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

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

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

1751
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1752
    return false;
1753
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1754
    // surrogate pairs
1755
    if (end - current < 6)
15✔
1756
      return addError(
2✔
1757
          "additional six characters expected to parse unicode surrogate pair.",
1758
          token, current);
1759
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1760
      unsigned int surrogatePair;
1761
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1762
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1763
      } else
1764
        return false;
×
1765
    } else
1766
      return addError("expecting another \\u token to begin the second half of "
2✔
1767
                      "a unicode surrogate pair",
1768
                      token, current);
1769
  }
1770
  return true;
1771
}
1772

1773
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
158✔
1774
                                            Location end,
1775
                                            unsigned int& ret_unicode) {
1776
  if (end - current < 4)
158✔
1777
    return addError(
2✔
1778
        "Bad unicode escape sequence in string: four digits expected.", token,
1779
        current);
1780
  int unicode = 0;
1781
  for (int index = 0; index < 4; ++index) {
783✔
1782
    Char c = *current++;
627✔
1783
    unicode *= 16;
627✔
1784
    if (c >= '0' && c <= '9')
627✔
1785
      unicode += c - '0';
400✔
1786
    else if (c >= 'a' && c <= 'f')
227✔
1787
      unicode += c - 'a' + 10;
115✔
1788
    else if (c >= 'A' && c <= 'F')
112✔
1789
      unicode += c - 'A' + 10;
111✔
1790
    else
1791
      return addError(
2✔
1792
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1793
          token, current);
1794
  }
1795
  ret_unicode = static_cast<unsigned int>(unicode);
156✔
1796
  return true;
156✔
1797
}
1798

1799
bool OurReader::addError(const String& message, Token& token, Location extra) {
165✔
1800
  ErrorInfo info;
1801
  info.token_ = token;
165✔
1802
  info.message_ = message;
1803
  info.extra_ = extra;
165✔
1804
  errors_.push_back(info);
165✔
1805
  return false;
165✔
1806
}
1807

1808
bool OurReader::recoverFromError(TokenType skipUntilToken) {
155✔
1809
  size_t errorCount = errors_.size();
1810
  Token skip;
1811
  for (;;) {
1812
    if (!readToken(skip))
415✔
1813
      errors_.resize(errorCount); // discard errors caused by recovery
175✔
1814
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
415✔
1815
      break;
1816
  }
1817
  errors_.resize(errorCount);
155✔
1818
  return false;
155✔
1819
}
1820

1821
bool OurReader::addErrorAndRecover(const String& message, Token& token,
85✔
1822
                                   TokenType skipUntilToken) {
1823
  addError(message, token);
85✔
1824
  return recoverFromError(skipUntilToken);
85✔
1825
}
1826

1827
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,818✔
1828

1829
OurReader::Char OurReader::getNextChar() {
175,354✔
1830
  if (current_ == end_)
175,354✔
1831
    return 0;
1832
  return *current_++;
174,396✔
1833
}
1834

1835
void OurReader::getLocationLineAndColumn(Location location, int& line,
187✔
1836
                                         int& column) const {
1837
  Location current = begin_;
187✔
1838
  Location lastLineStart = current;
1839
  line = 0;
187✔
1840
  while (current < location && current != end_) {
2,278!
1841
    Char c = *current++;
2,091✔
1842
    if (c == '\r') {
2,091✔
1843
      if (current != end_ && *current == '\n')
1!
1844
        ++current;
×
1845
      lastLineStart = current;
1846
      ++line;
1✔
1847
    } else if (c == '\n') {
2,090✔
1848
      lastLineStart = current;
1849
      ++line;
28✔
1850
    }
1851
  }
1852
  // column & line start at 1
1853
  column = int(location - lastLineStart) + 1;
187✔
1854
  ++line;
187✔
1855
}
187✔
1856

1857
String OurReader::getLocationLineAndColumn(Location location) const {
187✔
1858
  int line, column;
1859
  getLocationLineAndColumn(location, line, column);
187✔
1860
  char buffer[18 + 16 + 16 + 1];
1861
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
187✔
1862
  return buffer;
187✔
1863
}
1864

1865
String OurReader::getFormattedErrorMessages() const {
937✔
1866
  String formattedMessage;
1867
  for (const auto& error : errors_) {
1,101✔
1868
    formattedMessage +=
1869
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
164✔
1870
    formattedMessage += "  " + error.message_ + "\n";
164✔
1871
    if (error.extra_)
164✔
1872
      formattedMessage +=
1873
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
46✔
1874
  }
1875
  return formattedMessage;
937✔
1876
}
1877

1878
std::vector<CharReader::StructuredError>
1879
OurReader::getStructuredErrors() const {
2✔
1880
  std::vector<CharReader::StructuredError> allErrors;
1881
  for (const auto& error : errors_) {
3✔
1882
    CharReader::StructuredError structured;
1883
    structured.offset_start = error.token_.start_ - begin_;
1✔
1884
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1885
    structured.message = error.message_;
1✔
1886
    allErrors.push_back(structured);
1✔
1887
  }
1888
  return allErrors;
2✔
1889
}
×
1890

1891
class OurCharReader : public CharReader {
1892

1893
public:
1894
  OurCharReader(bool collectComments, OurFeatures const& features)
904✔
1895
      : CharReader(
904✔
1896
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,808✔
1897

1898
protected:
1899
  class OurImpl : public Impl {
1900
  public:
1901
    OurImpl(bool collectComments, OurFeatures const& features)
1902
        : collectComments_(collectComments), reader_(features) {}
904✔
1903

1904
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
947✔
1905
               String* errs) override {
1906
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
947✔
1907
      if (errs) {
939✔
1908
        *errs = reader_.getFormattedErrorMessages();
1,874✔
1909
      }
1910
      return ok;
939✔
1911
    }
1912

1913
    std::vector<CharReader::StructuredError>
1914
    getStructuredErrors() const override {
2✔
1915
      return reader_.getStructuredErrors();
2✔
1916
    }
1917

1918
  private:
1919
    bool const collectComments_;
1920
    OurReader reader_;
1921
  };
1922
};
1923

1924
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
901✔
1925
CharReaderBuilder::~CharReaderBuilder() = default;
901✔
1926
CharReader* CharReaderBuilder::newCharReader() const {
904✔
1927
  bool collectComments = settings_["collectComments"].asBool();
904✔
1928
  OurFeatures features = OurFeatures::all();
904✔
1929
  features.allowComments_ = settings_["allowComments"].asBool();
904✔
1930
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
904✔
1931
  features.strictRoot_ = settings_["strictRoot"].asBool();
904✔
1932
  features.allowDroppedNullPlaceholders_ =
904✔
1933
      settings_["allowDroppedNullPlaceholders"].asBool();
904✔
1934
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
904✔
1935
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
904✔
1936

1937
  // Stack limit is always a size_t, so we get this as an unsigned int
1938
  // regardless of it we have 64-bit integer support enabled.
1939
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
904✔
1940
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
904✔
1941
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
904✔
1942
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
904✔
1943
  features.skipBom_ = settings_["skipBom"].asBool();
904✔
1944
  return new OurCharReader(collectComments, features);
904✔
1945
}
1946

1947
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1948
  static const auto& valid_keys = *new std::set<String>{
1949
      "collectComments",
1950
      "allowComments",
1951
      "allowTrailingCommas",
1952
      "strictRoot",
1953
      "allowDroppedNullPlaceholders",
1954
      "allowNumericKeys",
1955
      "allowSingleQuotes",
1956
      "stackLimit",
1957
      "failIfExtra",
1958
      "rejectDupKeys",
1959
      "allowSpecialFloats",
1960
      "skipBom",
1961
  };
15!
1962
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
54✔
1963
    auto key = si.name();
25✔
1964
    if (valid_keys.count(key))
25✔
1965
      continue;
1966
    if (invalid)
1!
1967
      (*invalid)[key] = *si;
1✔
1968
    else
1969
      return false;
1970
  }
1971
  return invalid ? invalid->empty() : true;
2!
1972
}
2!
1973

1974
Value& CharReaderBuilder::operator[](const String& key) {
1✔
1975
  return settings_[key];
1✔
1976
}
1977
// static
1978
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1979
  //! [CharReaderBuilderStrictMode]
1980
  (*settings)["allowComments"] = false;
3✔
1981
  (*settings)["allowTrailingCommas"] = false;
3✔
1982
  (*settings)["strictRoot"] = true;
3✔
1983
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1984
  (*settings)["allowNumericKeys"] = false;
3✔
1985
  (*settings)["allowSingleQuotes"] = false;
3✔
1986
  (*settings)["stackLimit"] = 256;
3✔
1987
  (*settings)["failIfExtra"] = true;
3✔
1988
  (*settings)["rejectDupKeys"] = true;
3✔
1989
  (*settings)["allowSpecialFloats"] = false;
3✔
1990
  (*settings)["skipBom"] = true;
3✔
1991
  //! [CharReaderBuilderStrictMode]
1992
}
3✔
1993
// static
1994
void CharReaderBuilder::setDefaults(Json::Value* settings) {
901✔
1995
  //! [CharReaderBuilderDefaults]
1996
  (*settings)["collectComments"] = true;
901✔
1997
  (*settings)["allowComments"] = true;
901✔
1998
  (*settings)["allowTrailingCommas"] = true;
901✔
1999
  (*settings)["strictRoot"] = false;
901✔
2000
  (*settings)["allowDroppedNullPlaceholders"] = false;
901✔
2001
  (*settings)["allowNumericKeys"] = false;
901✔
2002
  (*settings)["allowSingleQuotes"] = false;
901✔
2003
  (*settings)["stackLimit"] = 256;
901✔
2004
  (*settings)["failIfExtra"] = false;
901✔
2005
  (*settings)["rejectDupKeys"] = false;
901✔
2006
  (*settings)["allowSpecialFloats"] = false;
901✔
2007
  (*settings)["skipBom"] = true;
901✔
2008
  //! [CharReaderBuilderDefaults]
2009
}
901✔
2010
// static
2011
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
2012
  //! [CharReaderBuilderECMA404Mode]
2013
  (*settings)["allowComments"] = false;
×
2014
  (*settings)["allowTrailingCommas"] = false;
×
2015
  (*settings)["strictRoot"] = false;
×
2016
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
2017
  (*settings)["allowNumericKeys"] = false;
×
2018
  (*settings)["allowSingleQuotes"] = false;
×
2019
  (*settings)["stackLimit"] = 256;
×
2020
  (*settings)["failIfExtra"] = true;
×
2021
  (*settings)["rejectDupKeys"] = false;
×
2022
  (*settings)["allowSpecialFloats"] = false;
×
2023
  (*settings)["skipBom"] = false;
×
2024
  //! [CharReaderBuilderECMA404Mode]
2025
}
×
2026

2027
std::vector<CharReader::StructuredError>
2028
CharReader::getStructuredErrors() const {
2✔
2029
  return _impl->getStructuredErrors();
2✔
2030
}
2031

2032
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
947✔
2033
                       String* errs) {
2034
  return _impl->parse(beginDoc, endDoc, root, errs);
947✔
2035
}
2036

2037
//////////////////////////////////
2038
// global functions
2039

2040
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
2041
                     String* errs) {
2042
  OStringStream ssin;
4✔
2043
  ssin << sin.rdbuf();
4✔
2044
  String doc = std::move(ssin).str();
2045
  char const* begin = doc.data();
2046
  char const* end = begin + doc.size();
4✔
2047
  // Note that we do not actually need a null-terminator.
2048
  CharReaderPtr const reader(fact.newCharReader());
4✔
2049
  return reader->parse(begin, end, root, errs);
8✔
2050
}
4✔
2051

2052
IStream& operator>>(IStream& sin, Value& root) {
1✔
2053
  CharReaderBuilder b;
1✔
2054
  String errs;
2055
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2056
  if (!ok) {
1!
2057
    throwRuntimeError(errs);
×
2058
  }
2059
  return sin;
1✔
2060
}
1✔
2061

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

© 2026 Coveralls, Inc