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

open-source-parsers / jsoncpp / 23129683363

16 Mar 2026 05:42AM UTC coverage: 90.082% (+0.1%) from 89.937%
23129683363

Pull #1662

github

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

2179 of 2585 branches covered (84.29%)

Branch coverage included in aggregate %.

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

64 existing lines in 2 files now uncovered.

2571 of 2688 relevant lines covered (95.65%)

23765.83 hits per line

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

89.99
/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_));
290✔
586
  if (!(is >> value)) {
145✔
587
    if (value == std::numeric_limits<double>::max())
24✔
588
      value = std::numeric_limits<double>::infinity();
12✔
589
    else if (value == std::numeric_limits<double>::lowest())
12!
590
      value = -std::numeric_limits<double>::infinity();
12✔
UNCOV
591
    else if (!std::isinf(value))
×
592
      return addError(
×
593
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
594
  }
595
  decoded = value;
145✔
596
  return true;
145✔
597
}
145✔
598

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

868
// Implementation of class Reader
869
// ////////////////////////////////
870

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1124
  return successful;
1125
}
1126

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1606
  return true;
1607
}
1608

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1844
class OurCharReader : public CharReader {
1845

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

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

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

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

1871
  private:
1872
    bool const collectComments_;
1873
    OurReader reader_;
1874
  };
1875
};
1876

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

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

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

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

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

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

1990
//////////////////////////////////
1991
// global functions
1992

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

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

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