arenastring.h
18.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_ARENASTRING_H__
#define GOOGLE_PROTOBUF_ARENASTRING_H__
#include <algorithm>
#include <string>
#include <type_traits>
#include <utility>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/port.h>
#include <google/protobuf/explicitly_constructed.h>
// must be last:
#include <google/protobuf/port_def.inc>
#ifdef SWIG
#error "You cannot SWIG proto headers"
#endif
namespace google {
namespace protobuf {
namespace internal {
class EpsCopyInputStream;
class SwapFieldHelper;
// Declared in message_lite.h
PROTOBUF_EXPORT extern ExplicitlyConstructedArenaString
fixed_address_empty_string;
// Lazy string instance to support string fields with non-empty default.
// These are initialized on the first call to .get().
class PROTOBUF_EXPORT LazyString {
public:
// We explicitly make LazyString an aggregate so that MSVC can do constant
// initialization on it without marking it `constexpr`.
// We do not want to use `constexpr` because it makes it harder to have extern
// storage for it and causes library bloat.
struct InitValue {
const char* ptr;
size_t size;
};
// We keep a union of the initialization value and the std::string to save on
// space. We don't need the string array after Init() is done.
union {
mutable InitValue init_value_;
alignas(std::string) mutable char string_buf_[sizeof(std::string)];
};
mutable std::atomic<const std::string*> inited_;
const std::string& get() const {
// This check generates less code than a call-once invocation.
auto* res = inited_.load(std::memory_order_acquire);
if (PROTOBUF_PREDICT_FALSE(res == nullptr)) return Init();
return *res;
}
private:
// Initialize the string in `string_buf_`, update `inited_` and return it.
// We return it here to avoid having to read it again in the inlined code.
const std::string& Init() const;
};
class TaggedStringPtr {
public:
// Bit flags qualifying string properties. We can use 2 bits as
// ptr_ is guaranteed and enforced to be aligned on 4 byte boundaries.
enum Flags {
kArenaBit = 0x1, // ptr is arena allocated
kMutableBit = 0x2, // ptr contents are fully mutable
kMask = 0x3 // Bit mask
};
// Composed logical types
enum Type {
// Default strings are immutable and never owned.
kDefault = 0,
// Allocated strings are mutable and (as the name implies) owned.
// A heap allocated string must be deleted.
kAllocated = kMutableBit,
// Mutable arena strings are strings where the string instance is owned
// by the arena, but the string contents itself are owned by the string
// instance. Mutable arena string instances need to be destroyed which is
// typically done through a cleanup action added to the arena owning it.
kMutableArena = kArenaBit | kMutableBit,
// Fixed size arena strings are strings where both the string instance and
// the string contents are fully owned by the arena. Fixed size arena
// strings are a platform and c++ library specific customization. Fixed
// size arena strings are immutable, with the exception of custom internal
// updates to the content that fit inside the existing capacity.
// Fixed size arena strings must never be deleted or destroyed.
kFixedSizeArena = kArenaBit,
};
TaggedStringPtr() = default;
explicit constexpr TaggedStringPtr(ExplicitlyConstructedArenaString* ptr)
: ptr_(ptr) {}
// Sets the value to `p`, tagging the value as being a 'default' value.
// See documentation for kDefault for more info.
inline const std::string* SetDefault(const std::string* p) {
return TagAs(kDefault, const_cast<std::string*>(p));
}
// Sets the value to `p`, tagging the value as a heap allocated value.
// Allocated strings are mutable and (as the name implies) owned.
// `p` must not be null
inline std::string* SetAllocated(std::string* p) {
return TagAs(kAllocated, p);
}
// Sets the value to `p`, tagging the value as a fixed size arena string.
// See documentation for kFixedSizeArena for more info.
// `p` must not be null
inline std::string* SetFixedSizeArena(std::string* p) {
return TagAs(kFixedSizeArena, p);
}
// Sets the value to `p`, tagging the value as a mutable arena string.
// See documentation for kMutableArena for more info.
// `p` must not be null
inline std::string* SetMutableArena(std::string* p) {
return TagAs(kMutableArena, p);
}
// Returns true if the contents of the current string are fully mutable.
inline bool IsMutable() const { return as_int() & kMutableBit; }
// Returns true if the current string is an immutable default value.
inline bool IsDefault() const { return (as_int() & kMask) == kDefault; }
// If the current string is a heap-allocated mutable value, returns a pointer
// to it. Returns nullptr otherwise.
inline std::string *GetIfAllocated() const {
auto allocated = as_int() ^ kAllocated;
if (allocated & kMask) return nullptr;
auto ptr = reinterpret_cast<std::string*>(allocated);
PROTOBUF_ASSUME(ptr != nullptr);
return ptr;
}
// Returns true if the current string is an arena allocated value.
// This means it's either a mutable or fixed size arena string.
inline bool IsArena() const { return as_int() & kArenaBit; }
// Returns true if the current string is a fixed size arena allocated value.
inline bool IsFixedSizeArena() const {
return (as_int() & kMask) == kFixedSizeArena;
}
// Returns the contained string pointer.
inline std::string* Get() const {
return reinterpret_cast<std::string*>(as_int() & ~kMask);
}
// Returns true if the contained pointer is null, indicating some error.
// The Null value is only used during parsing for temporary values.
// A persisted ArenaStringPtr value is never null.
inline bool IsNull() { return ptr_ == nullptr; }
private:
static inline void assert_aligned(const void* p) {
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(p) & kMask, 0UL);
}
inline std::string* TagAs(Type type, std::string* p) {
GOOGLE_DCHECK(p != nullptr);
assert_aligned(p);
ptr_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) | type);
return p;
}
uintptr_t as_int() const { return reinterpret_cast<uintptr_t>(ptr_); }
void* ptr_;
};
static_assert(std::is_trivial<TaggedStringPtr>::value,
"TaggedStringPtr must be trivial");
// This class encapsulates a pointer to a std::string with or without arena
// owned contents, tagged by the bottom bits of the string pointer. It is a
// high-level wrapper that almost directly corresponds to the interface required
// by string fields in generated code. It replaces the old std::string* pointer
// in such cases.
//
// The string pointer is tagged to be either a default, externally owned value,
// a mutable heap allocated value, or an arena allocated value. The object uses
// a single global instance of an empty string that is used as the initial
// default value. Fields that have empty default values directly use this global
// default. Fields that have non empty default values are supported through
// lazily initialized default values managed by the LazyString class.
//
// Generated code and reflection code both ensure that ptr_ is never null.
// Because ArenaStringPtr is used in oneof unions, its constructor is a NOP and
// the field is always manually initialized via method calls.
//
// See TaggedStringPtr for more information about the types of string values
// being held, and the mutable and ownership invariants for each type.
struct PROTOBUF_EXPORT ArenaStringPtr {
ArenaStringPtr() = default;
constexpr ArenaStringPtr(ExplicitlyConstructedArenaString* default_value,
ConstantInitialized)
: tagged_ptr_(default_value) {}
// Called from generated code / reflection runtime only. Resets value to point
// to a default string pointer, with the semantics that this ArenaStringPtr
// does not own the pointed-to memory. Disregards initial value of ptr_ (so
// this is the *ONLY* safe method to call after construction or when
// reinitializing after becoming the active field in a oneof union).
inline void InitDefault();
// Similar to `InitDefault` except that it allows the default value to be
// initialized to an externally owned string. This method is called from
// parsing code. `str` must not be null and outlive this instance.
inline void InitExternal(const std::string* str);
// Called from generated code / reflection runtime only. Resets the value of
// this instances to the heap allocated value in `str`. `str` must not be
// null. Invokes `arena->Own(str)` to transfer ownership into the arena if
// `arena` is not null, else, `str` will be owned by ArenaStringPtr. This
// function should only be used to initialize a ArenaStringPtr or on an
// instance known to not carry any heap allocated value.
inline void InitAllocated(std::string* str, Arena* arena);
void Set(ConstStringParam value, Arena* arena);
void Set(std::string&& value, Arena* arena);
void Set(const char* s, Arena* arena);
void Set(const char* s, size_t n, Arena* arena);
void SetBytes(ConstStringParam value, Arena* arena);
void SetBytes(std::string&& value, Arena* arena);
void SetBytes(const char* s, Arena* arena);
void SetBytes(const void* p, size_t n, Arena* arena);
template <typename RefWrappedType>
void Set(std::reference_wrapper<RefWrappedType> const_string_ref,
::google::protobuf::Arena* arena) {
Set(const_string_ref.get(), arena);
}
// Returns a mutable std::string reference.
// The version accepting a `LazyString` value is used in the generated code to
// initialize mutable copies for fields with a non-empty default where the
// default value is lazily initialized.
std::string* Mutable(Arena* arena);
std::string* Mutable(const LazyString& default_value, Arena* arena);
// Gets a mutable pointer with unspecified contents.
// This function is identical to Mutable(), except it is optimized for the
// case where the caller is not interested in the current contents. For
// example, if the current field is not mutable, it will re-initialize the
// value with an empty string rather than a (non-empty) default value.
// Likewise, if the current value is a fixed size arena string with contents,
// it will be initialized into an empty mutable arena string.
std::string* MutableNoCopy(Arena* arena);
// Basic accessors.
PROTOBUF_NDEBUG_INLINE const std::string& Get() const {
// Unconditionally mask away the tag.
return *tagged_ptr_.Get();
}
// Returns a pointer to the stored contents for this instance.
// This method is for internal debugging and tracking purposes only.
PROTOBUF_NDEBUG_INLINE const std::string* UnsafeGetPointer() const
PROTOBUF_RETURNS_NONNULL {
return tagged_ptr_.Get();
}
// Release returns a std::string* instance that is heap-allocated and is not
// Own()'d by any arena. If the field is not set, this returns nullptr. The
// caller retains ownership. Clears this field back to the default state.
// Used to implement release_<field>() methods on generated classes.
PROTOBUF_NODISCARD std::string* Release();
// Takes a std::string that is heap-allocated, and takes ownership. The
// std::string's destructor is registered with the arena. Used to implement
// set_allocated_<field> in generated classes.
void SetAllocated(std::string* value, Arena* arena);
// Frees storage (if not on an arena).
void Destroy();
// Clears content, but keeps allocated std::string, to avoid the overhead of
// heap operations. After this returns, the content (as seen by the user) will
// always be the empty std::string. Assumes that |default_value| is an empty
// std::string.
void ClearToEmpty();
// Clears content, assuming that the current value is not the empty
// string default.
void ClearNonDefaultToEmpty();
// Clears content, but keeps allocated std::string if arena != nullptr, to
// avoid the overhead of heap operations. After this returns, the content
// (as seen by the user) will always be equal to |default_value|.
void ClearToDefault(const LazyString& default_value, ::google::protobuf::Arena* arena);
// Swaps internal pointers. Arena-safety semantics: this is guarded by the
// logic in Swap()/UnsafeArenaSwap() at the message level, so this method is
// 'unsafe' if called directly.
inline PROTOBUF_NDEBUG_INLINE static void InternalSwap(ArenaStringPtr* rhs,
Arena* rhs_arena,
ArenaStringPtr* lhs,
Arena* lhs_arena);
// Internal setter used only at parse time to directly set a donated string
// value.
void UnsafeSetTaggedPointer(TaggedStringPtr value) { tagged_ptr_ = value; }
// Generated code only! An optimization, in certain cases the generated
// code is certain we can obtain a std::string with no default checks and
// tag tests.
std::string* UnsafeMutablePointer() PROTOBUF_RETURNS_NONNULL;
// Returns true if this instances holds an immutable default value.
inline bool IsDefault() const { return tagged_ptr_.IsDefault(); }
private:
template <typename... Args>
inline std::string* NewString(Arena* arena, Args&&... args) {
if (arena == nullptr) {
auto* s = new std::string(std::forward<Args>(args)...);
return tagged_ptr_.SetAllocated(s);
} else {
auto* s = Arena::Create<std::string>(arena, std::forward<Args>(args)...);
return tagged_ptr_.SetMutableArena(s);
}
}
TaggedStringPtr tagged_ptr_;
bool IsFixedSizeArena() const { return false; }
// Swaps tagged pointer without debug hardening. This is to allow python
// protobuf to maintain pointer stability even in DEBUG builds.
inline PROTOBUF_NDEBUG_INLINE static void UnsafeShallowSwap(
ArenaStringPtr* rhs, ArenaStringPtr* lhs) {
std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
}
friend class ::google::protobuf::internal::SwapFieldHelper;
friend class TcParser;
// Slow paths.
// MutableSlow requires that !IsString() || IsDefault
// Variadic to support 0 args for empty default and 1 arg for LazyString.
template <typename... Lazy>
std::string* MutableSlow(::google::protobuf::Arena* arena, const Lazy&... lazy_default);
friend class EpsCopyInputStream;
};
inline void ArenaStringPtr::InitDefault() {
tagged_ptr_ = TaggedStringPtr(&fixed_address_empty_string);
}
inline void ArenaStringPtr::InitExternal(const std::string* str) {
tagged_ptr_.SetDefault(str);
}
inline void ArenaStringPtr::InitAllocated(std::string* str, Arena* arena) {
if (arena != nullptr) {
tagged_ptr_.SetMutableArena(str);
arena->Own(str);
} else {
tagged_ptr_.SetAllocated(str);
}
}
inline void ArenaStringPtr::Set(const char* s, Arena* arena) {
Set(ConstStringParam{s}, arena);
}
inline void ArenaStringPtr::Set(const char* s, size_t n, Arena* arena) {
Set(ConstStringParam{s, n}, arena);
}
inline void ArenaStringPtr::SetBytes(ConstStringParam value, Arena* arena) {
Set(value, arena);
}
inline void ArenaStringPtr::SetBytes(std::string&& value, Arena* arena) {
Set(std::move(value), arena);
}
inline void ArenaStringPtr::SetBytes(const char* s, Arena* arena) {
Set(s, arena);
}
inline void ArenaStringPtr::SetBytes(const void* p, size_t n, Arena* arena) {
Set(ConstStringParam{static_cast<const char*>(p), n}, arena);
}
// Make sure rhs_arena allocated rhs, and lhs_arena allocated lhs.
inline PROTOBUF_NDEBUG_INLINE void ArenaStringPtr::InternalSwap( //
ArenaStringPtr* rhs, Arena* rhs_arena, //
ArenaStringPtr* lhs, Arena* lhs_arena) {
// Silence unused variable warnings in release buildls.
(void)rhs_arena;
(void)lhs_arena;
std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
auto force_realloc = [](ArenaStringPtr* p, Arena* arena) {
if (p->IsDefault()) return;
std::string* old_value = p->tagged_ptr_.Get();
std::string* new_value =
p->IsFixedSizeArena()
? Arena::Create<std::string>(arena, *old_value)
: Arena::Create<std::string>(arena, std::move(*old_value));
if (arena == nullptr) {
delete old_value;
p->tagged_ptr_.SetAllocated(new_value);
} else {
p->tagged_ptr_.SetMutableArena(new_value);
}
};
// Because, at this point, tagged_ptr_ has been swapped, arena should also be
// swapped.
force_realloc(lhs, rhs_arena);
force_realloc(rhs, lhs_arena);
#endif // PROTOBUF_FORCE_COPY_IN_SWAP
}
inline void ArenaStringPtr::ClearNonDefaultToEmpty() {
// Unconditionally mask away the tag.
tagged_ptr_.Get()->clear();
}
inline std::string* ArenaStringPtr::UnsafeMutablePointer() {
GOOGLE_DCHECK(tagged_ptr_.IsMutable());
GOOGLE_DCHECK(tagged_ptr_.Get() != nullptr);
return tagged_ptr_.Get();
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_ARENASTRING_H__