1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 #ifndef GOOGLE_PROTOBUF_METADATA_LITE_H__ 9 #define GOOGLE_PROTOBUF_METADATA_LITE_H__ 10 11 #include <string> 12 13 #include "google/protobuf/arena.h" 14 #include "google/protobuf/port.h" 15 16 // Must be included last. 17 #include "google/protobuf/port_def.inc" 18 19 #ifdef SWIG 20 #error "You cannot SWIG proto headers" 21 #endif 22 23 namespace google { 24 namespace protobuf { 25 26 class UnknownFieldSet; 27 28 namespace internal { 29 30 // This is the representation for messages that support arena allocation. It 31 // uses a tagged pointer to either store the owning Arena pointer, if there are 32 // no unknown fields, or a pointer to a block of memory with both the owning 33 // Arena pointer and the UnknownFieldSet, if there are unknown fields. Besides, 34 // it also uses the tag to distinguish whether the owning Arena pointer is also 35 // used by sub-structure allocation. This optimization allows for 36 // "zero-overhead" storage of the Arena pointer, relative to the above baseline 37 // implementation. 38 // 39 // The tagged pointer uses the least two significant bits to disambiguate cases. 40 // It uses bit 0 == 0 to indicate an arena pointer and bit 0 == 1 to indicate a 41 // UFS+Arena-container pointer. Besides it uses bit 1 == 0 to indicate arena 42 // allocation and bit 1 == 1 to indicate heap allocation. 43 class PROTOBUF_EXPORT InternalMetadata { 44 public: InternalMetadata()45 constexpr InternalMetadata() : ptr_(0) {} InternalMetadata(Arena * arena)46 explicit InternalMetadata(Arena* arena) { 47 ptr_ = reinterpret_cast<intptr_t>(arena); 48 } 49 50 // Delete will delete the unknown fields only if they weren't allocated on an 51 // arena. Then it updates the flags so that if you call 52 // have_unknown_fields(), it will return false. 53 // 54 // It is designed to be used as part of a Message class's destructor call, so 55 // that when control eventually gets to ~InternalMetadata(), we don't need to 56 // check for have_unknown_fields() again. 57 template <typename T> Delete()58 void Delete() { 59 // Note that Delete<> should be called not more than once. 60 if (have_unknown_fields()) { 61 DeleteOutOfLineHelper<T>(); 62 } 63 } 64 arena()65 PROTOBUF_NDEBUG_INLINE Arena* arena() const { 66 if (PROTOBUF_PREDICT_FALSE(have_unknown_fields())) { 67 return PtrValue<ContainerBase>()->arena; 68 } else { 69 return PtrValue<Arena>(); 70 } 71 } 72 have_unknown_fields()73 PROTOBUF_NDEBUG_INLINE bool have_unknown_fields() const { 74 return HasUnknownFieldsTag(); 75 } 76 raw_arena_ptr()77 PROTOBUF_NDEBUG_INLINE void* raw_arena_ptr() const { 78 return reinterpret_cast<void*>(ptr_); 79 } 80 81 template <typename T> unknown_fields(const T & (* default_instance)())82 PROTOBUF_NDEBUG_INLINE const T& unknown_fields( 83 const T& (*default_instance)()) const { 84 if (PROTOBUF_PREDICT_FALSE(have_unknown_fields())) { 85 return PtrValue<Container<T>>()->unknown_fields; 86 } else { 87 return default_instance(); 88 } 89 } 90 91 template <typename T> mutable_unknown_fields()92 PROTOBUF_NDEBUG_INLINE T* mutable_unknown_fields() { 93 if (PROTOBUF_PREDICT_TRUE(have_unknown_fields())) { 94 return &PtrValue<Container<T>>()->unknown_fields; 95 } else { 96 return mutable_unknown_fields_slow<T>(); 97 } 98 } 99 100 template <typename T> Swap(InternalMetadata * other)101 PROTOBUF_NDEBUG_INLINE void Swap(InternalMetadata* other) { 102 // Semantics here are that we swap only the unknown fields, not the arena 103 // pointer. We cannot simply swap ptr_ with other->ptr_ because we need to 104 // maintain our own arena ptr. Also, our ptr_ and other's ptr_ may be in 105 // different states (direct arena pointer vs. container with UFS) so we 106 // cannot simply swap ptr_ and then restore the arena pointers. We reuse 107 // UFS's swap implementation instead. 108 if (have_unknown_fields() || other->have_unknown_fields()) { 109 DoSwap<T>(other->mutable_unknown_fields<T>()); 110 } 111 } 112 InternalSwap(InternalMetadata * PROTOBUF_RESTRICT other)113 PROTOBUF_NDEBUG_INLINE void InternalSwap( 114 InternalMetadata* PROTOBUF_RESTRICT other) { 115 std::swap(ptr_, other->ptr_); 116 } 117 118 template <typename T> MergeFrom(const InternalMetadata & other)119 PROTOBUF_NDEBUG_INLINE void MergeFrom(const InternalMetadata& other) { 120 if (other.have_unknown_fields()) { 121 DoMergeFrom<T>(other.unknown_fields<T>(nullptr)); 122 } 123 } 124 125 template <typename T> Clear()126 PROTOBUF_NDEBUG_INLINE void Clear() { 127 if (have_unknown_fields()) { 128 DoClear<T>(); 129 } 130 } 131 132 private: 133 intptr_t ptr_; 134 135 // Tagged pointer implementation. 136 static constexpr intptr_t kUnknownFieldsTagMask = 1; 137 static constexpr intptr_t kPtrTagMask = kUnknownFieldsTagMask; 138 static constexpr intptr_t kPtrValueMask = ~kPtrTagMask; 139 140 // Accessors for pointer tag and pointer value. HasUnknownFieldsTag()141 PROTOBUF_ALWAYS_INLINE bool HasUnknownFieldsTag() const { 142 return ptr_ & kUnknownFieldsTagMask; 143 } 144 145 template <typename U> PtrValue()146 U* PtrValue() const { 147 return reinterpret_cast<U*>(ptr_ & kPtrValueMask); 148 } 149 150 // If ptr_'s tag is kTagContainer, it points to an instance of this struct. 151 struct ContainerBase { 152 Arena* arena; 153 }; 154 155 template <typename T> 156 struct Container : public ContainerBase { 157 T unknown_fields; 158 }; 159 160 template <typename T> DeleteOutOfLineHelper()161 PROTOBUF_NOINLINE void DeleteOutOfLineHelper() { 162 delete PtrValue<Container<T>>(); 163 // TODO: This store is load-bearing. Since we are destructing 164 // the message at this point, see if we can eliminate it. 165 ptr_ = 0; 166 } 167 168 template <typename T> mutable_unknown_fields_slow()169 PROTOBUF_NOINLINE T* mutable_unknown_fields_slow() { 170 Arena* my_arena = arena(); 171 Container<T>* container = Arena::Create<Container<T>>(my_arena); 172 // Two-step assignment works around a bug in clang's static analyzer: 173 // https://bugs.llvm.org/show_bug.cgi?id=34198. 174 ptr_ = reinterpret_cast<intptr_t>(container); 175 ptr_ |= kUnknownFieldsTagMask; 176 container->arena = my_arena; 177 return &(container->unknown_fields); 178 } 179 180 // Templated functions. 181 182 template <typename T> DoClear()183 PROTOBUF_NOINLINE void DoClear() { 184 mutable_unknown_fields<T>()->Clear(); 185 } 186 187 template <typename T> DoMergeFrom(const T & other)188 PROTOBUF_NOINLINE void DoMergeFrom(const T& other) { 189 mutable_unknown_fields<T>()->MergeFrom(other); 190 } 191 192 template <typename T> DoSwap(T * other)193 PROTOBUF_NOINLINE void DoSwap(T* other) { 194 mutable_unknown_fields<T>()->Swap(other); 195 } 196 197 // Private helper with debug checks for ~InternalMetadata() 198 void CheckedDestruct(); 199 }; 200 201 // String Template specializations. 202 203 template <> 204 PROTOBUF_EXPORT void InternalMetadata::DoClear<std::string>(); 205 template <> 206 PROTOBUF_EXPORT void InternalMetadata::DoMergeFrom<std::string>( 207 const std::string& other); 208 template <> 209 PROTOBUF_EXPORT void InternalMetadata::DoSwap<std::string>(std::string* other); 210 211 // Instantiated once in message.cc (where the definition of UnknownFieldSet is 212 // known) to prevent much duplication across translation units of a large build. 213 extern template PROTOBUF_EXPORT void 214 InternalMetadata::DoClear<UnknownFieldSet>(); 215 extern template PROTOBUF_EXPORT void 216 InternalMetadata::DoMergeFrom<UnknownFieldSet>(const UnknownFieldSet& other); 217 extern template PROTOBUF_EXPORT void 218 InternalMetadata::DoSwap<UnknownFieldSet>(UnknownFieldSet* other); 219 extern template PROTOBUF_EXPORT void 220 InternalMetadata::DeleteOutOfLineHelper<UnknownFieldSet>(); 221 extern template PROTOBUF_EXPORT UnknownFieldSet* 222 InternalMetadata::mutable_unknown_fields_slow<UnknownFieldSet>(); 223 224 // This helper RAII class is needed to efficiently parse unknown fields. We 225 // should only call mutable_unknown_fields if there are actual unknown fields. 226 // The obvious thing to just use a stack string and swap it at the end of 227 // the parse won't work, because the destructor of StringOutputStream needs to 228 // be called before we can modify the string (it check-fails). Using 229 // LiteUnknownFieldSetter setter(&_internal_metadata_); 230 // StringOutputStream stream(setter.buffer()); 231 // guarantees that the string is only swapped after stream is destroyed. 232 class PROTOBUF_EXPORT LiteUnknownFieldSetter { 233 public: LiteUnknownFieldSetter(InternalMetadata * metadata)234 explicit LiteUnknownFieldSetter(InternalMetadata* metadata) 235 : metadata_(metadata) { 236 if (metadata->have_unknown_fields()) { 237 buffer_.swap(*metadata->mutable_unknown_fields<std::string>()); 238 } 239 } ~LiteUnknownFieldSetter()240 ~LiteUnknownFieldSetter() { 241 if (!buffer_.empty()) 242 metadata_->mutable_unknown_fields<std::string>()->swap(buffer_); 243 } buffer()244 std::string* buffer() { return &buffer_; } 245 246 private: 247 InternalMetadata* metadata_; 248 std::string buffer_; 249 }; 250 251 } // namespace internal 252 } // namespace protobuf 253 } // namespace google 254 255 #include "google/protobuf/port_undef.inc" 256 257 #endif // GOOGLE_PROTOBUF_METADATA_LITE_H__ 258