diff options
Diffstat (limited to 'src/google/protobuf/generated_message_util.h')
-rw-r--r-- | src/google/protobuf/generated_message_util.h | 343 |
1 files changed, 264 insertions, 79 deletions
diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index 14101832..c26e3e52 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -39,25 +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> - -#ifndef PROTOBUF_FINAL -#if LANG_CXX11 -#define PROTOBUF_FINAL final -#else -#define PROTOBUF_FINAL -#endif -#endif // !PROTOBUF_FINAL +#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 { @@ -75,103 +76,287 @@ 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(); -// This type is used to define a global variable, without it's constructor -// and destructor run on start and end of the program lifetime. This circumvents -// the initial construction order fiasco, while keeping the address of the -// empty string a compile time constant. -template <typename T> -class ExplicitlyConstructed { - public: - void DefaultConstruct() { - new (&union_) T(); - init_ = true; + +// 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 +// helper here to keep the protobuf compiler from ever having to emit loops in +// IsInitialized() methods. We want the C++ compiler to inline this or not +// as it sees fit. +template <class Type> bool AllAreInitialized(const Type& t) { + for (int i = t.size(); --i >= 0; ) { + if (!t.Get(i).IsInitialized()) return false; } + return true; +} - bool IsInitialized() { return init_; } - void Shutdown() { - if (init_) { - init_ = false; - get_mutable()->~T(); +// "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; +} -#if LANG_CXX11 - constexpr -#endif - const T& - get() const { - return reinterpret_cast<const T&>(union_); - } - T* get_mutable() { return reinterpret_cast<T*>(&union_); } +LIBPROTOBUF_EXPORT void InitProtobufDefaults(); - private: - // Prefer c++14 aligned_storage, but for compatibility this will do. - union AlignedUnion { - char space[sizeof(T)]; - int64 align_to_int64; - void* align_to_ptr; - } union_; - bool init_; // false by linker +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); }; -// 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. +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); -// Default empty string object. Don't use this directly. Instead, call -// GetEmptyString() to get the reference. -LIBPROTOBUF_EXPORT extern ExplicitlyConstructed< ::std::string> fixed_address_empty_string; -LIBPROTOBUF_EXPORT extern ProtobufOnceType empty_string_once_init_; -LIBPROTOBUF_EXPORT void InitEmptyString(); +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); -LIBPROTOBUF_EXPORT inline const ::std::string& GetEmptyStringAlreadyInited() { - return fixed_address_empty_string.get(); +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); } -LIBPROTOBUF_EXPORT inline const ::std::string& GetEmptyString() { - ::google::protobuf::GoogleOnceInit(&empty_string_once_init_, &InitEmptyString); - return GetEmptyStringAlreadyInited(); +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); } -LIBPROTOBUF_EXPORT int StringSpaceUsedExcludingSelf(const string& str); +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(); + } +}; -// 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 -// helper here to keep the protobuf compiler from ever having to emit loops in -// IsInitialized() methods. We want the C++ compiler to inline this or not -// as it sees fit. -template <class Type> bool AllAreInitialized(const Type& t) { - for (int i = t.size(); --i >= 0; ) { - if (!t.Get(i).IsInitialized()) return false; +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); + } } - return true; } -LIBPROTOBUF_EXPORT void InitProtobufDefaults(); +LIBPROTOBUF_EXPORT MessageLite* DuplicateIfNonNullInternal(MessageLite* message); +LIBPROTOBUF_EXPORT MessageLite* GetOwnedMessageInternal(Arena* message_arena, + MessageLite* submessage, + Arena* submessage_arena); -// We compute sizes as size_t but cache them as int. This function converts a -// computed size to a cached size. Since we don't proceed with serialization if -// the total size was > INT_MAX, it is not important what this function returns -// for inputs > INT_MAX. -inline int ToCachedSize(size_t size) { - return static_cast<int>(size); +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))); } -// We mainly calculate sizes in terms of size_t, but some functions that compute -// sizes return "int". These int sizes are expected to always be positive. -// This function is more efficient than casting an int to size_t directly on -// 64-bit platforms because it avoids making the compiler emit a sign extending -// instruction, which we don't want and don't want to pay for. -inline size_t FromIntSize(int size) { - // Convert to unsigned before widening so sign extension is not necessary. - return static_cast<unsigned int>(size); +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}; +}; + +// 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[]; +}; + +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 |