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 |