• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "google/protobuf/arenastring.h"
9 
10 #include <cstddef>
11 
12 #include "absl/log/absl_check.h"
13 #include "absl/strings/string_view.h"
14 #include "absl/synchronization/mutex.h"
15 #include "google/protobuf/io/coded_stream.h"
16 #include "google/protobuf/message_lite.h"
17 #include "google/protobuf/parse_context.h"
18 
19 // clang-format off
20 #include "google/protobuf/port_def.inc"
21 // clang-format on
22 
23 namespace google {
24 namespace protobuf {
25 namespace internal {
26 
27 namespace  {
28 
29 // TaggedStringPtr::Flags uses the lower 2 bits as tags.
30 // Enforce that allocated data aligns to at least 4 bytes, and that
31 // the alignment of the global const string value does as well.
32 // The alignment guaranteed by `new std::string` depends on both:
33 // - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t
34 // - alignof(std::string)
35 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
36 constexpr size_t kNewAlign = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
37 #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
38 constexpr size_t kNewAlign = alignof(::max_align_t);
39 #else
40 constexpr size_t kNewAlign = alignof(std::max_align_t);
41 #endif
42 constexpr size_t kStringAlign = alignof(std::string);
43 
44 static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
45 static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
46 
47 }  // namespace
48 
Init() const49 const std::string& LazyString::Init() const {
50   static absl::Mutex mu{absl::kConstInit};
51   mu.Lock();
52   const std::string* res = inited_.load(std::memory_order_acquire);
53   if (res == nullptr) {
54     auto init_value = init_value_;
55     res = ::new (static_cast<void*>(string_buf_))
56         std::string(init_value.ptr, init_value.size);
57     inited_.store(res, std::memory_order_release);
58   }
59   mu.Unlock();
60   return *res;
61 }
62 
63 namespace {
64 
65 
66 #if defined(NDEBUG) || !defined(GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL)
67 
68 class ScopedCheckPtrInvariants {
69  public:
ScopedCheckPtrInvariants(const TaggedStringPtr *)70   explicit ScopedCheckPtrInvariants(const TaggedStringPtr*) {}
71 };
72 
73 #endif  // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
74 
75 // Creates a heap allocated std::string value.
CreateString(absl::string_view value)76 inline TaggedStringPtr CreateString(absl::string_view value) {
77   TaggedStringPtr res;
78   res.SetAllocated(new std::string(value.data(), value.length()));
79   return res;
80 }
81 
82 #ifndef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
83 
84 // Creates an arena allocated std::string value.
CreateArenaString(Arena & arena,absl::string_view s)85 TaggedStringPtr CreateArenaString(Arena& arena, absl::string_view s) {
86   TaggedStringPtr res;
87   res.SetMutableArena(Arena::Create<std::string>(&arena, s.data(), s.length()));
88   return res;
89 }
90 
91 #endif  // !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
92 
93 }  // namespace
94 
ForceCopy(Arena * arena) const95 TaggedStringPtr TaggedStringPtr::ForceCopy(Arena* arena) const {
96   return arena != nullptr ? CreateArenaString(*arena, *Get())
97                           : CreateString(*Get());
98 }
99 
Set(absl::string_view value,Arena * arena)100 void ArenaStringPtr::Set(absl::string_view value, Arena* arena) {
101   ScopedCheckPtrInvariants check(&tagged_ptr_);
102   if (IsDefault()) {
103     // If we're not on an arena, skip straight to a true string to avoid
104     // possible copy cost later.
105     tagged_ptr_ = arena != nullptr ? CreateArenaString(*arena, value)
106                                    : CreateString(value);
107   } else {
108     if (internal::DebugHardenForceCopyDefaultString()) {
109       if (arena == nullptr) {
110         auto* old = tagged_ptr_.GetIfAllocated();
111         tagged_ptr_ = CreateString(value);
112         delete old;
113       } else {
114         auto* old = UnsafeMutablePointer();
115         tagged_ptr_ = CreateArenaString(*arena, value);
116         old->assign("garbagedata");
117       }
118     } else {
119       UnsafeMutablePointer()->assign(value.data(), value.length());
120     }
121   }
122 }
123 
124 template <>
Set(const std::string & value,Arena * arena)125 void ArenaStringPtr::Set(const std::string& value, Arena* arena) {
126   ScopedCheckPtrInvariants check(&tagged_ptr_);
127   if (IsDefault()) {
128     // If we're not on an arena, skip straight to a true string to avoid
129     // possible copy cost later.
130     tagged_ptr_ = arena != nullptr ? CreateArenaString(*arena, value)
131                                    : CreateString(value);
132   } else {
133     if (internal::DebugHardenForceCopyDefaultString()) {
134       if (arena == nullptr) {
135         auto* old = tagged_ptr_.GetIfAllocated();
136         tagged_ptr_ = CreateString(value);
137         delete old;
138       } else {
139         auto* old = UnsafeMutablePointer();
140         tagged_ptr_ = CreateArenaString(*arena, value);
141         old->assign("garbagedata");
142       }
143     } else {
144       UnsafeMutablePointer()->assign(value);
145     }
146   }
147 }
148 
Set(std::string && value,Arena * arena)149 void ArenaStringPtr::Set(std::string&& value, Arena* arena) {
150   ScopedCheckPtrInvariants check(&tagged_ptr_);
151   if (IsDefault()) {
152     NewString(arena, std::move(value));
153   } else if (IsFixedSizeArena()) {
154     std::string* current = tagged_ptr_.Get();
155     auto* s = new (current) std::string(std::move(value));
156     arena->OwnDestructor(s);
157     tagged_ptr_.SetMutableArena(s);
158   } else /* !IsFixedSizeArena() */ {
159     *UnsafeMutablePointer() = std::move(value);
160   }
161 }
162 
Mutable(Arena * arena)163 std::string* ArenaStringPtr::Mutable(Arena* arena) {
164   ScopedCheckPtrInvariants check(&tagged_ptr_);
165   if (tagged_ptr_.IsMutable()) {
166     return tagged_ptr_.Get();
167   } else {
168     return MutableSlow(arena);
169   }
170 }
171 
Mutable(const LazyString & default_value,Arena * arena)172 std::string* ArenaStringPtr::Mutable(const LazyString& default_value,
173                                      Arena* arena) {
174   ScopedCheckPtrInvariants check(&tagged_ptr_);
175   if (tagged_ptr_.IsMutable()) {
176     return tagged_ptr_.Get();
177   } else {
178     return MutableSlow(arena, default_value);
179   }
180 }
181 
MutableNoCopy(Arena * arena)182 std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) {
183   ScopedCheckPtrInvariants check(&tagged_ptr_);
184   if (tagged_ptr_.IsMutable()) {
185     return tagged_ptr_.Get();
186   } else {
187     ABSL_DCHECK(IsDefault());
188     // Allocate empty. The contents are not relevant.
189     return NewString(arena);
190   }
191 }
192 
193 template <typename... Lazy>
MutableSlow(::google::protobuf::Arena * arena,const Lazy &...lazy_default)194 std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena,
195                                          const Lazy&... lazy_default) {
196   ABSL_DCHECK(IsDefault());
197 
198   // For empty defaults, this ends up calling the default constructor which is
199   // more efficient than a copy construction from
200   // GetEmptyStringAlreadyInited().
201   return NewString(arena, lazy_default.get()...);
202 }
203 
Release()204 std::string* ArenaStringPtr::Release() {
205   ScopedCheckPtrInvariants check(&tagged_ptr_);
206   if (IsDefault()) return nullptr;
207 
208   std::string* released = tagged_ptr_.Get();
209   if (tagged_ptr_.IsArena()) {
210     released = tagged_ptr_.IsMutable() ? new std::string(std::move(*released))
211                                        : new std::string(*released);
212   }
213   InitDefault();
214   return released;
215 }
216 
SetAllocated(std::string * value,Arena * arena)217 void ArenaStringPtr::SetAllocated(std::string* value, Arena* arena) {
218   ScopedCheckPtrInvariants check(&tagged_ptr_);
219   // Release what we have first.
220   Destroy();
221 
222   if (value == nullptr) {
223     InitDefault();
224   } else {
225 #ifndef NDEBUG
226     // On debug builds, copy the string so the address differs.  delete will
227     // fail if value was a stack-allocated temporary/etc., which would have
228     // failed when arena ran its cleanup list.
229     std::string* new_value = new std::string(std::move(*value));
230     delete value;
231     value = new_value;
232 #endif  // !NDEBUG
233     InitAllocated(value, arena);
234   }
235 }
236 
Destroy()237 void ArenaStringPtr::Destroy() {
238   delete tagged_ptr_.GetIfAllocated();
239 }
240 
ClearToEmpty()241 void ArenaStringPtr::ClearToEmpty() {
242   ScopedCheckPtrInvariants check(&tagged_ptr_);
243   if (IsDefault()) {
244     // Already set to default -- do nothing.
245   } else {
246     // Unconditionally mask away the tag.
247     //
248     // UpdateArenaString uses assign when capacity is larger than the new
249     // value, which is trivially true in the donated string case.
250     // const_cast<std::string*>(PtrValue<std::string>())->clear();
251     tagged_ptr_.Get()->clear();
252   }
253 }
254 
ClearToDefault(const LazyString & default_value,::google::protobuf::Arena * arena)255 void ArenaStringPtr::ClearToDefault(const LazyString& default_value,
256                                     ::google::protobuf::Arena* arena) {
257   ScopedCheckPtrInvariants check(&tagged_ptr_);
258   (void)arena;
259   if (IsDefault()) {
260     // Already set to default -- do nothing.
261   } else {
262     UnsafeMutablePointer()->assign(default_value.get());
263   }
264 }
265 
ReadArenaString(const char * ptr,ArenaStringPtr * s,Arena * arena)266 const char* EpsCopyInputStream::ReadArenaString(const char* ptr,
267                                                 ArenaStringPtr* s,
268                                                 Arena* arena) {
269   ScopedCheckPtrInvariants check(&s->tagged_ptr_);
270   ABSL_DCHECK(arena != nullptr);
271 
272   int size = ReadSize(&ptr);
273   if (!ptr) return nullptr;
274 
275   auto* str = s->NewString(arena);
276   ptr = ReadString(ptr, size, str);
277   GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
278   return ptr;
279 }
280 
281 }  // namespace internal
282 }  // namespace protobuf
283 }  // namespace google
284 
285 #include "google/protobuf/port_undef.inc"
286