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

open-source-parsers / jsoncpp / 27701753953

17 Jun 2026 03:50PM UTC coverage: 89.863% (-0.04%) from 89.903%
27701753953

push

github

web-flow
Merge 5e5978589 into 800aa28c4

2228 of 2650 branches covered (84.08%)

Branch coverage included in aggregate %.

6 of 8 new or added lines in 1 file covered. (75.0%)

2621 of 2746 relevant lines covered (95.45%)

23775.49 hits per line

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

89.78
/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
        if (surrogatePair < 0xDC00 || surrogatePair > 0xDFFF)
13!
NEW
682
          return addError("expecting a low surrogate (DC00-DFFF) to complete "
×
683
                          "the unicode surrogate pair",
684
                          token, current);
685
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
686
      } else
687
        return false;
688
    } else
689
      return addError("expecting another \\u token to begin the second half of "
2✔
690
                      "a unicode surrogate pair",
691
                      token, current);
692
  } else if (unicode >= 0xDC00 && unicode <= 0xDFFF) {
86!
NEW
693
    return addError("unexpected low surrogate (DC00-DFFF); a high surrogate "
×
694
                    "(D800-DBFF) must come first",
695
                    token, current);
696
  }
697
  return true;
698
}
699

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

726
bool Reader::addError(const String& message, Token& token, Location extra) {
16✔
727
  ErrorInfo info;
728
  info.token_ = token;
16✔
729
  info.message_ = message;
730
  info.extra_ = extra;
16✔
731
  errors_.push_back(info);
16✔
732
  return false;
16✔
733
}
734

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

748
bool Reader::addErrorAndRecover(const String& message, Token& token,
5✔
749
                                TokenType skipUntilToken) {
750
  addError(message, token);
5✔
751
  return recoverFromError(skipUntilToken);
5✔
752
}
753

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

756
Reader::Char Reader::getNextChar() {
160,551✔
757
  if (current_ == end_)
160,551✔
758
    return 0;
759
  return *current_++;
159,833✔
760
}
761

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

784
String Reader::getLocationLineAndColumn(Location location) const {
24✔
785
  int line, column;
786
  getLocationLineAndColumn(location, line, column);
24✔
787
  char buffer[18 + 16 + 16 + 1];
788
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
24✔
789
  return buffer;
24✔
790
}
791

792
// Deprecated. Preserved for backward compatibility
793
String Reader::getFormatedErrorMessages() const {
×
794
  return getFormattedErrorMessages();
×
795
}
796

797
String Reader::getFormattedErrorMessages() const {
19✔
798
  String formattedMessage;
799
  for (const auto& error : errors_) {
37✔
800
    formattedMessage +=
801
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
18✔
802
    formattedMessage += "  " + error.message_ + "\n";
18✔
803
    if (error.extra_)
18✔
804
      formattedMessage +=
805
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
12✔
806
  }
807
  return formattedMessage;
19✔
808
}
809

810
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
17✔
811
  std::vector<Reader::StructuredError> allErrors;
812
  for (const auto& error : errors_) {
33✔
813
    Reader::StructuredError structured;
814
    structured.offset_start = error.token_.start_ - begin_;
16✔
815
    structured.offset_limit = error.token_.end_ - begin_;
16✔
816
    structured.message = error.message_;
16✔
817
    allErrors.push_back(structured);
16✔
818
  }
819
  return allErrors;
17✔
820
}
×
821

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

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

856
bool Reader::good() const { return errors_.empty(); }
×
857

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

876
OurFeatures OurFeatures::all() { return {}; }
904✔
877

878
// Implementation of class Reader
879
// ////////////////////////////////
880

881
// Originally copied from the Reader class (now deprecated), used internally
882
// for implementing JSON reading.
883
class OurReader {
884
public:
885
  using Char = char;
886
  using Location = const Char*;
887

888
  explicit OurReader(OurFeatures const& features);
889
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
890
             bool collectComments = true);
891
  String getFormattedErrorMessages() const;
892
  std::vector<CharReader::StructuredError> getStructuredErrors() const;
893

894
private:
895
  OurReader(OurReader const&);      // no impl
896
  void operator=(OurReader const&); // no impl
897

898
  enum TokenType {
899
    tokenEndOfStream = 0,
900
    tokenObjectBegin,
901
    tokenObjectEnd,
902
    tokenArrayBegin,
903
    tokenArrayEnd,
904
    tokenString,
905
    tokenNumber,
906
    tokenTrue,
907
    tokenFalse,
908
    tokenNull,
909
    tokenNaN,
910
    tokenPosInf,
911
    tokenNegInf,
912
    tokenArraySeparator,
913
    tokenMemberSeparator,
914
    tokenComment,
915
    tokenError
916
  };
917

918
  class Token {
919
  public:
920
    TokenType type_;
921
    Location start_;
922
    Location end_;
923
  };
924

925
  class ErrorInfo {
167✔
926
  public:
927
    Token token_;
928
    String message_;
929
    Location extra_;
930
  };
931

932
  using Errors = std::deque<ErrorInfo>;
933

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

971
  static String normalizeEOL(Location begin, Location end);
972
  static bool containsNewLine(Location begin, Location end);
973

974
  using Nodes = std::stack<Value*>;
975

976
  Nodes nodes_{};
977
  Errors errors_{};
978
  String document_{};
979
  Location begin_ = nullptr;
980
  Location end_ = nullptr;
981
  Location current_ = nullptr;
982
  Location lastValueEnd_ = nullptr;
983
  Value* lastValue_ = nullptr;
984
  bool lastValueHasAComment_ = false;
985
  String commentsBefore_{};
986

987
  OurFeatures const features_;
988
  bool collectComments_ = false;
989
}; // OurReader
990

991
// complete copy of Read impl, for OurReader
992

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

1004
bool OurReader::containsNewLine(OurReader::Location begin,
167✔
1005
                                OurReader::Location end) {
1006
  newlineScanByteCountForTesting() += static_cast<size_t>(end - begin);
167✔
1007
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
211!
1008
}
1009

1010
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,808✔
1011

1012
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
949✔
1013
                      bool collectComments) {
1014
  if (!features_.allowComments_) {
949✔
1015
    collectComments = false;
1016
  }
1017

1018
  begin_ = beginDoc;
949✔
1019
  end_ = endDoc;
949✔
1020
  collectComments_ = collectComments;
949✔
1021
  current_ = begin_;
949✔
1022
  lastValueEnd_ = nullptr;
949✔
1023
  lastValue_ = nullptr;
949✔
1024
  commentsBefore_.clear();
1025
  errors_.clear();
949✔
1026
  while (!nodes_.empty())
949!
1027
    nodes_.pop();
×
1028
  nodes_.push(&root);
949!
1029

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

1058
bool OurReader::readValue() {
56,127✔
1059
  //  To preserve the old behaviour we cast size_t to int.
1060
  if (nodes_.size() > features_.stackLimit_)
56,127✔
1061
    throwRuntimeError("Exceeded stackLimit in readValue().");
8✔
1062
  Token token;
1063
  readTokenSkippingComments(token);
56,119✔
1064
  bool successful = true;
1065

1066
  if (collectComments_ && !commentsBefore_.empty()) {
56,119✔
1067
    currentValue().setComment(commentsBefore_, commentBefore);
1,206✔
1068
    commentsBefore_.clear();
1069
  }
1070

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

1141
  if (collectComments_) {
54,289✔
1142
    lastValueEnd_ = current_;
53,828✔
1143
    lastValueHasAComment_ = false;
53,828✔
1144
    lastValue_ = &currentValue();
53,828✔
1145
  }
1146

1147
  return successful;
1148
}
1149

1150
bool OurReader::readTokenSkippingComments(Token& token) {
111,067✔
1151
  bool success = readToken(token);
111,067✔
1152
  if (features_.allowComments_) {
111,067✔
1153
    while (success && token.type_ == tokenComment) {
111,519✔
1154
      success = readToken(token);
1,542✔
1155
    }
1156
  }
1157
  return success;
111,067✔
1158
}
1159

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

1271
void OurReader::skipSpaces() {
168,385✔
1272
  while (current_ != end_) {
288,121✔
1273
    Char c = *current_;
287,161✔
1274
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
287,161✔
1275
      ++current_;
119,736✔
1276
    else
1277
      break;
1278
  }
1279
}
168,385✔
1280

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

1299
void OurReader::skipBom(bool skipBom) {
949✔
1300
  // The default behavior is to skip BOM.
1301
  if (skipBom) {
949✔
1302
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
948✔
1303
      begin_ += 3;
1✔
1304
      current_ = begin_;
1✔
1305
    }
1306
  }
1307
}
949✔
1308

1309
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1310
  if (end_ - current_ < patternLength)
226✔
1311
    return false;
1312
  int index = patternLength;
1313
  while (index--)
797✔
1314
    if (current_[index] != pattern[index])
631✔
1315
      return false;
1316
  current_ += patternLength;
166✔
1317
  return true;
166✔
1318
}
1319

1320
bool OurReader::readComment() {
1,622✔
1321
  const Location commentBegin = current_ - 1;
1,622✔
1322
  const Char c = getNextChar();
1,622✔
1323
  bool successful = false;
1324
  bool cStyleWithEmbeddedNewline = false;
1,622✔
1325

1326
  const bool isCStyleComment = (c == '*');
1327
  const bool isCppStyleComment = (c == '/');
1328
  if (isCStyleComment) {
1,622✔
1329
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1,087✔
1330
  } else if (isCppStyleComment) {
535!
1331
    successful = readCppStyleComment();
535✔
1332
  }
1333

1334
  if (!successful)
1,622!
1335
    return false;
×
1336

1337
  if (collectComments_) {
1,622✔
1338
    CommentPlacement placement = commentBefore;
1339

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

1353
    addComment(commentBegin, current_, placement);
1,604✔
1354
  }
1355
  return true;
1356
}
1357

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

1378
void OurReader::addComment(Location begin, Location end,
1,604✔
1379
                           CommentPlacement placement) {
1380
  assert(collectComments_);
1,604!
1381
  const String& normalized = normalizeEOL(begin, end);
1,604✔
1382
  if (placement == commentAfterOnSameLine) {
1,604✔
1383
    assert(lastValue_ != nullptr);
65!
1384
    lastValue_->setComment(normalized, placement);
130✔
1385
  } else {
1386
    commentsBefore_ += normalized;
1,539✔
1387
  }
1388
}
1,604✔
1389

1390
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,087✔
1391
  *containsNewLineResult = false;
1,087✔
1392

1393
  while ((current_ + 1) < end_) {
6,025!
1394
    Char c = getNextChar();
4,938✔
1395
    if (c == '*' && *current_ == '/')
4,938!
1396
      break;
1397
    if (c == '\n')
3,851✔
1398
      *containsNewLineResult = true;
98✔
1399
  }
1400

1401
  return getNextChar() == '/';
1,087✔
1402
}
1403

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

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

1458
bool OurReader::readStringSingleQuote() {
11✔
1459
  Char c = 0;
1460
  while (current_ != end_) {
26!
1461
    c = getNextChar();
26✔
1462
    if (c == '\\')
26✔
1463
      getNextChar();
2✔
1464
    else if (c == '\'')
24✔
1465
      break;
1466
  }
1467
  return c == '\'';
11✔
1468
}
1469

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

1500
    Token colon;
1501
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
640!
1502
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1503
                                tokenObjectEnd);
1504
    }
1505
    Value& value = currentValue()[name];
633✔
1506
    nodes_.push(&value);
633!
1507
    bool ok = readValue();
633✔
1508
    nodes_.pop();
632✔
1509
    if (!ok) // error already set
632✔
1510
      return recoverFromError(tokenObjectEnd);
22✔
1511

1512
    Token comma;
1513
    if (!readTokenSkippingComments(comma) ||
610✔
1514
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
602✔
1515
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1516
                                comma, tokenObjectEnd);
1517
    }
1518
    if (comma.type_ == tokenObjectEnd)
583✔
1519
      return true;
1520
  }
1521
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1522
                            tokenObjectEnd);
1523
}
369✔
1524

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

1552
    Token currentToken;
1553
    // Accept Comment after last item in the array.
1554
    ok = readTokenSkippingComments(currentToken);
52,703✔
1555
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,703✔
1556
                         currentToken.type_ != tokenArrayEnd);
52,703✔
1557
    if (!ok || badTokenType) {
52,703✔
1558
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
48✔
1559
                                currentToken, tokenArrayEnd);
1560
    }
1561
    if (currentToken.type_ == tokenArrayEnd)
52,679✔
1562
      break;
1563
  }
52,412✔
1564
  return true;
267✔
1565
}
2,165✔
1566

1567
bool OurReader::decodeNumber(Token& token) {
52,849✔
1568
  Value decoded;
52,849✔
1569
  if (!decodeNumber(token, decoded))
52,849✔
1570
    return false;
1571
  currentValue().swapPayload(decoded);
52,838✔
1572
  currentValue().setOffsetStart(token.start_ - begin_);
52,838✔
1573
  currentValue().setOffsetLimit(token.end_ - begin_);
52,838✔
1574
  return true;
1575
}
52,849✔
1576

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

1587
  // We assume we can represent the largest and smallest integer types as
1588
  // unsigned integers with separate sign. This is only true if they can fit
1589
  // into an unsigned integer.
1590
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1591
                "Int must be smaller than UInt");
1592

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

1604
  static constexpr Value::LargestUInt positive_threshold =
1605
      Value::maxLargestUInt / 10;
1606
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1607

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

1618
  const Value::LargestUInt threshold =
1619
      isNegative ? negative_threshold : positive_threshold;
1620
  const Value::UInt max_last_digit =
1621
      isNegative ? negative_last_digit : positive_last_digit;
1622

1623
  Value::LargestUInt value = 0;
1624
  while (current < token.end_) {
235,312✔
1625
    Char c = *current++;
182,662✔
1626
    if (c < '0' || c > '9')
182,662✔
1627
      return decodeDouble(token, decoded);
183✔
1628

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

1644
  if (isNegative) {
52,650✔
1645
    // We use the same magnitude assumption here, just in case.
1646
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1647
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1648
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,561✔
1649
    decoded = Value::LargestInt(value);
52,537✔
1650
  } else {
1651
    decoded = value;
24✔
1652
  }
1653

1654
  return true;
1655
}
1656

1657
bool OurReader::decodeDouble(Token& token) {
×
1658
  Value decoded;
×
1659
  if (!decodeDouble(token, decoded))
×
1660
    return false;
1661
  currentValue().swapPayload(decoded);
×
1662
  currentValue().setOffsetStart(token.start_ - begin_);
×
1663
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1664
  return true;
1665
}
×
1666

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

1690
bool OurReader::decodeString(Token& token) {
521✔
1691
  String decoded_string;
1692
  if (!decodeString(token, decoded_string))
521✔
1693
    return false;
1694
  Value decoded(decoded_string);
496✔
1695
  currentValue().swapPayload(decoded);
496✔
1696
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1697
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1698
  return true;
1699
}
496✔
1700

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

1756
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
147✔
1757
                                       Location end, unsigned int& unicode) {
1758

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

1789
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
161✔
1790
                                            Location end,
1791
                                            unsigned int& ret_unicode) {
1792
  if (end - current < 4)
161✔
1793
    return addError(
2✔
1794
        "Bad unicode escape sequence in string: four digits expected.", token,
1795
        current);
1796
  int unicode = 0;
1797
  for (int index = 0; index < 4; ++index) {
798✔
1798
    Char c = *current++;
639✔
1799
    unicode *= 16;
639✔
1800
    if (c >= '0' && c <= '9')
639✔
1801
      unicode += c - '0';
409✔
1802
    else if (c >= 'a' && c <= 'f')
230✔
1803
      unicode += c - 'a' + 10;
115✔
1804
    else if (c >= 'A' && c <= 'F')
115✔
1805
      unicode += c - 'A' + 10;
114✔
1806
    else
1807
      return addError(
2✔
1808
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1809
          token, current);
1810
  }
1811
  ret_unicode = static_cast<unsigned int>(unicode);
159✔
1812
  return true;
159✔
1813
}
1814

1815
bool OurReader::addError(const String& message, Token& token, Location extra) {
167✔
1816
  ErrorInfo info;
1817
  info.token_ = token;
167✔
1818
  info.message_ = message;
1819
  info.extra_ = extra;
167✔
1820
  errors_.push_back(info);
167✔
1821
  return false;
167✔
1822
}
1823

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

1837
bool OurReader::addErrorAndRecover(const String& message, Token& token,
85✔
1838
                                   TokenType skipUntilToken) {
1839
  addError(message, token);
85✔
1840
  return recoverFromError(skipUntilToken);
85✔
1841
}
1842

1843
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,830✔
1844

1845
OurReader::Char OurReader::getNextChar() {
175,382✔
1846
  if (current_ == end_)
175,382✔
1847
    return 0;
1848
  return *current_++;
174,422✔
1849
}
1850

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

1873
String OurReader::getLocationLineAndColumn(Location location) const {
191✔
1874
  int line, column;
1875
  getLocationLineAndColumn(location, line, column);
191✔
1876
  char buffer[18 + 16 + 16 + 1];
1877
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
191✔
1878
  return buffer;
191✔
1879
}
1880

1881
String OurReader::getFormattedErrorMessages() const {
939✔
1882
  String formattedMessage;
1883
  for (const auto& error : errors_) {
1,105✔
1884
    formattedMessage +=
1885
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
166✔
1886
    formattedMessage += "  " + error.message_ + "\n";
166✔
1887
    if (error.extra_)
166✔
1888
      formattedMessage +=
1889
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
50✔
1890
  }
1891
  return formattedMessage;
939✔
1892
}
1893

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

1907
class OurCharReader : public CharReader {
1908

1909
public:
1910
  OurCharReader(bool collectComments, OurFeatures const& features)
904✔
1911
      : CharReader(
904✔
1912
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,808✔
1913

1914
protected:
1915
  class OurImpl : public Impl {
1916
  public:
1917
    OurImpl(bool collectComments, OurFeatures const& features)
1918
        : collectComments_(collectComments), reader_(features) {}
904✔
1919

1920
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
949✔
1921
               String* errs) override {
1922
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
949✔
1923
      if (errs) {
941✔
1924
        *errs = reader_.getFormattedErrorMessages();
1,878✔
1925
      }
1926
      return ok;
941✔
1927
    }
1928

1929
    std::vector<CharReader::StructuredError>
1930
    getStructuredErrors() const override {
2✔
1931
      return reader_.getStructuredErrors();
2✔
1932
    }
1933

1934
  private:
1935
    bool const collectComments_;
1936
    OurReader reader_;
1937
  };
1938
};
1939

1940
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
901✔
1941
CharReaderBuilder::~CharReaderBuilder() = default;
901✔
1942
CharReader* CharReaderBuilder::newCharReader() const {
904✔
1943
  bool collectComments = settings_["collectComments"].asBool();
904✔
1944
  OurFeatures features = OurFeatures::all();
904✔
1945
  features.allowComments_ = settings_["allowComments"].asBool();
904✔
1946
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
904✔
1947
  features.strictRoot_ = settings_["strictRoot"].asBool();
904✔
1948
  features.allowDroppedNullPlaceholders_ =
904✔
1949
      settings_["allowDroppedNullPlaceholders"].asBool();
904✔
1950
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
904✔
1951
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
904✔
1952

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

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

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

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

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

2053
//////////////////////////////////
2054
// global functions
2055

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

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

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