mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
861 lines
22 KiB
C++
861 lines
22 KiB
C++
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/json/json_parser.h"
|
|
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/macros.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/string_piece.h"
|
|
#include "base/strings/string_util.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/strings/utf_string_conversion_utils.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "base/third_party/icu/icu_utf.h"
|
|
#include "base/values.h"
|
|
|
|
namespace base {
|
|
namespace internal {
|
|
|
|
namespace {
|
|
|
|
// Chosen to support 99.9% of documents found in the wild late 2016.
|
|
// http://crbug.com/673263
|
|
const int kStackMaxDepth = 200;
|
|
|
|
const int32_t kExtendedASCIIStart = 0x80;
|
|
|
|
// Simple class that checks for maximum recursion/"stack overflow."
|
|
class StackMarker {
|
|
public:
|
|
explicit StackMarker(int* depth) : depth_(depth) {
|
|
++(*depth_);
|
|
DCHECK_LE(*depth_, kStackMaxDepth);
|
|
}
|
|
~StackMarker() {
|
|
--(*depth_);
|
|
}
|
|
|
|
bool IsTooDeep() const {
|
|
return *depth_ >= kStackMaxDepth;
|
|
}
|
|
|
|
private:
|
|
int* const depth_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StackMarker);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// This is U+FFFD.
|
|
const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
|
|
|
|
JSONParser::JSONParser(int options)
|
|
: options_(options),
|
|
start_pos_(nullptr),
|
|
pos_(nullptr),
|
|
end_pos_(nullptr),
|
|
index_(0),
|
|
stack_depth_(0),
|
|
line_number_(0),
|
|
index_last_line_(0),
|
|
error_code_(JSONReader::JSON_NO_ERROR),
|
|
error_line_(0),
|
|
error_column_(0) {
|
|
}
|
|
|
|
JSONParser::~JSONParser() {
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::Parse(StringPiece input) {
|
|
start_pos_ = input.data();
|
|
pos_ = start_pos_;
|
|
end_pos_ = start_pos_ + input.length();
|
|
index_ = 0;
|
|
line_number_ = 1;
|
|
index_last_line_ = 0;
|
|
|
|
error_code_ = JSONReader::JSON_NO_ERROR;
|
|
error_line_ = 0;
|
|
error_column_ = 0;
|
|
|
|
// When the input JSON string starts with a UTF-8 Byte-Order-Mark
|
|
// <0xEF 0xBB 0xBF>, advance the start position to avoid the
|
|
// ParseNextToken function mis-treating a Unicode BOM as an invalid
|
|
// character and returning NULL.
|
|
if (CanConsume(3) && static_cast<uint8_t>(*pos_) == 0xEF &&
|
|
static_cast<uint8_t>(*(pos_ + 1)) == 0xBB &&
|
|
static_cast<uint8_t>(*(pos_ + 2)) == 0xBF) {
|
|
NextNChars(3);
|
|
}
|
|
|
|
// Parse the first and any nested tokens.
|
|
std::unique_ptr<Value> root(ParseNextToken());
|
|
if (!root)
|
|
return nullptr;
|
|
|
|
// Make sure the input stream is at an end.
|
|
if (GetNextToken() != T_END_OF_INPUT) {
|
|
if (!CanConsume(1) || (NextChar() && GetNextToken() != T_END_OF_INPUT)) {
|
|
ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
JSONReader::JsonParseError JSONParser::error_code() const {
|
|
return error_code_;
|
|
}
|
|
|
|
std::string JSONParser::GetErrorMessage() const {
|
|
return FormatErrorMessage(error_line_, error_column_,
|
|
JSONReader::ErrorCodeToString(error_code_));
|
|
}
|
|
|
|
int JSONParser::error_line() const {
|
|
return error_line_;
|
|
}
|
|
|
|
int JSONParser::error_column() const {
|
|
return error_column_;
|
|
}
|
|
|
|
// StringBuilder ///////////////////////////////////////////////////////////////
|
|
|
|
JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {}
|
|
|
|
JSONParser::StringBuilder::StringBuilder(const char* pos)
|
|
: pos_(pos), length_(0) {}
|
|
|
|
JSONParser::StringBuilder::~StringBuilder() {
|
|
}
|
|
|
|
JSONParser::StringBuilder& JSONParser::StringBuilder::operator=(
|
|
StringBuilder&& other) = default;
|
|
|
|
void JSONParser::StringBuilder::Append(const char& c) {
|
|
DCHECK_GE(c, 0);
|
|
DCHECK_LT(static_cast<unsigned char>(c), 128);
|
|
|
|
if (string_)
|
|
string_->push_back(c);
|
|
else
|
|
++length_;
|
|
}
|
|
|
|
void JSONParser::StringBuilder::AppendString(const char* str, size_t len) {
|
|
DCHECK(string_);
|
|
string_->append(str, len);
|
|
}
|
|
|
|
void JSONParser::StringBuilder::Convert() {
|
|
if (string_)
|
|
return;
|
|
|
|
string_.emplace(pos_, length_);
|
|
}
|
|
|
|
StringPiece JSONParser::StringBuilder::AsStringPiece() {
|
|
if (string_)
|
|
return *string_;
|
|
return StringPiece(pos_, length_);
|
|
}
|
|
|
|
const std::string& JSONParser::StringBuilder::AsString() {
|
|
if (!string_)
|
|
Convert();
|
|
return *string_;
|
|
}
|
|
|
|
std::string JSONParser::StringBuilder::DestructiveAsString() {
|
|
if (string_)
|
|
return std::move(*string_);
|
|
return std::string(pos_, length_);
|
|
}
|
|
|
|
// JSONParser private //////////////////////////////////////////////////////////
|
|
|
|
inline bool JSONParser::CanConsume(int length) {
|
|
return pos_ + length <= end_pos_;
|
|
}
|
|
|
|
const char* JSONParser::NextChar() {
|
|
DCHECK(CanConsume(1));
|
|
++index_;
|
|
++pos_;
|
|
return pos_;
|
|
}
|
|
|
|
void JSONParser::NextNChars(int n) {
|
|
DCHECK(CanConsume(n));
|
|
index_ += n;
|
|
pos_ += n;
|
|
}
|
|
|
|
JSONParser::Token JSONParser::GetNextToken() {
|
|
EatWhitespaceAndComments();
|
|
if (!CanConsume(1))
|
|
return T_END_OF_INPUT;
|
|
|
|
switch (*pos_) {
|
|
case '{':
|
|
return T_OBJECT_BEGIN;
|
|
case '}':
|
|
return T_OBJECT_END;
|
|
case '[':
|
|
return T_ARRAY_BEGIN;
|
|
case ']':
|
|
return T_ARRAY_END;
|
|
case '"':
|
|
return T_STRING;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '-':
|
|
return T_NUMBER;
|
|
case 't':
|
|
return T_BOOL_TRUE;
|
|
case 'f':
|
|
return T_BOOL_FALSE;
|
|
case 'n':
|
|
return T_NULL;
|
|
case ',':
|
|
return T_LIST_SEPARATOR;
|
|
case ':':
|
|
return T_OBJECT_PAIR_SEPARATOR;
|
|
default:
|
|
return T_INVALID_TOKEN;
|
|
}
|
|
}
|
|
|
|
void JSONParser::EatWhitespaceAndComments() {
|
|
while (pos_ < end_pos_) {
|
|
switch (*pos_) {
|
|
case '\r':
|
|
case '\n':
|
|
index_last_line_ = index_;
|
|
// Don't increment line_number_ twice for "\r\n".
|
|
if (!(*pos_ == '\n' && pos_ > start_pos_ && *(pos_ - 1) == '\r'))
|
|
++line_number_;
|
|
// Fall through.
|
|
case ' ':
|
|
case '\t':
|
|
NextChar();
|
|
break;
|
|
case '/':
|
|
if (!EatComment())
|
|
return;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool JSONParser::EatComment() {
|
|
if (*pos_ != '/' || !CanConsume(1))
|
|
return false;
|
|
|
|
NextChar();
|
|
|
|
if (!CanConsume(1))
|
|
return false;
|
|
|
|
if (*pos_ == '/') {
|
|
// Single line comment, read to newline.
|
|
while (CanConsume(1)) {
|
|
if (*pos_ == '\n' || *pos_ == '\r')
|
|
return true;
|
|
NextChar();
|
|
}
|
|
} else if (*pos_ == '*') {
|
|
char previous_char = '\0';
|
|
// Block comment, read until end marker.
|
|
while (CanConsume(1)) {
|
|
if (previous_char == '*' && *pos_ == '/') {
|
|
// EatWhitespaceAndComments will inspect pos_, which will still be on
|
|
// the last / of the comment, so advance once more (which may also be
|
|
// end of input).
|
|
NextChar();
|
|
return true;
|
|
}
|
|
previous_char = *pos_;
|
|
NextChar();
|
|
}
|
|
|
|
// If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ParseNextToken() {
|
|
return ParseToken(GetNextToken());
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ParseToken(Token token) {
|
|
switch (token) {
|
|
case T_OBJECT_BEGIN:
|
|
return ConsumeDictionary();
|
|
case T_ARRAY_BEGIN:
|
|
return ConsumeList();
|
|
case T_STRING:
|
|
return ConsumeString();
|
|
case T_NUMBER:
|
|
return ConsumeNumber();
|
|
case T_BOOL_TRUE:
|
|
case T_BOOL_FALSE:
|
|
case T_NULL:
|
|
return ConsumeLiteral();
|
|
default:
|
|
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ConsumeDictionary() {
|
|
if (*pos_ != '{') {
|
|
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
StackMarker depth_check(&stack_depth_);
|
|
if (depth_check.IsTooDeep()) {
|
|
ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<Value::DictStorage::value_type> dict_storage;
|
|
|
|
NextChar();
|
|
Token token = GetNextToken();
|
|
while (token != T_OBJECT_END) {
|
|
if (token != T_STRING) {
|
|
ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
// First consume the key.
|
|
StringBuilder key;
|
|
if (!ConsumeStringRaw(&key)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Read the separator.
|
|
NextChar();
|
|
token = GetNextToken();
|
|
if (token != T_OBJECT_PAIR_SEPARATOR) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
// The next token is the value. Ownership transfers to |dict|.
|
|
NextChar();
|
|
std::unique_ptr<Value> value = ParseNextToken();
|
|
if (!value) {
|
|
// ReportError from deeper level.
|
|
return nullptr;
|
|
}
|
|
|
|
dict_storage.emplace_back(key.DestructiveAsString(), std::move(value));
|
|
|
|
NextChar();
|
|
token = GetNextToken();
|
|
if (token == T_LIST_SEPARATOR) {
|
|
NextChar();
|
|
token = GetNextToken();
|
|
if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
|
|
ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
|
|
return nullptr;
|
|
}
|
|
} else if (token != T_OBJECT_END) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return std::make_unique<Value>(
|
|
Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES));
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ConsumeList() {
|
|
if (*pos_ != '[') {
|
|
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
StackMarker depth_check(&stack_depth_);
|
|
if (depth_check.IsTooDeep()) {
|
|
ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<ListValue> list(new ListValue);
|
|
|
|
NextChar();
|
|
Token token = GetNextToken();
|
|
while (token != T_ARRAY_END) {
|
|
std::unique_ptr<Value> item = ParseToken(token);
|
|
if (!item) {
|
|
// ReportError from deeper level.
|
|
return nullptr;
|
|
}
|
|
|
|
list->Append(std::move(item));
|
|
|
|
NextChar();
|
|
token = GetNextToken();
|
|
if (token == T_LIST_SEPARATOR) {
|
|
NextChar();
|
|
token = GetNextToken();
|
|
if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
|
|
ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
|
|
return nullptr;
|
|
}
|
|
} else if (token != T_ARRAY_END) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return std::move(list);
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ConsumeString() {
|
|
StringBuilder string;
|
|
if (!ConsumeStringRaw(&string))
|
|
return nullptr;
|
|
|
|
return std::make_unique<Value>(string.DestructiveAsString());
|
|
}
|
|
|
|
bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
|
|
if (*pos_ != '"') {
|
|
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
|
|
return false;
|
|
}
|
|
|
|
// Strings are at minimum two characters: the surrounding double quotes.
|
|
if (!CanConsume(2)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return false;
|
|
}
|
|
|
|
// StringBuilder will internally build a StringPiece unless a UTF-16
|
|
// conversion occurs, at which point it will perform a copy into a
|
|
// std::string.
|
|
StringBuilder string(NextChar());
|
|
|
|
// Handle the empty string case early.
|
|
if (*pos_ == '"') {
|
|
*out = std::move(string);
|
|
return true;
|
|
}
|
|
|
|
int length = end_pos_ - start_pos_;
|
|
int32_t next_char = 0;
|
|
|
|
// There must always be at least two characters left in the stream: the next
|
|
// string character and the terminating closing quote.
|
|
while (CanConsume(2)) {
|
|
int start_index = index_;
|
|
pos_ = start_pos_ + index_; // CBU8_NEXT is postcrement.
|
|
CBU8_NEXT(start_pos_, index_, length, next_char);
|
|
if (next_char < 0 || !IsValidCharacter(next_char)) {
|
|
if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
|
|
ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
|
|
return false;
|
|
}
|
|
CBU8_NEXT(start_pos_, start_index, length, next_char);
|
|
string.Convert();
|
|
string.AppendString(kUnicodeReplacementString,
|
|
arraysize(kUnicodeReplacementString) - 1);
|
|
continue;
|
|
}
|
|
|
|
if (next_char == '"') {
|
|
--index_; // Rewind by one because of CBU8_NEXT.
|
|
*out = std::move(string);
|
|
return true;
|
|
}
|
|
|
|
// If this character is not an escape sequence...
|
|
if (next_char != '\\') {
|
|
if (next_char < kExtendedASCIIStart)
|
|
string.Append(static_cast<char>(next_char));
|
|
else
|
|
DecodeUTF8(next_char, &string);
|
|
} else {
|
|
// And if it is an escape sequence, the input string will be adjusted
|
|
// (either by combining the two characters of an encoded escape sequence,
|
|
// or with a UTF conversion), so using StringPiece isn't possible -- force
|
|
// a conversion.
|
|
string.Convert();
|
|
|
|
if (!CanConsume(1)) {
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
|
|
return false;
|
|
}
|
|
|
|
NextChar();
|
|
if (!CanConsume(1)) {
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
|
|
return false;
|
|
}
|
|
|
|
switch (*pos_) {
|
|
// Allowed esape sequences:
|
|
case 'x': { // UTF-8 sequence.
|
|
// UTF-8 \x escape sequences are not allowed in the spec, but they
|
|
// are supported here for backwards-compatiblity with the old parser.
|
|
if (!CanConsume(3)) {
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, 1);
|
|
return false;
|
|
}
|
|
|
|
int hex_digit = 0;
|
|
if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit) ||
|
|
!IsValidCharacter(hex_digit)) {
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
|
|
return false;
|
|
}
|
|
NextChar();
|
|
|
|
if (hex_digit < kExtendedASCIIStart)
|
|
string.Append(static_cast<char>(hex_digit));
|
|
else
|
|
DecodeUTF8(hex_digit, &string);
|
|
break;
|
|
}
|
|
case 'u': { // UTF-16 sequence.
|
|
// UTF units are of the form \uXXXX.
|
|
if (!CanConsume(5)) { // 5 being 'u' and four HEX digits.
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
|
|
return false;
|
|
}
|
|
|
|
// Skip the 'u'.
|
|
NextChar();
|
|
|
|
std::string utf8_units;
|
|
if (!DecodeUTF16(&utf8_units)) {
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
|
|
return false;
|
|
}
|
|
|
|
string.AppendString(utf8_units.data(), utf8_units.length());
|
|
break;
|
|
}
|
|
case '"':
|
|
string.Append('"');
|
|
break;
|
|
case '\\':
|
|
string.Append('\\');
|
|
break;
|
|
case '/':
|
|
string.Append('/');
|
|
break;
|
|
case 'b':
|
|
string.Append('\b');
|
|
break;
|
|
case 'f':
|
|
string.Append('\f');
|
|
break;
|
|
case 'n':
|
|
string.Append('\n');
|
|
break;
|
|
case 'r':
|
|
string.Append('\r');
|
|
break;
|
|
case 't':
|
|
string.Append('\t');
|
|
break;
|
|
case 'v': // Not listed as valid escape sequence in the RFC.
|
|
string.Append('\v');
|
|
break;
|
|
// All other escape squences are illegal.
|
|
default:
|
|
ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
|
|
return false;
|
|
}
|
|
|
|
// Entry is at the first X in \uXXXX.
|
|
bool JSONParser::DecodeUTF16(std::string* dest_string) {
|
|
if (!CanConsume(4))
|
|
return false;
|
|
|
|
// This is a 32-bit field because the shift operations in the
|
|
// conversion process below cause MSVC to error about "data loss."
|
|
// This only stores UTF-16 code units, though.
|
|
// Consume the UTF-16 code unit, which may be a high surrogate.
|
|
int code_unit16_high = 0;
|
|
if (!HexStringToInt(StringPiece(pos_, 4), &code_unit16_high))
|
|
return false;
|
|
|
|
// Only add 3, not 4, because at the end of this iteration, the parser has
|
|
// finished working with the last digit of the UTF sequence, meaning that
|
|
// the next iteration will advance to the next byte.
|
|
NextNChars(3);
|
|
|
|
// Used to convert the UTF-16 code units to a code point and then to a UTF-8
|
|
// code unit sequence.
|
|
char code_unit8[8] = { 0 };
|
|
size_t offset = 0;
|
|
|
|
// If this is a high surrogate, consume the next code unit to get the
|
|
// low surrogate.
|
|
if (CBU16_IS_SURROGATE(code_unit16_high)) {
|
|
// Make sure this is the high surrogate. If not, it's an encoding
|
|
// error.
|
|
if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high))
|
|
return false;
|
|
|
|
// Make sure that the token has more characters to consume the
|
|
// lower surrogate.
|
|
if (!CanConsume(6)) // 6 being '\' 'u' and four HEX digits.
|
|
return false;
|
|
if (*NextChar() != '\\' || *NextChar() != 'u')
|
|
return false;
|
|
|
|
NextChar(); // Read past 'u'.
|
|
int code_unit16_low = 0;
|
|
if (!HexStringToInt(StringPiece(pos_, 4), &code_unit16_low))
|
|
return false;
|
|
|
|
NextNChars(3);
|
|
|
|
if (!CBU16_IS_TRAIL(code_unit16_low)) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t code_point =
|
|
CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low);
|
|
if (!IsValidCharacter(code_point))
|
|
return false;
|
|
|
|
offset = 0;
|
|
CBU8_APPEND_UNSAFE(code_unit8, offset, code_point);
|
|
} else {
|
|
// Not a surrogate.
|
|
DCHECK(CBU16_IS_SINGLE(code_unit16_high));
|
|
if (!IsValidCharacter(code_unit16_high)) {
|
|
if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
|
|
return false;
|
|
}
|
|
dest_string->append(kUnicodeReplacementString);
|
|
return true;
|
|
}
|
|
|
|
CBU8_APPEND_UNSAFE(code_unit8, offset, code_unit16_high);
|
|
}
|
|
|
|
dest_string->append(code_unit8, offset);
|
|
return true;
|
|
}
|
|
|
|
void JSONParser::DecodeUTF8(const int32_t& point, StringBuilder* dest) {
|
|
DCHECK(IsValidCharacter(point));
|
|
|
|
// Anything outside of the basic ASCII plane will need to be decoded from
|
|
// int32_t to a multi-byte sequence.
|
|
if (point < kExtendedASCIIStart) {
|
|
dest->Append(static_cast<char>(point));
|
|
} else {
|
|
char utf8_units[4] = { 0 };
|
|
int offset = 0;
|
|
CBU8_APPEND_UNSAFE(utf8_units, offset, point);
|
|
dest->Convert();
|
|
// CBU8_APPEND_UNSAFE can overwrite up to 4 bytes, so utf8_units may not be
|
|
// zero terminated at this point. |offset| contains the correct length.
|
|
dest->AppendString(utf8_units, offset);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ConsumeNumber() {
|
|
const char* num_start = pos_;
|
|
const int start_index = index_;
|
|
int end_index = start_index;
|
|
|
|
if (*pos_ == '-')
|
|
NextChar();
|
|
|
|
if (!ReadInt(false)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
end_index = index_;
|
|
|
|
// The optional fraction part.
|
|
if (CanConsume(1) && *pos_ == '.') {
|
|
NextChar();
|
|
if (!ReadInt(true)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
end_index = index_;
|
|
}
|
|
|
|
// Optional exponent part.
|
|
if (CanConsume(1) && (*pos_ == 'e' || *pos_ == 'E')) {
|
|
NextChar();
|
|
if (!CanConsume(1)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
if (*pos_ == '-' || *pos_ == '+') {
|
|
NextChar();
|
|
}
|
|
if (!ReadInt(true)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
end_index = index_;
|
|
}
|
|
|
|
// ReadInt is greedy because numbers have no easily detectable sentinel,
|
|
// so save off where the parser should be on exit (see Consume invariant at
|
|
// the top of the header), then make sure the next token is one which is
|
|
// valid.
|
|
const char* exit_pos = pos_ - 1;
|
|
int exit_index = index_ - 1;
|
|
|
|
switch (GetNextToken()) {
|
|
case T_OBJECT_END:
|
|
case T_ARRAY_END:
|
|
case T_LIST_SEPARATOR:
|
|
case T_END_OF_INPUT:
|
|
break;
|
|
default:
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
|
|
pos_ = exit_pos;
|
|
index_ = exit_index;
|
|
|
|
StringPiece num_string(num_start, end_index - start_index);
|
|
|
|
int num_int;
|
|
if (StringToInt(num_string, &num_int))
|
|
return std::make_unique<Value>(num_int);
|
|
|
|
double num_double;
|
|
if (StringToDouble(num_string.as_string(), &num_double) &&
|
|
std::isfinite(num_double)) {
|
|
return std::make_unique<Value>(num_double);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool JSONParser::ReadInt(bool allow_leading_zeros) {
|
|
size_t len = 0;
|
|
char first = 0;
|
|
|
|
while (CanConsume(1)) {
|
|
if (!IsAsciiDigit(*pos_))
|
|
break;
|
|
|
|
if (len == 0)
|
|
first = *pos_;
|
|
|
|
++len;
|
|
NextChar();
|
|
}
|
|
|
|
if (len == 0)
|
|
return false;
|
|
|
|
if (!allow_leading_zeros && len > 1 && first == '0')
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
|
|
switch (*pos_) {
|
|
case 't': {
|
|
const char kTrueLiteral[] = "true";
|
|
const int kTrueLen = static_cast<int>(strlen(kTrueLiteral));
|
|
if (!CanConsume(kTrueLen) ||
|
|
!StringsAreEqual(pos_, kTrueLiteral, kTrueLen)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
NextNChars(kTrueLen - 1);
|
|
return std::make_unique<Value>(true);
|
|
}
|
|
case 'f': {
|
|
const char kFalseLiteral[] = "false";
|
|
const int kFalseLen = static_cast<int>(strlen(kFalseLiteral));
|
|
if (!CanConsume(kFalseLen) ||
|
|
!StringsAreEqual(pos_, kFalseLiteral, kFalseLen)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
NextNChars(kFalseLen - 1);
|
|
return std::make_unique<Value>(false);
|
|
}
|
|
case 'n': {
|
|
const char kNullLiteral[] = "null";
|
|
const int kNullLen = static_cast<int>(strlen(kNullLiteral));
|
|
if (!CanConsume(kNullLen) ||
|
|
!StringsAreEqual(pos_, kNullLiteral, kNullLen)) {
|
|
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
|
|
return nullptr;
|
|
}
|
|
NextNChars(kNullLen - 1);
|
|
return std::make_unique<Value>();
|
|
}
|
|
default:
|
|
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool JSONParser::StringsAreEqual(const char* one, const char* two, size_t len) {
|
|
return strncmp(one, two, len) == 0;
|
|
}
|
|
|
|
void JSONParser::ReportError(JSONReader::JsonParseError code,
|
|
int column_adjust) {
|
|
error_code_ = code;
|
|
error_line_ = line_number_;
|
|
error_column_ = index_ - index_last_line_ + column_adjust;
|
|
}
|
|
|
|
// static
|
|
std::string JSONParser::FormatErrorMessage(int line, int column,
|
|
const std::string& description) {
|
|
if (line || column) {
|
|
return StringPrintf("Line: %i, column: %i, %s",
|
|
line, column, description.c_str());
|
|
}
|
|
return description;
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace base
|