• 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/attributes.h"
25 #include "absl/base/config.h"
26 #include "absl/base/internal/endian.h"
27 #include "absl/base/internal/invoke.h"
28 #include "absl/base/optimization.h"
29 #include "absl/container/internal/compressed_tuple.h"
30 #include "absl/meta/type_traits.h"
31 #include "absl/strings/string_view.h"
32 
33 namespace absl {
34 ABSL_NAMESPACE_BEGIN
35 namespace cord_internal {
36 
37 // The overhead of a vtable is too much for Cord, so we roll our own subclasses
38 // using only a single byte to differentiate classes from each other - the "tag"
39 // byte.  Define the subclasses first so we can provide downcasting helper
40 // functions in the base class.
41 struct CordRep;
42 struct CordRepConcat;
43 struct CordRepExternal;
44 struct CordRepFlat;
45 struct CordRepSubstring;
46 struct CordRepCrc;
47 class CordRepRing;
48 class CordRepBtree;
49 
50 class CordzInfo;
51 
52 // Default feature enable states for cord ring buffers
53 enum CordFeatureDefaults {
54   kCordEnableRingBufferDefault = false,
55   kCordShallowSubcordsDefault = false
56 };
57 
58 extern std::atomic<bool> cord_ring_buffer_enabled;
59 extern std::atomic<bool> shallow_subcords_enabled;
60 
61 // `cord_btree_exhaustive_validation` can be set to force exhaustive validation
62 // in debug assertions, and code that calls `IsValid()` explicitly. By default,
63 // assertions should be relatively cheap and AssertValid() can easily lead to
64 // O(n^2) complexity as recursive / full tree validation is O(n).
65 extern std::atomic<bool> cord_btree_exhaustive_validation;
66 
enable_cord_ring_buffer(bool enable)67 inline void enable_cord_ring_buffer(bool enable) {
68   cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed);
69 }
70 
enable_shallow_subcords(bool enable)71 inline void enable_shallow_subcords(bool enable) {
72   shallow_subcords_enabled.store(enable, std::memory_order_relaxed);
73 }
74 
75 enum Constants {
76   // The inlined size to use with absl::InlinedVector.
77   //
78   // Note: The InlinedVectors in this file (and in cord.h) do not need to use
79   // the same value for their inlined size. The fact that they do is historical.
80   // It may be desirable for each to use a different inlined size optimized for
81   // that InlinedVector's usage.
82   //
83   // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
84   // the inlined vector size (47 exists for backward compatibility).
85   kInlinedVectorSize = 47,
86 
87   // Prefer copying blocks of at most this size, otherwise reference count.
88   kMaxBytesToCopy = 511
89 };
90 
91 // Emits a fatal error "Unexpected node type: xyz" and aborts the program.
92 ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep);
93 
94 // Compact class for tracking the reference count and state flags for CordRep
95 // instances.  Data is stored in an atomic int32_t for compactness and speed.
96 class RefcountAndFlags {
97  public:
RefcountAndFlags()98   constexpr RefcountAndFlags() : count_{kRefIncrement} {}
99   struct Immortal {};
RefcountAndFlags(Immortal)100   explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {}
101 
102   // Increments the reference count. Imposes no memory ordering.
Increment()103   inline void Increment() {
104     count_.fetch_add(kRefIncrement, std::memory_order_relaxed);
105   }
106 
107   // Asserts that the current refcount is greater than 0. If the refcount is
108   // greater than 1, decrements the reference count.
109   //
110   // Returns false if there are no references outstanding; true otherwise.
111   // Inserts barriers to ensure that state written before this method returns
112   // false will be visible to a thread that just observed this method returning
113   // false.  Always returns false when the immortal bit is set.
Decrement()114   inline bool Decrement() {
115     int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask;
116     assert(refcount > 0 || refcount & kImmortalFlag);
117     return refcount != kRefIncrement &&
118            (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
119             kRefcountMask) != kRefIncrement;
120   }
121 
122   // Same as Decrement but expect that refcount is greater than 1.
DecrementExpectHighRefcount()123   inline bool DecrementExpectHighRefcount() {
124     int32_t refcount =
125         count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
126         kRefcountMask;
127     assert(refcount > 0 || refcount & kImmortalFlag);
128     return refcount != kRefIncrement;
129   }
130 
131   // Returns the current reference count using acquire semantics.
Get()132   inline int32_t Get() const {
133     return count_.load(std::memory_order_acquire) >> kNumFlags;
134   }
135 
136   // Returns whether the atomic integer is 1.
137   // If the reference count is used in the conventional way, a
138   // reference count of 1 implies that the current thread owns the
139   // reference and no other thread shares it.
140   // This call performs the test for a reference count of one, and
141   // performs the memory barrier needed for the owning thread
142   // to act on the object, knowing that it has exclusive access to the
143   // object.  Always returns false when the immortal bit is set.
IsOne()144   inline bool IsOne() {
145     return (count_.load(std::memory_order_acquire) & kRefcountMask) ==
146            kRefIncrement;
147   }
148 
IsImmortal()149   bool IsImmortal() const {
150     return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0;
151   }
152 
153  private:
154   // We reserve the bottom bits for flags.
155   // kImmortalBit indicates that this entity should never be collected; it is
156   // used for the StringConstant constructor to avoid collecting immutable
157   // constant cords.
158   // kReservedFlag is reserved for future use.
159   enum Flags {
160     kNumFlags = 2,
161 
162     kImmortalFlag = 0x1,
163     kReservedFlag = 0x2,
164     kRefIncrement = (1 << kNumFlags),
165 
166     // Bitmask to use when checking refcount by equality.  This masks out
167     // all flags except kImmortalFlag, which is part of the refcount for
168     // purposes of equality.  (A refcount of 0 or 1 does not count as 0 or 1
169     // if the immortal bit is set.)
170     kRefcountMask = ~kReservedFlag,
171   };
172 
173   std::atomic<int32_t> count_;
174 };
175 
176 // Various representations that we allow
177 enum CordRepKind {
178   UNUSED_0 = 0,
179   SUBSTRING = 1,
180   CRC = 2,
181   BTREE = 3,
182   RING = 4,
183   EXTERNAL = 5,
184 
185   // We have different tags for different sized flat arrays,
186   // starting with FLAT, and limited to MAX_FLAT_TAG. The below values map to an
187   // allocated range of 32 bytes to 256 KB. The current granularity is:
188   // - 8 byte granularity for flat sizes in [32 - 512]
189   // - 64 byte granularity for flat sizes in (512 - 8KiB]
190   // - 4KiB byte granularity for flat sizes in (8KiB, 256 KiB]
191   // If a new tag is needed in the future, then 'FLAT' and 'MAX_FLAT_TAG' should
192   // be adjusted as well as the Tag <---> Size mapping logic so that FLAT still
193   // represents the minimum flat allocation size. (32 bytes as of now).
194   FLAT = 6,
195   MAX_FLAT_TAG = 248
196 };
197 
198 // There are various locations where we want to check if some rep is a 'plain'
199 // data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we
200 // can perform this check in a single branch as 'tag >= EXTERNAL'
201 // Likewise, we have some locations where we check for 'ring or external/flat',
202 // so likewise align RING to EXTERNAL.
203 // Note that we can leave this optimization to the compiler. The compiler will
204 // DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`.
205 static_assert(RING == BTREE + 1, "BTREE and RING not consecutive");
206 static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive");
207 static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive");
208 
209 struct CordRep {
210   // Result from an `extract edge` operation. Contains the (possibly changed)
211   // tree node as well as the extracted edge, or {tree, nullptr} if no edge
212   // could be extracted.
213   // On success, the returned `tree` value is null if `extracted` was the only
214   // data edge inside the tree, a data edge if there were only two data edges in
215   // the tree, or the (possibly new / smaller) remaining tree with the extracted
216   // data edge removed.
217   struct ExtractResult {
218     CordRep* tree;
219     CordRep* extracted;
220   };
221 
222   CordRep() = default;
CordRepCordRep223   constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l)
224       : length(l), refcount(immortal), tag(EXTERNAL), storage{} {}
225 
226   // The following three fields have to be less than 32 bytes since
227   // that is the smallest supported flat node size.
228   size_t length;
229   RefcountAndFlags refcount;
230   // If tag < FLAT, it represents CordRepKind and indicates the type of node.
231   // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
232   uint8_t tag;
233 
234   // `storage` provides two main purposes:
235   // - the starting point for FlatCordRep.Data() [flexible-array-member]
236   // - 3 bytes of additional storage for use by derived classes.
237   // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores
238   // a 'depth' value in storage[0], and the (future) CordRepBtree class stores
239   // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to
240   // allocate room for these in the derived class, as not all compilers reuse
241   // padding space from the base class (clang and gcc do, MSVC does not, etc)
242   uint8_t storage[3];
243 
244   // Returns true if this instance's tag matches the requested type.
IsRingCordRep245   constexpr bool IsRing() const { return tag == RING; }
IsSubstringCordRep246   constexpr bool IsSubstring() const { return tag == SUBSTRING; }
IsCrcCordRep247   constexpr bool IsCrc() const { return tag == CRC; }
IsExternalCordRep248   constexpr bool IsExternal() const { return tag == EXTERNAL; }
IsFlatCordRep249   constexpr bool IsFlat() const { return tag >= FLAT; }
IsBtreeCordRep250   constexpr bool IsBtree() const { return tag == BTREE; }
251 
252   inline CordRepRing* ring();
253   inline const CordRepRing* ring() const;
254   inline CordRepSubstring* substring();
255   inline const CordRepSubstring* substring() const;
256   inline CordRepCrc* crc();
257   inline const CordRepCrc* crc() const;
258   inline CordRepExternal* external();
259   inline const CordRepExternal* external() const;
260   inline CordRepFlat* flat();
261   inline const CordRepFlat* flat() const;
262   inline CordRepBtree* btree();
263   inline const CordRepBtree* btree() const;
264 
265   // --------------------------------------------------------------------
266   // Memory management
267 
268   // Destroys the provided `rep`.
269   static void Destroy(CordRep* rep);
270 
271   // Increments the reference count of `rep`.
272   // Requires `rep` to be a non-null pointer value.
273   static inline CordRep* Ref(CordRep* rep);
274 
275   // Decrements the reference count of `rep`. Destroys rep if count reaches
276   // zero. Requires `rep` to be a non-null pointer value.
277   static inline void Unref(CordRep* rep);
278 };
279 
280 struct CordRepSubstring : public CordRep {
281   size_t start;  // Starting offset of substring in child
282   CordRep* child;
283 
284   // Creates a substring on `child`, adopting a reference on `child`.
285   // Requires `child` to be either a flat or external node, and `pos` and `n` to
286   // form a non-empty partial sub range of `'child`, i.e.:
287   // `n > 0 && n < length && n + pos <= length`
288   static inline CordRepSubstring* Create(CordRep* child, size_t pos, size_t n);
289 
290   // Creates a substring of `rep`. Does not adopt a reference on `rep`.
291   // Requires `IsDataEdge(rep) && n > 0 && pos + n <= rep->length`.
292   // If `n == rep->length` then this method returns `CordRep::Ref(rep)`
293   // If `rep` is a substring of a flat or external node, then this method will
294   // return a new substring of that flat or external node with `pos` adjusted
295   // with the original `start` position.
296   static inline CordRep* Substring(CordRep* rep, size_t pos, size_t n);
297 };
298 
299 // Type for function pointer that will invoke the releaser function and also
300 // delete the `CordRepExternalImpl` corresponding to the passed in
301 // `CordRepExternal`.
302 using ExternalReleaserInvoker = void (*)(CordRepExternal*);
303 
304 // External CordReps are allocated together with a type erased releaser. The
305 // releaser is stored in the memory directly following the CordRepExternal.
306 struct CordRepExternal : public CordRep {
307   CordRepExternal() = default;
CordRepExternalCordRepExternal308   explicit constexpr CordRepExternal(absl::string_view str)
309       : CordRep(RefcountAndFlags::Immortal{}, str.size()),
310         base(str.data()),
311         releaser_invoker(nullptr) {}
312 
313   const char* base;
314   // Pointer to function that knows how to call and destroy the releaser.
315   ExternalReleaserInvoker releaser_invoker;
316 
317   // Deletes (releases) the external rep.
318   // Requires rep != nullptr and rep->IsExternal()
319   static void Delete(CordRep* rep);
320 };
321 
322 struct Rank1 {};
323 struct Rank0 : Rank1 {};
324 
325 template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
326                                  Releaser, absl::string_view>>
InvokeReleaser(Rank0,Releaser && releaser,absl::string_view data)327 void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
328   ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
329 }
330 
331 template <typename Releaser,
332           typename = ::absl::base_internal::invoke_result_t<Releaser>>
InvokeReleaser(Rank1,Releaser && releaser,absl::string_view)333 void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
334   ::absl::base_internal::invoke(std::forward<Releaser>(releaser));
335 }
336 
337 // We use CompressedTuple so that we can benefit from EBCO.
338 template <typename Releaser>
339 struct CordRepExternalImpl
340     : public CordRepExternal,
341       public ::absl::container_internal::CompressedTuple<Releaser> {
342   // The extra int arg is so that we can avoid interfering with copy/move
343   // constructors while still benefitting from perfect forwarding.
344   template <typename T>
CordRepExternalImplCordRepExternalImpl345   CordRepExternalImpl(T&& releaser, int)
346       : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) {
347     this->releaser_invoker = &Release;
348   }
349 
~CordRepExternalImplCordRepExternalImpl350   ~CordRepExternalImpl() {
351     InvokeReleaser(Rank0{}, std::move(this->template get<0>()),
352                    absl::string_view(base, length));
353   }
354 
ReleaseCordRepExternalImpl355   static void Release(CordRepExternal* rep) {
356     delete static_cast<CordRepExternalImpl*>(rep);
357   }
358 };
359 
Create(CordRep * child,size_t pos,size_t n)360 inline CordRepSubstring* CordRepSubstring::Create(CordRep* child, size_t pos,
361                                                   size_t n) {
362   assert(child != nullptr);
363   assert(n > 0);
364   assert(n < child->length);
365   assert(pos < child->length);
366   assert(n <= child->length - pos);
367 
368   // TODO(b/217376272): Harden internal logic.
369   // Move to strategical places inside the Cord logic and make this an assert.
370   if (ABSL_PREDICT_FALSE(!(child->IsExternal() || child->IsFlat()))) {
371     LogFatalNodeType(child);
372   }
373 
374   CordRepSubstring* rep = new CordRepSubstring();
375   rep->length = n;
376   rep->tag = SUBSTRING;
377   rep->start = pos;
378   rep->child = child;
379   return rep;
380 }
381 
Substring(CordRep * rep,size_t pos,size_t n)382 inline CordRep* CordRepSubstring::Substring(CordRep* rep, size_t pos,
383                                             size_t n) {
384   assert(rep != nullptr);
385   assert(n != 0);
386   assert(pos < rep->length);
387   assert(n <= rep->length - pos);
388   if (n == rep->length) return CordRep::Ref(rep);
389   if (rep->IsSubstring()) {
390     pos += rep->substring()->start;
391     rep = rep->substring()->child;
392   }
393   CordRepSubstring* substr = new CordRepSubstring();
394   substr->length = n;
395   substr->tag = SUBSTRING;
396   substr->start = pos;
397   substr->child = CordRep::Ref(rep);
398   return substr;
399 }
400 
Delete(CordRep * rep)401 inline void CordRepExternal::Delete(CordRep* rep) {
402   assert(rep != nullptr && rep->IsExternal());
403   auto* rep_external = static_cast<CordRepExternal*>(rep);
404   assert(rep_external->releaser_invoker != nullptr);
405   rep_external->releaser_invoker(rep_external);
406 }
407 
408 template <typename Str>
409 struct ConstInitExternalStorage {
410   ABSL_CONST_INIT static CordRepExternal value;
411 };
412 
413 template <typename Str>
414 CordRepExternal ConstInitExternalStorage<Str>::value(Str::value);
415 
416 enum {
417   kMaxInline = 15,
418 };
419 
GetOrNull(absl::string_view data,size_t pos)420 constexpr char GetOrNull(absl::string_view data, size_t pos) {
421   return pos < data.size() ? data[pos] : '\0';
422 }
423 
424 // We store cordz_info as 64 bit pointer value in big endian format. This
425 // guarantees that the least significant byte of cordz_info matches the last
426 // byte of the inline data representation in as_chars_, which holds the inlined
427 // size or the 'is_tree' bit.
428 using cordz_info_t = int64_t;
429 
430 // Assert that the `cordz_info` pointer value perfectly overlaps the last half
431 // of `as_chars_` and can hold a pointer value.
432 static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, "");
433 static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), "");
434 
435 // BigEndianByte() creates a big endian representation of 'value', i.e.: a big
436 // endian value where the last byte in the host's representation holds 'value`,
437 // with all other bytes being 0.
BigEndianByte(unsigned char value)438 static constexpr cordz_info_t BigEndianByte(unsigned char value) {
439 #if defined(ABSL_IS_BIG_ENDIAN)
440   return value;
441 #else
442   return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8);
443 #endif
444 }
445 
446 class InlineData {
447  public:
448   // DefaultInitType forces the use of the default initialization constructor.
449   enum DefaultInitType { kDefaultInit };
450 
451   // kNullCordzInfo holds the big endian representation of intptr_t(1)
452   // This is the 'null' / initial value of 'cordz_info'. The null value
453   // is specifically big endian 1 as with 64-bit pointers, the last
454   // byte of cordz_info overlaps with the last byte holding the tag.
455   static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1);
456 
InlineData()457   constexpr InlineData() : as_chars_{0} {}
InlineData(DefaultInitType)458   explicit InlineData(DefaultInitType) {}
InlineData(CordRep * rep)459   explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {}
InlineData(absl::string_view chars)460   explicit constexpr InlineData(absl::string_view chars)
461       : as_chars_{
462             GetOrNull(chars, 0),  GetOrNull(chars, 1),
463             GetOrNull(chars, 2),  GetOrNull(chars, 3),
464             GetOrNull(chars, 4),  GetOrNull(chars, 5),
465             GetOrNull(chars, 6),  GetOrNull(chars, 7),
466             GetOrNull(chars, 8),  GetOrNull(chars, 9),
467             GetOrNull(chars, 10), GetOrNull(chars, 11),
468             GetOrNull(chars, 12), GetOrNull(chars, 13),
469             GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {}
470 
471   // Returns true if the current instance is empty.
472   // The 'empty value' is an inlined data value of zero length.
is_empty()473   bool is_empty() const { return tag() == 0; }
474 
475   // Returns true if the current instance holds a tree value.
is_tree()476   bool is_tree() const { return (tag() & 1) != 0; }
477 
478   // Returns true if the current instance holds a cordz_info value.
479   // Requires the current instance to hold a tree value.
is_profiled()480   bool is_profiled() const {
481     assert(is_tree());
482     return as_tree_.cordz_info != kNullCordzInfo;
483   }
484 
485   // Returns true if either of the provided instances hold a cordz_info value.
486   // This method is more efficient than the equivalent `data1.is_profiled() ||
487   // data2.is_profiled()`. Requires both arguments to hold a tree.
is_either_profiled(const InlineData & data1,const InlineData & data2)488   static bool is_either_profiled(const InlineData& data1,
489                                  const InlineData& data2) {
490     assert(data1.is_tree() && data2.is_tree());
491     return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) !=
492            kNullCordzInfo;
493   }
494 
495   // Returns the cordz_info sampling instance for this instance, or nullptr
496   // if the current instance is not sampled and does not have CordzInfo data.
497   // Requires the current instance to hold a tree value.
cordz_info()498   CordzInfo* cordz_info() const {
499     assert(is_tree());
500     intptr_t info = static_cast<intptr_t>(
501         absl::big_endian::ToHost64(static_cast<uint64_t>(as_tree_.cordz_info)));
502     assert(info & 1);
503     return reinterpret_cast<CordzInfo*>(info - 1);
504   }
505 
506   // Sets the current cordz_info sampling instance for this instance, or nullptr
507   // if the current instance is not sampled and does not have CordzInfo data.
508   // Requires the current instance to hold a tree value.
set_cordz_info(CordzInfo * cordz_info)509   void set_cordz_info(CordzInfo* cordz_info) {
510     assert(is_tree());
511     uintptr_t info = reinterpret_cast<uintptr_t>(cordz_info) | 1;
512     as_tree_.cordz_info =
513         static_cast<cordz_info_t>(absl::big_endian::FromHost64(info));
514   }
515 
516   // Resets the current cordz_info to null / empty.
clear_cordz_info()517   void clear_cordz_info() {
518     assert(is_tree());
519     as_tree_.cordz_info = kNullCordzInfo;
520   }
521 
522   // Returns a read only pointer to the character data inside this instance.
523   // Requires the current instance to hold inline data.
as_chars()524   const char* as_chars() const {
525     assert(!is_tree());
526     return as_chars_;
527   }
528 
529   // Returns a mutable pointer to the character data inside this instance.
530   // Should be used for 'write only' operations setting an inlined value.
531   // Applications can set the value of inlined data either before or after
532   // setting the inlined size, i.e., both of the below are valid:
533   //
534   //   // Set inlined data and inline size
535   //   memcpy(data_.as_chars(), data, size);
536   //   data_.set_inline_size(size);
537   //
538   //   // Set inlined size and inline data
539   //   data_.set_inline_size(size);
540   //   memcpy(data_.as_chars(), data, size);
541   //
542   // It's an error to read from the returned pointer without a preceding write
543   // if the current instance does not hold inline data, i.e.: is_tree() == true.
as_chars()544   char* as_chars() { return as_chars_; }
545 
546   // Returns the tree value of this value.
547   // Requires the current instance to hold a tree value.
as_tree()548   CordRep* as_tree() const {
549     assert(is_tree());
550     return as_tree_.rep;
551   }
552 
553   // Initialize this instance to holding the tree value `rep`,
554   // initializing the cordz_info to null, i.e.: 'not profiled'.
make_tree(CordRep * rep)555   void make_tree(CordRep* rep) {
556     as_tree_.rep = rep;
557     as_tree_.cordz_info = kNullCordzInfo;
558   }
559 
560   // Set the tree value of this instance to 'rep`.
561   // Requires the current instance to already hold a tree value.
562   // Does not affect the value of cordz_info.
set_tree(CordRep * rep)563   void set_tree(CordRep* rep) {
564     assert(is_tree());
565     as_tree_.rep = rep;
566   }
567 
568   // Returns the size of the inlined character data inside this instance.
569   // Requires the current instance to hold inline data.
inline_size()570   size_t inline_size() const {
571     assert(!is_tree());
572     return tag() >> 1;
573   }
574 
575   // Sets the size of the inlined character data inside this instance.
576   // Requires `size` to be <= kMaxInline.
577   // See the documentation on 'as_chars()' for more information and examples.
set_inline_size(size_t size)578   void set_inline_size(size_t size) {
579     ABSL_ASSERT(size <= kMaxInline);
580     tag() = static_cast<char>(size << 1);
581   }
582 
583  private:
584   // See cordz_info_t for forced alignment and size of `cordz_info` details.
585   struct AsTree {
AsTreeAsTree586     explicit constexpr AsTree(absl::cord_internal::CordRep* tree)
587         : rep(tree), cordz_info(kNullCordzInfo) {}
588     // This union uses up extra space so that whether rep is 32 or 64 bits,
589     // cordz_info will still start at the eighth byte, and the last
590     // byte of cordz_info will still be the last byte of InlineData.
591     union {
592       absl::cord_internal::CordRep* rep;
593       cordz_info_t unused_aligner;
594     };
595     cordz_info_t cordz_info;
596   };
597 
tag()598   char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; }
tag()599   char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; }
600 
601   // If the data has length <= kMaxInline, we store it in `as_chars_`, and
602   // store the size in the last char of `as_chars_` shifted left + 1.
603   // Else we store it in a tree and store a pointer to that tree in
604   // `as_tree_.rep` and store a tag in `tagged_size`.
605   union {
606     char as_chars_[kMaxInline + 1];
607     AsTree as_tree_;
608   };
609 };
610 
611 static_assert(sizeof(InlineData) == kMaxInline + 1, "");
612 
substring()613 inline CordRepSubstring* CordRep::substring() {
614   assert(IsSubstring());
615   return static_cast<CordRepSubstring*>(this);
616 }
617 
substring()618 inline const CordRepSubstring* CordRep::substring() const {
619   assert(IsSubstring());
620   return static_cast<const CordRepSubstring*>(this);
621 }
622 
external()623 inline CordRepExternal* CordRep::external() {
624   assert(IsExternal());
625   return static_cast<CordRepExternal*>(this);
626 }
627 
external()628 inline const CordRepExternal* CordRep::external() const {
629   assert(IsExternal());
630   return static_cast<const CordRepExternal*>(this);
631 }
632 
Ref(CordRep * rep)633 inline CordRep* CordRep::Ref(CordRep* rep) {
634   assert(rep != nullptr);
635   rep->refcount.Increment();
636   return rep;
637 }
638 
Unref(CordRep * rep)639 inline void CordRep::Unref(CordRep* rep) {
640   assert(rep != nullptr);
641   // Expect refcount to be 0. Avoiding the cost of an atomic decrement should
642   // typically outweigh the cost of an extra branch checking for ref == 1.
643   if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) {
644     Destroy(rep);
645   }
646 }
647 
648 }  // namespace cord_internal
649 
650 ABSL_NAMESPACE_END
651 }  // namespace absl
652 #endif  // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
653