1 // Copyright 2021 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
16 #define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
17
18 #include <atomic>
19 #include <cassert>
20 #include <cstddef>
21 #include <cstdint>
22 #include <type_traits>
23
24 #include "absl/base/config.h"
25 #include "absl/base/internal/endian.h"
26 #include "absl/base/internal/invoke.h"
27 #include "absl/base/optimization.h"
28 #include "absl/container/internal/compressed_tuple.h"
29 #include "absl/meta/type_traits.h"
30 #include "absl/strings/string_view.h"
31
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace cord_internal {
35
36 class CordzInfo;
37
38 // Default feature enable states for cord ring buffers
39 enum CordFeatureDefaults {
40 kCordEnableBtreeDefault = false,
41 kCordEnableRingBufferDefault = false,
42 kCordShallowSubcordsDefault = false
43 };
44
45 extern std::atomic<bool> cord_btree_enabled;
46 extern std::atomic<bool> cord_ring_buffer_enabled;
47 extern std::atomic<bool> shallow_subcords_enabled;
48
enable_cord_btree(bool enable)49 inline void enable_cord_btree(bool enable) {
50 cord_btree_enabled.store(enable, std::memory_order_relaxed);
51 }
52
enable_cord_ring_buffer(bool enable)53 inline void enable_cord_ring_buffer(bool enable) {
54 cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
55 }
56
enable_shallow_subcords(bool enable)57 inline void enable_shallow_subcords(bool enable) {
58 shallow_subcords_enabled.store(enable, std::memory_order_relaxed);
59 }
60
61 enum Constants {
62 // The inlined size to use with absl::InlinedVector.
63 //
64 // Note: The InlinedVectors in this file (and in cord.h) do not need to use
65 // the same value for their inlined size. The fact that they do is historical.
66 // It may be desirable for each to use a different inlined size optimized for
67 // that InlinedVector's usage.
68 //
69 // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
70 // the inlined vector size (47 exists for backward compatibility).
71 kInlinedVectorSize = 47,
72
73 // Prefer copying blocks of at most this size, otherwise reference count.
74 kMaxBytesToCopy = 511
75 };
76
77 // Wraps std::atomic for reference counting.
78 class Refcount {
79 public:
Refcount()80 constexpr Refcount() : count_{kRefIncrement} {}
81 struct Immortal {};
Refcount(Immortal)82 explicit constexpr Refcount(Immortal) : count_(kImmortalTag) {}
83
84 // Increments the reference count. Imposes no memory ordering.
Increment()85 inline void Increment() {
86 count_.fetch_add(kRefIncrement, std::memory_order_relaxed);
87 }
88
89 // Asserts that the current refcount is greater than 0. If the refcount is
90 // greater than 1, decrements the reference count.
91 //
92 // Returns false if there are no references outstanding; true otherwise.
93 // Inserts barriers to ensure that state written before this method returns
94 // false will be visible to a thread that just observed this method returning
95 // false.
Decrement()96 inline bool Decrement() {
97 int32_t refcount = count_.load(std::memory_order_acquire);
98 assert(refcount > 0 || refcount & kImmortalTag);
99 return refcount != kRefIncrement &&
100 count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
101 kRefIncrement;
102 }
103
104 // Same as Decrement but expect that refcount is greater than 1.
DecrementExpectHighRefcount()105 inline bool DecrementExpectHighRefcount() {
106 int32_t refcount =
107 count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
108 assert(refcount > 0 || refcount & kImmortalTag);
109 return refcount != kRefIncrement;
110 }
111
112 // Returns the current reference count using acquire semantics.
Get()113 inline int32_t Get() const {
114 return count_.load(std::memory_order_acquire) >> kImmortalShift;
115 }
116
117 // Returns whether the atomic integer is 1.
118 // If the reference count is used in the conventional way, a
119 // reference count of 1 implies that the current thread owns the
120 // reference and no other thread shares it.
121 // This call performs the test for a reference count of one, and
122 // performs the memory barrier needed for the owning thread
123 // to act on the object, knowing that it has exclusive access to the
124 // object.
IsOne()125 inline bool IsOne() {
126 return count_.load(std::memory_order_acquire) == kRefIncrement;
127 }
128
IsImmortal()129 bool IsImmortal() const {
130 return (count_.load(std::memory_order_relaxed) & kImmortalTag) != 0;
131 }
132
133 private:
134 // We reserve the bottom bit to tag a reference count as immortal.
135 // By making it `1` we ensure that we never reach `0` when adding/subtracting
136 // `2`, thus it never looks as if it should be destroyed.
137 // These are used for the StringConstant constructor where we do not increase
138 // the refcount at construction time (due to constinit requirements) but we
139 // will still decrease it at destruction time to avoid branching on Unref.
140 enum {
141 kImmortalShift = 1,
142 kRefIncrement = 1 << kImmortalShift,
143 kImmortalTag = kRefIncrement - 1
144 };
145
146 std::atomic<int32_t> count_;
147 };
148
149 // The overhead of a vtable is too much for Cord, so we roll our own subclasses
150 // using only a single byte to differentiate classes from each other - the "tag"
151 // byte. Define the subclasses first so we can provide downcasting helper
152 // functions in the base class.
153
154 struct CordRepConcat;
155 struct CordRepExternal;
156 struct CordRepFlat;
157 struct CordRepSubstring;
158 class CordRepRing;
159 class CordRepBtree;
160
161 // Various representations that we allow
162 enum CordRepKind {
163 CONCAT = 0,
164 SUBSTRING = 1,
165 BTREE = 2,
166 RING = 3,
167 EXTERNAL = 4,
168
169 // We have different tags for different sized flat arrays,
170 // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on
171 // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed
172 // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well
173 // as the Tag <---> Size logic so that FLAT stil represents the minimum flat
174 // allocation size. (32 bytes as of now).
175 FLAT = 5,
176 MAX_FLAT_TAG = 225
177 };
178
179 // There are various locations where we want to check if some rep is a 'plain'
180 // data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we
181 // can perform this check in a single branch as 'tag >= EXTERNAL'
182 // Likewise, we have some locations where we check for 'ring or external/flat',
183 // so likewise align RING to EXTERNAL.
184 // Note that we can leave this optimization to the compiler. The compiler will
185 // DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`.
186 static_assert(RING == BTREE + 1, "BTREE and RING not consecutive");
187 static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive");
188 static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive");
189
190 struct CordRep {
191 CordRep() = default;
CordRepCordRep192 constexpr CordRep(Refcount::Immortal immortal, size_t l)
193 : length(l), refcount(immortal), tag(EXTERNAL), storage{} {}
194
195 // The following three fields have to be less than 32 bytes since
196 // that is the smallest supported flat node size.
197 size_t length;
198 Refcount refcount;
199 // If tag < FLAT, it represents CordRepKind and indicates the type of node.
200 // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
201 uint8_t tag;
202
203 // `storage` provides two main purposes:
204 // - the starting point for FlatCordRep.Data() [flexible-array-member]
205 // - 3 bytes of additional storage for use by derived classes.
206 // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores
207 // a 'depth' value in storage[0], and the (future) CordRepBtree class stores
208 // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to
209 // allocate room for these in the derived class, as not all compilers reuse
210 // padding space from the base class (clang and gcc do, MSVC does not, etc)
211 uint8_t storage[3];
212
213 inline CordRepRing* ring();
214 inline const CordRepRing* ring() const;
215 inline CordRepConcat* concat();
216 inline const CordRepConcat* concat() const;
217 inline CordRepSubstring* substring();
218 inline const CordRepSubstring* substring() const;
219 inline CordRepExternal* external();
220 inline const CordRepExternal* external() const;
221 inline CordRepFlat* flat();
222 inline const CordRepFlat* flat() const;
223
224 inline CordRepBtree* btree();
225 inline const CordRepBtree* btree() const;
226
227 // --------------------------------------------------------------------
228 // Memory management
229
230 // Destroys the provided `rep`.
231 static void Destroy(CordRep* rep);
232
233 // Increments the reference count of `rep`.
234 // Requires `rep` to be a non-null pointer value.
235 static inline CordRep* Ref(CordRep* rep);
236
237 // Decrements the reference count of `rep`. Destroys rep if count reaches
238 // zero. Requires `rep` to be a non-null pointer value.
239 static inline void Unref(CordRep* rep);
240 };
241
242 struct CordRepConcat : public CordRep {
243 CordRep* left;
244 CordRep* right;
245
depthCordRepConcat246 uint8_t depth() const { return storage[0]; }
set_depthCordRepConcat247 void set_depth(uint8_t depth) { storage[0] = depth; }
248 };
249
250 struct CordRepSubstring : public CordRep {
251 size_t start; // Starting offset of substring in child
252 CordRep* child;
253 };
254
255 // Type for function pointer that will invoke the releaser function and also
256 // delete the `CordRepExternalImpl` corresponding to the passed in
257 // `CordRepExternal`.
258 using ExternalReleaserInvoker = void (*)(CordRepExternal*);
259
260 // External CordReps are allocated together with a type erased releaser. The
261 // releaser is stored in the memory directly following the CordRepExternal.
262 struct CordRepExternal : public CordRep {
263 CordRepExternal() = default;
CordRepExternalCordRepExternal264 explicit constexpr CordRepExternal(absl::string_view str)
265 : CordRep(Refcount::Immortal{}, str.size()),
266 base(str.data()),
267 releaser_invoker(nullptr) {}
268
269 const char* base;
270 // Pointer to function that knows how to call and destroy the releaser.
271 ExternalReleaserInvoker releaser_invoker;
272
273 // Deletes (releases) the external rep.
274 // Requires rep != nullptr and rep->tag == EXTERNAL
275 static void Delete(CordRep* rep);
276 };
277
278 struct Rank1 {};
279 struct Rank0 : Rank1 {};
280
281 template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
282 Releaser, absl::string_view>>
InvokeReleaser(Rank0,Releaser && releaser,absl::string_view data)283 void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
284 ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
285 }
286
287 template <typename Releaser,
288 typename = ::absl::base_internal::invoke_result_t<Releaser>>
InvokeReleaser(Rank1,Releaser && releaser,absl::string_view)289 void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
290 ::absl::base_internal::invoke(std::forward<Releaser>(releaser));
291 }
292
293 // We use CompressedTuple so that we can benefit from EBCO.
294 template <typename Releaser>
295 struct CordRepExternalImpl
296 : public CordRepExternal,
297 public ::absl::container_internal::CompressedTuple<Releaser> {
298 // The extra int arg is so that we can avoid interfering with copy/move
299 // constructors while still benefitting from perfect forwarding.
300 template <typename T>
CordRepExternalImplCordRepExternalImpl301 CordRepExternalImpl(T&& releaser, int)
302 : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) {
303 this->releaser_invoker = &Release;
304 }
305
~CordRepExternalImplCordRepExternalImpl306 ~CordRepExternalImpl() {
307 InvokeReleaser(Rank0{}, std::move(this->template get<0>()),
308 absl::string_view(base, length));
309 }
310
ReleaseCordRepExternalImpl311 static void Release(CordRepExternal* rep) {
312 delete static_cast<CordRepExternalImpl*>(rep);
313 }
314 };
315
Delete(CordRep * rep)316 inline void CordRepExternal::Delete(CordRep* rep) {
317 assert(rep != nullptr && rep->tag == EXTERNAL);
318 auto* rep_external = static_cast<CordRepExternal*>(rep);
319 assert(rep_external->releaser_invoker != nullptr);
320 rep_external->releaser_invoker(rep_external);
321 }
322
323 template <typename Str>
324 struct ConstInitExternalStorage {
325 ABSL_CONST_INIT static CordRepExternal value;
326 };
327
328 template <typename Str>
329 CordRepExternal ConstInitExternalStorage<Str>::value(Str::value);
330
331 enum {
332 kMaxInline = 15,
333 };
334
GetOrNull(absl::string_view data,size_t pos)335 constexpr char GetOrNull(absl::string_view data, size_t pos) {
336 return pos < data.size() ? data[pos] : '\0';
337 }
338
339 // We store cordz_info as 64 bit pointer value in big endian format. This
340 // guarantees that the least significant byte of cordz_info matches the last
341 // byte of the inline data representation in as_chars_, which holds the inlined
342 // size or the 'is_tree' bit.
343 using cordz_info_t = int64_t;
344
345 // Assert that the `cordz_info` pointer value perfectly overlaps the last half
346 // of `as_chars_` and can hold a pointer value.
347 static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
348 static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
349
350 // BigEndianByte() creates a big endian representation of 'value', i.e.: a big
351 // endian value where the last byte in the host's representation holds 'value`,
352 // with all other bytes being 0.
BigEndianByte(unsigned char value)353 static constexpr cordz_info_t BigEndianByte(unsigned char value) {
354 #if defined(ABSL_IS_BIG_ENDIAN)
355 return value;
356 #else
357 return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
358 #endif
359 }
360
361 class InlineData {
362 public:
363 // DefaultInitType forces the use of the default initialization constructor.
364 enum DefaultInitType { kDefaultInit };
365
366 // kNullCordzInfo holds the big endian representation of intptr_t(1)
367 // This is the 'null' / initial value of 'cordz_info'. The null value
368 // is specifically big endian 1 as with 64-bit pointers, the last
369 // byte of cordz_info overlaps with the last byte holding the tag.
370 static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
371
InlineData()372 constexpr InlineData() : as_chars_{0} {}
InlineData(DefaultInitType)373 explicit InlineData(DefaultInitType) {}
InlineData(CordRep * rep)374 explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
InlineData(absl::string_view chars)375 explicit constexpr InlineData(absl::string_view chars)
376 : as_chars_{
377 GetOrNull(chars, 0), GetOrNull(chars, 1),
378 GetOrNull(chars, 2), GetOrNull(chars, 3),
379 GetOrNull(chars, 4), GetOrNull(chars, 5),
380 GetOrNull(chars, 6), GetOrNull(chars, 7),
381 GetOrNull(chars, 8), GetOrNull(chars, 9),
382 GetOrNull(chars, 10), GetOrNull(chars, 11),
383 GetOrNull(chars, 12), GetOrNull(chars, 13),
384 GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
385
386 // Returns true if the current instance is empty.
387 // The 'empty value' is an inlined data value of zero length.
is_empty()388 bool is_empty() const { return tag() == 0; }
389
390 // Returns true if the current instance holds a tree value.
is_tree()391 bool is_tree() const { return (tag() & 1) != 0; }
392
393 // Returns true if the current instance holds a cordz_info value.
394 // Requires the current instance to hold a tree value.
is_profiled()395 bool is_profiled() const {
396 assert(is_tree());
397 return as_tree_.cordz_info != kNullCordzInfo;
398 }
399
400 // Returns true if either of the provided instances hold a cordz_info value.
401 // This method is more efficient than the equivalent `data1.is_profiled() ||
402 // data2.is_profiled()`. Requires both arguments to hold a tree.
is_either_profiled(const InlineData & data1,const InlineData & data2)403 static bool is_either_profiled(const InlineData& data1,
404 const InlineData& data2) {
405 assert(data1.is_tree() && data2.is_tree());
406 return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) !=
407 kNullCordzInfo;
408 }
409
410 // Returns the cordz_info sampling instance for this instance, or nullptr
411 // if the current instance is not sampled and does not have CordzInfo data.
412 // Requires the current instance to hold a tree value.
cordz_info()413 CordzInfo* cordz_info() const {
414 assert(is_tree());
415 intptr_t info =
416 static_cast<intptr_t>(absl::big_endian::ToHost64(as_tree_.cordz_info));
417 assert(info & 1);
418 return reinterpret_cast<CordzInfo*>(info - 1);
419 }
420
421 // Sets the current cordz_info sampling instance for this instance, or nullptr
422 // if the current instance is not sampled and does not have CordzInfo data.
423 // Requires the current instance to hold a tree value.
set_cordz_info(CordzInfo * cordz_info)424 void set_cordz_info(CordzInfo* cordz_info) {
425 assert(is_tree());
426 intptr_t info = reinterpret_cast<intptr_t>(cordz_info) | 1;
427 as_tree_.cordz_info = absl::big_endian::FromHost64(info);
428 }
429
430 // Resets the current cordz_info to null / empty.
clear_cordz_info()431 void clear_cordz_info() {
432 assert(is_tree());
433 as_tree_.cordz_info = kNullCordzInfo;
434 }
435
436 // Returns a read only pointer to the character data inside this instance.
437 // Requires the current instance to hold inline data.
as_chars()438 const char* as_chars() const {
439 assert(!is_tree());
440 return as_chars_;
441 }
442
443 // Returns a mutable pointer to the character data inside this instance.
444 // Should be used for 'write only' operations setting an inlined value.
445 // Applications can set the value of inlined data either before or after
446 // setting the inlined size, i.e., both of the below are valid:
447 //
448 // // Set inlined data and inline size
449 // memcpy(data_.as_chars(), data, size);
450 // data_.set_inline_size(size);
451 //
452 // // Set inlined size and inline data
453 // data_.set_inline_size(size);
454 // memcpy(data_.as_chars(), data, size);
455 //
456 // It's an error to read from the returned pointer without a preceding write
457 // if the current instance does not hold inline data, i.e.: is_tree() == true.
as_chars()458 char* as_chars() { return as_chars_; }
459
460 // Returns the tree value of this value.
461 // Requires the current instance to hold a tree value.
as_tree()462 CordRep* as_tree() const {
463 assert(is_tree());
464 return as_tree_.rep;
465 }
466
467 // Initialize this instance to holding the tree value `rep`,
468 // initializing the cordz_info to null, i.e.: 'not profiled'.
make_tree(CordRep * rep)469 void make_tree(CordRep* rep) {
470 as_tree_.rep = rep;
471 as_tree_.cordz_info = kNullCordzInfo;
472 }
473
474 // Set the tree value of this instance to 'rep`.
475 // Requires the current instance to already hold a tree value.
476 // Does not affect the value of cordz_info.
set_tree(CordRep * rep)477 void set_tree(CordRep* rep) {
478 assert(is_tree());
479 as_tree_.rep = rep;
480 }
481
482 // Returns the size of the inlined character data inside this instance.
483 // Requires the current instance to hold inline data.
inline_size()484 size_t inline_size() const {
485 assert(!is_tree());
486 return tag() >> 1;
487 }
488
489 // Sets the size of the inlined character data inside this instance.
490 // Requires `size` to be <= kMaxInline.
491 // See the documentation on 'as_chars()' for more information and examples.
set_inline_size(size_t size)492 void set_inline_size(size_t size) {
493 ABSL_ASSERT(size <= kMaxInline);
494 tag() = static_cast<char>(size << 1);
495 }
496
497 private:
498 // See cordz_info_t for forced alignment and size of `cordz_info` details.
499 struct AsTree {
AsTreeAsTree500 explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
501 : rep(tree), cordz_info(kNullCordzInfo) {}
502 // This union uses up extra space so that whether rep is 32 or 64 bits,
503 // cordz_info will still start at the eighth byte, and the last
504 // byte of cordz_info will still be the last byte of InlineData.
505 union {
506 absl::cord_internal::CordRep* rep;
507 cordz_info_t unused_aligner;
508 };
509 cordz_info_t cordz_info;
510 };
511
tag()512 char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
tag()513 char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
514
515 // If the data has length <= kMaxInline, we store it in `as_chars_`, and
516 // store the size in the last char of `as_chars_` shifted left + 1.
517 // Else we store it in a tree and store a pointer to that tree in
518 // `as_tree_.rep` and store a tag in `tagged_size`.
519 union {
520 char as_chars_[kMaxInline + 1];
521 AsTree as_tree_;
522 };
523 };
524
525 static_assert(sizeof(InlineData) == kMaxInline + 1, "");
526
concat()527 inline CordRepConcat* CordRep::concat() {
528 assert(tag == CONCAT);
529 return static_cast<CordRepConcat*>(this);
530 }
531
concat()532 inline const CordRepConcat* CordRep::concat() const {
533 assert(tag == CONCAT);
534 return static_cast<const CordRepConcat*>(this);
535 }
536
substring()537 inline CordRepSubstring* CordRep::substring() {
538 assert(tag == SUBSTRING);
539 return static_cast<CordRepSubstring*>(this);
540 }
541
substring()542 inline const CordRepSubstring* CordRep::substring() const {
543 assert(tag == SUBSTRING);
544 return static_cast<const CordRepSubstring*>(this);
545 }
546
external()547 inline CordRepExternal* CordRep::external() {
548 assert(tag == EXTERNAL);
549 return static_cast<CordRepExternal*>(this);
550 }
551
external()552 inline const CordRepExternal* CordRep::external() const {
553 assert(tag == EXTERNAL);
554 return static_cast<const CordRepExternal*>(this);
555 }
556
Ref(CordRep * rep)557 inline CordRep* CordRep::Ref(CordRep* rep) {
558 assert(rep != nullptr);
559 rep->refcount.Increment();
560 return rep;
561 }
562
Unref(CordRep * rep)563 inline void CordRep::Unref(CordRep* rep) {
564 assert(rep != nullptr);
565 // Expect refcount to be 0. Avoiding the cost of an atomic decrement should
566 // typically outweigh the cost of an extra branch checking for ref == 1.
567 if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) {
568 Destroy(rep);
569 }
570 }
571
572 } // namespace cord_internal
573
574 ABSL_NAMESPACE_END
575 } // namespace absl
576 #endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
577