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

open-source-parsers / jsoncpp / 12134745591

03 Dec 2024 07:16AM CUT coverage: 95.295%. Remained the same
12134745591

push

github

web-flow
Merge d350f8286 into 2b3815c90

5306 of 5568 relevant lines covered (95.29%)

11935.1 hits per line

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

93.81
/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
  IStringStream is(String(token.start_, token.end_));
290✔
591
  if (!(is >> value)) {
145✔
592
    if (value == std::numeric_limits<double>::max())
24✔
593
      value = std::numeric_limits<double>::infinity();
12✔
594
    else if (value == std::numeric_limits<double>::lowest())
12✔
595
      value = -std::numeric_limits<double>::infinity();
12✔
596
    else if (!std::isinf(value))
×
597
      return addError(
×
598
          "'" + String(token.start_, token.end_) + "' is not a number.", token);
×
599
  }
600
  decoded = value;
145✔
601
  return true;
145✔
602
}
145✔
603

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

869
OurFeatures OurFeatures::all() { return {}; }
894✔
870

871
// Implementation of class Reader
872
// ////////////////////////////////
873

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

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

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

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

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

918
  class ErrorInfo {
156✔
919
  public:
920
    Token token_;
921
    String message_;
922
    Location extra_;
923
  };
924

925
  using Errors = std::deque<ErrorInfo>;
926

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

963
  static String normalizeEOL(Location begin, Location end);
964
  static bool containsNewLine(Location begin, Location end);
965

966
  using Nodes = std::stack<Value*>;
967

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

979
  OurFeatures const features_;
980
  bool collectComments_ = false;
981
}; // OurReader
982

983
// complete copy of Read impl, for OurReader
984

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

990
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
1,788✔
991

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

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

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

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

1046
  if (collectComments_ && !commentsBefore_.empty()) {
60,288✔
1047
    currentValue().setComment(commentsBefore_, commentBefore);
1,203✔
1048
    commentsBefore_.clear();
1049
  }
1050

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

1121
  if (collectComments_) {
54,250✔
1122
    lastValueEnd_ = current_;
53,789✔
1123
    lastValueHasAComment_ = false;
53,789✔
1124
    lastValue_ = &currentValue();
53,789✔
1125
  }
1126

1127
  return successful;
1128
}
1129

1130
bool OurReader::readTokenSkippingComments(Token& token) {
115,198✔
1131
  bool success = readToken(token);
115,198✔
1132
  if (features_.allowComments_) {
115,198✔
1133
    while (success && token.type_ == tokenComment) {
114,698✔
1134
      success = readToken(token);
590✔
1135
    }
1136
  }
1137
  return success;
115,198✔
1138
}
1139

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

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

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

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

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

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

1296
  if (!successful)
615✔
1297
    return false;
×
1298

1299
  if (collectComments_) {
615✔
1300
    CommentPlacement placement = commentBefore;
1301

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

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

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

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

1348
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
85✔
1349
  *containsNewLineResult = false;
85✔
1350

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

1359
  return getNextChar() == '/';
85✔
1360
}
1361

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1573
  const Value::LargestUInt threshold =
1574
      isNegative ? negative_threshold : positive_threshold;
52,837✔
1575
  const Value::UInt max_last_digit =
1576
      isNegative ? negative_last_digit : positive_last_digit;
52,837✔
1577

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

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

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

1609
  return true;
1610
}
1611

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

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

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

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

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

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

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

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

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

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

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

1783
OurReader::Char OurReader::getNextChar() {
174,124✔
1784
  if (current_ == end_)
174,124✔
1785
    return 0;
1786
  return *current_++;
173,186✔
1787
}
1788

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

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

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

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

1845
class OurCharReader : public CharReader {
1846

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

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

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

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

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

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

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

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

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

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

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

1991
//////////////////////////////////
1992
// global functions
1993

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

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

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