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
45namespace google {
46namespace protobuf {
47namespace 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.
59class 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    ptr_ = reinterpret_cast<void*>(
149        reinterpret_cast<intptr_t>(container) | kTagContainer);
150    container->arena_ = my_arena;
151    return &(container->unknown_fields_);
152  }
153};
154
155// Temporary compatibility typedef. Remove once this is released in components
156// and upb CL is submitted.
157typedef InternalMetadataWithArena InternalMetadata;
158
159}  // namespace internal
160}  // namespace protobuf
161
162}  // namespace google
163#endif  // GOOGLE_PROTOBUF_METADATA_H__
164