1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // This header file defines an internal class that encapsulates internal message 32 // metadata (Unknown-field set, Arena pointer, ...) and allows its 33 // representation to be made more space-efficient via various optimizations. 34 // 35 // Note that this is distinct from google::protobuf::Metadata, which encapsulates 36 // Descriptor and Reflection pointers. 37 38 #ifndef GOOGLE_PROTOBUF_METADATA_H__ 39 #define GOOGLE_PROTOBUF_METADATA_H__ 40 41 #include <google/protobuf/stubs/common.h> 42 #include <google/protobuf/arena.h> 43 #include <google/protobuf/unknown_field_set.h> 44 45 namespace google { 46 namespace protobuf { 47 namespace internal { 48 49 // This is the representation for messages that support arena allocation. It 50 // uses a tagged pointer to either store the Arena pointer, if there are no 51 // unknown fields, or a pointer to a block of memory with both the Arena pointer 52 // and the UnknownFieldSet, if there are unknown fields. This optimization 53 // allows for "zero-overhead" storage of the Arena pointer, relative to the 54 // above baseline implementation. 55 // 56 // The tagged pointer uses the LSB to disambiguate cases, and uses bit 0 == 0 to 57 // indicate an arena pointer and bit 0 == 1 to indicate a UFS+Arena-container 58 // pointer. 59 class LIBPROTOBUF_EXPORT InternalMetadataWithArena { 60 public: InternalMetadataWithArena()61 InternalMetadataWithArena() : ptr_(NULL) {} InternalMetadataWithArena(Arena * arena)62 explicit InternalMetadataWithArena(Arena* arena) 63 : ptr_ (arena) {} 64 ~InternalMetadataWithArena()65 ~InternalMetadataWithArena() { 66 if (have_unknown_fields() && arena() == NULL) { 67 delete PtrValue<Container>(); 68 } 69 ptr_ = NULL; 70 } 71 unknown_fields()72 GOOGLE_ATTRIBUTE_ALWAYS_INLINE const UnknownFieldSet& unknown_fields() const { 73 if (GOOGLE_PREDICT_FALSE(have_unknown_fields())) { 74 return PtrValue<Container>()->unknown_fields_; 75 } else { 76 return *UnknownFieldSet::default_instance(); 77 } 78 } 79 mutable_unknown_fields()80 GOOGLE_ATTRIBUTE_ALWAYS_INLINE UnknownFieldSet* mutable_unknown_fields() { 81 if (GOOGLE_PREDICT_TRUE(have_unknown_fields())) { 82 return &PtrValue<Container>()->unknown_fields_; 83 } else { 84 return mutable_unknown_fields_slow(); 85 } 86 } 87 arena()88 GOOGLE_ATTRIBUTE_ALWAYS_INLINE Arena* arena() const { 89 if (GOOGLE_PREDICT_FALSE(have_unknown_fields())) { 90 return PtrValue<Container>()->arena_; 91 } else { 92 return PtrValue<Arena>(); 93 } 94 } 95 have_unknown_fields()96 GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool have_unknown_fields() const { 97 return PtrTag() == kTagContainer; 98 } 99 Swap(InternalMetadataWithArena * other)100 GOOGLE_ATTRIBUTE_ALWAYS_INLINE void Swap(InternalMetadataWithArena* other) { 101 // Semantics here are that we swap only the unknown fields, not the arena 102 // pointer. We cannot simply swap ptr_ with other->ptr_ because we need to 103 // maintain our own arena ptr. Also, our ptr_ and other's ptr_ may be in 104 // different states (direct arena pointer vs. container with UFS) so we 105 // cannot simply swap ptr_ and then restore the arena pointers. We reuse 106 // UFS's swap implementation instead. 107 if (have_unknown_fields() || other->have_unknown_fields()) { 108 mutable_unknown_fields()->Swap(other->mutable_unknown_fields()); 109 } 110 } 111 raw_arena_ptr()112 GOOGLE_ATTRIBUTE_ALWAYS_INLINE void* raw_arena_ptr() const { 113 return ptr_; 114 } 115 116 private: 117 void* ptr_; 118 119 // Tagged pointer implementation. 120 enum { 121 // ptr_ is an Arena*. 122 kTagArena = 0, 123 // ptr_ is a Container*. 124 kTagContainer = 1, 125 }; 126 static const intptr_t kPtrTagMask = 1; 127 static const intptr_t kPtrValueMask = ~kPtrTagMask; 128 129 // Accessors for pointer tag and pointer value. PtrTag()130 GOOGLE_ATTRIBUTE_ALWAYS_INLINE int PtrTag() const { 131 return reinterpret_cast<intptr_t>(ptr_) & kPtrTagMask; 132 } 133 PtrValue()134 template<typename T> T* PtrValue() const { 135 return reinterpret_cast<T*>( 136 reinterpret_cast<intptr_t>(ptr_) & kPtrValueMask); 137 } 138 139 // If ptr_'s tag is kTagContainer, it points to an instance of this struct. 140 struct Container { 141 UnknownFieldSet unknown_fields_; 142 Arena* arena_; 143 }; 144 mutable_unknown_fields_slow()145 GOOGLE_ATTRIBUTE_NOINLINE UnknownFieldSet* mutable_unknown_fields_slow() { 146 Arena* my_arena = arena(); 147 Container* container = Arena::Create<Container>(my_arena); 148 // Two-step assignment works around a bug in clang's static analyzer: 149 // https://bugs.llvm.org/show_bug.cgi?id=34198. 150 ptr_ = container; 151 ptr_ = reinterpret_cast<void*>( 152 reinterpret_cast<intptr_t>(ptr_) | kTagContainer); 153 container->arena_ = my_arena; 154 return &(container->unknown_fields_); 155 } 156 }; 157 158 // Temporary compatibility typedef. Remove once this is released in components 159 // and upb CL is submitted. 160 typedef InternalMetadataWithArena InternalMetadata; 161 162 } // namespace internal 163 } // namespace protobuf 164 165 } // namespace google 166 #endif // GOOGLE_PROTOBUF_METADATA_H__ 167