Home | History | Annotate | Download | only in protobuf
      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:
     61   InternalMetadataWithArena() : ptr_(NULL) {}
     62   explicit InternalMetadataWithArena(Arena* arena)
     63       : ptr_ (arena) {}
     64 
     65   ~InternalMetadataWithArena() {
     66     if (have_unknown_fields() && arena() == NULL) {
     67       delete PtrValue<Container>();
     68     }
     69     ptr_ = NULL;
     70   }
     71 
     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 
     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 
     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 
     96   GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool have_unknown_fields() const {
     97     return PtrTag() == kTagContainer;
     98   }
     99 
    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 
    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.
    130   GOOGLE_ATTRIBUTE_ALWAYS_INLINE int PtrTag() const {
    131     return reinterpret_cast<intptr_t>(ptr_) & kPtrTagMask;
    132   }
    133 
    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 
    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