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 #include "absl/strings/internal/cord_rep_ring.h"
15 
16 #include <cassert>
17 #include <cstddef>
18 #include <cstdint>
19 #include <iostream>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 
24 #include "absl/base/internal/raw_logging.h"
25 #include "absl/base/internal/throw_delegate.h"
26 #include "absl/base/macros.h"
27 #include "absl/container/inlined_vector.h"
28 #include "absl/strings/internal/cord_internal.h"
29 #include "absl/strings/internal/cord_rep_consume.h"
30 #include "absl/strings/internal/cord_rep_flat.h"
31 
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace cord_internal {
35 
36 namespace {
37 
38 using index_type = CordRepRing::index_type;
39 
40 enum class Direction { kForward, kReversed };
41 
IsFlatOrExternal(CordRep * rep)42 inline bool IsFlatOrExternal(CordRep* rep) {
43   return rep->IsFlat() || rep->IsExternal();
44 }
45 
46 // Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise.
CheckCapacity(size_t n,size_t extra)47 inline void CheckCapacity(size_t n, size_t extra) {
48   if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) {
49     base_internal::ThrowStdLengthError("Maximum capacity exceeded");
50   }
51 }
52 
53 // Creates a flat from the provided string data, allocating up to `extra`
54 // capacity in the returned flat depending on kMaxFlatLength limitations.
55 // Requires `len` to be less or equal to `kMaxFlatLength`
CreateFlat(const char * s,size_t n,size_t extra=0)56 CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) {  // NOLINT
57   assert(n <= kMaxFlatLength);
58   auto* rep = CordRepFlat::New(n + extra);
59   rep->length = n;
60   memcpy(rep->Data(), s, n);
61   return rep;
62 }
63 
64 // Unrefs the entries in `[head, tail)`.
65 // Requires all entries to be a FLAT or EXTERNAL node.
UnrefEntries(const CordRepRing * rep,index_type head,index_type tail)66 void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
67   rep->ForEach(head, tail, [rep](index_type ix) {
68     CordRep* child = rep->entry_child(ix);
69     if (!child->refcount.Decrement()) {
70       if (child->tag >= FLAT) {
71         CordRepFlat::Delete(child->flat());
72       } else {
73         CordRepExternal::Delete(child->external());
74       }
75     }
76   });
77 }
78 
79 }  // namespace
80 
operator <<(std::ostream & s,const CordRepRing & rep)81 std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) {
82   // Note: 'pos' values are defined as size_t (for overflow reasons), but that
83   // prints really awkward for small prepended values such as -5. ssize_t is not
84   // portable (POSIX), so we use ptrdiff_t instead to cast to signed values.
85   s << "  CordRepRing(" << &rep << ", length = " << rep.length
86     << ", head = " << rep.head_ << ", tail = " << rep.tail_
87     << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get()
88     << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n";
89   CordRepRing::index_type head = rep.head();
90   do {
91     CordRep* child = rep.entry_child(head);
92     s << " entry[" << head << "] length = " << rep.entry_length(head)
93       << ", child " << child << ", clen = " << child->length
94       << ", tag = " << static_cast<int>(child->tag)
95       << ", rc = " << child->refcount.Get()
96       << ", offset = " << rep.entry_data_offset(head)
97       << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head))
98       << "\n";
99     head = rep.advance(head);
100   } while (head != rep.tail());
101   return s << "}\n";
102 }
103 
AddDataOffset(index_type index,size_t n)104 void CordRepRing::AddDataOffset(index_type index, size_t n) {
105   entry_data_offset()[index] += static_cast<offset_type>(n);
106 }
107 
SubLength(index_type index,size_t n)108 void CordRepRing::SubLength(index_type index, size_t n) {
109   entry_end_pos()[index] -= n;
110 }
111 
112 class CordRepRing::Filler {
113  public:
Filler(CordRepRing * rep,index_type pos)114   Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {}
115 
head() const116   index_type head() const { return head_; }
pos() const117   index_type pos() const { return pos_; }
118 
Add(CordRep * child,size_t offset,pos_type end_pos)119   void Add(CordRep* child, size_t offset, pos_type end_pos) {
120     rep_->entry_end_pos()[pos_] = end_pos;
121     rep_->entry_child()[pos_] = child;
122     rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset);
123     pos_ = rep_->advance(pos_);
124   }
125 
126  private:
127   CordRepRing* rep_;
128   index_type head_;
129   index_type pos_;
130 };
131 
132 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
133 constexpr size_t CordRepRing::kMaxCapacity;
134 #endif
135 
IsValid(std::ostream & output) const136 bool CordRepRing::IsValid(std::ostream& output) const {
137   if (capacity_ == 0) {
138     output << "capacity == 0";
139     return false;
140   }
141 
142   if (head_ >= capacity_ || tail_ >= capacity_) {
143     output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity "
144            << capacity_;
145     return false;
146   }
147 
148   const index_type back = retreat(tail_);
149   size_t pos_length = Distance(begin_pos_, entry_end_pos(back));
150   if (pos_length != length) {
151     output << "length " << length << " does not match positional length "
152            << pos_length << " from begin_pos " << begin_pos_ << " and entry["
153            << back << "].end_pos " << entry_end_pos(back);
154     return false;
155   }
156 
157   index_type head = head_;
158   pos_type begin_pos = begin_pos_;
159   do {
160     pos_type end_pos = entry_end_pos(head);
161     size_t entry_length = Distance(begin_pos, end_pos);
162     if (entry_length == 0) {
163       output << "entry[" << head << "] has an invalid length " << entry_length
164              << " from begin_pos " << begin_pos << " and end_pos " << end_pos;
165       return false;
166     }
167 
168     CordRep* child = entry_child(head);
169     if (child == nullptr) {
170       output << "entry[" << head << "].child == nullptr";
171       return false;
172     }
173     if (child->tag < FLAT && child->tag != EXTERNAL) {
174       output << "entry[" << head << "].child has an invalid tag "
175              << static_cast<int>(child->tag);
176       return false;
177     }
178 
179     size_t offset = entry_data_offset(head);
180     if (offset >= child->length || entry_length > child->length - offset) {
181       output << "entry[" << head << "] has offset " << offset
182              << " and entry length " << entry_length
183              << " which are outside of the child's length of " << child->length;
184       return false;
185     }
186 
187     begin_pos = end_pos;
188     head = advance(head);
189   } while (head != tail_);
190 
191   return true;
192 }
193 
194 #ifdef EXTRA_CORD_RING_VALIDATION
Validate(CordRepRing * rep,const char * file,int line)195 CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file,
196                                    int line) {
197   if (!rep->IsValid(std::cerr)) {
198     std::cerr << "\nERROR: CordRepRing corrupted";
199     if (line) std::cerr << " at line " << line;
200     if (file) std::cerr << " in file " << file;
201     std::cerr << "\nContent = " << *rep;
202     abort();
203   }
204   return rep;
205 }
206 #endif  // EXTRA_CORD_RING_VALIDATION
207 
New(size_t capacity,size_t extra)208 CordRepRing* CordRepRing::New(size_t capacity, size_t extra) {
209   CheckCapacity(capacity, extra);
210 
211   size_t size = AllocSize(capacity += extra);
212   void* mem = ::operator new(size);
213   auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity));
214   rep->tag = RING;
215   rep->capacity_ = static_cast<index_type>(capacity);
216   rep->begin_pos_ = 0;
217   return rep;
218 }
219 
SetCapacityForTesting(size_t capacity)220 void CordRepRing::SetCapacityForTesting(size_t capacity) {
221   // Adjust for the changed layout
222   assert(capacity <= capacity_);
223   assert(head() == 0 || head() < tail());
224   memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(),
225           Layout::Partial(capacity_).Pointer<1>(data_) + head(),
226           entries() * sizeof(Layout::ElementType<1>));
227   memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(),
228           Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(),
229           entries() * sizeof(Layout::ElementType<2>));
230   capacity_ = static_cast<index_type>(capacity);
231 }
232 
Delete(CordRepRing * rep)233 void CordRepRing::Delete(CordRepRing* rep) {
234   assert(rep != nullptr && rep->IsRing());
235 #if defined(__cpp_sized_deallocation)
236   size_t size = AllocSize(rep->capacity_);
237   rep->~CordRepRing();
238   ::operator delete(rep, size);
239 #else
240   rep->~CordRepRing();
241   ::operator delete(rep);
242 #endif
243 }
244 
Destroy(CordRepRing * rep)245 void CordRepRing::Destroy(CordRepRing* rep) {
246   UnrefEntries(rep, rep->head(), rep->tail());
247   Delete(rep);
248 }
249 
250 template <bool ref>
Fill(const CordRepRing * src,index_type head,index_type tail)251 void CordRepRing::Fill(const CordRepRing* src, index_type head,
252                        index_type tail) {
253   this->length = src->length;
254   head_ = 0;
255   tail_ = advance(0, src->entries(head, tail));
256   begin_pos_ = src->begin_pos_;
257 
258   // TODO(mvels): there may be opportunities here for large buffers.
259   auto* dst_pos = entry_end_pos();
260   auto* dst_child = entry_child();
261   auto* dst_offset = entry_data_offset();
262   src->ForEach(head, tail, [&](index_type index) {
263     *dst_pos++ = src->entry_end_pos(index);
264     CordRep* child = src->entry_child(index);
265     *dst_child++ = ref ? CordRep::Ref(child) : child;
266     *dst_offset++ = src->entry_data_offset(index);
267   });
268 }
269 
Copy(CordRepRing * rep,index_type head,index_type tail,size_t extra)270 CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head,
271                                index_type tail, size_t extra) {
272   CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra);
273   newrep->Fill<true>(rep, head, tail);
274   CordRep::Unref(rep);
275   return newrep;
276 }
277 
Mutable(CordRepRing * rep,size_t extra)278 CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) {
279   // Get current number of entries, and check for max capacity.
280   size_t entries = rep->entries();
281 
282   if (!rep->refcount.IsOne()) {
283     return Copy(rep, rep->head(), rep->tail(), extra);
284   } else if (entries + extra > rep->capacity()) {
285     const size_t min_grow = rep->capacity() + rep->capacity() / 2;
286     const size_t min_extra = (std::max)(extra, min_grow - entries);
287     CordRepRing* newrep = CordRepRing::New(entries, min_extra);
288     newrep->Fill<false>(rep, rep->head(), rep->tail());
289     CordRepRing::Delete(rep);
290     return newrep;
291   } else {
292     return rep;
293   }
294 }
295 
GetAppendBuffer(size_t size)296 Span<char> CordRepRing::GetAppendBuffer(size_t size) {
297   assert(refcount.IsOne());
298   index_type back = retreat(tail_);
299   CordRep* child = entry_child(back);
300   if (child->tag >= FLAT && child->refcount.IsOne()) {
301     size_t capacity = child->flat()->Capacity();
302     pos_type end_pos = entry_end_pos(back);
303     size_t data_offset = entry_data_offset(back);
304     size_t entry_length = Distance(entry_begin_pos(back), end_pos);
305     size_t used = data_offset + entry_length;
306     if (size_t n = (std::min)(capacity - used, size)) {
307       child->length = data_offset + entry_length + n;
308       entry_end_pos()[back] = end_pos + n;
309       this->length += n;
310       return {child->flat()->Data() + used, n};
311     }
312   }
313   return {nullptr, 0};
314 }
315 
GetPrependBuffer(size_t size)316 Span<char> CordRepRing::GetPrependBuffer(size_t size) {
317   assert(refcount.IsOne());
318   CordRep* child = entry_child(head_);
319   size_t data_offset = entry_data_offset(head_);
320   if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) {
321     size_t n = (std::min)(data_offset, size);
322     this->length += n;
323     begin_pos_ -= n;
324     data_offset -= n;
325     entry_data_offset()[head_] = static_cast<offset_type>(data_offset);
326     return {child->flat()->Data() + data_offset, n};
327   }
328   return {nullptr, 0};
329 }
330 
CreateFromLeaf(CordRep * child,size_t offset,size_t len,size_t extra)331 CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset,
332                                          size_t len, size_t extra) {
333   CordRepRing* rep = CordRepRing::New(1, extra);
334   rep->head_ = 0;
335   rep->tail_ = rep->advance(0);
336   rep->length = len;
337   rep->entry_end_pos()[0] = len;
338   rep->entry_child()[0] = child;
339   rep->entry_data_offset()[0] = static_cast<offset_type>(offset);
340   return Validate(rep);
341 }
342 
CreateSlow(CordRep * child,size_t extra)343 CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) {
344   CordRepRing* rep = nullptr;
345   Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
346     if (IsFlatOrExternal(child_arg)) {
347       rep = rep ? AppendLeaf(rep, child_arg, offset, len)
348                 : CreateFromLeaf(child_arg, offset, len, extra);
349     } else if (rep) {
350       rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
351     } else if (offset == 0 && child_arg->length == len) {
352       rep = Mutable(child_arg->ring(), extra);
353     } else {
354       rep = SubRing(child_arg->ring(), offset, len, extra);
355     }
356   });
357   return Validate(rep, nullptr, __LINE__);
358 }
359 
Create(CordRep * child,size_t extra)360 CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) {
361   size_t length = child->length;
362   if (IsFlatOrExternal(child)) {
363     return CreateFromLeaf(child, 0, length, extra);
364   }
365   if (child->IsRing()) {
366     return Mutable(child->ring(), extra);
367   }
368   return CreateSlow(child, extra);
369 }
370 
371 template <CordRepRing::AddMode mode>
AddRing(CordRepRing * rep,CordRepRing * ring,size_t offset,size_t len)372 CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring,
373                                   size_t offset, size_t len) {
374   assert(offset < ring->length);
375   constexpr bool append = mode == AddMode::kAppend;
376   Position head = ring->Find(offset);
377   Position tail = ring->FindTail(head.index, offset + len);
378   const index_type entries = ring->entries(head.index, tail.index);
379 
380   rep = Mutable(rep, entries);
381 
382   // The delta for making ring[head].end_pos into 'len - offset'
383   const pos_type delta_length =
384       (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) -
385       ring->entry_begin_pos(head.index) - head.offset;
386 
387   // Start filling at `tail`, or `entries` before `head`
388   Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries));
389 
390   if (ring->refcount.IsOne()) {
391     // Copy entries from source stealing the ref and adjusting the end position.
392     // Commit the filler as this is no-op.
393     ring->ForEach(head.index, tail.index, [&](index_type ix) {
394       filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix),
395                  ring->entry_end_pos(ix) + delta_length);
396     });
397 
398     // Unref entries we did not copy over, and delete source.
399     if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index);
400     if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_);
401     CordRepRing::Delete(ring);
402   } else {
403     ring->ForEach(head.index, tail.index, [&](index_type ix) {
404       CordRep* child = ring->entry_child(ix);
405       filler.Add(child, ring->entry_data_offset(ix),
406                  ring->entry_end_pos(ix) + delta_length);
407       CordRep::Ref(child);
408     });
409     CordRepRing::Unref(ring);
410   }
411 
412   if (head.offset) {
413     // Increase offset of first 'source' entry appended or prepended.
414     // This is always the entry in `filler.head()`
415     rep->AddDataOffset(filler.head(), head.offset);
416   }
417 
418   if (tail.offset) {
419     // Reduce length of last 'source' entry appended or prepended.
420     // This is always the entry tailed by `filler.pos()`
421     rep->SubLength(rep->retreat(filler.pos()), tail.offset);
422   }
423 
424   // Commit changes
425   rep->length += len;
426   if (append) {
427     rep->tail_ = filler.pos();
428   } else {
429     rep->head_ = filler.head();
430     rep->begin_pos_ -= len;
431   }
432 
433   return Validate(rep);
434 }
435 
AppendSlow(CordRepRing * rep,CordRep * child)436 CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) {
437   Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) {
438     if (child_arg->IsRing()) {
439       rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len);
440     } else {
441       rep = AppendLeaf(rep, child_arg, offset, len);
442     }
443   });
444   return rep;
445 }
446 
AppendLeaf(CordRepRing * rep,CordRep * child,size_t offset,size_t len)447 CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child,
448                                      size_t offset, size_t len) {
449   rep = Mutable(rep, 1);
450   index_type back = rep->tail_;
451   const pos_type begin_pos = rep->begin_pos_ + rep->length;
452   rep->tail_ = rep->advance(rep->tail_);
453   rep->length += len;
454   rep->entry_end_pos()[back] = begin_pos + len;
455   rep->entry_child()[back] = child;
456   rep->entry_data_offset()[back] = static_cast<offset_type>(offset);
457   return Validate(rep, nullptr, __LINE__);
458 }
459 
Append(CordRepRing * rep,CordRep * child)460 CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) {
461   size_t length = child->length;
462   if (IsFlatOrExternal(child)) {
463     return AppendLeaf(rep, child, 0, length);
464   }
465   if (child->IsRing()) {
466     return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length);
467   }
468   return AppendSlow(rep, child);
469 }
470 
PrependSlow(CordRepRing * rep,CordRep * child)471 CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) {
472   ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
473     if (IsFlatOrExternal(child_arg)) {
474       rep = PrependLeaf(rep, child_arg, offset, len);
475     } else {
476       rep = AddRing<AddMode::kPrepend>(rep, child_arg->ring(), offset, len);
477     }
478   });
479   return Validate(rep);
480 }
481 
PrependLeaf(CordRepRing * rep,CordRep * child,size_t offset,size_t len)482 CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child,
483                                       size_t offset, size_t len) {
484   rep = Mutable(rep, 1);
485   index_type head = rep->retreat(rep->head_);
486   pos_type end_pos = rep->begin_pos_;
487   rep->head_ = head;
488   rep->length += len;
489   rep->begin_pos_ -= len;
490   rep->entry_end_pos()[head] = end_pos;
491   rep->entry_child()[head] = child;
492   rep->entry_data_offset()[head] = static_cast<offset_type>(offset);
493   return Validate(rep);
494 }
495 
Prepend(CordRepRing * rep,CordRep * child)496 CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) {
497   size_t length = child->length;
498   if (IsFlatOrExternal(child)) {
499     return PrependLeaf(rep, child, 0, length);
500   }
501   if (child->IsRing()) {
502     return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length);
503   }
504   return PrependSlow(rep, child);
505 }
506 
Append(CordRepRing * rep,absl::string_view data,size_t extra)507 CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data,
508                                  size_t extra) {
509   if (rep->refcount.IsOne()) {
510     Span<char> avail = rep->GetAppendBuffer(data.length());
511     if (!avail.empty()) {
512       memcpy(avail.data(), data.data(), avail.length());
513       data.remove_prefix(avail.length());
514     }
515   }
516   if (data.empty()) return Validate(rep);
517 
518   const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
519   rep = Mutable(rep, flats);
520 
521   Filler filler(rep, rep->tail_);
522   pos_type pos = rep->begin_pos_ + rep->length;
523 
524   while (data.length() >= kMaxFlatLength) {
525     auto* flat = CreateFlat(data.data(), kMaxFlatLength);
526     filler.Add(flat, 0, pos += kMaxFlatLength);
527     data.remove_prefix(kMaxFlatLength);
528   }
529 
530   if (data.length()) {
531     auto* flat = CreateFlat(data.data(), data.length(), extra);
532     filler.Add(flat, 0, pos += data.length());
533   }
534 
535   rep->length = pos - rep->begin_pos_;
536   rep->tail_ = filler.pos();
537 
538   return Validate(rep);
539 }
540 
Prepend(CordRepRing * rep,absl::string_view data,size_t extra)541 CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data,
542                                   size_t extra) {
543   if (rep->refcount.IsOne()) {
544     Span<char> avail = rep->GetPrependBuffer(data.length());
545     if (!avail.empty()) {
546       const char* tail = data.data() + data.length() - avail.length();
547       memcpy(avail.data(), tail, avail.length());
548       data.remove_suffix(avail.length());
549     }
550   }
551   if (data.empty()) return rep;
552 
553   const size_t flats = (data.length() - 1) / kMaxFlatLength + 1;
554   rep = Mutable(rep, flats);
555   pos_type pos = rep->begin_pos_;
556   Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats)));
557 
558   size_t first_size = data.size() - (flats - 1) * kMaxFlatLength;
559   CordRepFlat* flat = CordRepFlat::New(first_size + extra);
560   flat->length = first_size + extra;
561   memcpy(flat->Data() + extra, data.data(), first_size);
562   data.remove_prefix(first_size);
563   filler.Add(flat, extra, pos);
564   pos -= first_size;
565 
566   while (!data.empty()) {
567     assert(data.size() >= kMaxFlatLength);
568     flat = CreateFlat(data.data(), kMaxFlatLength);
569     filler.Add(flat, 0, pos);
570     pos -= kMaxFlatLength;
571     data.remove_prefix(kMaxFlatLength);
572   }
573 
574   rep->head_ = filler.head();
575   rep->length += rep->begin_pos_ - pos;
576   rep->begin_pos_ = pos;
577 
578   return Validate(rep);
579 }
580 
581 // 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86
582 static constexpr index_type kBinarySearchThreshold = 32;
583 static constexpr index_type kBinarySearchEndCount = 8;
584 
585 template <bool wrap>
FindBinary(index_type head,index_type tail,size_t offset) const586 CordRepRing::index_type CordRepRing::FindBinary(index_type head,
587                                                 index_type tail,
588                                                 size_t offset) const {
589   index_type count = tail + (wrap ? capacity_ : 0) - head;
590   do {
591     count = (count - 1) / 2;
592     assert(count < entries(head, tail_));
593     index_type mid = wrap ? advance(head, count) : head + count;
594     index_type after_mid = wrap ? advance(mid) : mid + 1;
595     bool larger = (offset >= entry_end_offset(mid));
596     head = larger ? after_mid : head;
597     tail = larger ? tail : mid;
598     assert(head != tail);
599   } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount));
600   return head;
601 }
602 
FindSlow(index_type head,size_t offset) const603 CordRepRing::Position CordRepRing::FindSlow(index_type head,
604                                             size_t offset) const {
605   index_type tail = tail_;
606 
607   // Binary search until we are good for linear search
608   // Optimize for branchless / non wrapping ops
609   if (tail > head) {
610     index_type count = tail - head;
611     if (count > kBinarySearchThreshold) {
612       head = FindBinary<false>(head, tail, offset);
613     }
614   } else {
615     index_type count = capacity_ + tail - head;
616     if (count > kBinarySearchThreshold) {
617       head = FindBinary<true>(head, tail, offset);
618     }
619   }
620 
621   pos_type pos = entry_begin_pos(head);
622   pos_type end_pos = entry_end_pos(head);
623   while (offset >= Distance(begin_pos_, end_pos)) {
624     head = advance(head);
625     pos = end_pos;
626     end_pos = entry_end_pos(head);
627   }
628 
629   return {head, offset - Distance(begin_pos_, pos)};
630 }
631 
FindTailSlow(index_type head,size_t offset) const632 CordRepRing::Position CordRepRing::FindTailSlow(index_type head,
633                                                 size_t offset) const {
634   index_type tail = tail_;
635   const size_t tail_offset = offset - 1;
636 
637   // Binary search until we are good for linear search
638   // Optimize for branchless / non wrapping ops
639   if (tail > head) {
640     index_type count = tail - head;
641     if (count > kBinarySearchThreshold) {
642       head = FindBinary<false>(head, tail, tail_offset);
643     }
644   } else {
645     index_type count = capacity_ + tail - head;
646     if (count > kBinarySearchThreshold) {
647       head = FindBinary<true>(head, tail, tail_offset);
648     }
649   }
650 
651   size_t end_offset = entry_end_offset(head);
652   while (tail_offset >= end_offset) {
653     head = advance(head);
654     end_offset = entry_end_offset(head);
655   }
656 
657   return {advance(head), end_offset - offset};
658 }
659 
GetCharacter(size_t offset) const660 char CordRepRing::GetCharacter(size_t offset) const {
661   assert(offset < length);
662 
663   Position pos = Find(offset);
664   size_t data_offset = entry_data_offset(pos.index) + pos.offset;
665   return GetRepData(entry_child(pos.index))[data_offset];
666 }
667 
SubRing(CordRepRing * rep,size_t offset,size_t len,size_t extra)668 CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset,
669                                   size_t len, size_t extra) {
670   assert(offset <= rep->length);
671   assert(offset <= rep->length - len);
672 
673   if (len == 0) {
674     CordRep::Unref(rep);
675     return nullptr;
676   }
677 
678   // Find position of first byte
679   Position head = rep->Find(offset);
680   Position tail = rep->FindTail(head.index, offset + len);
681   const size_t new_entries = rep->entries(head.index, tail.index);
682 
683   if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) {
684     // We adopt a privately owned rep and no extra entries needed.
685     if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
686     if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
687     rep->head_ = head.index;
688     rep->tail_ = tail.index;
689   } else {
690     // Copy subset to new rep
691     rep = Copy(rep, head.index, tail.index, extra);
692     head.index = rep->head_;
693     tail.index = rep->tail_;
694   }
695 
696   // Adjust begin_pos and length
697   rep->length = len;
698   rep->begin_pos_ += offset;
699 
700   // Adjust head and tail blocks
701   if (head.offset) {
702     rep->AddDataOffset(head.index, head.offset);
703   }
704   if (tail.offset) {
705     rep->SubLength(rep->retreat(tail.index), tail.offset);
706   }
707 
708   return Validate(rep);
709 }
710 
RemovePrefix(CordRepRing * rep,size_t len,size_t extra)711 CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len,
712                                        size_t extra) {
713   assert(len <= rep->length);
714   if (len == rep->length) {
715     CordRep::Unref(rep);
716     return nullptr;
717   }
718 
719   Position head = rep->Find(len);
720   if (rep->refcount.IsOne()) {
721     if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index);
722     rep->head_ = head.index;
723   } else {
724     rep = Copy(rep, head.index, rep->tail_, extra);
725     head.index = rep->head_;
726   }
727 
728   // Adjust begin_pos and length
729   rep->length -= len;
730   rep->begin_pos_ += len;
731 
732   // Adjust head block
733   if (head.offset) {
734     rep->AddDataOffset(head.index, head.offset);
735   }
736 
737   return Validate(rep);
738 }
739 
RemoveSuffix(CordRepRing * rep,size_t len,size_t extra)740 CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len,
741                                        size_t extra) {
742   assert(len <= rep->length);
743 
744   if (len == rep->length) {
745     CordRep::Unref(rep);
746     return nullptr;
747   }
748 
749   Position tail = rep->FindTail(rep->length - len);
750   if (rep->refcount.IsOne()) {
751     // We adopt a privately owned rep, scrub.
752     if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_);
753     rep->tail_ = tail.index;
754   } else {
755     // Copy subset to new rep
756     rep = Copy(rep, rep->head_, tail.index, extra);
757     tail.index = rep->tail_;
758   }
759 
760   // Adjust length
761   rep->length -= len;
762 
763   // Adjust tail block
764   if (tail.offset) {
765     rep->SubLength(rep->retreat(tail.index), tail.offset);
766   }
767 
768   return Validate(rep);
769 }
770 
771 }  // namespace cord_internal
772 ABSL_NAMESPACE_END
773 }  // namespace absl
774