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

open-source-parsers / jsoncpp / 13823356124

12 Mar 2025 11:14PM CUT coverage: 95.292%. Remained the same
13823356124

Pull #1599

github

baylesj
remove the copts, that's unusual
Pull Request #1599: Add jsoncpp_test bazel configuration

5364 of 5629 relevant lines covered (95.29%)

11794.59 hits per line

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

93.78
/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 <limits>
20
#include <memory>
21
#include <set>
22
#include <sstream>
23
#include <utility>
24

25
#include <cstdio>
26

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

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

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

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

47
namespace Json {
48

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

51
// Implementation of class Features
52
// ////////////////////////////////
53

54
Features::Features() = default;
606✔
55

56
Features Features::all() { return {}; }
15✔
57

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

67
// Implementation of class Reader
68
// ////////////////////////////////
69

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

74
// Class Reader
75
// //////////////////////////////////////////////////////////////////
76

77
Reader::Reader() : features_(Features::all()) {}
30✔
78

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

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

89
bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
1✔
90
  // std::istream_iterator<char> begin(is);
91
  // std::istream_iterator<char> end;
92
  // Those would allow streamed input from a file, if parse() were a
93
  // template function.
94

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

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

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

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

140
bool Reader::readValue() {
53,560✔
141
  // readValue() may call itself only if it calls readObject() or ReadArray().
142
  // These methods execute nodes_.push() just before and nodes_.pop)() just
143
  // after calling readValue(). parse() executes one nodes_.push(), so > instead
144
  // of >=.
145
  if (nodes_.size() > stackLimit_g)
53,560✔
146
    throwRuntimeError("Exceeded stackLimit in readValue().");
×
147

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

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

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

209
  if (collectComments_) {
53,555✔
210
    lastValueEnd_ = current_;
53,554✔
211
    lastValue_ = &currentValue();
53,554✔
212
  }
213

214
  return successful;
215
}
216

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

657
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
658
                                    Location end, unsigned int& unicode) {
659

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

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

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

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

730
bool Reader::addErrorAndRecover(const String& message, Token& token,
5✔
731
                                TokenType skipUntilToken) {
732
  addError(message, token);
5✔
733
  return recoverFromError(skipUntilToken);
5✔
734
}
735

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

738
Reader::Char Reader::getNextChar() {
160,543✔
739
  if (current_ == end_)
160,543✔
740
    return 0;
741
  return *current_++;
159,826✔
742
}
743

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

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

774
// Deprecated. Preserved for backward compatibility
775
String Reader::getFormatedErrorMessages() const {
×
776
  return getFormattedErrorMessages();
×
777
}
778

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

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

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

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

838
bool Reader::good() const { return errors_.empty(); }
×
839

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

858
OurFeatures OurFeatures::all() { return {}; }
894✔
859

860
// Implementation of class Reader
861
// ////////////////////////////////
862

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

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

876
private:
877
  OurReader(OurReader const&);      // no impl
878
  void operator=(OurReader const&); // no impl
879

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

900
  class Token {
901
  public:
902
    TokenType type_;
903
    Location start_;
904
    Location end_;
905
  };
906

907
  class ErrorInfo {
156✔
908
  public:
909
    Token token_;
910
    String message_;
911
    Location extra_;
912
  };
913

914
  using Errors = std::deque<ErrorInfo>;
915

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

952
  static String normalizeEOL(Location begin, Location end);
953
  static bool containsNewLine(Location begin, Location end);
954

955
  using Nodes = std::stack<Value*>;
956

957
  Nodes nodes_{};
958
  Errors errors_{};
959
  String document_{};
960
  Location begin_ = nullptr;
961
  Location end_ = nullptr;
962
  Location current_ = nullptr;
963
  Location lastValueEnd_ = nullptr;
964
  Value* lastValue_ = nullptr;
965
  bool lastValueHasAComment_ = false;
966
  String commentsBefore_{};
967

968
  OurFeatures const features_;
969
  bool collectComments_ = false;
970
}; // OurReader
971

972
// complete copy of Read impl, for OurReader
973

974
bool OurReader::containsNewLine(OurReader::Location begin,
185✔
975
                                OurReader::Location end) {
976
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
225✔
977
}
978

979
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,788✔
980

981
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
926✔
982
                      bool collectComments) {
983
  if (!features_.allowComments_) {
926✔
984
    collectComments = false;
985
  }
986

987
  begin_ = beginDoc;
926✔
988
  end_ = endDoc;
926✔
989
  collectComments_ = collectComments;
926✔
990
  current_ = begin_;
926✔
991
  lastValueEnd_ = nullptr;
926✔
992
  lastValue_ = nullptr;
926✔
993
  commentsBefore_.clear();
994
  errors_.clear();
926✔
995
  while (!nodes_.empty())
926✔
996
    nodes_.pop();
×
997
  nodes_.push(&root);
926✔
998

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

1027
bool OurReader::readValue() {
60,295✔
1028
  //  To preserve the old behaviour we cast size_t to int.
1029
  if (nodes_.size() > features_.stackLimit_)
60,295✔
1030
    throwRuntimeError("Exceeded stackLimit in readValue().");
7✔
1031
  Token token;
1032
  readTokenSkippingComments(token);
60,288✔
1033
  bool successful = true;
1034

1035
  if (collectComments_ && !commentsBefore_.empty()) {
60,288✔
1036
    currentValue().setComment(commentsBefore_, commentBefore);
1,203✔
1037
    commentsBefore_.clear();
1038
  }
1039

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

1110
  if (collectComments_) {
54,250✔
1111
    lastValueEnd_ = current_;
53,789✔
1112
    lastValueHasAComment_ = false;
53,789✔
1113
    lastValue_ = &currentValue();
53,789✔
1114
  }
1115

1116
  return successful;
1117
}
1118

1119
bool OurReader::readTokenSkippingComments(Token& token) {
115,198✔
1120
  bool success = readToken(token);
115,198✔
1121
  if (features_.allowComments_) {
115,198✔
1122
    while (success && token.type_ == tokenComment) {
114,698✔
1123
      success = readToken(token);
590✔
1124
    }
1125
  }
1126
  return success;
115,198✔
1127
}
1128

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

1240
void OurReader::skipSpaces() {
175,630✔
1241
  while (current_ != end_) {
295,356✔
1242
    Char c = *current_;
294,418✔
1243
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
294,418✔
1244
      ++current_;
119,726✔
1245
    else
1246
      break;
1247
  }
1248
}
175,630✔
1249

1250
void OurReader::skipBom(bool skipBom) {
926✔
1251
  // The default behavior is to skip BOM.
1252
  if (skipBom) {
926✔
1253
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
925✔
1254
      begin_ += 3;
1✔
1255
      current_ = begin_;
1✔
1256
    }
1257
  }
1258
}
926✔
1259

1260
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1261
  if (end_ - current_ < patternLength)
226✔
1262
    return false;
1263
  int index = patternLength;
1264
  while (index--)
797✔
1265
    if (current_[index] != pattern[index])
631✔
1266
      return false;
1267
  current_ += patternLength;
166✔
1268
  return true;
166✔
1269
}
1270

1271
bool OurReader::readComment() {
615✔
1272
  const Location commentBegin = current_ - 1;
615✔
1273
  const Char c = getNextChar();
615✔
1274
  bool successful = false;
1275
  bool cStyleWithEmbeddedNewline = false;
615✔
1276

1277
  const bool isCStyleComment = (c == '*');
1278
  const bool isCppStyleComment = (c == '/');
1279
  if (isCStyleComment) {
615✔
1280
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
85✔
1281
  } else if (isCppStyleComment) {
530✔
1282
    successful = readCppStyleComment();
530✔
1283
  }
1284

1285
  if (!successful)
615✔
1286
    return false;
×
1287

1288
  if (collectComments_) {
615✔
1289
    CommentPlacement placement = commentBefore;
1290

1291
    if (!lastValueHasAComment_) {
597✔
1292
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
579✔
1293
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
65✔
1294
          placement = commentAfterOnSameLine;
1295
          lastValueHasAComment_ = true;
64✔
1296
        }
1297
      }
1298
    }
1299

1300
    addComment(commentBegin, current_, placement);
597✔
1301
  }
1302
  return true;
1303
}
1304

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

1325
void OurReader::addComment(Location begin, Location end,
597✔
1326
                           CommentPlacement placement) {
1327
  assert(collectComments_);
597✔
1328
  const String& normalized = normalizeEOL(begin, end);
597✔
1329
  if (placement == commentAfterOnSameLine) {
597✔
1330
    assert(lastValue_ != nullptr);
64✔
1331
    lastValue_->setComment(normalized, placement);
128✔
1332
  } else {
1333
    commentsBefore_ += normalized;
533✔
1334
  }
1335
}
597✔
1336

1337
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
85✔
1338
  *containsNewLineResult = false;
85✔
1339

1340
  while ((current_ + 1) < end_) {
2,754✔
1341
    Char c = getNextChar();
2,669✔
1342
    if (c == '*' && *current_ == '/')
2,669✔
1343
      break;
1344
    if (c == '\n')
2,584✔
1345
      *containsNewLineResult = true;
97✔
1346
  }
1347

1348
  return getNextChar() == '/';
85✔
1349
}
1350

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

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

1405
bool OurReader::readStringSingleQuote() {
11✔
1406
  Char c = 0;
1407
  while (current_ != end_) {
26✔
1408
    c = getNextChar();
26✔
1409
    if (c == '\\')
26✔
1410
      getNextChar();
2✔
1411
    else if (c == '\'')
24✔
1412
      break;
1413
  }
1414
  return c == '\'';
11✔
1415
}
1416

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

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

1459
    Token comma;
1460
    if (!readTokenSkippingComments(comma) ||
609✔
1461
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
601✔
1462
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1463
                                comma, tokenObjectEnd);
1464
    }
1465
    if (comma.type_ == tokenObjectEnd)
582✔
1466
      return true;
1467
  }
1468
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
52✔
1469
                            tokenObjectEnd);
1470
}
367✔
1471

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

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

1511
bool OurReader::decodeNumber(Token& token) {
52,834✔
1512
  Value decoded;
52,834✔
1513
  if (!decodeNumber(token, decoded))
52,834✔
1514
    return false;
1515
  currentValue().swapPayload(decoded);
52,825✔
1516
  currentValue().setOffsetStart(token.start_ - begin_);
52,825✔
1517
  currentValue().setOffsetLimit(token.end_ - begin_);
52,825✔
1518
  return true;
1519
}
52,834✔
1520

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

1531
  // We assume we can represent the largest and smallest integer types as
1532
  // unsigned integers with separate sign. This is only true if they can fit
1533
  // into an unsigned integer.
1534
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1535
                "Int must be smaller than UInt");
1536

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

1548
  static constexpr Value::LargestUInt positive_threshold =
1549
      Value::maxLargestUInt / 10;
1550
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1551

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

1562
  const Value::LargestUInt threshold =
1563
      isNegative ? negative_threshold : positive_threshold;
1564
  const Value::UInt max_last_digit =
1565
      isNegative ? negative_last_digit : positive_last_digit;
1566

1567
  Value::LargestUInt value = 0;
1568
  while (current < token.end_) {
235,282✔
1569
    Char c = *current++;
182,641✔
1570
    if (c < '0' || c > '9')
182,641✔
1571
      return decodeDouble(token, decoded);
177✔
1572

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

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

1598
  return true;
1599
}
1600

1601
bool OurReader::decodeDouble(Token& token) {
×
1602
  Value decoded;
×
1603
  if (!decodeDouble(token, decoded))
×
1604
    return false;
1605
  currentValue().swapPayload(decoded);
×
1606
  currentValue().setOffsetStart(token.start_ - begin_);
×
1607
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1608
  return true;
1609
}
×
1610

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

1627
bool OurReader::decodeString(Token& token) {
513✔
1628
  String decoded_string;
1629
  if (!decodeString(token, decoded_string))
513✔
1630
    return false;
1631
  Value decoded(decoded_string);
496✔
1632
  currentValue().swapPayload(decoded);
496✔
1633
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1634
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1635
  return true;
1636
}
496✔
1637

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

1691
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1692
                                       Location end, unsigned int& unicode) {
1693

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

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

1742
bool OurReader::addError(const String& message, Token& token, Location extra) {
156✔
1743
  ErrorInfo info;
1744
  info.token_ = token;
156✔
1745
  info.message_ = message;
1746
  info.extra_ = extra;
156✔
1747
  errors_.push_back(info);
156✔
1748
  return false;
156✔
1749
}
1750

1751
bool OurReader::recoverFromError(TokenType skipUntilToken) {
152✔
1752
  size_t errorCount = errors_.size();
1753
  Token skip;
1754
  for (;;) {
1755
    if (!readToken(skip))
410✔
1756
      errors_.resize(errorCount); // discard errors caused by recovery
173✔
1757
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
410✔
1758
      break;
1759
  }
1760
  errors_.resize(errorCount);
152✔
1761
  return false;
152✔
1762
}
1763

1764
bool OurReader::addErrorAndRecover(const String& message, Token& token,
84✔
1765
                                   TokenType skipUntilToken) {
1766
  addError(message, token);
84✔
1767
  return recoverFromError(skipUntilToken);
84✔
1768
}
1769

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

1772
OurReader::Char OurReader::getNextChar() {
174,124✔
1773
  if (current_ == end_)
174,124✔
1774
    return 0;
1775
  return *current_++;
173,186✔
1776
}
1777

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

1800
String OurReader::getLocationLineAndColumn(Location location) const {
172✔
1801
  int line, column;
1802
  getLocationLineAndColumn(location, line, column);
172✔
1803
  char buffer[18 + 16 + 16 + 1];
1804
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
172✔
1805
  return buffer;
172✔
1806
}
1807

1808
String OurReader::getFormattedErrorMessages() const {
917✔
1809
  String formattedMessage;
1810
  for (const auto& error : errors_) {
1,072✔
1811
    formattedMessage +=
1812
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
155✔
1813
    formattedMessage += "  " + error.message_ + "\n";
155✔
1814
    if (error.extra_)
155✔
1815
      formattedMessage +=
1816
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
34✔
1817
  }
1818
  return formattedMessage;
917✔
1819
}
1820

1821
std::vector<CharReader::StructuredError>
1822
OurReader::getStructuredErrors() const {
2✔
1823
  std::vector<CharReader::StructuredError> allErrors;
1824
  for (const auto& error : errors_) {
3✔
1825
    CharReader::StructuredError structured;
1826
    structured.offset_start = error.token_.start_ - begin_;
1✔
1827
    structured.offset_limit = error.token_.end_ - begin_;
1✔
1828
    structured.message = error.message_;
1✔
1829
    allErrors.push_back(structured);
1✔
1830
  }
1831
  return allErrors;
2✔
1832
}
×
1833

1834
class OurCharReader : public CharReader {
1835

1836
public:
1837
  OurCharReader(bool collectComments, OurFeatures const& features)
894✔
1838
      : CharReader(
894✔
1839
            std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1,788✔
1840

1841
protected:
1842
  class OurImpl : public Impl {
1843
  public:
1844
    OurImpl(bool collectComments, OurFeatures const& features)
1845
        : collectComments_(collectComments), reader_(features) {}
894✔
1846

1847
    bool parse(char const* beginDoc, char const* endDoc, Value* root,
926✔
1848
               String* errs) override {
1849
      bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
926✔
1850
      if (errs) {
919✔
1851
        *errs = reader_.getFormattedErrorMessages();
1,834✔
1852
      }
1853
      return ok;
919✔
1854
    }
1855

1856
    std::vector<CharReader::StructuredError>
1857
    getStructuredErrors() const override {
2✔
1858
      return reader_.getStructuredErrors();
2✔
1859
    }
1860

1861
  private:
1862
    bool const collectComments_;
1863
    OurReader reader_;
1864
  };
1865
};
1866

1867
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
891✔
1868
CharReaderBuilder::~CharReaderBuilder() = default;
1,782✔
1869
CharReader* CharReaderBuilder::newCharReader() const {
894✔
1870
  bool collectComments = settings_["collectComments"].asBool();
894✔
1871
  OurFeatures features = OurFeatures::all();
894✔
1872
  features.allowComments_ = settings_["allowComments"].asBool();
894✔
1873
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
894✔
1874
  features.strictRoot_ = settings_["strictRoot"].asBool();
894✔
1875
  features.allowDroppedNullPlaceholders_ =
894✔
1876
      settings_["allowDroppedNullPlaceholders"].asBool();
894✔
1877
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
894✔
1878
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
894✔
1879

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

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

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

1970
std::vector<CharReader::StructuredError>
1971
CharReader::getStructuredErrors() const {
2✔
1972
  return _impl->getStructuredErrors();
2✔
1973
}
1974

1975
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
926✔
1976
                       String* errs) {
1977
  return _impl->parse(beginDoc, endDoc, root, errs);
926✔
1978
}
1979

1980
//////////////////////////////////
1981
// global functions
1982

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

1995
IStream& operator>>(IStream& sin, Value& root) {
1✔
1996
  CharReaderBuilder b;
1✔
1997
  String errs;
1998
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
1999
  if (!ok) {
1✔
2000
    throwRuntimeError(errs);
×
2001
  }
2002
  return sin;
1✔
2003
}
1✔
2004

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