diff options
Diffstat (limited to 'src/google/protobuf/generated_message_util.h')
-rw-r--r-- | src/google/protobuf/generated_message_util.h | 309 |
1 files changed, 274 insertions, 35 deletions
diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index 8967726e..c26e3e52 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -39,16 +39,26 @@ #define GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__ #include <assert.h> +#include <atomic> +#include <climits> #include <string> +#include <vector> +#include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> -#include <google/protobuf/stubs/once.h> +#include <google/protobuf/stubs/once.h> // Add direct dep on port for pb.cc +#include <google/protobuf/has_bits.h> +#include <google/protobuf/implicit_weak_message.h> +#include <google/protobuf/map_entry_lite.h> +#include <google/protobuf/message_lite.h> +#include <google/protobuf/wire_format_lite.h> namespace google { namespace protobuf { class Arena; + namespace io { class CodedInputStream; } namespace internal { @@ -66,33 +76,39 @@ namespace internal { #define GOOGLE_PROTOBUF_DEPRECATED_ATTR +// Returns the offset of the given field within the given aggregate type. +// This is equivalent to the ANSI C offsetof() macro. However, according +// to the C++ standard, offsetof() only works on POD types, and GCC +// enforces this requirement with a warning. In practice, this rule is +// unnecessarily strict; there is probably no compiler or platform on +// which the offsets of the direct fields of a class are non-constant. +// Fields inherited from superclasses *can* have non-constant offsets, +// but that's not what this macro will be used for. +#if defined(__clang__) +// For Clang we use __builtin_offsetof() and suppress the warning, +// to avoid Control Flow Integrity and UBSan vptr sanitizers from +// crashing while trying to validate the invalid reinterpet_casts. +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(TYPE, FIELD) \ + _Pragma("clang diagnostic pop") +#else +// Note that we calculate relative to the pointer value 16 here since if we +// just use zero, GCC complains about dereferencing a NULL pointer. We +// choose 16 rather than some other number just in case the compiler would +// be confused by an unaligned pointer. +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \ + static_cast< ::google::protobuf::uint32>( \ + reinterpret_cast<const char*>( \ + &reinterpret_cast<const TYPE*>(16)->FIELD) - \ + reinterpret_cast<const char*>(16)) +#endif + // Constants for special floating point values. LIBPROTOBUF_EXPORT double Infinity(); LIBPROTOBUF_EXPORT double NaN(); -// TODO(jieluo): Change to template. We have tried to use template, -// but it causes net/rpc/python:rpcutil_test fail (the empty string will -// init twice). It may related to swig. Change to template after we -// found the solution. - -// Default empty string object. Don't use the pointer directly. Instead, call -// GetEmptyString() to get the reference. -LIBPROTOBUF_EXPORT extern const ::std::string* empty_string_; -LIBPROTOBUF_EXPORT extern ProtobufOnceType empty_string_once_init_; -LIBPROTOBUF_EXPORT void InitEmptyString(); - - -LIBPROTOBUF_EXPORT inline const ::std::string& GetEmptyStringAlreadyInited() { - assert(empty_string_ != NULL); - return *empty_string_; -} -LIBPROTOBUF_EXPORT inline const ::std::string& GetEmptyString() { - ::google::protobuf::GoogleOnceInit(&empty_string_once_init_, &InitEmptyString); - return GetEmptyStringAlreadyInited(); -} - -LIBPROTOBUF_EXPORT int StringSpaceUsedExcludingSelf(const string& str); - // True if IsInitialized() is true for all elements of t. Type is expected // to be a RepeatedPtrField<some message type>. It's useful to have this @@ -106,19 +122,242 @@ template <class Type> bool AllAreInitialized(const Type& t) { return true; } -class ArenaString; +// "Weak" variant of AllAreInitialized, used to implement implicit weak fields. +// This version operates on MessageLite to avoid introducing a dependency on the +// concrete message type. +template <class T> +bool AllAreInitializedWeak(const ::google::protobuf::RepeatedPtrField<T>& t) { + for (int i = t.size(); --i >= 0;) { + if (!reinterpret_cast<const ::google::protobuf::internal::RepeatedPtrFieldBase&>(t) + .Get<::google::protobuf::internal::ImplicitWeakTypeHandler<T> >(i) + .IsInitialized()) { + return false; + } + } + return true; +} + +LIBPROTOBUF_EXPORT void InitProtobufDefaults(); + +struct LIBPROTOBUF_EXPORT FieldMetadata { + uint32 offset; // offset of this field in the struct + uint32 tag; // field * 8 + wire_type + // byte offset * 8 + bit_offset; + // if the high bit is set then this is the byte offset of the oneof_case + // for this field. + uint32 has_offset; + uint32 type; // the type of this field. + const void* ptr; // auxiliary data + + // From the serializer point of view each fundamental type can occur in + // 4 different ways. For simplicity we treat all combinations as a cartesion + // product although not all combinations are allowed. + enum FieldTypeClass { + kPresence, + kNoPresence, + kRepeated, + kPacked, + kOneOf, + kNumTypeClasses // must be last enum + }; + // C++ protobuf has 20 fundamental types, were we added Cord and StringPiece + // and also distinquish the same types if they have different wire format. + enum { + kCordType = 19, + kStringPieceType = 20, + kInlinedType = 21, + kNumTypes = 21, + kSpecial = kNumTypes * kNumTypeClasses, + }; + + static int CalculateType(int fundamental_type, FieldTypeClass type_class); +}; + +inline bool IsPresent(const void* base, uint32 hasbit) { + const uint32* has_bits_array = static_cast<const uint32*>(base); + return (has_bits_array[hasbit / 32] & (1u << (hasbit & 31))) != 0; +} + +inline bool IsOneofPresent(const void* base, uint32 offset, uint32 tag) { + const uint32* oneof = + reinterpret_cast<const uint32*>(static_cast<const uint8*>(base) + offset); + return *oneof == tag >> 3; +} + +typedef void (*SpecialSerializer)(const uint8* base, uint32 offset, uint32 tag, + uint32 has_offset, + ::google::protobuf::io::CodedOutputStream* output); + +LIBPROTOBUF_EXPORT void ExtensionSerializer(const uint8* base, uint32 offset, uint32 tag, + uint32 has_offset, + ::google::protobuf::io::CodedOutputStream* output); +LIBPROTOBUF_EXPORT void UnknownFieldSerializerLite(const uint8* base, uint32 offset, uint32 tag, + uint32 has_offset, + ::google::protobuf::io::CodedOutputStream* output); + +struct SerializationTable { + int num_fields; + const FieldMetadata* field_table; +}; + +LIBPROTOBUF_EXPORT void SerializeInternal(const uint8* base, const FieldMetadata* table, + int num_fields, ::google::protobuf::io::CodedOutputStream* output); + +inline void TableSerialize(const ::google::protobuf::MessageLite& msg, + const SerializationTable* table, + ::google::protobuf::io::CodedOutputStream* output) { + const FieldMetadata* field_table = table->field_table; + int num_fields = table->num_fields - 1; + const uint8* base = reinterpret_cast<const uint8*>(&msg); + // TODO(gerbens) This skips the first test if we could use the fast + // array serialization path, we should make this + // int cached_size = + // *reinterpret_cast<const int32*>(base + field_table->offset); + // SerializeWithCachedSize(msg, field_table + 1, num_fields, cached_size, ...) + // But we keep conformance with the old way for now. + SerializeInternal(base, field_table + 1, num_fields, output); +} + +uint8* SerializeInternalToArray(const uint8* base, const FieldMetadata* table, + int num_fields, bool is_deterministic, + uint8* buffer); + +inline uint8* TableSerializeToArray(const ::google::protobuf::MessageLite& msg, + const SerializationTable* table, + bool is_deterministic, uint8* buffer) { + const uint8* base = reinterpret_cast<const uint8*>(&msg); + const FieldMetadata* field_table = table->field_table + 1; + int num_fields = table->num_fields - 1; + return SerializeInternalToArray(base, field_table, num_fields, + is_deterministic, buffer); +} + +template <typename T> +struct CompareHelper { + bool operator()(const T& a, const T& b) { return a < b; } +}; + +template <> +struct CompareHelper<ArenaStringPtr> { + bool operator()(const ArenaStringPtr& a, const ArenaStringPtr& b) { + return a.Get() < b.Get(); + } +}; + +struct CompareMapKey { + template <typename T> + bool operator()(const MapEntryHelper<T>& a, const MapEntryHelper<T>& b) { + return Compare(a.key_, b.key_); + } + template <typename T> + bool Compare(const T& a, const T& b) { + return CompareHelper<T>()(a, b); + } +}; + +template <typename MapFieldType, const SerializationTable* table> +void MapFieldSerializer(const uint8* base, uint32 offset, uint32 tag, + uint32 has_offset, + ::google::protobuf::io::CodedOutputStream* output) { + typedef MapEntryHelper<typename MapFieldType::EntryTypeTrait> Entry; + typedef typename MapFieldType::MapType::const_iterator Iter; + + const MapFieldType& map_field = + *reinterpret_cast<const MapFieldType*>(base + offset); + const SerializationTable* t = + table + + has_offset; // has_offset is overloaded for maps to mean table offset + if (!output->IsSerializationDeterministic()) { + for (Iter it = map_field.GetMap().begin(); it != map_field.GetMap().end(); + ++it) { + Entry map_entry(*it); + output->WriteVarint32(tag); + output->WriteVarint32(map_entry._cached_size_); + SerializeInternal(reinterpret_cast<const uint8*>(&map_entry), + t->field_table, t->num_fields, output); + } + } else { + std::vector<Entry> v; + for (Iter it = map_field.GetMap().begin(); it != map_field.GetMap().end(); + ++it) { + v.push_back(Entry(*it)); + } + std::sort(v.begin(), v.end(), CompareMapKey()); + for (int i = 0; i < v.size(); i++) { + output->WriteVarint32(tag); + output->WriteVarint32(v[i]._cached_size_); + SerializeInternal(reinterpret_cast<const uint8*>(&v[i]), t->field_table, + t->num_fields, output); + } + } +} + +LIBPROTOBUF_EXPORT MessageLite* DuplicateIfNonNullInternal(MessageLite* message); +LIBPROTOBUF_EXPORT MessageLite* GetOwnedMessageInternal(Arena* message_arena, + MessageLite* submessage, + Arena* submessage_arena); + +template <typename T> +T* DuplicateIfNonNull(T* message) { + // The casts must be reinterpret_cast<> because T might be a forward-declared + // type that the compiler doesn't know is related to MessageLite. + return reinterpret_cast<T*>( + DuplicateIfNonNullInternal(reinterpret_cast<MessageLite*>(message))); +} + +template <typename T> +T* GetOwnedMessage(Arena* message_arena, T* submessage, + Arena* submessage_arena) { + // The casts must be reinterpret_cast<> because T might be a forward-declared + // type that the compiler doesn't know is related to MessageLite. + return reinterpret_cast<T*>(GetOwnedMessageInternal( + message_arena, reinterpret_cast<MessageLite*>(submessage), + submessage_arena)); +} + +// Hide atomic from the public header and allow easy change to regular int +// on platforms where the atomic might have a perf impact. +class LIBPROTOBUF_EXPORT CachedSize { + public: + int Get() const { return size_.load(std::memory_order_relaxed); } + void Set(int size) { size_.store(size, std::memory_order_relaxed); } + private: + std::atomic<int> size_{0}; +}; -// Read a length (varint32), followed by a string, from *input. Return a -// pointer to a copy of the string that resides in *arena. Requires both -// args to be non-NULL. If something goes wrong while reading the data -// then NULL is returned (e.g., input does not start with a valid varint). -LIBPROTOBUF_EXPORT ArenaString* ReadArenaString( - ::google::protobuf::io::CodedInputStream* input, - ::google::protobuf::Arena* arena); +// SCCInfo represents information of a strongly connected component of +// mutual dependent messages. +struct LIBPROTOBUF_EXPORT SCCInfoBase { + // We use 0 for the Initialized state, because test eax,eax, jnz is smaller + // and is subject to macro fusion. + enum { + kInitialized = 0, // final state + kRunning = 1, + kUninitialized = -1, // initial state + }; + std::atomic<int> visit_status; + int num_deps; + void (*init_func)(); + // This is followed by an array of num_deps + // const SCCInfoBase* deps[]; +}; -// Helper function to crash on merge failure. -// Moved out of generated code to reduce binary size. -LIBPROTOBUF_EXPORT void MergeFromFail(const char* file, int line) GOOGLE_ATTRIBUTE_NORETURN; +template <int N> +struct SCCInfo { + SCCInfoBase base; + // Semantically this is const SCCInfo<T>* which is is a templated type. + // The obvious inheriting from SCCInfoBase mucks with struct initialization. + // Attempts showed the compiler was generating dynamic initialization code. + // Zero length arrays produce warnings with MSVC. + SCCInfoBase* deps[N ? N : 1]; +}; + +LIBPROTOBUF_EXPORT void InitSCCImpl(SCCInfoBase* scc); + +inline void InitSCC(SCCInfoBase* scc) { + auto status = scc->visit_status.load(std::memory_order_acquire); + if (GOOGLE_PREDICT_FALSE(status != SCCInfoBase::kInitialized)) InitSCCImpl(scc); +} } // namespace internal } // namespace protobuf |