• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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_REP_RING_H_
16 #define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
17 
18 #include <cassert>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iosfwd>
22 #include <limits>
23 #include <memory>
24 
25 #include "absl/container/internal/layout.h"
26 #include "absl/strings/internal/cord_internal.h"
27 #include "absl/strings/internal/cord_rep_flat.h"
28 
29 namespace absl {
30 ABSL_NAMESPACE_BEGIN
31 namespace cord_internal {
32 
33 // All operations modifying a ring buffer are implemented as static methods
34 // requiring a CordRepRing instance with a reference adopted by the method.
35 //
36 // The methods return the modified ring buffer, which may be equal to the input
37 // if the input was not shared, and having large enough capacity to accommodate
38 // any newly added node(s). Otherwise, a copy of the input rep with the new
39 // node(s) added is returned.
40 //
41 // Any modification on non shared ring buffers with enough capacity will then
42 // require minimum atomic operations. Caller should where possible provide
43 // reasonable `extra` hints for both anticipated extra `flat` byte space, as
44 // well as anticipated extra nodes required for complex operations.
45 //
46 // Example of code creating a ring buffer, adding some data to it,
47 // and discarding the buffer when done:
48 //
49 //   void FunWithRings() {
50 //     // Create ring with 3 flats
51 //     CordRep* flat = CreateFlat("Hello");
52 //     CordRepRing* ring = CordRepRing::Create(flat, 2);
53 //     ring = CordRepRing::Append(ring, CreateFlat(" "));
54 //     ring = CordRepRing::Append(ring, CreateFlat("world"));
55 //     DoSomethingWithRing(ring);
56 //     CordRep::Unref(ring);
57 //   }
58 //
59 // Example of code Copying an existing ring buffer and modifying it:
60 //
61 //   void MoreFunWithRings(CordRepRing* src) {
62 //     CordRepRing* ring = CordRep::Ref(src)->ring();
63 //     ring = CordRepRing::Append(ring, CreateFlat("Hello"));
64 //     ring = CordRepRing::Append(ring, CreateFlat(" "));
65 //     ring = CordRepRing::Append(ring, CreateFlat("world"));
66 //     DoSomethingWithRing(ring);
67 //     CordRep::Unref(ring);
68 //   }
69 //
70 class CordRepRing : public CordRep {
71  public:
72   // `pos_type` represents a 'logical position'. A CordRepRing instance has a
73   // `begin_pos` (default 0), and each node inside the buffer will have an
74   // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus
75   // this node's length. The purpose is to allow for a binary search on this
76   // position, while allowing O(1) prepend and append operations.
77   using pos_type = size_t;
78 
79   // `index_type` is the type for the `head`, `tail` and `capacity` indexes.
80   // Ring buffers are limited to having no more than four billion entries.
81   using index_type = uint32_t;
82 
83   // `offset_type` is the type for the data offset inside a child rep's data.
84   using offset_type = uint32_t;
85 
86   // Position holds the node index and relative offset into the node for
87   // some physical offset in the contained data as returned by the Find()
88   // and FindTail() methods.
89   struct Position {
90     index_type index;
91     size_t offset;
92   };
93 
94   // The maximum # of child nodes that can be hosted inside a CordRepRing.
95   static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)();
96 
97   // CordRepring can not be default constructed, moved, copied or assigned.
98   CordRepRing() = delete;
99   CordRepRing(const CordRepRing&) = delete;
100   CordRepRing& operator=(const CordRepRing&) = delete;
101 
102   // Returns true if this instance is valid, false if some or all of the
103   // invariants are broken. Intended for debug purposes only.
104   // `output` receives an explanation of the broken invariants.
105   bool IsValid(std::ostream& output) const;
106 
107   // Returns the size in bytes for a CordRepRing with `capacity' entries.
108   static constexpr size_t AllocSize(size_t capacity);
109 
110   // Returns the distance in bytes from `pos` to `end_pos`.
111   static constexpr size_t Distance(pos_type pos, pos_type end_pos);
112 
113   // Creates a new ring buffer from the provided `rep`. Adopts a reference
114   // on `rep`. The returned ring buffer has a capacity of at least `extra + 1`
115   static CordRepRing* Create(CordRep* child, size_t extra = 0);
116 
117   // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries.
head()118   index_type head() const { return head_; }
tail()119   index_type tail() const { return tail_; }
capacity()120   index_type capacity() const { return capacity_; }
121 
122   // Returns the number of entries in this instance.
entries()123   index_type entries() const { return entries(head_, tail_); }
124 
125   // Returns the logical begin position of this instance.
begin_pos()126   pos_type begin_pos() const { return begin_pos_; }
127 
128   // Returns the number of entries for a given head-tail range.
129   // Requires `head` and `tail` values to be less than `capacity()`.
entries(index_type head,index_type tail)130   index_type entries(index_type head, index_type tail) const {
131     assert(head < capacity_ && tail < capacity_);
132     return tail - head + ((tail > head) ? 0 : capacity_);
133   }
134 
135   // Returns the logical end position of entry `index`.
entry_end_pos(index_type index)136   pos_type const& entry_end_pos(index_type index) const {
137     assert(IsValidIndex(index));
138     return Layout::Partial().Pointer<0>(data_)[index];
139   }
140 
141   // Returns the child pointer of entry `index`.
entry_child(index_type index)142   CordRep* const& entry_child(index_type index) const {
143     assert(IsValidIndex(index));
144     return Layout::Partial(capacity()).Pointer<1>(data_)[index];
145   }
146 
147   // Returns the data offset of entry `index`
entry_data_offset(index_type index)148   offset_type const& entry_data_offset(index_type index) const {
149     assert(IsValidIndex(index));
150     return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index];
151   }
152 
153   // Appends the provided child node to the `rep` instance.
154   // Adopts a reference from `rep` and `child` which may not be null.
155   // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
156   // containing a FLAT or EXTERNAL node, then flat or external the node is added
157   // 'as is', with an offset added for the SUBSTRING case.
158   // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or
159   // CONCAT tree, then all child nodes not excluded by any start offset or
160   // length values are added recursively.
161   static CordRepRing* Append(CordRepRing* rep, CordRep* child);
162 
163   // Appends the provided string data to the `rep` instance.
164   // This function will attempt to utilize any remaining capacity in the last
165   // node of the input if that node is not shared (directly or indirectly), and
166   // of type FLAT. Remaining data will be added as one or more FLAT nodes.
167   // Any last node added to the ring buffer will be allocated with up to
168   // `extra` bytes of capacity for (anticipated) subsequent append actions.
169   static CordRepRing* Append(CordRepRing* rep, string_view data,
170                              size_t extra = 0);
171 
172   // Prepends the provided child node to the `rep` instance.
173   // Adopts a reference from `rep` and `child` which may not be null.
174   // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node
175   // containing a FLAT or EXTERNAL node, then flat or external the node is
176   // prepended 'as is', with an optional offset added for the SUBSTRING case.
177   // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING
178   // or CONCAT tree, then all child nodes not excluded by any start offset or
179   // length values are added recursively.
180   static CordRepRing* Prepend(CordRepRing* rep, CordRep* child);
181 
182   // Prepends the provided string data to the `rep` instance.
183   // This function will attempt to utilize any remaining capacity in the first
184   // node of the input if that node is not shared (directly or indirectly), and
185   // of type FLAT. Remaining data will be added as one or more FLAT nodes.
186   // Any first node prepnded to the ring buffer will be allocated with up to
187   // `extra` bytes of capacity for (anticipated) subsequent prepend actions.
188   static CordRepRing* Prepend(CordRepRing* rep, string_view data,
189                               size_t extra = 0);
190 
191   // Returns a span referencing potentially unused capacity in the last node.
192   // The returned span may be empty if no such capacity is available, or if the
193   // current instance is shared. Else, a span of size `n <= size` is returned.
194   // If non empty, the ring buffer is adjusted to the new length, with the newly
195   // added capacity left uninitialized. Callers should assign a value to the
196   // entire span before any other operations on this instance.
197   Span<char> GetAppendBuffer(size_t size);
198 
199   // Returns a span referencing potentially unused capacity in the first node.
200   // This function is identical to GetAppendBuffer except that it returns a span
201   // referencing up to `size` capacity directly before the existing data.
202   Span<char> GetPrependBuffer(size_t size);
203 
204   // Returns a cord ring buffer containing `len` bytes of data starting at
205   // `offset`. If the input is not shared, this function will remove all head
206   // and tail child nodes outside of the requested range, and adjust the new
207   // head and tail nodes as required. If the input is shared, this function
208   // returns a new instance sharing some or all of the nodes from the input.
209   static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len,
210                               size_t extra = 0);
211 
212   // Returns a cord ring buffer with the first `len` bytes removed.
213   // If the input is not shared, this function will remove all head child nodes
214   // fully inside the first `length` bytes, and adjust the new head as required.
215   // If the input is shared, this function returns a new instance sharing some
216   // or all of the nodes from the input.
217   static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len,
218                                    size_t extra = 0);
219 
220   // Returns a cord ring buffer with the last `len` bytes removed.
221   // If the input is not shared, this function will remove all head child nodes
222   // fully inside the first `length` bytes, and adjust the new head as required.
223   // If the input is shared, this function returns a new instance sharing some
224   // or all of the nodes from the input.
225   static CordRepRing* RemovePrefix(CordRepRing* r, size_t len,
226                                    size_t extra = 0);
227 
228   // Returns the character at `offset`. Requires that `offset < length`.
229   char GetCharacter(size_t offset) const;
230 
231   // Returns true if this instance manages a single contiguous buffer, in which
232   // case the (optional) output parameter `fragment` is set. Otherwise, the
233   // function returns false, and `fragment` is left unchanged.
234   bool IsFlat(absl::string_view* fragment) const;
235 
236   // Returns true if the data starting at `offset` with length `len` is
237   // managed by this instance inside a single contiguous buffer, in which case
238   // the (optional) output parameter `fragment` is set to the contiguous memory
239   // starting at offset `offset` with length `length`. Otherwise, the function
240   // returns false, and `fragment` is left unchanged.
241   bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const;
242 
243   // Testing only: set capacity to requested capacity.
244   void SetCapacityForTesting(size_t capacity);
245 
246   // Returns the CordRep data pointer for the provided CordRep.
247   // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep.
248   static const char* GetLeafData(const CordRep* rep);
249 
250   // Returns the CordRep data pointer for the provided CordRep.
251   // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep.
252   static const char* GetRepData(const CordRep* rep);
253 
254   // Advances the provided position, wrapping around capacity as needed.
255   // Requires `index` < capacity()
256   inline index_type advance(index_type index) const;
257 
258   // Advances the provided position by 'n`, wrapping around capacity as needed.
259   // Requires `index` < capacity() and `n` <= capacity.
260   inline index_type advance(index_type index, index_type n) const;
261 
262   // Retreats the provided position, wrapping around 0 as needed.
263   // Requires `index` < capacity()
264   inline index_type retreat(index_type index) const;
265 
266   // Retreats the provided position by 'n', wrapping around 0 as needed.
267   // Requires `index` < capacity()
268   inline index_type retreat(index_type index, index_type n) const;
269 
270   // Returns the logical begin position of entry `index`
entry_begin_pos(index_type index)271   pos_type const& entry_begin_pos(index_type index) const {
272     return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index));
273   }
274 
275   // Returns the physical start offset of entry `index`
entry_start_offset(index_type index)276   size_t entry_start_offset(index_type index) const {
277     return Distance(begin_pos_, entry_begin_pos(index));
278   }
279 
280   // Returns the physical end offset of entry `index`
entry_end_offset(index_type index)281   size_t entry_end_offset(index_type index) const {
282     return Distance(begin_pos_, entry_end_pos(index));
283   }
284 
285   // Returns the data length for entry `index`
entry_length(index_type index)286   size_t entry_length(index_type index) const {
287     return Distance(entry_begin_pos(index), entry_end_pos(index));
288   }
289 
290   // Returns the data for entry `index`
291   absl::string_view entry_data(index_type index) const;
292 
293   // Returns the position for `offset` as {index, prefix}. `index` holds the
294   // index of the entry at the specified offset and `prefix` holds the relative
295   // offset inside that entry.
296   // Requires `offset` < length.
297   //
298   // For example we can implement GetCharacter(offset) as:
299   //   char GetCharacter(size_t offset) {
300   //     Position pos = this->Find(offset);
301   //     return this->entry_data(pos.pos)[pos.offset];
302   //   }
303   inline Position Find(size_t offset) const;
304 
305   // Find starting at `head`
306   inline Position Find(index_type head, size_t offset) const;
307 
308   // Returns the tail position for `offset` as {tail index, suffix}.
309   // `tail index` holds holds the index of the entry holding the offset directly
310   // before 'offset` advanced by one. 'suffix` holds the relative offset from
311   // that relative offset in the entry to the end of the entry.
312   // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5)
313   // will return {retreat(tail), 5)} provided the preceding entry contains at
314   // least 5 bytes of data.
315   // Requires offset >= 1 && offset <= length.
316   //
317   // This function is very useful in functions that need to clip the end of some
318   // ring buffer such as 'RemovePrefix'.
319   // For example, we could implement RemovePrefix for non shared instances as:
320   //   void RemoveSuffix(size_t n) {
321   //     Position pos = FindTail(length - n);
322   //     UnrefEntries(pos.pos, this->tail_);
323   //     this->tail_ = pos.pos;
324   //     entry(retreat(pos.pos)).end_pos -= pos.offset;
325   //   }
326   inline Position FindTail(size_t offset) const;
327 
328   // Find tail starting at `head`
329   inline Position FindTail(index_type head, size_t offset) const;
330 
331   // Invokes f(index_type index) for each entry inside the range [head, tail>
332   template <typename F>
ForEach(index_type head,index_type tail,F && f)333   void ForEach(index_type head, index_type tail, F&& f) const {
334     index_type n1 = (tail > head) ? tail : capacity_;
335     for (index_type i = head; i < n1; ++i) f(i);
336     if (tail <= head) {
337       for (index_type i = 0; i < tail; ++i) f(i);
338     }
339   }
340 
341   // Invokes f(index_type index) for each entry inside this instance.
342   template <typename F>
ForEach(F && f)343   void ForEach(F&& f) const {
344     ForEach(head_, tail_, std::forward<F>(f));
345   }
346 
347   // Dump this instance's data tp stream `s` in human readable format, excluding
348   // the actual data content itself. Intended for debug purposes only.
349   friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
350 
351  private:
352   enum class AddMode { kAppend, kPrepend };
353 
354   using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>;
355 
356   class Filler;
357   class Transaction;
358   class CreateTransaction;
359 
360   static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment();
361 
362   // Creates a new CordRepRing.
CordRepRing(index_type capacity)363   explicit CordRepRing(index_type capacity) : capacity_(capacity) {}
364 
365   // Returns true if `index` is a valid index into this instance.
366   bool IsValidIndex(index_type index) const;
367 
368   // Debug use only: validates the provided CordRepRing invariants.
369   // Verification of all CordRepRing methods can be enabled by defining
370   // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION`
371   // Verification is VERY expensive, so only do it for debugging purposes.
372   static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr,
373                                int line = 0);
374 
375   // Allocates a CordRepRing large enough to hold `capacity + extra' entries.
376   // The returned capacity may be larger if the allocated memory allows for it.
377   // The maximum capacity of a CordRepRing is capped at kMaxCapacity.
378   // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity.
379   static CordRepRing* New(size_t capacity, size_t extra);
380 
381   // Deallocates (but does not destroy) the provided ring buffer.
382   static void Delete(CordRepRing* rep);
383 
384   // Destroys the provided ring buffer, decrementing the reference count of all
385   // contained child CordReps. The provided 1\`rep` should have a ref count of
386   // one (pre decrement destroy call observing `refcount.IsOne()`) or zero (post
387   // decrement destroy call observing `!refcount.Decrement()`).
388   static void Destroy(CordRepRing* rep);
389 
390   // Returns a mutable reference to the logical end position array.
entry_end_pos()391   pos_type* entry_end_pos() {
392     return Layout::Partial().Pointer<0>(data_);
393   }
394 
395   // Returns a mutable reference to the child pointer array.
entry_child()396   CordRep** entry_child() {
397     return Layout::Partial(capacity()).Pointer<1>(data_);
398   }
399 
400   // Returns a mutable reference to the data offset array.
entry_data_offset()401   offset_type* entry_data_offset() {
402     return Layout::Partial(capacity(), capacity()).Pointer<2>(data_);
403   }
404 
405   // Find implementations for the non fast path 0 / length cases.
406   Position FindSlow(index_type head, size_t offset) const;
407   Position FindTailSlow(index_type head, size_t offset) const;
408 
409   // Finds the index of the first node that is inside a reasonable distance
410   // of the node at `offset` from which we can continue with a linear search.
411   template <bool wrap>
412   index_type FindBinary(index_type head, index_type tail, size_t offset) const;
413 
414   // Fills the current (initialized) instance from the provided source, copying
415   // entries [head, tail). Adds a reference to copied entries if `ref` is true.
416   template <bool ref>
417   void Fill(const CordRepRing* src, index_type head, index_type tail);
418 
419   // Create a copy of 'rep', copying all entries [head, tail), allocating room
420   // for `extra` entries. Adds a reference on all copied entries.
421   static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail,
422                            size_t extra = 0);
423 
424   // Returns a Mutable CordRepRing reference from `rep` with room for at least
425   // `extra` additional nodes. Adopts a reference count from `rep`.
426   // This function will return `rep` if, and only if:
427   // - rep.entries + extra <= rep.capacity
428   // - rep.refcount == 1
429   // Otherwise, this function will create a new copy of `rep` with additional
430   // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance.
431   //
432   // If a new CordRepRing can not be allocated, or the new capacity would exceed
433   // the maxmimum capacity, then the input is consumed only, and an exception is
434   // thrown.
435   static CordRepRing* Mutable(CordRepRing* rep, size_t extra);
436 
437   // Slow path for Append(CordRepRing* rep, CordRep* child). This function is
438   // exercised if the provided `child` in Append() is not a leaf node, i.e., a
439   // ring buffer or old (concat) cord tree.
440   static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child);
441 
442   // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
443   static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child,
444                                  size_t offset, size_t length);
445 
446   // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL.
447   static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child,
448                                   size_t offset, size_t length);
449 
450   // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is
451   // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
452   // ring buffer or old (concat) cord tree.
453   static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child);
454 
455   // Slow path for Create(CordRep* child, size_t extra). This function is
456   // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a
457   // ring buffer or old (concat) cord tree.
458   static CordRepRing* CreateSlow(CordRep* child, size_t extra);
459 
460   // Creates a new ring buffer from the provided `child` leaf node. Requires
461   // `child` to be FLAT or EXTERNAL. on `rep`.
462   // The returned ring buffer has a capacity of at least `1 + extra`
463   static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset,
464                                      size_t length, size_t extra);
465 
466   // Appends or prepends (depending on AddMode) the ring buffer in `ring' to
467   // `rep` starting at `offset` with length `len`.
468   template <AddMode mode>
469   static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring,
470                               size_t offset, size_t len);
471 
472   // Increases the data offset for entry `index` by `n`.
473   void AddDataOffset(index_type index, size_t n);
474 
475   // Descreases the length for entry `index` by `n`.
476   void SubLength(index_type index, size_t n);
477 
478   index_type head_;
479   index_type tail_;
480   index_type capacity_;
481   pos_type begin_pos_;
482 
483   alignas(kLayoutAlignment) char data_[kLayoutAlignment];
484 
485   friend struct CordRep;
486 };
487 
AllocSize(size_t capacity)488 constexpr size_t CordRepRing::AllocSize(size_t capacity) {
489   return sizeof(CordRepRing) - sizeof(data_) +
490          Layout(capacity, capacity, capacity).AllocSize();
491 }
492 
Distance(pos_type pos,pos_type end_pos)493 inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) {
494   return (end_pos - pos);
495 }
496 
GetLeafData(const CordRep * rep)497 inline const char* CordRepRing::GetLeafData(const CordRep* rep) {
498   return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base;
499 }
500 
GetRepData(const CordRep * rep)501 inline const char* CordRepRing::GetRepData(const CordRep* rep) {
502   if (rep->tag >= FLAT) return rep->flat()->Data();
503   if (rep->tag == EXTERNAL) return rep->external()->base;
504   return GetLeafData(rep->substring()->child) + rep->substring()->start;
505 }
506 
advance(index_type index)507 inline CordRepRing::index_type CordRepRing::advance(index_type index) const {
508   assert(index < capacity_);
509   return ++index == capacity_ ? 0 : index;
510 }
511 
advance(index_type index,index_type n)512 inline CordRepRing::index_type CordRepRing::advance(index_type index,
513                                                     index_type n) const {
514   assert(index < capacity_ && n <= capacity_);
515   return (index += n) >= capacity_ ? index - capacity_ : index;
516 }
517 
retreat(index_type index)518 inline CordRepRing::index_type CordRepRing::retreat(index_type index) const {
519   assert(index < capacity_);
520   return (index > 0 ? index : capacity_) - 1;
521 }
522 
retreat(index_type index,index_type n)523 inline CordRepRing::index_type CordRepRing::retreat(index_type index,
524                                                     index_type n) const {
525   assert(index < capacity_ && n <= capacity_);
526   return index >= n ? index - n : capacity_ - n + index;
527 }
528 
entry_data(index_type index)529 inline absl::string_view CordRepRing::entry_data(index_type index) const {
530   size_t data_offset = entry_data_offset(index);
531   return {GetRepData(entry_child(index)) + data_offset, entry_length(index)};
532 }
533 
IsValidIndex(index_type index)534 inline bool CordRepRing::IsValidIndex(index_type index) const {
535   if (index >= capacity_) return false;
536   return (tail_ > head_) ? (index >= head_ && index < tail_)
537                          : (index >= head_ || index < tail_);
538 }
539 
540 #ifndef EXTRA_CORD_RING_VALIDATION
Validate(CordRepRing * rep,const char *,int)541 inline CordRepRing* CordRepRing::Validate(CordRepRing* rep,
542                                           const char* /*file*/, int /*line*/) {
543   return rep;
544 }
545 #endif
546 
Find(size_t offset)547 inline CordRepRing::Position CordRepRing::Find(size_t offset) const {
548   assert(offset < length);
549   return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset);
550 }
551 
Find(index_type head,size_t offset)552 inline CordRepRing::Position CordRepRing::Find(index_type head,
553                                                size_t offset) const {
554   assert(offset < length);
555   assert(IsValidIndex(head) && offset >= entry_start_offset(head));
556   return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset);
557 }
558 
FindTail(size_t offset)559 inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const {
560   assert(offset > 0 && offset <= length);
561   return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset);
562 }
563 
FindTail(index_type head,size_t offset)564 inline CordRepRing::Position CordRepRing::FindTail(index_type head,
565                                                    size_t offset) const {
566   assert(offset > 0 && offset <= length);
567   assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1);
568   return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset);
569 }
570 
571 // Now that CordRepRing is defined, we can define CordRep's helper casts:
ring()572 inline CordRepRing* CordRep::ring() {
573   assert(tag == RING);
574   return static_cast<CordRepRing*>(this);
575 }
576 
ring()577 inline const CordRepRing* CordRep::ring() const {
578   assert(tag == RING);
579   return static_cast<const CordRepRing*>(this);
580 }
581 
IsFlat(absl::string_view * fragment)582 inline bool CordRepRing::IsFlat(absl::string_view* fragment) const {
583   if (entries() == 1) {
584     if (fragment) *fragment = entry_data(head());
585     return true;
586   }
587   return false;
588 }
589 
IsFlat(size_t offset,size_t len,absl::string_view * fragment)590 inline bool CordRepRing::IsFlat(size_t offset, size_t len,
591                                 absl::string_view* fragment) const {
592   const Position pos = Find(offset);
593   const absl::string_view data = entry_data(pos.index);
594   if (data.length() >= len && data.length() - len >= pos.offset) {
595     if (fragment) *fragment = data.substr(pos.offset, len);
596     return true;
597   }
598   return false;
599 }
600 
601 std::ostream& operator<<(std::ostream& s, const CordRepRing& rep);
602 
603 }  // namespace cord_internal
604 ABSL_NAMESPACE_END
605 }  // namespace absl
606 
607 #endif  // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_
608