• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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