blob: f7533ea33cf69785ee042dc9bdff320e56af2c7c [file] [log] [blame]
// 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 "base/float_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/third_party/icu/icu_utf.h"
#include "base/utf_string_conversion_utils.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
namespace base {
namespace internal {
namespace {
const int kStackMaxDepth = 100;
const int32 kExtendedASCIIStart = 0x80;
// This and the class below are used to own the JSON input string for when
// string tokens are stored as StringPiece instead of std::string. This
// optimization avoids about 2/3rds of string memory copies. The constructor
// takes ownership of the input string. The real root value is Swap()ed into
// the new instance.
class DictionaryHiddenRootValue : public base::DictionaryValue {
public:
DictionaryHiddenRootValue(std::string* json, Value* root) : json_(json) {
DCHECK(root->IsType(Value::TYPE_DICTIONARY));
DictionaryValue::Swap(static_cast<DictionaryValue*>(root));
}
virtual void Swap(DictionaryValue* other) override {
DVLOG(1) << "Swap()ing a DictionaryValue inefficiently.";
// First deep copy to convert JSONStringValue to std::string and swap that
// copy with |other|, which contains the new contents of |this|.
scoped_ptr<base::DictionaryValue> copy(DeepCopy());
copy->Swap(other);
// Then erase the contents of the current dictionary and swap in the
// new contents, originally from |other|.
Clear();
json_.reset();
DictionaryValue::Swap(copy.get());
}
// Not overriding DictionaryValue::Remove because it just calls through to
// the method below.
virtual bool RemoveWithoutPathExpansion(const std::string& key,
Value** out) override {
// If the caller won't take ownership of the removed value, just call up.
if (!out)
return DictionaryValue::RemoveWithoutPathExpansion(key, out);
DVLOG(1) << "Remove()ing from a DictionaryValue inefficiently.";
// Otherwise, remove the value while its still "owned" by this and copy it
// to convert any JSONStringValues to std::string.
Value* out_owned = NULL;
if (!DictionaryValue::RemoveWithoutPathExpansion(key, &out_owned))
return false;
*out = out_owned->DeepCopy();
delete out_owned;
return true;
}
private:
scoped_ptr<std::string> json_;
DISALLOW_COPY_AND_ASSIGN(DictionaryHiddenRootValue);
};
class ListHiddenRootValue : public base::ListValue {
public:
ListHiddenRootValue(std::string* json, Value* root) : json_(json) {
DCHECK(root->IsType(Value::TYPE_LIST));
ListValue::Swap(static_cast<ListValue*>(root));
}
virtual void Swap(ListValue* other) override {
DVLOG(1) << "Swap()ing a ListValue inefficiently.";
// First deep copy to convert JSONStringValue to std::string and swap that
// copy with |other|, which contains the new contents of |this|.
scoped_ptr<base::ListValue> copy(DeepCopy());
copy->Swap(other);
// Then erase the contents of the current list and swap in the new contents,
// originally from |other|.
Clear();
json_.reset();
ListValue::Swap(copy.get());
}
virtual bool Remove(size_t index, Value** out) override {
// If the caller won't take ownership of the removed value, just call up.
if (!out)
return ListValue::Remove(index, out);
DVLOG(1) << "Remove()ing from a ListValue inefficiently.";
// Otherwise, remove the value while its still "owned" by this and copy it
// to convert any JSONStringValues to std::string.
Value* out_owned = NULL;
if (!ListValue::Remove(index, &out_owned))
return false;
*out = out_owned->DeepCopy();
delete out_owned;
return true;
}
private:
scoped_ptr<std::string> json_;
DISALLOW_COPY_AND_ASSIGN(ListHiddenRootValue);
};
// A variant on StringValue that uses StringPiece instead of copying the string
// into the Value. This can only be stored in a child of hidden root (above),
// otherwise the referenced string will not be guaranteed to outlive it.
class JSONStringValue : public base::Value {
public:
explicit JSONStringValue(const base::StringPiece& piece)
: Value(TYPE_STRING),
string_piece_(piece) {
}
// Overridden from base::Value:
virtual bool GetAsString(std::string* out_value) const override {
string_piece_.CopyToString(out_value);
return true;
}
virtual bool GetAsString(string16* out_value) const override {
*out_value = UTF8ToUTF16(string_piece_);
return true;
}
virtual Value* DeepCopy() const override {
return new StringValue(string_piece_.as_string());
}
virtual bool Equals(const Value* other) const override {
std::string other_string;
return other->IsType(TYPE_STRING) && other->GetAsString(&other_string) &&
StringPiece(other_string) == string_piece_;
}
private:
// The location in the original input stream.
base::StringPiece string_piece_;
DISALLOW_COPY_AND_ASSIGN(JSONStringValue);
};
// 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_(NULL),
pos_(NULL),
end_pos_(NULL),
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() {
}
Value* JSONParser::Parse(const StringPiece& input) {
scoped_ptr<std::string> input_copy;
// If the children of a JSON root can be detached, then hidden roots cannot
// be used, so do not bother copying the input because StringPiece will not
// be used anywhere.
if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
input_copy.reset(new std::string(input.as_string()));
start_pos_ = input_copy->data();
} else {
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>(*pos_) == 0xEF &&
static_cast<uint8>(*(pos_ + 1)) == 0xBB &&
static_cast<uint8>(*(pos_ + 2)) == 0xBF) {
NextNChars(3);
}
// Parse the first and any nested tokens.
scoped_ptr<Value> root(ParseNextToken());
if (!root.get())
return NULL;
// 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 NULL;
}
}
// Dictionaries and lists can contain JSONStringValues, so wrap them in a
// hidden root.
if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
if (root->IsType(Value::TYPE_DICTIONARY)) {
return new DictionaryHiddenRootValue(input_copy.release(), root.get());
} else if (root->IsType(Value::TYPE_LIST)) {
return new ListHiddenRootValue(input_copy.release(), root.get());
} else if (root->IsType(Value::TYPE_STRING)) {
// A string type could be a JSONStringValue, but because there's no
// corresponding HiddenRootValue, the memory will be lost. Deep copy to
// preserve it.
return root->DeepCopy();
}
}
// All other values can be returned directly.
return root.release();
}
JSONReader::JsonParseError JSONParser::error_code() const {
return error_code_;
}
std::string JSONParser::GetErrorMessage() const {
return FormatErrorMessage(error_line_, error_column_,
JSONReader::ErrorCodeToString(error_code_));
}
// StringBuilder ///////////////////////////////////////////////////////////////
JSONParser::StringBuilder::StringBuilder()
: pos_(NULL),
length_(0),
string_(NULL) {
}
JSONParser::StringBuilder::StringBuilder(const char* pos)
: pos_(pos),
length_(0),
string_(NULL) {
}
void JSONParser::StringBuilder::Swap(StringBuilder* other) {
std::swap(other->string_, string_);
std::swap(other->pos_, pos_);
std::swap(other->length_, length_);
}
JSONParser::StringBuilder::~StringBuilder() {
delete string_;
}
void JSONParser::StringBuilder::Append(const char& c) {
DCHECK_GE(c, 0);
DCHECK_LT(c, 128);
if (string_)
string_->push_back(c);
else
++length_;
}
void JSONParser::StringBuilder::AppendString(const std::string& str) {
DCHECK(string_);
string_->append(str);
}
void JSONParser::StringBuilder::Convert() {
if (string_)
return;
string_ = new std::string(pos_, length_);
}
bool JSONParser::StringBuilder::CanBeStringPiece() const {
return !string_;
}
StringPiece JSONParser::StringBuilder::AsStringPiece() {
if (string_)
return StringPiece();
return StringPiece(pos_, length_);
}
const std::string& JSONParser::StringBuilder::AsString() {
if (!string_)
Convert();
return *string_;
}
// 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_;
++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;
char next_char = *NextChar();
if (next_char == '/') {
// Single line comment, read to newline.
while (CanConsume(1)) {
char next_char = *NextChar();
if (next_char == '\n' || next_char == '\r')
return true;
}
} else if (next_char == '*') {
// Block comment, read until end marker.
while (CanConsume(2)) {
if (*NextChar() == '*' && *NextChar() == '/') {
// 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;
}
}
// If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
}
return false;
}
Value* JSONParser::ParseNextToken() {
return ParseToken(GetNextToken());
}
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 NULL;
}
}
Value* JSONParser::ConsumeDictionary() {
if (*pos_ != '{') {
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
return NULL;
}
StackMarker depth_check(&stack_depth_);
if (depth_check.IsTooDeep()) {
ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
return NULL;
}
scoped_ptr<DictionaryValue> dict(new DictionaryValue);
NextChar();
Token token = GetNextToken();
while (token != T_OBJECT_END) {
if (token != T_STRING) {
ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1);
return NULL;
}
// First consume the key.
StringBuilder key;
if (!ConsumeStringRaw(&key)) {
return NULL;
}
// Read the separator.
NextChar();
token = GetNextToken();
if (token != T_OBJECT_PAIR_SEPARATOR) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
// The next token is the value. Ownership transfers to |dict|.
NextChar();
Value* value = ParseNextToken();
if (!value) {
// ReportError from deeper level.
return NULL;
}
dict->SetWithoutPathExpansion(key.AsString(), 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 NULL;
}
} else if (token != T_OBJECT_END) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
return NULL;
}
}
return dict.release();
}
Value* JSONParser::ConsumeList() {
if (*pos_ != '[') {
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
return NULL;
}
StackMarker depth_check(&stack_depth_);
if (depth_check.IsTooDeep()) {
ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
return NULL;
}
scoped_ptr<ListValue> list(new ListValue);
NextChar();
Token token = GetNextToken();
while (token != T_ARRAY_END) {
Value* item = ParseToken(token);
if (!item) {
// ReportError from deeper level.
return NULL;
}
list->Append(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 NULL;
}
} else if (token != T_ARRAY_END) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
}
return list.release();
}
Value* JSONParser::ConsumeString() {
StringBuilder string;
if (!ConsumeStringRaw(&string))
return NULL;
// Create the Value representation, using a hidden root, if configured
// to do so, and if the string can be represented by StringPiece.
if (string.CanBeStringPiece() && !(options_ & JSON_DETACHABLE_CHILDREN)) {
return new JSONStringValue(string.AsStringPiece());
} else {
if (string.CanBeStringPiece())
string.Convert();
return new StringValue(string.AsString());
}
}
bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
if (*pos_ != '"') {
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 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());
int length = end_pos_ - start_pos_;
int32 next_char = 0;
while (CanConsume(1)) {
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);
continue;
}
// If this character is an escape sequence...
if (next_char == '\\') {
// 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;
}
switch (*NextChar()) {
// 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(2)) {
ReportError(JSONReader::JSON_INVALID_ESCAPE, 1);
return false;
}
int hex_digit = 0;
if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit)) {
ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
return false;
}
NextChar();
if (hex_digit < kExtendedASCIIStart)
string.Append(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);
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;
}
} else if (next_char == '"') {
--index_; // Rewind by one because of CBU8_NEXT.
out->Swap(&string);
return true;
} else {
if (next_char < kExtendedASCIIStart)
string.Append(next_char);
else
DecodeUTF8(next_char, &string);
}
}
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 code_point = CBU16_GET_SUPPLEMENTARY(code_unit16_high,
code_unit16_low);
offset = 0;
CBU8_APPEND_UNSAFE(code_unit8, offset, code_point);
} else {
// Not a surrogate.
DCHECK(CBU16_IS_SINGLE(code_unit16_high));
CBU8_APPEND_UNSAFE(code_unit8, offset, code_unit16_high);
}
dest_string->append(code_unit8);
return true;
}
void JSONParser::DecodeUTF8(const int32& point, StringBuilder* dest) {
// Anything outside of the basic ASCII plane will need to be decoded from
// int32 to a multi-byte sequence.
if (point < kExtendedASCIIStart) {
dest->Append(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(std::string(utf8_units, offset));
}
}
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 NULL;
}
end_index = index_;
// The optional fraction part.
if (*pos_ == '.') {
if (!CanConsume(1)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
NextChar();
if (!ReadInt(true)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
end_index = index_;
}
// Optional exponent part.
if (*pos_ == 'e' || *pos_ == 'E') {
NextChar();
if (*pos_ == '-' || *pos_ == '+')
NextChar();
if (!ReadInt(true)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
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 NULL;
}
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 new FundamentalValue(num_int);
double num_double;
if (base::StringToDouble(num_string.as_string(), &num_double) &&
IsFinite(num_double)) {
return new FundamentalValue(num_double);
}
return NULL;
}
bool JSONParser::ReadInt(bool allow_leading_zeros) {
char first = *pos_;
int len = 0;
char c = first;
while (CanConsume(1) && IsAsciiDigit(c)) {
c = *NextChar();
++len;
}
if (len == 0)
return false;
if (!allow_leading_zeros && len > 1 && first == '0')
return false;
return true;
}
Value* JSONParser::ConsumeLiteral() {
switch (*pos_) {
case 't': {
const char* kTrueLiteral = "true";
const int kTrueLen = static_cast<int>(strlen(kTrueLiteral));
if (!CanConsume(kTrueLen - 1) ||
!StringsAreEqual(pos_, kTrueLiteral, kTrueLen)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
NextNChars(kTrueLen - 1);
return new FundamentalValue(true);
}
case 'f': {
const char* kFalseLiteral = "false";
const int kFalseLen = static_cast<int>(strlen(kFalseLiteral));
if (!CanConsume(kFalseLen - 1) ||
!StringsAreEqual(pos_, kFalseLiteral, kFalseLen)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
NextNChars(kFalseLen - 1);
return new FundamentalValue(false);
}
case 'n': {
const char* kNullLiteral = "null";
const int kNullLen = static_cast<int>(strlen(kNullLiteral));
if (!CanConsume(kNullLen - 1) ||
!StringsAreEqual(pos_, kNullLiteral, kNullLen)) {
ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
return NULL;
}
NextNChars(kNullLen - 1);
return Value::CreateNullValue();
}
default:
ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
return NULL;
}
}
// 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