1 // Copyright 2020 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/strings/cord.h"
16
17 #include <algorithm>
18 #include <atomic>
19 #include <cstddef>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <iomanip>
23 #include <ios>
24 #include <iostream>
25 #include <limits>
26 #include <ostream>
27 #include <sstream>
28 #include <type_traits>
29 #include <unordered_set>
30 #include <vector>
31
32 #include "absl/base/casts.h"
33 #include "absl/base/internal/raw_logging.h"
34 #include "absl/base/macros.h"
35 #include "absl/base/port.h"
36 #include "absl/container/fixed_array.h"
37 #include "absl/container/inlined_vector.h"
38 #include "absl/strings/cord_buffer.h"
39 #include "absl/strings/escaping.h"
40 #include "absl/strings/internal/cord_data_edge.h"
41 #include "absl/strings/internal/cord_internal.h"
42 #include "absl/strings/internal/cord_rep_btree.h"
43 #include "absl/strings/internal/cord_rep_crc.h"
44 #include "absl/strings/internal/cord_rep_flat.h"
45 #include "absl/strings/internal/cordz_statistics.h"
46 #include "absl/strings/internal/cordz_update_scope.h"
47 #include "absl/strings/internal/cordz_update_tracker.h"
48 #include "absl/strings/internal/resize_uninitialized.h"
49 #include "absl/strings/str_cat.h"
50 #include "absl/strings/str_format.h"
51 #include "absl/strings/str_join.h"
52 #include "absl/strings/string_view.h"
53
54 namespace absl {
55 ABSL_NAMESPACE_BEGIN
56
57 using ::absl::cord_internal::CordRep;
58 using ::absl::cord_internal::CordRepBtree;
59 using ::absl::cord_internal::CordRepCrc;
60 using ::absl::cord_internal::CordRepExternal;
61 using ::absl::cord_internal::CordRepFlat;
62 using ::absl::cord_internal::CordRepSubstring;
63 using ::absl::cord_internal::CordzUpdateTracker;
64 using ::absl::cord_internal::InlineData;
65 using ::absl::cord_internal::kMaxFlatLength;
66 using ::absl::cord_internal::kMinFlatLength;
67
68 using ::absl::cord_internal::kInlinedVectorSize;
69 using ::absl::cord_internal::kMaxBytesToCopy;
70
71 static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
72 int indent = 0);
73 static bool VerifyNode(CordRep* root, CordRep* start_node,
74 bool full_validation);
75
VerifyTree(CordRep * node)76 static inline CordRep* VerifyTree(CordRep* node) {
77 // Verification is expensive, so only do it in debug mode.
78 // Even in debug mode we normally do only light validation.
79 // If you are debugging Cord itself, you should define the
80 // macro EXTRA_CORD_VALIDATION, e.g. by adding
81 // --copt=-DEXTRA_CORD_VALIDATION to the blaze line.
82 #ifdef EXTRA_CORD_VALIDATION
83 assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/true));
84 #else // EXTRA_CORD_VALIDATION
85 assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/false));
86 #endif // EXTRA_CORD_VALIDATION
87 static_cast<void>(&VerifyNode);
88
89 return node;
90 }
91
CreateFlat(const char * data,size_t length,size_t alloc_hint)92 static CordRepFlat* CreateFlat(const char* data, size_t length,
93 size_t alloc_hint) {
94 CordRepFlat* flat = CordRepFlat::New(length + alloc_hint);
95 flat->length = length;
96 memcpy(flat->Data(), data, length);
97 return flat;
98 }
99
100 // Creates a new flat or Btree out of the specified array.
101 // The returned node has a refcount of 1.
NewBtree(const char * data,size_t length,size_t alloc_hint)102 static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) {
103 if (length <= kMaxFlatLength) {
104 return CreateFlat(data, length, alloc_hint);
105 }
106 CordRepFlat* flat = CreateFlat(data, kMaxFlatLength, 0);
107 data += kMaxFlatLength;
108 length -= kMaxFlatLength;
109 auto* root = CordRepBtree::Create(flat);
110 return CordRepBtree::Append(root, {data, length}, alloc_hint);
111 }
112
113 // Create a new tree out of the specified array.
114 // The returned node has a refcount of 1.
NewTree(const char * data,size_t length,size_t alloc_hint)115 static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) {
116 if (length == 0) return nullptr;
117 return NewBtree(data, length, alloc_hint);
118 }
119
120 namespace cord_internal {
121
InitializeCordRepExternal(absl::string_view data,CordRepExternal * rep)122 void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) {
123 assert(!data.empty());
124 rep->length = data.size();
125 rep->tag = EXTERNAL;
126 rep->base = data.data();
127 VerifyTree(rep);
128 }
129
130 } // namespace cord_internal
131
132 // Creates a CordRep from the provided string. If the string is large enough,
133 // and not wasteful, we move the string into an external cord rep, preserving
134 // the already allocated string contents.
135 // Requires the provided string length to be larger than `kMaxInline`.
CordRepFromString(std::string && src)136 static CordRep* CordRepFromString(std::string&& src) {
137 assert(src.length() > cord_internal::kMaxInline);
138 if (
139 // String is short: copy data to avoid external block overhead.
140 src.size() <= kMaxBytesToCopy ||
141 // String is wasteful: copy data to avoid pinning too much unused memory.
142 src.size() < src.capacity() / 2
143 ) {
144 return NewTree(src.data(), src.size(), 0);
145 }
146
147 struct StringReleaser {
148 void operator()(absl::string_view /* data */) {}
149 std::string data;
150 };
151 const absl::string_view original_data = src;
152 auto* rep =
153 static_cast<::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
154 absl::cord_internal::NewExternalRep(original_data,
155 StringReleaser{std::move(src)}));
156 // Moving src may have invalidated its data pointer, so adjust it.
157 rep->base = rep->template get<0>().data.data();
158 return rep;
159 }
160
161 // --------------------------------------------------------------------
162 // Cord::InlineRep functions
163
164 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
165 constexpr unsigned char Cord::InlineRep::kMaxInline;
166 #endif
167
set_data(const char * data,size_t n)168 inline void Cord::InlineRep::set_data(const char* data, size_t n) {
169 static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
170
171 cord_internal::SmallMemmove<true>(data_.as_chars(), data, n);
172 set_inline_size(n);
173 }
174
set_data(size_t n)175 inline char* Cord::InlineRep::set_data(size_t n) {
176 assert(n <= kMaxInline);
177 ResetToEmpty();
178 set_inline_size(n);
179 return data_.as_chars();
180 }
181
reduce_size(size_t n)182 inline void Cord::InlineRep::reduce_size(size_t n) {
183 size_t tag = inline_size();
184 assert(tag <= kMaxInline);
185 assert(tag >= n);
186 tag -= n;
187 memset(data_.as_chars() + tag, 0, n);
188 set_inline_size(tag);
189 }
190
remove_prefix(size_t n)191 inline void Cord::InlineRep::remove_prefix(size_t n) {
192 cord_internal::SmallMemmove(data_.as_chars(), data_.as_chars() + n,
193 inline_size() - n);
194 reduce_size(n);
195 }
196
197 // Returns `rep` converted into a CordRepBtree.
198 // Directly returns `rep` if `rep` is already a CordRepBtree.
ForceBtree(CordRep * rep)199 static CordRepBtree* ForceBtree(CordRep* rep) {
200 return rep->IsBtree()
201 ? rep->btree()
202 : CordRepBtree::Create(cord_internal::RemoveCrcNode(rep));
203 }
204
AppendTreeToInlined(CordRep * tree,MethodIdentifier method)205 void Cord::InlineRep::AppendTreeToInlined(CordRep* tree,
206 MethodIdentifier method) {
207 assert(!is_tree());
208 if (!data_.is_empty()) {
209 CordRepFlat* flat = MakeFlatWithExtraCapacity(0);
210 tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree);
211 }
212 EmplaceTree(tree, method);
213 }
214
AppendTreeToTree(CordRep * tree,MethodIdentifier method)215 void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) {
216 assert(is_tree());
217 const CordzUpdateScope scope(data_.cordz_info(), method);
218 tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree);
219 SetTree(tree, scope);
220 }
221
AppendTree(CordRep * tree,MethodIdentifier method)222 void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) {
223 assert(tree != nullptr);
224 assert(tree->length != 0);
225 assert(!tree->IsCrc());
226 if (data_.is_tree()) {
227 AppendTreeToTree(tree, method);
228 } else {
229 AppendTreeToInlined(tree, method);
230 }
231 }
232
PrependTreeToInlined(CordRep * tree,MethodIdentifier method)233 void Cord::InlineRep::PrependTreeToInlined(CordRep* tree,
234 MethodIdentifier method) {
235 assert(!is_tree());
236 if (!data_.is_empty()) {
237 CordRepFlat* flat = MakeFlatWithExtraCapacity(0);
238 tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree);
239 }
240 EmplaceTree(tree, method);
241 }
242
PrependTreeToTree(CordRep * tree,MethodIdentifier method)243 void Cord::InlineRep::PrependTreeToTree(CordRep* tree,
244 MethodIdentifier method) {
245 assert(is_tree());
246 const CordzUpdateScope scope(data_.cordz_info(), method);
247 tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree);
248 SetTree(tree, scope);
249 }
250
PrependTree(CordRep * tree,MethodIdentifier method)251 void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) {
252 assert(tree != nullptr);
253 assert(tree->length != 0);
254 assert(!tree->IsCrc());
255 if (data_.is_tree()) {
256 PrependTreeToTree(tree, method);
257 } else {
258 PrependTreeToInlined(tree, method);
259 }
260 }
261
262 // Searches for a non-full flat node at the rightmost leaf of the tree. If a
263 // suitable leaf is found, the function will update the length field for all
264 // nodes to account for the size increase. The append region address will be
265 // written to region and the actual size increase will be written to size.
PrepareAppendRegion(CordRep * root,char ** region,size_t * size,size_t max_length)266 static inline bool PrepareAppendRegion(CordRep* root, char** region,
267 size_t* size, size_t max_length) {
268 if (root->IsBtree() && root->refcount.IsOne()) {
269 Span<char> span = root->btree()->GetAppendBuffer(max_length);
270 if (!span.empty()) {
271 *region = span.data();
272 *size = span.size();
273 return true;
274 }
275 }
276
277 CordRep* dst = root;
278 if (!dst->IsFlat() || !dst->refcount.IsOne()) {
279 *region = nullptr;
280 *size = 0;
281 return false;
282 }
283
284 const size_t in_use = dst->length;
285 const size_t capacity = dst->flat()->Capacity();
286 if (in_use == capacity) {
287 *region = nullptr;
288 *size = 0;
289 return false;
290 }
291
292 const size_t size_increase = std::min(capacity - in_use, max_length);
293 dst->length += size_increase;
294
295 *region = dst->flat()->Data() + in_use;
296 *size = size_increase;
297 return true;
298 }
299
AssignSlow(const Cord::InlineRep & src)300 void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
301 assert(&src != this);
302 assert(is_tree() || src.is_tree());
303 auto constexpr method = CordzUpdateTracker::kAssignCord;
304 if (ABSL_PREDICT_TRUE(!is_tree())) {
305 EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method);
306 return;
307 }
308
309 CordRep* tree = as_tree();
310 if (CordRep* src_tree = src.tree()) {
311 // Leave any existing `cordz_info` in place, and let MaybeTrackCord()
312 // decide if this cord should be (or remains to be) sampled or not.
313 data_.set_tree(CordRep::Ref(src_tree));
314 CordzInfo::MaybeTrackCord(data_, src.data_, method);
315 } else {
316 CordzInfo::MaybeUntrackCord(data_.cordz_info());
317 data_ = src.data_;
318 }
319 CordRep::Unref(tree);
320 }
321
UnrefTree()322 void Cord::InlineRep::UnrefTree() {
323 if (is_tree()) {
324 CordzInfo::MaybeUntrackCord(data_.cordz_info());
325 CordRep::Unref(tree());
326 }
327 }
328
329 // --------------------------------------------------------------------
330 // Constructors and destructors
331
Cord(absl::string_view src,MethodIdentifier method)332 Cord::Cord(absl::string_view src, MethodIdentifier method)
333 : contents_(InlineData::kDefaultInit) {
334 const size_t n = src.size();
335 if (n <= InlineRep::kMaxInline) {
336 contents_.set_data(src.data(), n);
337 } else {
338 CordRep* rep = NewTree(src.data(), n, 0);
339 contents_.EmplaceTree(rep, method);
340 }
341 }
342
343 template <typename T, Cord::EnableIfString<T>>
Cord(T && src)344 Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) {
345 if (src.size() <= InlineRep::kMaxInline) {
346 contents_.set_data(src.data(), src.size());
347 } else {
348 CordRep* rep = CordRepFromString(std::forward<T>(src));
349 contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString);
350 }
351 }
352
353 template Cord::Cord(std::string&& src);
354
355 // The destruction code is separate so that the compiler can determine
356 // that it does not need to call the destructor on a moved-from Cord.
DestroyCordSlow()357 void Cord::DestroyCordSlow() {
358 assert(contents_.is_tree());
359 CordzInfo::MaybeUntrackCord(contents_.cordz_info());
360 CordRep::Unref(VerifyTree(contents_.as_tree()));
361 }
362
363 // --------------------------------------------------------------------
364 // Mutators
365
Clear()366 void Cord::Clear() {
367 if (CordRep* tree = contents_.clear()) {
368 CordRep::Unref(tree);
369 }
370 }
371
AssignLargeString(std::string && src)372 Cord& Cord::AssignLargeString(std::string&& src) {
373 auto constexpr method = CordzUpdateTracker::kAssignString;
374 assert(src.size() > kMaxBytesToCopy);
375 CordRep* rep = CordRepFromString(std::move(src));
376 if (CordRep* tree = contents_.tree()) {
377 CordzUpdateScope scope(contents_.cordz_info(), method);
378 contents_.SetTree(rep, scope);
379 CordRep::Unref(tree);
380 } else {
381 contents_.EmplaceTree(rep, method);
382 }
383 return *this;
384 }
385
operator =(absl::string_view src)386 Cord& Cord::operator=(absl::string_view src) {
387 auto constexpr method = CordzUpdateTracker::kAssignString;
388 const char* data = src.data();
389 size_t length = src.size();
390 CordRep* tree = contents_.tree();
391 if (length <= InlineRep::kMaxInline) {
392 // Embed into this->contents_, which is somewhat subtle:
393 // - MaybeUntrackCord must be called before Unref(tree).
394 // - MaybeUntrackCord must be called before set_data() clobbers cordz_info.
395 // - set_data() must be called before Unref(tree) as it may reference tree.
396 if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info());
397 contents_.set_data(data, length);
398 if (tree != nullptr) CordRep::Unref(tree);
399 return *this;
400 }
401 if (tree != nullptr) {
402 CordzUpdateScope scope(contents_.cordz_info(), method);
403 if (tree->IsFlat() && tree->flat()->Capacity() >= length &&
404 tree->refcount.IsOne()) {
405 // Copy in place if the existing FLAT node is reusable.
406 memmove(tree->flat()->Data(), data, length);
407 tree->length = length;
408 VerifyTree(tree);
409 return *this;
410 }
411 contents_.SetTree(NewTree(data, length, 0), scope);
412 CordRep::Unref(tree);
413 } else {
414 contents_.EmplaceTree(NewTree(data, length, 0), method);
415 }
416 return *this;
417 }
418
419 // TODO(sanjay): Move to Cord::InlineRep section of file. For now,
420 // we keep it here to make diffs easier.
AppendArray(absl::string_view src,MethodIdentifier method)421 void Cord::InlineRep::AppendArray(absl::string_view src,
422 MethodIdentifier method) {
423 MaybeRemoveEmptyCrcNode();
424 if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
425
426 size_t appended = 0;
427 CordRep* rep = tree();
428 const CordRep* const root = rep;
429 CordzUpdateScope scope(root ? cordz_info() : nullptr, method);
430 if (root != nullptr) {
431 rep = cord_internal::RemoveCrcNode(rep);
432 char* region;
433 if (PrepareAppendRegion(rep, ®ion, &appended, src.size())) {
434 memcpy(region, src.data(), appended);
435 }
436 } else {
437 // Try to fit in the inline buffer if possible.
438 size_t inline_length = inline_size();
439 if (src.size() <= kMaxInline - inline_length) {
440 // Append new data to embedded array
441 memcpy(data_.as_chars() + inline_length, src.data(), src.size());
442 set_inline_size(inline_length + src.size());
443 return;
444 }
445
446 // Allocate flat to be a perfect fit on first append exceeding inlined size.
447 // Subsequent growth will use amortized growth until we reach maximum flat
448 // size.
449 rep = CordRepFlat::New(inline_length + src.size());
450 appended = std::min(src.size(), rep->flat()->Capacity() - inline_length);
451 memcpy(rep->flat()->Data(), data_.as_chars(), inline_length);
452 memcpy(rep->flat()->Data() + inline_length, src.data(), appended);
453 rep->length = inline_length + appended;
454 }
455
456 src.remove_prefix(appended);
457 if (src.empty()) {
458 CommitTree(root, rep, scope, method);
459 return;
460 }
461
462 // TODO(b/192061034): keep legacy 10% growth rate: consider other rates.
463 rep = ForceBtree(rep);
464 const size_t min_growth = std::max<size_t>(rep->length / 10, src.size());
465 rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size());
466
467 CommitTree(root, rep, scope, method);
468 }
469
TakeRep() const470 inline CordRep* Cord::TakeRep() const& {
471 return CordRep::Ref(contents_.tree());
472 }
473
TakeRep()474 inline CordRep* Cord::TakeRep() && {
475 CordRep* rep = contents_.tree();
476 contents_.clear();
477 return rep;
478 }
479
480 template <typename C>
AppendImpl(C && src)481 inline void Cord::AppendImpl(C&& src) {
482 auto constexpr method = CordzUpdateTracker::kAppendCord;
483
484 contents_.MaybeRemoveEmptyCrcNode();
485 if (src.empty()) return;
486
487 if (empty()) {
488 // Since destination is empty, we can avoid allocating a node,
489 if (src.contents_.is_tree()) {
490 // by taking the tree directly
491 CordRep* rep =
492 cord_internal::RemoveCrcNode(std::forward<C>(src).TakeRep());
493 contents_.EmplaceTree(rep, method);
494 } else {
495 // or copying over inline data
496 contents_.data_ = src.contents_.data_;
497 }
498 return;
499 }
500
501 // For short cords, it is faster to copy data if there is room in dst.
502 const size_t src_size = src.contents_.size();
503 if (src_size <= kMaxBytesToCopy) {
504 CordRep* src_tree = src.contents_.tree();
505 if (src_tree == nullptr) {
506 // src has embedded data.
507 contents_.AppendArray({src.contents_.data(), src_size}, method);
508 return;
509 }
510 if (src_tree->IsFlat()) {
511 // src tree just has one flat node.
512 contents_.AppendArray({src_tree->flat()->Data(), src_size}, method);
513 return;
514 }
515 if (&src == this) {
516 // ChunkIterator below assumes that src is not modified during traversal.
517 Append(Cord(src));
518 return;
519 }
520 // TODO(mec): Should we only do this if "dst" has space?
521 for (absl::string_view chunk : src.Chunks()) {
522 Append(chunk);
523 }
524 return;
525 }
526
527 // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize)
528 CordRep* rep = cord_internal::RemoveCrcNode(std::forward<C>(src).TakeRep());
529 contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord);
530 }
531
ExtractAppendBuffer(CordRep * rep,size_t min_capacity)532 static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep,
533 size_t min_capacity) {
534 switch (rep->tag) {
535 case cord_internal::BTREE:
536 return CordRepBtree::ExtractAppendBuffer(rep->btree(), min_capacity);
537 default:
538 if (rep->IsFlat() && rep->refcount.IsOne() &&
539 rep->flat()->Capacity() - rep->length >= min_capacity) {
540 return {nullptr, rep};
541 }
542 return {rep, nullptr};
543 }
544 }
545
CreateAppendBuffer(InlineData & data,size_t block_size,size_t capacity)546 static CordBuffer CreateAppendBuffer(InlineData& data, size_t block_size,
547 size_t capacity) {
548 // Watch out for overflow, people can ask for size_t::max().
549 const size_t size = data.inline_size();
550 const size_t max_capacity = std::numeric_limits<size_t>::max() - size;
551 capacity = (std::min)(max_capacity, capacity) + size;
552 CordBuffer buffer =
553 block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity)
554 : CordBuffer::CreateWithDefaultLimit(capacity);
555 cord_internal::SmallMemmove(buffer.data(), data.as_chars(), size);
556 buffer.SetLength(size);
557 data = {};
558 return buffer;
559 }
560
GetAppendBufferSlowPath(size_t block_size,size_t capacity,size_t min_capacity)561 CordBuffer Cord::GetAppendBufferSlowPath(size_t block_size, size_t capacity,
562 size_t min_capacity) {
563 auto constexpr method = CordzUpdateTracker::kGetAppendBuffer;
564 CordRep* tree = contents_.tree();
565 if (tree != nullptr) {
566 CordzUpdateScope scope(contents_.cordz_info(), method);
567 CordRep::ExtractResult result = ExtractAppendBuffer(tree, min_capacity);
568 if (result.extracted != nullptr) {
569 contents_.SetTreeOrEmpty(result.tree, scope);
570 return CordBuffer(result.extracted->flat());
571 }
572 return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity)
573 : CordBuffer::CreateWithDefaultLimit(capacity);
574 }
575 return CreateAppendBuffer(contents_.data_, block_size, capacity);
576 }
577
Append(const Cord & src)578 void Cord::Append(const Cord& src) {
579 AppendImpl(src);
580 }
581
Append(Cord && src)582 void Cord::Append(Cord&& src) {
583 AppendImpl(std::move(src));
584 }
585
586 template <typename T, Cord::EnableIfString<T>>
Append(T && src)587 void Cord::Append(T&& src) {
588 if (src.size() <= kMaxBytesToCopy) {
589 Append(absl::string_view(src));
590 } else {
591 CordRep* rep = CordRepFromString(std::forward<T>(src));
592 contents_.AppendTree(rep, CordzUpdateTracker::kAppendString);
593 }
594 }
595
596 template void Cord::Append(std::string&& src);
597
Prepend(const Cord & src)598 void Cord::Prepend(const Cord& src) {
599 contents_.MaybeRemoveEmptyCrcNode();
600 if (src.empty()) return;
601
602 CordRep* src_tree = src.contents_.tree();
603 if (src_tree != nullptr) {
604 CordRep::Ref(src_tree);
605 contents_.PrependTree(cord_internal::RemoveCrcNode(src_tree),
606 CordzUpdateTracker::kPrependCord);
607 return;
608 }
609
610 // `src` cord is inlined.
611 absl::string_view src_contents(src.contents_.data(), src.contents_.size());
612 return Prepend(src_contents);
613 }
614
PrependArray(absl::string_view src,MethodIdentifier method)615 void Cord::PrependArray(absl::string_view src, MethodIdentifier method) {
616 contents_.MaybeRemoveEmptyCrcNode();
617 if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
618
619 if (!contents_.is_tree()) {
620 size_t cur_size = contents_.inline_size();
621 if (cur_size + src.size() <= InlineRep::kMaxInline) {
622 // Use embedded storage.
623 InlineData data;
624 memcpy(data.as_chars(), src.data(), src.size());
625 memcpy(data.as_chars() + src.size(), contents_.data(), cur_size);
626 data.set_inline_size(cur_size + src.size());
627 contents_.data_ = data;
628 return;
629 }
630 }
631 CordRep* rep = NewTree(src.data(), src.size(), 0);
632 contents_.PrependTree(rep, method);
633 }
634
AppendPrecise(absl::string_view src,MethodIdentifier method)635 void Cord::AppendPrecise(absl::string_view src, MethodIdentifier method) {
636 assert(!src.empty());
637 assert(src.size() <= cord_internal::kMaxFlatLength);
638 if (contents_.remaining_inline_capacity() >= src.size()) {
639 const size_t inline_length = contents_.inline_size();
640 memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size());
641 contents_.set_inline_size(inline_length + src.size());
642 } else {
643 contents_.AppendTree(CordRepFlat::Create(src), method);
644 }
645 }
646
PrependPrecise(absl::string_view src,MethodIdentifier method)647 void Cord::PrependPrecise(absl::string_view src, MethodIdentifier method) {
648 assert(!src.empty());
649 assert(src.size() <= cord_internal::kMaxFlatLength);
650 if (contents_.remaining_inline_capacity() >= src.size()) {
651 const size_t cur_size = contents_.inline_size();
652 InlineData data;
653 memcpy(data.as_chars(), src.data(), src.size());
654 memcpy(data.as_chars() + src.size(), contents_.data(), cur_size);
655 data.set_inline_size(cur_size + src.size());
656 contents_.data_ = data;
657 } else {
658 contents_.PrependTree(CordRepFlat::Create(src), method);
659 }
660 }
661
662 template <typename T, Cord::EnableIfString<T>>
Prepend(T && src)663 inline void Cord::Prepend(T&& src) {
664 if (src.size() <= kMaxBytesToCopy) {
665 Prepend(absl::string_view(src));
666 } else {
667 CordRep* rep = CordRepFromString(std::forward<T>(src));
668 contents_.PrependTree(rep, CordzUpdateTracker::kPrependString);
669 }
670 }
671
672 template void Cord::Prepend(std::string&& src);
673
RemovePrefix(size_t n)674 void Cord::RemovePrefix(size_t n) {
675 ABSL_INTERNAL_CHECK(n <= size(),
676 absl::StrCat("Requested prefix size ", n,
677 " exceeds Cord's size ", size()));
678 contents_.MaybeRemoveEmptyCrcNode();
679 CordRep* tree = contents_.tree();
680 if (tree == nullptr) {
681 contents_.remove_prefix(n);
682 } else {
683 auto constexpr method = CordzUpdateTracker::kRemovePrefix;
684 CordzUpdateScope scope(contents_.cordz_info(), method);
685 tree = cord_internal::RemoveCrcNode(tree);
686 if (n >= tree->length) {
687 CordRep::Unref(tree);
688 tree = nullptr;
689 } else if (tree->IsBtree()) {
690 CordRep* old = tree;
691 tree = tree->btree()->SubTree(n, tree->length - n);
692 CordRep::Unref(old);
693 } else if (tree->IsSubstring() && tree->refcount.IsOne()) {
694 tree->substring()->start += n;
695 tree->length -= n;
696 } else {
697 CordRep* rep = CordRepSubstring::Substring(tree, n, tree->length - n);
698 CordRep::Unref(tree);
699 tree = rep;
700 }
701 contents_.SetTreeOrEmpty(tree, scope);
702 }
703 }
704
RemoveSuffix(size_t n)705 void Cord::RemoveSuffix(size_t n) {
706 ABSL_INTERNAL_CHECK(n <= size(),
707 absl::StrCat("Requested suffix size ", n,
708 " exceeds Cord's size ", size()));
709 contents_.MaybeRemoveEmptyCrcNode();
710 CordRep* tree = contents_.tree();
711 if (tree == nullptr) {
712 contents_.reduce_size(n);
713 } else {
714 auto constexpr method = CordzUpdateTracker::kRemoveSuffix;
715 CordzUpdateScope scope(contents_.cordz_info(), method);
716 tree = cord_internal::RemoveCrcNode(tree);
717 if (n >= tree->length) {
718 CordRep::Unref(tree);
719 tree = nullptr;
720 } else if (tree->IsBtree()) {
721 tree = CordRepBtree::RemoveSuffix(tree->btree(), n);
722 } else if (!tree->IsExternal() && tree->refcount.IsOne()) {
723 assert(tree->IsFlat() || tree->IsSubstring());
724 tree->length -= n;
725 } else {
726 CordRep* rep = CordRepSubstring::Substring(tree, 0, tree->length - n);
727 CordRep::Unref(tree);
728 tree = rep;
729 }
730 contents_.SetTreeOrEmpty(tree, scope);
731 }
732 }
733
Subcord(size_t pos,size_t new_size) const734 Cord Cord::Subcord(size_t pos, size_t new_size) const {
735 Cord sub_cord;
736 size_t length = size();
737 if (pos > length) pos = length;
738 if (new_size > length - pos) new_size = length - pos;
739 if (new_size == 0) return sub_cord;
740
741 CordRep* tree = contents_.tree();
742 if (tree == nullptr) {
743 sub_cord.contents_.set_data(contents_.data() + pos, new_size);
744 return sub_cord;
745 }
746
747 if (new_size <= InlineRep::kMaxInline) {
748 char* dest = sub_cord.contents_.data_.as_chars();
749 Cord::ChunkIterator it = chunk_begin();
750 it.AdvanceBytes(pos);
751 size_t remaining_size = new_size;
752 while (remaining_size > it->size()) {
753 cord_internal::SmallMemmove(dest, it->data(), it->size());
754 remaining_size -= it->size();
755 dest += it->size();
756 ++it;
757 }
758 cord_internal::SmallMemmove(dest, it->data(), remaining_size);
759 sub_cord.contents_.set_inline_size(new_size);
760 return sub_cord;
761 }
762
763 tree = cord_internal::SkipCrcNode(tree);
764 if (tree->IsBtree()) {
765 tree = tree->btree()->SubTree(pos, new_size);
766 } else {
767 tree = CordRepSubstring::Substring(tree, pos, new_size);
768 }
769 sub_cord.contents_.EmplaceTree(tree, contents_.data_,
770 CordzUpdateTracker::kSubCord);
771 return sub_cord;
772 }
773
774 // --------------------------------------------------------------------
775 // Comparators
776
777 namespace {
778
ClampResult(int memcmp_res)779 int ClampResult(int memcmp_res) {
780 return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0);
781 }
782
CompareChunks(absl::string_view * lhs,absl::string_view * rhs,size_t * size_to_compare)783 int CompareChunks(absl::string_view* lhs, absl::string_view* rhs,
784 size_t* size_to_compare) {
785 size_t compared_size = std::min(lhs->size(), rhs->size());
786 assert(*size_to_compare >= compared_size);
787 *size_to_compare -= compared_size;
788
789 int memcmp_res = ::memcmp(lhs->data(), rhs->data(), compared_size);
790 if (memcmp_res != 0) return memcmp_res;
791
792 lhs->remove_prefix(compared_size);
793 rhs->remove_prefix(compared_size);
794
795 return 0;
796 }
797
798 // This overload set computes comparison results from memcmp result. This
799 // interface is used inside GenericCompare below. Differet implementations
800 // are specialized for int and bool. For int we clamp result to {-1, 0, 1}
801 // set. For bool we just interested in "value == 0".
802 template <typename ResultType>
ComputeCompareResult(int memcmp_res)803 ResultType ComputeCompareResult(int memcmp_res) {
804 return ClampResult(memcmp_res);
805 }
806 template <>
ComputeCompareResult(int memcmp_res)807 bool ComputeCompareResult<bool>(int memcmp_res) {
808 return memcmp_res == 0;
809 }
810
811 } // namespace
812
813 // Helper routine. Locates the first flat or external chunk of the Cord without
814 // initializing the iterator, and returns a string_view referencing the data.
FindFlatStartPiece() const815 inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
816 if (!is_tree()) {
817 return absl::string_view(data_.as_chars(), data_.inline_size());
818 }
819
820 CordRep* node = cord_internal::SkipCrcNode(tree());
821 if (node->IsFlat()) {
822 return absl::string_view(node->flat()->Data(), node->length);
823 }
824
825 if (node->IsExternal()) {
826 return absl::string_view(node->external()->base, node->length);
827 }
828
829 if (node->IsBtree()) {
830 CordRepBtree* tree = node->btree();
831 int height = tree->height();
832 while (--height >= 0) {
833 tree = tree->Edge(CordRepBtree::kFront)->btree();
834 }
835 return tree->Data(tree->begin());
836 }
837
838 // Get the child node if we encounter a SUBSTRING.
839 size_t offset = 0;
840 size_t length = node->length;
841 assert(length != 0);
842
843 if (node->IsSubstring()) {
844 offset = node->substring()->start;
845 node = node->substring()->child;
846 }
847
848 if (node->IsFlat()) {
849 return absl::string_view(node->flat()->Data() + offset, length);
850 }
851
852 assert(node->IsExternal() && "Expect FLAT or EXTERNAL node here");
853
854 return absl::string_view(node->external()->base + offset, length);
855 }
856
SetExpectedChecksum(uint32_t crc)857 void Cord::SetExpectedChecksum(uint32_t crc) {
858 auto constexpr method = CordzUpdateTracker::kSetExpectedChecksum;
859 if (empty()) {
860 contents_.MaybeRemoveEmptyCrcNode();
861 CordRep* rep = CordRepCrc::New(nullptr, crc);
862 contents_.EmplaceTree(rep, method);
863 } else if (!contents_.is_tree()) {
864 CordRep* rep = contents_.MakeFlatWithExtraCapacity(0);
865 rep = CordRepCrc::New(rep, crc);
866 contents_.EmplaceTree(rep, method);
867 } else {
868 const CordzUpdateScope scope(contents_.data_.cordz_info(), method);
869 CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), crc);
870 contents_.SetTree(rep, scope);
871 }
872 }
873
ExpectedChecksum() const874 absl::optional<uint32_t> Cord::ExpectedChecksum() const {
875 if (!contents_.is_tree() || !contents_.tree()->IsCrc()) {
876 return absl::nullopt;
877 }
878 return contents_.tree()->crc()->crc;
879 }
880
CompareSlowPath(absl::string_view rhs,size_t compared_size,size_t size_to_compare) const881 inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
882 size_t size_to_compare) const {
883 auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
884 if (!chunk->empty()) return true;
885 ++*it;
886 if (it->bytes_remaining_ == 0) return false;
887 *chunk = **it;
888 return true;
889 };
890
891 Cord::ChunkIterator lhs_it = chunk_begin();
892
893 // compared_size is inside first chunk.
894 absl::string_view lhs_chunk =
895 (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
896 assert(compared_size <= lhs_chunk.size());
897 assert(compared_size <= rhs.size());
898 lhs_chunk.remove_prefix(compared_size);
899 rhs.remove_prefix(compared_size);
900 size_to_compare -= compared_size; // skip already compared size.
901
902 while (advance(&lhs_it, &lhs_chunk) && !rhs.empty()) {
903 int comparison_result = CompareChunks(&lhs_chunk, &rhs, &size_to_compare);
904 if (comparison_result != 0) return comparison_result;
905 if (size_to_compare == 0) return 0;
906 }
907
908 return static_cast<int>(rhs.empty()) - static_cast<int>(lhs_chunk.empty());
909 }
910
CompareSlowPath(const Cord & rhs,size_t compared_size,size_t size_to_compare) const911 inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size,
912 size_t size_to_compare) const {
913 auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
914 if (!chunk->empty()) return true;
915 ++*it;
916 if (it->bytes_remaining_ == 0) return false;
917 *chunk = **it;
918 return true;
919 };
920
921 Cord::ChunkIterator lhs_it = chunk_begin();
922 Cord::ChunkIterator rhs_it = rhs.chunk_begin();
923
924 // compared_size is inside both first chunks.
925 absl::string_view lhs_chunk =
926 (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
927 absl::string_view rhs_chunk =
928 (rhs_it.bytes_remaining_ != 0) ? *rhs_it : absl::string_view();
929 assert(compared_size <= lhs_chunk.size());
930 assert(compared_size <= rhs_chunk.size());
931 lhs_chunk.remove_prefix(compared_size);
932 rhs_chunk.remove_prefix(compared_size);
933 size_to_compare -= compared_size; // skip already compared size.
934
935 while (advance(&lhs_it, &lhs_chunk) && advance(&rhs_it, &rhs_chunk)) {
936 int memcmp_res = CompareChunks(&lhs_chunk, &rhs_chunk, &size_to_compare);
937 if (memcmp_res != 0) return memcmp_res;
938 if (size_to_compare == 0) return 0;
939 }
940
941 return static_cast<int>(rhs_chunk.empty()) -
942 static_cast<int>(lhs_chunk.empty());
943 }
944
GetFirstChunk(const Cord & c)945 inline absl::string_view Cord::GetFirstChunk(const Cord& c) {
946 if (c.empty()) return {};
947 return c.contents_.FindFlatStartPiece();
948 }
GetFirstChunk(absl::string_view sv)949 inline absl::string_view Cord::GetFirstChunk(absl::string_view sv) {
950 return sv;
951 }
952
953 // Compares up to 'size_to_compare' bytes of 'lhs' with 'rhs'. It is assumed
954 // that 'size_to_compare' is greater that size of smallest of first chunks.
955 template <typename ResultType, typename RHS>
GenericCompare(const Cord & lhs,const RHS & rhs,size_t size_to_compare)956 ResultType GenericCompare(const Cord& lhs, const RHS& rhs,
957 size_t size_to_compare) {
958 absl::string_view lhs_chunk = Cord::GetFirstChunk(lhs);
959 absl::string_view rhs_chunk = Cord::GetFirstChunk(rhs);
960
961 size_t compared_size = std::min(lhs_chunk.size(), rhs_chunk.size());
962 assert(size_to_compare >= compared_size);
963 int memcmp_res = ::memcmp(lhs_chunk.data(), rhs_chunk.data(), compared_size);
964 if (compared_size == size_to_compare || memcmp_res != 0) {
965 return ComputeCompareResult<ResultType>(memcmp_res);
966 }
967
968 return ComputeCompareResult<ResultType>(
969 lhs.CompareSlowPath(rhs, compared_size, size_to_compare));
970 }
971
EqualsImpl(absl::string_view rhs,size_t size_to_compare) const972 bool Cord::EqualsImpl(absl::string_view rhs, size_t size_to_compare) const {
973 return GenericCompare<bool>(*this, rhs, size_to_compare);
974 }
975
EqualsImpl(const Cord & rhs,size_t size_to_compare) const976 bool Cord::EqualsImpl(const Cord& rhs, size_t size_to_compare) const {
977 return GenericCompare<bool>(*this, rhs, size_to_compare);
978 }
979
980 template <typename RHS>
SharedCompareImpl(const Cord & lhs,const RHS & rhs)981 inline int SharedCompareImpl(const Cord& lhs, const RHS& rhs) {
982 size_t lhs_size = lhs.size();
983 size_t rhs_size = rhs.size();
984 if (lhs_size == rhs_size) {
985 return GenericCompare<int>(lhs, rhs, lhs_size);
986 }
987 if (lhs_size < rhs_size) {
988 auto data_comp_res = GenericCompare<int>(lhs, rhs, lhs_size);
989 return data_comp_res == 0 ? -1 : data_comp_res;
990 }
991
992 auto data_comp_res = GenericCompare<int>(lhs, rhs, rhs_size);
993 return data_comp_res == 0 ? +1 : data_comp_res;
994 }
995
Compare(absl::string_view rhs) const996 int Cord::Compare(absl::string_view rhs) const {
997 return SharedCompareImpl(*this, rhs);
998 }
999
CompareImpl(const Cord & rhs) const1000 int Cord::CompareImpl(const Cord& rhs) const {
1001 return SharedCompareImpl(*this, rhs);
1002 }
1003
EndsWith(absl::string_view rhs) const1004 bool Cord::EndsWith(absl::string_view rhs) const {
1005 size_t my_size = size();
1006 size_t rhs_size = rhs.size();
1007
1008 if (my_size < rhs_size) return false;
1009
1010 Cord tmp(*this);
1011 tmp.RemovePrefix(my_size - rhs_size);
1012 return tmp.EqualsImpl(rhs, rhs_size);
1013 }
1014
EndsWith(const Cord & rhs) const1015 bool Cord::EndsWith(const Cord& rhs) const {
1016 size_t my_size = size();
1017 size_t rhs_size = rhs.size();
1018
1019 if (my_size < rhs_size) return false;
1020
1021 Cord tmp(*this);
1022 tmp.RemovePrefix(my_size - rhs_size);
1023 return tmp.EqualsImpl(rhs, rhs_size);
1024 }
1025
1026 // --------------------------------------------------------------------
1027 // Misc.
1028
operator std::string() const1029 Cord::operator std::string() const {
1030 std::string s;
1031 absl::CopyCordToString(*this, &s);
1032 return s;
1033 }
1034
CopyCordToString(const Cord & src,std::string * dst)1035 void CopyCordToString(const Cord& src, std::string* dst) {
1036 if (!src.contents_.is_tree()) {
1037 src.contents_.CopyTo(dst);
1038 } else {
1039 absl::strings_internal::STLStringResizeUninitialized(dst, src.size());
1040 src.CopyToArraySlowPath(&(*dst)[0]);
1041 }
1042 }
1043
CopyToArraySlowPath(char * dst) const1044 void Cord::CopyToArraySlowPath(char* dst) const {
1045 assert(contents_.is_tree());
1046 absl::string_view fragment;
1047 if (GetFlatAux(contents_.tree(), &fragment)) {
1048 memcpy(dst, fragment.data(), fragment.size());
1049 return;
1050 }
1051 for (absl::string_view chunk : Chunks()) {
1052 memcpy(dst, chunk.data(), chunk.size());
1053 dst += chunk.size();
1054 }
1055 }
1056
AdvanceAndReadBytes(size_t n)1057 Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
1058 ABSL_HARDENING_ASSERT(bytes_remaining_ >= n &&
1059 "Attempted to iterate past `end()`");
1060 Cord subcord;
1061 auto constexpr method = CordzUpdateTracker::kCordReader;
1062
1063 if (n <= InlineRep::kMaxInline) {
1064 // Range to read fits in inline data. Flatten it.
1065 char* data = subcord.contents_.set_data(n);
1066 while (n > current_chunk_.size()) {
1067 memcpy(data, current_chunk_.data(), current_chunk_.size());
1068 data += current_chunk_.size();
1069 n -= current_chunk_.size();
1070 ++*this;
1071 }
1072 memcpy(data, current_chunk_.data(), n);
1073 if (n < current_chunk_.size()) {
1074 RemoveChunkPrefix(n);
1075 } else if (n > 0) {
1076 ++*this;
1077 }
1078 return subcord;
1079 }
1080
1081 if (btree_reader_) {
1082 size_t chunk_size = current_chunk_.size();
1083 if (n <= chunk_size && n <= kMaxBytesToCopy) {
1084 subcord = Cord(current_chunk_.substr(0, n), method);
1085 if (n < chunk_size) {
1086 current_chunk_.remove_prefix(n);
1087 } else {
1088 current_chunk_ = btree_reader_.Next();
1089 }
1090 } else {
1091 CordRep* rep;
1092 current_chunk_ = btree_reader_.Read(n, chunk_size, rep);
1093 subcord.contents_.EmplaceTree(rep, method);
1094 }
1095 bytes_remaining_ -= n;
1096 return subcord;
1097 }
1098
1099 // Short circuit if reading the entire data edge.
1100 assert(current_leaf_ != nullptr);
1101 if (n == current_leaf_->length) {
1102 bytes_remaining_ = 0;
1103 current_chunk_ = {};
1104 CordRep* tree = CordRep::Ref(current_leaf_);
1105 subcord.contents_.EmplaceTree(VerifyTree(tree), method);
1106 return subcord;
1107 }
1108
1109 // From this point on, we need a partial substring node.
1110 // Get pointer to the underlying flat or external data payload and
1111 // compute data pointer and offset into current flat or external.
1112 CordRep* payload = current_leaf_->IsSubstring()
1113 ? current_leaf_->substring()->child
1114 : current_leaf_;
1115 const char* data = payload->IsExternal() ? payload->external()->base
1116 : payload->flat()->Data();
1117 const size_t offset = static_cast<size_t>(current_chunk_.data() - data);
1118
1119 auto* tree = CordRepSubstring::Substring(payload, offset, n);
1120 subcord.contents_.EmplaceTree(VerifyTree(tree), method);
1121 bytes_remaining_ -= n;
1122 current_chunk_.remove_prefix(n);
1123 return subcord;
1124 }
1125
operator [](size_t i) const1126 char Cord::operator[](size_t i) const {
1127 ABSL_HARDENING_ASSERT(i < size());
1128 size_t offset = i;
1129 const CordRep* rep = contents_.tree();
1130 if (rep == nullptr) {
1131 return contents_.data()[i];
1132 }
1133 rep = cord_internal::SkipCrcNode(rep);
1134 while (true) {
1135 assert(rep != nullptr);
1136 assert(offset < rep->length);
1137 if (rep->IsFlat()) {
1138 // Get the "i"th character directly from the flat array.
1139 return rep->flat()->Data()[offset];
1140 } else if (rep->IsBtree()) {
1141 return rep->btree()->GetCharacter(offset);
1142 } else if (rep->IsExternal()) {
1143 // Get the "i"th character from the external array.
1144 return rep->external()->base[offset];
1145 } else {
1146 // This must be a substring a node, so bypass it to get to the child.
1147 assert(rep->IsSubstring());
1148 offset += rep->substring()->start;
1149 rep = rep->substring()->child;
1150 }
1151 }
1152 }
1153
FlattenSlowPath()1154 absl::string_view Cord::FlattenSlowPath() {
1155 assert(contents_.is_tree());
1156 size_t total_size = size();
1157 CordRep* new_rep;
1158 char* new_buffer;
1159
1160 // Try to put the contents into a new flat rep. If they won't fit in the
1161 // biggest possible flat node, use an external rep instead.
1162 if (total_size <= kMaxFlatLength) {
1163 new_rep = CordRepFlat::New(total_size);
1164 new_rep->length = total_size;
1165 new_buffer = new_rep->flat()->Data();
1166 CopyToArraySlowPath(new_buffer);
1167 } else {
1168 new_buffer = std::allocator<char>().allocate(total_size);
1169 CopyToArraySlowPath(new_buffer);
1170 new_rep = absl::cord_internal::NewExternalRep(
1171 absl::string_view(new_buffer, total_size), [](absl::string_view s) {
1172 std::allocator<char>().deallocate(const_cast<char*>(s.data()),
1173 s.size());
1174 });
1175 }
1176 CordzUpdateScope scope(contents_.cordz_info(), CordzUpdateTracker::kFlatten);
1177 CordRep::Unref(contents_.as_tree());
1178 contents_.SetTree(new_rep, scope);
1179 return absl::string_view(new_buffer, total_size);
1180 }
1181
GetFlatAux(CordRep * rep,absl::string_view * fragment)1182 /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
1183 assert(rep != nullptr);
1184 if (rep->length == 0) {
1185 *fragment = absl::string_view();
1186 return true;
1187 }
1188 rep = cord_internal::SkipCrcNode(rep);
1189 if (rep->IsFlat()) {
1190 *fragment = absl::string_view(rep->flat()->Data(), rep->length);
1191 return true;
1192 } else if (rep->IsExternal()) {
1193 *fragment = absl::string_view(rep->external()->base, rep->length);
1194 return true;
1195 } else if (rep->IsBtree()) {
1196 return rep->btree()->IsFlat(fragment);
1197 } else if (rep->IsSubstring()) {
1198 CordRep* child = rep->substring()->child;
1199 if (child->IsFlat()) {
1200 *fragment = absl::string_view(
1201 child->flat()->Data() + rep->substring()->start, rep->length);
1202 return true;
1203 } else if (child->IsExternal()) {
1204 *fragment = absl::string_view(
1205 child->external()->base + rep->substring()->start, rep->length);
1206 return true;
1207 } else if (child->IsBtree()) {
1208 return child->btree()->IsFlat(rep->substring()->start, rep->length,
1209 fragment);
1210 }
1211 }
1212 return false;
1213 }
1214
ForEachChunkAux(absl::cord_internal::CordRep * rep,absl::FunctionRef<void (absl::string_view)> callback)1215 /* static */ void Cord::ForEachChunkAux(
1216 absl::cord_internal::CordRep* rep,
1217 absl::FunctionRef<void(absl::string_view)> callback) {
1218 assert(rep != nullptr);
1219 if (rep->length == 0) return;
1220 rep = cord_internal::SkipCrcNode(rep);
1221
1222 if (rep->IsBtree()) {
1223 ChunkIterator it(rep), end;
1224 while (it != end) {
1225 callback(*it);
1226 ++it;
1227 }
1228 return;
1229 }
1230
1231 // This is a leaf node, so invoke our callback.
1232 absl::cord_internal::CordRep* current_node = cord_internal::SkipCrcNode(rep);
1233 absl::string_view chunk;
1234 bool success = GetFlatAux(current_node, &chunk);
1235 assert(success);
1236 if (success) {
1237 callback(chunk);
1238 }
1239 }
1240
DumpNode(CordRep * rep,bool include_data,std::ostream * os,int indent)1241 static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
1242 int indent) {
1243 const int kIndentStep = 1;
1244 absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
1245 absl::InlinedVector<int, kInlinedVectorSize> indents;
1246 for (;;) {
1247 *os << std::setw(3) << rep->refcount.Get();
1248 *os << " " << std::setw(7) << rep->length;
1249 *os << " [";
1250 if (include_data) *os << static_cast<void*>(rep);
1251 *os << "]";
1252 *os << " " << std::setw(indent) << "";
1253 bool leaf = false;
1254 if (rep == nullptr) {
1255 *os << "NULL\n";
1256 leaf = true;
1257 } else if (rep->IsCrc()) {
1258 *os << "CRC crc=" << rep->crc()->crc << "\n";
1259 indent += kIndentStep;
1260 rep = rep->crc()->child;
1261 } else if (rep->IsSubstring()) {
1262 *os << "SUBSTRING @ " << rep->substring()->start << "\n";
1263 indent += kIndentStep;
1264 rep = rep->substring()->child;
1265 } else { // Leaf or ring
1266 leaf = true;
1267 if (rep->IsExternal()) {
1268 *os << "EXTERNAL [";
1269 if (include_data)
1270 *os << absl::CEscape(std::string(rep->external()->base, rep->length));
1271 *os << "]\n";
1272 } else if (rep->IsFlat()) {
1273 *os << "FLAT cap=" << rep->flat()->Capacity() << " [";
1274 if (include_data)
1275 *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length));
1276 *os << "]\n";
1277 } else {
1278 CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os);
1279 }
1280 }
1281 if (leaf) {
1282 if (stack.empty()) break;
1283 rep = stack.back();
1284 stack.pop_back();
1285 indent = indents.back();
1286 indents.pop_back();
1287 }
1288 }
1289 ABSL_INTERNAL_CHECK(indents.empty(), "");
1290 }
1291
ReportError(CordRep * root,CordRep * node)1292 static std::string ReportError(CordRep* root, CordRep* node) {
1293 std::ostringstream buf;
1294 buf << "Error at node " << node << " in:";
1295 DumpNode(root, true, &buf);
1296 return buf.str();
1297 }
1298
VerifyNode(CordRep * root,CordRep * start_node,bool)1299 static bool VerifyNode(CordRep* root, CordRep* start_node,
1300 bool /* full_validation */) {
1301 absl::InlinedVector<CordRep*, 2> worklist;
1302 worklist.push_back(start_node);
1303 do {
1304 CordRep* node = worklist.back();
1305 worklist.pop_back();
1306
1307 ABSL_INTERNAL_CHECK(node != nullptr, ReportError(root, node));
1308 if (node != root) {
1309 ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node));
1310 ABSL_INTERNAL_CHECK(!node->IsCrc(), ReportError(root, node));
1311 }
1312
1313 if (node->IsFlat()) {
1314 ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(),
1315 ReportError(root, node));
1316 } else if (node->IsExternal()) {
1317 ABSL_INTERNAL_CHECK(node->external()->base != nullptr,
1318 ReportError(root, node));
1319 } else if (node->IsSubstring()) {
1320 ABSL_INTERNAL_CHECK(
1321 node->substring()->start < node->substring()->child->length,
1322 ReportError(root, node));
1323 ABSL_INTERNAL_CHECK(node->substring()->start + node->length <=
1324 node->substring()->child->length,
1325 ReportError(root, node));
1326 } else if (node->IsCrc()) {
1327 ABSL_INTERNAL_CHECK(
1328 node->crc()->child != nullptr || node->crc()->length == 0,
1329 ReportError(root, node));
1330 if (node->crc()->child != nullptr) {
1331 ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length,
1332 ReportError(root, node));
1333 worklist.push_back(node->crc()->child);
1334 }
1335 }
1336 } while (!worklist.empty());
1337 return true;
1338 }
1339
operator <<(std::ostream & out,const Cord & cord)1340 std::ostream& operator<<(std::ostream& out, const Cord& cord) {
1341 for (absl::string_view chunk : cord.Chunks()) {
1342 out.write(chunk.data(), static_cast<std::streamsize>(chunk.size()));
1343 }
1344 return out;
1345 }
1346
1347 namespace strings_internal {
FlatOverhead()1348 size_t CordTestAccess::FlatOverhead() { return cord_internal::kFlatOverhead; }
MaxFlatLength()1349 size_t CordTestAccess::MaxFlatLength() { return cord_internal::kMaxFlatLength; }
FlatTagToLength(uint8_t tag)1350 size_t CordTestAccess::FlatTagToLength(uint8_t tag) {
1351 return cord_internal::TagToLength(tag);
1352 }
LengthToTag(size_t s)1353 uint8_t CordTestAccess::LengthToTag(size_t s) {
1354 ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s));
1355 return cord_internal::AllocatedSizeToTag(s + cord_internal::kFlatOverhead);
1356 }
SizeofCordRepExternal()1357 size_t CordTestAccess::SizeofCordRepExternal() {
1358 return sizeof(CordRepExternal);
1359 }
SizeofCordRepSubstring()1360 size_t CordTestAccess::SizeofCordRepSubstring() {
1361 return sizeof(CordRepSubstring);
1362 }
1363 } // namespace strings_internal
1364 ABSL_NAMESPACE_END
1365 } // namespace absl
1366