• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
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, &region, &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