diff options
Diffstat (limited to 'src/google/protobuf/util/internal/protostream_objectwriter.h')
-rw-r--r-- | src/google/protobuf/util/internal/protostream_objectwriter.h | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h new file mode 100644 index 00000000..eb4a59f9 --- /dev/null +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -0,0 +1,455 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__ +#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__ + +#include <deque> +#include <google/protobuf/stubs/hash.h> +#include <string> + +#include <google/protobuf/stubs/common.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/util/internal/type_info.h> +#include <google/protobuf/util/internal/datapiece.h> +#include <google/protobuf/util/internal/error_listener.h> +#include <google/protobuf/util/internal/structured_objectwriter.h> +#include <google/protobuf/util/type_resolver.h> +#include <google/protobuf/stubs/bytestream.h> + +namespace google { +namespace protobuf { +namespace io { +class CodedOutputStream; +} // namespace io +} // namespace protobuf + + +namespace protobuf { +class Type; +class Field; +} // namespace protobuf + + +namespace protobuf { +namespace util { +namespace converter { + +class ObjectLocationTracker; + +// An ObjectWriter that can write protobuf bytes directly from writer events. +// +// It also supports streaming. +class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter { + public: + // Constructor. Does not take ownership of any parameter passed in. + ProtoStreamObjectWriter(TypeResolver* type_resolver, + const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener); + virtual ~ProtoStreamObjectWriter(); + + // ObjectWriter methods. + virtual ProtoStreamObjectWriter* StartObject(StringPiece name); + virtual ProtoStreamObjectWriter* EndObject(); + virtual ProtoStreamObjectWriter* StartList(StringPiece name); + virtual ProtoStreamObjectWriter* EndList(); + virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, + bool value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, + int32 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name, + uint32 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, + int64 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name, + uint64 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name, + double value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, + float value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderString(StringPiece name, + StringPiece value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name, + StringPiece value) { + return RenderDataPiece(name, DataPiece(value, false)); + } + virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) { + return RenderDataPiece(name, DataPiece::NullData()); + } + + // Renders a DataPiece 'value' into a field whose wire type is determined + // from the given field 'name'. + ProtoStreamObjectWriter* RenderDataPiece(StringPiece name, + const DataPiece& value); + + // Returns the location tracker to use for tracking locations for errors. + const LocationTrackerInterface& location() { + return element_ != NULL ? *element_ : *tracker_; + } + + // When true, we finished writing to output a complete message. + bool done() const { return done_; } + + private: + // Function that renders a well known type with modified behavior. + typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*, + const DataPiece&); + + // Handles writing Anys out using nested object writers and the like. + class LIBPROTOBUF_EXPORT AnyWriter { + public: + explicit AnyWriter(ProtoStreamObjectWriter* parent); + ~AnyWriter(); + + // Passes a StartObject call through to the Any writer. + void StartObject(StringPiece name); + + // Passes an EndObject call through to the Any. Returns true if the any + // handled the EndObject call, false if the Any is now all done and is no + // longer needed. + bool EndObject(); + + // Passes a StartList call through to the Any writer. + void StartList(StringPiece name); + + // Passes an EndList call through to the Any writer. + void EndList(); + + // Renders a data piece on the any. + void RenderDataPiece(StringPiece name, const DataPiece& value); + + private: + // Handles starting up the any once we have a type. + void StartAny(const DataPiece& value); + + // Writes the Any out to the parent writer in its serialized form. + void WriteAny(); + + // The parent of this writer, needed for various bits such as type info and + // the listeners. + ProtoStreamObjectWriter* parent_; + + // The nested object writer, used to write events. + google::protobuf::scoped_ptr<ProtoStreamObjectWriter> ow_; + + // The type_url_ that this Any represents. + string type_url_; + + // Whether this any is invalid. This allows us to only report an invalid + // Any message a single time rather than every time we get a nested field. + bool invalid_; + + // The output data and wrapping ByteSink. + string data_; + strings::StringByteSink output_; + + // The depth within the Any, so we can track when we're done. + int depth_; + + // True if the message type contained in Any has a special "value" message + // injected. This is true for well-known message types like Any or Struct. + bool has_injected_value_message_; + }; + + class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface { + public: + // Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY, + // STRUCT etc. are used to deduce other information based on their position + // on the stack of elements. + enum ElementType { + MESSAGE, // Simple message + LIST, // List/repeated element + MAP, // Proto3 map type + MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields + ANY, // Proto3 Any type + STRUCT, // Proto3 struct type + STRUCT_VALUE, // Struct's Value message type + STRUCT_LIST, // List type indicator within a struct + STRUCT_LIST_VALUE, // Struct Value's ListValue message type + STRUCT_MAP, // Struct within a struct type + STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value' + // fields + }; + + // Constructor for the root element. No parent nor field. + ProtoElement(TypeInfo* typeinfo, const google::protobuf::Type& type, + ProtoStreamObjectWriter* enclosing); + + // Constructor for a field of an element. + ProtoElement(ProtoElement* parent, const google::protobuf::Field* field, + const google::protobuf::Type& type, ElementType element_type); + + virtual ~ProtoElement() {} + + // Called just before the destructor for clean up: + // - reports any missing required fields + // - computes the space needed by the size field, and augment the + // length of all parent messages by this additional space. + // - releases and returns the parent pointer. + ProtoElement* pop(); + + // Accessors + const google::protobuf::Field* field() const { return field_; } + const google::protobuf::Type& type() const { return type_; } + + // These functions return true if the element type is corresponding to the + // type in function name. + bool IsMap() { return element_type_ == MAP; } + bool IsStructMap() { return element_type_ == STRUCT_MAP; } + bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; } + bool IsStructList() { return element_type_ == STRUCT_LIST; } + bool IsAny() { return element_type_ == ANY; } + + ElementType element_type() { return element_type_; } + + void RegisterField(const google::protobuf::Field* field); + virtual string ToString() const; + + AnyWriter* any() const { return any_.get(); } + + virtual ProtoElement* parent() const { + return static_cast<ProtoElement*>(BaseElement::parent()); + } + + private: + // Used for access to variables of the enclosing instance of + // ProtoStreamObjectWriter. + ProtoStreamObjectWriter* ow_; + + // A writer for Any objects, handles all Any-related nonsense. + google::protobuf::scoped_ptr<AnyWriter> any_; + + // Describes the element as a field in the parent message. + // field_ is NULL if and only if this element is the root element. + const google::protobuf::Field* field_; + + // TypeInfo to lookup types. + TypeInfo* typeinfo_; + + // Additional variables if this element is a message: + // (Root element is always a message). + // descriptor_ : describes allowed fields in the message. + // required_fields_: set of required fields. + // is_repeated_type_ : true if the element is of type list or map. + // size_index_ : index into ProtoStreamObjectWriter::size_insert_ + // for later insertion of serialized message length. + const google::protobuf::Type& type_; + std::set<const google::protobuf::Field*> required_fields_; + const bool is_repeated_type_; + const int size_index_; + + // Tracks position in repeated fields, needed for LocationTrackerInterface. + int array_index_; + + // The type of this element, see enum for permissible types. + ElementType element_type_; + + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); + }; + + // Container for inserting 'size' information at the 'pos' position. + struct SizeInfo { + const int pos; + int size; + }; + + ProtoStreamObjectWriter(TypeInfo* typeinfo, + const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener); + + ProtoElement* element() { return element_.get(); } + + // Helper methods for calling ErrorListener. See error_listener.h. + void InvalidName(StringPiece unknown_name, StringPiece message); + void InvalidValue(StringPiece type_name, StringPiece value); + void MissingField(StringPiece missing_name); + + // Common code for BeginObject() and BeginList() that does invalid_depth_ + // bookkeeping associated with name lookup. + const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list); + + // Lookup the field in the current element. Looks in the base descriptor + // and in any extension. This will report an error if the field cannot be + // found or if multiple matching extensions are found. + const google::protobuf::Field* Lookup(StringPiece name); + + // Lookup the field type in the type descriptor. Returns NULL if the type + // is not known. + const google::protobuf::Type* LookupType( + const google::protobuf::Field* field); + + // Looks up the oneof struct Value field depending on the type. + // On failure to find, it returns an appropriate error. + util::StatusOr<const google::protobuf::Field*> LookupStructField( + DataPiece::Type type); + + // Starts an entry in map. This will be called after placing map element at + // the top of the stack. Uses this information to write map entries. + const google::protobuf::Field* StartMapEntry(StringPiece name); + + // Starts a google.protobuf.Struct. + // 'field' is of type google.protobuf.Struct. + // If field is NULL, it indicates that the top-level message is a struct + // type. + void StartStruct(const google::protobuf::Field* field); + + // Starts another struct within a struct. + // 'field' is of type google.protobuf.Value (see struct.proto). + const google::protobuf::Field* StartStructValueInStruct( + const google::protobuf::Field* field); + + // Starts a list within a struct. + // 'field' is of type google.protobuf.ListValue (see struct.proto). + const google::protobuf::Field* StartListValueInStruct( + const google::protobuf::Field* field); + + // Starts the repeated "values" field in struct.proto's + // google.protobuf.ListValue type. 'field' should be of type + // google.protobuf.ListValue. + const google::protobuf::Field* StartRepeatedValuesInListValue( + const google::protobuf::Field* field); + + // Pops sentinel elements off the stack. + void SkipElements(); + + // Write serialized output to the final output ByteSink, inserting all + // the size information for nested messages that are missing from the + // intermediate Cord buffer. + void WriteRootMessage(); + + // Returns true if the field is a map. + bool IsMap(const google::protobuf::Field& field); + + // Returns true if the field is an any. + bool IsAny(const google::protobuf::Field& field); + + // Helper method to write proto tags based on the given field. + void WriteTag(const google::protobuf::Field& field); + + // Helper function to render primitive data types in DataPiece. + void RenderSimpleDataPiece(const google::protobuf::Field& field, + const google::protobuf::Type& type, + const DataPiece& data); + + // Renders google.protobuf.Value in struct.proto. It picks the right oneof + // type based on value's type. + static util::Status RenderStructValue(ProtoStreamObjectWriter* ow, + const DataPiece& value); + + // Renders google.protobuf.Timestamp value. + static util::Status RenderTimestamp(ProtoStreamObjectWriter* ow, + const DataPiece& value); + + // Renders google.protobuf.FieldMask value. + static util::Status RenderFieldMask(ProtoStreamObjectWriter* ow, + const DataPiece& value); + + // Renders google.protobuf.Duration value. + static util::Status RenderDuration(ProtoStreamObjectWriter* ow, + const DataPiece& value); + + // Renders wrapper message types for primitive types in + // google/protobuf/wrappers.proto. + static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow, + const DataPiece& value); + + // Helper functions to create the map and find functions responsible for + // rendering well known types, keyed by type URL. + static hash_map<string, TypeRenderer>* CreateRendererMap(); + static TypeRenderer* FindTypeRenderer(const string& type_url); + + // Returns the ProtoElement::ElementType for the given Type. + static ProtoElement::ElementType GetElementType( + const google::protobuf::Type& type); + + // Variables for describing the structure of the input tree: + // master_type_: descriptor for the whole protobuf message. + // typeinfo_ : the TypeInfo object to lookup types. + const google::protobuf::Type& master_type_; + TypeInfo* typeinfo_; + // Whether we own the typeinfo_ object. + bool own_typeinfo_; + + // Indicates whether we finished writing root message completely. + bool done_; + + // Variable for internal state processing: + // element_ : the current element. + // size_insert_: sizes of nested messages. + // pos - position to insert the size field. + // size - size value to be inserted. + google::protobuf::scoped_ptr<ProtoElement> element_; + std::deque<SizeInfo> size_insert_; + + // Variables for output generation: + // output_ : pointer to an external ByteSink for final user-visible output. + // buffer_ : buffer holding partial message before being ready for output_. + // adapter_ : internal adapter between CodedOutputStream and Cord buffer_. + // stream_ : wrapper for writing tags and other encodings in wire format. + strings::ByteSink* output_; + string buffer_; + google::protobuf::io::StringOutputStream adapter_; + google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_; + + // Variables for error tracking and reporting: + // listener_ : a place to report any errors found. + // invalid_depth_: number of enclosing invalid nested messages. + // tracker_ : the root location tracker interface. + ErrorListener* listener_; + int invalid_depth_; + google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_; + + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter); +}; + +} // namespace converter +} // namespace util +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__ |