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

open-source-parsers / jsoncpp / 10784089608

10 Sep 2024 01:27AM CUT coverage: 95.383%. First build
10784089608

Pull #1561

github

web-flow
Merge branch 'master' into baylesj-patch-6
Pull Request #1561: Add code coverage

5268 of 5523 relevant lines covered (95.38%)

12029.53 hits per line

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

94.19
/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
#if __cplusplus >= 201103L
27

28
#if !defined(sscanf)
29
#define sscanf std::sscanf
30
#endif
31

32
#endif //__cplusplus
33

34
#if defined(_MSC_VER)
35
#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
36
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
37
#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
38
#endif //_MSC_VER
39

40
#if defined(_MSC_VER)
41
// Disable warning about strdup being deprecated.
42
#pragma warning(disable : 4996)
43
#endif
44

45
// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
46
// time to change the stack limit
47
#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
48
#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
49
#endif
50

51
static size_t const stackLimit_g =
52
    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
53

54
namespace Json {
55

56
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
57
using CharReaderPtr = std::unique_ptr<CharReader>;
58
#else
59
using CharReaderPtr = std::auto_ptr<CharReader>;
60
#endif
61

62
// Implementation of class Features
63
// ////////////////////////////////
64

65
Features::Features() = default;
606✔
66

67
Features Features::all() { return {}; }
15✔
68

69
Features Features::strictMode() {
100✔
70
  Features features;
100✔
71
  features.allowComments_ = false;
100✔
72
  features.strictRoot_ = true;
100✔
73
  features.allowDroppedNullPlaceholders_ = false;
100✔
74
  features.allowNumericKeys_ = false;
100✔
75
  return features;
100✔
76
}
77

78
// Implementation of class Reader
79
// ////////////////////////////////
80

81
bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
184✔
82
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
347✔
83
}
84

85
// Class Reader
86
// //////////////////////////////////////////////////////////////////
87

88
Reader::Reader() : features_(Features::all()) {}
30✔
89

90
Reader::Reader(const Features& features) : features_(features) {}
1,372✔
91

92
bool Reader::parse(const std::string& document, Value& root,
25✔
93
                   bool collectComments) {
94
  document_.assign(document.begin(), document.end());
25✔
95
  const char* begin = document_.c_str();
96
  const char* end = begin + document_.length();
25✔
97
  return parse(begin, end, root, collectComments);
25✔
98
}
99

100
bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
1✔
101
  // std::istream_iterator<char> begin(is);
102
  // std::istream_iterator<char> end;
103
  // Those would allow streamed input from a file, if parse() were a
104
  // template function.
105

106
  // Since String is reference-counted, this at least does not
107
  // create an extra copy.
108
  String doc(std::istreambuf_iterator<char>(is), {});
109
  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
2✔
110
}
111

112
bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
710✔
113
                   bool collectComments) {
114
  if (!features_.allowComments_) {
710✔
115
    collectComments = false;
116
  }
117

118
  begin_ = beginDoc;
710✔
119
  end_ = endDoc;
710✔
120
  collectComments_ = collectComments;
710✔
121
  current_ = begin_;
710✔
122
  lastValueEnd_ = nullptr;
710✔
123
  lastValue_ = nullptr;
710✔
124
  commentsBefore_.clear();
125
  errors_.clear();
710✔
126
  while (!nodes_.empty())
721✔
127
    nodes_.pop();
11✔
128
  nodes_.push(&root);
710✔
129

130
  bool successful = readValue();
710✔
131
  Token token;
132
  readTokenSkippingComments(token);
710✔
133
  if (collectComments_ && !commentsBefore_.empty())
710✔
134
    root.setComment(commentsBefore_, commentAfter);
26✔
135
  if (features_.strictRoot_) {
710✔
136
    if (!root.isArray() && !root.isObject()) {
1✔
137
      // Set error location to start of doc, ideally should be first token found
138
      // in doc
139
      token.type_ = tokenError;
1✔
140
      token.start_ = beginDoc;
1✔
141
      token.end_ = endDoc;
1✔
142
      addError(
1✔
143
          "A valid JSON document must be either an array or an object value.",
144
          token);
145
      return false;
1✔
146
    }
147
  }
148
  return successful;
149
}
150

151
bool Reader::readValue() {
53,560✔
152
  // readValue() may call itself only if it calls readObject() or ReadArray().
153
  // These methods execute nodes_.push() just before and nodes_.pop)() just
154
  // after calling readValue(). parse() executes one nodes_.push(), so > instead
155
  // of >=.
156
  if (nodes_.size() > stackLimit_g)
53,560✔
157
    throwRuntimeError("Exceeded stackLimit in readValue().");
×
158

159
  Token token;
160
  readTokenSkippingComments(token);
53,560✔
161
  bool successful = true;
162

163
  if (collectComments_ && !commentsBefore_.empty()) {
53,560✔
164
    currentValue().setComment(commentsBefore_, commentBefore);
1,191✔
165
    commentsBefore_.clear();
166
  }
167

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

220
  if (collectComments_) {
53,555✔
221
    lastValueEnd_ = current_;
53,554✔
222
    lastValue_ = &currentValue();
53,554✔
223
  }
224

225
  return successful;
226
}
227

228
bool Reader::readTokenSkippingComments(Token& token) {
107,540✔
229
  bool success = readToken(token);
107,540✔
230
  if (features_.allowComments_) {
107,540✔
231
    while (success && token.type_ == tokenComment) {
108,107✔
232
      success = readToken(token);
569✔
233
    }
234
  }
235
  return success;
107,540✔
236
}
237

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

309
void Reader::skipSpaces() {
108,752✔
310
  while (current_ != end_) {
226,612✔
311
    Char c = *current_;
225,895✔
312
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
225,895✔
313
      ++current_;
117,860✔
314
    else
315
      break;
316
  }
317
}
108,752✔
318

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

330
bool Reader::readComment() {
569✔
331
  Location commentBegin = current_ - 1;
569✔
332
  Char c = getNextChar();
569✔
333
  bool successful = false;
334
  if (c == '*')
569✔
335
    successful = readCStyleComment();
73✔
336
  else if (c == '/')
496✔
337
    successful = readCppStyleComment();
496✔
338
  if (!successful)
569✔
339
    return false;
×
340

341
  if (collectComments_) {
569✔
342
    CommentPlacement placement = commentBefore;
343
    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
569✔
344
      if (c != '*' || !containsNewLine(commentBegin, current_))
50✔
345
        placement = commentAfterOnSameLine;
346
    }
347

348
    addComment(commentBegin, current_, placement);
569✔
349
  }
350
  return true;
351
}
352

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

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

384
bool Reader::readCStyleComment() {
73✔
385
  while ((current_ + 1) < end_) {
2,419✔
386
    Char c = getNextChar();
2,419✔
387
    if (c == '*' && *current_ == '/')
2,419✔
388
      break;
389
  }
390
  return getNextChar() == '/';
73✔
391
}
392

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

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

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

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

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

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

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

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

525
bool Reader::decodeNumber(Token& token) {
52,648✔
526
  Value decoded;
52,648✔
527
  if (!decodeNumber(token, decoded))
52,648✔
528
    return false;
529
  currentValue().swapPayload(decoded);
52,648✔
530
  currentValue().setOffsetStart(token.start_ - begin_);
52,648✔
531
  currentValue().setOffsetLimit(token.end_ - begin_);
52,648✔
532
  return true;
533
}
52,648✔
534

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

578
bool Reader::decodeDouble(Token& token) {
×
579
  Value decoded;
×
580
  if (!decodeDouble(token, decoded))
×
581
    return false;
582
  currentValue().swapPayload(decoded);
×
583
  currentValue().setOffsetStart(token.start_ - begin_);
×
584
  currentValue().setOffsetLimit(token.end_ - begin_);
×
585
  return true;
586
}
×
587

588
bool Reader::decodeDouble(Token& token, Value& decoded) {
145✔
589
  double value = 0;
145✔
590
  String buffer(token.start_, token.end_);
145✔
591
  IStringStream is(buffer);
145✔
592
  if (!(is >> value)) {
145✔
593
    if (value == std::numeric_limits<double>::max())
24✔
594
      value = std::numeric_limits<double>::infinity();
12✔
595
    else if (value == std::numeric_limits<double>::lowest())
12✔
596
      value = -std::numeric_limits<double>::infinity();
12✔
597
    else if (!std::isinf(value))
×
598
      return addError(
×
599
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
600
  }
601
  decoded = value;
145✔
602
  return true;
145✔
603
}
145✔
604

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

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

669
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
103✔
670
                                    Location end, unsigned int& unicode) {
671

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

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

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

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

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

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

750
Reader::Char Reader::getNextChar() {
160,543✔
751
  if (current_ == end_)
160,543✔
752
    return 0;
753
  return *current_++;
159,826✔
754
}
755

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

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

786
// Deprecated. Preserved for backward compatibility
787
String Reader::getFormatedErrorMessages() const {
×
788
  return getFormattedErrorMessages();
×
789
}
790

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

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

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

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

850
bool Reader::good() const { return errors_.empty(); }
×
851

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

870
OurFeatures OurFeatures::all() { return {}; }
892✔
871

872
// Implementation of class Reader
873
// ////////////////////////////////
874

875
// Originally copied from the Reader class (now deprecated), used internally
876
// for implementing JSON reading.
877
class OurReader {
878
public:
879
  using Char = char;
880
  using Location = const Char*;
881
  struct StructuredError {
×
882
    ptrdiff_t offset_start;
883
    ptrdiff_t offset_limit;
884
    String message;
885
  };
886

887
  explicit OurReader(OurFeatures const& features);
888
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
889
             bool collectComments = true);
890
  String getFormattedErrorMessages() const;
891
  std::vector<StructuredError> getStructuredErrors() const;
892

893
private:
894
  OurReader(OurReader const&);      // no impl
895
  void operator=(OurReader const&); // no impl
896

897
  enum TokenType {
898
    tokenEndOfStream = 0,
899
    tokenObjectBegin,
900
    tokenObjectEnd,
901
    tokenArrayBegin,
902
    tokenArrayEnd,
903
    tokenString,
904
    tokenNumber,
905
    tokenTrue,
906
    tokenFalse,
907
    tokenNull,
908
    tokenNaN,
909
    tokenPosInf,
910
    tokenNegInf,
911
    tokenArraySeparator,
912
    tokenMemberSeparator,
913
    tokenComment,
914
    tokenError
915
  };
916

917
  class Token {
918
  public:
919
    TokenType type_;
920
    Location start_;
921
    Location end_;
922
  };
923

924
  class ErrorInfo {
155✔
925
  public:
926
    Token token_;
927
    String message_;
928
    Location extra_;
929
  };
930

931
  using Errors = std::deque<ErrorInfo>;
932

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

969
  static String normalizeEOL(Location begin, Location end);
970
  static bool containsNewLine(Location begin, Location end);
971

972
  using Nodes = std::stack<Value*>;
973

974
  Nodes nodes_{};
975
  Errors errors_{};
976
  String document_{};
977
  Location begin_ = nullptr;
978
  Location end_ = nullptr;
979
  Location current_ = nullptr;
980
  Location lastValueEnd_ = nullptr;
981
  Value* lastValue_ = nullptr;
982
  bool lastValueHasAComment_ = false;
983
  String commentsBefore_{};
984

985
  OurFeatures const features_;
986
  bool collectComments_ = false;
987
}; // OurReader
988

989
// complete copy of Read impl, for OurReader
990

991
bool OurReader::containsNewLine(OurReader::Location begin,
185✔
992
                                OurReader::Location end) {
993
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
225✔
994
}
995

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

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

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

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

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

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

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

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

1133
  return successful;
1134
}
1135

1136
bool OurReader::readTokenSkippingComments(Token& token) {
115,192✔
1137
  bool success = readToken(token);
115,192✔
1138
  if (features_.allowComments_) {
115,192✔
1139
    while (success && token.type_ == tokenComment) {
114,692✔
1140
      success = readToken(token);
590✔
1141
    }
1142
  }
1143
  return success;
115,192✔
1144
}
1145

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

1257
void OurReader::skipSpaces() {
175,621✔
1258
  while (current_ != end_) {
295,343✔
1259
    Char c = *current_;
294,407✔
1260
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
294,407✔
1261
      ++current_;
119,722✔
1262
    else
1263
      break;
1264
  }
1265
}
175,621✔
1266

1267
void OurReader::skipBom(bool skipBom) {
924✔
1268
  // The default behavior is to skip BOM.
1269
  if (skipBom) {
924✔
1270
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
923✔
1271
      begin_ += 3;
1✔
1272
      current_ = begin_;
1✔
1273
    }
1274
  }
1275
}
924✔
1276

1277
bool OurReader::match(const Char* pattern, int patternLength) {
226✔
1278
  if (end_ - current_ < patternLength)
226✔
1279
    return false;
1280
  int index = patternLength;
1281
  while (index--)
797✔
1282
    if (current_[index] != pattern[index])
631✔
1283
      return false;
1284
  current_ += patternLength;
166✔
1285
  return true;
166✔
1286
}
1287

1288
bool OurReader::readComment() {
615✔
1289
  const Location commentBegin = current_ - 1;
615✔
1290
  const Char c = getNextChar();
615✔
1291
  bool successful = false;
1292
  bool cStyleWithEmbeddedNewline = false;
615✔
1293

1294
  const bool isCStyleComment = (c == '*');
1295
  const bool isCppStyleComment = (c == '/');
1296
  if (isCStyleComment) {
615✔
1297
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
85✔
1298
  } else if (isCppStyleComment) {
530✔
1299
    successful = readCppStyleComment();
530✔
1300
  }
1301

1302
  if (!successful)
615✔
1303
    return false;
×
1304

1305
  if (collectComments_) {
615✔
1306
    CommentPlacement placement = commentBefore;
1307

1308
    if (!lastValueHasAComment_) {
597✔
1309
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
579✔
1310
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
65✔
1311
          placement = commentAfterOnSameLine;
1312
          lastValueHasAComment_ = true;
64✔
1313
        }
1314
      }
1315
    }
1316

1317
    addComment(commentBegin, current_, placement);
597✔
1318
  }
1319
  return true;
1320
}
1321

1322
String OurReader::normalizeEOL(OurReader::Location begin,
597✔
1323
                               OurReader::Location end) {
1324
  String normalized;
1325
  normalized.reserve(static_cast<size_t>(end - begin));
597✔
1326
  OurReader::Location current = begin;
1327
  while (current != end) {
21,944✔
1328
    char c = *current++;
21,347✔
1329
    if (c == '\r') {
21,347✔
1330
      if (current != end && *current == '\n')
3✔
1331
        // convert dos EOL
1332
        ++current;
1✔
1333
      // convert Mac EOL
1334
      normalized += '\n';
1335
    } else {
1336
      normalized += c;
21,344✔
1337
    }
1338
  }
1339
  return normalized;
597✔
1340
}
1341

1342
void OurReader::addComment(Location begin, Location end,
597✔
1343
                           CommentPlacement placement) {
1344
  assert(collectComments_);
597✔
1345
  const String& normalized = normalizeEOL(begin, end);
597✔
1346
  if (placement == commentAfterOnSameLine) {
597✔
1347
    assert(lastValue_ != nullptr);
64✔
1348
    lastValue_->setComment(normalized, placement);
128✔
1349
  } else {
1350
    commentsBefore_ += normalized;
533✔
1351
  }
1352
}
597✔
1353

1354
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
85✔
1355
  *containsNewLineResult = false;
85✔
1356

1357
  while ((current_ + 1) < end_) {
2,754✔
1358
    Char c = getNextChar();
2,669✔
1359
    if (c == '*' && *current_ == '/')
2,669✔
1360
      break;
1361
    if (c == '\n')
2,584✔
1362
      *containsNewLineResult = true;
97✔
1363
  }
1364

1365
  return getNextChar() == '/';
85✔
1366
}
1367

1368
bool OurReader::readCppStyleComment() {
530✔
1369
  while (current_ != end_) {
18,083✔
1370
    Char c = getNextChar();
18,077✔
1371
    if (c == '\n')
18,077✔
1372
      break;
1373
    if (c == '\r') {
17,556✔
1374
      // Consume DOS EOL. It will be normalized in addComment.
1375
      if (current_ != end_ && *current_ == '\n')
3✔
1376
        getNextChar();
1✔
1377
      // Break on Moc OS 9 EOL.
1378
      break;
1379
    }
1380
  }
1381
  return true;
530✔
1382
}
1383

1384
bool OurReader::readNumber(bool checkInf) {
52,873✔
1385
  Location p = current_;
52,873✔
1386
  if (checkInf && p != end_ && *p == 'I') {
52,873✔
1387
    current_ = ++p;
6✔
1388
    return false;
6✔
1389
  }
1390
  char c = '0'; // stopgap for already consumed character
1391
  // integral part
1392
  while (c >= '0' && c <= '9')
235,512✔
1393
    c = (current_ = p) < end_ ? *p++ : '\0';
182,645✔
1394
  // fractional part
1395
  if (c == '.') {
52,867✔
1396
    c = (current_ = p) < end_ ? *p++ : '\0';
113✔
1397
    while (c >= '0' && c <= '9')
785✔
1398
      c = (current_ = p) < end_ ? *p++ : '\0';
672✔
1399
  }
1400
  // exponential part
1401
  if (c == 'e' || c == 'E') {
52,867✔
1402
    c = (current_ = p) < end_ ? *p++ : '\0';
103✔
1403
    if (c == '+' || c == '-')
103✔
1404
      c = (current_ = p) < end_ ? *p++ : '\0';
76✔
1405
    while (c >= '0' && c <= '9')
306✔
1406
      c = (current_ = p) < end_ ? *p++ : '\0';
203✔
1407
  }
1408
  return true;
1409
}
1410
bool OurReader::readString() {
1,168✔
1411
  Char c = 0;
1412
  while (current_ != end_) {
34,509✔
1413
    c = getNextChar();
34,509✔
1414
    if (c == '\\')
34,509✔
1415
      getNextChar();
1,275✔
1416
    else if (c == '"')
33,234✔
1417
      break;
1418
  }
1419
  return c == '"';
1,168✔
1420
}
1421

1422
bool OurReader::readStringSingleQuote() {
11✔
1423
  Char c = 0;
1424
  while (current_ != end_) {
26✔
1425
    c = getNextChar();
26✔
1426
    if (c == '\\')
26✔
1427
      getNextChar();
2✔
1428
    else if (c == '\'')
24✔
1429
      break;
1430
  }
1431
  return c == '\'';
11✔
1432
}
1433

1434
bool OurReader::readObject(Token& token) {
365✔
1435
  Token tokenName;
1436
  String name;
1437
  Value init(objectValue);
365✔
1438
  currentValue().swapPayload(init);
365✔
1439
  currentValue().setOffsetStart(token.start_ - begin_);
365✔
1440
  while (readTokenSkippingComments(tokenName)) {
689✔
1441
    if (tokenName.type_ == tokenObjectEnd &&
680✔
1442
        (name.empty() ||
6✔
1443
         features_.allowTrailingCommas_)) // empty object or trailing comma
6✔
1444
      return true;
339✔
1445
    name.clear();
1446
    if (tokenName.type_ == tokenString) {
656✔
1447
      if (!decodeString(tokenName, name))
637✔
1448
        return recoverFromError(tokenObjectEnd);
×
1449
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
19✔
1450
      Value numberName;
3✔
1451
      if (!decodeNumber(tokenName, numberName))
3✔
1452
        return recoverFromError(tokenObjectEnd);
×
1453
      name = numberName.asString();
3✔
1454
    } else {
3✔
1455
      break;
1456
    }
1457
    if (name.length() >= (1U << 30))
640✔
1458
      throwRuntimeError("keylength >= 2^30");
×
1459
    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
640✔
1460
      String msg = "Duplicate key: '" + name + "'";
1✔
1461
      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1✔
1462
    }
1463

1464
    Token colon;
1465
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
639✔
1466
      return addErrorAndRecover("Missing ':' after object member name", colon,
14✔
1467
                                tokenObjectEnd);
1468
    }
1469
    Value& value = currentValue()[name];
632✔
1470
    nodes_.push(&value);
632✔
1471
    bool ok = readValue();
632✔
1472
    nodes_.pop();
631✔
1473
    if (!ok) // error already set
631✔
1474
      return recoverFromError(tokenObjectEnd);
22✔
1475

1476
    Token comma;
1477
    if (!readTokenSkippingComments(comma) ||
609✔
1478
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
601✔
1479
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
54✔
1480
                                comma, tokenObjectEnd);
1481
    }
1482
    if (comma.type_ == tokenObjectEnd)
582✔
1483
      return true;
1484
  }
1485
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
50✔
1486
                            tokenObjectEnd);
1487
}
365✔
1488

1489
bool OurReader::readArray(Token& token) {
6,359✔
1490
  Value init(arrayValue);
6,359✔
1491
  currentValue().swapPayload(init);
6,359✔
1492
  currentValue().setOffsetStart(token.start_ - begin_);
6,359✔
1493
  int index = 0;
1494
  for (;;) {
1495
    skipSpaces();
58,765✔
1496
    if (current_ != end_ && *current_ == ']' &&
58,765✔
1497
        (index == 0 ||
14✔
1498
         (features_.allowTrailingCommas_ &&
14✔
1499
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
14✔
1500
                                                      // comma
1501
    {
1502
      Token endArray;
1503
      readToken(endArray);
28✔
1504
      return true;
1505
    }
1506
    Value& value = currentValue()[index++];
58,737✔
1507
    nodes_.push(&value);
58,737✔
1508
    bool ok = readValue();
58,737✔
1509
    nodes_.pop();
52,737✔
1510
    if (!ok) // error already set
52,737✔
1511
      return recoverFromError(tokenArrayEnd);
46✔
1512

1513
    Token currentToken;
1514
    // Accept Comment after last item in the array.
1515
    ok = readTokenSkippingComments(currentToken);
52,691✔
1516
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
52,691✔
1517
                         currentToken.type_ != tokenArrayEnd);
52,691✔
1518
    if (!ok || badTokenType) {
52,691✔
1519
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
46✔
1520
                                currentToken, tokenArrayEnd);
1521
    }
1522
    if (currentToken.type_ == tokenArrayEnd)
52,668✔
1523
      break;
1524
  }
52,406✔
1525
  return true;
262✔
1526
}
6,359✔
1527

1528
bool OurReader::decodeNumber(Token& token) {
52,834✔
1529
  Value decoded;
52,834✔
1530
  if (!decodeNumber(token, decoded))
52,834✔
1531
    return false;
1532
  currentValue().swapPayload(decoded);
52,825✔
1533
  currentValue().setOffsetStart(token.start_ - begin_);
52,825✔
1534
  currentValue().setOffsetLimit(token.end_ - begin_);
52,825✔
1535
  return true;
1536
}
52,834✔
1537

1538
bool OurReader::decodeNumber(Token& token, Value& decoded) {
52,837✔
1539
  // Attempts to parse the number as an integer. If the number is
1540
  // larger than the maximum supported value of an integer then
1541
  // we decode the number as a double.
1542
  Location current = token.start_;
52,837✔
1543
  const bool isNegative = *current == '-';
52,837✔
1544
  if (isNegative) {
52,837✔
1545
    ++current;
141✔
1546
  }
1547

1548
  // We assume we can represent the largest and smallest integer types as
1549
  // unsigned integers with separate sign. This is only true if they can fit
1550
  // into an unsigned integer.
1551
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1552
                "Int must be smaller than UInt");
1553

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

1565
  static constexpr Value::LargestUInt positive_threshold =
1566
      Value::maxLargestUInt / 10;
1567
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1568

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

1579
  const Value::LargestUInt threshold =
1580
      isNegative ? negative_threshold : positive_threshold;
52,837✔
1581
  const Value::UInt max_last_digit =
1582
      isNegative ? negative_last_digit : positive_last_digit;
52,837✔
1583

1584
  Value::LargestUInt value = 0;
1585
  while (current < token.end_) {
235,282✔
1586
    Char c = *current++;
182,641✔
1587
    if (c < '0' || c > '9')
182,641✔
1588
      return decodeDouble(token, decoded);
177✔
1589

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

1605
  if (isNegative) {
52,641✔
1606
    // We use the same magnitude assumption here, just in case.
1607
    const auto last_digit = static_cast<Value::UInt>(value % 10);
89✔
1608
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
89✔
1609
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
52,552✔
1610
    decoded = Value::LargestInt(value);
52,528✔
1611
  } else {
1612
    decoded = value;
24✔
1613
  }
1614

1615
  return true;
1616
}
1617

1618
bool OurReader::decodeDouble(Token& token) {
×
1619
  Value decoded;
×
1620
  if (!decodeDouble(token, decoded))
×
1621
    return false;
1622
  currentValue().swapPayload(decoded);
×
1623
  currentValue().setOffsetStart(token.start_ - begin_);
×
1624
  currentValue().setOffsetLimit(token.end_ - begin_);
×
1625
  return true;
1626
}
×
1627

1628
bool OurReader::decodeDouble(Token& token, Value& decoded) {
196✔
1629
  double value = 0;
196✔
1630
  const String buffer(token.start_, token.end_);
196✔
1631
  IStringStream is(buffer);
196✔
1632
  if (!(is >> value)) {
196✔
1633
    if (value == std::numeric_limits<double>::max())
33✔
1634
      value = std::numeric_limits<double>::infinity();
12✔
1635
    else if (value == std::numeric_limits<double>::lowest())
21✔
1636
      value = -std::numeric_limits<double>::infinity();
12✔
1637
    else if (!std::isinf(value))
9✔
1638
      return addError(
9✔
1639
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
27✔
1640
  }
1641
  decoded = value;
187✔
1642
  return true;
187✔
1643
}
196✔
1644

1645
bool OurReader::decodeString(Token& token) {
513✔
1646
  String decoded_string;
1647
  if (!decodeString(token, decoded_string))
513✔
1648
    return false;
1649
  Value decoded(decoded_string);
496✔
1650
  currentValue().swapPayload(decoded);
496✔
1651
  currentValue().setOffsetStart(token.start_ - begin_);
496✔
1652
  currentValue().setOffsetLimit(token.end_ - begin_);
496✔
1653
  return true;
1654
}
496✔
1655

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

1709
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
145✔
1710
                                       Location end, unsigned int& unicode) {
1711

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

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

1760
bool OurReader::addError(const String& message, Token& token, Location extra) {
155✔
1761
  ErrorInfo info;
1762
  info.token_ = token;
155✔
1763
  info.message_ = message;
1764
  info.extra_ = extra;
155✔
1765
  errors_.push_back(info);
155✔
1766
  return false;
155✔
1767
}
1768

1769
bool OurReader::recoverFromError(TokenType skipUntilToken) {
151✔
1770
  size_t errorCount = errors_.size();
1771
  Token skip;
1772
  for (;;) {
1773
    if (!readToken(skip))
407✔
1774
      errors_.resize(errorCount); // discard errors caused by recovery
173✔
1775
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
407✔
1776
      break;
1777
  }
1778
  errors_.resize(errorCount);
151✔
1779
  return false;
151✔
1780
}
1781

1782
bool OurReader::addErrorAndRecover(const String& message, Token& token,
83✔
1783
                                   TokenType skipUntilToken) {
1784
  addError(message, token);
83✔
1785
  return recoverFromError(skipUntilToken);
83✔
1786
}
1787

1788
Value& OurReader::currentValue() { return *(nodes_.top()); }
288,302✔
1789

1790
OurReader::Char OurReader::getNextChar() {
174,115✔
1791
  if (current_ == end_)
174,115✔
1792
    return 0;
1793
  return *current_++;
173,179✔
1794
}
1795

1796
void OurReader::getLocationLineAndColumn(Location location, int& line,
172✔
1797
                                         int& column) const {
1798
  Location current = begin_;
172✔
1799
  Location lastLineStart = current;
1800
  line = 0;
172✔
1801
  while (current < location && current != end_) {
2,253✔
1802
    Char c = *current++;
2,081✔
1803
    if (c == '\r') {
2,081✔
1804
      if (current != end_ && *current == '\n')
1✔
1805
        ++current;
×
1806
      lastLineStart = current;
1807
      ++line;
1✔
1808
    } else if (c == '\n') {
2,080✔
1809
      lastLineStart = current;
1810
      ++line;
28✔
1811
    }
1812
  }
1813
  // column & line start at 1
1814
  column = int(location - lastLineStart) + 1;
172✔
1815
  ++line;
172✔
1816
}
172✔
1817

1818
String OurReader::getLocationLineAndColumn(Location location) const {
172✔
1819
  int line, column;
1820
  getLocationLineAndColumn(location, line, column);
172✔
1821
  char buffer[18 + 16 + 16 + 1];
1822
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
172✔
1823
  return buffer;
172✔
1824
}
1825

1826
String OurReader::getFormattedErrorMessages() const {
917✔
1827
  String formattedMessage;
1828
  for (const auto& error : errors_) {
1,072✔
1829
    formattedMessage +=
1830
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
310✔
1831
    formattedMessage += "  " + error.message_ + "\n";
310✔
1832
    if (error.extra_)
155✔
1833
      formattedMessage +=
1834
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
34✔
1835
  }
1836
  return formattedMessage;
917✔
1837
}
1838

1839
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
×
1840
  std::vector<OurReader::StructuredError> allErrors;
1841
  for (const auto& error : errors_) {
×
1842
    OurReader::StructuredError structured;
1843
    structured.offset_start = error.token_.start_ - begin_;
×
1844
    structured.offset_limit = error.token_.end_ - begin_;
×
1845
    structured.message = error.message_;
×
1846
    allErrors.push_back(structured);
×
1847
  }
1848
  return allErrors;
×
1849
}
×
1850

1851
class OurCharReader : public CharReader {
1852
  bool const collectComments_;
1853
  OurReader reader_;
1854

1855
public:
1856
  OurCharReader(bool collectComments, OurFeatures const& features)
1857
      : collectComments_(collectComments), reader_(features) {}
892✔
1858
  bool parse(char const* beginDoc, char const* endDoc, Value* root,
924✔
1859
             String* errs) override {
1860
    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
924✔
1861
    if (errs) {
917✔
1862
      *errs = reader_.getFormattedErrorMessages();
1,834✔
1863
    }
1864
    return ok;
917✔
1865
  }
1866
};
1867

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

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

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

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

1955
//////////////////////////////////
1956
// global functions
1957

1958
bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
4✔
1959
                     String* errs) {
1960
  OStringStream ssin;
4✔
1961
  ssin << sin.rdbuf();
4✔
1962
  String doc = ssin.str();
1963
  char const* begin = doc.data();
1964
  char const* end = begin + doc.size();
4✔
1965
  // Note that we do not actually need a null-terminator.
1966
  CharReaderPtr const reader(fact.newCharReader());
4✔
1967
  return reader->parse(begin, end, root, errs);
8✔
1968
}
4✔
1969

1970
IStream& operator>>(IStream& sin, Value& root) {
1✔
1971
  CharReaderBuilder b;
1✔
1972
  String errs;
1973
  bool ok = parseFromStream(b, sin, &root, &errs);
1✔
1974
  if (!ok) {
1✔
1975
    throwRuntimeError(errs);
×
1976
  }
1977
  return sin;
1✔
1978
}
1✔
1979

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