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

open-source-parsers / jsoncpp / 23129739262

16 Mar 2026 05:44AM UTC coverage: 90.093% (+0.2%) from 89.937%
23129739262

Pull #1662

github

web-flow
Merge 1d4d1173e into 9a5bbec0d
Pull Request #1662: Fix number parsing failing under non-C locales

2183 of 2589 branches covered (84.32%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

66 existing lines in 2 files now uncovered.

2573 of 2690 relevant lines covered (95.65%)

23748.16 hits per line

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

90.01
/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
  // std::istream_iterator<char> begin(is);
92
  // std::istream_iterator<char> end;
93
  // Those would allow streamed input from a file, if parse() were a
94
  // template function.
95

96
  // Since String is reference-counted, this at least does not
97
  // create an extra copy.
98
  String doc(std::istreambuf_iterator<char>(is), {});
1✔
99
  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
2✔
100
}
101

102
bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
711✔
103
                   bool collectComments) {
104
  if (!features_.allowComments_) {
711✔
105
    collectComments = false;
106
  }
107

108
  begin_ = beginDoc;
711✔
109
  end_ = endDoc;
711✔
110
  collectComments_ = collectComments;
711✔
111
  current_ = begin_;
711✔
112
  lastValueEnd_ = nullptr;
711✔
113
  lastValue_ = nullptr;
711✔
114
  commentsBefore_.clear();
115
  errors_.clear();
711✔
116
  while (!nodes_.empty())
722✔
117
    nodes_.pop();
11✔
118
  nodes_.push(&root);
711!
119

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

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

154
  Token token;
155
  readTokenSkippingComments(token);
53,564✔
156
  bool successful = true;
157

158
  if (collectComments_ && !commentsBefore_.empty()) {
53,564✔
159
    currentValue().setComment(commentsBefore_, commentBefore);
1,191✔
160
    commentsBefore_.clear();
161
  }
162

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

215
  if (collectComments_) {
53,559✔
216
    lastValueEnd_ = current_;
53,558✔
217
    lastValue_ = &currentValue();
53,558✔
218
  }
219

220
  return successful;
221
}
222

223
bool Reader::readTokenSkippingComments(Token& token) {
107,548✔
224
  bool success = readToken(token);
107,548✔
225
  if (features_.allowComments_) {
107,548✔
226
    while (success && token.type_ == tokenComment) {
108,115✔
227
      success = readToken(token);
569✔
228
    }
229
  }
230
  return success;
107,548✔
231
}
232

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

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

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

325
bool Reader::readComment() {
569✔
326
  Location commentBegin = current_ - 1;
569✔
327
  Char c = getNextChar();
569✔
328
  bool successful = false;
329
  if (c == '*')
569✔
330
    successful = readCStyleComment();
73✔
331
  else if (c == '/')
496!
332
    successful = readCppStyleComment();
496✔
333
  if (!successful)
569!
334
    return false;
×
335

336
  if (collectComments_) {
569!
337
    CommentPlacement placement = commentBefore;
338
    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
569✔
339
      if (c != '*' || !containsNewLine(commentBegin, current_))
50!
340
        placement = commentAfterOnSameLine;
341
    }
342

343
    addComment(commentBegin, current_, placement);
569✔
344
  }
345
  return true;
346
}
347

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

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

379
bool Reader::readCStyleComment() {
73✔
380
  while ((current_ + 1) < end_) {
2,419!
381
    Char c = getNextChar();
2,419✔
382
    if (c == '*' && *current_ == '/')
2,419!
383
      break;
384
  }
385
  return getNextChar() == '/';
73✔
386
}
387

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

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

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

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

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

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

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

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

520
bool Reader::decodeNumber(Token& token) {
52,650✔
521
  Value decoded;
52,650✔
522
  if (!decodeNumber(token, decoded))
52,650!
523
    return false;
524
  currentValue().swapPayload(decoded);
52,650✔
525
  currentValue().setOffsetStart(token.start_ - begin_);
52,650✔
526
  currentValue().setOffsetLimit(token.end_ - begin_);
52,650✔
527
  return true;
528
}
52,650✔
529

530
bool Reader::decodeNumber(Token& token, Value& decoded) {
52,651✔
531
  // Attempts to parse the number as an integer. If the number is
532
  // larger than the maximum supported value of an integer then
533
  // we decode the number as a double.
534
  Location current = token.start_;
52,651✔
535
  bool isNegative = *current == '-';
52,651✔
536
  if (isNegative)
52,651✔
537
    ++current;
133✔
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
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
    else if (!std::isinf(value))
×
593
      return addError(
×
594
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
595
  }
596
  decoded = value;
145✔
597
  return true;
145✔
598
}
145✔
599

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

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

666
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
667
                                    Location end, unsigned int& unicode) {
668

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

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

717
bool Reader::addError(const String& message, Token& token, Location extra) {
16✔
718
  ErrorInfo info;
719
  info.token_ = token;
16✔
720
  info.message_ = message;
721
  info.extra_ = extra;
16✔
722
  errors_.push_back(info);
16✔
723
  return false;
16✔
724
}
725

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

739
bool Reader::addErrorAndRecover(const String& message, Token& token,
5✔
740
                                TokenType skipUntilToken) {
741
  addError(message, token);
5✔
742
  return recoverFromError(skipUntilToken);
5✔
743
}
744

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

747
Reader::Char Reader::getNextChar() {
160,551✔
748
  if (current_ == end_)
160,551✔
749
    return 0;
750
  return *current_++;
159,833✔
751
}
752

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

775
String Reader::getLocationLineAndColumn(Location location) const {
24✔
776
  int line, column;
777
  getLocationLineAndColumn(location, line, column);
24✔
778
  char buffer[18 + 16 + 16 + 1];
779
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
24✔
780
  return buffer;
24✔
781
}
782

783
// Deprecated. Preserved for backward compatibility
UNCOV
784
String Reader::getFormatedErrorMessages() const {
×
UNCOV
785
  return getFormattedErrorMessages();
×
786
}
787

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

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

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

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

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

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

867
OurFeatures OurFeatures::all() { return {}; }
901✔
868

869
// Implementation of class Reader
870
// ////////////////////////////////
871

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

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

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

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

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

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

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

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

961
  static String normalizeEOL(Location begin, Location end);
962
  static bool containsNewLine(Location begin, Location end);
963

964
  using Nodes = std::stack<Value*>;
965

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

977
  OurFeatures const features_;
978
  bool collectComments_ = false;
979
}; // OurReader
980

981
// complete copy of Read impl, for OurReader
982

983
bool OurReader::containsNewLine(OurReader::Location begin,
185✔
984
                                OurReader::Location end) {
985
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
225!
986
}
987

988
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,802✔
989

990
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
933✔
991
                      bool collectComments) {
992
  if (!features_.allowComments_) {
933✔
993
    collectComments = false;
994
  }
995

996
  begin_ = beginDoc;
933✔
997
  end_ = endDoc;
933✔
998
  collectComments_ = collectComments;
933✔
999
  current_ = begin_;
933✔
1000
  lastValueEnd_ = nullptr;
933✔
1001
  lastValue_ = nullptr;
933✔
1002
  commentsBefore_.clear();
1003
  errors_.clear();
933✔
1004
  while (!nodes_.empty())
933!
UNCOV
1005
    nodes_.pop();
×
1006
  nodes_.push(&root);
933!
1007

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

1036
bool OurReader::readValue() {
56,094✔
1037
  //  To preserve the old behaviour we cast size_t to int.
1038
  if (nodes_.size() > features_.stackLimit_)
56,094✔
1039
    throwRuntimeError("Exceeded stackLimit in readValue().");
8✔
1040
  Token token;
1041
  readTokenSkippingComments(token);
56,086✔
1042
  bool successful = true;
1043

1044
  if (collectComments_ && !commentsBefore_.empty()) {
56,086✔
1045
    currentValue().setComment(commentsBefore_, commentBefore);
1,203✔
1046
    commentsBefore_.clear();
1047
  }
1048

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

1119
  if (collectComments_) {
54,256✔
1120
    lastValueEnd_ = current_;
53,795✔
1121
    lastValueHasAComment_ = false;
53,795✔
1122
    lastValue_ = &currentValue();
53,795✔
1123
  }
1124

1125
  return successful;
1126
}
1127

1128
bool OurReader::readTokenSkippingComments(Token& token) {
111,002✔
1129
  bool success = readToken(token);
111,002✔
1130
  if (features_.allowComments_) {
111,002✔
1131
    while (success && token.type_ == tokenComment) {
110,502✔
1132
      success = readToken(token);
590✔
1133
    }
1134
  }
1135
  return success;
111,002✔
1136
}
1137

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

1249
void OurReader::skipSpaces() {
167,226✔
1250
  while (current_ != end_) {
286,952✔
1251
    Char c = *current_;
286,008✔
1252
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
286,008✔
1253
      ++current_;
119,726✔
1254
    else
1255
      break;
1256
  }
1257
}
167,226✔
1258

1259
void OurReader::skipBom(bool skipBom) {
933✔
1260
  // The default behavior is to skip BOM.
1261
  if (skipBom) {
933✔
1262
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
932✔
1263
      begin_ += 3;
1✔
1264
      current_ = begin_;
1✔
1265
    }
1266
  }
1267
}
933✔
1268

1269
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1270
  if (end_ - current_ < patternLength)
226✔
1271
    return false;
1272
  int index = patternLength;
1273
  while (index--)
797✔
1274
    if (current_[index] != pattern[index])
631✔
1275
      return false;
1276
  current_ += patternLength;
166✔
1277
  return true;
166✔
1278
}
1279

1280
bool OurReader::readComment() {
615✔
1281
  const Location commentBegin = current_ - 1;
615✔
1282
  const Char c = getNextChar();
615✔
1283
  bool successful = false;
1284
  bool cStyleWithEmbeddedNewline = false;
615✔
1285

1286
  const bool isCStyleComment = (c == '*');
1287
  const bool isCppStyleComment = (c == '/');
1288
  if (isCStyleComment) {
615✔
1289
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
85✔
1290
  } else if (isCppStyleComment) {
530!
1291
    successful = readCppStyleComment();
530✔
1292
  }
1293

1294
  if (!successful)
615!
UNCOV
1295
    return false;
×
1296

1297
  if (collectComments_) {
615✔
1298
    CommentPlacement placement = commentBefore;
1299

1300
    if (!lastValueHasAComment_) {
597✔
1301
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
579✔
1302
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
65✔
1303
          placement = commentAfterOnSameLine;
1304
          lastValueHasAComment_ = true;
64✔
1305
        }
1306
      }
1307
    }
1308

1309
    addComment(commentBegin, current_, placement);
597✔
1310
  }
1311
  return true;
1312
}
1313

1314
String OurReader::normalizeEOL(OurReader::Location begin,
597✔
1315
                               OurReader::Location end) {
1316
  String normalized;
1317
  normalized.reserve(static_cast<size_t>(end - begin));
597✔
1318
  OurReader::Location current = begin;
1319
  while (current != end) {
21,944✔
1320
    char c = *current++;
21,347✔
1321
    if (c == '\r') {
21,347✔
1322
      if (current != end && *current == '\n')
3!
1323
        // convert dos EOL
1324
        ++current;
1✔
1325
      // convert Mac EOL
1326
      normalized += '\n';
1327
    } else {
1328
      normalized += c;
1329
    }
1330
  }
1331
  return normalized;
597✔
1332
}
1333

1334
void OurReader::addComment(Location begin, Location end,
597✔
1335
                           CommentPlacement placement) {
1336
  assert(collectComments_);
597!
1337
  const String& normalized = normalizeEOL(begin, end);
597✔
1338
  if (placement == commentAfterOnSameLine) {
597✔
1339
    assert(lastValue_ != nullptr);
64!
1340
    lastValue_->setComment(normalized, placement);
128✔
1341
  } else {
1342
    commentsBefore_ += normalized;
533✔
1343
  }
1344
}
597✔
1345

1346
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
85✔
1347
  *containsNewLineResult = false;
85✔
1348

1349
  while ((current_ + 1) < end_) {
2,754!
1350
    Char c = getNextChar();
2,669✔
1351
    if (c == '*' && *current_ == '/')
2,669!
1352
      break;
1353
    if (c == '\n')
2,584✔
1354
      *containsNewLineResult = true;
97✔
1355
  }
1356

1357
  return getNextChar() == '/';
85✔
1358
}
1359

1360
bool OurReader::readCppStyleComment() {
530✔
1361
  while (current_ != end_) {
18,083✔
1362
    Char c = getNextChar();
18,077✔
1363
    if (c == '\n')
18,077✔
1364
      break;
1365
    if (c == '\r') {
17,556✔
1366
      // Consume DOS EOL. It will be normalized in addComment.
1367
      if (current_ != end_ && *current_ == '\n')
3!
1368
        getNextChar();
1✔
1369
      // Break on Moc OS 9 EOL.
1370
      break;
1371
    }
1372
  }
1373
  return true;
530✔
1374
}
1375

1376
bool OurReader::readNumber(bool checkInf) {
52,875✔
1377
  Location p = current_;
52,875✔
1378
  if (checkInf && p != end_ && *p == 'I') {
52,875!
1379
    current_ = ++p;
6✔
1380
    return false;
6✔
1381
  }
1382
  char c = '0'; // stopgap for already consumed character
1383
  // integral part
1384
  while (c >= '0' && c <= '9')
235,516✔
1385
    c = (current_ = p) < end_ ? *p++ : '\0';
182,647✔
1386
  // fractional part
1387
  if (c == '.') {
52,869✔
1388
    c = (current_ = p) < end_ ? *p++ : '\0';
113!
1389
    while (c >= '0' && c <= '9')
785✔
1390
      c = (current_ = p) < end_ ? *p++ : '\0';
672✔
1391
  }
1392
  // exponential part
1393
  if (c == 'e' || c == 'E') {
52,869✔
1394
    c = (current_ = p) < end_ ? *p++ : '\0';
103!
1395
    if (c == '+' || c == '-')
103✔
1396
      c = (current_ = p) < end_ ? *p++ : '\0';
76!
1397
    while (c >= '0' && c <= '9')
306✔
1398
      c = (current_ = p) < end_ ? *p++ : '\0';
203✔
1399
  }
1400
  return true;
1401
}
1402
bool OurReader::readString() {
1,174✔
1403
  Char c = 0;
1404
  while (current_ != end_) {
34,521!
1405
    c = getNextChar();
34,521✔
1406
    if (c == '\\')
34,521✔
1407
      getNextChar();
1,275✔
1408
    else if (c == '"')
33,246✔
1409
      break;
1410
  }
1411
  return c == '"';
1,174✔
1412
}
1413

1414
bool OurReader::readStringSingleQuote() {
11✔
1415
  Char c = 0;
1416
  while (current_ != end_) {
26!
1417
    c = getNextChar();
26✔
1418
    if (c == '\\')
26✔
1419
      getNextChar();
2✔
1420
    else if (c == '\'')
24✔
1421
      break;
1422
  }
1423
  return c == '\'';
11✔
1424
}
1425

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

1456
    Token colon;
1457
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
639!
1458
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1459
                                tokenObjectEnd);
1460
    }
1461
    Value& value = currentValue()[name];
632✔
1462
    nodes_.push(&value);
632!
1463
    bool ok = readValue();
632✔
1464
    nodes_.pop();
631✔
1465
    if (!ok) // error already set
631✔
1466
      return recoverFromError(tokenObjectEnd);
22✔
1467

1468
    Token comma;
1469
    if (!readTokenSkippingComments(comma) ||
609✔
1470
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
601✔
1471
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1472
                                comma, tokenObjectEnd);
1473
    }
1474
    if (comma.type_ == tokenObjectEnd)
582✔
1475
      return true;
1476
  }
1477
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1478
                            tokenObjectEnd);
1479
}
367✔
1480

1481
bool OurReader::readArray(Token& token) {
2,151✔
1482
  Value init(arrayValue);
2,151✔
1483
  currentValue().swapPayload(init);
2,151✔
1484
  currentValue().setOffsetStart(token.start_ - begin_);
2,151✔
1485
  int index = 0;
1486
  for (;;) {
1487
    skipSpaces();
54,557✔
1488
    if (current_ != end_ && *current_ == ']' &&
54,557!
1489
        (index == 0 ||
14✔
1490
         (features_.allowTrailingCommas_ &&
14!
1491
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
14✔
1492
                                                      // comma
1493
    {
1494
      Token endArray;
1495
      readToken(endArray);
28✔
1496
      return true;
1497
    }
1498
    Value& value = currentValue()[index++];
54,529✔
1499
    nodes_.push(&value);
54,529✔
1500
    bool ok = readValue();
54,529✔
1501
    nodes_.pop();
52,737✔
1502
    if (!ok) // error already set
52,737✔
1503
      return recoverFromError(tokenArrayEnd);
46✔
1504

1505
    Token currentToken;
1506
    // Accept Comment after last item in the array.
1507
    ok = readTokenSkippingComments(currentToken);
52,691✔
1508
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,691✔
1509
                         currentToken.type_ != tokenArrayEnd);
52,691✔
1510
    if (!ok || badTokenType) {
52,691✔
1511
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
46✔
1512
                                currentToken, tokenArrayEnd);
1513
    }
1514
    if (currentToken.type_ == tokenArrayEnd)
52,668✔
1515
      break;
1516
  }
52,406✔
1517
  return true;
262✔
1518
}
2,151✔
1519

1520
bool OurReader::decodeNumber(Token& token) {
52,834✔
1521
  Value decoded;
52,834✔
1522
  if (!decodeNumber(token, decoded))
52,834✔
1523
    return false;
1524
  currentValue().swapPayload(decoded);
52,825✔
1525
  currentValue().setOffsetStart(token.start_ - begin_);
52,825✔
1526
  currentValue().setOffsetLimit(token.end_ - begin_);
52,825✔
1527
  return true;
1528
}
52,834✔
1529

1530
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,837✔
1531
  // Attempts to parse the number as an integer. If the number is
1532
  // larger than the maximum supported value of an integer then
1533
  // we decode the number as a double.
1534
  Location current = token.start_;
52,837✔
1535
  const bool isNegative = *current == '-';
52,837✔
1536
  if (isNegative) {
52,837✔
1537
    ++current;
141✔
1538
  }
1539

1540
  // We assume we can represent the largest and smallest integer types as
1541
  // unsigned integers with separate sign. This is only true if they can fit
1542
  // into an unsigned integer.
1543
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1544
                "Int must be smaller than UInt");
1545

1546
  // We need to convert minLargestInt into a positive number. The easiest way
1547
  // to do this conversion is to assume our "threshold" value of minLargestInt
1548
  // divided by 10 can fit in maxLargestInt when absolute valued. This should
1549
  // be a safe assumption.
1550
  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1551
                "The absolute value of minLargestInt must be greater than or "
1552
                "equal to maxLargestInt");
1553
  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1554
                "The absolute value of minLargestInt must be only 1 magnitude "
1555
                "larger than maxLargest Int");
1556

1557
  static constexpr Value::LargestUInt positive_threshold =
1558
      Value::maxLargestUInt / 10;
1559
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1560

1561
  // For the negative values, we have to be more careful. Since typically
1562
  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1563
  // then take the inverse. This assumes that minLargestInt is only a single
1564
  // power of 10 different in magnitude, which we check above. For the last
1565
  // digit, we take the modulus before negating for the same reason.
1566
  static constexpr auto negative_threshold =
1567
      Value::LargestUInt(-(Value::minLargestInt / 10));
1568
  static constexpr auto negative_last_digit =
1569
      Value::UInt(-(Value::minLargestInt % 10));
1570

1571
  const Value::LargestUInt threshold =
1572
      isNegative ? negative_threshold : positive_threshold;
1573
  const Value::UInt max_last_digit =
1574
      isNegative ? negative_last_digit : positive_last_digit;
1575

1576
  Value::LargestUInt value = 0;
1577
  while (current < token.end_) {
235,282✔
1578
    Char c = *current++;
182,641✔
1579
    if (c < '0' || c > '9')
182,641✔
1580
      return decodeDouble(token, decoded);
177✔
1581

1582
    const auto digit(static_cast<Value::UInt>(c - '0'));
182,464✔
1583
    if (value >= threshold) {
182,464✔
1584
      // We've hit or exceeded the max value divided by 10 (rounded down). If
1585
      // a) we've only just touched the limit, meaning value == threshold,
1586
      // b) this is the last digit, or
1587
      // c) it's small enough to fit in that rounding delta, we're okay.
1588
      // Otherwise treat this number as a double to avoid overflow.
1589
      if (value > threshold || current != token.end_ ||
43!
1590
          digit > max_last_digit) {
1591
        return decodeDouble(token, decoded);
19✔
1592
      }
1593
    }
1594
    value = value * 10 + digit;
182,445✔
1595
  }
1596

1597
  if (isNegative) {
52,641✔
1598
    // We use the same magnitude assumption here, just in case.
1599
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1600
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1601
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,552✔
1602
    decoded = Value::LargestInt(value);
52,528✔
1603
  } else {
1604
    decoded = value;
24✔
1605
  }
1606

1607
  return true;
1608
}
1609

1610
bool OurReader::decodeDouble(Token& token) {
×
UNCOV
1611
  Value decoded;
×
1612
  if (!decodeDouble(token, decoded))
×
1613
    return false;
1614
  currentValue().swapPayload(decoded);
×
UNCOV
1615
  currentValue().setOffsetStart(token.start_ - begin_);
×
1616
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1617
  return true;
UNCOV
1618
}
×
1619

1620
bool OurReader::decodeDouble(Token& token, Value& decoded) {
196✔
1621
  double value = 0;
196✔
1622
  IStringStream is(String(token.start_, token.end_));
196✔
1623
  is.imbue(std::locale::classic());
196✔
1624
  if (!(is >> value)) {
196✔
1625
    if (value == std::numeric_limits<double>::max())
33✔
1626
      value = std::numeric_limits<double>::infinity();
12✔
1627
    else if (value == std::numeric_limits<double>::lowest())
21✔
1628
      value = -std::numeric_limits<double>::infinity();
12✔
1629
    else if (!std::isinf(value))
9!
1630
      return addError(
9✔
1631
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
18✔
1632
  }
1633
  decoded = value;
187✔
1634
  return true;
187✔
1635
}
196✔
1636

1637
bool OurReader::decodeString(Token& token) {
519✔
1638
  String decoded_string;
1639
  if (!decodeString(token, decoded_string))
519✔
1640
    return false;
1641
  Value decoded(decoded_string);
496✔
1642
  currentValue().swapPayload(decoded);
496✔
1643
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1644
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1645
  return true;
1646
}
496✔
1647

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

1703
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1704
                                       Location end, unsigned int& unicode) {
1705

1706
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
145✔
1707
    return false;
1708
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
143✔
1709
    // surrogate pairs
1710
    if (end - current < 6)
15✔
1711
      return addError(
2✔
1712
          "additional six characters expected to parse unicode surrogate pair.",
1713
          token, current);
1714
    if (*(current++) == '\\' && *(current++) == 'u') {
14!
1715
      unsigned int surrogatePair;
1716
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
13!
1717
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
13✔
1718
      } else
UNCOV
1719
        return false;
×
1720
    } else
1721
      return addError("expecting another \\u token to begin the second half of "
2✔
1722
                      "a unicode surrogate pair",
1723
                      token, current);
1724
  }
1725
  return true;
1726
}
1727

1728
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
158✔
1729
                                            Location end,
1730
                                            unsigned int& ret_unicode) {
1731
  if (end - current < 4)
158✔
1732
    return addError(
2✔
1733
        "Bad unicode escape sequence in string: four digits expected.", token,
1734
        current);
1735
  int unicode = 0;
1736
  for (int index = 0; index < 4; ++index) {
783✔
1737
    Char c = *current++;
627✔
1738
    unicode *= 16;
627✔
1739
    if (c >= '0' && c <= '9')
627✔
1740
      unicode += c - '0';
400✔
1741
    else if (c >= 'a' && c <= 'f')
227✔
1742
      unicode += c - 'a' + 10;
115✔
1743
    else if (c >= 'A' && c <= 'F')
112✔
1744
      unicode += c - 'A' + 10;
111✔
1745
    else
1746
      return addError(
2✔
1747
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1748
          token, current);
1749
  }
1750
  ret_unicode = static_cast<unsigned int>(unicode);
156✔
1751
  return true;
156✔
1752
}
1753

1754
bool OurReader::addError(const String& message, Token& token, Location extra) {
162✔
1755
  ErrorInfo info;
1756
  info.token_ = token;
162✔
1757
  info.message_ = message;
1758
  info.extra_ = extra;
162✔
1759
  errors_.push_back(info);
162✔
1760
  return false;
162✔
1761
}
1762

1763
bool OurReader::recoverFromError(TokenType skipUntilToken) {
152✔
1764
  size_t errorCount = errors_.size();
1765
  Token skip;
1766
  for (;;) {
1767
    if (!readToken(skip))
410✔
1768
      errors_.resize(errorCount); // discard errors caused by recovery
173✔
1769
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
410✔
1770
      break;
1771
  }
1772
  errors_.resize(errorCount);
152✔
1773
  return false;
152✔
1774
}
1775

1776
bool OurReader::addErrorAndRecover(const String& message, Token& token,
84✔
1777
                                   TokenType skipUntilToken) {
1778
  addError(message, token);
84✔
1779
  return recoverFromError(skipUntilToken);
84✔
1780
}
1781

1782
Value& OurReader::currentValue() { return *(nodes_.top()); }
275,692✔
1783

1784
OurReader::Char OurReader::getNextChar() {
169,940✔
1785
  if (current_ == end_)
169,940✔
1786
    return 0;
1787
  return *current_++;
168,996✔
1788
}
1789

1790
void OurReader::getLocationLineAndColumn(Location location, int& line,
184✔
1791
                                         int& column) const {
1792
  Location current = begin_;
184✔
1793
  Location lastLineStart = current;
1794
  line = 0;
184✔
1795
  while (current < location && current != end_) {
2,271!
1796
    Char c = *current++;
2,087✔
1797
    if (c == '\r') {
2,087✔
1798
      if (current != end_ && *current == '\n')
1!
UNCOV
1799
        ++current;
×
1800
      lastLineStart = current;
1801
      ++line;
1✔
1802
    } else if (c == '\n') {
2,086✔
1803
      lastLineStart = current;
1804
      ++line;
28✔
1805
    }
1806
  }
1807
  // column & line start at 1
1808
  column = int(location - lastLineStart) + 1;
184✔
1809
  ++line;
184✔
1810
}
184✔
1811

1812
String OurReader::getLocationLineAndColumn(Location location) const {
184✔
1813
  int line, column;
1814
  getLocationLineAndColumn(location, line, column);
184✔
1815
  char buffer[18 + 16 + 16 + 1];
1816
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
184✔
1817
  return buffer;
184✔
1818
}
1819

1820
String OurReader::getFormattedErrorMessages() const {
923✔
1821
  String formattedMessage;
1822
  for (const auto& error : errors_) {
1,084✔
1823
    formattedMessage +=
1824
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
161✔
1825
    formattedMessage += "  " + error.message_ + "\n";
161✔
1826
    if (error.extra_)
161✔
1827
      formattedMessage +=
1828
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
46✔
1829
  }
1830
  return formattedMessage;
923✔
1831
}
1832

1833
std::vector<CharReader::StructuredError>
1834
OurReader::getStructuredErrors() const {
2✔
1835
  std::vector<CharReader::StructuredError> allErrors;
1836
  for (const auto& error : errors_) {
3✔
1837
    CharReader::StructuredError structured;
1838
    structured.offset_start = error.token_.start_ - begin_;
1✔
1839
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1840
    structured.message = error.message_;
1✔
1841
    allErrors.push_back(structured);
1✔
1842
  }
1843
  return allErrors;
2✔
UNCOV
1844
}
×
1845

1846
class OurCharReader : public CharReader {
1847

1848
public:
1849
  OurCharReader(bool collectComments, OurFeatures const& features)
901✔
1850
      : CharReader(
901✔
1851
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,802✔
1852

1853
protected:
1854
  class OurImpl : public Impl {
1855
  public:
1856
    OurImpl(bool collectComments, OurFeatures const& features)
1857
        : collectComments_(collectComments), reader_(features) {}
901✔
1858

1859
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
933✔
1860
               String* errs) override {
1861
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
933✔
1862
      if (errs) {
925✔
1863
        *errs = reader_.getFormattedErrorMessages();
1,846✔
1864
      }
1865
      return ok;
925✔
1866
    }
1867

1868
    std::vector<CharReader::StructuredError>
1869
    getStructuredErrors() const override {
2✔
1870
      return reader_.getStructuredErrors();
2✔
1871
    }
1872

1873
  private:
1874
    bool const collectComments_;
1875
    OurReader reader_;
1876
  };
1877
};
1878

1879
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
898✔
1880
CharReaderBuilder::~CharReaderBuilder() = default;
898✔
1881
CharReader* CharReaderBuilder::newCharReader() const {
901✔
1882
  bool collectComments = settings_["collectComments"].asBool();
901✔
1883
  OurFeatures features = OurFeatures::all();
901✔
1884
  features.allowComments_ = settings_["allowComments"].asBool();
901✔
1885
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
901✔
1886
  features.strictRoot_ = settings_["strictRoot"].asBool();
901✔
1887
  features.allowDroppedNullPlaceholders_ =
901✔
1888
      settings_["allowDroppedNullPlaceholders"].asBool();
901✔
1889
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
901✔
1890
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
901✔
1891

1892
  // Stack limit is always a size_t, so we get this as an unsigned int
1893
  // regardless of it we have 64-bit integer support enabled.
1894
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
901✔
1895
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
901✔
1896
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
901✔
1897
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
901✔
1898
  features.skipBom_ = settings_["skipBom"].asBool();
901✔
1899
  return new OurCharReader(collectComments, features);
901✔
1900
}
1901

1902
bool CharReaderBuilder::validate(Json::Value* invalid) const {
2✔
1903
  static const auto& valid_keys = *new std::set<String>{
1904
      "collectComments",
1905
      "allowComments",
1906
      "allowTrailingCommas",
1907
      "strictRoot",
1908
      "allowDroppedNullPlaceholders",
1909
      "allowNumericKeys",
1910
      "allowSingleQuotes",
1911
      "stackLimit",
1912
      "failIfExtra",
1913
      "rejectDupKeys",
1914
      "allowSpecialFloats",
1915
      "skipBom",
1916
  };
15!
1917
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
54✔
1918
    auto key = si.name();
25✔
1919
    if (valid_keys.count(key))
25✔
1920
      continue;
1921
    if (invalid)
1!
1922
      (*invalid)[key] = *si;
1✔
1923
    else
1924
      return false;
1925
  }
1926
  return invalid ? invalid->empty() : true;
2!
1927
}
2!
1928

1929
Value& CharReaderBuilder::operator[](const String& key) {
1✔
1930
  return settings_[key];
1✔
1931
}
1932
// static
1933
void CharReaderBuilder::strictMode(Json::Value* settings) {
3✔
1934
  //! [CharReaderBuilderStrictMode]
1935
  (*settings)["allowComments"] = false;
3✔
1936
  (*settings)["allowTrailingCommas"] = false;
3✔
1937
  (*settings)["strictRoot"] = true;
3✔
1938
  (*settings)["allowDroppedNullPlaceholders"] = false;
3✔
1939
  (*settings)["allowNumericKeys"] = false;
3✔
1940
  (*settings)["allowSingleQuotes"] = false;
3✔
1941
  (*settings)["stackLimit"] = 256;
3✔
1942
  (*settings)["failIfExtra"] = true;
3✔
1943
  (*settings)["rejectDupKeys"] = true;
3✔
1944
  (*settings)["allowSpecialFloats"] = false;
3✔
1945
  (*settings)["skipBom"] = true;
3✔
1946
  //! [CharReaderBuilderStrictMode]
1947
}
3✔
1948
// static
1949
void CharReaderBuilder::setDefaults(Json::Value* settings) {
898✔
1950
  //! [CharReaderBuilderDefaults]
1951
  (*settings)["collectComments"] = true;
898✔
1952
  (*settings)["allowComments"] = true;
898✔
1953
  (*settings)["allowTrailingCommas"] = true;
898✔
1954
  (*settings)["strictRoot"] = false;
898✔
1955
  (*settings)["allowDroppedNullPlaceholders"] = false;
898✔
1956
  (*settings)["allowNumericKeys"] = false;
898✔
1957
  (*settings)["allowSingleQuotes"] = false;
898✔
1958
  (*settings)["stackLimit"] = 256;
898✔
1959
  (*settings)["failIfExtra"] = false;
898✔
1960
  (*settings)["rejectDupKeys"] = false;
898✔
1961
  (*settings)["allowSpecialFloats"] = false;
898✔
1962
  (*settings)["skipBom"] = true;
898✔
1963
  //! [CharReaderBuilderDefaults]
1964
}
898✔
1965
// static
1966
void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
×
1967
  //! [CharReaderBuilderECMA404Mode]
1968
  (*settings)["allowComments"] = false;
×
1969
  (*settings)["allowTrailingCommas"] = false;
×
1970
  (*settings)["strictRoot"] = false;
×
1971
  (*settings)["allowDroppedNullPlaceholders"] = false;
×
1972
  (*settings)["allowNumericKeys"] = false;
×
1973
  (*settings)["allowSingleQuotes"] = false;
×
1974
  (*settings)["stackLimit"] = 256;
×
UNCOV
1975
  (*settings)["failIfExtra"] = true;
×
1976
  (*settings)["rejectDupKeys"] = false;
×
UNCOV
1977
  (*settings)["allowSpecialFloats"] = false;
×
UNCOV
1978
  (*settings)["skipBom"] = false;
×
1979
  //! [CharReaderBuilderECMA404Mode]
UNCOV
1980
}
×
1981

1982
std::vector<CharReader::StructuredError>
1983
CharReader::getStructuredErrors() const {
2✔
1984
  return _impl->getStructuredErrors();
2✔
1985
}
1986

1987
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
933✔
1988
                       String* errs) {
1989
  return _impl->parse(beginDoc, endDoc, root, errs);
933✔
1990
}
1991

1992
//////////////////////////////////
1993
// global functions
1994

1995
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
1996
                     String* errs) {
1997
  OStringStream ssin;
4✔
1998
  ssin << sin.rdbuf();
4✔
1999
  String doc = std::move(ssin).str();
2000
  char const* begin = doc.data();
2001
  char const* end = begin + doc.size();
4✔
2002
  // Note that we do not actually need a null-terminator.
2003
  CharReaderPtr const reader(fact.newCharReader());
4✔
2004
  return reader->parse(begin, end, root, errs);
8✔
2005
}
4✔
2006

2007
IStream& operator>>(IStream& sin, Value& root) {
1✔
2008
  CharReaderBuilder b;
1✔
2009
  String errs;
2010
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
2011
  if (!ok) {
1!
UNCOV
2012
    throwRuntimeError(errs);
×
2013
  }
2014
  return sin;
1✔
2015
}
1✔
2016

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