| Index: third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc
|
| diff --git a/third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc b/third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc
|
| deleted file mode 100644
|
| index d439a221d89f9fb2078752fa166794c922b22098..0000000000000000000000000000000000000000
|
| --- a/third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc
|
| +++ /dev/null
|
| @@ -1,740 +0,0 @@
|
| -// Protocol Buffers - Google's data interchange format
|
| -// Copyright 2008 Google Inc. All rights reserved.
|
| -// https://developers.google.com/protocol-buffers/
|
| -//
|
| -// Redistribution and use in source and binary forms, with or without
|
| -// modification, are permitted provided that the following conditions are
|
| -// met:
|
| -//
|
| -// * Redistributions of source code must retain the above copyright
|
| -// notice, this list of conditions and the following disclaimer.
|
| -// * Redistributions in binary form must reproduce the above
|
| -// copyright notice, this list of conditions and the following disclaimer
|
| -// in the documentation and/or other materials provided with the
|
| -// distribution.
|
| -// * Neither the name of Google Inc. nor the names of its
|
| -// contributors may be used to endorse or promote products derived from
|
| -// this software without specific prior written permission.
|
| -//
|
| -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| -
|
| -#include <google/protobuf/util/internal/json_stream_parser.h>
|
| -
|
| -#include <algorithm>
|
| -#include <cctype>
|
| -#include <cerrno>
|
| -#include <cstdlib>
|
| -#include <cstring>
|
| -#include <memory>
|
| -#ifndef _SHARED_PTR_H
|
| -#include <google/protobuf/stubs/shared_ptr.h>
|
| -#endif
|
| -
|
| -#include <google/protobuf/stubs/common.h>
|
| -#include <google/protobuf/stubs/strutil.h>
|
| -#include <google/protobuf/util/internal/object_writer.h>
|
| -
|
| -namespace google {
|
| -namespace protobuf {
|
| -namespace util {
|
| -
|
| -// Allow these symbols to be referenced as util::Status, util::error::* in
|
| -// this file.
|
| -using util::Status;
|
| -namespace error {
|
| -using util::error::INTERNAL;
|
| -using util::error::INVALID_ARGUMENT;
|
| -} // namespace error
|
| -
|
| -namespace converter {
|
| -
|
| -// Number of digits in a unicode escape sequence (/uXXXX)
|
| -static const int kUnicodeEscapedLength = 6;
|
| -
|
| -// Length of the true, false, and null literals.
|
| -static const int true_len = strlen("true");
|
| -static const int false_len = strlen("false");
|
| -static const int null_len = strlen("null");
|
| -
|
| -inline bool IsLetter(char c) {
|
| - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_') ||
|
| - (c == '$');
|
| -}
|
| -
|
| -inline bool IsAlphanumeric(char c) {
|
| - return IsLetter(c) || ('0' <= c && c <= '9');
|
| -}
|
| -
|
| -static bool ConsumeKey(StringPiece* input, StringPiece* key) {
|
| - if (input->empty() || !IsLetter((*input)[0])) return false;
|
| - int len = 1;
|
| - for (; len < input->size(); ++len) {
|
| - if (!IsAlphanumeric((*input)[len])) {
|
| - break;
|
| - }
|
| - }
|
| - *key = StringPiece(input->data(), len);
|
| - *input = StringPiece(input->data() + len, input->size() - len);
|
| - return true;
|
| -}
|
| -
|
| -static bool MatchKey(StringPiece input) {
|
| - return !input.empty() && IsLetter(input[0]);
|
| -}
|
| -
|
| -JsonStreamParser::JsonStreamParser(ObjectWriter* ow)
|
| - : ow_(ow),
|
| - stack_(),
|
| - leftover_(),
|
| - json_(),
|
| - p_(),
|
| - key_(),
|
| - key_storage_(),
|
| - finishing_(false),
|
| - parsed_(),
|
| - parsed_storage_(),
|
| - string_open_(0),
|
| - utf8_storage_(),
|
| - utf8_length_(0) {
|
| - // Initialize the stack with a single value to be parsed.
|
| - stack_.push(VALUE);
|
| -}
|
| -
|
| -JsonStreamParser::~JsonStreamParser() {}
|
| -
|
| -util::Status JsonStreamParser::Parse(StringPiece json) {
|
| - return ParseChunk(json);
|
| -}
|
| -
|
| -util::Status JsonStreamParser::FinishParse() {
|
| - // If we do not expect anything and there is nothing left to parse we're all
|
| - // done.
|
| - if (stack_.empty() && leftover_.empty()) {
|
| - return util::Status::OK;
|
| - }
|
| - // Parse the remainder in finishing mode, which reports errors for things like
|
| - // unterminated strings or unknown tokens that would normally be retried.
|
| - p_ = json_ = StringPiece(leftover_);
|
| - finishing_ = true;
|
| - util::Status result = RunParser();
|
| - if (result.ok()) {
|
| - SkipWhitespace();
|
| - if (!p_.empty()) {
|
| - result = ReportFailure("Parsing terminated before end of input.");
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseChunk(StringPiece chunk) {
|
| - // If we have leftovers from a previous chunk, append the new chunk to it and
|
| - // create a new StringPiece pointing at the string's data. This could be
|
| - // large but we rely on the chunks to be small, assuming they are fragments
|
| - // of a Cord.
|
| - if (!leftover_.empty()) {
|
| - chunk.AppendToString(&leftover_);
|
| - p_ = json_ = StringPiece(leftover_);
|
| - } else {
|
| - p_ = json_ = chunk;
|
| - }
|
| -
|
| - finishing_ = false;
|
| - util::Status result = RunParser();
|
| - if (!result.ok()) return result;
|
| -
|
| - SkipWhitespace();
|
| - if (p_.empty()) {
|
| - // If we parsed everything we had, clear the leftover.
|
| - leftover_.clear();
|
| - } else {
|
| - // If we do not expect anything i.e. stack is empty, and we have non-empty
|
| - // string left to parse, we report an error.
|
| - if (stack_.empty()) {
|
| - return ReportFailure("Parsing terminated before end of input.");
|
| - }
|
| - // If we expect future data i.e. stack is non-empty, and we have some
|
| - // unparsed data left, we save it for later parse.
|
| - leftover_ = p_.ToString();
|
| - }
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::RunParser() {
|
| - while (!stack_.empty()) {
|
| - ParseType type = stack_.top();
|
| - TokenType t = (string_open_ == 0) ? GetNextTokenType() : BEGIN_STRING;
|
| - stack_.pop();
|
| - util::Status result;
|
| - switch (type) {
|
| - case VALUE:
|
| - result = ParseValue(t);
|
| - break;
|
| -
|
| - case OBJ_MID:
|
| - result = ParseObjectMid(t);
|
| - break;
|
| -
|
| - case ENTRY:
|
| - result = ParseEntry(t);
|
| - break;
|
| -
|
| - case ENTRY_MID:
|
| - result = ParseEntryMid(t);
|
| - break;
|
| -
|
| - case ARRAY_VALUE:
|
| - result = ParseArrayValue(t);
|
| - break;
|
| -
|
| - case ARRAY_MID:
|
| - result = ParseArrayMid(t);
|
| - break;
|
| -
|
| - default:
|
| - result = util::Status(util::error::INTERNAL,
|
| - StrCat("Unknown parse type: ", type));
|
| - break;
|
| - }
|
| - if (!result.ok()) {
|
| - // If we were cancelled, save our state and try again later.
|
| - if (!finishing_ && result == util::Status::CANCELLED) {
|
| - stack_.push(type);
|
| - // If we have a key we still need to render, make sure to save off the
|
| - // contents in our own storage.
|
| - if (!key_.empty() && key_storage_.empty()) {
|
| - key_.AppendToString(&key_storage_);
|
| - key_ = StringPiece(key_storage_);
|
| - }
|
| - result = util::Status::OK;
|
| - }
|
| - return result;
|
| - }
|
| - }
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseValue(TokenType type) {
|
| - switch (type) {
|
| - case BEGIN_OBJECT:
|
| - return HandleBeginObject();
|
| - case BEGIN_ARRAY:
|
| - return HandleBeginArray();
|
| - case BEGIN_STRING:
|
| - return ParseString();
|
| - case BEGIN_NUMBER:
|
| - return ParseNumber();
|
| - case BEGIN_TRUE:
|
| - return ParseTrue();
|
| - case BEGIN_FALSE:
|
| - return ParseFalse();
|
| - case BEGIN_NULL:
|
| - return ParseNull();
|
| - case UNKNOWN:
|
| - return ReportUnknown("Expected a value.");
|
| - default: {
|
| - // Special case for having been cut off while parsing, wait for more data.
|
| - // This handles things like 'fals' being at the end of the string, we
|
| - // don't know if the next char would be e, completing it, or something
|
| - // else, making it invalid.
|
| - if (!finishing_ && p_.length() < false_len) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| - return ReportFailure("Unexpected token.");
|
| - }
|
| - }
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseString() {
|
| - util::Status result = ParseStringHelper();
|
| - if (result.ok()) {
|
| - ow_->RenderString(key_, parsed_);
|
| - key_.clear();
|
| - parsed_.clear();
|
| - parsed_storage_.clear();
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseStringHelper() {
|
| - // If we haven't seen the start quote, grab it and remember it for later.
|
| - if (string_open_ == 0) {
|
| - string_open_ = *p_.data();
|
| - GOOGLE_DCHECK(string_open_ == '\"' || string_open_ == '\'');
|
| - Advance();
|
| - }
|
| - // Track where we last copied data from so we can minimize copying.
|
| - const char* last = p_.data();
|
| - while (!p_.empty()) {
|
| - const char* data = p_.data();
|
| - if (*data == '\\') {
|
| - // We're about to handle an escape, copy all bytes from last to data.
|
| - if (last < data) {
|
| - parsed_storage_.append(last, data - last);
|
| - last = data;
|
| - }
|
| - // If we ran out of string after the \, cancel or report an error
|
| - // depending on if we expect more data later.
|
| - if (p_.length() == 1) {
|
| - if (!finishing_) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| - return ReportFailure("Closing quote expected in string.");
|
| - }
|
| - // Parse a unicode escape if we found \u in the string.
|
| - if (data[1] == 'u') {
|
| - util::Status result = ParseUnicodeEscape();
|
| - if (!result.ok()) {
|
| - return result;
|
| - }
|
| - // Move last pointer past the unicode escape and continue.
|
| - last = p_.data();
|
| - continue;
|
| - }
|
| - // Handle the standard set of backslash-escaped characters.
|
| - switch (data[1]) {
|
| - case 'b':
|
| - parsed_storage_.push_back('\b');
|
| - break;
|
| - case 'f':
|
| - parsed_storage_.push_back('\f');
|
| - break;
|
| - case 'n':
|
| - parsed_storage_.push_back('\n');
|
| - break;
|
| - case 'r':
|
| - parsed_storage_.push_back('\r');
|
| - break;
|
| - case 't':
|
| - parsed_storage_.push_back('\t');
|
| - break;
|
| - case 'v':
|
| - parsed_storage_.push_back('\v');
|
| - break;
|
| - default:
|
| - parsed_storage_.push_back(data[1]);
|
| - }
|
| - // We handled two characters, so advance past them and continue.
|
| - p_.remove_prefix(2);
|
| - last = p_.data();
|
| - continue;
|
| - }
|
| - // If we found the closing quote note it, advance past it, and return.
|
| - if (*data == string_open_) {
|
| - // If we didn't copy anything, reuse the input buffer.
|
| - if (parsed_storage_.empty()) {
|
| - parsed_ = StringPiece(last, data - last);
|
| - } else {
|
| - if (last < data) {
|
| - parsed_storage_.append(last, data - last);
|
| - last = data;
|
| - }
|
| - parsed_ = StringPiece(parsed_storage_);
|
| - }
|
| - // Clear the quote char so next time we try to parse a string we'll
|
| - // start fresh.
|
| - string_open_ = 0;
|
| - Advance();
|
| - return util::Status::OK;
|
| - }
|
| - // Normal character, just advance past it.
|
| - Advance();
|
| - }
|
| - // If we ran out of characters, copy over what we have so far.
|
| - if (last < p_.data()) {
|
| - parsed_storage_.append(last, p_.data() - last);
|
| - }
|
| - // If we didn't find the closing quote but we expect more data, cancel for now
|
| - if (!finishing_) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| - // End of string reached without a closing quote, report an error.
|
| - string_open_ = 0;
|
| - return ReportFailure("Closing quote expected in string.");
|
| -}
|
| -
|
| -// Converts a unicode escaped character to a decimal value stored in a char32
|
| -// for use in UTF8 encoding utility. We assume that str begins with \uhhhh and
|
| -// convert that from the hex number to a decimal value.
|
| -//
|
| -// There are some security exploits with UTF-8 that we should be careful of:
|
| -// - http://www.unicode.org/reports/tr36/#UTF-8_Exploit
|
| -// - http://sites/intl-eng/design-guide/core-application
|
| -util::Status JsonStreamParser::ParseUnicodeEscape() {
|
| - if (p_.length() < kUnicodeEscapedLength) {
|
| - if (!finishing_) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| - return ReportFailure("Illegal hex string.");
|
| - }
|
| - GOOGLE_DCHECK_EQ('\\', p_.data()[0]);
|
| - GOOGLE_DCHECK_EQ('u', p_.data()[1]);
|
| - uint32 code = 0;
|
| - for (int i = 2; i < kUnicodeEscapedLength; ++i) {
|
| - if (!isxdigit(p_.data()[i])) {
|
| - return ReportFailure("Invalid escape sequence.");
|
| - }
|
| - code = (code << 4) + hex_digit_to_int(p_.data()[i]);
|
| - }
|
| - char buf[UTFmax];
|
| - int len = EncodeAsUTF8Char(code, buf);
|
| - // Advance past the unicode escape.
|
| - p_.remove_prefix(kUnicodeEscapedLength);
|
| - parsed_storage_.append(buf, len);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseNumber() {
|
| - NumberResult number;
|
| - util::Status result = ParseNumberHelper(&number);
|
| - if (result.ok()) {
|
| - switch (number.type) {
|
| - case NumberResult::DOUBLE:
|
| - ow_->RenderDouble(key_, number.double_val);
|
| - key_.clear();
|
| - break;
|
| -
|
| - case NumberResult::INT:
|
| - ow_->RenderInt64(key_, number.int_val);
|
| - key_.clear();
|
| - break;
|
| -
|
| - case NumberResult::UINT:
|
| - ow_->RenderUint64(key_, number.uint_val);
|
| - key_.clear();
|
| - break;
|
| -
|
| - default:
|
| - return ReportFailure("Unable to parse number.");
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) {
|
| - const char* data = p_.data();
|
| - int length = p_.length();
|
| -
|
| - // Look for the first non-numeric character, or the end of the string.
|
| - int index = 0;
|
| - bool floating = false;
|
| - bool negative = data[index] == '-';
|
| - // Find the first character that cannot be part of the number. Along the way
|
| - // detect if the number needs to be parsed as a double.
|
| - // Note that this restricts numbers to the JSON specification, so for example
|
| - // we do not support hex or octal notations.
|
| - for (; index < length; ++index) {
|
| - char c = data[index];
|
| - if (isdigit(c)) continue;
|
| - if (c == '.' || c == 'e' || c == 'E') {
|
| - floating = true;
|
| - continue;
|
| - }
|
| - if (c == '+' || c == '-') continue;
|
| - // Not a valid number character, break out.
|
| - break;
|
| - }
|
| -
|
| - // If the entire input is a valid number, and we may have more content in the
|
| - // future, we abort for now and resume when we know more.
|
| - if (index == length && !finishing_) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| -
|
| - // Create a string containing just the number, so we can use safe_strtoX
|
| - string number = p_.substr(0, index).ToString();
|
| -
|
| - // Floating point number, parse as a double.
|
| - if (floating) {
|
| - if (!safe_strtod(number, &result->double_val)) {
|
| - return ReportFailure("Unable to parse number.");
|
| - }
|
| - result->type = NumberResult::DOUBLE;
|
| - p_.remove_prefix(index);
|
| - return util::Status::OK;
|
| - }
|
| -
|
| - // Positive non-floating point number, parse as a uint64.
|
| - if (!negative) {
|
| - if (!safe_strtou64(number, &result->uint_val)) {
|
| - return ReportFailure("Unable to parse number.");
|
| - }
|
| - result->type = NumberResult::UINT;
|
| - p_.remove_prefix(index);
|
| - return util::Status::OK;
|
| - }
|
| -
|
| - // Negative non-floating point number, parse as an int64.
|
| - if (!safe_strto64(number, &result->int_val)) {
|
| - return ReportFailure("Unable to parse number.");
|
| - }
|
| - result->type = NumberResult::INT;
|
| - p_.remove_prefix(index);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::HandleBeginObject() {
|
| - GOOGLE_DCHECK_EQ('{', *p_.data());
|
| - Advance();
|
| - ow_->StartObject(key_);
|
| - key_.clear();
|
| - stack_.push(ENTRY);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseObjectMid(TokenType type) {
|
| - if (type == UNKNOWN) {
|
| - return ReportUnknown("Expected , or } after key:value pair.");
|
| - }
|
| -
|
| - // Object is complete, advance past the comma and render the EndObject.
|
| - if (type == END_OBJECT) {
|
| - Advance();
|
| - ow_->EndObject();
|
| - return util::Status::OK;
|
| - }
|
| - // Found a comma, advance past it and get ready for an entry.
|
| - if (type == VALUE_SEPARATOR) {
|
| - Advance();
|
| - stack_.push(ENTRY);
|
| - return util::Status::OK;
|
| - }
|
| - // Illegal token after key:value pair.
|
| - return ReportFailure("Expected , or } after key:value pair.");
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseEntry(TokenType type) {
|
| - if (type == UNKNOWN) {
|
| - return ReportUnknown("Expected an object key or }.");
|
| - }
|
| -
|
| - // Close the object and return. This allows for trailing commas.
|
| - if (type == END_OBJECT) {
|
| - ow_->EndObject();
|
| - Advance();
|
| - return util::Status::OK;
|
| - }
|
| -
|
| - util::Status result;
|
| - if (type == BEGIN_STRING) {
|
| - // Key is a string (standard JSON), parse it and store the string.
|
| - result = ParseStringHelper();
|
| - if (result.ok()) {
|
| - key_storage_.clear();
|
| - if (!parsed_storage_.empty()) {
|
| - parsed_storage_.swap(key_storage_);
|
| - key_ = StringPiece(key_storage_);
|
| - } else {
|
| - key_ = parsed_;
|
| - }
|
| - parsed_.clear();
|
| - }
|
| - } else if (type == BEGIN_KEY) {
|
| - // Key is a bare key (back compat), create a StringPiece pointing to it.
|
| - result = ParseKey();
|
| - } else {
|
| - // Unknown key type, report an error.
|
| - result = ReportFailure("Expected an object key or }.");
|
| - }
|
| - // On success we next expect an entry mid ':' then an object mid ',' or '}'
|
| - if (result.ok()) {
|
| - stack_.push(OBJ_MID);
|
| - stack_.push(ENTRY_MID);
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseEntryMid(TokenType type) {
|
| - if (type == UNKNOWN) {
|
| - return ReportUnknown("Expected : between key:value pair.");
|
| - }
|
| - if (type == ENTRY_SEPARATOR) {
|
| - Advance();
|
| - stack_.push(VALUE);
|
| - return util::Status::OK;
|
| - }
|
| - return ReportFailure("Expected : between key:value pair.");
|
| -}
|
| -
|
| -util::Status JsonStreamParser::HandleBeginArray() {
|
| - GOOGLE_DCHECK_EQ('[', *p_.data());
|
| - Advance();
|
| - ow_->StartList(key_);
|
| - key_.clear();
|
| - stack_.push(ARRAY_VALUE);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseArrayValue(TokenType type) {
|
| - if (type == UNKNOWN) {
|
| - return ReportUnknown("Expected a value or ] within an array.");
|
| - }
|
| -
|
| - if (type == END_ARRAY) {
|
| - ow_->EndList();
|
| - Advance();
|
| - return util::Status::OK;
|
| - }
|
| -
|
| - // The ParseValue call may push something onto the stack so we need to make
|
| - // sure an ARRAY_MID is after it, so we push it on now.
|
| - stack_.push(ARRAY_MID);
|
| - util::Status result = ParseValue(type);
|
| - if (result == util::Status::CANCELLED) {
|
| - // If we were cancelled, pop back off the ARRAY_MID so we don't try to
|
| - // push it on again when we try over.
|
| - stack_.pop();
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseArrayMid(TokenType type) {
|
| - if (type == UNKNOWN) {
|
| - return ReportUnknown("Expected , or ] after array value.");
|
| - }
|
| -
|
| - if (type == END_ARRAY) {
|
| - ow_->EndList();
|
| - Advance();
|
| - return util::Status::OK;
|
| - }
|
| -
|
| - // Found a comma, advance past it and expect an array value next.
|
| - if (type == VALUE_SEPARATOR) {
|
| - Advance();
|
| - stack_.push(ARRAY_VALUE);
|
| - return util::Status::OK;
|
| - }
|
| - // Illegal token after array value.
|
| - return ReportFailure("Expected , or ] after array value.");
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseTrue() {
|
| - ow_->RenderBool(key_, true);
|
| - key_.clear();
|
| - p_.remove_prefix(true_len);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseFalse() {
|
| - ow_->RenderBool(key_, false);
|
| - key_.clear();
|
| - p_.remove_prefix(false_len);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseNull() {
|
| - ow_->RenderNull(key_);
|
| - key_.clear();
|
| - p_.remove_prefix(null_len);
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ReportFailure(StringPiece message) {
|
| - static const int kContextLength = 20;
|
| - const char* p_start = p_.data();
|
| - const char* json_start = json_.data();
|
| - const char* begin = max(p_start - kContextLength, json_start);
|
| - const char* end = min(p_start + kContextLength, json_start + json_.size());
|
| - StringPiece segment(begin, end - begin);
|
| - string location(p_start - begin, ' ');
|
| - location.push_back('^');
|
| - return util::Status(util::error::INVALID_ARGUMENT,
|
| - StrCat(message, "\n", segment, "\n", location));
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ReportUnknown(StringPiece message) {
|
| - // If we aren't finishing the parse, cancel parsing and try later.
|
| - if (!finishing_) {
|
| - return util::Status::CANCELLED;
|
| - }
|
| - if (p_.empty()) {
|
| - return ReportFailure(StrCat("Unexpected end of string. ", message));
|
| - }
|
| - return ReportFailure(message);
|
| -}
|
| -
|
| -void JsonStreamParser::SkipWhitespace() {
|
| - while (!p_.empty() && ascii_isspace(*p_.data())) {
|
| - Advance();
|
| - }
|
| -}
|
| -
|
| -void JsonStreamParser::Advance() {
|
| - // Advance by moving one UTF8 character while making sure we don't go beyond
|
| - // the length of StringPiece.
|
| - p_.remove_prefix(
|
| - min<int>(p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length())));
|
| -}
|
| -
|
| -util::Status JsonStreamParser::ParseKey() {
|
| - StringPiece original = p_;
|
| - if (!ConsumeKey(&p_, &key_)) {
|
| - return ReportFailure("Invalid key or variable name.");
|
| - }
|
| - // If we consumed everything but expect more data, reset p_ and cancel since
|
| - // we can't know if the key was complete or not.
|
| - if (!finishing_ && p_.empty()) {
|
| - p_ = original;
|
| - return util::Status::CANCELLED;
|
| - }
|
| - // Since we aren't using the key storage, clear it out.
|
| - key_storage_.clear();
|
| - return util::Status::OK;
|
| -}
|
| -
|
| -JsonStreamParser::TokenType JsonStreamParser::GetNextTokenType() {
|
| - SkipWhitespace();
|
| -
|
| - int size = p_.size();
|
| - if (size == 0) {
|
| - // If we ran out of data, report unknown and we'll place the previous parse
|
| - // type onto the stack and try again when we have more data.
|
| - return UNKNOWN;
|
| - }
|
| - // TODO(sven): Split this method based on context since different contexts
|
| - // support different tokens. Would slightly speed up processing?
|
| - const char* data = p_.data();
|
| - if (*data == '\"' || *data == '\'') return BEGIN_STRING;
|
| - if (*data == '-' || ('0' <= *data && *data <= '9')) {
|
| - return BEGIN_NUMBER;
|
| - }
|
| - if (size >= true_len && !strncmp(data, "true", true_len)) {
|
| - return BEGIN_TRUE;
|
| - }
|
| - if (size >= false_len && !strncmp(data, "false", false_len)) {
|
| - return BEGIN_FALSE;
|
| - }
|
| - if (size >= null_len && !strncmp(data, "null", null_len)) {
|
| - return BEGIN_NULL;
|
| - }
|
| - if (*data == '{') return BEGIN_OBJECT;
|
| - if (*data == '}') return END_OBJECT;
|
| - if (*data == '[') return BEGIN_ARRAY;
|
| - if (*data == ']') return END_ARRAY;
|
| - if (*data == ':') return ENTRY_SEPARATOR;
|
| - if (*data == ',') return VALUE_SEPARATOR;
|
| - if (MatchKey(p_)) {
|
| - return BEGIN_KEY;
|
| - }
|
| -
|
| - // We don't know that we necessarily have an invalid token here, just that we
|
| - // can't parse what we have so far. So we don't report an error and just
|
| - // return UNKNOWN so we can try again later when we have more data, or if we
|
| - // finish and we have leftovers.
|
| - return UNKNOWN;
|
| -}
|
| -
|
| -} // namespace converter
|
| -} // namespace util
|
| -} // namespace protobuf
|
| -} // namespace google
|
|
|