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