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