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

open-source-parsers / jsoncpp / 32423773260

20 Aug 2026 10:19PM UTC coverage: 89.949% (+0.04%) from 89.907%
32423773260

Pull #1711

github

baylesj
docs: pin workflow actions, drop fragile caller graphs

zizmor requires actions pinned to commit SHAs and persist-credentials
off. The caller graph for Json::Value::Value exceeded doxygen's node
limit in CI, which WARN_AS_ERROR turned into a failure; caller graphs of
the implementation add little to API docs, so drop them and leave
headroom in DOT_GRAPH_MAX_NODES for the remaining graphs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pull Request #1711: docs: modernize doxygen build and publish from this repository

2201 of 2614 branches covered (84.2%)

Branch coverage included in aggregate %.

2605 of 2729 relevant lines covered (95.46%)

23441.29 hits per line

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

90.03
/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
    else if (!std::isinf(value))
×
588
      return addError(
×
589
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
590
  }
591
  decoded = value;
145✔
592
  return true;
145✔
593
}
145✔
594

595
bool Reader::decodeString(Token& token) {
374✔
596
  String decoded_string;
597
  if (!decodeString(token, decoded_string))
374✔
598
    return false;
599
  Value decoded(decoded_string);
369✔
600
  currentValue().swapPayload(decoded);
369✔
601
  currentValue().setOffsetStart(token.start_ - begin_);
369✔
602
  currentValue().setOffsetLimit(token.end_ - begin_);
369✔
603
  return true;
604
}
369✔
605

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

661
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
662
                                    Location end, unsigned int& unicode) {
663

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

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

712
bool Reader::addError(const String& message, Token& token, Location extra) {
16✔
713
  ErrorInfo info;
714
  info.token_ = token;
16✔
715
  info.message_ = message;
716
  info.extra_ = extra;
16✔
717
  errors_.push_back(info);
16✔
718
  return false;
16✔
719
}
720

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

734
bool Reader::addErrorAndRecover(const String& message, Token& token,
5✔
735
                                TokenType skipUntilToken) {
736
  addError(message, token);
5✔
737
  return recoverFromError(skipUntilToken);
5✔
738
}
739

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

742
Reader::Char Reader::getNextChar() {
160,551✔
743
  if (current_ == end_)
160,551✔
744
    return 0;
745
  return *current_++;
159,833✔
746
}
747

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

770
String Reader::getLocationLineAndColumn(Location location) const {
24✔
771
  int line, column;
772
  getLocationLineAndColumn(location, line, column);
24✔
773
  char buffer[18 + 16 + 16 + 1];
774
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
24✔
775
  return buffer;
24✔
776
}
777

778
// Deprecated. Preserved for backward compatibility
779
String Reader::getFormatedErrorMessages() const {
×
780
  return getFormattedErrorMessages();
×
781
}
782

783
String Reader::getFormattedErrorMessages() const {
19✔
784
  String formattedMessage;
785
  for (const auto& error : errors_) {
37✔
786
    formattedMessage +=
787
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
18✔
788
    formattedMessage += "  " + error.message_ + "\n";
18✔
789
    if (error.extra_)
18✔
790
      formattedMessage +=
791
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
12✔
792
  }
793
  return formattedMessage;
19✔
794
}
795

796
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
17✔
797
  std::vector<Reader::StructuredError> allErrors;
798
  for (const auto& error : errors_) {
33✔
799
    Reader::StructuredError structured;
800
    structured.offset_start = error.token_.start_ - begin_;
16✔
801
    structured.offset_limit = error.token_.end_ - begin_;
16✔
802
    structured.message = error.message_;
16✔
803
    allErrors.push_back(structured);
16✔
804
  }
805
  return allErrors;
17✔
806
}
×
807

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

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

842
bool Reader::good() const { return errors_.empty(); }
×
843

844
// Originally copied from the Features class (now deprecated), used internally
845
// for features implementation.
846
class OurFeatures {
847
public:
848
  static OurFeatures all();
849
  bool allowComments_;
850
  bool allowTrailingCommas_;
851
  bool strictRoot_;
852
  bool allowDroppedNullPlaceholders_;
853
  bool allowNumericKeys_;
854
  bool allowSingleQuotes_;
855
  bool failIfExtra_;
856
  bool rejectDupKeys_;
857
  bool allowSpecialFloats_;
858
  bool skipBom_;
859
  size_t stackLimit_;
860
}; // OurFeatures
861

862
OurFeatures OurFeatures::all() { return {}; }
902✔
863

864
// Implementation of class Reader
865
// ////////////////////////////////
866

867
// Originally copied from the Reader class (now deprecated), used internally
868
// for implementing JSON reading.
869
class OurReader {
870
public:
871
  using Char = char;
872
  using Location = const Char*;
873

874
  explicit OurReader(OurFeatures const& features);
875
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
876
             bool collectComments = true);
877
  String getFormattedErrorMessages() const;
878
  std::vector<CharReader::StructuredError> getStructuredErrors() const;
879

880
private:
881
  OurReader(OurReader const&);      // no impl
882
  void operator=(OurReader const&); // no impl
883

884
  enum TokenType {
885
    tokenEndOfStream = 0,
886
    tokenObjectBegin,
887
    tokenObjectEnd,
888
    tokenArrayBegin,
889
    tokenArrayEnd,
890
    tokenString,
891
    tokenNumber,
892
    tokenTrue,
893
    tokenFalse,
894
    tokenNull,
895
    tokenNaN,
896
    tokenPosInf,
897
    tokenNegInf,
898
    tokenArraySeparator,
899
    tokenMemberSeparator,
900
    tokenComment,
901
    tokenError
902
  };
903

904
  class Token {
905
  public:
906
    TokenType type_;
907
    Location start_;
908
    Location end_;
909
  };
910

911
  class ErrorInfo {
162✔
912
  public:
913
    Token token_;
914
    String message_;
915
    Location extra_;
916
  };
917

918
  using Errors = std::deque<ErrorInfo>;
919

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

956
  static String normalizeEOL(Location begin, Location end);
957
  static bool containsNewLine(Location begin, Location end);
958

959
  using Nodes = std::stack<Value*>;
960

961
  Nodes nodes_{};
962
  Errors errors_{};
963
  String document_{};
964
  Location begin_ = nullptr;
965
  Location end_ = nullptr;
966
  Location current_ = nullptr;
967
  Location lastValueEnd_ = nullptr;
968
  Value* lastValue_ = nullptr;
969
  bool lastValueHasAComment_ = false;
970
  String commentsBefore_{};
971

972
  OurFeatures const features_;
973
  bool collectComments_ = false;
974
}; // OurReader
975

976
// complete copy of Read impl, for OurReader
977

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

989
bool OurReader::containsNewLine(OurReader::Location begin,
162✔
990
                                OurReader::Location end) {
991
  newlineScanByteCountForTesting() += static_cast<size_t>(end - begin);
162✔
992
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
202!
993
}
994

995
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,804✔
996

997
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
934✔
998
                      bool collectComments) {
999
  if (!features_.allowComments_) {
934✔
1000
    collectComments = false;
1001
  }
1002

1003
  begin_ = beginDoc;
934✔
1004
  end_ = endDoc;
934✔
1005
  collectComments_ = collectComments;
934✔
1006
  current_ = begin_;
934✔
1007
  lastValueEnd_ = nullptr;
934✔
1008
  lastValue_ = nullptr;
934✔
1009
  commentsBefore_.clear();
1010
  errors_.clear();
934✔
1011
  while (!nodes_.empty())
934!
1012
    nodes_.pop();
×
1013
  nodes_.push(&root);
934!
1014

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

1043
bool OurReader::readValue() {
56,096✔
1044
  //  To preserve the old behaviour we cast size_t to int.
1045
  if (nodes_.size() > features_.stackLimit_)
56,096✔
1046
    throwRuntimeError("Exceeded stackLimit in readValue().");
8✔
1047
  Token token;
1048
  readTokenSkippingComments(token);
56,088✔
1049
  bool successful = true;
1050

1051
  if (collectComments_ && !commentsBefore_.empty()) {
56,088✔
1052
    currentValue().setComment(commentsBefore_, commentBefore);
1,203✔
1053
    commentsBefore_.clear();
1054
  }
1055

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

1126
  if (collectComments_) {
54,258✔
1127
    lastValueEnd_ = current_;
53,797✔
1128
    lastValueHasAComment_ = false;
53,797✔
1129
    lastValue_ = &currentValue();
53,797✔
1130
  }
1131

1132
  return successful;
1133
}
1134

1135
bool OurReader::readTokenSkippingComments(Token& token) {
111,006✔
1136
  bool success = readToken(token);
111,006✔
1137
  if (features_.allowComments_) {
111,006✔
1138
    while (success && token.type_ == tokenComment) {
111,507✔
1139
      success = readToken(token);
1,591✔
1140
    }
1141
  }
1142
  return success;
111,006✔
1143
}
1144

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

1256
void OurReader::skipSpaces() {
168,232✔
1257
  while (current_ != end_) {
287,959✔
1258
    Char c = *current_;
287,014✔
1259
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
287,014✔
1260
      ++current_;
119,727✔
1261
    else
1262
      break;
1263
  }
1264
}
168,232✔
1265

1266
void OurReader::skipBom(bool skipBom) {
934✔
1267
  // The default behavior is to skip BOM.
1268
  if (skipBom) {
934✔
1269
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
933✔
1270
      begin_ += 3;
1✔
1271
      current_ = begin_;
1✔
1272
    }
1273
  }
1274
}
934✔
1275

1276
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1277
  if (end_ - current_ < patternLength)
226✔
1278
    return false;
1279
  int index = patternLength;
1280
  while (index--)
797✔
1281
    if (current_[index] != pattern[index])
631✔
1282
      return false;
1283
  current_ += patternLength;
166✔
1284
  return true;
166✔
1285
}
1286

1287
bool OurReader::readComment() {
1,616✔
1288
  const Location commentBegin = current_ - 1;
1,616✔
1289
  const Char c = getNextChar();
1,616✔
1290
  bool successful = false;
1291
  bool cStyleWithEmbeddedNewline = false;
1,616✔
1292

1293
  const bool isCStyleComment = (c == '*');
1294
  const bool isCppStyleComment = (c == '/');
1295
  if (isCStyleComment) {
1,616✔
1296
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1,086✔
1297
  } else if (isCppStyleComment) {
530!
1298
    successful = readCppStyleComment();
530✔
1299
  }
1300

1301
  if (!successful)
1,616!
1302
    return false;
×
1303

1304
  if (collectComments_) {
1,616✔
1305
    CommentPlacement placement = commentBefore;
1306

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

1320
    addComment(commentBegin, current_, placement);
1,598✔
1321
  }
1322
  return true;
1323
}
1324

1325
String OurReader::normalizeEOL(OurReader::Location begin,
1,598✔
1326
                               OurReader::Location end) {
1327
  String normalized;
1328
  normalized.reserve(static_cast<size_t>(end - begin));
1,598✔
1329
  OurReader::Location current = begin;
1330
  while (current != end) {
28,206✔
1331
    char c = *current++;
26,608✔
1332
    if (c == '\r') {
26,608✔
1333
      if (current != end && *current == '\n')
3!
1334
        // convert dos EOL
1335
        ++current;
1✔
1336
      // convert Mac EOL
1337
      normalized += '\n';
1338
    } else {
1339
      normalized += c;
1340
    }
1341
  }
1342
  return normalized;
1,598✔
1343
}
1344

1345
void OurReader::addComment(Location begin, Location end,
1,598✔
1346
                           CommentPlacement placement) {
1347
  assert(collectComments_);
1,598!
1348
  const String& normalized = normalizeEOL(begin, end);
1,598✔
1349
  if (placement == commentAfterOnSameLine) {
1,598✔
1350
    assert(lastValue_ != nullptr);
64!
1351
    lastValue_->setComment(normalized, placement);
128✔
1352
  } else {
1353
    commentsBefore_ += normalized;
1,534✔
1354
  }
1355
}
1,598✔
1356

1357
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,086✔
1358
  *containsNewLineResult = false;
1,086✔
1359

1360
  while ((current_ + 1) < end_) {
6,013!
1361
    Char c = getNextChar();
4,927✔
1362
    if (c == '*' && *current_ == '/')
4,927!
1363
      break;
1364
    if (c == '\n')
3,841✔
1365
      *containsNewLineResult = true;
98✔
1366
  }
1367

1368
  return getNextChar() == '/';
1,086✔
1369
}
1370

1371
bool OurReader::readCppStyleComment() {
530✔
1372
  while (current_ != end_) {
18,083✔
1373
    Char c = getNextChar();
18,077✔
1374
    if (c == '\n')
18,077✔
1375
      break;
1376
    if (c == '\r') {
17,556✔
1377
      // Consume DOS EOL. It will be normalized in addComment.
1378
      if (current_ != end_ && *current_ == '\n')
3!
1379
        getNextChar();
1✔
1380
      // Break on Moc OS 9 EOL.
1381
      break;
1382
    }
1383
  }
1384
  return true;
530✔
1385
}
1386

1387
bool OurReader::readNumber(bool checkInf) {
52,876✔
1388
  Location p = current_;
52,876✔
1389
  if (checkInf && p != end_ && *p == 'I') {
52,876!
1390
    current_ = ++p;
6✔
1391
    return false;
6✔
1392
  }
1393
  char c = '0'; // stopgap for already consumed character
1394
  // integral part
1395
  while (c >= '0' && c <= '9')
235,518✔
1396
    c = (current_ = p) < end_ ? *p++ : '\0';
182,648✔
1397
  // fractional part
1398
  if (c == '.') {
52,870✔
1399
    c = (current_ = p) < end_ ? *p++ : '\0';
113!
1400
    while (c >= '0' && c <= '9')
785✔
1401
      c = (current_ = p) < end_ ? *p++ : '\0';
672✔
1402
  }
1403
  // exponential part
1404
  if (c == 'e' || c == 'E') {
52,870✔
1405
    c = (current_ = p) < end_ ? *p++ : '\0';
103!
1406
    if (c == '+' || c == '-')
103✔
1407
      c = (current_ = p) < end_ ? *p++ : '\0';
76!
1408
    while (c >= '0' && c <= '9')
306✔
1409
      c = (current_ = p) < end_ ? *p++ : '\0';
203✔
1410
  }
1411
  return true;
1412
}
1413
bool OurReader::readString() {
1,174✔
1414
  Char c = 0;
1415
  while (current_ != end_) {
34,521!
1416
    c = getNextChar();
34,521✔
1417
    if (c == '\\')
34,521✔
1418
      getNextChar();
1,275✔
1419
    else if (c == '"')
33,246✔
1420
      break;
1421
  }
1422
  return c == '"';
1,174✔
1423
}
1424

1425
bool OurReader::readStringSingleQuote() {
11✔
1426
  Char c = 0;
1427
  while (current_ != end_) {
26!
1428
    c = getNextChar();
26✔
1429
    if (c == '\\')
26✔
1430
      getNextChar();
2✔
1431
    else if (c == '\'')
24✔
1432
      break;
1433
  }
1434
  return c == '\'';
11✔
1435
}
1436

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

1467
    Token colon;
1468
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
639!
1469
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1470
                                tokenObjectEnd);
1471
    }
1472
    Value& value = currentValue()[name];
632✔
1473
    nodes_.push(&value);
632!
1474
    bool ok = readValue();
632✔
1475
    nodes_.pop();
631✔
1476
    if (!ok) // error already set
631✔
1477
      return recoverFromError(tokenObjectEnd);
22✔
1478

1479
    Token comma;
1480
    if (!readTokenSkippingComments(comma) ||
609✔
1481
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
601✔
1482
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1483
                                comma, tokenObjectEnd);
1484
    }
1485
    if (comma.type_ == tokenObjectEnd)
582✔
1486
      return true;
1487
  }
1488
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1489
                            tokenObjectEnd);
1490
}
367✔
1491

1492
bool OurReader::readArray(Token& token) {
2,152✔
1493
  Value init(arrayValue);
2,152✔
1494
  currentValue().swapPayload(init);
2,152✔
1495
  currentValue().setOffsetStart(token.start_ - begin_);
2,152✔
1496
  int index = 0;
1497
  for (;;) {
1498
    skipSpaces();
54,558✔
1499
    if (current_ != end_ && *current_ == ']' &&
54,558!
1500
        (index == 0 ||
14✔
1501
         (features_.allowTrailingCommas_ &&
14!
1502
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
14✔
1503
                                                      // comma
1504
    {
1505
      Token endArray;
1506
      readToken(endArray);
28✔
1507
      return true;
1508
    }
1509
    Value& value = currentValue()[index++];
54,530✔
1510
    nodes_.push(&value);
54,530✔
1511
    bool ok = readValue();
54,530✔
1512
    nodes_.pop();
52,738✔
1513
    if (!ok) // error already set
52,738✔
1514
      return recoverFromError(tokenArrayEnd);
46✔
1515

1516
    Token currentToken;
1517
    // Accept Comment after last item in the array.
1518
    ok = readTokenSkippingComments(currentToken);
52,692✔
1519
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,692✔
1520
                         currentToken.type_ != tokenArrayEnd);
52,692✔
1521
    if (!ok || badTokenType) {
52,692✔
1522
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
46✔
1523
                                currentToken, tokenArrayEnd);
1524
    }
1525
    if (currentToken.type_ == tokenArrayEnd)
52,669✔
1526
      break;
1527
  }
52,406✔
1528
  return true;
263✔
1529
}
2,152✔
1530

1531
bool OurReader::decodeNumber(Token& token) {
52,835✔
1532
  Value decoded;
52,835✔
1533
  if (!decodeNumber(token, decoded))
52,835✔
1534
    return false;
1535
  currentValue().swapPayload(decoded);
52,826✔
1536
  currentValue().setOffsetStart(token.start_ - begin_);
52,826✔
1537
  currentValue().setOffsetLimit(token.end_ - begin_);
52,826✔
1538
  return true;
1539
}
52,835✔
1540

1541
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,838✔
1542
  // Attempts to parse the number as an integer. If the number is
1543
  // larger than the maximum supported value of an integer then
1544
  // we decode the number as a double.
1545
  Location current = token.start_;
52,838✔
1546
  const bool isNegative = *current == '-';
52,838✔
1547
  if (isNegative) {
52,838✔
1548
    ++current;
141✔
1549
  }
1550

1551
  // We assume we can represent the largest and smallest integer types as
1552
  // unsigned integers with separate sign. This is only true if they can fit
1553
  // into an unsigned integer.
1554
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1555
                "Int must be smaller than UInt");
1556

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

1568
  static constexpr Value::LargestUInt positive_threshold =
1569
      Value::maxLargestUInt / 10;
1570
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1571

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

1582
  const Value::LargestUInt threshold =
1583
      isNegative ? negative_threshold : positive_threshold;
1584
  const Value::UInt max_last_digit =
1585
      isNegative ? negative_last_digit : positive_last_digit;
1586

1587
  Value::LargestUInt value = 0;
1588
  while (current < token.end_) {
235,284✔
1589
    Char c = *current++;
182,642✔
1590
    if (c < '0' || c > '9')
182,642✔
1591
      return decodeDouble(token, decoded);
177✔
1592

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

1608
  if (isNegative) {
52,642✔
1609
    // We use the same magnitude assumption here, just in case.
1610
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1611
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1612
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,553✔
1613
    decoded = Value::LargestInt(value);
52,529✔
1614
  } else {
1615
    decoded = value;
24✔
1616
  }
1617

1618
  return true;
1619
}
1620

1621
bool OurReader::decodeDouble(Token& token) {
×
1622
  Value decoded;
×
1623
  if (!decodeDouble(token, decoded))
×
1624
    return false;
1625
  currentValue().swapPayload(decoded);
×
1626
  currentValue().setOffsetStart(token.start_ - begin_);
×
1627
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1628
  return true;
1629
}
×
1630

1631
bool OurReader::decodeDouble(Token& token, Value& decoded) {
196✔
1632
  double value = 0;
196✔
1633
  IStringStream is(String(token.start_, token.end_));
196✔
1634
  is.imbue(std::locale::classic());
196✔
1635
  if (!(is >> value)) {
196✔
1636
    if (value == std::numeric_limits<double>::max())
33✔
1637
      value = std::numeric_limits<double>::infinity();
12✔
1638
    else if (value == std::numeric_limits<double>::lowest())
21✔
1639
      value = -std::numeric_limits<double>::infinity();
12✔
1640
    else if (!std::isinf(value))
9!
1641
      return addError(
9✔
1642
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
18✔
1643
  }
1644
  decoded = value;
187✔
1645
  return true;
187✔
1646
}
196✔
1647

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

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

1714
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1715
                                       Location end, unsigned int& unicode) {
1716

1717
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1718
    return false;
1719
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1720
    // surrogate pairs
1721
    if (end - current < 6)
15✔
1722
      return addError(
2✔
1723
          "additional six characters expected to parse unicode surrogate pair.",
1724
          token, current);
1725
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1726
      unsigned int surrogatePair;
1727
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1728
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1729
      } else
1730
        return false;
×
1731
    } else
1732
      return addError("expecting another \\u token to begin the second half of "
2✔
1733
                      "a unicode surrogate pair",
1734
                      token, current);
1735
  }
1736
  return true;
1737
}
1738

1739
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
158✔
1740
                                            Location end,
1741
                                            unsigned int& ret_unicode) {
1742
  if (end - current < 4)
158✔
1743
    return addError(
2✔
1744
        "Bad unicode escape sequence in string: four digits expected.", token,
1745
        current);
1746
  int unicode = 0;
1747
  for (int index = 0; index < 4; ++index) {
783✔
1748
    Char c = *current++;
627✔
1749
    unicode *= 16;
627✔
1750
    if (c >= '0' && c <= '9')
627✔
1751
      unicode += c - '0';
400✔
1752
    else if (c >= 'a' && c <= 'f')
227✔
1753
      unicode += c - 'a' + 10;
115✔
1754
    else if (c >= 'A' && c <= 'F')
112✔
1755
      unicode += c - 'A' + 10;
111✔
1756
    else
1757
      return addError(
2✔
1758
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1759
          token, current);
1760
  }
1761
  ret_unicode = static_cast<unsigned int>(unicode);
156✔
1762
  return true;
156✔
1763
}
1764

1765
bool OurReader::addError(const String& message, Token& token, Location extra) {
162✔
1766
  ErrorInfo info;
1767
  info.token_ = token;
162✔
1768
  info.message_ = message;
1769
  info.extra_ = extra;
162✔
1770
  errors_.push_back(info);
162✔
1771
  return false;
162✔
1772
}
1773

1774
bool OurReader::recoverFromError(TokenType skipUntilToken) {
152✔
1775
  size_t errorCount = errors_.size();
1776
  Token skip;
1777
  for (;;) {
1778
    if (!readToken(skip))
410✔
1779
      errors_.resize(errorCount); // discard errors caused by recovery
173✔
1780
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
410✔
1781
      break;
1782
  }
1783
  errors_.resize(errorCount);
152✔
1784
  return false;
152✔
1785
}
1786

1787
bool OurReader::addErrorAndRecover(const String& message, Token& token,
84✔
1788
                                   TokenType skipUntilToken) {
1789
  addError(message, token);
84✔
1790
  return recoverFromError(skipUntilToken);
84✔
1791
}
1792

1793
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,701✔
1794

1795
OurReader::Char OurReader::getNextChar() {
175,205✔
1796
  if (current_ == end_)
175,205✔
1797
    return 0;
1798
  return *current_++;
174,260✔
1799
}
1800

1801
void OurReader::getLocationLineAndColumn(Location location, int& line,
184✔
1802
                                         int& column) const {
1803
  Location current = begin_;
184✔
1804
  Location lastLineStart = current;
1805
  line = 0;
184✔
1806
  while (current < location && current != end_) {
2,271!
1807
    Char c = *current++;
2,087✔
1808
    if (c == '\r') {
2,087✔
1809
      if (current != end_ && *current == '\n')
1!
1810
        ++current;
×
1811
      lastLineStart = current;
1812
      ++line;
1✔
1813
    } else if (c == '\n') {
2,086✔
1814
      lastLineStart = current;
1815
      ++line;
28✔
1816
    }
1817
  }
1818
  // column & line start at 1
1819
  column = int(location - lastLineStart) + 1;
184✔
1820
  ++line;
184✔
1821
}
184✔
1822

1823
String OurReader::getLocationLineAndColumn(Location location) const {
184✔
1824
  int line, column;
1825
  getLocationLineAndColumn(location, line, column);
184✔
1826
  char buffer[18 + 16 + 16 + 1];
1827
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
184✔
1828
  return buffer;
184✔
1829
}
1830

1831
String OurReader::getFormattedErrorMessages() const {
924✔
1832
  String formattedMessage;
1833
  for (const auto& error : errors_) {
1,085✔
1834
    formattedMessage +=
1835
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
161✔
1836
    formattedMessage += "  " + error.message_ + "\n";
161✔
1837
    if (error.extra_)
161✔
1838
      formattedMessage +=
1839
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
46✔
1840
  }
1841
  return formattedMessage;
924✔
1842
}
1843

1844
std::vector<CharReader::StructuredError>
1845
OurReader::getStructuredErrors() const {
2✔
1846
  std::vector<CharReader::StructuredError> allErrors;
1847
  for (const auto& error : errors_) {
3✔
1848
    CharReader::StructuredError structured;
1849
    structured.offset_start = error.token_.start_ - begin_;
1✔
1850
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1851
    structured.message = error.message_;
1✔
1852
    allErrors.push_back(structured);
1✔
1853
  }
1854
  return allErrors;
2✔
1855
}
×
1856

1857
class OurCharReader : public CharReader {
1858

1859
public:
1860
  OurCharReader(bool collectComments, OurFeatures const& features)
902✔
1861
      : CharReader(
902✔
1862
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,804✔
1863

1864
protected:
1865
  class OurImpl : public Impl {
1866
  public:
1867
    OurImpl(bool collectComments, OurFeatures const& features)
1868
        : collectComments_(collectComments), reader_(features) {}
902✔
1869

1870
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
934✔
1871
               String* errs) override {
1872
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
934✔
1873
      if (errs) {
926✔
1874
        *errs = reader_.getFormattedErrorMessages();
1,848✔
1875
      }
1876
      return ok;
926✔
1877
    }
1878

1879
    std::vector<CharReader::StructuredError>
1880
    getStructuredErrors() const override {
2✔
1881
      return reader_.getStructuredErrors();
2✔
1882
    }
1883

1884
  private:
1885
    bool const collectComments_;
1886
    OurReader reader_;
1887
  };
1888
};
1889

1890
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
899✔
1891
CharReaderBuilder::~CharReaderBuilder() = default;
899✔
1892
CharReader* CharReaderBuilder::newCharReader() const {
902✔
1893
  bool collectComments = settings_["collectComments"].asBool();
902✔
1894
  OurFeatures features = OurFeatures::all();
902✔
1895
  features.allowComments_ = settings_["allowComments"].asBool();
902✔
1896
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
902✔
1897
  features.strictRoot_ = settings_["strictRoot"].asBool();
902✔
1898
  features.allowDroppedNullPlaceholders_ =
902✔
1899
      settings_["allowDroppedNullPlaceholders"].asBool();
902✔
1900
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
902✔
1901
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
902✔
1902

1903
  // Stack limit is always a size_t, so we get this as an unsigned int
1904
  // regardless of it we have 64-bit integer support enabled.
1905
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
902✔
1906
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
902✔
1907
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
902✔
1908
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
902✔
1909
  features.skipBom_ = settings_["skipBom"].asBool();
902✔
1910
  return new OurCharReader(collectComments, features);
902✔
1911
}
1912

1913
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1914
  static const auto& valid_keys = *new std::set<String>{
1915
      "collectComments",
1916
      "allowComments",
1917
      "allowTrailingCommas",
1918
      "strictRoot",
1919
      "allowDroppedNullPlaceholders",
1920
      "allowNumericKeys",
1921
      "allowSingleQuotes",
1922
      "stackLimit",
1923
      "failIfExtra",
1924
      "rejectDupKeys",
1925
      "allowSpecialFloats",
1926
      "skipBom",
1927
  };
15!
1928
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
54✔
1929
    auto key = si.name();
25✔
1930
    if (valid_keys.count(key))
25✔
1931
      continue;
1932
    if (invalid)
1!
1933
      (*invalid)[key] = *si;
1✔
1934
    else
1935
      return false;
1936
  }
1937
  return invalid ? invalid->empty() : true;
2!
1938
}
2!
1939

1940
Value& CharReaderBuilder::operator[](const String& key) {
1✔
1941
  return settings_[key];
1✔
1942
}
1943
// static
1944
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1945
  //! [CharReaderBuilderStrictMode]
1946
  (*settings)["allowComments"] = false;
3✔
1947
  (*settings)["allowTrailingCommas"] = false;
3✔
1948
  (*settings)["strictRoot"] = true;
3✔
1949
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1950
  (*settings)["allowNumericKeys"] = false;
3✔
1951
  (*settings)["allowSingleQuotes"] = false;
3✔
1952
  (*settings)["stackLimit"] = 256;
3✔
1953
  (*settings)["failIfExtra"] = true;
3✔
1954
  (*settings)["rejectDupKeys"] = true;
3✔
1955
  (*settings)["allowSpecialFloats"] = false;
3✔
1956
  (*settings)["skipBom"] = true;
3✔
1957
  //! [CharReaderBuilderStrictMode]
1958
}
3✔
1959
// static
1960
void CharReaderBuilder::setDefaults(Json::Value* settings) {
899✔
1961
  //! [CharReaderBuilderDefaults]
1962
  (*settings)["collectComments"] = true;
899✔
1963
  (*settings)["allowComments"] = true;
899✔
1964
  (*settings)["allowTrailingCommas"] = true;
899✔
1965
  (*settings)["strictRoot"] = false;
899✔
1966
  (*settings)["allowDroppedNullPlaceholders"] = false;
899✔
1967
  (*settings)["allowNumericKeys"] = false;
899✔
1968
  (*settings)["allowSingleQuotes"] = false;
899✔
1969
  (*settings)["stackLimit"] = 256;
899✔
1970
  (*settings)["failIfExtra"] = false;
899✔
1971
  (*settings)["rejectDupKeys"] = false;
899✔
1972
  (*settings)["allowSpecialFloats"] = false;
899✔
1973
  (*settings)["skipBom"] = true;
899✔
1974
  //! [CharReaderBuilderDefaults]
1975
}
899✔
1976
// static
1977
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
1978
  //! [CharReaderBuilderECMA404Mode]
1979
  (*settings)["allowComments"] = false;
×
1980
  (*settings)["allowTrailingCommas"] = false;
×
1981
  (*settings)["strictRoot"] = false;
×
1982
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
1983
  (*settings)["allowNumericKeys"] = false;
×
1984
  (*settings)["allowSingleQuotes"] = false;
×
1985
  (*settings)["stackLimit"] = 256;
×
1986
  (*settings)["failIfExtra"] = true;
×
1987
  (*settings)["rejectDupKeys"] = false;
×
1988
  (*settings)["allowSpecialFloats"] = false;
×
1989
  (*settings)["skipBom"] = false;
×
1990
  //! [CharReaderBuilderECMA404Mode]
1991
}
×
1992

1993
std::vector<CharReader::StructuredError>
1994
CharReader::getStructuredErrors() const {
2✔
1995
  return _impl->getStructuredErrors();
2✔
1996
}
1997

1998
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
934✔
1999
                       String* errs) {
2000
  return _impl->parse(beginDoc, endDoc, root, errs);
934✔
2001
}
2002

2003
//////////////////////////////////
2004
// global functions
2005

2006
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
2007
                     String* errs) {
2008
  OStringStream ssin;
4✔
2009
  ssin << sin.rdbuf();
4✔
2010
  String doc = std::move(ssin).str();
2011
  char const* begin = doc.data();
2012
  char const* end = begin + doc.size();
4✔
2013
  // Note that we do not actually need a null-terminator.
2014
  CharReaderPtr const reader(fact.newCharReader());
4✔
2015
  return reader->parse(begin, end, root, errs);
8✔
2016
}
4✔
2017

2018
IStream& operator>>(IStream& sin, Value& root) {
1✔
2019
  CharReaderBuilder b;
1✔
2020
  String errs;
2021
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2022
  if (!ok) {
1!
2023
    throwRuntimeError(errs);
×
2024
  }
2025
  return sin;
1✔
2026
}
1✔
2027

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