text:
// 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/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/internal/object_writer.h>
#include <google/protobuf/util/internal/json_escaping.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/mathlimits.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::CANCELLED;
using util::error::INTERNAL;
using util::error::INVALID_ARGUMENT;
}  // namespace error

namespace converter {

// Number of digits in an escaped UTF-16 code unit ('\\' 'u' X X X X)
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),
      chunk_storage_(),
      coerce_to_utf8_(false),
      allow_empty_null_(false),
      loose_float_number_conversion_(false) {
  // Initialize the stack with a single value to be parsed.
  stack_.push(VALUE);
}

JsonStreamParser::~JsonStreamParser() {}

util::Status JsonStreamParser::Parse(StringPiece json) {
  StringPiece chunk = json;
  // 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()) {
    // Don't point chunk to leftover_ because leftover_ will be updated in
    // ParseChunk(chunk).
    chunk_storage_.swap(leftover_);
    StrAppend(&chunk_storage_, json);
    chunk = StringPiece(chunk_storage_);
  }

  // Find the structurally valid UTF8 prefix and parse only that.
  int n = internal::UTF8SpnStructurallyValid(chunk);
  if (n > 0) {
    util::Status status = ParseChunk(chunk.substr(0, n));

    // Any leftover characters are stashed in leftover_ for later parsing when
    // there is more data available.
    StrAppend(&leftover_, chunk.substr(n));
    return status;
  } else {
    leftover_.assign(chunk.data(), chunk.size());
    return util::Status();
  }
}

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();
  }

  // Storage for UTF8-coerced string.
  google::protobuf::scoped_array<char> utf8;
  if (coerce_to_utf8_) {
    utf8.reset(new char[leftover_.size()]);
    char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' ');
    p_ = json_ = StringPiece(coerced, leftover_.size());
  } else {
    p_ = json_ = leftover_;
    if (!internal::IsStructurallyValidUTF8(leftover_)) {
      return ReportFailure("Encountered non UTF-8 code points.");
    }
  }

  // Parse the remainder in finishing mode, which reports errors for things like
  // unterminated strings or unknown tokens that would normally be retried.
  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) {
  // Do not do any work if the chunk is empty.
  if (chunk.empty()) return util::Status();

  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();
}

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(error::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()) {
          StrAppend(&key_storage_, key_);
          key_ = StringPiece(key_storage_);
        }
        result = util::Status();
      }
      return result;
    }
  }
  return util::Status();
}

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: {
      if (allow_empty_null_ && IsEmptyNullAllowed(type)) {
        return ParseEmptyNull();
      }

      // 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(error::CANCELLED, "");
      }
      return ReportFailure("Unexpected token.");
    }
  }
}

util::Status JsonStreamParser::ParseString() {
  util::Status result = ParseStringHelper();
  if (result.ok()) {
    ow_->RenderString(key_, parsed_);
    key_ = StringPiece();
    parsed_ = StringPiece();
    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);
      }
      // 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(error::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);
        }
        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();
    }
    // 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(error::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(error::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]);
  }
  if (code >= JsonEscaping::kMinHighSurrogate &&
      code <= JsonEscaping::kMaxHighSurrogate) {
    if (p_.length() < 2 * kUnicodeEscapedLength) {
      if (!finishing_) {
        return util::Status(error::CANCELLED, "");
      }
      if (!coerce_to_utf8_) {
        return ReportFailure("Missing low surrogate.");
      }
    } else if (p_.data()[kUnicodeEscapedLength] == '\\' &&
               p_.data()[kUnicodeEscapedLength + 1] == 'u') {
      uint32 low_code = 0;
      for (int i = kUnicodeEscapedLength + 2; i < 2 * kUnicodeEscapedLength;
           ++i) {
        if (!isxdigit(p_.data()[i])) {
          return ReportFailure("Invalid escape sequence.");
        }
        low_code = (low_code << 4) + hex_digit_to_int(p_.data()[i]);
      }
      if (low_code >= JsonEscaping::kMinLowSurrogate &&
          low_code <= JsonEscaping::kMaxLowSurrogate) {
        // Convert UTF-16 surrogate pair to 21-bit Unicode codepoint.
        code = (((code & 0x3FF) << 10) | (low_code & 0x3FF)) +
               JsonEscaping::kMinSupplementaryCodePoint;
        // Advance past the first code unit escape.
        p_.remove_prefix(kUnicodeEscapedLength);
      } else if (!coerce_to_utf8_) {
        return ReportFailure("Invalid low surrogate.");
      }
    } else if (!coerce_to_utf8_) {
      return ReportFailure("Missing low surrogate.");
    }
  }
  if (!coerce_to_utf8_ && !IsValidCodePoint(code)) {
    return ReportFailure("Invalid unicode code point.");
  }
  char buf[UTFmax];
  int len = EncodeAsUTF8Char(code, buf);
  // Advance past the [final] code unit escape.
  p_.remove_prefix(kUnicodeEscapedLength);
  parsed_storage_.append(buf, len);
  return util::Status();
}

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_ = StringPiece();
        break;

      case NumberResult::INT:
        ow_->RenderInt64(key_, number.int_val);
        key_ = StringPiece();
        break;

      case NumberResult::UINT:
        ow_->RenderUint64(key_, number.uint_val);
        key_ = StringPiece();
        break;

      default:
        return ReportFailure("Unable to parse number.");
    }
  }
  return result;
}

util::Status JsonStreamParser::ParseDoubleHelper(
    const string& number, NumberResult* result) {
  if (!safe_strtod(number, &result->double_val)) {
    return ReportFailure("Unable to parse number.");
  }
  if (!loose_float_number_conversion_ &&
      !MathLimits<double>::IsFinite(result->double_val)) {
    return ReportFailure("Number exceeds the range of double.");
  }
  result->type = NumberResult::DOUBLE;
  return util::Status();
}

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 == '-' || c == 'x') 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(error::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) {
    util::Status status = ParseDoubleHelper(number, result);
    if (status.ok()) {
      p_.remove_prefix(index);
    }
    return status;
  }

  // Positive non-floating point number, parse as a uint64.
  if (!negative) {
    // Octal/Hex numbers are not valid JSON values.
    if (number.length() >= 2 && number[0] == '0') {
      return ReportFailure("Octal/hex numbers are not valid JSON values.");
    }
    if (safe_strtou64(number, &result->uint_val)) {
      result->type = NumberResult::UINT;
      p_.remove_prefix(index);
      return util::Status();
    } else {
      // If the value is too large, parse it as double.
      util::Status status = ParseDoubleHelper(number, result);
      if (status.ok()) {
        p_.remove_prefix(index);
      }
      return status;
    }
  }

  // Octal/Hex numbers are not valid JSON values.
  if (number.length() >= 3 && number[1] == '0') {
    return ReportFailure("Octal/hex numbers are not valid JSON values.");
  }
  // Negative non-floating point number, parse as an int64.
  if (safe_strto64(number, &result->int_val)) {
    result->type = NumberResult::INT;
    p_.remove_prefix(index);
    return util::Status();
  } else {
    // If the value is too large, parse it as double.
    util::Status status = ParseDoubleHelper(number, result);
    if (status.ok()) {
      p_.remove_prefix(index);
    }
    return status;
  }
}

util::Status JsonStreamParser::HandleBeginObject() {
  GOOGLE_DCHECK_EQ('{', *p_.data());
  Advance();
  ow_->StartObject(key_);
  key_ = StringPiece();
  stack_.push(ENTRY);
  return util::Status();
}

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();
  }
  // Found a comma, advance past it and get ready for an entry.
  if (type == VALUE_SEPARATOR) {
    Advance();
    stack_.push(ENTRY);
    return util::Status();
  }
  // 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();
  }

  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_ = StringPiece();
    }
  } 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();
  }
  return ReportFailure("Expected : between key:value pair.");
}

util::Status JsonStreamParser::HandleBeginArray() {
  GOOGLE_DCHECK_EQ('[', *p_.data());
  Advance();
  ow_->StartList(key_);
  key_ = StringPiece();
  stack_.push(ARRAY_VALUE);
  return util::Status();
}

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();
  }

  // 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. Also, the parsing of
  // empty-null array value is relying on this ARRAY_MID token.
  stack_.push(ARRAY_MID);
  util::Status result = ParseValue(type);
  if (result == util::Status(error::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();
  }

  // Found a comma, advance past it and expect an array value next.
  if (type == VALUE_SEPARATOR) {
    Advance();
    stack_.push(ARRAY_VALUE);
    return util::Status();
  }
  // Illegal token after array value.
  return ReportFailure("Expected , or ] after array value.");
}

util::Status JsonStreamParser::ParseTrue() {
  ow_->RenderBool(key_, true);
  key_ = StringPiece();
  p_.remove_prefix(true_len);
  return util::Status();
}

util::Status JsonStreamParser::ParseFalse() {
  ow_->RenderBool(key_, false);
  key_ = StringPiece();
  p_.remove_prefix(false_len);
  return util::Status();
}

util::Status JsonStreamParser::ParseNull() {
  ow_->RenderNull(key_);
  key_ = StringPiece();
  p_.remove_prefix(null_len);
  return util::Status();
}

util::Status JsonStreamParser::ParseEmptyNull() {
  ow_->RenderNull(key_);
  key_ = StringPiece();
  return util::Status();
}

bool JsonStreamParser::IsEmptyNullAllowed(TokenType type) {
  if (stack_.empty()) return false;
  return (stack_.top() == ARRAY_MID && type == VALUE_SEPARATOR) ||
         stack_.top() == OBJ_MID;
}

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 = std::max(p_start - kContextLength, json_start);
  const char* end =
      std::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(error::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(std::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(error::CANCELLED, "");
  }
  // Since we aren't using the key storage, clear it out.
  key_storage_.clear();
  return util::Status();
}

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


text:
// 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.

#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__

#include <string>

#include <google/protobuf/stubs/common.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

// LocationTrackerInterface is an interface for classes that track
// the location information for the purpose of error reporting.
class LIBPROTOBUF_EXPORT LocationTrackerInterface {
 public:
  virtual ~LocationTrackerInterface() {}

  // Returns the object location as human readable string.
  virtual string ToString() const = 0;

 protected:
  LocationTrackerInterface() {}

 private:
  // Please do not add any data members to this class.
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LocationTrackerInterface);
};

}  // namespace converter
}  // namespace util
}  // namespace protobuf

}  // namespace google
#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__

mime_type: text/plain


text:
// 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.

#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__

#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif

#include <google/protobuf/stubs/casts.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/internal/object_writer.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

// An StructuredObjectWriter is an ObjectWriter for writing
// tree-structured data in a stream of events representing objects
// and collections. Implementation of this interface can be used to
// write an object stream to an in-memory structure, protobufs,
// JSON, XML, or any other output format desired. The ObjectSource
// interface is typically used as the source of an object stream.
//
// See JsonObjectWriter for a sample implementation of
// StructuredObjectWriter and its use.
//
// Derived classes could be thread-unsafe.
class LIBPROTOBUF_EXPORT StructuredObjectWriter : public ObjectWriter {
 public:
  virtual ~StructuredObjectWriter() {}

 protected:
  // A base element class for subclasses to extend, makes tracking state easier.
  //
  // StructuredObjectWriter behaves as a visitor. BaseElement represents a node
  // in the input tree. Implementation of StructuredObjectWriter should also
  // extend BaseElement to keep track of the location in the input tree.
  class LIBPROTOBUF_EXPORT BaseElement {
   public:
    // Takes ownership of the parent Element.
    explicit BaseElement(BaseElement* parent)
        : parent_(parent), level_(parent == NULL ? 0 : parent->level() + 1) {}
    virtual ~BaseElement() {}

    // Releases ownership of the parent and returns a pointer to it.
    template <typename ElementType>
    ElementType* pop() {
      return down_cast<ElementType*>(parent_.release());
    }

    // Returns true if this element is the root.
    bool is_root() const { return parent_ == NULL; }

    // Returns the number of hops from this element to the root element.
    int level() const { return level_; }

   protected:
    // Returns pointer to parent element without releasing ownership.
    virtual BaseElement* parent() const { return parent_.get(); }

   private:
    // Pointer to the parent Element.
    google::protobuf::scoped_ptr<BaseElement> parent_;

    // Number of hops to the root Element.
    // The root Element has NULL parent_ and a level_ of 0.
    const int level_;

    GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseElement);
  };

  StructuredObjectWriter() {}

  // Returns the current element. Used for indentation and name overrides.
  virtual BaseElement* element() = 0;

 private:
  // Do not add any data members to this class.
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StructuredObjectWriter);
};

}  // namespace converter
}  // namespace util
}  // namespace protobuf

}  // namespace google
#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__


text:
// 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/proto_writer.h>

#include <functional>
#include <stack>

#include <google/protobuf/stubs/once.h>
#include <google/protobuf/stubs/time.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/util/internal/field_mask_utility.h>
#include <google/protobuf/util/internal/object_location_tracker.h>
#include <google/protobuf/util/internal/constants.h>
#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/statusor.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

using google::protobuf::internal::WireFormatLite;
using google::protobuf::io::CodedOutputStream;
using util::error::INVALID_ARGUMENT;
using util::Status;
using util::StatusOr;

ProtoWriter::ProtoWriter(TypeResolver* type_resolver,
                         const google::protobuf::Type& type,
                         strings::ByteSink* output, ErrorListener* listener)
    : master_type_(type),
      typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
      own_typeinfo_(true),
      done_(false),
      ignore_unknown_fields_(false),
      use_lower_camel_for_enums_(false),
      element_(NULL),
      size_insert_(),
      output_(output),
      buffer_(),
      adapter_(&buffer_),
      stream_(new CodedOutputStream(&adapter_)),
      listener_(listener),
      invalid_depth_(0),
      tracker_(new ObjectLocationTracker()) {}

ProtoWriter::ProtoWriter(const TypeInfo* typeinfo,
                         const google::protobuf::Type& type,
                         strings::ByteSink* output, ErrorListener* listener)
    : master_type_(type),
      typeinfo_(typeinfo),
      own_typeinfo_(false),
      done_(false),
      ignore_unknown_fields_(false),
      use_lower_camel_for_enums_(false),
      element_(NULL),
      size_insert_(),
      output_(output),
      buffer_(),
      adapter_(&buffer_),
      stream_(new CodedOutputStream(&adapter_)),
      listener_(listener),
      invalid_depth_(0),
      tracker_(new ObjectLocationTracker()) {}

ProtoWriter::~ProtoWriter() {
  if (own_typeinfo_) {
    delete typeinfo_;
  }
  if (element_ == NULL) return;
  // Cleanup explicitly in order to avoid destructor stack overflow when input
  // is deeply nested.
  // Cast to BaseElement to avoid doing additional checks (like missing fields)
  // during pop().
  google::protobuf::scoped_ptr<BaseElement> element(
      static_cast<BaseElement*>(element_.get())->pop<BaseElement>());
  while (element != NULL) {
    element.reset(element->pop<BaseElement>());
  }
}

namespace {

// Writes an INT32 field, including tag to the stream.
inline Status WriteInt32(int field_number, const DataPiece& data,
                         CodedOutputStream* stream) {
  StatusOr<int32> i32 = data.ToInt32();
  if (i32.ok()) {
    WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
  }
  return i32.status();
}

// writes an SFIXED32 field, including tag, to the stream.
inline Status WriteSFixed32(int field_number, const DataPiece& data,
                            CodedOutputStream* stream) {
  StatusOr<int32> i32 = data.ToInt32();
  if (i32.ok()) {
    WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
  }
  return i32.status();
}

// Writes an SINT32 field, including tag, to the stream.
inline Status WriteSInt32(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<int32> i32 = data.ToInt32();
  if (i32.ok()) {
    WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
  }
  return i32.status();
}

// Writes a FIXED32 field, including tag, to the stream.
inline Status WriteFixed32(int field_number, const DataPiece& data,
                           CodedOutputStream* stream) {
  StatusOr<uint32> u32 = data.ToUint32();
  if (u32.ok()) {
    WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
  }
  return u32.status();
}

// Writes a UINT32 field, including tag, to the stream.
inline Status WriteUInt32(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<uint32> u32 = data.ToUint32();
  if (u32.ok()) {
    WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
  }
  return u32.status();
}

// Writes an INT64 field, including tag, to the stream.
inline Status WriteInt64(int field_number, const DataPiece& data,
                         CodedOutputStream* stream) {
  StatusOr<int64> i64 = data.ToInt64();
  if (i64.ok()) {
    WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
  }
  return i64.status();
}

// Writes an SFIXED64 field, including tag, to the stream.
inline Status WriteSFixed64(int field_number, const DataPiece& data,
                            CodedOutputStream* stream) {
  StatusOr<int64> i64 = data.ToInt64();
  if (i64.ok()) {
    WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
  }
  return i64.status();
}

// Writes an SINT64 field, including tag, to the stream.
inline Status WriteSInt64(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<int64> i64 = data.ToInt64();
  if (i64.ok()) {
    WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
  }
  return i64.status();
}

// Writes a FIXED64 field, including tag, to the stream.
inline Status WriteFixed64(int field_number, const DataPiece& data,
                           CodedOutputStream* stream) {
  StatusOr<uint64> u64 = data.ToUint64();
  if (u64.ok()) {
    WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
  }
  return u64.status();
}

// Writes a UINT64 field, including tag, to the stream.
inline Status WriteUInt64(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<uint64> u64 = data.ToUint64();
  if (u64.ok()) {
    WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
  }
  return u64.status();
}

// Writes a DOUBLE field, including tag, to the stream.
inline Status WriteDouble(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<double> d = data.ToDouble();
  if (d.ok()) {
    WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
  }
  return d.status();
}

// Writes a FLOAT field, including tag, to the stream.
inline Status WriteFloat(int field_number, const DataPiece& data,
                         CodedOutputStream* stream) {
  StatusOr<float> f = data.ToFloat();
  if (f.ok()) {
    WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
  }
  return f.status();
}

// Writes a BOOL field, including tag, to the stream.
inline Status WriteBool(int field_number, const DataPiece& data,
                        CodedOutputStream* stream) {
  StatusOr<bool> b = data.ToBool();
  if (b.ok()) {
    WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
  }
  return b.status();
}

// Writes a BYTES field, including tag, to the stream.
inline Status WriteBytes(int field_number, const DataPiece& data,
                         CodedOutputStream* stream) {
  StatusOr<string> c = data.ToBytes();
  if (c.ok()) {
    WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
  }
  return c.status();
}

// Writes a STRING field, including tag, to the stream.
inline Status WriteString(int field_number, const DataPiece& data,
                          CodedOutputStream* stream) {
  StatusOr<string> s = data.ToString();
  if (s.ok()) {
    WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
  }
  return s.status();
}

// Writes an ENUM field, including tag, to the stream.
inline Status WriteEnum(int field_number, const DataPiece& data,
                        const google::protobuf::Enum* enum_type,
                        CodedOutputStream* stream,
                        bool use_lower_camel_for_enums) {
  StatusOr<int> e = data.ToEnum(enum_type, use_lower_camel_for_enums);
  if (e.ok()) {
    WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
  }
  return e.status();
}

// Given a google::protobuf::Type, returns the set of all required fields.
std::set<const google::protobuf::Field*> GetRequiredFields(
    const google::protobuf::Type& type) {
  std::set<const google::protobuf::Field*> required;
  for (int i = 0; i < type.fields_size(); i++) {
    const google::protobuf::Field& field = type.fields(i);
    if (field.cardinality() ==
        google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
      required.insert(&field);
    }
  }
  return required;
}

}  // namespace

ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo,
                                        const google::protobuf::Type& type,
                                        ProtoWriter* enclosing)
    : BaseElement(NULL),
      ow_(enclosing),
      parent_field_(NULL),
      typeinfo_(typeinfo),
      proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
      type_(type),
      size_index_(-1),
      array_index_(-1),
      // oneof_indices_ values are 1-indexed (0 means not present).
      oneof_indices_(type.oneofs_size() + 1) {
  if (!proto3_) {
    required_fields_ = GetRequiredFields(type_);
  }
}

ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent,
                                        const google::protobuf::Field* field,
                                        const google::protobuf::Type& type,
                                        bool is_list)
    : BaseElement(parent),
      ow_(this->parent()->ow_),
      parent_field_(field),
      typeinfo_(this->parent()->typeinfo_),
      proto3_(type.syntax() == google::protobuf::SYNTAX_PROTO3),
      type_(type),
      size_index_(
          !is_list && field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE
              ? ow_->size_insert_.size()
              : -1),
      array_index_(is_list ? 0 : -1),
      // oneof_indices_ values are 1-indexed (0 means not present).
      oneof_indices_(type_.oneofs_size() + 1) {
  if (!is_list) {
    if (ow_->IsRepeated(*field)) {
      // Update array_index_ if it is an explicit list.
      if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
    } else if (!proto3_) {
      // For required fields tracking.
      this->parent()->RegisterField(field);
    }

    if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
      if (!proto3_) {
        required_fields_ = GetRequiredFields(type_);
      }
      int start_pos = ow_->stream_->ByteCount();
      // length of serialized message is the final buffer position minus
      // starting buffer position, plus length adjustments for size fields
      // of any nested messages. We start with -start_pos here, so we only
      // need to add the final buffer position to it at the end.
      SizeInfo info = {start_pos, -start_pos};
      ow_->size_insert_.push_back(info);
    }
  }
}

ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() {
  if (!proto3_) {
    // Calls the registered error listener for any required field(s) not yet
    // seen.
    for (std::set<const google::protobuf::Field*>::iterator it =
             required_fields_.begin();
         it != required_fields_.end(); ++it) {
      ow_->MissingField((*it)->name());
    }
  }
  // Computes the total number of proto bytes used by a message, also adjusts
  // the size of all parent messages by the length of this size field.
  // If size_index_ < 0, this is not a message, so no size field is added.
  if (size_index_ >= 0) {
    // Add the final buffer position to compute the total length of this
    // serialized message. The stored value (before this addition) already
    // contains the total length of the size fields of all nested messages
    // minus the initial buffer position.
    ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
    // Calculate the length required to serialize the size field of the
    // message, and propagate this additional size information upward to
    // all enclosing messages.
    int size = ow_->size_insert_[size_index_].size;
    int length = CodedOutputStream::VarintSize32(size);
    for (ProtoElement* e = parent(); e != NULL; e = e->parent()) {
      // Only nested messages have size field, lists do not have size field.
      if (e->size_index_ >= 0) {
        ow_->size_insert_[e->size_index_].size += length;
      }
    }
  }
  return BaseElement::pop<ProtoElement>();
}

void ProtoWriter::ProtoElement::RegisterField(
    const google::protobuf::Field* field) {
  if (!required_fields_.empty() &&
      field->cardinality() ==
          google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
    required_fields_.erase(field);
  }
}

string ProtoWriter::ProtoElement::ToString() const {
  if (parent() == NULL) return "";
  string loc = parent()->ToString();
  if (!ow_->IsRepeated(*parent_field_) ||
      parent()->parent_field_ != parent_field_) {
    string name = parent_field_->name();
    int i = 0;
    while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
    if (i > 0 && i == name.size()) {  // safe field name
      if (loc.empty()) {
        loc = name;
      } else {
        StrAppend(&loc, ".", name);
      }
    } else {
      StrAppend(&loc, "[\"", CEscape(name), "\"]");
    }
  }
  if (ow_->IsRepeated(*parent_field_) && array_index_ > 0) {
    StrAppend(&loc, "[", array_index_ - 1, "]");
  }
  return loc.empty() ? "." : loc;
}

bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) {
  return oneof_indices_[index];
}

void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) {
  oneof_indices_[index] = true;
}

void ProtoWriter::InvalidName(StringPiece unknown_name, StringPiece message) {
  listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
}

void ProtoWriter::InvalidValue(StringPiece type_name, StringPiece value) {
  listener_->InvalidValue(location(), type_name, value);
}

void ProtoWriter::MissingField(StringPiece missing_name) {
  listener_->MissingField(location(), missing_name);
}

ProtoWriter* ProtoWriter::StartObject(StringPiece name) {
  // Starting the root message. Create the root ProtoElement and return.
  if (element_ == NULL) {
    if (!name.empty()) {
      InvalidName(name, "Root element should not be named.");
    }
    element_.reset(new ProtoElement(typeinfo_, master_type_, this));
    return this;
  }

  const google::protobuf::Field* field = NULL;
  field = BeginNamed(name, false);
  if (field == NULL) return this;

  // Check to see if this field is a oneof and that no oneof in that group has
  // already been set.
  if (!ValidOneof(*field, name)) {
    ++invalid_depth_;
    return this;
  }

  const google::protobuf::Type* type = LookupType(field);
  if (type == NULL) {
    ++invalid_depth_;
    InvalidName(name,
                StrCat("Missing descriptor for field: ", field->type_url()));
    return this;
  }

  return StartObjectField(*field, *type);
}

ProtoWriter* ProtoWriter::EndObject() {
  if (invalid_depth_ > 0) {
    --invalid_depth_;
    return this;
  }

  if (element_ != NULL) {
    element_.reset(element_->pop());
  }

  // If ending the root element,
  // then serialize the full message with calculated sizes.
  if (element_ == NULL) {
    WriteRootMessage();
  }
  return this;
}

ProtoWriter* ProtoWriter::StartList(StringPiece name) {
  const google::protobuf::Field* field = BeginNamed(name, true);
  if (field == NULL) return this;

  if (!ValidOneof(*field, name)) {
    ++invalid_depth_;
    return this;
  }

  const google::protobuf::Type* type = LookupType(field);
  if (type == NULL) {
    ++invalid_depth_;
    InvalidName(name,
                StrCat("Missing descriptor for field: ", field->type_url()));
    return this;
  }

  return StartListField(*field, *type);
}

ProtoWriter* ProtoWriter::EndList() {
  if (invalid_depth_ > 0) {
    --invalid_depth_;
  } else if (element_ != NULL) {
    element_.reset(element_->pop());
  }
  return this;
}

ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name,
                                          const DataPiece& data) {
  Status status;
  if (invalid_depth_ > 0) return this;

  const google::protobuf::Field* field = Lookup(name);
  if (field == NULL) return this;

  if (!ValidOneof(*field, name)) return this;

  const google::protobuf::Type* type = LookupType(field);
  if (type == NULL) {
    InvalidName(name,
                StrCat("Missing descriptor for field: ", field->type_url()));
    return this;
  }

  return RenderPrimitiveField(*field, *type, data);
}

bool ProtoWriter::ValidOneof(const google::protobuf::Field& field,
                             StringPiece unnormalized_name) {
  if (element_ == NULL) return true;

  if (field.oneof_index() > 0) {
    if (element_->IsOneofIndexTaken(field.oneof_index())) {
      InvalidValue(
          "oneof",
          StrCat("oneof field '",
                 element_->type().oneofs(field.oneof_index() - 1),
                 "' is already set. Cannot set '", unnormalized_name, "'"));
      return false;
    }
    element_->TakeOneofIndex(field.oneof_index());
  }
  return true;
}

bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) {
  return field.cardinality() ==
         google::protobuf::Field_Cardinality_CARDINALITY_REPEATED;
}

ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field,
                                           const google::protobuf::Type& type) {
    WriteTag(field);
  element_.reset(new ProtoElement(element_.release(), &field, type, false));
  return this;
}

ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field,
                                         const google::protobuf::Type& type) {
  element_.reset(new ProtoElement(element_.release(), &field, type, true));
  return this;
}

ProtoWriter* ProtoWriter::RenderPrimitiveField(
    const google::protobuf::Field& field, const google::protobuf::Type& type,
    const DataPiece& data) {
  Status status;

  // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
  // error location reporting and required field accounting.
  //
  // For proto3, since there is no required field tracking, we only need to push
  // ProtoElement for error cases.
  if (!element_->proto3()) {
    element_.reset(new ProtoElement(element_.release(), &field, type, false));
  }

  if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN ||
      field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
    // Push a ProtoElement for location reporting purposes.
    if (element_->proto3()) {
      element_.reset(new ProtoElement(element_.release(), &field, type, false));
    }
    InvalidValue(field.type_url().empty()
                     ? google::protobuf::Field_Kind_Name(field.kind())
                     : field.type_url(),
                 data.ValueAsStringOrDefault(""));
    element_.reset(element()->pop());
    return this;
  }

  switch (field.kind()) {
    case google::protobuf::Field_Kind_TYPE_INT32: {
      status = WriteInt32(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_SFIXED32: {
      status = WriteSFixed32(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_SINT32: {
      status = WriteSInt32(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_FIXED32: {
      status = WriteFixed32(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_UINT32: {
      status = WriteUInt32(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_INT64: {
      status = WriteInt64(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_SFIXED64: {
      status = WriteSFixed64(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_SINT64: {
      status = WriteSInt64(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_FIXED64: {
      status = WriteFixed64(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_UINT64: {
      status = WriteUInt64(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_DOUBLE: {
      status = WriteDouble(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_FLOAT: {
      status = WriteFloat(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_BOOL: {
      status = WriteBool(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_BYTES: {
      status = WriteBytes(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_STRING: {
      status = WriteString(field.number(), data, stream_.get());
      break;
    }
    case google::protobuf::Field_Kind_TYPE_ENUM: {
      status = WriteEnum(field.number(), data,
                         typeinfo_->GetEnumByTypeUrl(field.type_url()),
                         stream_.get(), use_lower_camel_for_enums_);
      break;
    }
    default:  // TYPE_GROUP or TYPE_MESSAGE
      status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
  }

  if (!status.ok()) {
    // Push a ProtoElement for location reporting purposes.
    if (element_->proto3()) {
      element_.reset(new ProtoElement(element_.release(), &field, type, false));
    }
    InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
                 status.error_message());
    element_.reset(element()->pop());
    return this;
  }

  if (!element_->proto3()) element_.reset(element()->pop());

  return this;
}

const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name,
                                                       bool is_list) {
  if (invalid_depth_ > 0) {
    ++invalid_depth_;
    return NULL;
  }
  const google::protobuf::Field* field = Lookup(name);
  if (field == NULL) {
    ++invalid_depth_;
    // InvalidName() already called in Lookup().
    return NULL;
  }
  if (is_list && !IsRepeated(*field)) {
    ++invalid_depth_;
    InvalidName(name, "Proto field is not repeating, cannot start list.");
    return NULL;
  }
  return field;
}

const google::protobuf::Field* ProtoWriter::Lookup(
    StringPiece unnormalized_name) {
  ProtoElement* e = element();
  if (e == NULL) {
    InvalidName(unnormalized_name, "Root element must be a message.");
    return NULL;
  }
  if (unnormalized_name.empty()) {
    // Objects in repeated field inherit the same field descriptor.
    if (e->parent_field() == NULL) {
      InvalidName(unnormalized_name, "Proto fields must have a name.");
    } else if (!IsRepeated(*e->parent_field())) {
      InvalidName(unnormalized_name, "Proto fields must have a name.");
      return NULL;
    }
    return e->parent_field();
  }
  const google::protobuf::Field* field =
      typeinfo_->FindField(&e->type(), unnormalized_name);
  if (field == NULL && !ignore_unknown_fields_) {
    InvalidName(unnormalized_name, "Cannot find field.");
  }
  return field;
}

const google::protobuf::Type* ProtoWriter::LookupType(
    const google::protobuf::Field* field) {
  return ((field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE ||
           field->kind() == google::protobuf::Field_Kind_TYPE_GROUP)
              ? typeinfo_->GetTypeByTypeUrl(field->type_url())
              : &element_->type());
}

void ProtoWriter::WriteRootMessage() {
  GOOGLE_DCHECK(!done_);
  int curr_pos = 0;
  // Calls the destructor of CodedOutputStream to remove any uninitialized
  // memory from the Cord before we read it.
  stream_.reset(NULL);
  const void* data;
  int length;
  google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
  while (input_stream.Next(&data, &length)) {
    if (length == 0) continue;
    int num_bytes = length;
    // Write up to where we need to insert the size field.
    // The number of bytes we may write is the smaller of:
    //   - the current fragment size
    //   - the distance to the next position where a size field needs to be
    //     inserted.
    if (!size_insert_.empty() &&
        size_insert_.front().pos - curr_pos < num_bytes) {
      num_bytes = size_insert_.front().pos - curr_pos;
    }
    output_->Append(static_cast<const char*>(data), num_bytes);
    if (num_bytes < length) {
      input_stream.BackUp(length - num_bytes);
    }
    curr_pos += num_bytes;
    // Insert the size field.
    //   size_insert_.front():      the next <index, size> pair to be written.
    //   size_insert_.front().pos:  position of the size field.
    //   size_insert_.front().size: the size (integer) to be inserted.
    if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
      // Varint32 occupies at most 10 bytes.
      uint8 insert_buffer[10];
      uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
          size_insert_.front().size, insert_buffer);
      output_->Append(reinterpret_cast<const char*>(insert_buffer),
                      insert_buffer_pos - insert_buffer);
      size_insert_.pop_front();
    }
  }
  output_->Flush();
  stream_.reset(new CodedOutputStream(&adapter_));
  done_ = true;
}

void ProtoWriter::WriteTag(const google::protobuf::Field& field) {
  WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
      static_cast<WireFormatLite::FieldType>(field.kind()));
  stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
}

}  // namespace converter
}  // namespace util
}  // namespace protobuf
}  // namespace google


text:
// 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.

#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__

#include <string>

#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stringpiece.h>
#include <google/protobuf/stubs/statusor.h>

namespace google {
namespace protobuf {
class Enum;
}  // namespace protobuf

namespace protobuf {
namespace util {
namespace converter {

// Container for a single piece of data together with its data type.
//
// For primitive types (int32, int64, uint32, uint64, double, float, bool),
// the data is stored by value.
//
// For string, a StringPiece is stored. For Cord, a pointer to Cord is stored.
// Just like StringPiece, the DataPiece class does not own the storage for
// the actual string or Cord, so it is the user's responsiblity to guarantee
// that the underlying storage is still valid when the DataPiece is accessed.
class LIBPROTOBUF_EXPORT DataPiece {
 public:
  // Identifies data type of the value.
  // These are the types supported by DataPiece.
  enum Type {
    TYPE_INT32 = 1,
    TYPE_INT64 = 2,
    TYPE_UINT32 = 3,
    TYPE_UINT64 = 4,
    TYPE_DOUBLE = 5,
    TYPE_FLOAT = 6,
    TYPE_BOOL = 7,
    TYPE_ENUM = 8,
    TYPE_STRING = 9,
    TYPE_BYTES = 10,
    TYPE_NULL = 11,  // explicit NULL type
  };

  // Constructors and Destructor
  explicit DataPiece(const int32 value)
      : type_(TYPE_INT32), i32_(value), use_strict_base64_decoding_(false) {}
  explicit DataPiece(const int64 value)
      : type_(TYPE_INT64), i64_(value), use_strict_base64_decoding_(false) {}
  explicit DataPiece(const uint32 value)
      : type_(TYPE_UINT32), u32_(value), use_strict_base64_decoding_(false) {}
  explicit DataPiece(const uint64 value)
      : type_(TYPE_UINT64), u64_(value), use_strict_base64_decoding_(false) {}
  explicit DataPiece(const double value)
      : type_(TYPE_DOUBLE),
        double_(value),
        use_strict_base64_decoding_(false) {}
  explicit DataPiece(const float value)
      : type_(TYPE_FLOAT), float_(value), use_strict_base64_decoding_(false) {}
  explicit DataPiece(const bool value)
      : type_(TYPE_BOOL), bool_(value), use_strict_base64_decoding_(false) {}
  DataPiece(StringPiece value, bool use_strict_base64_decoding)
      : type_(TYPE_STRING),
        str_(StringPiecePod::CreateFromStringPiece(value)),
        use_strict_base64_decoding_(use_strict_base64_decoding) {}
  // Constructor for bytes. The second parameter is not used.
  DataPiece(StringPiece value, bool dummy, bool use_strict_base64_decoding)
      : type_(TYPE_BYTES),
        str_(StringPiecePod::CreateFromStringPiece(value)),
        use_strict_base64_decoding_(use_strict_base64_decoding) {}

  DataPiece(const DataPiece& r) : type_(r.type_) { InternalCopy(r); }

  DataPiece& operator=(const DataPiece& x) {
    InternalCopy(x);
    return *this;
  }

  static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); }

  virtual ~DataPiece() {
  }

  // Accessors
  Type type() const { return type_; }

  bool use_strict_base64_decoding() { return use_strict_base64_decoding_; }

  StringPiece str() const {
    GOOGLE_LOG_IF(DFATAL, type_ != TYPE_STRING) << "Not a string type.";
    return str_;
  }

  // Parses, casts or converts the value stored in the DataPiece into an int32.
  util::StatusOr<int32> ToInt32() const;

  // Parses, casts or converts the value stored in the DataPiece into a uint32.
  util::StatusOr<uint32> ToUint32() const;

  // Parses, casts or converts the value stored in the DataPiece into an int64.
  util::StatusOr<int64> ToInt64() const;

  // Parses, casts or converts the value stored in the DataPiece into a uint64.
  util::StatusOr<uint64> ToUint64() const;

  // Parses, casts or converts the value stored in the DataPiece into a double.
  util::StatusOr<double> ToDouble() const;

  // Parses, casts or converts the value stored in the DataPiece into a float.
  util::StatusOr<float> ToFloat() const;

  // Parses, casts or converts the value stored in the DataPiece into a bool.
  util::StatusOr<bool> ToBool() const;

  // Parses, casts or converts the value stored in the DataPiece into a string.
  util::StatusOr<string> ToString() const;

  // Tries to convert the value contained in this datapiece to string. If the
  // conversion fails, it returns the default_string.
  string ValueAsStringOrDefault(StringPiece default_string) const;

  util::StatusOr<string> ToBytes() const;

  // Converts a value into protocol buffer enum number. If the value is a
  // string, first attempts conversion by name, trying names as follows:
  //   1) the directly provided string value.
  //   2) the value upper-cased and replacing '-' by '_'
  //   3) if use_lower_camel_for_enums is true it also attempts by comparing
  //   enum name without underscore with the value upper cased above.
  // If the value is not a string, attempts to convert to a 32-bit integer.
  // If none of these succeeds, returns a conversion error status.
  util::StatusOr<int> ToEnum(const google::protobuf::Enum* enum_type,
                               bool use_lower_camel_for_enums) const;

 private:
  // Disallow implicit constructor.
  DataPiece();

  // Helper to create NULL or ENUM types.
  DataPiece(Type type, int32 val)
      : type_(type), i32_(val), use_strict_base64_decoding_(false) {}

  // For numeric conversion between
  //     int32, int64, uint32, uint64, double, float and bool
  template <typename To>
  util::StatusOr<To> GenericConvert() const;

  // For conversion from string to
  //     int32, int64, uint32, uint64, double, float and bool
  template <typename To>
  util::StatusOr<To> StringToNumber(bool (*func)(StringPiece, To*)) const;

  // Decodes a base64 string. Returns true on success.
  bool DecodeBase64(StringPiece src, string* dest) const;

  // Helper function to initialize this DataPiece with 'other'.
  void InternalCopy(const DataPiece& other);

  // Data type for this piece of data.
  Type type_;

  typedef ::google::protobuf::internal::StringPiecePod StringPiecePod;

  // Stored piece of data.
  union {
    int32 i32_;
    int64 i64_;
    uint32 u32_;
    uint64 u64_;
    double double_;
    float float_;
    bool bool_;
    StringPiecePod str_;
  };

  // Uses a stricter version of base64 decoding for byte fields.
  bool use_strict_base64_decoding_;
};

}  // namespace converter
}  // namespace util
}  // namespace protobuf

}  // namespace google
#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__


text:
// 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/object_writer.h>

#include <google/protobuf/util/internal/datapiece.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

// static
void ObjectWriter::RenderDataPieceTo(const DataPiece& data, StringPiece name,
                                     ObjectWriter* ow) {
  switch (data.type()) {
    case DataPiece::TYPE_INT32: {
      ow->RenderInt32(name, data.ToInt32().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_INT64: {
      ow->RenderInt64(name, data.ToInt64().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_UINT32: {
      ow->RenderUint32(name, data.ToUint32().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_UINT64: {
      ow->RenderUint64(name, data.ToUint64().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_DOUBLE: {
      ow->RenderDouble(name, data.ToDouble().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_FLOAT: {
      ow->RenderFloat(name, data.ToFloat().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_BOOL: {
      ow->RenderBool(name, data.ToBool().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_STRING: {
      ow->RenderString(name, data.ToString().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_BYTES: {
      ow->RenderBytes(name, data.ToBytes().ValueOrDie());
      break;
    }
    case DataPiece::TYPE_NULL: {
      ow->RenderNull(name);
      break;
    }
    default:
      break;
  }
}

}  // namespace converter
}  // namespace util
}  // namespace protobuf
}  // namespace google


text:
// 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/type_info.h>

#include <map>
#include <set>

#include <google/protobuf/stubs/common.h>
#include <google/protobuf/type.pb.h>
#include <google/protobuf/util/internal/utility.h>
#include <google/protobuf/stubs/stringpiece.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/status.h>
#include <google/protobuf/stubs/statusor.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

namespace {
// A TypeInfo that looks up information provided by a TypeResolver.
class TypeInfoForTypeResolver : public TypeInfo {
 public:
  explicit TypeInfoForTypeResolver(TypeResolver* type_resolver)
      : type_resolver_(type_resolver) {}

  virtual ~TypeInfoForTypeResolver() {
    DeleteCachedTypes(&cached_types_);
    DeleteCachedTypes(&cached_enums_);
  }

  virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl(
      StringPiece type_url) const {
    std::map<StringPiece, StatusOrType>::iterator it =
        cached_types_.find(type_url);
    if (it != cached_types_.end()) {
      return it->second;
    }
    // Stores the string value so it can be referenced using StringPiece in the
    // cached_types_ map.
    const string& string_type_url =
        *string_storage_.insert(type_url.ToString()).first;
    google::protobuf::scoped_ptr<google::protobuf::Type> type(new google::protobuf::Type());
    util::Status status =
        type_resolver_->ResolveMessageType(string_type_url, type.get());
    StatusOrType result =
        status.ok() ? StatusOrType(type.release()) : StatusOrType(status);
    cached_types_[string_type_url] = result;
    return result;
  }

  virtual const google::protobuf::Type* GetTypeByTypeUrl(
      StringPiece type_url) const {
    StatusOrType result = ResolveTypeUrl(type_url);
    return result.ok() ? result.ValueOrDie() : NULL;
  }

  virtual const google::protobuf::Enum* GetEnumByTypeUrl(
      StringPiece type_url) const {
    std::map<StringPiece, StatusOrEnum>::iterator it =
        cached_enums_.find(type_url);
    if (it != cached_enums_.end()) {
      return it->second.ok() ? it->second.ValueOrDie() : NULL;
    }
    // Stores the string value so it can be referenced using StringPiece in the
    // cached_enums_ map.
    const string& string_type_url =
        *string_storage_.insert(type_url.ToString()).first;
    google::protobuf::scoped_ptr<google::protobuf::Enum> enum_type(
        new google::protobuf::Enum());
    util::Status status =
        type_resolver_->ResolveEnumType(string_type_url, enum_type.get());
    StatusOrEnum result =
        status.ok() ? StatusOrEnum(enum_type.release()) : StatusOrEnum(status);
    cached_enums_[string_type_url] = result;
    return result.ok() ? result.ValueOrDie() : NULL;
  }

  virtual const google::protobuf::Field* FindField(
      const google::protobuf::Type* type, StringPiece camel_case_name) const {
    std::map<const google::protobuf::Type*, CamelCaseNameTable>::const_iterator
        it = indexed_types_.find(type);
    const CamelCaseNameTable& camel_case_name_table =
        (it == indexed_types_.end())
            ? PopulateNameLookupTable(type, &indexed_types_[type])
            : it->second;
    StringPiece name =
        FindWithDefault(camel_case_name_table, camel_case_name, StringPiece());
    if (name.empty()) {
      // Didn't find a mapping. Use whatever provided.
      name = camel_case_name;
    }
    return FindFieldInTypeOrNull(type, name);
  }

 private:
  typedef util::StatusOr<const google::protobuf::Type*> StatusOrType;
  typedef util::StatusOr<const google::protobuf::Enum*> StatusOrEnum;
  typedef std::map<StringPiece, StringPiece> CamelCaseNameTable;

  template <typename T>
  static void DeleteCachedTypes(std::map<StringPiece, T>* cached_types) {
    for (typename std::map<StringPiece, T>::iterator it = cached_types->begin();
         it != cached_types->end(); ++it) {
      if (it->second.ok()) {
        delete it->second.ValueOrDie();
      }
    }
  }

  const CamelCaseNameTable& PopulateNameLookupTable(
      const google::protobuf::Type* type,
      CamelCaseNameTable* camel_case_name_table) const {
    for (int i = 0; i < type->fields_size(); ++i) {
      const google::protobuf::Field& field = type->fields(i);
      StringPiece name = field.name();
      StringPiece camel_case_name = field.json_name();
      const StringPiece* existing =
          InsertOrReturnExisting(camel_case_name_table, camel_case_name, name);
      if (existing && *existing != name) {
        GOOGLE_LOG(WARNING) << "Field '" << name << "' and '" << *existing
                     << "' map to the same camel case name '" << camel_case_name
                     << "'.";
      }
    }
    return *camel_case_name_table;
  }

  TypeResolver* type_resolver_;

  // Stores string values that will be referenced by StringPieces in
  // cached_types_, cached_enums_.
  mutable std::set<string> string_storage_;

  mutable std::map<StringPiece, StatusOrType> cached_types_;
  mutable std::map<StringPiece, StatusOrEnum> cached_enums_;

  mutable std::map<const google::protobuf::Type*, CamelCaseNameTable>
      indexed_types_;
};
}  // namespace

TypeInfo* TypeInfo::NewTypeInfo(TypeResolver* type_resolver) {
  return new TypeInfoForTypeResolver(type_resolver);
}

}  // namespace converter
}  // namespace util
}  // namespace protobuf
}  // namespace google


text:
// 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/error_listener.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {

}  // namespace converter
}  // namespace util
}  // namespace protobuf
}  // namespace google


text:
// 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/type_info_test_helper.h>

#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <vector>

#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/util/internal/default_value_objectwriter.h>
#include <google/protobuf/util/internal/type_info.h>
#include <google/protobuf/util/internal/constants.h>
#include <google/protobuf/util/internal/protostream_objectsource.h>
#include <google/protobuf/util/internal/protostream_objectwriter.h>
#include <google/protobuf/util/type_resolver.h>
#include <google/protobuf/util/type_resolver_util.h>

namespace google {
namespace protobuf {
namespace util {
namespace converter {
namespace testing {

void TypeInfoTestHelper::ResetTypeInfo(
    const std::vector<const Descriptor*>& descriptors) {
  switch (type_) {
    case USE_TYPE_RESOLVER: {
      const DescriptorPool* pool = descriptors[0]->file()->pool();
      for (int i = 1; i < descriptors.size(); ++i) {
        GOOGLE_CHECK(pool == descriptors[i]->file()->pool())
            << "Descriptors from different pools are not supported.";
      }
      type_resolver_.reset(
          NewTypeResolverForDescriptorPool(kTypeServiceBaseUrl, pool));
      typeinfo_.reset(TypeInfo::NewTypeInfo(type_resolver_.get()));
      return;
    }
  }
  GOOGLE_LOG(FATAL) << "Can not reach here.";
}

void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor) {
  std::vector<const Descriptor*> descriptors;
  descriptors.push_back(descriptor);
  ResetTypeInfo(descriptors);
}

void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor1,
                                       const Descriptor* descriptor2) {
  std::vector<const Descriptor*> descriptors;
  descriptors.push_back(descriptor1);
  descriptors.push_back(descriptor2);
  ResetTypeInfo(descriptors);
}

TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); }

ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource(
    io::CodedInputStream* coded_input, const string& type_url) {
  const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url);
  switch (type_) {
    case USE_TYPE_RESOLVER: {
      return new ProtoStreamObjectSource(coded_input, type_resolver_.get(),
                                         *type);
    }
  }
  GOOGLE_LOG(FATAL) << "Can not reach here.";
  return NULL;
}

ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter(
    const string& type_url, strings::ByteSink* output, ErrorListener* listener,
    const ProtoStreamObjectWriter::Options& options) {
  const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url);
  switch (type_) {
    case USE_TYPE_RESOLVER: {
      return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output,
                                         listener, options);
    }
  }
  GOOGLE_LOG(FATAL) << "Can not reach here.";
  return NULL;
}

DefaultValueObjectWriter* TypeInfoTestHelper::NewDefaultValueWriter(
    const string& type_url, ObjectWriter* writer) {
  const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url);
  switch (type_) {
    case USE_TYPE_RESOLVER: {
      return new DefaultValueObjectWriter(type_resolver_.get(), *type, writer);
    }
  }
  GOOGLE_LOG(FATAL) << "Can not reach here.";
  return NULL;
}

}  // namespace testing
}  // namespace converter
}  // namespace util
}  // namespace protobuf
}  // namespace google


text:
// 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.

#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__

#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <string>
#include <utility>

#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/type.pb.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/stubs/stringpiece.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/status.h>
#include <google/protobuf/stubs/statusor.h>

namespace google {
namespace protobuf {
class Method;
class Any;
class Bool;
class Option;
class Field;
class Type;
class Enum;
class EnumValue;
}  // namespace protobuf

namespace protobuf {
namespace util {
namespace converter {

// Size of "type.googleapis.com"
static const int64 kTypeUrlSize = 19;

// Finds the tech option identified by option_name. Parses the boolean value and
// returns it.
// When the option with the given name is not found, default_value is returned.
LIBPROTOBUF_EXPORT bool GetBoolOptionOrDefault(
    const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
    const string& option_name, bool default_value);

// Returns int64 option value. If the option isn't found, returns the
// default_value.
LIBPROTOBUF_EXPORT int64 GetInt64OptionOrDefault(
    const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
    const string& option_name, int64 default_value);

// Returns double option value. If the option isn't found, returns the
// default_value.
LIBPROTOBUF_EXPORT double GetDoubleOptionOrDefault(
    const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
    const string& option_name, double default_value);

// Returns string option value. If the option isn't found, returns the
// default_value.
LIBPROTOBUF_EXPORT string GetStringOptionOrDefault(
    const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
    const string& option_name, const string& default_value);

// Returns a boolean value contained in Any type.
// TODO(skarvaje): Make these utilities dealing with Any types more generic,
// add more error checking and move to a more public/sharable location so others
// can use.
LIBPROTOBUF_EXPORT bool GetBoolFromAny(const google::protobuf::Any& any);

// Returns int64 value contained in Any type.
LIBPROTOBUF_EXPORT int64 GetInt64FromAny(const google::protobuf::Any& any);

// Returns double value contained in Any type.
LIBPROTOBUF_EXPORT double GetDoubleFromAny(const google::protobuf::Any& any);

// Returns string value contained in Any type.
LIBPROTOBUF_EXPORT string GetStringFromAny(const google::protobuf::Any& any);

// Returns the type string without the url prefix. e.g.: If the passed type is
// 'type.googleapis.com/tech.type.Bool', the returned value is 'tech.type.Bool'.
LIBPROTOBUF_EXPORT const StringPiece GetTypeWithoutUrl(StringPiece type_url);

// Returns the simple_type with the base type url (kTypeServiceBaseUrl)
// prefixed.
//
// E.g:
// GetFullTypeWithUrl("google.protobuf.Timestamp") returns the string
// "type.googleapis.com/google.protobuf.Timestamp".
LIBPROTOBUF_EXPORT const string GetFullTypeWithUrl(StringPiece simple_type);

// Finds and returns option identified by name and option_name within the
// provided map. Returns NULL if none found.
const google::protobuf::Option* FindOptionOrNull(
    const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
    const string& option_name);

// Finds and returns the field identified by field_name in the passed tech Type
// object. Returns NULL if none found.
const google::protobuf::Field* FindFieldInTypeOrNull(
    const google::protobuf::Type* type, StringPiece field_name);

// Similar to FindFieldInTypeOrNull, but this looks up fields with given
// json_name.
const google::protobuf::Field* FindJsonFieldInTypeOrNull(
    const google::protobuf::Type* type, StringPiece json_name);

// Similar to FindFieldInTypeOrNull, but this looks up fields by number.
const google::protobuf::Field* FindFieldInTypeByNumberOrNull(
    const google::protobuf::Type* type, int32 number);

// Finds and returns the EnumValue identified by enum_name in the passed tech
// Enum object. Returns NULL if none found.
const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
    const google::protobuf::Enum* enum_type, StringPiece enum_name);

// Finds and returns the EnumValue identified by value in the passed tech
// Enum object. Returns NULL if none found.
const google::protobuf::EnumValue* FindEnumValueByNumberOrNull(
    const google::protobuf::Enum* enum_type, int32 value);

// Finds and returns the EnumValue identified by enum_name without underscore in
// the passed tech Enum object. Returns NULL if none found.
// For Ex. if enum_name is ACTIONANDADVENTURE it can get accepted if
// EnumValue's name is action_and_adventure or ACTION_AND_ADVENTURE.
const google::protobuf::EnumValue* FindEnumValueByNameWithoutUnderscoreOrNull(
    const google::protobuf::Enum* enum_type, StringPiece enum_name);

// Converts input to camel-case and returns it.
LIBPROTOBUF_EXPORT string ToCamelCase(const StringPiece input);

// Converts input to snake_case and returns it.
LIBPROTOBUF_EXPORT string ToSnakeCase(StringPiece input);

// Returns true if type_name represents a well-known type.
LIBPROTOBUF_EXPORT bool IsWellKnownType(const string& type_name);

// Returns true if 'bool_string' represents a valid boolean value. Only "true",
// "false", "0" and "1" are allowed.
LIBPROTOBUF_EXPORT bool IsValidBoolString(const string& bool_string);

// Returns true if "field" is a protobuf map field based on its type.
LIBPROTOBUF_EXPORT bool IsMap(const google::protobuf::Field& field,
           const google::protobuf::Type& type);

// Returns true if the given type has special MessageSet wire format.
bool IsMessageSetWireFormat(const google::protobuf::Type& type);

// Infinity/NaN-aware conversion to string.
LIBPROTOBUF_EXPORT string DoubleAsString(double value);
LIBPROTOBUF_EXPORT string FloatAsString(float value);

// Convert from int32, int64, uint32, uint64, double or float to string.
template <typename T>
string ValueAsString(T value) {
  return SimpleItoa(value);
}

template <>
inline string ValueAsString(float value) {
  return FloatAsString(value);
}

template <>
inline string ValueAsString(double value) {
  return DoubleAsString(value);
}

// Converts a string to float. Unlike safe_strtof, conversion will fail if the
// value fits into double but not float (e.g., DBL_MAX).
LIBPROTOBUF_EXPORT bool SafeStrToFloat(StringPiece str, float* value);
}  // namespace converter
}  // namespace util
}  // namespace protobuf

}  // namespace google
#endif  // GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__