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