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

open-source-parsers / jsoncpp / 27673820998

17 Jun 2026 07:45AM UTC coverage: 89.894% (-0.07%) from 89.959%
27673820998

Pull #1696

github

baylesj
fix: allow a comment between a trailing comma and ']' (#1500)

Trailing commas and comments are both allowed by default, but they did
not compose inside arrays: readArray detected a trailing-comma ']' with a
raw `*current_ == ']'` peek that skipped only whitespace, not comments.
So `[1, 2, /* c */]` left current_ at the comment, the peek failed, and
the parser tried to read another value -- which hit ']' and reported
"value, object or array expected". readObject already handled this via
readTokenSkippingComments.

Add skipCommentTokens() (skip whitespace and comments, leaving current_
at the next significant character) and use it in readArray before the
']' check. Consumed comments stay in commentsBefore_, so a comment before
a real element is still attached to it; if the array ends, they are
simply not attached -- matching object behavior.

Adds CharReaderTest/parseTrailingCommaWithComment covering line/block
comments after a trailing comma, an empty array containing only a
comment, the object form, and that a comment before a real element is
still attached.
Pull Request #1696: fix: allow a comment between a trailing comma and ']' (#1500)

2209 of 2626 branches covered (84.12%)

Branch coverage included in aggregate %.

13 of 14 new or added lines in 1 file covered. (92.86%)

56 existing lines in 2 files now uncovered.

2612 of 2737 relevant lines covered (95.43%)

23455.69 hits per line

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

89.88
/src/lib_json/json_reader.cpp
1
// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
2
// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
3
// Distributed under MIT license, or public domain if desired and
4
// recognized in your jurisdiction.
5
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
6

7
#if !defined(JSON_IS_AMALGAMATION)
8
#include "json_tool.h"
9
#include <json/assertions.h>
10
#include <json/reader.h>
11
#include <json/value.h>
12
#endif // if !defined(JSON_IS_AMALGAMATION)
13
#include <algorithm>
14
#include <cassert>
15
#include <cmath>
16
#include <cstring>
17
#include <iostream>
18
#include <istream>
19
#include <iterator>
20
#include <limits>
21
#include <memory>
22
#include <set>
23
#include <sstream>
24
#include <utility>
25

26
#include <cstdio>
27

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

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

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

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

48
namespace Json {
49

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

215
  return successful;
216
}
217

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

578
bool Reader::decodeDouble(Token& token, Value& decoded) {
145✔
579
  double value = 0;
145✔
580
  IStringStream is(String(token.start_, token.end_));
145✔
581
  is.imbue(std::locale::classic());
145✔
582
  if (!(is >> value)) {
145✔
583
    if (value == std::numeric_limits<double>::max())
24✔
584
      value = std::numeric_limits<double>::infinity();
12✔
585
    else if (value == std::numeric_limits<double>::lowest())
12!
586
      value = -std::numeric_limits<double>::infinity();
12✔
UNCOV
587
    else if (!std::isinf(value))
×
UNCOV
588
      return addError(
×
UNCOV
589
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
590
  }
591
  decoded = value;
145✔
592
  return true;
145✔
593
}
145✔
594

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

996
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,806✔
997

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

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

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

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

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

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

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

1133
  return successful;
1134
}
1135

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

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

1257
void OurReader::skipSpaces() {
168,340✔
1258
  while (current_ != end_) {
288,072✔
1259
    Char c = *current_;
287,121✔
1260
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
287,121✔
1261
      ++current_;
119,732✔
1262
    else
1263
      break;
1264
  }
1265
}
168,340✔
1266

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

1285
void OurReader::skipBom(bool skipBom) {
940✔
1286
  // The default behavior is to skip BOM.
1287
  if (skipBom) {
940✔
1288
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
939✔
1289
      begin_ += 3;
1✔
1290
      current_ = begin_;
1✔
1291
    }
1292
  }
1293
}
940✔
1294

1295
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1296
  if (end_ - current_ < patternLength)
226✔
1297
    return false;
1298
  int index = patternLength;
1299
  while (index--)
797✔
1300
    if (current_[index] != pattern[index])
631✔
1301
      return false;
1302
  current_ += patternLength;
166✔
1303
  return true;
166✔
1304
}
1305

1306
bool OurReader::readComment() {
1,622✔
1307
  const Location commentBegin = current_ - 1;
1,622✔
1308
  const Char c = getNextChar();
1,622✔
1309
  bool successful = false;
1310
  bool cStyleWithEmbeddedNewline = false;
1,622✔
1311

1312
  const bool isCStyleComment = (c == '*');
1313
  const bool isCppStyleComment = (c == '/');
1314
  if (isCStyleComment) {
1,622✔
1315
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1,087✔
1316
  } else if (isCppStyleComment) {
535!
1317
    successful = readCppStyleComment();
535✔
1318
  }
1319

1320
  if (!successful)
1,622!
UNCOV
1321
    return false;
×
1322

1323
  if (collectComments_) {
1,622✔
1324
    CommentPlacement placement = commentBefore;
1325

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

1339
    addComment(commentBegin, current_, placement);
1,604✔
1340
  }
1341
  return true;
1342
}
1343

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

1364
void OurReader::addComment(Location begin, Location end,
1,604✔
1365
                           CommentPlacement placement) {
1366
  assert(collectComments_);
1,604!
1367
  const String& normalized = normalizeEOL(begin, end);
1,604✔
1368
  if (placement == commentAfterOnSameLine) {
1,604✔
1369
    assert(lastValue_ != nullptr);
65!
1370
    lastValue_->setComment(normalized, placement);
130✔
1371
  } else {
1372
    commentsBefore_ += normalized;
1,539✔
1373
  }
1374
}
1,604✔
1375

1376
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
1,087✔
1377
  *containsNewLineResult = false;
1,087✔
1378

1379
  while ((current_ + 1) < end_) {
6,025!
1380
    Char c = getNextChar();
4,938✔
1381
    if (c == '*' && *current_ == '/')
4,938!
1382
      break;
1383
    if (c == '\n')
3,851✔
1384
      *containsNewLineResult = true;
98✔
1385
  }
1386

1387
  return getNextChar() == '/';
1,087✔
1388
}
1389

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

1406
bool OurReader::readNumber(bool checkInf) {
52,883✔
1407
  Location p = current_;
52,883✔
1408
  if (checkInf && p != end_ && *p == 'I') {
52,883!
1409
    current_ = ++p;
6✔
1410
    return false;
6✔
1411
  }
1412
  char c = '0'; // stopgap for already consumed character
1413
  // integral part
1414
  while (c >= '0' && c <= '9')
235,532✔
1415
    c = (current_ = p) < end_ ? *p++ : '\0';
182,655✔
1416
  // fractional part
1417
  if (c == '.') {
52,877✔
1418
    c = (current_ = p) < end_ ? *p++ : '\0';
113!
1419
    while (c >= '0' && c <= '9')
785✔
1420
      c = (current_ = p) < end_ ? *p++ : '\0';
672✔
1421
  }
1422
  // exponential part
1423
  if (c == 'e' || c == 'E') {
52,877✔
1424
    c = (current_ = p) < end_ ? *p++ : '\0';
103!
1425
    if (c == '+' || c == '-')
103✔
1426
      c = (current_ = p) < end_ ? *p++ : '\0';
76!
1427
    while (c >= '0' && c <= '9')
306✔
1428
      c = (current_ = p) < end_ ? *p++ : '\0';
203✔
1429
  }
1430
  return true;
1431
}
1432
bool OurReader::readString() {
1,175✔
1433
  Char c = 0;
1434
  while (current_ != end_) {
34,523!
1435
    c = getNextChar();
34,523✔
1436
    if (c == '\\')
34,523✔
1437
      getNextChar();
1,275✔
1438
    else if (c == '"')
33,248✔
1439
      break;
1440
  }
1441
  return c == '"';
1,175✔
1442
}
1443

1444
bool OurReader::readStringSingleQuote() {
11✔
1445
  Char c = 0;
1446
  while (current_ != end_) {
26!
1447
    c = getNextChar();
26✔
1448
    if (c == '\\')
26✔
1449
      getNextChar();
2✔
1450
    else if (c == '\'')
24✔
1451
      break;
1452
  }
1453
  return c == '\'';
11✔
1454
}
1455

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

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

1498
    Token comma;
1499
    if (!readTokenSkippingComments(comma) ||
610✔
1500
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
602✔
1501
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1502
                                comma, tokenObjectEnd);
1503
    }
1504
    if (comma.type_ == tokenObjectEnd)
583✔
1505
      return true;
1506
  }
1507
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1508
                            tokenObjectEnd);
1509
}
369✔
1510

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

1538
    Token currentToken;
1539
    // Accept Comment after last item in the array.
1540
    ok = readTokenSkippingComments(currentToken);
52,699✔
1541
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,699✔
1542
                         currentToken.type_ != tokenArrayEnd);
52,699✔
1543
    if (!ok || badTokenType) {
52,699✔
1544
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
46✔
1545
                                currentToken, tokenArrayEnd);
1546
    }
1547
    if (currentToken.type_ == tokenArrayEnd)
52,676✔
1548
      break;
1549
  }
52,412✔
1550
  return true;
264✔
1551
}
2,157✔
1552

1553
bool OurReader::decodeNumber(Token& token) {
52,842✔
1554
  Value decoded;
52,842✔
1555
  if (!decodeNumber(token, decoded))
52,842✔
1556
    return false;
1557
  currentValue().swapPayload(decoded);
52,833✔
1558
  currentValue().setOffsetStart(token.start_ - begin_);
52,833✔
1559
  currentValue().setOffsetLimit(token.end_ - begin_);
52,833✔
1560
  return true;
1561
}
52,842✔
1562

1563
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,845✔
1564
  // Attempts to parse the number as an integer. If the number is
1565
  // larger than the maximum supported value of an integer then
1566
  // we decode the number as a double.
1567
  Location current = token.start_;
52,845✔
1568
  const bool isNegative = *current == '-';
52,845✔
1569
  if (isNegative) {
52,845✔
1570
    ++current;
141✔
1571
  }
1572

1573
  // We assume we can represent the largest and smallest integer types as
1574
  // unsigned integers with separate sign. This is only true if they can fit
1575
  // into an unsigned integer.
1576
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1577
                "Int must be smaller than UInt");
1578

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

1590
  static constexpr Value::LargestUInt positive_threshold =
1591
      Value::maxLargestUInt / 10;
1592
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1593

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

1604
  const Value::LargestUInt threshold =
1605
      isNegative ? negative_threshold : positive_threshold;
1606
  const Value::UInt max_last_digit =
1607
      isNegative ? negative_last_digit : positive_last_digit;
1608

1609
  Value::LargestUInt value = 0;
1610
  while (current < token.end_) {
235,298✔
1611
    Char c = *current++;
182,649✔
1612
    if (c < '0' || c > '9')
182,649✔
1613
      return decodeDouble(token, decoded);
177✔
1614

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

1630
  if (isNegative) {
52,649✔
1631
    // We use the same magnitude assumption here, just in case.
1632
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1633
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1634
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,560✔
1635
    decoded = Value::LargestInt(value);
52,536✔
1636
  } else {
1637
    decoded = value;
24✔
1638
  }
1639

1640
  return true;
1641
}
1642

UNCOV
1643
bool OurReader::decodeDouble(Token& token) {
×
UNCOV
1644
  Value decoded;
×
UNCOV
1645
  if (!decodeDouble(token, decoded))
×
1646
    return false;
UNCOV
1647
  currentValue().swapPayload(decoded);
×
UNCOV
1648
  currentValue().setOffsetStart(token.start_ - begin_);
×
1649
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1650
  return true;
1651
}
×
1652

1653
bool OurReader::decodeDouble(Token& token, Value& decoded) {
196✔
1654
  double value = 0;
196✔
1655
  IStringStream is(String(token.start_, token.end_));
196✔
1656
  is.imbue(std::locale::classic());
196✔
1657
  if (!(is >> value)) {
196✔
1658
    if (value == std::numeric_limits<double>::max())
33✔
1659
      value = std::numeric_limits<double>::infinity();
12✔
1660
    else if (value == std::numeric_limits<double>::lowest())
21✔
1661
      value = -std::numeric_limits<double>::infinity();
12✔
1662
    else if (!std::isinf(value))
9!
1663
      return addError(
9✔
1664
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
18✔
1665
  }
1666
  decoded = value;
187✔
1667
  return true;
187✔
1668
}
196✔
1669

1670
bool OurReader::decodeString(Token& token) {
519✔
1671
  String decoded_string;
1672
  if (!decodeString(token, decoded_string))
519✔
1673
    return false;
1674
  Value decoded(decoded_string);
496✔
1675
  currentValue().swapPayload(decoded);
496✔
1676
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1677
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1678
  return true;
1679
}
496✔
1680

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

1736
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1737
                                       Location end, unsigned int& unicode) {
1738

1739
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1740
    return false;
1741
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1742
    // surrogate pairs
1743
    if (end - current < 6)
15✔
1744
      return addError(
2✔
1745
          "additional six characters expected to parse unicode surrogate pair.",
1746
          token, current);
1747
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1748
      unsigned int surrogatePair;
1749
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1750
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1751
      } else
UNCOV
1752
        return false;
×
1753
    } else
1754
      return addError("expecting another \\u token to begin the second half of "
2✔
1755
                      "a unicode surrogate pair",
1756
                      token, current);
1757
  }
1758
  return true;
1759
}
1760

1761
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
158✔
1762
                                            Location end,
1763
                                            unsigned int& ret_unicode) {
1764
  if (end - current < 4)
158✔
1765
    return addError(
2✔
1766
        "Bad unicode escape sequence in string: four digits expected.", token,
1767
        current);
1768
  int unicode = 0;
1769
  for (int index = 0; index < 4; ++index) {
783✔
1770
    Char c = *current++;
627✔
1771
    unicode *= 16;
627✔
1772
    if (c >= '0' && c <= '9')
627✔
1773
      unicode += c - '0';
400✔
1774
    else if (c >= 'a' && c <= 'f')
227✔
1775
      unicode += c - 'a' + 10;
115✔
1776
    else if (c >= 'A' && c <= 'F')
112✔
1777
      unicode += c - 'A' + 10;
111✔
1778
    else
1779
      return addError(
2✔
1780
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1781
          token, current);
1782
  }
1783
  ret_unicode = static_cast<unsigned int>(unicode);
156✔
1784
  return true;
156✔
1785
}
1786

1787
bool OurReader::addError(const String& message, Token& token, Location extra) {
162✔
1788
  ErrorInfo info;
1789
  info.token_ = token;
162✔
1790
  info.message_ = message;
1791
  info.extra_ = extra;
162✔
1792
  errors_.push_back(info);
162✔
1793
  return false;
162✔
1794
}
1795

1796
bool OurReader::recoverFromError(TokenType skipUntilToken) {
152✔
1797
  size_t errorCount = errors_.size();
1798
  Token skip;
1799
  for (;;) {
1800
    if (!readToken(skip))
410✔
1801
      errors_.resize(errorCount); // discard errors caused by recovery
173✔
1802
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
410✔
1803
      break;
1804
  }
1805
  errors_.resize(errorCount);
152✔
1806
  return false;
152✔
1807
}
1808

1809
bool OurReader::addErrorAndRecover(const String& message, Token& token,
84✔
1810
                                   TokenType skipUntilToken) {
1811
  addError(message, token);
84✔
1812
  return recoverFromError(skipUntilToken);
84✔
1813
}
1814

1815
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,766✔
1816

1817
OurReader::Char OurReader::getNextChar() {
175,325✔
1818
  if (current_ == end_)
175,325✔
1819
    return 0;
1820
  return *current_++;
174,374✔
1821
}
1822

1823
void OurReader::getLocationLineAndColumn(Location location, int& line,
184✔
1824
                                         int& column) const {
1825
  Location current = begin_;
184✔
1826
  Location lastLineStart = current;
1827
  line = 0;
184✔
1828
  while (current < location && current != end_) {
2,271!
1829
    Char c = *current++;
2,087✔
1830
    if (c == '\r') {
2,087✔
1831
      if (current != end_ && *current == '\n')
1!
UNCOV
1832
        ++current;
×
1833
      lastLineStart = current;
1834
      ++line;
1✔
1835
    } else if (c == '\n') {
2,086✔
1836
      lastLineStart = current;
1837
      ++line;
28✔
1838
    }
1839
  }
1840
  // column & line start at 1
1841
  column = int(location - lastLineStart) + 1;
184✔
1842
  ++line;
184✔
1843
}
184✔
1844

1845
String OurReader::getLocationLineAndColumn(Location location) const {
184✔
1846
  int line, column;
1847
  getLocationLineAndColumn(location, line, column);
184✔
1848
  char buffer[18 + 16 + 16 + 1];
1849
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
184✔
1850
  return buffer;
184✔
1851
}
1852

1853
String OurReader::getFormattedErrorMessages() const {
930✔
1854
  String formattedMessage;
1855
  for (const auto& error : errors_) {
1,091✔
1856
    formattedMessage +=
1857
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
161✔
1858
    formattedMessage += "  " + error.message_ + "\n";
161✔
1859
    if (error.extra_)
161✔
1860
      formattedMessage +=
1861
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
46✔
1862
  }
1863
  return formattedMessage;
930✔
1864
}
1865

1866
std::vector<CharReader::StructuredError>
1867
OurReader::getStructuredErrors() const {
2✔
1868
  std::vector<CharReader::StructuredError> allErrors;
1869
  for (const auto& error : errors_) {
3✔
1870
    CharReader::StructuredError structured;
1871
    structured.offset_start = error.token_.start_ - begin_;
1✔
1872
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1873
    structured.message = error.message_;
1✔
1874
    allErrors.push_back(structured);
1✔
1875
  }
1876
  return allErrors;
2✔
UNCOV
1877
}
×
1878

1879
class OurCharReader : public CharReader {
1880

1881
public:
1882
  OurCharReader(bool collectComments, OurFeatures const& features)
903✔
1883
      : CharReader(
903✔
1884
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,806✔
1885

1886
protected:
1887
  class OurImpl : public Impl {
1888
  public:
1889
    OurImpl(bool collectComments, OurFeatures const& features)
1890
        : collectComments_(collectComments), reader_(features) {}
903✔
1891

1892
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
940✔
1893
               String* errs) override {
1894
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
940✔
1895
      if (errs) {
932✔
1896
        *errs = reader_.getFormattedErrorMessages();
1,860✔
1897
      }
1898
      return ok;
932✔
1899
    }
1900

1901
    std::vector<CharReader::StructuredError>
1902
    getStructuredErrors() const override {
2✔
1903
      return reader_.getStructuredErrors();
2✔
1904
    }
1905

1906
  private:
1907
    bool const collectComments_;
1908
    OurReader reader_;
1909
  };
1910
};
1911

1912
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
900✔
1913
CharReaderBuilder::~CharReaderBuilder() = default;
900✔
1914
CharReader* CharReaderBuilder::newCharReader() const {
903✔
1915
  bool collectComments = settings_["collectComments"].asBool();
903✔
1916
  OurFeatures features = OurFeatures::all();
903✔
1917
  features.allowComments_ = settings_["allowComments"].asBool();
903✔
1918
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
903✔
1919
  features.strictRoot_ = settings_["strictRoot"].asBool();
903✔
1920
  features.allowDroppedNullPlaceholders_ =
903✔
1921
      settings_["allowDroppedNullPlaceholders"].asBool();
903✔
1922
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
903✔
1923
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
903✔
1924

1925
  // Stack limit is always a size_t, so we get this as an unsigned int
1926
  // regardless of it we have 64-bit integer support enabled.
1927
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
903✔
1928
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
903✔
1929
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
903✔
1930
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
903✔
1931
  features.skipBom_ = settings_["skipBom"].asBool();
903✔
1932
  return new OurCharReader(collectComments, features);
903✔
1933
}
1934

1935
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1936
  static const auto& valid_keys = *new std::set<String>{
1937
      "collectComments",
1938
      "allowComments",
1939
      "allowTrailingCommas",
1940
      "strictRoot",
1941
      "allowDroppedNullPlaceholders",
1942
      "allowNumericKeys",
1943
      "allowSingleQuotes",
1944
      "stackLimit",
1945
      "failIfExtra",
1946
      "rejectDupKeys",
1947
      "allowSpecialFloats",
1948
      "skipBom",
1949
  };
15!
1950
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
54✔
1951
    auto key = si.name();
25✔
1952
    if (valid_keys.count(key))
25✔
1953
      continue;
1954
    if (invalid)
1!
1955
      (*invalid)[key] = *si;
1✔
1956
    else
1957
      return false;
1958
  }
1959
  return invalid ? invalid->empty() : true;
2!
1960
}
2!
1961

1962
Value& CharReaderBuilder::operator[](const String& key) {
1✔
1963
  return settings_[key];
1✔
1964
}
1965
// static
1966
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1967
  //! [CharReaderBuilderStrictMode]
1968
  (*settings)["allowComments"] = false;
3✔
1969
  (*settings)["allowTrailingCommas"] = false;
3✔
1970
  (*settings)["strictRoot"] = true;
3✔
1971
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1972
  (*settings)["allowNumericKeys"] = false;
3✔
1973
  (*settings)["allowSingleQuotes"] = false;
3✔
1974
  (*settings)["stackLimit"] = 256;
3✔
1975
  (*settings)["failIfExtra"] = true;
3✔
1976
  (*settings)["rejectDupKeys"] = true;
3✔
1977
  (*settings)["allowSpecialFloats"] = false;
3✔
1978
  (*settings)["skipBom"] = true;
3✔
1979
  //! [CharReaderBuilderStrictMode]
1980
}
3✔
1981
// static
1982
void CharReaderBuilder::setDefaults(Json::Value* settings) {
900✔
1983
  //! [CharReaderBuilderDefaults]
1984
  (*settings)["collectComments"] = true;
900✔
1985
  (*settings)["allowComments"] = true;
900✔
1986
  (*settings)["allowTrailingCommas"] = true;
900✔
1987
  (*settings)["strictRoot"] = false;
900✔
1988
  (*settings)["allowDroppedNullPlaceholders"] = false;
900✔
1989
  (*settings)["allowNumericKeys"] = false;
900✔
1990
  (*settings)["allowSingleQuotes"] = false;
900✔
1991
  (*settings)["stackLimit"] = 256;
900✔
1992
  (*settings)["failIfExtra"] = false;
900✔
1993
  (*settings)["rejectDupKeys"] = false;
900✔
1994
  (*settings)["allowSpecialFloats"] = false;
900✔
1995
  (*settings)["skipBom"] = true;
900✔
1996
  //! [CharReaderBuilderDefaults]
1997
}
900✔
1998
// static
UNCOV
1999
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
2000
  //! [CharReaderBuilderECMA404Mode]
UNCOV
2001
  (*settings)["allowComments"] = false;
×
UNCOV
2002
  (*settings)["allowTrailingCommas"] = false;
×
UNCOV
2003
  (*settings)["strictRoot"] = false;
×
UNCOV
2004
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
UNCOV
2005
  (*settings)["allowNumericKeys"] = false;
×
UNCOV
2006
  (*settings)["allowSingleQuotes"] = false;
×
UNCOV
2007
  (*settings)["stackLimit"] = 256;
×
UNCOV
2008
  (*settings)["failIfExtra"] = true;
×
UNCOV
2009
  (*settings)["rejectDupKeys"] = false;
×
UNCOV
2010
  (*settings)["allowSpecialFloats"] = false;
×
2011
  (*settings)["skipBom"] = false;
×
2012
  //! [CharReaderBuilderECMA404Mode]
2013
}
×
2014

2015
std::vector<CharReader::StructuredError>
2016
CharReader::getStructuredErrors() const {
2✔
2017
  return _impl->getStructuredErrors();
2✔
2018
}
2019

2020
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
940✔
2021
                       String* errs) {
2022
  return _impl->parse(beginDoc, endDoc, root, errs);
940✔
2023
}
2024

2025
//////////////////////////////////
2026
// global functions
2027

2028
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
2029
                     String* errs) {
2030
  OStringStream ssin;
4✔
2031
  ssin << sin.rdbuf();
4✔
2032
  String doc = std::move(ssin).str();
2033
  char const* begin = doc.data();
2034
  char const* end = begin + doc.size();
4✔
2035
  // Note that we do not actually need a null-terminator.
2036
  CharReaderPtr const reader(fact.newCharReader());
4✔
2037
  return reader->parse(begin, end, root, errs);
8✔
2038
}
4✔
2039

2040
IStream& operator>>(IStream& sin, Value& root) {
1✔
2041
  CharReaderBuilder b;
1✔
2042
  String errs;
2043
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2044
  if (!ok) {
1!
UNCOV
2045
    throwRuntimeError(errs);
×
2046
  }
2047
  return sin;
1✔
2048
}
1✔
2049

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