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

open-source-parsers / jsoncpp / 28665911023

03 Jul 2026 02:10PM UTC coverage: 89.915% (+0.008%) from 89.907%
28665911023

push

github

web-flow
Merge ffa5de9dd into edc01ab10

2230 of 2648 branches covered (84.21%)

Branch coverage included in aggregate %.

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

2620 of 2746 relevant lines covered (95.41%)

23853.77 hits per line

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

89.9
/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
  // A number token that is only a sign (e.g. "-") carries no digits; the loop
534
  // below never runs and would otherwise decode it as zero. Reject it via
535
  // decodeDouble, matching how a non-digit inside the token is handled.
536
  if (current == token.end_)
52,651!
NEW
537
    return decodeDouble(token, decoded);
×
538
  // TODO: Help the compiler do the div and mod at compile time or get rid of
539
  // them.
540
  Value::LargestUInt maxIntegerValue =
541
      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
52,651✔
542
                 : Value::maxLargestUInt;
543
  Value::LargestUInt threshold = maxIntegerValue / 10;
52,651✔
544
  Value::LargestUInt value = 0;
545
  while (current < token.end_) {
234,760✔
546
    Char c = *current++;
182,254✔
547
    if (c < '0' || c > '9')
182,254✔
548
      return decodeDouble(token, decoded);
127✔
549
    auto digit(static_cast<Value::UInt>(c - '0'));
182,127✔
550
    if (value >= threshold) {
182,127✔
551
      // We've hit or exceeded the max value divided by 10 (rounded down). If
552
      // a) we've only just touched the limit, b) this is the last digit, and
553
      // c) it's small enough to fit in that rounding delta, we're okay.
554
      // Otherwise treat this number as a double to avoid overflow.
555
      if (value > threshold || current != token.end_ ||
42!
556
          digit > maxIntegerValue % 10) {
30✔
557
        return decodeDouble(token, decoded);
18✔
558
      }
559
    }
560
    value = value * 10 + digit;
182,109✔
561
  }
562
  if (isNegative && value == maxIntegerValue)
52,506✔
563
    decoded = Value::minLargestInt;
12✔
564
  else if (isNegative)
52,494✔
565
    decoded = -Value::LargestInt(value);
72✔
566
  else if (value <= Value::LargestUInt(Value::maxInt))
52,422✔
567
    decoded = Value::LargestInt(value);
52,362✔
568
  else
569
    decoded = value;
60✔
570
  return true;
571
}
572

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

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

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

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

672
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
673
                                    Location end, unsigned int& unicode) {
674

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

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

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

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

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

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

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

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

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

789
// Deprecated. Preserved for backward compatibility
790
String Reader::getFormatedErrorMessages() const {
×
791
  return getFormattedErrorMessages();
×
792
}
793

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

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

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

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

853
bool Reader::good() const { return errors_.empty(); }
×
854

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

873
OurFeatures OurFeatures::all() { return {}; }
905✔
874

875
// Implementation of class Reader
876
// ////////////////////////////////
877

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

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

891
private:
892
  OurReader(OurReader const&);      // no impl
893
  void operator=(OurReader const&); // no impl
894

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

915
  class Token {
916
  public:
917
    TokenType type_;
918
    Location start_;
919
    Location end_;
920
  };
921

922
  class ErrorInfo {
169✔
923
  public:
924
    Token token_;
925
    String message_;
926
    Location extra_;
927
  };
928

929
  using Errors = std::deque<ErrorInfo>;
930

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

968
  static String normalizeEOL(Location begin, Location end);
969
  static bool containsNewLine(Location begin, Location end);
970

971
  using Nodes = std::stack<Value*>;
972

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

984
  OurFeatures const features_;
985
  bool collectComments_ = false;
986
}; // OurReader
987

988
// complete copy of Read impl, for OurReader
989

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

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

1007
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,810✔
1008

1009
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
952✔
1010
                      bool collectComments) {
1011
  if (!features_.allowComments_) {
952✔
1012
    collectComments = false;
1013
  }
1014

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

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

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

1063
  if (collectComments_ && !commentsBefore_.empty()) {
56,122✔
1064
    currentValue().setComment(commentsBefore_, commentBefore);
1,206✔
1065
    commentsBefore_.clear();
1066
  }
1067

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

1138
  if (collectComments_) {
54,292✔
1139
    lastValueEnd_ = current_;
53,831✔
1140
    lastValueHasAComment_ = false;
53,831✔
1141
    lastValue_ = &currentValue();
53,831✔
1142
  }
1143

1144
  return successful;
1145
}
1146

1147
bool OurReader::readTokenSkippingComments(Token& token) {
111,073✔
1148
  bool success = readToken(token);
111,073✔
1149
  if (features_.allowComments_) {
111,073✔
1150
    while (success && token.type_ == tokenComment) {
111,525✔
1151
      success = readToken(token);
1,542✔
1152
    }
1153
  }
1154
  return success;
111,073✔
1155
}
1156

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

1268
void OurReader::skipSpaces() {
168,392✔
1269
  while (current_ != end_) {
288,124✔
1270
    Char c = *current_;
287,162✔
1271
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
287,162✔
1272
      ++current_;
119,732✔
1273
    else
1274
      break;
1275
  }
1276
}
168,392✔
1277

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

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

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

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

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

1331
  if (!successful)
1,622!
1332
    return false;
×
1333

1334
  if (collectComments_) {
1,622✔
1335
    CommentPlacement placement = commentBefore;
1336

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

1350
    addComment(commentBegin, current_, placement);
1,604✔
1351
  }
1352
  return true;
1353
}
1354

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

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

1387
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,087✔
1388
  *containsNewLineResult = false;
1,087✔
1389

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

1398
  return getNextChar() == '/';
1,087✔
1399
}
1400

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

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

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

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

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

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

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

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

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

1574
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,857✔
1575
  // Attempts to parse the number as an integer. If the number is
1576
  // larger than the maximum supported value of an integer then
1577
  // we decode the number as a double.
1578
  Location current = token.start_;
52,857✔
1579
  const bool isNegative = *current == '-';
52,857✔
1580
  if (isNegative) {
52,857✔
1581
    ++current;
147✔
1582
  }
1583
  // A number token that is only a sign (e.g. "-") carries no digits; the loop
1584
  // below never runs and would otherwise decode it as zero. Reject it via
1585
  // decodeDouble, matching how a non-digit inside the token is handled.
1586
  if (current == token.end_)
52,857✔
1587
    return decodeDouble(token, decoded);
5✔
1588

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

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

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

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

1620
  const Value::LargestUInt threshold =
1621
      isNegative ? negative_threshold : positive_threshold;
52,852✔
1622
  const Value::UInt max_last_digit =
1623
      isNegative ? negative_last_digit : positive_last_digit;
1624

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

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

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

1656
  return true;
1657
}
1658

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

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

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

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

1758
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1759
                                       Location end, unsigned int& unicode) {
1760

1761
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1762
    return false;
1763
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1764
    // surrogate pairs
1765
    if (end - current < 6)
15✔
1766
      return addError(
2✔
1767
          "additional six characters expected to parse unicode surrogate pair.",
1768
          token, current);
1769
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1770
      unsigned int surrogatePair;
1771
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1772
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1773
      } else
1774
        return false;
×
1775
    } else
1776
      return addError("expecting another \\u token to begin the second half of "
2✔
1777
                      "a unicode surrogate pair",
1778
                      token, current);
1779
  }
1780
  return true;
1781
}
1782

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

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

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

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

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

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

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

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

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

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

1901
class OurCharReader : public CharReader {
1902

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

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

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

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

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

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

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

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

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

2037
std::vector<CharReader::StructuredError>
2038
CharReader::getStructuredErrors() const {
2✔
2039
  return _impl->getStructuredErrors();
2✔
2040
}
2041

2042
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
952✔
2043
                       String* errs) {
2044
  return _impl->parse(beginDoc, endDoc, root, errs);
952✔
2045
}
2046

2047
//////////////////////////////////
2048
// global functions
2049

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

2062
IStream& operator>>(IStream& sin, Value& root) {
1✔
2063
  CharReaderBuilder b;
1✔
2064
  String errs;
2065
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2066
  if (!ok) {
1!
2067
    throwRuntimeError(errs);
×
2068
  }
2069
  return sin;
1✔
2070
}
1✔
2071

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

© 2026 Coveralls, Inc