diff options
author | Abseil Team <absl-team@google.com> | 2021-08-06 06:19:26 -0700 |
---|---|---|
committer | dinord <dinor@google.com> | 2021-08-06 13:27:35 +0000 |
commit | bf31a10b65d945665cecfb9d8807702ae4a7fde1 (patch) | |
tree | 19424ad3fe5f31790e792a6c99530012e84e5643 /absl/strings/internal | |
parent | ab01e0403a40813857a8e580d9cf1580ba0c4139 (diff) |
Export of internal Abseil changes
--
93c607726d663800b4bfa472cba043fd3f5d0e97 by Derek Mauro <dmauro@google.com>:
Internal change
PiperOrigin-RevId: 389158822
--
55b3bb50bbc168567c6ba25d07df2c2c39e864af by Martijn Vels <mvels@google.com>:
Change CordRepRing alternative implementation to CordRepBtree alternative.
This changes makes CordRepBtree (BTREE) the alternative to CordRepConcat (CONCAT) trees, enabled through the internal / experimental 'cord_btree_enabled' latch.
PiperOrigin-RevId: 389030571
--
d6fc346143606c096bca8eb5029e4c429ac6e305 by Todd Lipcon <tlipcon@google.com>:
Fix a small typo in SequenceLock doc comment
PiperOrigin-RevId: 388972936
--
e46f9245dce8b4150e3ca2664e0cf42b75f90a83 by Martijn Vels <mvels@google.com>:
Add 'shallow' validation mode to CordRepBtree which will be the default for internal assertions.
PiperOrigin-RevId: 388753606
--
b5e74f163b490beb006f848ace67bb650433fe13 by Martijn Vels <mvels@google.com>:
Add btree statistics to CordzInfo, and reduce rounding errors
PiperOrigin-RevId: 388715878
--
105bcbf80de649937e693b29b18220f9e6841a51 by Evan Brown <ezb@google.com>:
Skip length checking when constructing absl::string_view from `const char*`.
The length check causes unnecessary code bloat.
PiperOrigin-RevId: 388271741
--
bed595158f24839efe49c65ae483f797d79fe0ae by Derek Mauro <dmauro@google.com>:
Internal change
PiperOrigin-RevId: 387713428
GitOrigin-RevId: 93c607726d663800b4bfa472cba043fd3f5d0e97
Change-Id: I2a4840f5ffcd7f70b7d7d45cce66f23c42cf565f
Diffstat (limited to 'absl/strings/internal')
-rw-r--r-- | absl/strings/internal/cord_internal.cc | 1 | ||||
-rw-r--r-- | absl/strings/internal/cord_internal.h | 6 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_btree.cc | 21 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_btree.h | 32 | ||||
-rw-r--r-- | absl/strings/internal/cord_rep_btree_test.cc | 43 | ||||
-rw-r--r-- | absl/strings/internal/cordz_info.cc | 51 | ||||
-rw-r--r-- | absl/strings/internal/cordz_info_statistics_test.cc | 133 | ||||
-rw-r--r-- | absl/strings/internal/cordz_statistics.h | 3 |
8 files changed, 248 insertions, 42 deletions
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index f79cb628..1767e6fc 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -31,6 +31,7 @@ ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled( kCordEnableRingBufferDefault); ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled( kCordShallowSubcordsDefault); +ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false); void CordRep::Destroy(CordRep* rep) { assert(rep != nullptr); diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 371a7f9c..8ae644ba 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -46,6 +46,12 @@ extern std::atomic<bool> cord_btree_enabled; extern std::atomic<bool> cord_ring_buffer_enabled; extern std::atomic<bool> shallow_subcords_enabled; +// `cord_btree_exhaustive_validation` can be set to force exhaustive validation +// in debug assertions, and code that calls `IsValid()` explicitly. By default, +// assertions should be relatively cheap and AssertValid() can easily lead to +// O(n^2) complexity as recursive / full tree validation is O(n). +extern std::atomic<bool> cord_btree_exhaustive_validation; + inline void enable_cord_btree(bool enable) { cord_btree_enabled.store(enable, std::memory_order_relaxed); } diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 6978cfd2..fd3a0045 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -32,6 +32,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +constexpr size_t CordRepBtree::kMaxCapacity; // NOLINT: needed for c++ < c++17 + namespace { using NodeStack = CordRepBtree * [CordRepBtree::kMaxDepth]; @@ -42,6 +44,10 @@ using CopyResult = CordRepBtree::CopyResult; constexpr auto kFront = CordRepBtree::kFront; constexpr auto kBack = CordRepBtree::kBack; +inline bool exhaustive_validation() { + return cord_btree_exhaustive_validation.load(std::memory_order_relaxed); +} + // Implementation of the various 'Dump' functions. // Prints the entire tree structure or 'rep'. External callers should // not specify 'depth' and leave it to its default (0) value. @@ -357,7 +363,7 @@ void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin, Delete(tree); } -bool CordRepBtree::IsValid(const CordRepBtree* tree) { +bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { #define NODE_CHECK_VALID(x) \ if (!(x)) { \ ABSL_RAW_LOG(ERROR, "CordRepBtree::CheckValid() FAILED: %s", #x); \ @@ -389,9 +395,9 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree) { child_length += edge->length; } NODE_CHECK_EQ(child_length, tree->length); - if (tree->height() > 0) { + if ((!shallow || exhaustive_validation()) && tree->height() > 0) { for (CordRep* edge : tree->Edges()) { - if (!IsValid(edge->btree())) return false; + if (!IsValid(edge->btree(), shallow)) return false; } } return true; @@ -402,16 +408,17 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree) { #ifndef NDEBUG -CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { - if (!IsValid(tree)) { +CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, bool shallow) { + if (!IsValid(tree, shallow)) { Dump(tree, "CordRepBtree validation failed:", false, std::cout); ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); } return tree; } -const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { - if (!IsValid(tree)) { +const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool shallow) { + if (!IsValid(tree, shallow)) { Dump(tree, "CordRepBtree validation failed:", false, std::cout); ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); } diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 7d854731..56e1e4af 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -266,10 +266,28 @@ class CordRepBtree : public CordRep { // holding a FLAT or EXTERNAL child rep. static bool IsDataEdge(const CordRep* rep); - // Diagnostics - static bool IsValid(const CordRepBtree* tree); - static CordRepBtree* AssertValid(CordRepBtree* tree); - static const CordRepBtree* AssertValid(const CordRepBtree* tree); + // Diagnostics: returns true if `tree` is valid and internally consistent. + // If `shallow` is false, then the provided top level node and all child nodes + // below it are recursively checked. If `shallow` is true, only the provided + // node in `tree` and the cumulative length, type and height of the direct + // child nodes of `tree` are checked. The value of `shallow` is ignored if the + // internal `cord_btree_exhaustive_validation` diagnostics variable is true, + // in which case the performed validations works as if `shallow` were false. + // This function is intended for debugging and testing purposes only. + static bool IsValid(const CordRepBtree* tree, bool shallow = false); + + // Diagnostics: asserts that the provided tree is valid. + // `AssertValid()` performs a shallow validation by default. `shallow` can be + // set to false in which case an exhaustive validation is performed. This + // function is implemented in terms of calling `IsValid()` and asserting the + // return value to be true. See `IsValid()` for more information. + // This function is intended for debugging and testing purposes only. + static CordRepBtree* AssertValid(CordRepBtree* tree, bool shallow = true); + static const CordRepBtree* AssertValid(const CordRepBtree* tree, + bool shallow = true); + + // Diagnostics: dump the contents of this tree to `stream`. + // This function is intended for debugging and testing purposes only. static void Dump(const CordRep* rep, std::ostream& stream); static void Dump(const CordRep* rep, absl::string_view label, std::ostream& stream); @@ -834,11 +852,13 @@ inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) { #ifdef NDEBUG -inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { +inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, + bool /* shallow */) { return tree; } -inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { +inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool /* shallow */) { return tree; } diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index 7a0b7811..073a7d45 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -24,6 +24,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/cleanup/cleanup.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_test_util.h" #include "absl/strings/str_cat.h" @@ -1346,6 +1347,48 @@ TEST(CordRepBtreeTest, AssertValid) { CordRep::Unref(tree); } +TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) { + // Restore exhaustive validation on any exit. + const bool exhaustive_validation = cord_btree_exhaustive_validation.load(); + auto cleanup = absl::MakeCleanup([exhaustive_validation] { + cord_btree_exhaustive_validation.store(exhaustive_validation); + }); + + // Create a tree of at least 2 levels, and mess with the original flat, which + // should go undetected in shallow mode as the flat is too far away, but + // should be detected in forced non-shallow mode. + CordRep* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(flat); + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * 2; + for (size_t i = 0; i < n; ++i) { + tree = CordRepBtree::Append(tree, MakeFlat("Hello world")); + } + flat->length = 100; + + cord_btree_exhaustive_validation.store(false); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_TRUE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); + CordRepBtree::AssertValid(tree); + CordRepBtree::AssertValid(tree, true); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*"); +#endif + + cord_btree_exhaustive_validation.store(true); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree), ".*"); + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, true), ".*"); +#endif + + flat->length = 3; + CordRep::Unref(tree); +} + } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index a3a0b9c0..5c18bbc5 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -19,6 +19,7 @@ #include "absl/container/inlined_vector.h" #include "absl/debugging/stacktrace.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" @@ -83,19 +84,23 @@ class CordRepAnalyzer { // Process all top level linear nodes (substrings and flats). repref = CountLinearReps(repref, memory_usage_); - // We should have have either a concat or ring node node if not null. if (repref.rep != nullptr) { - assert(repref.rep->tag == RING || repref.rep->tag == CONCAT); if (repref.rep->tag == RING) { AnalyzeRing(repref); + } else if (repref.rep->tag == BTREE) { + AnalyzeBtree(repref); } else if (repref.rep->tag == CONCAT) { AnalyzeConcat(repref); + } else { + // We should have either a concat, btree, or ring node if not null. + assert(false); } } // Adds values to output statistics_.estimated_memory_usage += memory_usage_.total; - statistics_.estimated_fair_share_memory_usage += memory_usage_.fair_share; + statistics_.estimated_fair_share_memory_usage += + static_cast<size_t>(memory_usage_.fair_share); } private: @@ -117,13 +122,13 @@ class CordRepAnalyzer { // Memory usage values struct MemoryUsage { size_t total = 0; - size_t fair_share = 0; + double fair_share = 0.0; // Adds 'size` memory usage to this class, with a cumulative (recursive) // reference count of `refcount` void Add(size_t size, size_t refcount) { total += size; - fair_share += size / refcount; + fair_share += static_cast<double>(size) / refcount; } }; @@ -215,28 +220,32 @@ class CordRepAnalyzer { } } - // Counts the provided ring buffer child into `child_usage`. - void CountRingChild(const CordRep* child, MemoryUsage& child_usage) { - RepRef rep{child, static_cast<size_t>(child->refcount.Get())}; - rep = CountLinearReps(rep, child_usage); - assert(rep.rep == nullptr); - } - - // Analyzes the provided ring. As ring buffers can have many child nodes, the - // effect of rounding errors can become non trivial, so we compute the totals - // first at the ring level, and then divide the fair share of the total - // including children fair share totals. + // Analyzes the provided ring. void AnalyzeRing(RepRef rep) { statistics_.node_count++; statistics_.node_counts.ring++; - MemoryUsage ring_usage; const CordRepRing* ring = rep.rep->ring(); - ring_usage.Add(CordRepRing::AllocSize(ring->capacity()), 1); + memory_usage_.Add(CordRepRing::AllocSize(ring->capacity()), rep.refcount); ring->ForEach([&](CordRepRing::index_type pos) { - CountRingChild(ring->entry_child(pos), ring_usage); + CountLinearReps(rep.Child(ring->entry_child(pos)), memory_usage_); }); - memory_usage_.total += ring_usage.total; - memory_usage_.fair_share += ring_usage.fair_share / rep.refcount; + } + + // Analyzes the provided btree. + void AnalyzeBtree(RepRef rep) { + statistics_.node_count++; + statistics_.node_counts.btree++; + memory_usage_.Add(sizeof(CordRepBtree), rep.refcount); + const CordRepBtree* tree = rep.rep->btree(); + if (tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + AnalyzeBtree(rep.Child(edge)); + } + } else { + for (CordRep* edge : tree->Edges()) { + CountLinearReps(rep.Child(edge), memory_usage_); + } + } } CordzStatistics& statistics_; diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index 9f2842d9..7430d281 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -21,6 +21,7 @@ #include "absl/base/config.h" #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_info.h" @@ -42,6 +43,8 @@ inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { namespace { +using ::testing::Ge; + // Creates a flat of the specified allocated size CordRepFlat* Flat(size_t size) { // Round up to a tag size, as we are going to poke an exact tag size back into @@ -134,8 +137,8 @@ size_t SizeOf(const CordRepRing* rep) { } // Computes fair share memory used in a naive 'we dare to recurse' way. -size_t FairShare(CordRep* rep, size_t ref = 1) { - size_t self = 0, children = 0; +double FairShareImpl(CordRep* rep, size_t ref) { + double self = 0.0, children = 0.0; ref *= rep->refcount.Get(); if (rep->tag >= FLAT) { self = SizeOf(rep->flat()); @@ -143,22 +146,32 @@ size_t FairShare(CordRep* rep, size_t ref = 1) { self = SizeOf(rep->external()); } else if (rep->tag == SUBSTRING) { self = SizeOf(rep->substring()); - children = FairShare(rep->substring()->child, ref); + children = FairShareImpl(rep->substring()->child, ref); + } else if (rep->tag == BTREE) { + self = SizeOf(rep->btree()); + for (CordRep*edge : rep->btree()->Edges()) { + children += FairShareImpl(edge, ref); + } } else if (rep->tag == RING) { self = SizeOf(rep->ring()); rep->ring()->ForEach([&](CordRepRing::index_type i) { - self += FairShare(rep->ring()->entry_child(i)); + self += FairShareImpl(rep->ring()->entry_child(i), 1); }); } else if (rep->tag == CONCAT) { self = SizeOf(rep->concat()); - children = FairShare(rep->concat()->left, ref) + - FairShare(rep->concat()->right, ref); + children = FairShareImpl(rep->concat()->left, ref) + + FairShareImpl(rep->concat()->right, ref); } else { assert(false); } return self / ref + children; } +// Returns the fair share memory size from `ShareFhareImpl()` as a size_t. +size_t FairShare(CordRep* rep, size_t ref = 1) { + return static_cast<size_t>(FairShareImpl(rep, ref)); +} + // Samples the cord and returns CordzInfo::GetStatistics() CordzStatistics SampleCord(CordRep* rep) { InlineData cord(rep); @@ -191,6 +204,7 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { STATS_MATCHER_EXPECT_EQ(node_counts.concat); STATS_MATCHER_EXPECT_EQ(node_counts.substring); STATS_MATCHER_EXPECT_EQ(node_counts.ring); + STATS_MATCHER_EXPECT_EQ(node_counts.btree); STATS_MATCHER_EXPECT_EQ(estimated_memory_usage); STATS_MATCHER_EXPECT_EQ(estimated_fair_share_memory_usage); @@ -424,6 +438,103 @@ TEST(CordzInfoStatisticsTest, SharedSubstringRing) { EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); } +TEST(CordzInfoStatisticsTest, BtreeLeaf) { + ASSERT_THAT(CordRepBtree::kMaxCapacity, Ge(3)); + RefHelper ref; + auto* flat1 = Flat(2000); + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + auto* external = External(3000); + + CordRepBtree* tree = CordRepBtree::Create(flat1); + tree = CordRepBtree::Append(tree, substr); + tree = CordRepBtree::Append(tree, external); + size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + size_t flat3_size = 0; + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + flat3_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + ref.NeedsUnref(tree); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(substr) + + flat3_size + SizeOf(external); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 1 + 3 + 1 + flat3_count; + expected.node_counts.flat = 2 + flat3_count; + expected.node_counts.flat_128 = flat3_count; + expected.node_counts.flat_256 = 1; + expected.node_counts.external = 1; + expected.node_counts.substring = 1; + expected.node_counts.btree = 1; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, BtreeNodeShared) { + RefHelper ref; + static constexpr int leaf_count = 3; + const size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + ASSERT_THAT(flat3_count, Ge(0)); + + CordRepBtree* tree = nullptr; + size_t mem_size = 0; + for (int i = 0; i < leaf_count; ++i) { + auto* flat1 = ref.Ref(Flat(2000), 9); + mem_size += SizeOf(flat1); + if (i == 0) { + tree = CordRepBtree::Create(flat1); + } else { + tree = CordRepBtree::Append(tree, flat1); + } + + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + mem_size += SizeOf(flat2) + SizeOf(substr); + tree = CordRepBtree::Append(tree, substr); + + auto* external = External(30); + mem_size += SizeOf(external); + tree = CordRepBtree::Append(tree, external); + + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + mem_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + + if (i == 0) { + mem_size += SizeOf(tree); + } else { + mem_size += SizeOf(tree->Edges().back()->btree()); + } + } + ref.NeedsUnref(tree); + + // Ref count: 2 for top (add 1), 5 for leaf 0 (add 4). + ref.Ref(tree, 1); + ref.Ref(tree->Edges().front(), 4); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + mem_size; + expected.estimated_fair_share_memory_usage = FairShare(tree); + + expected.node_count = 1 + leaf_count * (1 + 3 + 1 + flat3_count); + expected.node_counts.flat = leaf_count * (2 + flat3_count); + expected.node_counts.flat_128 = leaf_count * flat3_count; + expected.node_counts.flat_256 = leaf_count; + expected.node_counts.external = leaf_count; + expected.node_counts.substring = leaf_count; + expected.node_counts.btree = 1 + leaf_count; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + TEST(CordzInfoStatisticsTest, ThreadSafety) { Notification stop; static constexpr int kNumThreads = 8; @@ -471,9 +582,15 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) { CordRep::Unref(cord.as_tree()); cord.set_inline_size(0); } else { - // 50/50 Ring or Flat coin toss + // Coin toss to 25% ring, 25% btree, and 50% flat. CordRep* rep = Flat(256); - rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep; + if (coin_toss(gen) != 0) { + if (coin_toss(gen) != 0) { + rep = CordRepRing::Create(rep); + } else { + rep = CordRepBtree::Create(rep); + } + } cord.make_tree(rep); // 50/50 sample diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index e03c651e..da4c7dbb 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -40,6 +40,7 @@ struct CordzStatistics { size_t substring = 0; // #substring reps size_t concat = 0; // #concat reps size_t ring = 0; // #ring buffer reps + size_t btree = 0; // #btree reps }; // The size of the cord in bytes. This matches the result of Cord::size(). @@ -61,6 +62,8 @@ struct CordzStatistics { // The total number of nodes referenced by this cord. // For ring buffer Cords, this includes the 'ring buffer' node. + // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all + // the substring, flat and external nodes referenced by the tree. // A value of 0 implies the property has not been recorded. int64_t node_count = 0; |