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