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

open-source-parsers / jsoncpp / 19290553842

12 Nov 2025 08:05AM UTC coverage: 65.346%. Remained the same
19290553842

push

github

web-flow
Merge 39b994c4b into 4bcbc6ac0

6336 of 12448 branches covered (50.9%)

Branch coverage included in aggregate %.

5838 of 6182 relevant lines covered (94.44%)

10221.46 hits per line

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

77.51
/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 1000
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;
606✔
56

57
Features Features::all() { return {}; }
15✔
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()) {}
30!
79

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

82
bool Reader::parse(const std::string& document, Value& root,
25✔
83
                   bool collectComments) {
84
  document_.assign(document.begin(), document.end());
25✔
85
  const char* begin = document_.c_str();
86
  const char* end = begin + document_.length();
25✔
87
  return parse(begin, end, root, collectComments);
25✔
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,
710✔
103
                   bool collectComments) {
104
  if (!features_.allowComments_) {
710✔
105
    collectComments = false;
106
  }
107

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

120
  bool successful = readValue();
710✔
121
  Token token;
122
  readTokenSkippingComments(token);
710✔
123
  if (collectComments_ && !commentsBefore_.empty())
710✔
124
    root.setComment(commentsBefore_, commentAfter);
26!
125
  if (features_.strictRoot_) {
710✔
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,560!
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,560!
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,560✔
156
  bool successful = true;
157

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

163
  switch (token.type_) {
53,560!
164
  case tokenObjectBegin:
244✔
165
    successful = readObject(token);
244✔
166
    currentValue().setOffsetLimit(current_ - begin_);
244✔
167
    break;
168
  case tokenArrayBegin:
189✔
169
    successful = readArray(token);
189✔
170
    currentValue().setOffsetLimit(current_ - begin_);
189✔
171
    break;
172
  case tokenNumber:
52,648✔
173
    successful = decodeNumber(token);
52,648✔
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:
×
197
  case tokenObjectEnd:
198
  case tokenArrayEnd:
199
    if (features_.allowDroppedNullPlaceholders_) {
×
200
      // "Un-read" the current token and mark the current value as a null
201
      // token.
202
      current_--;
×
203
      Value v;
×
204
      currentValue().swapPayload(v);
×
205
      currentValue().setOffsetStart(current_ - begin_ - 1);
×
206
      currentValue().setOffsetLimit(current_ - begin_);
×
207
      break;
208
    } // Else, fall through...
×
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,555✔
216
    lastValueEnd_ = current_;
53,554✔
217
    lastValue_ = &currentValue();
53,554✔
218
  }
219

220
  return successful;
221
}
222

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

233
bool Reader::readToken(Token& token) {
108,563✔
234
  skipSpaces();
108,563✔
235
  token.start_ = current_;
108,563✔
236
  Char c = getNextChar();
108,563✔
237
  bool ok = true;
238
  switch (c) {
108,563✔
239
  case '{':
244✔
240
    token.type_ = tokenObjectBegin;
244✔
241
    break;
242
  case '}':
242✔
243
    token.type_ = tokenObjectEnd;
242✔
244
    break;
245
  case '[':
189✔
246
    token.type_ = tokenArrayBegin;
189✔
247
    break;
248
  case ']':
188✔
249
    token.type_ = tokenArrayEnd;
188✔
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,649✔
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,649✔
271
    readNumber();
52,649✔
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,443✔
286
    token.type_ = tokenArraySeparator;
52,443✔
287
    break;
288
  case ':':
417✔
289
    token.type_ = tokenMemberSeparator;
417✔
290
    break;
291
  case 0:
717✔
292
    token.type_ = tokenEndOfStream;
717✔
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,563✔
301
  return ok;
108,563✔
302
}
303

304
void Reader::skipSpaces() {
108,752✔
305
  while (current_ != end_) {
226,612✔
306
    Char c = *current_;
225,895✔
307
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
225,895✔
308
      ++current_;
117,860✔
309
    else
310
      break;
311
  }
312
}
108,752✔
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,649✔
405
  Location p = current_;
52,649✔
406
  char c = '0'; // stopgap for already consumed character
407
  // integral part
408
  while (c >= '0' && c <= '9')
234,907✔
409
    c = (current_ = p) < end_ ? *p++ : '\0';
182,258✔
410
  // fractional part
411
  if (c == '.') {
52,649✔
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,649✔
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,649✔
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) {
189✔
486
  Value init(arrayValue);
189✔
487
  currentValue().swapPayload(init);
189!
488
  currentValue().setOffsetStart(token.start_ - begin_);
189!
489
  skipSpaces();
189!
490
  if (current_ != end_ && *current_ == ']') // empty array
189!
491
  {
492
    Token endArray;
493
    readToken(endArray);
12!
494
    return true;
495
  }
496
  int index = 0;
497
  for (;;) {
498
    Value& value = currentValue()[index++];
52,435!
499
    nodes_.push(&value);
52,435!
500
    bool ok = readValue();
52,435!
501
    nodes_.pop();
52,435✔
502
    if (!ok) // error already set
52,435✔
503
      return recoverFromError(tokenArrayEnd);
6!
504

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

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

530
bool Reader::decodeNumber(Token& token, Value& decoded) {
52,649✔
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,649✔
535
  bool isNegative = *current == '-';
52,649✔
536
  if (isNegative)
52,649✔
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,649✔
544
  Value::LargestUInt value = 0;
545
  while (current < token.end_) {
234,756✔
546
    Char c = *current++;
182,252✔
547
    if (c < '0' || c > '9')
182,252✔
548
      return decodeDouble(token, decoded);
127✔
549
    auto digit(static_cast<Value::UInt>(c - '0'));
182,125✔
550
    if (value >= threshold) {
182,125✔
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,107✔
561
  }
562
  if (isNegative && value == maxIntegerValue)
52,504✔
563
    decoded = Value::minLargestInt;
12✔
564
  else if (isNegative)
52,492✔
565
    decoded = -Value::LargestInt(value);
72✔
566
  else if (value <= Value::LargestUInt(Value::maxInt))
52,420✔
567
    decoded = Value::LargestInt(value);
52,360✔
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✔
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!
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
      decoded += c;
658
    }
659
  }
660
  return true;
661
}
662

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

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

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

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

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

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

742
Value& Reader::currentValue() { return *(nodes_.top()); }
267,461✔
743

744
Reader::Char Reader::getNextChar() {
160,543✔
745
  if (current_ == end_)
160,543✔
746
    return 0;
747
  return *current_++;
159,826✔
748
}
749

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

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

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

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

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

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

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

844
bool Reader::good() const { return errors_.empty(); }
×
845

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

864
OurFeatures OurFeatures::all() { return {}; }
894✔
865

866
// Implementation of class Reader
867
// ////////////////////////////////
868

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

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

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

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

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

913
  class ErrorInfo {
156✔
914
  public:
915
    Token token_;
916
    String message_;
917
    Location extra_;
918
  };
919

920
  using Errors = std::deque<ErrorInfo>;
921

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

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

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

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

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

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

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

985
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,788✔
986

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

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

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

1033
bool OurReader::readValue() {
60,295✔
1034
  //  To preserve the old behaviour we cast size_t to int.
1035
  if (nodes_.size() > features_.stackLimit_)
60,295✔
1036
    throwRuntimeError("Exceeded stackLimit in readValue().");
7✔
1037
  Token token;
1038
  readTokenSkippingComments(token);
60,288✔
1039
  bool successful = true;
1040

1041
  if (collectComments_ && !commentsBefore_.empty()) {
60,288✔
1042
    currentValue().setComment(commentsBefore_, commentBefore);
1,203!
1043
    commentsBefore_.clear();
1044
  }
1045

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

1116
  if (collectComments_) {
54,250✔
1117
    lastValueEnd_ = current_;
53,789✔
1118
    lastValueHasAComment_ = false;
53,789✔
1119
    lastValue_ = &currentValue();
53,789✔
1120
  }
1121

1122
  return successful;
1123
}
1124

1125
bool OurReader::readTokenSkippingComments(Token& token) {
115,198✔
1126
  bool success = readToken(token);
115,198✔
1127
  if (features_.allowComments_) {
115,198✔
1128
    while (success && token.type_ == tokenComment) {
114,698✔
1129
      success = readToken(token);
590✔
1130
    }
1131
  }
1132
  return success;
115,198✔
1133
}
1134

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

1246
void OurReader::skipSpaces() {
175,630✔
1247
  while (current_ != end_) {
295,356✔
1248
    Char c = *current_;
294,418✔
1249
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
294,418✔
1250
      ++current_;
119,726✔
1251
    else
1252
      break;
1253
  }
1254
}
175,630✔
1255

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

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

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

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

1291
  if (!successful)
615!
1292
    return false;
×
1293

1294
  if (collectComments_) {
615✔
1295
    CommentPlacement placement = commentBefore;
1296

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

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

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

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

1343
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
85✔
1344
  *containsNewLineResult = false;
85✔
1345

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

1354
  return getNextChar() == '/';
85✔
1355
}
1356

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1604
  return true;
1605
}
1606

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

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

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

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

1697
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1698
                                       Location end, unsigned int& unicode) {
1699

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

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

1748
bool OurReader::addError(const String& message, Token& token, Location extra) {
156!
1749
  ErrorInfo info;
1750
  info.token_ = token;
156!
1751
  info.message_ = message;
1752
  info.extra_ = extra;
156✔
1753
  errors_.push_back(info);
156!
1754
  return false;
156✔
1755
}
1756

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

1770
bool OurReader::addErrorAndRecover(const String& message, Token& token,
84✔
1771
                                   TokenType skipUntilToken) {
1772
  addError(message, token);
84✔
1773
  return recoverFromError(skipUntilToken);
84✔
1774
}
1775

1776
Value& OurReader::currentValue() { return *(nodes_.top()); }
288,310✔
1777

1778
OurReader::Char OurReader::getNextChar() {
174,124✔
1779
  if (current_ == end_)
174,124✔
1780
    return 0;
1781
  return *current_++;
173,186✔
1782
}
1783

1784
void OurReader::getLocationLineAndColumn(Location location, int& line,
172✔
1785
                                         int& column) const {
1786
  Location current = begin_;
172✔
1787
  Location lastLineStart = current;
1788
  line = 0;
172✔
1789
  while (current < location && current != end_) {
2,253!
1790
    Char c = *current++;
2,081✔
1791
    if (c == '\r') {
2,081✔
1792
      if (current != end_ && *current == '\n')
1!
1793
        ++current;
×
1794
      lastLineStart = current;
1795
      ++line;
1✔
1796
    } else if (c == '\n') {
2,080✔
1797
      lastLineStart = current;
1798
      ++line;
28✔
1799
    }
1800
  }
1801
  // column & line start at 1
1802
  column = int(location - lastLineStart) + 1;
172✔
1803
  ++line;
172✔
1804
}
172✔
1805

1806
String OurReader::getLocationLineAndColumn(Location location) const {
172✔
1807
  int line, column;
1808
  getLocationLineAndColumn(location, line, column);
172✔
1809
  char buffer[18 + 16 + 16 + 1];
1810
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
172✔
1811
  return buffer;
172✔
1812
}
1813

1814
String OurReader::getFormattedErrorMessages() const {
917✔
1815
  String formattedMessage;
1816
  for (const auto& error : errors_) {
1,072✔
1817
    formattedMessage +=
1818
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
155!
1819
    formattedMessage += "  " + error.message_ + "\n";
155!
1820
    if (error.extra_)
155✔
1821
      formattedMessage +=
1822
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
34!
1823
  }
1824
  return formattedMessage;
917✔
1825
}
1826

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

1840
class OurCharReader : public CharReader {
1841

1842
public:
1843
  OurCharReader(bool collectComments, OurFeatures const& features)
894✔
1844
      : CharReader(
894✔
1845
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,788✔
1846

1847
protected:
1848
  class OurImpl : public Impl {
1849
  public:
1850
    OurImpl(bool collectComments, OurFeatures const& features)
1851
        : collectComments_(collectComments), reader_(features) {}
894!
1852

1853
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
926✔
1854
               String* errs) override {
1855
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
926✔
1856
      if (errs) {
919✔
1857
        *errs = reader_.getFormattedErrorMessages();
1,834✔
1858
      }
1859
      return ok;
919✔
1860
    }
1861

1862
    std::vector<CharReader::StructuredError>
1863
    getStructuredErrors() const override {
2✔
1864
      return reader_.getStructuredErrors();
2✔
1865
    }
1866

1867
  private:
1868
    bool const collectComments_;
1869
    OurReader reader_;
1870
  };
1871
};
1872

1873
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
891!
1874
CharReaderBuilder::~CharReaderBuilder() = default;
891✔
1875
CharReader* CharReaderBuilder::newCharReader() const {
894✔
1876
  bool collectComments = settings_["collectComments"].asBool();
894✔
1877
  OurFeatures features = OurFeatures::all();
894✔
1878
  features.allowComments_ = settings_["allowComments"].asBool();
894✔
1879
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
894✔
1880
  features.strictRoot_ = settings_["strictRoot"].asBool();
894✔
1881
  features.allowDroppedNullPlaceholders_ =
894✔
1882
      settings_["allowDroppedNullPlaceholders"].asBool();
894✔
1883
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
894✔
1884
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
894✔
1885

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

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

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

1976
std::vector<CharReader::StructuredError>
1977
CharReader::getStructuredErrors() const {
2✔
1978
  return _impl->getStructuredErrors();
2✔
1979
}
1980

1981
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
926✔
1982
                       String* errs) {
1983
  return _impl->parse(beginDoc, endDoc, root, errs);
926✔
1984
}
1985

1986
//////////////////////////////////
1987
// global functions
1988

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

2001
IStream& operator>>(IStream& sin, Value& root) {
1✔
2002
  CharReaderBuilder b;
1✔
2003
  String errs;
2004
  bool ok = parseFromStream(b, sin, &root, &errs);
1!
2005
  if (!ok) {
1!
2006
    throwRuntimeError(errs);
×
2007
  }
2008
  return sin;
1✔
2009
}
1✔
2010

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

© 2025 Coveralls, Inc