| Index: third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc
|
| diff --git a/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..034d616ff18d4c50c392ed7bcb08d481d4643226
|
| --- /dev/null
|
| +++ b/third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc
|
| @@ -0,0 +1,1045 @@
|
| +// 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/protostream_objectsource.h>
|
| +
|
| +#include <utility>
|
| +
|
| +#include <google/protobuf/stubs/casts.h>
|
| +#include <google/protobuf/stubs/logging.h>
|
| +#include <google/protobuf/stubs/common.h>
|
| +#include <google/protobuf/stubs/stringprintf.h>
|
| +#include <google/protobuf/stubs/time.h>
|
| +#include <google/protobuf/io/coded_stream.h>
|
| +#include <google/protobuf/io/zero_copy_stream_impl.h>
|
| +#include <google/protobuf/descriptor.h>
|
| +#include <google/protobuf/wire_format.h>
|
| +#include <google/protobuf/wire_format_lite.h>
|
| +#include <google/protobuf/util/internal/field_mask_utility.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/status_macros.h>
|
| +
|
| +
|
| +namespace google {
|
| +namespace protobuf {
|
| +namespace util {
|
| +using util::Status;
|
| +using util::StatusOr;
|
| +namespace error {
|
| +using util::error::Code;
|
| +using util::error::INTERNAL;
|
| +}
|
| +namespace converter {
|
| +
|
| +using google::protobuf::Descriptor;
|
| +using google::protobuf::EnumValueDescriptor;
|
| +using google::protobuf::FieldDescriptor;
|
| +using google::protobuf::internal::WireFormat;
|
| +using google::protobuf::internal::WireFormatLite;
|
| +using util::Status;
|
| +using util::StatusOr;
|
| +
|
| +namespace {
|
| +// Finds a field with the given number. NULL if none found.
|
| +const google::protobuf::Field* FindFieldByNumber(
|
| + const google::protobuf::Type& type, int number);
|
| +
|
| +// Returns true if the field is packable.
|
| +bool IsPackable(const google::protobuf::Field& field);
|
| +
|
| +// Finds an enum value with the given number. NULL if none found.
|
| +const google::protobuf::EnumValue* FindEnumValueByNumber(
|
| + const google::protobuf::Enum& tech_enum, int number);
|
| +
|
| +// Utility function to format nanos.
|
| +const string FormatNanos(uint32 nanos);
|
| +} // namespace
|
| +
|
| +
|
| +ProtoStreamObjectSource::ProtoStreamObjectSource(
|
| + google::protobuf::io::CodedInputStream* stream, TypeResolver* type_resolver,
|
| + const google::protobuf::Type& type)
|
| + : stream_(stream),
|
| + typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
|
| + own_typeinfo_(true),
|
| + type_(type) {
|
| + GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
|
| +}
|
| +
|
| +ProtoStreamObjectSource::ProtoStreamObjectSource(
|
| + google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo,
|
| + const google::protobuf::Type& type)
|
| + : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) {
|
| + GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
|
| +}
|
| +
|
| +ProtoStreamObjectSource::~ProtoStreamObjectSource() {
|
| + if (own_typeinfo_) {
|
| + delete typeinfo_;
|
| + }
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name,
|
| + ObjectWriter* ow) const {
|
| + return WriteMessage(type_, name, 0, true, ow);
|
| +}
|
| +
|
| +const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField(
|
| + const google::protobuf::Type& type, uint32 tag) const {
|
| + // Lookup the new field in the type by tag number.
|
| + const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3);
|
| + // Verify if the field corresponds to the wire type in tag.
|
| + // If there is any discrepancy, mark the field as not found.
|
| + if (field != NULL) {
|
| + WireFormatLite::WireType expected_type =
|
| + WireFormatLite::WireTypeForFieldType(
|
| + static_cast<WireFormatLite::FieldType>(field->kind()));
|
| + WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag);
|
| + if (actual_type != expected_type &&
|
| + (!IsPackable(*field) ||
|
| + actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
|
| + field = NULL;
|
| + }
|
| + }
|
| + return field;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
|
| + StringPiece name,
|
| + const uint32 end_tag,
|
| + bool include_start_and_end,
|
| + ObjectWriter* ow) const {
|
| +
|
| + const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
|
| + if (type_renderer != NULL) {
|
| + return (*type_renderer)(this, type, name, ow);
|
| + }
|
| +
|
| + const google::protobuf::Field* field = NULL;
|
| + string field_name;
|
| + // last_tag set to dummy value that is different from tag.
|
| + uint32 tag = stream_->ReadTag(), last_tag = tag + 1;
|
| +
|
| + if (include_start_and_end) {
|
| + ow->StartObject(name);
|
| + }
|
| + while (tag != end_tag) {
|
| + if (tag != last_tag) { // Update field only if tag is changed.
|
| + last_tag = tag;
|
| + field = FindAndVerifyField(type, tag);
|
| + if (field != NULL) {
|
| + field_name = field->json_name();
|
| + }
|
| + }
|
| + if (field == NULL) {
|
| + // If we didn't find a field, skip this unknown tag.
|
| + // TODO(wpoon): Check return boolean value.
|
| + WireFormat::SkipField(stream_, tag, NULL);
|
| + tag = stream_->ReadTag();
|
| + continue;
|
| + }
|
| +
|
| + if (field->cardinality() ==
|
| + google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
|
| + bool check_maps = true;
|
| +
|
| + if (check_maps && IsMap(*field)) {
|
| + ow->StartObject(field_name);
|
| + ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
|
| + ow->EndObject();
|
| + } else {
|
| + ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow));
|
| + }
|
| + } else {
|
| + // Render the field.
|
| + RETURN_IF_ERROR(RenderField(field, field_name, ow));
|
| + tag = stream_->ReadTag();
|
| + }
|
| + }
|
| + if (include_start_and_end) {
|
| + ow->EndObject();
|
| + }
|
| + return Status::OK;
|
| +}
|
| +
|
| +StatusOr<uint32> ProtoStreamObjectSource::RenderList(
|
| + const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
|
| + ObjectWriter* ow) const {
|
| + uint32 tag_to_return = 0;
|
| + ow->StartList(name);
|
| + if (IsPackable(*field) &&
|
| + list_tag ==
|
| + WireFormatLite::MakeTag(field->number(),
|
| + WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
|
| + RETURN_IF_ERROR(RenderPacked(field, ow));
|
| + // Since packed fields have a single tag, read another tag from stream to
|
| + // return.
|
| + tag_to_return = stream_->ReadTag();
|
| + } else {
|
| + do {
|
| + RETURN_IF_ERROR(RenderField(field, "", ow));
|
| + } while ((tag_to_return = stream_->ReadTag()) == list_tag);
|
| + }
|
| + ow->EndList();
|
| + return tag_to_return;
|
| +}
|
| +
|
| +StatusOr<uint32> ProtoStreamObjectSource::RenderMap(
|
| + const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
|
| + ObjectWriter* ow) const {
|
| + const google::protobuf::Type* field_type =
|
| + typeinfo_->GetTypeByTypeUrl(field->type_url());
|
| + uint32 tag_to_return = 0;
|
| + do {
|
| + // Render map entry message type.
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32); // message length
|
| + int old_limit = stream_->PushLimit(buffer32);
|
| + string map_key;
|
| + for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
|
| + const google::protobuf::Field* field =
|
| + FindAndVerifyField(*field_type, tag);
|
| + if (field == NULL) {
|
| + WireFormat::SkipField(stream_, tag, NULL);
|
| + continue;
|
| + }
|
| + // Map field numbers are key = 1 and value = 2
|
| + if (field->number() == 1) {
|
| + map_key = ReadFieldValueAsString(*field);
|
| + } else if (field->number() == 2) {
|
| + if (map_key.empty()) {
|
| + return Status(util::error::INTERNAL, "Map key must be non-empty");
|
| + }
|
| + RETURN_IF_ERROR(RenderField(field, map_key, ow));
|
| + }
|
| + }
|
| + stream_->PopLimit(old_limit);
|
| + } while ((tag_to_return = stream_->ReadTag()) == list_tag);
|
| + return tag_to_return;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderPacked(
|
| + const google::protobuf::Field* field, ObjectWriter* ow) const {
|
| + uint32 length;
|
| + stream_->ReadVarint32(&length);
|
| + int old_limit = stream_->PushLimit(length);
|
| + while (stream_->BytesUntilLimit() > 0) {
|
| + RETURN_IF_ERROR(RenderField(field, StringPiece(), ow));
|
| + }
|
| + stream_->PopLimit(old_limit);
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderTimestamp(
|
| + const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
|
| + StringPiece field_name, ObjectWriter* ow) {
|
| + pair<int64, int32> p = os->ReadSecondsAndNanos(type);
|
| + int64 seconds = p.first;
|
| + int32 nanos = p.second;
|
| + if (seconds > kMaxSeconds || seconds < kMinSeconds) {
|
| + return Status(
|
| + util::error::INTERNAL,
|
| + StrCat("Timestamp seconds exceeds limit for field: ", field_name));
|
| + }
|
| +
|
| + if (nanos < 0 || nanos >= kNanosPerSecond) {
|
| + return Status(
|
| + util::error::INTERNAL,
|
| + StrCat("Timestamp nanos exceeds limit for field: ", field_name));
|
| + }
|
| +
|
| + ow->RenderString(field_name,
|
| + ::google::protobuf::internal::FormatTime(seconds, nanos));
|
| +
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderDuration(
|
| + const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
|
| + StringPiece field_name, ObjectWriter* ow) {
|
| + pair<int64, int32> p = os->ReadSecondsAndNanos(type);
|
| + int64 seconds = p.first;
|
| + int32 nanos = p.second;
|
| + if (seconds > kMaxSeconds || seconds < kMinSeconds) {
|
| + return Status(
|
| + util::error::INTERNAL,
|
| + StrCat("Duration seconds exceeds limit for field: ", field_name));
|
| + }
|
| +
|
| + if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
|
| + return Status(
|
| + util::error::INTERNAL,
|
| + StrCat("Duration nanos exceeds limit for field: ", field_name));
|
| + }
|
| +
|
| + string sign = "";
|
| + if (seconds < 0) {
|
| + if (nanos > 0) {
|
| + return Status(util::error::INTERNAL,
|
| + StrCat("Duration nanos is non-negative, but seconds is "
|
| + "negative for field: ",
|
| + field_name));
|
| + }
|
| + sign = "-";
|
| + seconds = -seconds;
|
| + nanos = -nanos;
|
| + } else if (seconds == 0 && nanos < 0) {
|
| + sign = "-";
|
| + nanos = -nanos;
|
| + }
|
| + string formatted_duration = StringPrintf("%s%lld%ss", sign.c_str(), seconds,
|
| + FormatNanos(nanos).c_str());
|
| + ow->RenderString(field_name, formatted_duration);
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderDouble(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint64 buffer64 = 0; // default value of Double wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadLittleEndian64(&buffer64);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderDouble(field_name, bit_cast<double>(buffer64));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderFloat(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint32 buffer32 = 0; // default value of Float wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadLittleEndian32(&buffer32);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderFloat(field_name, bit_cast<float>(buffer32));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderInt64(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint64 buffer64 = 0; // default value of Int64 wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint64(&buffer64);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderUInt64(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint64 buffer64 = 0; // default value of UInt64 wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint64(&buffer64);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderInt32(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint32 buffer32 = 0; // default value of Int32 wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint32(&buffer32);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderUInt32(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint32 buffer32 = 0; // default value of UInt32 wrapper value
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint32(&buffer32);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderBool(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint64 buffer64 = 0; // results in 'false' value as default, which is the
|
| + // default value of Bool wrapper
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint64(&buffer64);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderBool(field_name, buffer64 != 0);
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderString(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint32 buffer32;
|
| + string str; // default value of empty for String wrapper
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint32(&buffer32); // string size.
|
| + os->stream_->ReadString(&str, buffer32);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderString(field_name, str);
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderBytes(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| + uint32 buffer32;
|
| + string str;
|
| + if (tag != 0) {
|
| + os->stream_->ReadVarint32(&buffer32);
|
| + os->stream_->ReadString(&str, buffer32);
|
| + os->stream_->ReadTag();
|
| + }
|
| + ow->RenderBytes(field_name, str);
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderStruct(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + const google::protobuf::Field* field = NULL;
|
| + uint32 tag = os->stream_->ReadTag();
|
| + ow->StartObject(field_name);
|
| + while (tag != 0) {
|
| + field = os->FindAndVerifyField(type, tag);
|
| + // google.protobuf.Struct has only one field that is a map. Hence we use
|
| + // RenderMap to render that field.
|
| + if (os->IsMap(*field)) {
|
| + ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow));
|
| + }
|
| + }
|
| + ow->EndObject();
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderStructValue(
|
| + const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
|
| + StringPiece field_name, ObjectWriter* ow) {
|
| + const google::protobuf::Field* field = NULL;
|
| + for (uint32 tag = os->stream_->ReadTag(); tag != 0;
|
| + tag = os->stream_->ReadTag()) {
|
| + field = os->FindAndVerifyField(type, tag);
|
| + if (field == NULL) {
|
| + WireFormat::SkipField(os->stream_, tag, NULL);
|
| + continue;
|
| + }
|
| + RETURN_IF_ERROR(os->RenderField(field, field_name, ow));
|
| + }
|
| + return Status::OK;
|
| +}
|
| +
|
| +// TODO(skarvaje): Avoid code duplication of for loops and SkipField logic.
|
| +Status ProtoStreamObjectSource::RenderStructListValue(
|
| + const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
|
| + StringPiece field_name, ObjectWriter* ow) {
|
| + uint32 tag = os->stream_->ReadTag();
|
| +
|
| + // Render empty list when we find empty ListValue message.
|
| + if (tag == 0) {
|
| + ow->StartList(field_name);
|
| + ow->EndList();
|
| + return Status::OK;
|
| + }
|
| +
|
| + while (tag != 0) {
|
| + const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
|
| + if (field == NULL) {
|
| + WireFormat::SkipField(os->stream_, tag, NULL);
|
| + tag = os->stream_->ReadTag();
|
| + continue;
|
| + }
|
| + ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow));
|
| + }
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os,
|
| + const google::protobuf::Type& type,
|
| + StringPiece field_name,
|
| + ObjectWriter* ow) {
|
| + // An Any is of the form { string type_url = 1; bytes value = 2; }
|
| + uint32 tag;
|
| + string type_url;
|
| + string value;
|
| +
|
| + // First read out the type_url and value from the proto stream
|
| + for (tag = os->stream_->ReadTag(); tag != 0; tag = os->stream_->ReadTag()) {
|
| + const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
|
| + if (field == NULL) {
|
| + WireFormat::SkipField(os->stream_, tag, NULL);
|
| + continue;
|
| + }
|
| + // 'type_url' has field number of 1 and 'value' has field number 2
|
| + // //google/protobuf/any.proto
|
| + if (field->number() == 1) {
|
| + // read type_url
|
| + uint32 type_url_size;
|
| + os->stream_->ReadVarint32(&type_url_size);
|
| + os->stream_->ReadString(&type_url, type_url_size);
|
| + } else if (field->number() == 2) {
|
| + // read value
|
| + uint32 value_size;
|
| + os->stream_->ReadVarint32(&value_size);
|
| + os->stream_->ReadString(&value, value_size);
|
| + }
|
| + }
|
| +
|
| + // If there is no value, we don't lookup the type, we just output it (if
|
| + // present). If both type and value are empty we output an empty object.
|
| + if (value.empty()) {
|
| + ow->StartObject(field_name);
|
| + if (!type_url.empty()) {
|
| + ow->RenderString("@type", type_url);
|
| + }
|
| + ow->EndObject();
|
| + return util::Status::OK;
|
| + }
|
| +
|
| + // If there is a value but no type, we cannot render it, so report an error.
|
| + if (type_url.empty()) {
|
| + // TODO(sven): Add an external message once those are ready.
|
| + return util::Status(util::error::INTERNAL,
|
| + "Invalid Any, the type_url is missing.");
|
| + }
|
| +
|
| + util::StatusOr<const google::protobuf::Type*> resolved_type =
|
| + os->typeinfo_->ResolveTypeUrl(type_url);
|
| +
|
| + if (!resolved_type.ok()) {
|
| + // Convert into an internal error, since this means the backend gave us
|
| + // an invalid response (missing or invalid type information).
|
| + return util::Status(util::error::INTERNAL,
|
| + resolved_type.status().error_message());
|
| + }
|
| + // nested_type cannot be null at this time.
|
| + const google::protobuf::Type* nested_type = resolved_type.ValueOrDie();
|
| +
|
| + google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size());
|
| + google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream);
|
| + // We know the type so we can render it. Recursively parse the nested stream
|
| + // using a nested ProtoStreamObjectSource using our nested type information.
|
| + ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type);
|
| +
|
| + // We manually call start and end object here so we can inject the @type.
|
| + ow->StartObject(field_name);
|
| + ow->RenderString("@type", type_url);
|
| + util::Status result =
|
| + nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow);
|
| + ow->EndObject();
|
| + return result;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderFieldMask(
|
| + const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
|
| + StringPiece field_name, ObjectWriter* ow) {
|
| + string combined;
|
| + uint32 buffer32;
|
| + uint32 paths_field_tag = 0;
|
| + for (uint32 tag = os->stream_->ReadTag(); tag != 0;
|
| + tag = os->stream_->ReadTag()) {
|
| + if (paths_field_tag == 0) {
|
| + const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
|
| + if (field != NULL && field->number() == 1 &&
|
| + field->name() == "paths") {
|
| + paths_field_tag = tag;
|
| + }
|
| + }
|
| + if (paths_field_tag != tag) {
|
| + return util::Status(util::error::INTERNAL,
|
| + "Invalid FieldMask, unexpected field.");
|
| + }
|
| + string str;
|
| + os->stream_->ReadVarint32(&buffer32); // string size.
|
| + os->stream_->ReadString(&str, buffer32);
|
| + if (!combined.empty()) {
|
| + combined.append(",");
|
| + }
|
| + combined.append(ConvertFieldMaskPath(str, &ToCamelCase));
|
| + }
|
| + ow->RenderString(field_name, combined);
|
| + return Status::OK;
|
| +}
|
| +
|
| +
|
| +hash_map<string, ProtoStreamObjectSource::TypeRenderer>*
|
| + ProtoStreamObjectSource::renderers_ = NULL;
|
| +GOOGLE_PROTOBUF_DECLARE_ONCE(source_renderers_init_);
|
| +
|
| +void ProtoStreamObjectSource::InitRendererMap() {
|
| + renderers_ = new hash_map<string, ProtoStreamObjectSource::TypeRenderer>();
|
| + (*renderers_)["google.protobuf.Timestamp"] =
|
| + &ProtoStreamObjectSource::RenderTimestamp;
|
| + (*renderers_)["google.protobuf.Duration"] =
|
| + &ProtoStreamObjectSource::RenderDuration;
|
| + (*renderers_)["google.protobuf.DoubleValue"] =
|
| + &ProtoStreamObjectSource::RenderDouble;
|
| + (*renderers_)["google.protobuf.FloatValue"] =
|
| + &ProtoStreamObjectSource::RenderFloat;
|
| + (*renderers_)["google.protobuf.Int64Value"] =
|
| + &ProtoStreamObjectSource::RenderInt64;
|
| + (*renderers_)["google.protobuf.UInt64Value"] =
|
| + &ProtoStreamObjectSource::RenderUInt64;
|
| + (*renderers_)["google.protobuf.Int32Value"] =
|
| + &ProtoStreamObjectSource::RenderInt32;
|
| + (*renderers_)["google.protobuf.UInt32Value"] =
|
| + &ProtoStreamObjectSource::RenderUInt32;
|
| + (*renderers_)["google.protobuf.BoolValue"] =
|
| + &ProtoStreamObjectSource::RenderBool;
|
| + (*renderers_)["google.protobuf.StringValue"] =
|
| + &ProtoStreamObjectSource::RenderString;
|
| + (*renderers_)["google.protobuf.BytesValue"] =
|
| + &ProtoStreamObjectSource::RenderBytes;
|
| + (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
|
| + (*renderers_)["google.protobuf.Struct"] =
|
| + &ProtoStreamObjectSource::RenderStruct;
|
| + (*renderers_)["google.protobuf.Value"] =
|
| + &ProtoStreamObjectSource::RenderStructValue;
|
| + (*renderers_)["google.protobuf.ListValue"] =
|
| + &ProtoStreamObjectSource::RenderStructListValue;
|
| + (*renderers_)["google.protobuf.FieldMask"] =
|
| + &ProtoStreamObjectSource::RenderFieldMask;
|
| + ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
|
| +}
|
| +
|
| +void ProtoStreamObjectSource::DeleteRendererMap() {
|
| + delete ProtoStreamObjectSource::renderers_;
|
| + renderers_ = NULL;
|
| +}
|
| +
|
| +// static
|
| +ProtoStreamObjectSource::TypeRenderer*
|
| +ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) {
|
| + ::google::protobuf::GoogleOnceInit(&source_renderers_init_, &InitRendererMap);
|
| + return FindOrNull(*renderers_, type_url);
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderField(
|
| + const google::protobuf::Field* field, StringPiece field_name,
|
| + ObjectWriter* ow) const {
|
| + // Short-circuit message types as it tends to call WriteMessage recursively
|
| + // and ends up using a lot of stack space. Keep the stack usage of this
|
| + // message small in order to preserve stack space and not crash.
|
| + if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32); // message length
|
| + int old_limit = stream_->PushLimit(buffer32);
|
| + // Get the nested message type for this field.
|
| + const google::protobuf::Type* type =
|
| + typeinfo_->GetTypeByTypeUrl(field->type_url());
|
| + if (type == NULL) {
|
| + return Status(util::error::INTERNAL,
|
| + StrCat("Invalid configuration. Could not find the type: ",
|
| + field->type_url()));
|
| + }
|
| +
|
| + // Short-circuit any special type rendering to save call-stack space.
|
| + const TypeRenderer* type_renderer = FindTypeRenderer(type->name());
|
| +
|
| + bool use_type_renderer = type_renderer != NULL;
|
| +
|
| + if (use_type_renderer) {
|
| + RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow));
|
| + } else {
|
| + RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
|
| + }
|
| + if (!stream_->ConsumedEntireMessage()) {
|
| + return Status(util::error::INVALID_ARGUMENT,
|
| + "Nested protocol message not parsed in its entirety.");
|
| + }
|
| + stream_->PopLimit(old_limit);
|
| + } else {
|
| + // Render all other non-message types.
|
| + return RenderNonMessageField(field, field_name, ow);
|
| + }
|
| + return Status::OK;
|
| +}
|
| +
|
| +Status ProtoStreamObjectSource::RenderNonMessageField(
|
| + const google::protobuf::Field* field, StringPiece field_name,
|
| + ObjectWriter* ow) const {
|
| + // Temporary buffers of different types.
|
| + uint32 buffer32;
|
| + uint64 buffer64;
|
| + string strbuffer;
|
| + switch (field->kind()) {
|
| + case google::protobuf::Field_Kind_TYPE_BOOL: {
|
| + stream_->ReadVarint64(&buffer64);
|
| + ow->RenderBool(field_name, buffer64 != 0);
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_INT32: {
|
| + stream_->ReadVarint32(&buffer32);
|
| + ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_INT64: {
|
| + stream_->ReadVarint64(&buffer64);
|
| + ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_UINT32: {
|
| + stream_->ReadVarint32(&buffer32);
|
| + ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_UINT64: {
|
| + stream_->ReadVarint64(&buffer64);
|
| + ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SINT32: {
|
| + stream_->ReadVarint32(&buffer32);
|
| + ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SINT64: {
|
| + stream_->ReadVarint64(&buffer64);
|
| + ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SFIXED32: {
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SFIXED64: {
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FIXED32: {
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FIXED64: {
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FLOAT: {
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + ow->RenderFloat(field_name, bit_cast<float>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_DOUBLE: {
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + ow->RenderDouble(field_name, bit_cast<double>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_ENUM: {
|
| + stream_->ReadVarint32(&buffer32);
|
| +
|
| + // If the field represents an explicit NULL value, render null.
|
| + if (field->type_url() == kStructNullValueTypeUrl) {
|
| + ow->RenderNull(field_name);
|
| + break;
|
| + }
|
| +
|
| + // Get the nested enum type for this field.
|
| + // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
|
| + // up.
|
| + const google::protobuf::Enum* en =
|
| + typeinfo_->GetEnumByTypeUrl(field->type_url());
|
| + // Lookup the name of the enum, and render that. Skips unknown enums.
|
| + if (en != NULL) {
|
| + const google::protobuf::EnumValue* enum_value =
|
| + FindEnumValueByNumber(*en, buffer32);
|
| + if (enum_value != NULL) {
|
| + ow->RenderString(field_name, enum_value->name());
|
| + }
|
| + } else {
|
| + GOOGLE_LOG(INFO) << "Unknown enum skipped: " << field->type_url();
|
| + }
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_STRING: {
|
| + stream_->ReadVarint32(&buffer32); // string size.
|
| + stream_->ReadString(&strbuffer, buffer32);
|
| + ow->RenderString(field_name, strbuffer);
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_BYTES: {
|
| + stream_->ReadVarint32(&buffer32); // bytes size.
|
| + stream_->ReadString(&strbuffer, buffer32);
|
| + ow->RenderBytes(field_name, strbuffer);
|
| + break;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + return Status::OK;
|
| +}
|
| +
|
| +// TODO(skarvaje): Fix this to avoid code duplication.
|
| +const string ProtoStreamObjectSource::ReadFieldValueAsString(
|
| + const google::protobuf::Field& field) const {
|
| + string result;
|
| + switch (field.kind()) {
|
| + case google::protobuf::Field_Kind_TYPE_BOOL: {
|
| + uint64 buffer64;
|
| + stream_->ReadVarint64(&buffer64);
|
| + result = buffer64 != 0 ? "true" : "false";
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_INT32: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32);
|
| + result = SimpleItoa(bit_cast<int32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_INT64: {
|
| + uint64 buffer64;
|
| + stream_->ReadVarint64(&buffer64);
|
| + result = SimpleItoa(bit_cast<int64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_UINT32: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32);
|
| + result = SimpleItoa(bit_cast<uint32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_UINT64: {
|
| + uint64 buffer64;
|
| + stream_->ReadVarint64(&buffer64);
|
| + result = SimpleItoa(bit_cast<uint64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SINT32: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32);
|
| + result = SimpleItoa(WireFormatLite::ZigZagDecode32(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SINT64: {
|
| + uint64 buffer64;
|
| + stream_->ReadVarint64(&buffer64);
|
| + result = SimpleItoa(WireFormatLite::ZigZagDecode64(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SFIXED32: {
|
| + uint32 buffer32;
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + result = SimpleItoa(bit_cast<int32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_SFIXED64: {
|
| + uint64 buffer64;
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + result = SimpleItoa(bit_cast<int64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FIXED32: {
|
| + uint32 buffer32;
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + result = SimpleItoa(bit_cast<uint32>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FIXED64: {
|
| + uint64 buffer64;
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + result = SimpleItoa(bit_cast<uint64>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_FLOAT: {
|
| + uint32 buffer32;
|
| + stream_->ReadLittleEndian32(&buffer32);
|
| + result = SimpleFtoa(bit_cast<float>(buffer32));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_DOUBLE: {
|
| + uint64 buffer64;
|
| + stream_->ReadLittleEndian64(&buffer64);
|
| + result = SimpleDtoa(bit_cast<double>(buffer64));
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_ENUM: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32);
|
| + // Get the nested enum type for this field.
|
| + // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
|
| + // up.
|
| + const google::protobuf::Enum* en =
|
| + typeinfo_->GetEnumByTypeUrl(field.type_url());
|
| + // Lookup the name of the enum, and render that. Skips unknown enums.
|
| + if (en != NULL) {
|
| + const google::protobuf::EnumValue* enum_value =
|
| + FindEnumValueByNumber(*en, buffer32);
|
| + if (enum_value != NULL) {
|
| + result = enum_value->name();
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_STRING: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32); // string size.
|
| + stream_->ReadString(&result, buffer32);
|
| + break;
|
| + }
|
| + case google::protobuf::Field_Kind_TYPE_BYTES: {
|
| + uint32 buffer32;
|
| + stream_->ReadVarint32(&buffer32); // bytes size.
|
| + stream_->ReadString(&result, buffer32);
|
| + break;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +// Field is a map if it is a repeated message and it has an option "map_type".
|
| +// TODO(skarvaje): Consider pre-computing the IsMap() into Field directly.
|
| +bool ProtoStreamObjectSource::IsMap(
|
| + const google::protobuf::Field& field) const {
|
| + const google::protobuf::Type* field_type =
|
| + typeinfo_->GetTypeByTypeUrl(field.type_url());
|
| +
|
| + // TODO(xiaofeng): Unify option names.
|
| + return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE &&
|
| + (GetBoolOptionOrDefault(field_type->options(),
|
| + "google.protobuf.MessageOptions.map_entry", false) ||
|
| + GetBoolOptionOrDefault(field_type->options(), "map_entry", false));
|
| +}
|
| +
|
| +std::pair<int64, int32> ProtoStreamObjectSource::ReadSecondsAndNanos(
|
| + const google::protobuf::Type& type) const {
|
| + uint64 seconds = 0;
|
| + uint32 nanos = 0;
|
| + uint32 tag = 0;
|
| + int64 signed_seconds = 0;
|
| + int32 signed_nanos = 0;
|
| +
|
| + for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
|
| + const google::protobuf::Field* field = FindAndVerifyField(type, tag);
|
| + if (field == NULL) {
|
| + WireFormat::SkipField(stream_, tag, NULL);
|
| + continue;
|
| + }
|
| + // 'seconds' has field number of 1 and 'nanos' has field number 2
|
| + // //google/protobuf/timestamp.proto & duration.proto
|
| + if (field->number() == 1) {
|
| + // read seconds
|
| + stream_->ReadVarint64(&seconds);
|
| + signed_seconds = bit_cast<int64>(seconds);
|
| + } else if (field->number() == 2) {
|
| + // read nanos
|
| + stream_->ReadVarint32(&nanos);
|
| + signed_nanos = bit_cast<int32>(nanos);
|
| + }
|
| + }
|
| + return std::pair<int64, int32>(signed_seconds, signed_nanos);
|
| +}
|
| +
|
| +namespace {
|
| +// TODO(skarvaje): Speed this up by not doing a linear scan.
|
| +const google::protobuf::Field* FindFieldByNumber(
|
| + const google::protobuf::Type& type, int number) {
|
| + for (int i = 0; i < type.fields_size(); ++i) {
|
| + if (type.fields(i).number() == number) {
|
| + return &type.fields(i);
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +// TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable()
|
| +// using tech Field.
|
| +bool IsPackable(const google::protobuf::Field& field) {
|
| + return field.cardinality() ==
|
| + google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
|
| + google::protobuf::FieldDescriptor::IsTypePackable(
|
| + static_cast<google::protobuf::FieldDescriptor::Type>(field.kind()));
|
| +}
|
| +
|
| +// TODO(skarvaje): Speed this up by not doing a linear scan.
|
| +const google::protobuf::EnumValue* FindEnumValueByNumber(
|
| + const google::protobuf::Enum& tech_enum, int number) {
|
| + for (int i = 0; i < tech_enum.enumvalue_size(); ++i) {
|
| + const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i);
|
| + if (ev.number() == number) {
|
| + return &ev;
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +// TODO(skarvaje): Look into optimizing this by not doing computation on
|
| +// double.
|
| +const string FormatNanos(uint32 nanos) {
|
| + const char* format =
|
| + (nanos % 1000 != 0) ? "%.9f" : (nanos % 1000000 != 0) ? "%.6f" : "%.3f";
|
| + string formatted =
|
| + StringPrintf(format, static_cast<double>(nanos) / kNanosPerSecond);
|
| + // remove the leading 0 before decimal.
|
| + return formatted.substr(1);
|
| +}
|
| +} // namespace
|
| +
|
| +} // namespace converter
|
| +} // namespace util
|
| +} // namespace protobuf
|
| +} // namespace google
|
|
|