Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

open-source-parsers / jsoncpp / 1902

9 Nov 2021 - 10:36 First build on bugfix/large-floats at 93.483%
1902

Pull #1353

travis-ci-com

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
<a href="https://github.com/open-source-parsers/jsoncpp/commit/<a class=hub.com/open-source-parsers/jsoncpp/commit/<a class="double-link" href="https://git"><a class=hub.com/open-source-parsers/jsoncpp/commit/<a class="double-link" href="https://git"><a class=hub.com/open-source-parsers/jsoncpp/commit/<a class="double-link" href="https://git"><a class=hub.com/open-source-parsers/jsoncpp/commit/7f3b523ca38180a3a75e75da3c8cca5019055d74">7f3b523ca">&lt;a href=&quot;https://github.com/open-source-parsers/jsoncpp/commit/</a><a class="double-link" href="https://github.com/open-source-parsers/jsoncpp/commit/&lt;a class=&quot;double-link&quot; href=&quot;https://git">&lt;a class=</a>hub.com/open-source-parsers/jsoncpp/commit/&lt;a class=&quot;double-link&quot; href=&quot;https://git">&lt;a class=</a>hub.com/open-source-parsers/jsoncpp/commit/&lt;a class=&quot;double-link&quot; href=&quot;https://git">&lt;a class=</a>hub.com/open-source-parsers/jsoncpp/commit/&lt;a class=&quot;double-link&quot; href=&quot;https://git">&lt;a class=</a>hub.com/open-source-parsers/jsoncpp/commit/7f3b523ca38180a3a75e75da3c8cca5019055d74">7f3b523ca</a><a href="https://github.com/open-source-parsers/jsoncpp/commit/7f3b523ca38180a3a75e75da3c8cca5019055d74">&quot;&gt;&amp;lt;a href=&amp;quot;https://github.com/open-source-parsers/jsoncpp/commit/&lt;/a&gt;&lt;a class=&quot;double-link&quot; href=&quot;https://github.com/open-source-parsers/jsoncpp/commit/&amp;lt;a class=&amp;quot;double-link&amp;quot; href=&amp;quot;https://git&quot;&gt;&amp;lt;a class=&lt;/a&gt;hub.com/open-source-parsers/jsoncpp/commit/&amp;lt;a class=&amp;quot;double-link&amp;quot; href=&amp;quot;https://git&quot;&gt;&amp;lt;a class=&lt;/a&gt;hub.com/open-source-parsers/jsoncpp/commit/&amp;lt;a class=&amp;quot;double-link&amp;quot; href=&amp;quot;https://git&quot;&gt;&amp;lt;a class=&lt;/a&gt;hub.com/open-source-parsers/jsoncpp/commit/&amp;lt;a class=&amp;quot;double-link&amp;quot; href=&amp;quot;https://git&quot;&gt;&amp;lt;a class=&lt;/a&gt;hub.com/open-source-parsers/jsoncpp/commit/7f3b523ca38180a3a75e75da3c8cca5019055d74&quot;&gt;7f3b523ca&lt;/a&gt;&lt;a href=&quot;https://github.com/open-source-parsers/jsoncpp/commit/7f3b523ca38180a3a75e75da3c8cca5019055d74&quot;&gt;&amp;quot;&amp;gt;&amp;amp;lt;a href=&amp;amp;quot;https://github.com/open-source-pa... (continued)
Pull Request #1353: Parse large floats as infinity (#1349)

1 of 11 new or added lines in 1 file covered. (9.09%)

2740 of 2931 relevant lines covered (93.48%)

1524.51 hits per line

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

92.29
/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;
66

67
Features Features::all() { return {}; }
60×
68

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

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

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

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

88
Reader::Reader() : features_(Features::all()) {}
60×
89

90
Reader::Reader(const Features& features) : features_(features) {}
8×
91

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

100
bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
4×
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), {});
8×
109
  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
8×
110
}
111

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

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

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

151
bool Reader::readValue() {
256×
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)
256×
157
    throwRuntimeError("Exceeded stackLimit in readValue().");
!
158

159
  Token token;
160
  skipCommentTokens(token);
256×
161
  bool successful = true;
256×
162

163
  if (collectComments_ && !commentsBefore_.empty()) {
256×
164
    currentValue().setComment(commentsBefore_, commentBefore);
4×
165
    commentsBefore_.clear();
4×
166
  }
167

168
  switch (token.type_) {
256×
169
  case tokenObjectBegin:
170
    successful = readObject(token);
64×
171
    currentValue().setOffsetLimit(current_ - begin_);
64×
172
    break;
64×
173
  case tokenArrayBegin:
174
    successful = readArray(token);
36×
175
    currentValue().setOffsetLimit(current_ - begin_);
36×
176
    break;
36×
177
  case tokenNumber:
178
    successful = decodeNumber(token);
16×
179
    break;
16×
180
  case tokenString:
181
    successful = decodeString(token);
104×
182
    break;
104×
183
  case tokenTrue: {
184
    Value v(true);
16×
185
    currentValue().swapPayload(v);
8×
186
    currentValue().setOffsetStart(token.start_ - begin_);
8×
187
    currentValue().setOffsetLimit(token.end_ - begin_);
8×
188
  } break;
8×
189
  case tokenFalse: {
190
    Value v(false);
8×
191
    currentValue().swapPayload(v);
4×
192
    currentValue().setOffsetStart(token.start_ - begin_);
4×
193
    currentValue().setOffsetLimit(token.end_ - begin_);
4×
194
  } break;
4×
195
  case tokenNull: {
196
    Value v;
8×
197
    currentValue().swapPayload(v);
4×
198
    currentValue().setOffsetStart(token.start_ - begin_);
4×
199
    currentValue().setOffsetLimit(token.end_ - begin_);
4×
200
  } break;
4×
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_);
20×
216
    currentValue().setOffsetLimit(token.end_ - begin_);
20×
217
    return addError("Syntax error: value, object or array expected.", token);
20×
218
  }
219

220
  if (collectComments_) {
236×
221
    lastValueEnd_ = current_;
232×
222
    lastValue_ = &currentValue();
232×
223
  }
224

225
  return successful;
236×
226
}
227

228
void Reader::skipCommentTokens(Token& token) {
360×
229
  if (features_.allowComments_) {
360×
230
    do {
16×
231
      readToken(token);
368×
232
    } while (token.type_ == tokenComment);
368×
233
  } else {
234
    readToken(token);
8×
235
  }
236
}
360×
237

238
bool Reader::readToken(Token& token) {
764×
239
  skipSpaces();
764×
240
  token.start_ = current_;
764×
241
  Char c = getNextChar();
764×
242
  bool ok = true;
764×
243
  switch (c) {
764×
244
  case '{':
245
    token.type_ = tokenObjectBegin;
64×
246
    break;
64×
247
  case '}':
248
    token.type_ = tokenObjectEnd;
56×
249
    break;
56×
250
  case '[':
251
    token.type_ = tokenArrayBegin;
36×
252
    break;
36×
253
  case ']':
254
    token.type_ = tokenArrayEnd;
32×
255
    break;
32×
256
  case '"':
257
    token.type_ = tokenString;
192×
258
    ok = readString();
192×
259
    break;
192×
260
  case '/':
261
    token.type_ = tokenComment;
20×
262
    ok = readComment();
20×
263
    break;
20×
264
  case '0':
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;
20×
276
    readNumber();
20×
277
    break;
20×
278
  case 't':
279
    token.type_ = tokenTrue;
8×
280
    ok = match("rue", 3);
8×
281
    break;
8×
282
  case 'f':
283
    token.type_ = tokenFalse;
12×
284
    ok = match("alse", 4);
12×
285
    break;
12×
286
  case 'n':
287
    token.type_ = tokenNull;
16×
288
    ok = match("ull", 3);
16×
289
    break;
16×
290
  case ',':
291
    token.type_ = tokenArraySeparator;
60×
292
    break;
60×
293
  case ':':
294
    token.type_ = tokenMemberSeparator;
84×
295
    break;
84×
296
  case 0:
297
    token.type_ = tokenEndOfStream;
132×
298
    break;
132×
299
  default:
300
    ok = false;
32×
301
    break;
32×
302
  }
303
  if (!ok)
764×
304
    token.type_ = tokenError;
52×
305
  token.end_ = current_;
764×
306
  return ok;
764×
307
}
308

309
void Reader::skipSpaces() {
1,160×
310
  while (current_ != end_) {
1,520×
311
    Char c = *current_;
1,028×
312
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1,028×
313
      ++current_;
360×
314
    else
315
      break;
316
  }
317
}
800×
318

319
bool Reader::match(const Char* pattern, int patternLength) {
36×
320
  if (end_ - current_ < patternLength)
36×
321
    return false;
4×
322
  int index = patternLength;
32×
323
  while (index--)
136×
324
    if (current_[index] != pattern[index])
68×
325
      return false;
16×
326
  current_ += patternLength;
16×
327
  return true;
16×
328
}
329

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

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

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

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

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

384
bool Reader::readCStyleComment() {
76×
385
  while ((current_ + 1) < end_) {
148×
386
    Char c = getNextChar();
76×
387
    if (c == '*' && *current_ == '/')
76×
388
      break;
4×
389
  }
390
  return getNextChar() == '/';
4×
391
}
392

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

409
void Reader::readNumber() {
20×
410
  Location p = current_;
20×
411
  char c = '0'; // stopgap for already consumed character
20×
412
  // integral part
413
  while (c >= '0' && c <= '9')
132×
414
    c = (current_ = p) < end_ ? *p++ : '\0';
56×
415
  // fractional part
416
  if (c == '.') {
20×
417
    c = (current_ = p) < end_ ? *p++ : '\0';
4×
418
    while (c >= '0' && c <= '9')
12×
419
      c = (current_ = p) < end_ ? *p++ : '\0';
4×
420
  }
421
  // exponential part
422
  if (c == 'e' || c == 'E') {
20×
423
    c = (current_ = p) < end_ ? *p++ : '\0';
4×
424
    if (c == '+' || c == '-')
4×
425
      c = (current_ = p) < end_ ? *p++ : '\0';
4×
426
    while (c >= '0' && c <= '9')
20×
427
      c = (current_ = p) < end_ ? *p++ : '\0';
8×
428
  }
429
}
20×
430

431
bool Reader::readString() {
192×
432
  Char c = '\0';
192×
433
  while (current_ != end_) {
2,136×
434
    c = getNextChar();
1,164×
435
    if (c == '\\')
1,164×
436
      getNextChar();
72×
437
    else if (c == '"')
1,092×
438
      break;
192×
439
  }
440
  return c == '"';
192×
441
}
442

443
bool Reader::readObject(Token& token) {
64×
444
  Token tokenName;
445
  String name;
128×
446
  Value init(objectValue);
128×
447
  currentValue().swapPayload(init);
64×
448
  currentValue().setOffsetStart(token.start_ - begin_);
64×
449
  while (readToken(tokenName)) {
104×
450
    bool initialTokenOk = true;
84×
451
    while (tokenName.type_ == tokenComment && initialTokenOk)
92×
452
      initialTokenOk = readToken(tokenName);
4×
453
    if (!initialTokenOk)
84×
454
      break;
4×
455
    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
84×
456
      return true;
60×
457
    name.clear();
84×
458
    if (tokenName.type_ == tokenString) {
84×
459
      if (!decodeString(tokenName, name))
76×
460
        return recoverFromError(tokenObjectEnd);
!
461
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
8×
462
      Value numberName;
8×
463
      if (!decodeNumber(tokenName, numberName))
4×
464
        return recoverFromError(tokenObjectEnd);
!
465
      name = numberName.asString();
4×
466
    } else {
467
      break;
468
    }
469

470
    Token colon;
471
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
80×
472
      return addErrorAndRecover("Missing ':' after object member name", colon,
8×
473
                                tokenObjectEnd);
4×
474
    }
475
    Value& value = currentValue()[name];
76×
476
    nodes_.push(&value);
76×
477
    bool ok = readValue();
76×
478
    nodes_.pop();
76×
479
    if (!ok) // error already set
76×
480
      return recoverFromError(tokenObjectEnd);
20×
481

482
    Token comma;
483
    if (!readToken(comma) ||
116×
484
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
84×
485
         comma.type_ != tokenComment)) {
4×
486
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
8×
487
                                comma, tokenObjectEnd);
4×
488
    }
489
    bool finalizeTokenOk = true;
52×
490
    while (comma.type_ == tokenComment && finalizeTokenOk)
52×
491
      finalizeTokenOk = readToken(comma);
!
492
    if (comma.type_ == tokenObjectEnd)
52×
493
      return true;
32×
494
  }
495
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
8×
496
                            tokenObjectEnd);
4×
497
}
498

499
bool Reader::readArray(Token& token) {
36×
500
  Value init(arrayValue);
72×
501
  currentValue().swapPayload(init);
36×
502
  currentValue().setOffsetStart(token.start_ - begin_);
36×
503
  skipSpaces();
36×
504
  if (current_ != end_ && *current_ == ']') // empty array
36×
505
  {
506
    Token endArray;
507
    readToken(endArray);
!
508
    return true;
!
509
  }
510
  int index = 0;
36×
511
  for (;;) {
40×
512
    Value& value = currentValue()[index++];
76×
513
    nodes_.push(&value);
76×
514
    bool ok = readValue();
76×
515
    nodes_.pop();
76×
516
    if (!ok) // error already set
76×
517
      return recoverFromError(tokenArrayEnd);
40×
518

519
    Token currentToken;
520
    // Accept Comment after last item in the array.
521
    ok = readToken(currentToken);
60×
522
    while (currentToken.type_ == tokenComment && ok) {
60×
523
      ok = readToken(currentToken);
!
524
    }
525
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
80×
526
                         currentToken.type_ != tokenArrayEnd);
80×
527
    if (!ok || badTokenType) {
60×
528
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
16×
529
                                currentToken, tokenArrayEnd);
8×
530
    }
531
    if (currentToken.type_ == tokenArrayEnd)
52×
532
      break;
12×
533
  }
534
  return true;
12×
535
}
536

537
bool Reader::decodeNumber(Token& token) {
16×
538
  Value decoded;
32×
539
  if (!decodeNumber(token, decoded))
16×
540
    return false;
!
541
  currentValue().swapPayload(decoded);
16×
542
  currentValue().setOffsetStart(token.start_ - begin_);
16×
543
  currentValue().setOffsetLimit(token.end_ - begin_);
16×
544
  return true;
16×
545
}
546

547
bool Reader::decodeNumber(Token& token, Value& decoded) {
20×
548
  // Attempts to parse the number as an integer. If the number is
549
  // larger than the maximum supported value of an integer then
550
  // we decode the number as a double.
551
  Location current = token.start_;
20×
552
  bool isNegative = *current == '-';
20×
553
  if (isNegative)
20×
554
    ++current;
4×
555
  // TODO: Help the compiler do the div and mod at compile time or get rid of
556
  // them.
557
  Value::LargestUInt maxIntegerValue =
558
      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
559
                 : Value::maxLargestUInt;
20×
560
  Value::LargestUInt threshold = maxIntegerValue / 10;
20×
561
  Value::LargestUInt value = 0;
20×
562
  while (current < token.end_) {
124×
563
    Char c = *current++;
56×
564
    if (c < '0' || c > '9')
56×
565
      return decodeDouble(token, decoded);
4×
566
    auto digit(static_cast<Value::UInt>(c - '0'));
52×
567
    if (value >= threshold) {
52×
568
      // We've hit or exceeded the max value divided by 10 (rounded down). If
569
      // a) we've only just touched the limit, b) this is the last digit, and
570
      // c) it's small enough to fit in that rounding delta, we're okay.
571
      // Otherwise treat this number as a double to avoid overflow.
572
      if (value > threshold || current != token.end_ ||
!
573
          digit > maxIntegerValue % 10) {
!
574
        return decodeDouble(token, decoded);
!
575
      }
576
    }
577
    value = value * 10 + digit;
52×
578
  }
579
  if (isNegative && value == maxIntegerValue)
16×
580
    decoded = Value::minLargestInt;
!
581
  else if (isNegative)
16×
582
    decoded = -Value::LargestInt(value);
!
583
  else if (value <= Value::LargestUInt(Value::maxInt))
16×
584
    decoded = Value::LargestInt(value);
16×
585
  else
586
    decoded = value;
!
587
  return true;
16×
588
}
589

590
bool Reader::decodeDouble(Token& token) {
!
591
  Value decoded;
!
592
  if (!decodeDouble(token, decoded))
!
593
    return false;
!
594
  currentValue().swapPayload(decoded);
!
595
  currentValue().setOffsetStart(token.start_ - begin_);
!
596
  currentValue().setOffsetLimit(token.end_ - begin_);
!
597
  return true;
!
598
}
599

600
bool Reader::decodeDouble(Token& token, Value& decoded) {
4×
601
  double value = 0;
4×
602
  String buffer(token.start_, token.end_);
8×
603
  IStringStream is(buffer);
8×
604
  if (!(is >> value)) {
4×
NEW
605
    if (value == std::numeric_limits<double>::max())
!
NEW
606
      value = std::numeric_limits<double>::infinity();
!
NEW
607
    else if (value == std::numeric_limits<double>::lowest())
!
NEW
608
      value = -std::numeric_limits<double>::infinity();
!
NEW
609
    else if (!std::isinf(value))
!
610
      return addError(
611
        "'" + String(token.start_, token.end_) + "' is not a number.", token);
!
612
  }
613
  decoded = value;
4×
614
  return true;
4×
615
}
616

617
bool Reader::decodeString(Token& token) {
104×
618
  String decoded_string;
208×
619
  if (!decodeString(token, decoded_string))
104×
620
    return false;
20×
621
  Value decoded(decoded_string);
168×
622
  currentValue().swapPayload(decoded);
84×
623
  currentValue().setOffsetStart(token.start_ - begin_);
84×
624
  currentValue().setOffsetLimit(token.end_ - begin_);
84×
625
  return true;
84×
626
}
627

628
bool Reader::decodeString(Token& token, String& decoded) {
180×
629
  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
180×
630
  Location current = token.start_ + 1; // skip '"'
180×
631
  Location end = token.end_ - 1;       // do not include '"'
180×
632
  while (current != end) {
1,636×
633
    Char c = *current++;
748×
634
    if (c == '"')
748×
635
      break;
!
636
    if (c == '\\') {
748×
637
      if (current == end)
64×
638
        return addError("Empty escape sequence in string", token, current);
!
639
      Char escape = *current++;
64×
640
      switch (escape) {
64×
641
      case '"':
642
        decoded += '"';
4×
643
        break;
4×
644
      case '/':
645
        decoded += '/';
4×
646
        break;
4×
647
      case '\\':
648
        decoded += '\\';
4×
649
        break;
4×
650
      case 'b':
651
        decoded += '\b';
4×
652
        break;
4×
653
      case 'f':
654
        decoded += '\f';
4×
655
        break;
4×
656
      case 'n':
657
        decoded += '\n';
4×
658
        break;
4×
659
      case 'r':
660
        decoded += '\r';
4×
661
        break;
4×
662
      case 't':
663
        decoded += '\t';
4×
664
        break;
4×
665
      case 'u': {
666
        unsigned int unicode;
667
        if (!decodeUnicodeCodePoint(token, current, end, unicode))
28×
668
          return false;
16×
669
        decoded += codePointToUTF8(unicode);
12×
670
      } break;
12×
671
      default:
672
        return addError("Bad escape sequence in string", token, current);
4×
673
      }
674
    } else {
675
      decoded += c;
684×
676
    }
677
  }
678
  return true;
160×
679
}
680

681
bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
28×
682
                                    Location end, unsigned int& unicode) {
683

684
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
28×
685
    return false;
8×
686
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
20×
687
    // surrogate pairs
688
    if (end - current < 6)
12×
689
      return addError(
8×
690
          "additional six characters expected to parse unicode surrogate pair.",
691
          token, current);
4×
692
    if (*(current++) == '\\' && *(current++) == 'u') {
8×
693
      unsigned int surrogatePair;
694
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
4×
695
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
4×
696
      } else
697
        return false;
!
698
    } else
699
      return addError("expecting another \\u token to begin the second half of "
8×
700
                      "a unicode surrogate pair",
701
                      token, current);
4×
702
  }
703
  return true;
12×
704
}
705

706
bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
32×
707
                                         Location end,
708
                                         unsigned int& ret_unicode) {
709
  if (end - current < 4)
32×
710
    return addError(
8×
711
        "Bad unicode escape sequence in string: four digits expected.", token,
712
        current);
4×
713
  int unicode = 0;
28×
714
  for (int index = 0; index < 4; ++index) {
132×
715
    Char c = *current++;
108×
716
    unicode *= 16;
108×
717
    if (c >= '0' && c <= '9')
108×
718
      unicode += c - '0';
72×
719
    else if (c >= 'a' && c <= 'f')
36×
720
      unicode += c - 'a' + 10;
32×
721
    else if (c >= 'A' && c <= 'F')
4×
722
      unicode += c - 'A' + 10;
!
723
    else
724
      return addError(
8×
725
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
726
          token, current);
4×
727
  }
728
  ret_unicode = static_cast<unsigned int>(unicode);
24×
729
  return true;
24×
730
}
731

732
bool Reader::addError(const String& message, Token& token, Location extra) {
64×
733
  ErrorInfo info;
128×
734
  info.token_ = token;
64×
735
  info.message_ = message;
64×
736
  info.extra_ = extra;
64×
737
  errors_.push_back(info);
64×
738
  return false;
128×
739
}
740

741
bool Reader::recoverFromError(TokenType skipUntilToken) {
56×
742
  size_t const errorCount = errors_.size();
56×
743
  Token skip;
744
  for (;;) {
48×
745
    if (!readToken(skip))
104×
746
      errors_.resize(errorCount); // discard errors caused by recovery
40×
747
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
104×
748
      break;
749
  }
750
  errors_.resize(errorCount);
56×
751
  return false;
56×
752
}
753

754
bool Reader::addErrorAndRecover(const String& message, Token& token,
20×
755
                                TokenType skipUntilToken) {
756
  addError(message, token);
20×
757
  return recoverFromError(skipUntilToken);
20×
758
}
759

760
Value& Reader::currentValue() { return *(nodes_.top()); }
1,076×
761

762
Reader::Char Reader::getNextChar() {
2,284×
763
  if (current_ == end_)
2,284×
764
    return 0;
132×
765
  return *current_++;
2,152×
766
}
767

768
void Reader::getLocationLineAndColumn(Location location, int& line,
96×
769
                                      int& column) const {
770
  Location current = begin_;
96×
771
  Location lastLineStart = current;
96×
772
  line = 0;
96×
773
  while (current < location && current != end_) {
2,008×
774
    Char c = *current++;
956×
775
    if (c == '\r') {
956×
776
      if (*current == '\n')
!
777
        ++current;
!
778
      lastLineStart = current;
!
779
      ++line;
!
780
    } else if (c == '\n') {
956×
781
      lastLineStart = current;
!
782
      ++line;
!
783
    }
784
  }
785
  // column & line start at 1
786
  column = int(location - lastLineStart) + 1;
96×
787
  ++line;
96×
788
}
96×
789

790
String Reader::getLocationLineAndColumn(Location location) const {
96×
791
  int line, column;
792
  getLocationLineAndColumn(location, line, column);
96×
793
  char buffer[18 + 16 + 16 + 1];
794
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
96×
795
  return buffer;
96×
796
}
797

798
// Deprecated. Preserved for backward compatibility
799
String Reader::getFormatedErrorMessages() const {
!
800
  return getFormattedErrorMessages();
!
801
}
802

803
String Reader::getFormattedErrorMessages() const {
76×
804
  String formattedMessage;
76×
805
  for (const auto& error : errors_) {
148×
806
    formattedMessage +=
807
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
72×
808
    formattedMessage += "  " + error.message_ + "\n";
72×
809
    if (error.extra_)
72×
810
      formattedMessage +=
811
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
24×
812
  }
813
  return formattedMessage;
76×
814
}
815

816
std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
68×
817
  std::vector<Reader::StructuredError> allErrors;
68×
818
  for (const auto& error : errors_) {
132×
819
    Reader::StructuredError structured;
128×
820
    structured.offset_start = error.token_.start_ - begin_;
64×
821
    structured.offset_limit = error.token_.end_ - begin_;
64×
822
    structured.message = error.message_;
64×
823
    allErrors.push_back(structured);
64×
824
  }
825
  return allErrors;
68×
826
}
827

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

844
bool Reader::pushError(const Value& value, const String& message,
4×
845
                       const Value& extra) {
846
  ptrdiff_t const length = end_ - begin_;
4×
847
  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
8×
848
      extra.getOffsetLimit() > length)
4×
849
    return false;
!
850
  Token token;
851
  token.type_ = tokenError;
4×
852
  token.start_ = begin_ + value.getOffsetStart();
4×
853
  token.end_ = begin_ + value.getOffsetLimit();
4×
854
  ErrorInfo info;
8×
855
  info.token_ = token;
4×
856
  info.message_ = message;
4×
857
  info.extra_ = begin_ + extra.getOffsetStart();
4×
858
  errors_.push_back(info);
4×
859
  return true;
4×
860
}
861

862
bool Reader::good() const { return errors_.empty(); }
!
863

864
// Originally copied from the Features class (now deprecated), used internally
865
// for features implementation.
866
class OurFeatures {
867
public:
868
  static OurFeatures all();
869
  bool allowComments_;
870
  bool allowTrailingCommas_;
871
  bool strictRoot_;
872
  bool allowDroppedNullPlaceholders_;
873
  bool allowNumericKeys_;
874
  bool allowSingleQuotes_;
875
  bool failIfExtra_;
876
  bool rejectDupKeys_;
877
  bool allowSpecialFloats_;
878
  bool skipBom_;
879
  size_t stackLimit_;
880
}; // OurFeatures
881

882
OurFeatures OurFeatures::all() { return {}; }
196×
883

884
// Implementation of class Reader
885
// ////////////////////////////////
886

887
// Originally copied from the Reader class (now deprecated), used internally
888
// for implementing JSON reading.
889
class OurReader {
196×
890
public:
891
  using Char = char;
892
  using Location = const Char*;
893
  struct StructuredError {
!
894
    ptrdiff_t offset_start;
895
    ptrdiff_t offset_limit;
896
    String message;
897
  };
898

899
  explicit OurReader(OurFeatures const& features);
900
  bool parse(const char* beginDoc, const char* endDoc, Value& root,
901
             bool collectComments = true);
902
  String getFormattedErrorMessages() const;
903
  std::vector<StructuredError> getStructuredErrors() const;
904

905
private:
906
  OurReader(OurReader const&);      // no impl
907
  void operator=(OurReader const&); // no impl
908

909
  enum TokenType {
910
    tokenEndOfStream = 0,
911
    tokenObjectBegin,
912
    tokenObjectEnd,
913
    tokenArrayBegin,
914
    tokenArrayEnd,
915
    tokenString,
916
    tokenNumber,
917
    tokenTrue,
918
    tokenFalse,
919
    tokenNull,
920
    tokenNaN,
921
    tokenPosInf,
922
    tokenNegInf,
923
    tokenArraySeparator,
924
    tokenMemberSeparator,
925
    tokenComment,
926
    tokenError
927
  };
928

929
  class Token {
930
  public:
931
    TokenType type_;
932
    Location start_;
933
    Location end_;
934
  };
935

936
  class ErrorInfo {
560×
937
  public:
938
    Token token_;
939
    String message_;
940
    Location extra_;
941
  };
942

943
  using Errors = std::deque<ErrorInfo>;
944

945
  bool readToken(Token& token);
946
  void skipSpaces();
947
  void skipBom(bool skipBom);
948
  bool match(const Char* pattern, int patternLength);
949
  bool readComment();
950
  bool readCStyleComment(bool* containsNewLineResult);
951
  bool readCppStyleComment();
952
  bool readString();
953
  bool readStringSingleQuote();
954
  bool readNumber(bool checkInf);
955
  bool readValue();
956
  bool readObject(Token& token);
957
  bool readArray(Token& token);
958
  bool decodeNumber(Token& token);
959
  bool decodeNumber(Token& token, Value& decoded);
960
  bool decodeString(Token& token);
961
  bool decodeString(Token& token, String& decoded);
962
  bool decodeDouble(Token& token);
963
  bool decodeDouble(Token& token, Value& decoded);
964
  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
965
                              unsigned int& unicode);
966
  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
967
                                   Location end, unsigned int& unicode);
968
  bool addError(const String& message, Token& token, Location extra = nullptr);
969
  bool recoverFromError(TokenType skipUntilToken);
970
  bool addErrorAndRecover(const String& message, Token& token,
971
                          TokenType skipUntilToken);
972
  void skipUntilSpace();
973
  Value& currentValue();
974
  Char getNextChar();
975
  void getLocationLineAndColumn(Location location, int& line,
976
                                int& column) const;
977
  String getLocationLineAndColumn(Location location) const;
978
  void addComment(Location begin, Location end, CommentPlacement placement);
979
  void skipCommentTokens(Token& token);
980

981
  static String normalizeEOL(Location begin, Location end);
982
  static bool containsNewLine(Location begin, Location end);
983

984
  using Nodes = std::stack<Value*>;
985

986
  Nodes nodes_{};
987
  Errors errors_{};
988
  String document_{};
989
  Location begin_ = nullptr;
990
  Location end_ = nullptr;
991
  Location current_ = nullptr;
992
  Location lastValueEnd_ = nullptr;
993
  Value* lastValue_ = nullptr;
994
  bool lastValueHasAComment_ = false;
995
  String commentsBefore_{};
996

997
  OurFeatures const features_;
998
  bool collectComments_ = false;
999
}; // OurReader
1000

1001
// complete copy of Read impl, for OurReader
1002

1003
bool OurReader::containsNewLine(OurReader::Location begin,
44×
1004
                                OurReader::Location end) {
1005
  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
88×
1006
}
1007

1008
OurReader::OurReader(OurFeatures const& features) : features_(features) {}
196×
1009

1010
bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
324×
1011
                      bool collectComments) {
1012
  if (!features_.allowComments_) {
324×
1013
    collectComments = false;
12×
1014
  }
1015

1016
  begin_ = beginDoc;
324×
1017
  end_ = endDoc;
324×
1018
  collectComments_ = collectComments;
324×
1019
  current_ = begin_;
324×
1020
  lastValueEnd_ = nullptr;
324×
1021
  lastValue_ = nullptr;
324×
1022
  commentsBefore_.clear();
324×
1023
  errors_.clear();
324×
1024
  while (!nodes_.empty())
324×
1025
    nodes_.pop();
!
1026
  nodes_.push(&root);
324×
1027

1028
  // skip byte order mark if it exists at the beginning of the UTF-8 text.
1029
  skipBom(features_.skipBom_);
324×
1030
  bool successful = readValue();
324×
1031
  nodes_.pop();
320×
1032
  Token token;
1033
  skipCommentTokens(token);
320×
1034
  if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
320×
1035
    addError("Extra non-whitespace after JSON value.", token);
20×
1036
    return false;
20×
1037
  }
1038
  if (collectComments_ && !commentsBefore_.empty())
300×
1039
    root.setComment(commentsBefore_, commentAfter);
20×
1040
  if (features_.strictRoot_) {
300×
1041
    if (!root.isArray() && !root.isObject()) {
8×
1042
      // Set error location to start of doc, ideally should be first token found
1043
      // in doc
1044
      token.type_ = tokenError;
4×
1045
      token.start_ = beginDoc;
4×
1046
      token.end_ = endDoc;
4×
1047
      addError(
8×
1048
          "A valid JSON document must be either an array or an object value.",
1049
          token);
4×
1050
      return false;
4×
1051
    }
1052
  }
1053
  return successful;
296×
1054
}
1055

1056
bool OurReader::readValue() {
824×
1057
  //  To preserve the old behaviour we cast size_t to int.
1058
  if (nodes_.size() > features_.stackLimit_)
824×
1059
    throwRuntimeError("Exceeded stackLimit in readValue().");
8×
1060
  Token token;
1061
  skipCommentTokens(token);
820×
1062
  bool successful = true;
820×
1063

1064
  if (collectComments_ && !commentsBefore_.empty()) {
820×
1065
    currentValue().setComment(commentsBefore_, commentBefore);
20×
1066
    commentsBefore_.clear();
20×
1067
  }
1068

1069
  switch (token.type_) {
820×
1070
  case tokenObjectBegin:
1071
    successful = readObject(token);
188×
1072
    currentValue().setOffsetLimit(current_ - begin_);
184×
1073
    break;
184×
1074
  case tokenArrayBegin:
1075
    successful = readArray(token);
116×
1076
    currentValue().setOffsetLimit(current_ - begin_);
116×
1077
    break;
116×
1078
  case tokenNumber:
1079
    successful = decodeNumber(token);
52×
1080
    break;
52×
1081
  case tokenString:
1082
    successful = decodeString(token);
180×
1083
    break;
180×
1084
  case tokenTrue: {
1085
    Value v(true);
120×
1086
    currentValue().swapPayload(v);
60×
1087
    currentValue().setOffsetStart(token.start_ - begin_);
60×
1088
    currentValue().setOffsetLimit(token.end_ - begin_);
60×
1089
  } break;
60×
1090
  case tokenFalse: {
1091
    Value v(false);
8×
1092
    currentValue().swapPayload(v);
4×
1093
    currentValue().setOffsetStart(token.start_ - begin_);
4×
1094
    currentValue().setOffsetLimit(token.end_ - begin_);
4×
1095
  } break;
4×
1096
  case tokenNull: {
1097
    Value v;
56×
1098
    currentValue().swapPayload(v);
28×
1099
    currentValue().setOffsetStart(token.start_ - begin_);
28×
1100
    currentValue().setOffsetLimit(token.end_ - begin_);
28×
1101
  } break;
28×
1102
  case tokenNaN: {
1103
    Value v(std::numeric_limits<double>::quiet_NaN());
8×
1104
    currentValue().swapPayload(v);
4×
1105
    currentValue().setOffsetStart(token.start_ - begin_);
4×
1106
    currentValue().setOffsetLimit(token.end_ - begin_);
4×
1107
  } break;
4×
1108
  case tokenPosInf: {
1109
    Value v(std::numeric_limits<double>::infinity());
40×
1110
    currentValue().swapPayload(v);
20×
1111
    currentValue().setOffsetStart(token.start_ - begin_);
20×
1112
    currentValue().setOffsetLimit(token.end_ - begin_);
20×
1113
  } break;
20×
1114
  case tokenNegInf: {
1115
    Value v(-std::numeric_limits<double>::infinity());
24×
1116
    currentValue().swapPayload(v);
12×
1117
    currentValue().setOffsetStart(token.start_ - begin_);
12×
1118
    currentValue().setOffsetLimit(token.end_ - begin_);
12×
1119
  } break;
12×
1120
  case tokenArraySeparator:
1121
  case tokenObjectEnd:
1122
  case tokenArrayEnd:
1123
    if (features_.allowDroppedNullPlaceholders_) {
116×
1124
      // "Un-read" the current token and mark the current value as a null
1125
      // token.
1126
      current_--;
116×
1127
      Value v;
232×
1128
      currentValue().swapPayload(v);
116×
1129
      currentValue().setOffsetStart(current_ - begin_ - 1);
116×
1130
      currentValue().setOffsetLimit(current_ - begin_);
116×
1131
      break;
116×
1132
    } // else, fall through ...
1133
  default:
1134
    currentValue().setOffsetStart(token.start_ - begin_);
40×
1135
    currentValue().setOffsetLimit(token.end_ - begin_);
40×
1136
    return addError("Syntax error: value, object or array expected.", token);
40×
1137
  }
1138

1139
  if (collectComments_) {
776×
1140
    lastValueEnd_ = current_;
756×
1141
    lastValueHasAComment_ = false;
756×
1142
    lastValue_ = &currentValue();
756×
1143
  }
1144

1145
  return successful;
776×
1146
}
1147

1148
void OurReader::skipCommentTokens(Token& token) {
1,140×
1149
  if (features_.allowComments_) {
1,140×
1150
    do {
64×
1151
      readToken(token);
1,172×
1152
    } while (token.type_ == tokenComment);
1,172×
1153
  } else {
1154
    readToken(token);
32×
1155
  }
1156
}
1,140×
1157

1158
bool OurReader::readToken(Token& token) {
2,592×
1159
  skipSpaces();
2,592×
1160
  token.start_ = current_;
2,592×
1161
  Char c = getNextChar();
2,592×
1162
  bool ok = true;
2,592×
1163
  switch (c) {
2,592×
1164
  case '{':
1165
    token.type_ = tokenObjectBegin;
188×
1166
    break;
188×
1167
  case '}':
1168
    token.type_ = tokenObjectEnd;
180×
1169
    break;
180×
1170
  case '[':
1171
    token.type_ = tokenArrayBegin;
116×
1172
    break;
116×
1173
  case ']':
1174
    token.type_ = tokenArrayEnd;
144×
1175
    break;
144×
1176
  case '"':
1177
    token.type_ = tokenString;
412×
1178
    ok = readString();
412×
1179
    break;
412×
1180
  case '\'':
1181
    if (features_.allowSingleQuotes_) {
44×
1182
      token.type_ = tokenString;
44×
1183
      ok = readStringSingleQuote();
44×
1184
    } else {
1185
      // If we don't allow single quotes, this is a failure case.
1186
      ok = false;
!
1187
    }
1188
    break;
44×
1189
  case '/':
1190
    token.type_ = tokenComment;
84×
1191
    ok = readComment();
84×
1192
    break;
84×
1193
  case '0':
1194
  case '1':
1195
  case '2':
1196
  case '3':
1197
  case '4':
1198
  case '5':
1199
  case '6':
1200
  case '7':
1201
  case '8':
1202
  case '9':
1203
    token.type_ = tokenNumber;
48×
1204
    readNumber(false);
48×
1205
    break;
48×
1206
  case '-':
1207
    if (readNumber(true)) {
24×
1208
      token.type_ = tokenNumber;
12×
1209
    } else {
1210
      token.type_ = tokenNegInf;
12×
1211
      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
12×
1212
    }
1213
    break;
24×
1214
  case '+':
1215
    if (readNumber(true)) {
16×
1216
      token.type_ = tokenNumber;
4×
1217
    } else {
1218
      token.type_ = tokenPosInf;
12×
1219
      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
12×
1220
    }
1221
    break;
16×
1222
  case 't':
1223
    token.type_ = tokenTrue;
96×
1224
    ok = match("rue", 3);
96×
1225
    break;
96×
1226
  case 'f':
1227
    token.type_ = tokenFalse;
40×
1228
    ok = match("alse", 4);
40×
1229
    break;
40×
1230
  case 'n':
1231
    token.type_ = tokenNull;
100×
1232
    ok = match("ull", 3);
100×
1233
    break;
100×
1234
  case 'N':
1235
    if (features_.allowSpecialFloats_) {
12×
1236
      token.type_ = tokenNaN;
4×
1237
      ok = match("aN", 2);
4×
1238
    } else {
1239
      ok = false;
8×
1240
    }
1241
    break;
12×
1242
  case 'I':
1243
    if (features_.allowSpecialFloats_) {
32×
1244
      token.type_ = tokenPosInf;
28×
1245
      ok = match("nfinity", 7);
28×
1246
    } else {
1247
      ok = false;
4×
1248
    }
1249
    break;
32×
1250
  case ',':
1251
    token.type_ = tokenArraySeparator;
304×
1252
    break;
304×
1253
  case ':':
1254
    token.type_ = tokenMemberSeparator;
296×
1255
    break;
296×
1256
  case 0:
1257
    token.type_ = tokenEndOfStream;
312×
1258
    break;
312×
1259
  default:
1260
    ok = false;
144×
1261
    break;
144×
1262
  }
1263
  if (!ok)
2,592×
1264
    token.type_ = tokenError;
300×
1265
  token.end_ = current_;
2,592×
1266
  return ok;
2,592×
1267
}
1268

1269
void OurReader::skipSpaces() {
3,436×
1270
  while (current_ != end_) {
4,024×
1271
    Char c = *current_;
3,124×
1272
    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
3,124×
1273
      ++current_;
588×
1274
    else
1275
      break;
1276
  }
1277
}
2,848×
1278

1279
void OurReader::skipBom(bool skipBom) {
324×
1280
  // The default behavior is to skip BOM.
1281
  if (skipBom) {
324×
1282
    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
320×
1283
      begin_ += 3;
4×
1284
      current_ = begin_;
4×
1285
    }
1286
  }
1287
}
324×
1288

1289
bool OurReader::match(const Char* pattern, int patternLength) {
292×
1290
  if (end_ - current_ < patternLength)
292×
1291
    return false;
36×
1292
  int index = patternLength;
256×
1293
  while (index--)
1,560×
1294
    if (current_[index] != pattern[index])
760×
1295
      return false;
108×
1296
  current_ += patternLength;
148×
1297
  return true;
148×
1298
}
1299

1300
bool OurReader::readComment() {
84×
1301
  const Location commentBegin = current_ - 1;
84×
1302
  const Char c = getNextChar();
84×
1303
  bool successful = false;
84×
1304
  bool cStyleWithEmbeddedNewline = false;
84×
1305

1306
  const bool isCStyleComment = (c == '*');
84×
1307
  const bool isCppStyleComment = (c == '/');
84×
1308
  if (isCStyleComment) {
84×
1309
    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
4×
1310
  } else if (isCppStyleComment) {
80×
1311
    successful = readCppStyleComment();
80×
1312
  }
1313

1314
  if (!successful)
84×
1315
    return false;
!
1316

1317
  if (collectComments_) {
84×
1318
    CommentPlacement placement = commentBefore;
84×
1319

1320
    if (!lastValueHasAComment_) {
84×
1321
      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
60×
1322
        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
44×
1323
          placement = commentAfterOnSameLine;
40×
1324
          lastValueHasAComment_ = true;
40×
1325
        }
1326
      }
1327
    }
1328

1329
    addComment(commentBegin, current_, placement);
84×
1330
  }
1331
  return true;
84×
1332
}
1333

1334
String OurReader::normalizeEOL(OurReader::Location begin,
84×
1335
                               OurReader::Location end) {
1336
  String normalized;
84×
1337
  normalized.reserve(static_cast<size_t>(end - begin));
84×
1338
  OurReader::Location current = begin;
84×
1339
  while (current != end) {
1,900×
1340
    char c = *current++;
908×
1341
    if (c == '\r') {
908×
1342
      if (current != end && *current == '\n')
12×
1343
        // convert dos EOL
1344
        ++current;
4×
1345
      // convert Mac EOL
1346
      normalized += '\n';
12×
1347
    } else {
1348
      normalized += c;
896×
1349
    }
1350
  }
1351
  return normalized;
84×
1352
}
1353

1354
void OurReader::addComment(Location begin, Location end,
84×
1355
                           CommentPlacement placement) {
1356
  assert(collectComments_);
84×
1357
  const String& normalized = normalizeEOL(begin, end);
168×
1358
  if (placement == commentAfterOnSameLine) {
84×
1359
    assert(lastValue_ != nullptr);
40×
1360
    lastValue_->setComment(normalized, placement);
40×
1361
  } else {
1362
    commentsBefore_ += normalized;
44×
1363
  }
1364
}
84×
1365

1366
bool OurReader::readCStyleComment(bool* containsNewLineResult) {
4×
1367
  *containsNewLineResult = false;
4×
1368

1369
  while ((current_ + 1) < end_) {
132×
1370
    Char c = getNextChar();
68×
1371
    if (c == '*' && *current_ == '/')
68×
1372
      break;
4×
1373
    if (c == '\n')
64×
1374
      *containsNewLineResult = true;
4×
1375
  }
1376

1377
  return getNextChar() == '/';
4×
1378
}
1379

1380
bool OurReader::readCppStyleComment() {
668×
1381
  while (current_ != end_) {
1,256×
1382
    Char c = getNextChar();
668×
1383
    if (c == '\n')
668×
1384
      break;
68×
1385
    if (c == '\r') {
600×
1386
      // Consume DOS EOL. It will be normalized in addComment.
1387
      if (current_ != end_ && *current_ == '\n')
12×
1388
        getNextChar();
4×
1389
      // Break on Moc OS 9 EOL.
1390
      break;
12×
1391
    }
1392
  }
1393
  return true;
80×
1394
}
1395

1396
bool OurReader::readNumber(bool checkInf) {
88×
1397
  Location p = current_;
88×
1398
  if (checkInf && p != end_ && *p == 'I') {
88×
1399
    current_ = ++p;
24×
1400
    return false;
24×
1401
  }
1402
  char c = '0'; // stopgap for already consumed character
64×
1403
  // integral part
1404
  while (c >= '0' && c <= '9')
416×
1405
    c = (current_ = p) < end_ ? *p++ : '\0';
176×
1406
  // fractional part
1407
  if (c == '.') {
64×
1408
    c = (current_ = p) < end_ ? *p++ : '\0';
8×
1409
    while (c >= '0' && c <= '9')
32×
1410
      c = (current_ = p) < end_ ? *p++ : '\0';
12×
1411
  }
1412
  // exponential part
1413
  if (c == 'e' || c == 'E') {
64×
1414
    c = (current_ = p) < end_ ? *p++ : '\0';
4×
1415
    if (c == '+' || c == '-')
4×
1416
      c = (current_ = p) < end_ ? *p++ : '\0';
4×
1417
    while (c >= '0' && c <= '9')
20×
1418
      c = (current_ = p) < end_ ? *p++ : '\0';
8×
1419
  }
1420
  return true;
64×
1421
}
1422
bool OurReader::readString() {
412×
1423
  Char c = 0;
412×
1424
  while (current_ != end_) {
3,740×
1425
    c = getNextChar();
2,076×
1426
    if (c == '\\')
2,076×
1427
      getNextChar();
84×
1428
    else if (c == '"')
1,992×
1429
      break;
412×
1430
  }
1431
  return c == '"';
412×
1432
}
1433

1434
bool OurReader::readStringSingleQuote() {
44×
1435
  Char c = 0;
44×
1436
  while (current_ != end_) {
164×
1437
    c = getNextChar();
104×
1438
    if (c == '\\')
104×
1439
      getNextChar();
8×
1440
    else if (c == '\'')
96×
1441
      break;
44×
1442
  }
1443
  return c == '\'';
44×
1444
}
1445

1446
bool OurReader::readObject(Token& token) {
188×
1447
  Token tokenName;
1448
  String name;
376×
1449
  Value init(objectValue);
376×
1450
  currentValue().swapPayload(init);
188×
1451
  currentValue().setOffsetStart(token.start_ - begin_);
188×
1452
  while (readToken(tokenName)) {
356×
1453
    bool initialTokenOk = true;
272×
1454
    while (tokenName.type_ == tokenComment && initialTokenOk)
280×
1455
      initialTokenOk = readToken(tokenName);
4×
1456
    if (!initialTokenOk)
272×
1457
      break;
4×
1458
    if (tokenName.type_ == tokenObjectEnd &&
272×
1459
        (name.empty() ||
!
1460
         features_.allowTrailingCommas_)) // empty object or trailing comma
!
1461
      return true;
180×
1462
    name.clear();
272×
1463
    if (tokenName.type_ == tokenString) {
272×
1464
      if (!decodeString(tokenName, name))
256×
1465
        return recoverFromError(tokenObjectEnd);
!
1466
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
16×
1467
      Value numberName;
24×
1468
      if (!decodeNumber(tokenName, numberName))
12×
1469
        return recoverFromError(tokenObjectEnd);
!
1470
      name = numberName.asString();
12×
1471
    } else {
1472
      break;
1473
    }
1474
    if (name.length() >= (1U << 30))
268×
1475
      throwRuntimeError("keylength >= 2^30");
!
1476
    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
268×
1477
      String msg = "Duplicate key: '" + name + "'";
8×
1478
      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
4×
1479
    }
1480

1481
    Token colon;
1482
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
264×
1483
      return addErrorAndRecover("Missing ':' after object member name", colon,
8×
1484
                                tokenObjectEnd);
4×
1485
    }
1486
    Value& value = currentValue()[name];
260×
1487
    nodes_.push(&value);
260×
1488
    bool ok = readValue();
260×
1489
    nodes_.pop();
256×
1490
    if (!ok) // error already set
256×
1491
      return recoverFromError(tokenObjectEnd);
40×
1492

1493
    Token comma;
1494
    if (!readToken(comma) ||
448×
1495
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
320×
1496
         comma.type_ != tokenComment)) {
20×
1497
      return addErrorAndRecover("Missing ',' or '}' in object declaration",
72×
1498
                                comma, tokenObjectEnd);
36×
1499
    }
1500
    bool finalizeTokenOk = true;
180×
1501
    while (comma.type_ == tokenComment && finalizeTokenOk)
188×
1502
      finalizeTokenOk = readToken(comma);
4×
1503
    if (comma.type_ == tokenObjectEnd)
180×
1504
      return true;
96×
1505
  }
1506
  return addErrorAndRecover("Missing '}' or object member name", tokenName,
8×
1507
                            tokenObjectEnd);
4×
1508
}
1509

1510
bool OurReader::readArray(Token& token) {
116×
1511
  Value init(arrayValue);
232×
1512
  currentValue().swapPayload(init);
116×
1513
  currentValue().setOffsetStart(token.start_ - begin_);
116×
1514
  int index = 0;
116×
1515
  for (;;) {
140×
1516
    skipSpaces();
256×
1517
    if (current_ != end_ && *current_ == ']' &&
256×
1518
        (index == 0 ||
32×
1519
         (features_.allowTrailingCommas_ &&
64×
1520
          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
32×
1521
                                                      // comma
1522
    {
1523
      Token endArray;
1524
      readToken(endArray);
16×
1525
      return true;
16×
1526
    }
1527
    Value& value = currentValue()[index++];
240×
1528
    nodes_.push(&value);
240×
1529
    bool ok = readValue();
240×
1530
    nodes_.pop();
240×
1531
    if (!ok) // error already set
240×
1532
      return recoverFromError(tokenArrayEnd);
16×
1533

1534
    Token currentToken;
1535
    // Accept Comment after last item in the array.
1536
    ok = readToken(currentToken);
224×
1537
    while (currentToken.type_ == tokenComment && ok) {
240×
1538
      ok = readToken(currentToken);
8×
1539
    }
1540
    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
308×
1541
                         currentToken.type_ != tokenArrayEnd);
308×
1542
    if (!ok || badTokenType) {
224×
1543
      return addErrorAndRecover("Missing ',' or ']' in array declaration",
16×
1544
                                currentToken, tokenArrayEnd);
8×
1545
    }
1546
    if (currentToken.type_ == tokenArrayEnd)
216×
1547
      break;
76×
1548
  }
1549
  return true;
76×
1550
}
1551

1552
bool OurReader::decodeNumber(Token& token) {
52×
1553
  Value decoded;
104×
1554
  if (!decodeNumber(token, decoded))
52×
1555
    return false;
!
1556
  currentValue().swapPayload(decoded);
52×
1557
  currentValue().setOffsetStart(token.start_ - begin_);
52×
1558
  currentValue().setOffsetLimit(token.end_ - begin_);
52×
1559
  return true;
52×
1560
}
1561

1562
bool OurReader::decodeNumber(Token& token, Value& decoded) {
64×
1563
  // Attempts to parse the number as an integer. If the number is
1564
  // larger than the maximum supported value of an integer then
1565
  // we decode the number as a double.
1566
  Location current = token.start_;
64×
1567
  const bool isNegative = *current == '-';
64×
1568
  if (isNegative) {
64×
1569
    ++current;
12×
1570
  }
1571

1572
  // We assume we can represent the largest and smallest integer types as
1573
  // unsigned integers with separate sign. This is only true if they can fit
1574
  // into an unsigned integer.
1575
  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1576
                "Int must be smaller than UInt");
1577

1578
  // We need to convert minLargestInt into a positive number. The easiest way
1579
  // to do this conversion is to assume our "threshold" value of minLargestInt
1580
  // divided by 10 can fit in maxLargestInt when absolute valued. This should
1581
  // be a safe assumption.
1582
  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1583
                "The absolute value of minLargestInt must be greater than or "
1584
                "equal to maxLargestInt");
1585
  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1586
                "The absolute value of minLargestInt must be only 1 magnitude "
1587
                "larger than maxLargest Int");
1588

1589
  static constexpr Value::LargestUInt positive_threshold =
1590
      Value::maxLargestUInt / 10;
1591
  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1592

1593
  // For the negative values, we have to be more careful. Since typically
1594
  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
1595
  // then take the inverse. This assumes that minLargestInt is only a single
1596
  // power of 10 different in magnitude, which we check above. For the last
1597
  // digit, we take the modulus before negating for the same reason.
1598
  static constexpr auto negative_threshold =
1599
      Value::LargestUInt(-(Value::minLargestInt / 10));
1600
  static constexpr auto negative_last_digit =
1601
      Value::UInt(-(Value::minLargestInt % 10));
1602

1603
  const Value::LargestUInt threshold =
1604
      isNegative ? negative_threshold : positive_threshold;
64×
1605
  const Value::UInt max_last_digit =
1606
      isNegative ? negative_last_digit : positive_last_digit;
64×
1607

1608
  Value::LargestUInt value = 0;
64×
1609
  while (current < token.end_) {
352×
1610
    Char c = *current++;
160×
1611
    if (c < '0' || c > '9')
160×
1612
      return decodeDouble(token, decoded);
12×
1613

1614
    const auto digit(static_cast<Value::UInt>(c - '0'));
148×
1615
    if (value >= threshold) {
148×
1616
      // We've hit or exceeded the max value divided by 10 (rounded down). If
1617
      // a) we've only just touched the limit, meaing value == threshold,
1618
      // b) this is the last digit, or
1619
      // c) it's small enough to fit in that rounding delta, we're okay.
1620
      // Otherwise treat this number as a double to avoid overflow.
1621
      if (value > threshold || current != token.end_ ||
4×
1622
          digit > max_last_digit) {
1623
        return decodeDouble(token, decoded);
4×
1624
      }
1625
    }
1626
    value = value * 10 + digit;
144×
1627
  }
1628

1629
  if (isNegative) {
48×
1630
    // We use the same magnitude assumption here, just in case.
1631
    const auto last_digit = static_cast<Value::UInt>(value % 10);
8×
1632
    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
8×
1633
  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
40×
1634
    decoded = Value::LargestInt(value);
40×
1635
  } else {
1636
    decoded = value;
!
1637
  }
1638

1639
  return true;
48×
1640
}
1641

1642
bool OurReader::decodeDouble(Token& token) {
!
1643
  Value decoded;
!
1644
  if (!decodeDouble(token, decoded))
!
1645
    return false;
!
1646
  currentValue().swapPayload(decoded);
!
1647
  currentValue().setOffsetStart(token.start_ - begin_);
!
1648
  currentValue().setOffsetLimit(token.end_ - begin_);
!
1649
  return true;
!
1650
}
1651

1652
bool OurReader::decodeDouble(Token& token, Value& decoded) {
16×
1653
  double value = 0;
16×
1654
  const String buffer(token.start_, token.end_);
32×
1655
  IStringStream is(buffer);
32×
1656
  if (!(is >> value)) {
16×
NEW
1657
    if (value == std::numeric_limits<double>::max())
!
NEW
1658
      value = std::numeric_limits<double>::infinity();
!
NEW
1659
    else if (value == std::numeric_limits<double>::lowest())
!
NEW
1660
      value = -std::numeric_limits<double>::infinity();
!
NEW
1661
    else if (!std::isinf(value))
!
1662
      return addError(
1663
        "'" + String(token.start_, token.end_) + "' is not a number.", token);
!
1664
  }
1665
  decoded = value;
16×
1666
  return true;
16×
1667
}
1668

1669
bool OurReader::decodeString(Token& token) {
180×
1670
  String decoded_string;
360×
1671
  if (!decodeString(token, decoded_string))
180×
1672
    return false;
20×
1673
  Value decoded(decoded_string);
320×
1674
  currentValue().swapPayload(decoded);
160×
1675
  currentValue().setOffsetStart(token.start_ - begin_);
160×
1676
  currentValue().setOffsetLimit(token.end_ - begin_);
160×
1677
  return true;
160×
1678
}
1679

1680
bool OurReader::decodeString(Token& token, String& decoded) {
436×
1681
  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
436×
1682
  Location current = token.start_ + 1; // skip '"'
436×
1683
  Location end = token.end_ - 1;       // do not include '"'
436×
1684
  while (current != end) {
3,228×
1685
    Char c = *current++;
1,416×
1686
    if (c == '"')
1,416×
1687
      break;
!
1688
    if (c == '\\') {
1,416×
1689
      if (current == end)
84×
1690
        return addError("Empty escape sequence in string", token, current);
!
1691
      Char escape = *current++;
84×
1692
      switch (escape) {
84×
1693
      case '"':
1694
        decoded += '"';
4×
1695
        break;
4×
1696
      case '/':
1697
        decoded += '/';
4×
1698
        break;
4×
1699
      case '\\':
1700
        decoded += '\\';
8×
1701
        break;
8×
1702
      case 'b':
1703
        decoded += '\b';
4×
1704
        break;
4×
1705
      case 'f':
1706
        decoded += '\f';
4×
1707
        break;
4×
1708
      case 'n':
1709
        decoded += '\n';
4×
1710
        break;
4×
1711
      case 'r':
1712
        decoded += '\r';
4×
1713
        break;
4×
1714
      case 't':
1715
        decoded += '\t';
8×
1716
        break;
8×
1717
      case 'u': {
1718
        unsigned int unicode;
1719
        if (!decodeUnicodeCodePoint(token, current, end, unicode))
40×
1720
          return false;
16×
1721
        decoded += codePointToUTF8(unicode);
24×
1722
      } break;
24×
1723
      default:
1724
        return addError("Bad escape sequence in string", token, current);
4×
1725
      }
1726
    } else {
1727
      decoded += c;
1,332×
1728
    }
1729
  }
1730
  return true;
416×
1731
}
1732

1733
bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
40×
1734
                                       Location end, unsigned int& unicode) {
1735

1736
  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
40×
1737
    return false;
8×
1738
  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
32×
1739
    // surrogate pairs
1740
    if (end - current < 6)
12×
1741
      return addError(
8×
1742
          "additional six characters expected to parse unicode surrogate pair.",
1743
          token, current);
4×
1744
    if (*(current++) == '\\' && *(current++) == 'u') {
8×
1745
      unsigned int surrogatePair;
1746
      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
4×
1747
        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
4×
1748
      } else
1749
        return false;
!
1750
    } else
1751
      return addError("expecting another \\u token to begin the second half of "
8×
1752
                      "a unicode surrogate pair",
1753
                      token, current);
4×
1754
  }
1755
  return true;
24×
1756
}
1757

1758
bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
44×
1759
                                            Location end,
1760
                                            unsigned int& ret_unicode) {
1761
  if (end - current < 4)
44×
1762
    return addError(
8×
1763
        "Bad unicode escape sequence in string: four digits expected.", token,
1764
        current);
4×
1765
  int unicode = 0;
40×
1766
  for (int index = 0; index < 4; ++index) {
192×
1767
    Char c = *current++;
156×
1768
    unicode *= 16;
156×
1769
    if (c >= '0' && c <= '9')
156×
1770
      unicode += c - '0';
88×
1771
    else if (c >= 'a' && c <= 'f')
68×
1772
      unicode += c - 'a' + 10;
52×
1773
    else if (c >= 'A' && c <= 'F')
16×
1774
      unicode += c - 'A' + 10;
12×
1775
    else
1776
      return addError(
8×
1777
          "Bad unicode escape sequence in string: hexadecimal digit expected.",
1778
          token, current);
4×
1779
  }
1780
  ret_unicode = static_cast<unsigned int>(unicode);
36×
1781
  return true;
36×
1782
}
1783

1784
bool OurReader::addError(const String& message, Token& token, Location extra) {
140×
1785
  ErrorInfo info;
280×
1786
  info.token_ = token;
140×
1787
  info.message_ = message;
140×
1788
  info.extra_ = extra;
140×
1789
  errors_.push_back(info);
140×
1790
  return false;
280×
1791
}
1792

1793
bool OurReader::recoverFromError(TokenType skipUntilToken) {
112×
1794
  size_t errorCount = errors_.size();
112×
1795
  Token skip;
1796
  for (;;) {
268×
1797
    if (!readToken(skip))
380×
1798
      errors_.resize(errorCount); // discard errors caused by recovery
236×
1799
    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
380×
1800
      break;
1801
  }
1802
  errors_.resize(errorCount);
112×
1803
  return false;
112×
1804
}
1805

1806
bool OurReader::addErrorAndRecover(const String& message, Token& token,
56×
1807
                                   TokenType skipUntilToken) {
1808
  addError(message, token);
56×
1809
  return recoverFromError(skipUntilToken);
56×
1810
}
1811

1812
Value& OurReader::currentValue() { return *(nodes_.top()); }
3,644×
1813

1814
OurReader::Char OurReader::getNextChar() {
5,692×
1815
  if (current_ == end_)
5,692×
1816
    return 0;
312×
1817
  return *current_++;
5,380×
1818
}
1819

1820
void OurReader::getLocationLineAndColumn(Location location, int& line,
160×
1821
                                         int& column) const {
1822
  Location current = begin_;
160×
1823
  Location lastLineStart = current;
160×
1824
  line = 0;
160×
1825
  while (current < location && current != end_) {
3,056×
1826
    Char c = *current++;
1,448×
1827
    if (c == '\r') {
1,448×
1828
      if (*current == '\n')
4×
1829
        ++current;
!
1830
      lastLineStart = current;
4×
1831
      ++line;
4×
1832
    } else if (c == '\n') {
1,444×
1833
      lastLineStart = current;
4×
1834
      ++line;
4×
1835
    }
1836
  }
1837
  // column & line start at 1
1838
  column = int(location - lastLineStart) + 1;
160×
1839
  ++line;
160×
1840
}
160×
1841

1842
String OurReader::getLocationLineAndColumn(Location location) const {
160×
1843
  int line, column;
1844
  getLocationLineAndColumn(location, line, column);
160×
1845
  char buffer[18 + 16 + 16 + 1];
1846
  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
160×
1847
  return buffer;
160×
1848
}
1849

1850
String OurReader::getFormattedErrorMessages() const {
320×
1851
  String formattedMessage;
320×
1852
  for (const auto& error : errors_) {
460×
1853
    formattedMessage +=
1854
        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
140×
1855
    formattedMessage += "  " + error.message_ + "\n";
140×
1856
    if (error.extra_)
140×
1857
      formattedMessage +=
1858
          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
20×
1859
  }
1860
  return formattedMessage;
320×
1861
}
1862

1863
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
!
1864
  std::vector<OurReader::StructuredError> allErrors;
!
1865
  for (const auto& error : errors_) {
!
1866
    OurReader::StructuredError structured;
!
1867
    structured.offset_start = error.token_.start_ - begin_;
!
1868
    structured.offset_limit = error.token_.end_ - begin_;
!
1869
    structured.message = error.message_;
!
1870
    allErrors.push_back(structured);
!
1871
  }
1872
  return allErrors;
!
1873
}
1874

1875
class OurCharReader : public CharReader {
392×
1876
  bool const collectComments_;
1877
  OurReader reader_;
1878

1879
public:
1880
  OurCharReader(bool collectComments, OurFeatures const& features)
196×
1881
      : collectComments_(collectComments), reader_(features) {}
196×
1882
  bool parse(char const* beginDoc, char const* endDoc, Value* root,
324×
1883
             String* errs) override {
1884
    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
324×
1885
    if (errs) {
320×
1886
      *errs = reader_.getFormattedErrorMessages();
320×
1887
    }
1888
    return ok;
320×
1889
  }
1890
};
1891

1892
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
184×
1893
CharReaderBuilder::~CharReaderBuilder() = default;
1894
CharReader* CharReaderBuilder::newCharReader() const {
196×
1895
  bool collectComments = settings_["collectComments"].asBool();
196×
1896
  OurFeatures features = OurFeatures::all();
196×
1897
  features.allowComments_ = settings_["allowComments"].asBool();
196×
1898
  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
196×
1899
  features.strictRoot_ = settings_["strictRoot"].asBool();
196×
1900
  features.allowDroppedNullPlaceholders_ =
1901
      settings_["allowDroppedNullPlaceholders"].asBool();
196×
1902
  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
196×
1903
  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
196×
1904

1905
  // Stack limit is always a size_t, so we get this as an unsigned int
1906
  // regardless of it we have 64-bit integer support enabled.
1907
  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
196×
1908
  features.failIfExtra_ = settings_["failIfExtra"].asBool();
196×
1909
  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
196×
1910
  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
196×
1911
  features.skipBom_ = settings_["skipBom"].asBool();
196×
1912
  return new OurCharReader(collectComments, features);
196×
1913
}
1914

1915
bool CharReaderBuilder::validate(Json::Value* invalid) const {
8×
1916
  static const auto& valid_keys = *new std::set<String>{
1917
      "collectComments",
1918
      "allowComments",
1919
      "allowTrailingCommas",
1920
      "strictRoot",
1921
      "allowDroppedNullPlaceholders",
1922
      "allowNumericKeys",
1923
      "allowSingleQuotes",
1924
      "stackLimit",
1925
      "failIfExtra",
1926
      "rejectDupKeys",
1927
      "allowSpecialFloats",
1928
      "skipBom",
1929
  };
8×
1930
  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
108×
1931
    auto key = si.name();
104×
1932
    if (valid_keys.count(key))
100×
1933
      continue;
96×
1934
    if (invalid)
4×
1935
      (*invalid)[key] = *si;
4×
1936
    else
1937
      return false;
!
1938
  }
1939
  return invalid ? invalid->empty() : true;
8×
1940
}
1941

1942
Value& CharReaderBuilder::operator[](const String& key) {
4×
1943
  return settings_[key];
4×
1944
}
1945
// static
1946
void CharReaderBuilder::strictMode(Json::Value* settings) {
12×
1947
  //! [CharReaderBuilderStrictMode]
1948
  (*settings)["allowComments"] = false;
12×
1949
  (*settings)["allowTrailingCommas"] = false;
12×
1950
  (*settings)["strictRoot"] = true;
12×
1951
  (*settings)["allowDroppedNullPlaceholders"] = false;
12×
1952
  (*settings)["allowNumericKeys"] = false;
12×
1953
  (*settings)["allowSingleQuotes"] = false;
12×
1954
  (*settings)["stackLimit"] = 1000;
12×
1955
  (*settings)["failIfExtra"] = true;
12×
1956
  (*settings)["rejectDupKeys"] = true;
12×
1957
  (*settings)["allowSpecialFloats"] = false;
12×
1958
  (*settings)["skipBom"] = true;
12×
1959
  //! [CharReaderBuilderStrictMode]
1960
}
12×
1961
// static
1962
void CharReaderBuilder::setDefaults(Json::Value* settings) {
184×
1963
  //! [CharReaderBuilderDefaults]
1964
  (*settings)["collectComments"] = true;
184×
1965
  (*settings)["allowComments"] = true;
184×
1966
  (*settings)["allowTrailingCommas"] = true;
184×
1967
  (*settings)["strictRoot"] = false;
184×
1968
  (*settings)["allowDroppedNullPlaceholders"] = false;
184×
1969
  (*settings)["allowNumericKeys"] = false;
184×
1970
  (*settings)["allowSingleQuotes"] = false;
184×
1971
  (*settings)["stackLimit"] = 1000;
184×
1972
  (*settings)["failIfExtra"] = false;
184×
1973
  (*settings)["rejectDupKeys"] = false;
184×
1974
  (*settings)["allowSpecialFloats"] = false;
184×
1975
  (*settings)["skipBom"] = true;
184×
1976
  //! [CharReaderBuilderDefaults]
1977
}
184×
1978

1979
//////////////////////////////////
1980
// global functions
1981

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

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

2004
} // namespace Json
12×
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc