| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274 |
- // 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_objectwriter.h>
- #include <functional>
- #include <stack>
- #include <google/protobuf/stubs/once.h>
- #include <google/protobuf/stubs/time.h>
- #include <google/protobuf/wire_format_lite.h>
- #include <google/protobuf/util/internal/field_mask_utility.h>
- #include <google/protobuf/util/internal/object_location_tracker.h>
- #include <google/protobuf/util/internal/constants.h>
- #include <google/protobuf/util/internal/utility.h>
- #include <google/protobuf/stubs/strutil.h>
- #include <google/protobuf/stubs/map_util.h>
- #include <google/protobuf/stubs/statusor.h>
- namespace google {
- namespace protobuf {
- namespace util {
- namespace converter {
- using google::protobuf::internal::WireFormatLite;
- using util::error::INVALID_ARGUMENT;
- using util::Status;
- using util::StatusOr;
- ProtoStreamObjectWriter::ProtoStreamObjectWriter(
- TypeResolver* type_resolver, const google::protobuf::Type& type,
- strings::ByteSink* output, ErrorListener* listener,
- const ProtoStreamObjectWriter::Options& options)
- : ProtoWriter(type_resolver, type, output, listener),
- master_type_(type),
- current_(nullptr),
- options_(options) {
- set_ignore_unknown_fields(options_.ignore_unknown_fields);
- set_use_lower_camel_for_enums(options_.use_lower_camel_for_enums);
- }
- ProtoStreamObjectWriter::ProtoStreamObjectWriter(
- const TypeInfo* typeinfo, const google::protobuf::Type& type,
- strings::ByteSink* output, ErrorListener* listener)
- : ProtoWriter(typeinfo, type, output, listener),
- master_type_(type),
- current_(nullptr),
- options_(ProtoStreamObjectWriter::Options::Defaults()) {}
- ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
- if (current_ == nullptr) return;
- // Cleanup explicitly in order to avoid destructor stack overflow when input
- // is deeply nested.
- // Cast to BaseElement to avoid doing additional checks (like missing fields)
- // during pop().
- std::unique_ptr<BaseElement> element(
- static_cast<BaseElement*>(current_.get())->pop<BaseElement>());
- while (element != nullptr) {
- element.reset(element->pop<BaseElement>());
- }
- }
- namespace {
- // Utility method to split a string representation of Timestamp or Duration and
- // return the parts.
- void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
- StringPiece* nanos) {
- size_t idx = input.rfind('.');
- if (idx != string::npos) {
- *seconds = input.substr(0, idx);
- *nanos = input.substr(idx + 1);
- } else {
- *seconds = input;
- *nanos = StringPiece();
- }
- }
- Status GetNanosFromStringPiece(StringPiece s_nanos,
- const char* parse_failure_message,
- const char* exceeded_limit_message,
- int32* nanos) {
- *nanos = 0;
- // Count the number of leading 0s and consume them.
- int num_leading_zeros = 0;
- while (s_nanos.Consume("0")) {
- num_leading_zeros++;
- }
- int32 i_nanos = 0;
- // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to
- // "0." + s_nanos.ToString() seconds. An int32 is used for the
- // conversion to 'nanos', rather than a double, so that there is no
- // loss of precision.
- if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) {
- return Status(INVALID_ARGUMENT, parse_failure_message);
- }
- if (i_nanos > kNanosPerSecond || i_nanos < 0) {
- return Status(INVALID_ARGUMENT, exceeded_limit_message);
- }
- // s_nanos should only have digits. No whitespace.
- if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) {
- return Status(INVALID_ARGUMENT, parse_failure_message);
- }
- if (i_nanos > 0) {
- // 'scale' is the number of digits to the right of the decimal
- // point in "0." + s_nanos.ToString()
- int32 scale = num_leading_zeros + s_nanos.size();
- // 'conversion' converts i_nanos into nanoseconds.
- // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale))
- // For efficiency, we precompute the conversion factor.
- int32 conversion = 0;
- switch (scale) {
- case 1:
- conversion = 100000000;
- break;
- case 2:
- conversion = 10000000;
- break;
- case 3:
- conversion = 1000000;
- break;
- case 4:
- conversion = 100000;
- break;
- case 5:
- conversion = 10000;
- break;
- case 6:
- conversion = 1000;
- break;
- case 7:
- conversion = 100;
- break;
- case 8:
- conversion = 10;
- break;
- case 9:
- conversion = 1;
- break;
- default:
- return Status(INVALID_ARGUMENT, exceeded_limit_message);
- }
- *nanos = i_nanos * conversion;
- }
- return Status();
- }
- } // namespace
- ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
- : parent_(parent),
- ow_(),
- invalid_(false),
- data_(),
- output_(&data_),
- depth_(0),
- is_well_known_type_(false),
- well_known_type_render_(nullptr) {}
- ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
- void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
- ++depth_;
- // If an object writer is absent, that means we have not called StartAny()
- // before reaching here, which happens when we have data before the "@type"
- // field.
- if (ow_ == nullptr) {
- // Save data before the "@type" field for later replay.
- uninterpreted_events_.push_back(Event(Event::START_OBJECT, name));
- } else if (is_well_known_type_ && depth_ == 1) {
- // For well-known types, the only other field besides "@type" should be a
- // "value" field.
- if (name != "value" && !invalid_) {
- parent_->InvalidValue("Any",
- "Expect a \"value\" field for well-known types.");
- invalid_ = true;
- }
- ow_->StartObject("");
- } else {
- // Forward the call to the child writer if:
- // 1. the type is not a well-known type.
- // 2. or, we are in a nested Any, Struct, or Value object.
- ow_->StartObject(name);
- }
- }
- bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
- --depth_;
- if (ow_ == nullptr) {
- if (depth_ >= 0) {
- // Save data before the "@type" field for later replay.
- uninterpreted_events_.push_back(Event(Event::END_OBJECT));
- }
- } else if (depth_ >= 0 || !is_well_known_type_) {
- // As long as depth_ >= 0, we know we haven't reached the end of Any.
- // Propagate these EndObject() calls to the contained ow_. For regular
- // message types, we propagate the end of Any as well.
- ow_->EndObject();
- }
- // A negative depth_ implies that we have reached the end of Any
- // object. Now we write out its contents.
- if (depth_ < 0) {
- WriteAny();
- return false;
- }
- return true;
- }
- void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
- ++depth_;
- if (ow_ == nullptr) {
- // Save data before the "@type" field for later replay.
- uninterpreted_events_.push_back(Event(Event::START_LIST, name));
- } else if (is_well_known_type_ && depth_ == 1) {
- if (name != "value" && !invalid_) {
- parent_->InvalidValue("Any",
- "Expect a \"value\" field for well-known types.");
- invalid_ = true;
- }
- ow_->StartList("");
- } else {
- ow_->StartList(name);
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::EndList() {
- --depth_;
- if (depth_ < 0) {
- GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
- depth_ = 0;
- }
- if (ow_ == nullptr) {
- // Save data before the "@type" field for later replay.
- uninterpreted_events_.push_back(Event(Event::END_LIST));
- } else {
- ow_->EndList();
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
- StringPiece name, const DataPiece& value) {
- // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
- // should go to the contained ow_ as they indicate nested Anys.
- if (depth_ == 0 && ow_ == nullptr && name == "@type") {
- StartAny(value);
- } else if (ow_ == nullptr) {
- // Save data before the "@type" field.
- uninterpreted_events_.push_back(Event(name, value));
- } else if (depth_ == 0 && is_well_known_type_) {
- if (name != "value" && !invalid_) {
- parent_->InvalidValue("Any",
- "Expect a \"value\" field for well-known types.");
- invalid_ = true;
- }
- if (well_known_type_render_ == nullptr) {
- // Only Any and Struct don't have a special type render but both of
- // them expect a JSON object (i.e., a StartObject() call).
- if (value.type() != DataPiece::TYPE_NULL && !invalid_) {
- parent_->InvalidValue("Any", "Expect a JSON object.");
- invalid_ = true;
- }
- } else {
- ow_->ProtoWriter::StartObject("");
- Status status = (*well_known_type_render_)(ow_.get(), value);
- if (!status.ok()) ow_->InvalidValue("Any", status.error_message());
- ow_->ProtoWriter::EndObject();
- }
- } else {
- ow_->RenderDataPiece(name, value);
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
- // Figure out the type url. This is a copy-paste from WriteString but we also
- // need the value, so we can't just call through to that.
- if (value.type() == DataPiece::TYPE_STRING) {
- type_url_ = value.str().ToString();
- } else {
- StatusOr<string> s = value.ToString();
- if (!s.ok()) {
- parent_->InvalidValue("String", s.status().error_message());
- invalid_ = true;
- return;
- }
- type_url_ = s.ValueOrDie();
- }
- // Resolve the type url, and report an error if we failed to resolve it.
- StatusOr<const google::protobuf::Type*> resolved_type =
- parent_->typeinfo()->ResolveTypeUrl(type_url_);
- if (!resolved_type.ok()) {
- parent_->InvalidValue("Any", resolved_type.status().error_message());
- invalid_ = true;
- return;
- }
- // At this point, type is never null.
- const google::protobuf::Type* type = resolved_type.ValueOrDie();
- well_known_type_render_ = FindTypeRenderer(type_url_);
- if (well_known_type_render_ != nullptr ||
- // Explicitly list Any and Struct here because they don't have a
- // custom renderer.
- type->name() == kAnyType || type->name() == kStructType) {
- is_well_known_type_ = true;
- }
- // Create our object writer and initialize it with the first StartObject
- // call.
- ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_,
- parent_->listener()));
- // Don't call StartObject() for well-known types yet. Depending on the
- // type of actual data, we may not need to call StartObject(). For
- // example:
- // {
- // "@type": "type.googleapis.com/google.protobuf.Value",
- // "value": [1, 2, 3],
- // }
- // With the above JSON representation, we will only call StartList() on the
- // contained ow_.
- if (!is_well_known_type_) {
- ow_->StartObject("");
- }
- // Now we know the proto type and can interpret all data fields we gathered
- // before the "@type" field.
- for (int i = 0; i < uninterpreted_events_.size(); ++i) {
- uninterpreted_events_[i].Replay(this);
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
- if (ow_ == nullptr) {
- if (uninterpreted_events_.empty()) {
- // We never got any content, so just return immediately, which is
- // equivalent to writing an empty Any.
- return;
- } else {
- // There are uninterpreted data, but we never got a "@type" field.
- if (!invalid_) {
- parent_->InvalidValue("Any", StrCat("Missing @type for any field in ",
- parent_->master_type_.name()));
- invalid_ = true;
- }
- return;
- }
- }
- // Render the type_url and value fields directly to the stream.
- // type_url has tag 1 and value has tag 2.
- WireFormatLite::WriteString(1, type_url_, parent_->stream());
- if (!data_.empty()) {
- WireFormatLite::WriteBytes(2, data_, parent_->stream());
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::Event::Replay(
- AnyWriter* writer) const {
- switch (type_) {
- case START_OBJECT:
- writer->StartObject(name_);
- break;
- case END_OBJECT:
- writer->EndObject();
- break;
- case START_LIST:
- writer->StartList(name_);
- break;
- case END_LIST:
- writer->EndList();
- break;
- case RENDER_DATA_PIECE:
- writer->RenderDataPiece(name_, value_);
- break;
- }
- }
- void ProtoStreamObjectWriter::AnyWriter::Event::DeepCopy() {
- // DataPiece only contains a string reference. To make sure the referenced
- // string value stays valid, we make a copy of the string value and update
- // DataPiece to reference our own copy.
- if (value_.type() == DataPiece::TYPE_STRING) {
- StrAppend(&value_storage_, value_.str());
- value_ = DataPiece(value_storage_, value_.use_strict_base64_decoding());
- } else if (value_.type() == DataPiece::TYPE_BYTES) {
- value_storage_ = value_.ToBytes().ValueOrDie();
- value_ =
- DataPiece(value_storage_, true, value_.use_strict_base64_decoding());
- }
- }
- ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing,
- ItemType item_type, bool is_placeholder,
- bool is_list)
- : BaseElement(nullptr),
- ow_(enclosing),
- any_(),
- item_type_(item_type),
- is_placeholder_(is_placeholder),
- is_list_(is_list) {
- if (item_type_ == ANY) {
- any_.reset(new AnyWriter(ow_));
- }
- if (item_type == MAP) {
- map_keys_.reset(new hash_set<string>);
- }
- }
- ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent,
- ItemType item_type, bool is_placeholder,
- bool is_list)
- : BaseElement(parent),
- ow_(this->parent()->ow_),
- any_(),
- item_type_(item_type),
- is_placeholder_(is_placeholder),
- is_list_(is_list) {
- if (item_type == ANY) {
- any_.reset(new AnyWriter(ow_));
- }
- if (item_type == MAP) {
- map_keys_.reset(new hash_set<string>);
- }
- }
- bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent(
- StringPiece map_key) {
- return InsertIfNotPresent(map_keys_.get(), map_key.ToString());
- }
- ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
- StringPiece name) {
- if (invalid_depth() > 0) {
- IncrementInvalidDepth();
- return this;
- }
- // Starting the root message. Create the root Item and return.
- // ANY message type does not need special handling, just set the ItemType
- // to ANY.
- if (current_ == nullptr) {
- ProtoWriter::StartObject(name);
- current_.reset(new Item(
- this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE,
- false, false));
- // If master type is a special type that needs extra values to be written to
- // stream, we write those values.
- if (master_type_.name() == kStructType) {
- // Struct has a map<string, Value> field called "fields".
- // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
- // "fields": [
- Push("fields", Item::MAP, true, true);
- return this;
- }
- if (master_type_.name() == kStructValueType) {
- // We got a StartObject call with google.protobuf.Value field. The only
- // object within that type is a struct type. So start a struct.
- //
- // The struct field in Value type is named "struct_value"
- // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
- // Also start the map field "fields" within the struct.
- // "struct_value": {
- // "fields": [
- Push("struct_value", Item::MESSAGE, true, false);
- Push("fields", Item::MAP, true, true);
- return this;
- }
- if (master_type_.name() == kStructListValueType) {
- InvalidValue(kStructListValueType,
- "Cannot start root message with ListValue.");
- }
- return this;
- }
- // Send all ANY events to AnyWriter.
- if (current_->IsAny()) {
- current_->any()->StartObject(name);
- return this;
- }
- // If we are within a map, we render name as keys and send StartObject to the
- // value field.
- if (current_->IsMap()) {
- if (!ValidMapKey(name)) {
- IncrementInvalidDepth();
- return this;
- }
- // Map is a repeated field of message type with a "key" and a "value" field.
- // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps
- // message MapFieldEntry {
- // key_type key = 1;
- // value_type value = 2;
- // }
- //
- // repeated MapFieldEntry map_field = N;
- //
- // That means, we render the following element within a list (hence no
- // name):
- // { "key": "<name>", "value": {
- Push("", Item::MESSAGE, false, false);
- ProtoWriter::RenderDataPiece("key",
- DataPiece(name, use_strict_base64_decoding()));
- Push("value", Item::MESSAGE, true, false);
- // Make sure we are valid so far after starting map fields.
- if (invalid_depth() > 0) return this;
- // If top of stack is g.p.Struct type, start the struct the map field within
- // it.
- if (element() != nullptr && IsStruct(*element()->parent_field())) {
- // Render "fields": [
- Push("fields", Item::MAP, true, true);
- return this;
- }
- // If top of stack is g.p.Value type, start the Struct within it.
- if (element() != nullptr && IsStructValue(*element()->parent_field())) {
- // Render
- // "struct_value": {
- // "fields": [
- Push("struct_value", Item::MESSAGE, true, false);
- Push("fields", Item::MAP, true, true);
- }
- return this;
- }
- const google::protobuf::Field* field = BeginNamed(name, false);
- if (field == nullptr) return this;
- if (IsStruct(*field)) {
- // Start a struct object.
- // Render
- // "<name>": {
- // "fields": {
- Push(name, Item::MESSAGE, false, false);
- Push("fields", Item::MAP, true, true);
- return this;
- }
- if (IsStructValue(*field)) {
- // We got a StartObject call with google.protobuf.Value field. The only
- // object within that type is a struct type. So start a struct.
- // Render
- // "<name>": {
- // "struct_value": {
- // "fields": {
- Push(name, Item::MESSAGE, false, false);
- Push("struct_value", Item::MESSAGE, true, false);
- Push("fields", Item::MAP, true, true);
- return this;
- }
- if (IsMap(*field)) {
- // Begin a map. A map is triggered by a StartObject() call if the current
- // field has a map type.
- // A map type is always repeated, hence set is_list to true.
- // Render
- // "<name>": [
- Push(name, Item::MAP, false, true);
- return this;
- }
- // A regular message type. Pass it directly to ProtoWriter.
- // Render
- // "<name>": {
- Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false);
- return this;
- }
- ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
- if (invalid_depth() > 0) {
- DecrementInvalidDepth();
- return this;
- }
- if (current_ == nullptr) return this;
- if (current_->IsAny()) {
- if (current_->any()->EndObject()) return this;
- }
- Pop();
- return this;
- }
- ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
- if (invalid_depth() > 0) {
- IncrementInvalidDepth();
- return this;
- }
- // Since we cannot have a top-level repeated item in protobuf, the only way
- // this is valid is if we start a special type google.protobuf.ListValue or
- // google.protobuf.Value.
- if (current_ == nullptr) {
- if (!name.empty()) {
- InvalidName(name, "Root element should not be named.");
- IncrementInvalidDepth();
- return this;
- }
- // If master type is a special type that needs extra values to be written to
- // stream, we write those values.
- if (master_type_.name() == kStructValueType) {
- // We got a StartList with google.protobuf.Value master type. This means
- // we have to start the "list_value" within google.protobuf.Value.
- //
- // See
- // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto
- //
- // Render
- // "<name>": {
- // "list_value": {
- // "values": [ // Start this list.
- ProtoWriter::StartObject(name);
- current_.reset(new Item(this, Item::MESSAGE, false, false));
- Push("list_value", Item::MESSAGE, true, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- if (master_type_.name() == kStructListValueType) {
- // We got a StartList with google.protobuf.ListValue master type. This
- // means we have to start the "values" within google.protobuf.ListValue.
- //
- // Render
- // "<name>": {
- // "values": [ // Start this list.
- ProtoWriter::StartObject(name);
- current_.reset(new Item(this, Item::MESSAGE, false, false));
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- // Send the event to ProtoWriter so proper errors can be reported.
- //
- // Render a regular list:
- // "<name>": [
- ProtoWriter::StartList(name);
- current_.reset(new Item(this, Item::MESSAGE, false, true));
- return this;
- }
- if (current_->IsAny()) {
- current_->any()->StartList(name);
- return this;
- }
- // If the top of stack is a map, we are starting a list value within a map.
- // Since map does not allow repeated values, this can only happen when the map
- // value is of a special type that renders a list in JSON. These can be one
- // of 3 cases:
- // i. We are rendering a list value within google.protobuf.Struct
- // ii. We are rendering a list value within google.protobuf.Value
- // iii. We are rendering a list value with type google.protobuf.ListValue.
- if (current_->IsMap()) {
- if (!ValidMapKey(name)) {
- IncrementInvalidDepth();
- return this;
- }
- // Start the repeated map entry object.
- // Render
- // { "key": "<name>", "value": {
- Push("", Item::MESSAGE, false, false);
- ProtoWriter::RenderDataPiece("key",
- DataPiece(name, use_strict_base64_decoding()));
- Push("value", Item::MESSAGE, true, false);
- // Make sure we are valid after pushing all above items.
- if (invalid_depth() > 0) return this;
- // case i and ii above. Start "list_value" field within g.p.Value
- if (element() != nullptr && element()->parent_field() != nullptr) {
- // Render
- // "list_value": {
- // "values": [ // Start this list
- if (IsStructValue(*element()->parent_field())) {
- Push("list_value", Item::MESSAGE, true, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- // Render
- // "values": [
- if (IsStructListValue(*element()->parent_field())) {
- // case iii above. Bind directly to g.p.ListValue
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- }
- // Report an error.
- InvalidValue("Map", StrCat("Cannot have repeated items ('", name,
- "') within a map."));
- return this;
- }
- // When name is empty and stack is not empty, we are rendering an item within
- // a list.
- if (name.empty()) {
- if (element() != nullptr && element()->parent_field() != nullptr) {
- if (IsStructValue(*element()->parent_field())) {
- // Since it is g.p.Value, we bind directly to the list_value.
- // Render
- // { // g.p.Value item within the list
- // "list_value": {
- // "values": [
- Push("", Item::MESSAGE, false, false);
- Push("list_value", Item::MESSAGE, true, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- if (IsStructListValue(*element()->parent_field())) {
- // Since it is g.p.ListValue, we bind to it directly.
- // Render
- // { // g.p.ListValue item within the list
- // "values": [
- Push("", Item::MESSAGE, false, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- }
- // Pass the event to underlying ProtoWriter.
- Push(name, Item::MESSAGE, false, true);
- return this;
- }
- // name is not empty
- const google::protobuf::Field* field = Lookup(name);
- if (field == nullptr) {
- IncrementInvalidDepth();
- return this;
- }
- if (IsStructValue(*field)) {
- // If g.p.Value is repeated, start that list. Otherwise, start the
- // "list_value" within it.
- if (IsRepeated(*field)) {
- // Render it just like a regular repeated field.
- // "<name>": [
- Push(name, Item::MESSAGE, false, true);
- return this;
- }
- // Start the "list_value" field.
- // Render
- // "<name>": {
- // "list_value": {
- // "values": [
- Push(name, Item::MESSAGE, false, false);
- Push("list_value", Item::MESSAGE, true, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- if (IsStructListValue(*field)) {
- // If g.p.ListValue is repeated, start that list. Otherwise, start the
- // "values" within it.
- if (IsRepeated(*field)) {
- // Render it just like a regular repeated field.
- // "<name>": [
- Push(name, Item::MESSAGE, false, true);
- return this;
- }
- // Start the "values" field within g.p.ListValue.
- // Render
- // "<name>": {
- // "values": [
- Push(name, Item::MESSAGE, false, false);
- Push("values", Item::MESSAGE, true, true);
- return this;
- }
- // If we are here, the field should be repeated. Report an error otherwise.
- if (!IsRepeated(*field)) {
- IncrementInvalidDepth();
- InvalidName(name, "Proto field is not repeating, cannot start list.");
- return this;
- }
- if (IsMap(*field)) {
- InvalidValue("Map",
- StrCat("Cannot bind a list to map for field '", name, "'."));
- IncrementInvalidDepth();
- return this;
- }
- // Pass the event to ProtoWriter.
- // Render
- // "<name>": [
- Push(name, Item::MESSAGE, false, true);
- return this;
- }
- ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
- if (invalid_depth() > 0) {
- DecrementInvalidDepth();
- return this;
- }
- if (current_ == nullptr) return this;
- if (current_->IsAny()) {
- current_->any()->EndList();
- return this;
- }
- Pop();
- return this;
- }
- Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
- const DataPiece& data) {
- string struct_field_name;
- switch (data.type()) {
- // Our JSON parser parses numbers as either int64, uint64, or double.
- case DataPiece::TYPE_INT64: {
- // If the option to treat integers as strings is set, then render them as
- // strings. Otherwise, fallback to rendering them as double.
- if (ow->options_.struct_integers_as_strings) {
- StatusOr<int64> int_value = data.ToInt64();
- if (int_value.ok()) {
- ow->ProtoWriter::RenderDataPiece(
- "string_value",
- DataPiece(SimpleItoa(int_value.ValueOrDie()), true));
- return Status();
- }
- }
- struct_field_name = "number_value";
- break;
- }
- case DataPiece::TYPE_UINT64: {
- // If the option to treat integers as strings is set, then render them as
- // strings. Otherwise, fallback to rendering them as double.
- if (ow->options_.struct_integers_as_strings) {
- StatusOr<uint64> int_value = data.ToUint64();
- if (int_value.ok()) {
- ow->ProtoWriter::RenderDataPiece(
- "string_value",
- DataPiece(SimpleItoa(int_value.ValueOrDie()), true));
- return Status();
- }
- }
- struct_field_name = "number_value";
- break;
- }
- case DataPiece::TYPE_DOUBLE: {
- if (ow->options_.struct_integers_as_strings) {
- StatusOr<double> double_value = data.ToDouble();
- if (double_value.ok()) {
- ow->ProtoWriter::RenderDataPiece(
- "string_value",
- DataPiece(SimpleDtoa(double_value.ValueOrDie()), true));
- return Status();
- }
- }
- struct_field_name = "number_value";
- break;
- }
- case DataPiece::TYPE_STRING: {
- struct_field_name = "string_value";
- break;
- }
- case DataPiece::TYPE_BOOL: {
- struct_field_name = "bool_value";
- break;
- }
- case DataPiece::TYPE_NULL: {
- struct_field_name = "null_value";
- break;
- }
- default: {
- return Status(INVALID_ARGUMENT,
- "Invalid struct data type. Only number, string, boolean or "
- "null values are supported.");
- }
- }
- ow->ProtoWriter::RenderDataPiece(struct_field_name, data);
- return Status();
- }
- Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
- const DataPiece& data) {
- if (data.type() == DataPiece::TYPE_NULL) return Status();
- if (data.type() != DataPiece::TYPE_STRING) {
- return Status(INVALID_ARGUMENT,
- StrCat("Invalid data type for timestamp, value is ",
- data.ValueAsStringOrDefault("")));
- }
- StringPiece value(data.str());
- int64 seconds;
- int32 nanos;
- if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
- &nanos)) {
- return Status(INVALID_ARGUMENT, StrCat("Invalid time format: ", value));
- }
- ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
- ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
- return Status();
- }
- static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
- StringPiece path) {
- ow->ProtoWriter::RenderDataPiece(
- "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true));
- return Status();
- }
- Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
- const DataPiece& data) {
- if (data.type() == DataPiece::TYPE_NULL) return Status();
- if (data.type() != DataPiece::TYPE_STRING) {
- return Status(INVALID_ARGUMENT,
- StrCat("Invalid data type for field mask, value is ",
- data.ValueAsStringOrDefault("")));
- }
- // TODO(tsun): figure out how to do proto descriptor based snake case
- // conversions as much as possible. Because ToSnakeCase sometimes returns the
- // wrong value.
- std::unique_ptr<ResultCallback1<util::Status, StringPiece> > callback(
- ::google::protobuf::NewPermanentCallback(&RenderOneFieldPath, ow));
- return DecodeCompactFieldMaskPaths(data.str(), callback.get());
- }
- Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
- const DataPiece& data) {
- if (data.type() == DataPiece::TYPE_NULL) return Status();
- if (data.type() != DataPiece::TYPE_STRING) {
- return Status(INVALID_ARGUMENT,
- StrCat("Invalid data type for duration, value is ",
- data.ValueAsStringOrDefault("")));
- }
- StringPiece value(data.str());
- if (!StringEndsWith(value, "s")) {
- return Status(INVALID_ARGUMENT,
- "Illegal duration format; duration must end with 's'");
- }
- value = value.substr(0, value.size() - 1);
- int sign = 1;
- if (StringStartsWith(value, "-")) {
- sign = -1;
- value = value.substr(1);
- }
- StringPiece s_secs, s_nanos;
- SplitSecondsAndNanos(value, &s_secs, &s_nanos);
- uint64 unsigned_seconds;
- if (!safe_strtou64(s_secs, &unsigned_seconds)) {
- return Status(INVALID_ARGUMENT,
- "Invalid duration format, failed to parse seconds");
- }
- int32 nanos = 0;
- Status nanos_status = GetNanosFromStringPiece(
- s_nanos, "Invalid duration format, failed to parse nano seconds",
- "Duration value exceeds limits", &nanos);
- if (!nanos_status.ok()) {
- return nanos_status;
- }
- nanos = sign * nanos;
- int64 seconds = sign * unsigned_seconds;
- if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds ||
- nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
- return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
- }
- ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds));
- ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos));
- return Status();
- }
- Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
- const DataPiece& data) {
- if (data.type() == DataPiece::TYPE_NULL) return Status();
- ow->ProtoWriter::RenderDataPiece("value", data);
- return Status();
- }
- ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
- StringPiece name, const DataPiece& data) {
- Status status;
- if (invalid_depth() > 0) return this;
- if (current_ == nullptr) {
- const TypeRenderer* type_renderer =
- FindTypeRenderer(GetFullTypeWithUrl(master_type_.name()));
- if (type_renderer == nullptr) {
- InvalidName(name, "Root element must be a message.");
- return this;
- }
- // Render the special type.
- // "<name>": {
- // ... Render special type ...
- // }
- ProtoWriter::StartObject(name);
- status = (*type_renderer)(this, data);
- if (!status.ok()) {
- InvalidValue(master_type_.name(),
- StrCat("Field '", name, "', ", status.error_message()));
- }
- ProtoWriter::EndObject();
- return this;
- }
- if (current_->IsAny()) {
- current_->any()->RenderDataPiece(name, data);
- return this;
- }
- const google::protobuf::Field* field = nullptr;
- if (current_->IsMap()) {
- if (!ValidMapKey(name)) return this;
- // Render an item in repeated map list.
- // { "key": "<name>", "value":
- Push("", Item::MESSAGE, false, false);
- ProtoWriter::RenderDataPiece("key",
- DataPiece(name, use_strict_base64_decoding()));
- field = Lookup("value");
- if (field == nullptr) {
- Pop();
- GOOGLE_LOG(DFATAL) << "Map does not have a value field.";
- return this;
- }
- const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
- if (type_renderer != nullptr) {
- // Map's value type is a special type. Render it like a message:
- // "value": {
- // ... Render special type ...
- // }
- Push("value", Item::MESSAGE, true, false);
- status = (*type_renderer)(this, data);
- if (!status.ok()) {
- InvalidValue(field->type_url(),
- StrCat("Field '", name, "', ", status.error_message()));
- }
- Pop();
- return this;
- }
- // If we are rendering explicit null values and the backend proto field is
- // not of the google.protobuf.NullType type, we do nothing.
- if (data.type() == DataPiece::TYPE_NULL &&
- field->type_url() != kStructNullValueTypeUrl) {
- Pop();
- return this;
- }
- // Render the map value as a primitive type.
- ProtoWriter::RenderDataPiece("value", data);
- Pop();
- return this;
- }
- field = Lookup(name);
- if (field == nullptr) return this;
- // Check if the field is of special type. Render it accordingly if so.
- const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url());
- if (type_renderer != nullptr) {
- // Pass through null value only for google.protobuf.Value. For other
- // types we ignore null value just like for regular field types.
- if (data.type() != DataPiece::TYPE_NULL ||
- field->type_url() == kStructValueTypeUrl) {
- Push(name, Item::MESSAGE, false, false);
- status = (*type_renderer)(this, data);
- if (!status.ok()) {
- InvalidValue(field->type_url(),
- StrCat("Field '", name, "', ", status.error_message()));
- }
- Pop();
- }
- return this;
- }
- // If we are rendering explicit null values and the backend proto field is
- // not of the google.protobuf.NullType type, we do nothing.
- if (data.type() == DataPiece::TYPE_NULL &&
- field->type_url() != kStructNullValueTypeUrl) {
- return this;
- }
- ProtoWriter::RenderDataPiece(name, data);
- return this;
- }
- // Map of functions that are responsible for rendering well known type
- // represented by the key.
- hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
- ProtoStreamObjectWriter::renderers_ = NULL;
- GOOGLE_PROTOBUF_DECLARE_ONCE(writer_renderers_init_);
- void ProtoStreamObjectWriter::InitRendererMap() {
- renderers_ = new hash_map<string, ProtoStreamObjectWriter::TypeRenderer>();
- (*renderers_)["type.googleapis.com/google.protobuf.Timestamp"] =
- &ProtoStreamObjectWriter::RenderTimestamp;
- (*renderers_)["type.googleapis.com/google.protobuf.Duration"] =
- &ProtoStreamObjectWriter::RenderDuration;
- (*renderers_)["type.googleapis.com/google.protobuf.FieldMask"] =
- &ProtoStreamObjectWriter::RenderFieldMask;
- (*renderers_)["type.googleapis.com/google.protobuf.Double"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Float"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Int64"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.UInt64"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Int32"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.UInt32"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Bool"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.String"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Bytes"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.DoubleValue"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.FloatValue"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Int64Value"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.UInt64Value"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Int32Value"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.UInt32Value"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.BoolValue"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.StringValue"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.BytesValue"] =
- &ProtoStreamObjectWriter::RenderWrapperType;
- (*renderers_)["type.googleapis.com/google.protobuf.Value"] =
- &ProtoStreamObjectWriter::RenderStructValue;
- ::google::protobuf::internal::OnShutdown(&DeleteRendererMap);
- }
- void ProtoStreamObjectWriter::DeleteRendererMap() {
- delete ProtoStreamObjectWriter::renderers_;
- renderers_ = NULL;
- }
- ProtoStreamObjectWriter::TypeRenderer*
- ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
- ::google::protobuf::GoogleOnceInit(&writer_renderers_init_, &InitRendererMap);
- return FindOrNull(*renderers_, type_url);
- }
- bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) {
- if (current_ == nullptr) return true;
- if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) {
- listener()->InvalidName(
- location(), unnormalized_name,
- StrCat("Repeated map key: '", unnormalized_name, "' is already set."));
- return false;
- }
- return true;
- }
- void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type,
- bool is_placeholder, bool is_list) {
- is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name);
- // invalid_depth == 0 means it is a successful StartObject or StartList.
- if (invalid_depth() == 0)
- current_.reset(
- new Item(current_.release(), item_type, is_placeholder, is_list));
- }
- void ProtoStreamObjectWriter::Pop() {
- // Pop all placeholder items sending StartObject or StartList events to
- // ProtoWriter according to is_list value.
- while (current_ != nullptr && current_->is_placeholder()) {
- PopOneElement();
- }
- if (current_ != nullptr) {
- PopOneElement();
- }
- }
- void ProtoStreamObjectWriter::PopOneElement() {
- current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject();
- current_.reset(current_->pop<Item>());
- }
- bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
- if (field.type_url().empty() ||
- field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE ||
- field.cardinality() !=
- google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
- return false;
- }
- const google::protobuf::Type* field_type =
- typeinfo()->GetTypeByTypeUrl(field.type_url());
- return google::protobuf::util::converter::IsMap(field, *field_type);
- }
- bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) {
- return GetTypeWithoutUrl(field.type_url()) == kAnyType;
- }
- bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) {
- return GetTypeWithoutUrl(field.type_url()) == kStructType;
- }
- bool ProtoStreamObjectWriter::IsStructValue(
- const google::protobuf::Field& field) {
- return GetTypeWithoutUrl(field.type_url()) == kStructValueType;
- }
- bool ProtoStreamObjectWriter::IsStructListValue(
- const google::protobuf::Field& field) {
- return GetTypeWithoutUrl(field.type_url()) == kStructListValueType;
- }
- } // namespace converter
- } // namespace util
- } // namespace protobuf
- } // namespace google
|