diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2015-06-17 13:12:11 -0700 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2015-06-17 13:12:11 -0700 |
commit | e9a122eb19ec54dbca15da80355ed0c17cada9b1 (patch) | |
tree | fac37489873ccf47777b2e652db580f9315d7bc7 /src/google/protobuf/util/internal/protostream_objectsource.cc | |
parent | b36395b2bac88f70070544c325bc09234cb1f1a6 (diff) | |
parent | 818c5eee08840355d70d2f3bdf1a2f17986a5e70 (diff) | |
download | protobuf-e9a122eb19ec54dbca15da80355ed0c17cada9b1.tar.gz protobuf-e9a122eb19ec54dbca15da80355ed0c17cada9b1.tar.bz2 protobuf-e9a122eb19ec54dbca15da80355ed0c17cada9b1.zip |
Merge pull request #501 from xfxyjwf/down
Down-integrate from internal code base.
Diffstat (limited to 'src/google/protobuf/util/internal/protostream_objectsource.cc')
-rw-r--r-- | src/google/protobuf/util/internal/protostream_objectsource.cc | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc new file mode 100644 index 00000000..53a0e47a --- /dev/null +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -0,0 +1,1051 @@ +// 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/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, 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->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) { + if (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_->GetType(field->type_url()); + uint32 tag_to_return = 0; + if (IsPackable(*field) && + list_tag == + WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) { + RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow)); + tag_to_return = stream_->ReadTag(); + } else { + do { + RETURN_IF_ERROR(RenderMapEntry(field_type, ow)); + } while ((tag_to_return = stream_->ReadTag()) == list_tag); + } + return tag_to_return; +} + +Status ProtoStreamObjectSource::RenderMapEntry( + const google::protobuf::Type* type, ObjectWriter* ow) const { + 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(*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"); + } + // Disable case normalization for map keys as they are just data. We + // retain them intact. + ow->DisableCaseNormalizationForNextKey(); + RETURN_IF_ERROR(RenderField(field, map_key, ow)); + } + } + stream_->PopLimit(old_limit); + + return Status::OK; +} + +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::RenderPackedMapEntry( + const google::protobuf::Type* type, ObjectWriter* ow) const { + uint32 length; + stream_->ReadVarint32(&length); + int old_limit = stream_->PushLimit(length); + while (stream_->BytesUntilLimit() > 0) { + RETURN_IF_ERROR(RenderMapEntry(type, 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(); + + // We know the type so we can render it. Recursively parse the nested stream + // using a nested ProtoStreamObjectSource using our nested type information. + google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size()); + google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream); + 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::CreateRendererMap() { + hash_map<string, ProtoStreamObjectSource::TypeRenderer>* result = + new hash_map<string, ProtoStreamObjectSource::TypeRenderer>(); + (*result)["google.protobuf.Timestamp"] = + &ProtoStreamObjectSource::RenderTimestamp; + (*result)["google.protobuf.Duration"] = + &ProtoStreamObjectSource::RenderDuration; + (*result)["google.protobuf.DoubleValue"] = + &ProtoStreamObjectSource::RenderDouble; + (*result)["google.protobuf.FloatValue"] = + &ProtoStreamObjectSource::RenderFloat; + (*result)["google.protobuf.Int64Value"] = + &ProtoStreamObjectSource::RenderInt64; + (*result)["google.protobuf.UInt64Value"] = + &ProtoStreamObjectSource::RenderUInt64; + (*result)["google.protobuf.Int32Value"] = + &ProtoStreamObjectSource::RenderInt32; + (*result)["google.protobuf.UInt32Value"] = + &ProtoStreamObjectSource::RenderUInt32; + (*result)["google.protobuf.BoolValue"] = &ProtoStreamObjectSource::RenderBool; + (*result)["google.protobuf.StringValue"] = + &ProtoStreamObjectSource::RenderString; + (*result)["google.protobuf.BytesValue"] = + &ProtoStreamObjectSource::RenderBytes; + (*result)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny; + (*result)["google.protobuf.Struct"] = &ProtoStreamObjectSource::RenderStruct; + (*result)["google.protobuf.Value"] = + &ProtoStreamObjectSource::RenderStructValue; + (*result)["google.protobuf.ListValue"] = + &ProtoStreamObjectSource::RenderStructListValue; + (*result)["google.protobuf.FieldMask"] = + &ProtoStreamObjectSource::RenderFieldMask; + return result; +} + +// static +ProtoStreamObjectSource::TypeRenderer* +ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) { + static hash_map<string, TypeRenderer>* renderers = CreateRendererMap(); + return FindOrNull(*renderers, type_url); +} + +Status ProtoStreamObjectSource::RenderField( + const google::protobuf::Field* field, StringPiece field_name, + ObjectWriter* ow) const { + switch (field->kind()) { + case google::protobuf::Field_Kind_TYPE_BOOL: { + uint64 buffer64; + stream_->ReadVarint64(&buffer64); + ow->RenderBool(field_name, buffer64 != 0); + break; + } + case google::protobuf::Field_Kind_TYPE_INT32: { + uint32 buffer32; + stream_->ReadVarint32(&buffer32); + ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_INT64: { + uint64 buffer64; + stream_->ReadVarint64(&buffer64); + ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_UINT32: { + uint32 buffer32; + stream_->ReadVarint32(&buffer32); + ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_UINT64: { + uint64 buffer64; + stream_->ReadVarint64(&buffer64); + ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_SINT32: { + uint32 buffer32; + stream_->ReadVarint32(&buffer32); + ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_SINT64: { + uint64 buffer64; + stream_->ReadVarint64(&buffer64); + ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_SFIXED32: { + uint32 buffer32; + stream_->ReadLittleEndian32(&buffer32); + ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_SFIXED64: { + uint64 buffer64; + stream_->ReadLittleEndian64(&buffer64); + ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_FIXED32: { + uint32 buffer32; + stream_->ReadLittleEndian32(&buffer32); + ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_FIXED64: { + uint64 buffer64; + stream_->ReadLittleEndian64(&buffer64); + ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_FLOAT: { + uint32 buffer32; + stream_->ReadLittleEndian32(&buffer32); + ow->RenderFloat(field_name, bit_cast<float>(buffer32)); + break; + } + case google::protobuf::Field_Kind_TYPE_DOUBLE: { + uint64 buffer64; + stream_->ReadLittleEndian64(&buffer64); + ow->RenderDouble(field_name, bit_cast<double>(buffer64)); + break; + } + case google::protobuf::Field_Kind_TYPE_ENUM: { + uint32 buffer32; + 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_->GetEnum(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) << "Unkown enum skipped: " << field->type_url(); + } + break; + } + case google::protobuf::Field_Kind_TYPE_STRING: { + uint32 buffer32; + string str; + stream_->ReadVarint32(&buffer32); // string size. + stream_->ReadString(&str, buffer32); + ow->RenderString(field_name, str); + break; + } + case google::protobuf::Field_Kind_TYPE_BYTES: { + uint32 buffer32; + stream_->ReadVarint32(&buffer32); // bytes size. + string value; + stream_->ReadString(&value, buffer32); + ow->RenderBytes(field_name, value); + break; + } + case 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_->GetType(field->type_url()); + if (type == NULL) { + return Status(util::error::INTERNAL, + StrCat("Invalid configuration. Could not find the type: ", + field->type_url())); + } + 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); + 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_->GetEnum(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_->GetType(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; + int64 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 |