diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h index 61b53d55..960ccd08 100644 --- a/absl/cleanup/cleanup.h +++ b/absl/cleanup/cleanup.h @@ -86,25 +86,25 @@ class ABSL_MUST_USE_RESULT Cleanup final { "Callbacks that return values are not supported."); public: - Cleanup(Callback callback) // NOLINT - : storage_(std::move(callback), /* is_callback_engaged = */ true) {} + Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT Cleanup(Cleanup&& other) = default; void Cancel() && { ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); - storage_.DisengageCallback(); + storage_.DestroyCallback(); } void Invoke() && { ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); - storage_.DisengageCallback(); storage_.InvokeCallback(); + storage_.DestroyCallback(); } ~Cleanup() { if (storage_.IsCallbackEngaged()) { storage_.InvokeCallback(); + storage_.DestroyCallback(); } } diff --git a/absl/cleanup/cleanup_test.cc b/absl/cleanup/cleanup_test.cc index 792595d6..46b88589 100644 --- a/absl/cleanup/cleanup_test.cc +++ b/absl/cleanup/cleanup_test.cc @@ -264,4 +264,48 @@ TYPED_TEST(CleanupTest, Move) { EXPECT_FALSE(called); // Destructor shouldn't invoke the callback } +int DestructionCount = 0; + +struct DestructionCounter { + void operator()() {} + + ~DestructionCounter() { ++DestructionCount; } +}; + +TYPED_TEST(CleanupTest, DestructorDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + } + + EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys +} + +TYPED_TEST(CleanupTest, CancelDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Cancel(); + EXPECT_EQ(DestructionCount, 1); // Cancel destroys + } + + EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy +} + +TYPED_TEST(CleanupTest, InvokeDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Invoke(); + EXPECT_EQ(DestructionCount, 1); // Invoke destroys + } + + EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy +} + } // namespace diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h index b4c40737..2783fcb7 100644 --- a/absl/cleanup/internal/cleanup.h +++ b/absl/cleanup/internal/cleanup.h @@ -15,10 +15,12 @@ #ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_ #define ABSL_CLEANUP_INTERNAL_CLEANUP_H_ +#include #include #include #include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" #include "absl/base/thread_annotations.h" #include "absl/utility/utility.h" @@ -45,14 +47,22 @@ class Storage { public: Storage() = delete; - Storage(Callback callback, bool is_callback_engaged) - : callback_(std::move(callback)), - is_callback_engaged_(is_callback_engaged) {} + explicit Storage(Callback callback) { + // Placement-new into a character buffer is used for eager destruction when + // the cleanup is invoked or cancelled. To ensure this optimizes well, the + // behavior is implemented locally instead of using an absl::optional. + ::new (GetCallbackBuffer()) Callback(std::move(callback)); + is_callback_engaged_ = true; + } + + Storage(Storage&& other) { + ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); - Storage(Storage&& other) - : callback_(std::move(other.callback_)), - is_callback_engaged_( - absl::exchange(other.is_callback_engaged_, false)) {} + ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); + is_callback_engaged_ = true; + + other.DestroyCallback(); + } Storage(const Storage& other) = delete; @@ -60,17 +70,26 @@ class Storage { Storage& operator=(const Storage& other) = delete; + void* GetCallbackBuffer() { return static_cast(+callback_buffer_); } + + Callback& GetCallback() { + return *reinterpret_cast(GetCallbackBuffer()); + } + bool IsCallbackEngaged() const { return is_callback_engaged_; } - void DisengageCallback() { is_callback_engaged_ = false; } + void DestroyCallback() { + is_callback_engaged_ = false; + GetCallback().~Callback(); + } void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS { - std::move(callback_)(); + std::move(GetCallback())(); } private: - Callback callback_; bool is_callback_engaged_; + alignas(Callback) char callback_buffer_[sizeof(Callback)]; }; } // namespace cleanup_internal diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index a5dee6aa..e219956f 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -23,6 +23,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +ABSL_CONST_INIT ABSL_DLL alignas(16) const ctrl_t kEmptyGroup[16] = { + ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; + constexpr size_t Group::kWidth; // Returns "random" seed. diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index d7783263..bafafd46 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -291,13 +291,9 @@ static_assert(ctrl_t::kDeleted == static_cast(-2), // A single block of empty control bytes for tables without any slots allocated. // This enables removing a branch in the hot path of find(). +ABSL_DLL extern const ctrl_t kEmptyGroup[16]; inline ctrl_t* EmptyGroup() { - alignas(16) static constexpr ctrl_t empty_group[] = { - ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; - return const_cast(empty_group); + return const_cast(kEmptyGroup); } // Mixes a randomly generated per-process seed with `hash` and `ctrl` to diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index 949709e8..a883567f 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -255,9 +256,6 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, matching_flags; flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) { - // Ignore retired flags. - if (flag.IsRetired()) return; - // If the flag has been stripped, pretend that it doesn't exist. if (flag.Help() == flags_internal::kStrippedFlagHelp) return; @@ -275,6 +273,14 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, absl::string_view file_separator; // controls blank lines between files for (auto& package : matching_flags) { if (format == HelpFormat::kHumanReadable) { + // Hide packages with only retired flags + bool all_package_flags_are_retired = true; + for (const auto& flags_in_file : package.second) { + for (const auto* flag : flags_in_file.second) { + all_package_flags_are_retired &= flag->IsRetired(); + } + } + if (all_package_flags_are_retired) continue; out << package_separator; package_separator = "\n\n"; } @@ -334,8 +340,11 @@ void FlagsHelpImpl(std::ostream& out, // Produces the help message describing specific flag. void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format) { - if (format == HelpFormat::kHumanReadable) + if (format == HelpFormat::kHumanReadable) { + // Ignore retired flags + if (flag.IsRetired()) return; flags_internal::FlagHelpHumanReadable(flag, out); + } } // -------------------------------------------------------------------- diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 044d71c8..5055640a 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -61,6 +61,9 @@ ABSL_FLAG( "Even more long long long long long long long long long long long long " "help message."); +ABSL_RETIRED_FLAG(int64_t, usage_reporting_test_flag_07, 1, + "usage_reporting_test_flag_07 help message"); + namespace { namespace flags = absl::flags_internal; diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 5160df79..17d88744 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -138,28 +138,21 @@ uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {} +#if !defined(ABSL_HAVE_INTRINSIC_INT128) uint128 operator/(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast(lhs) / - static_cast(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return quotient; -#endif // ABSL_HAVE_INTRINSIC_INT128 } + uint128 operator%(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast(lhs) % - static_cast(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return remainder; -#endif // ABSL_HAVE_INTRINSIC_INT128 } +#endif // !defined(ABSL_HAVE_INTRINSIC_INT128) namespace { diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 198aa195..a9cbe489 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -18,6 +18,10 @@ // ----------------------------------------------------------------------------- // // This header file defines 128-bit integer types, `uint128` and `int128`. +// +// TODO(absl-team): This module is inconsistent as many inline `uint128` methods +// are defined in this file, while many inline `int128` methods are defined in +// the `int128_*_intrinsic.inc` files. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -783,8 +787,13 @@ inline uint128::operator long double() const { // Comparison operators. inline bool operator==(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) == + static_cast(rhs); +#else return (Uint128Low64(lhs) == Uint128Low64(rhs) && Uint128High64(lhs) == Uint128High64(rhs)); +#endif } inline bool operator!=(uint128 lhs, uint128 rhs) { @@ -819,52 +828,76 @@ constexpr inline int128 operator+(int128 val) { } inline uint128 operator-(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return -static_cast(val); +#else uint64_t hi = ~Uint128High64(val); uint64_t lo = ~Uint128Low64(val) + 1; if (lo == 0) ++hi; // carry return MakeUint128(hi, lo); +#endif } constexpr inline bool operator!(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return !static_cast(val); +#else return !Uint128High64(val) && !Uint128Low64(val); +#endif } // Logical operators. constexpr inline uint128 operator~(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return ~static_cast(val); +#else return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif } constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) | + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), - Uint128Low64(lhs) | Uint128Low64(rhs)); + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif } constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) & + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), - Uint128Low64(lhs) & Uint128Low64(rhs)); + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif } constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) ^ + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), - Uint128Low64(lhs) ^ Uint128Low64(rhs)); + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif } inline uint128& uint128::operator|=(uint128 other) { - hi_ |= other.hi_; - lo_ |= other.lo_; + *this = *this | other; return *this; } inline uint128& uint128::operator&=(uint128 other) { - hi_ &= other.hi_; - lo_ &= other.lo_; + *this = *this & other; return *this; } inline uint128& uint128::operator^=(uint128 other) { - hi_ ^= other.hi_; - lo_ ^= other.lo_; + *this = *this ^ other; return *this; } @@ -907,21 +940,31 @@ inline uint128 operator>>(uint128 lhs, int amount) { } inline uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) + + static_cast(rhs); +#else uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), Uint128Low64(lhs) + Uint128Low64(rhs)); if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)); } return result; +#endif } inline uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) - + static_cast(rhs); +#else uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), Uint128Low64(lhs) - Uint128Low64(rhs)); if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)); } return result; +#endif } inline uint128 operator*(uint128 lhs, uint128 rhs) { @@ -951,6 +994,18 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { #endif // ABSL_HAVE_INTRINSIC128 } +#if defined(ABSL_HAVE_INTRINSIC_INT128) +inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast(lhs) / + static_cast(rhs); +} + +inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast(lhs) % + static_cast(rhs); +} +#endif + // Increment/decrement operators. inline uint128 uint128::operator++(int) { diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index ecbd2dbd..e9d72fa8 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -61,12 +61,6 @@ using ::absl::cord_internal::InlineData; using ::absl::cord_internal::kMaxFlatLength; using ::absl::cord_internal::kMinFlatLength; -using ::absl::cord_internal::BTREE; -using ::absl::cord_internal::CONCAT; -using ::absl::cord_internal::EXTERNAL; -using ::absl::cord_internal::FLAT; -using ::absl::cord_internal::SUBSTRING; - using ::absl::cord_internal::kInlinedVectorSize; using ::absl::cord_internal::kMaxBytesToCopy; @@ -106,7 +100,7 @@ static inline bool btree_enabled() { } static inline bool IsRootBalanced(CordRep* node) { - if (node->tag != CONCAT) { + if (!node->IsConcat()) { return true; } else if (node->concat()->depth() <= 15) { return true; @@ -143,7 +137,7 @@ static inline CordRep* VerifyTree(CordRep* node) { // Return the depth of a node static int Depth(const CordRep* rep) { - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { return rep->concat()->depth(); } else { return 0; @@ -176,7 +170,7 @@ static CordRep* RawConcat(CordRep* left, CordRep* right) { } CordRepConcat* rep = new CordRepConcat(); - rep->tag = CONCAT; + rep->tag = cord_internal::CONCAT; SetConcatChildren(rep, left, right); return rep; @@ -273,7 +267,7 @@ static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { CordRepSubstring* rep = new CordRepSubstring(); assert((offset + length) <= child->length); rep->length = length; - rep->tag = SUBSTRING; + rep->tag = cord_internal::SUBSTRING; rep->start = offset; rep->child = child; return VerifyTree(rep); @@ -347,7 +341,7 @@ inline void Cord::InlineRep::remove_prefix(size_t n) { // Returns `rep` converted into a CordRepBtree. // Directly returns `rep` if `rep` is already a CordRepBtree. static CordRepBtree* ForceBtree(CordRep* rep) { - return rep->tag == BTREE ? rep->btree() : CordRepBtree::Create(rep); + return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(rep); } void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, @@ -425,7 +419,7 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { - if (root->tag == BTREE && root->refcount.IsOne()) { + if (root->IsBtree() && root->refcount.IsOne()) { Span span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { *region = span.data(); @@ -436,11 +430,11 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->tag == CONCAT && dst->refcount.IsOne()) { + while (dst->IsConcat() && dst->refcount.IsOne()) { dst = dst->concat()->right; } - if (dst->tag < FLAT || !dst->refcount.IsOne()) { + if (!dst->IsFlat() || !dst->refcount.IsOne()) { *region = nullptr; *size = 0; return false; @@ -506,19 +500,20 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, CommitTree(root, rep, scope, method); } -// If the rep is a leaf, this will increment the value at total_mem_usage and -// will return true. -static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { +// Computes the memory side of the provided edge which must be a valid data edge +// for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node. +static bool RepMemoryUsageDataEdge(const CordRep* rep, + size_t* total_mem_usage) { size_t maybe_sub_size = 0; - if (rep->tag == SUBSTRING) { + if (ABSL_PREDICT_FALSE(rep->IsSubstring())) { maybe_sub_size = sizeof(cord_internal::CordRepSubstring); rep = rep->substring()->child; } - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize(); return true; } - if (rep->tag == EXTERNAL) { + if (rep->IsExternal()) { // We don't know anything about the embedded / bound data, but we can safely // assume it is 'at least' a word / pointer to data. In the future we may // choose to use the 'data' byte as a tag to identify the types of some @@ -531,6 +526,25 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { return false; } +// If the rep is a leaf, this will increment the value at total_mem_usage and +// will return true. +static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { + if (rep->IsFlat()) { + *total_mem_usage += rep->flat()->AllocatedSize(); + return true; + } + if (rep->IsExternal()) { + // We don't know anything about the embedded / bound data, but we can safely + // assume it is 'at least' a word / pointer to data. In the future we may + // choose to use the 'data' byte as a tag to identify the types of some + // well-known externals, such as a std::string instance. + *total_mem_usage += + sizeof(cord_internal::CordRepExternalImpl) + rep->length; + return true; + } + return false; +} + void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { assert(&src != this); assert(is_tree() || src.is_tree()); @@ -634,7 +648,7 @@ Cord& Cord::operator=(absl::string_view src) { } if (tree != nullptr) { CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag >= FLAT && tree->flat()->Capacity() >= length && + if (tree->IsFlat() && tree->flat()->Capacity() >= length && tree->refcount.IsOne()) { // Copy in place if the existing FLAT node is reusable. memmove(tree->flat()->Data(), data, length); @@ -753,7 +767,7 @@ inline void Cord::AppendImpl(C&& src) { contents_.AppendArray({src.contents_.data(), src_size}, method); return; } - if (src_tree->tag >= FLAT) { + if (src_tree->IsFlat()) { // src tree just has one flat node. contents_.AppendArray({src_tree->flat()->Data(), src_size}, method); return; @@ -843,7 +857,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { if (n == 0) return CordRep::Ref(node); absl::InlinedVector rhs_stack; - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->left->length) { // Push right to stack, descend left. @@ -862,7 +876,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { } else { size_t start = n; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { // Consider in-place update of node, similar to in RemoveSuffixFrom(). start += node->substring()->start; node = node->substring()->child; @@ -885,7 +899,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { absl::InlinedVector lhs_stack; bool inplace_ok = node->refcount.IsOne(); - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->right->length) { // Push left to stack, descend right. @@ -902,7 +916,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { if (n == 0) { CordRep::Ref(node); - } else if (inplace_ok && node->tag != EXTERNAL) { + } else if (inplace_ok && !node->IsExternal()) { // Consider making a new buffer if the current node capacity is much // larger than the new length. CordRep::Ref(node); @@ -910,7 +924,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { } else { size_t start = 0; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { start = node->substring()->start; node = node->substring()->child; } @@ -933,7 +947,7 @@ void Cord::RemovePrefix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemovePrefix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == BTREE) { + if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(n, tree->length - n); CordRep::Unref(old); @@ -956,7 +970,7 @@ void Cord::RemoveSuffix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == BTREE) { + if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(0, tree->length - n); CordRep::Unref(old); @@ -998,8 +1012,8 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { results.push_back(Concat(left, right)); } else if (pos == 0 && n == node->length) { results.push_back(CordRep::Ref(node)); - } else if (node->tag != CONCAT) { - if (node->tag == SUBSTRING) { + } else if (!node->IsConcat()) { + if (node->IsSubstring()) { pos += node->substring()->start; node = node->substring()->child; } @@ -1051,7 +1065,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { return sub_cord; } - if (tree->tag == BTREE) { + if (tree->IsBtree()) { tree = tree->btree()->SubTree(pos, new_size); } else { tree = NewSubRange(tree, pos, new_size); @@ -1076,7 +1090,7 @@ class CordForest { CordRep* node = pending.back(); pending.pop_back(); CheckNode(node); - if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) { + if (ABSL_PREDICT_FALSE(!node->IsConcat())) { AddNode(node); continue; } @@ -1170,7 +1184,7 @@ class CordForest { static void CheckNode(CordRep* node) { ABSL_INTERNAL_CHECK(node->length != 0u, ""); - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ""); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, ""); ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length + @@ -1190,7 +1204,7 @@ class CordForest { static CordRep* Rebalance(CordRep* node) { VerifyTree(node); - assert(node->tag == CONCAT); + assert(node->IsConcat()); if (node->length == 0) { return nullptr; @@ -1248,15 +1262,15 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { } CordRep* node = tree(); - if (node->tag >= FLAT) { + if (node->IsFlat()) { return absl::string_view(node->flat()->Data(), node->length); } - if (node->tag == EXTERNAL) { + if (node->IsExternal()) { return absl::string_view(node->external()->base, node->length); } - if (node->tag == BTREE) { + if (node->IsBtree()) { CordRepBtree* tree = node->btree(); int height = tree->height(); while (--height >= 0) { @@ -1266,7 +1280,7 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { } // Walk down the left branches until we hit a non-CONCAT node. - while (node->tag == CONCAT) { + while (node->IsConcat()) { node = node->concat()->left; } @@ -1275,16 +1289,16 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { size_t length = node->length; assert(length != 0); - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - if (node->tag >= FLAT) { + if (node->IsFlat()) { return absl::string_view(node->flat()->Data() + offset, length); } - assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here"); + assert(node->IsExternal() && "Expect FLAT or EXTERNAL node here"); return absl::string_view(node->external()->base + offset, length); } @@ -1478,7 +1492,7 @@ Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { // Walk down the left branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { stack_of_right_children.push_back(node->concat()->right); node = node->concat()->left; } @@ -1486,15 +1500,15 @@ Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length != 0); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset, length); current_leaf_ = node; return *this; @@ -1547,8 +1561,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Range to read is a proper subrange of the current chunk. assert(current_leaf_ != nullptr); CordRep* subnode = CordRep::Ref(current_leaf_); - const char* data = subnode->tag == EXTERNAL ? subnode->external()->base - : subnode->flat()->Data(); + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, n); subcord.contents_.EmplaceTree(VerifyTree(subnode), method); RemoveChunkPrefix(n); @@ -1560,8 +1574,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { assert(current_leaf_ != nullptr); CordRep* subnode = CordRep::Ref(current_leaf_); if (current_chunk_.size() < subnode->length) { - const char* data = subnode->tag == EXTERNAL ? subnode->external()->base - : subnode->flat()->Data(); + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, current_chunk_.size()); } @@ -1599,7 +1613,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. stack_of_right_children.push_back(node->concat()->right); @@ -1616,20 +1630,20 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } // Range to read ends with a proper (possibly empty) subrange of the current // chunk. - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); if (n > 0) { subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n)); } const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; @@ -1672,7 +1686,7 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. stack_of_right_children.push_back(node->concat()->right); @@ -1688,15 +1702,15 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; @@ -1712,15 +1726,15 @@ char Cord::operator[](size_t i) const { while (true) { assert(rep != nullptr); assert(offset < rep->length); - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { // Get the "i"th character directly from the flat array. return rep->flat()->Data()[offset]; - } else if (rep->tag == BTREE) { + } else if (rep->IsBtree()) { return rep->btree()->GetCharacter(offset); - } else if (rep->tag == EXTERNAL) { + } else if (rep->IsExternal()) { // Get the "i"th character from the external array. return rep->external()->base[offset]; - } else if (rep->tag == CONCAT) { + } else if (rep->IsConcat()) { // Recursively branch to the side of the concatenation that the "i"th // character is on. size_t left_length = rep->concat()->left->length; @@ -1732,7 +1746,7 @@ char Cord::operator[](size_t i) const { } } else { // This must be a substring a node, so bypass it to get to the child. - assert(rep->tag == SUBSTRING); + assert(rep->IsSubstring()); offset += rep->substring()->start; rep = rep->substring()->child; } @@ -1769,25 +1783,25 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { assert(rep != nullptr); - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { *fragment = absl::string_view(rep->flat()->Data(), rep->length); return true; - } else if (rep->tag == EXTERNAL) { + } else if (rep->IsExternal()) { *fragment = absl::string_view(rep->external()->base, rep->length); return true; - } else if (rep->tag == BTREE) { + } else if (rep->IsBtree()) { return rep->btree()->IsFlat(fragment); - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsSubstring()) { CordRep* child = rep->substring()->child; - if (child->tag >= FLAT) { + if (child->IsFlat()) { *fragment = absl::string_view( child->flat()->Data() + rep->substring()->start, rep->length); return true; - } else if (child->tag == EXTERNAL) { + } else if (child->IsExternal()) { *fragment = absl::string_view( child->external()->base + rep->substring()->start, rep->length); return true; - } else if (child->tag == BTREE) { + } else if (child->IsBtree()) { return child->btree()->IsFlat(rep->substring()->start, rep->length, fragment); } @@ -1798,7 +1812,7 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ void Cord::ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef callback) { - if (rep->tag == BTREE) { + if (rep->IsBtree()) { ChunkIterator it(rep), end; while (it != end) { callback(*it); @@ -1814,7 +1828,7 @@ absl::string_view Cord::FlattenSlowPath() { absl::cord_internal::CordRep* stack[stack_max]; absl::cord_internal::CordRep* current_node = rep; while (true) { - if (current_node->tag == CONCAT) { + if (current_node->IsConcat()) { if (stack_pos == stack_max) { // There's no more room on our stack array to add another right branch, // and the idea is to avoid allocations, so call this function @@ -1861,23 +1875,23 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << "]"; *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); *os << " " << std::setw(indent) << ""; - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { *os << "CONCAT depth=" << Depth(rep) << "\n"; indent += kIndentStep; indents.push_back(indent); stack.push_back(rep->concat()->right); rep = rep->concat()->left; - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsSubstring()) { *os << "SUBSTRING @ " << rep->substring()->start << "\n"; indent += kIndentStep; rep = rep->substring()->child; } else { // Leaf or ring - if (rep->tag == EXTERNAL) { + if (rep->IsExternal()) { *os << "EXTERNAL ["; if (include_data) *os << absl::CEscape(std::string(rep->external()->base, rep->length)); *os << "]\n"; - } else if (rep->tag >= FLAT) { + } else if (rep->IsFlat()) { *os << "FLAT cap=" << rep->flat()->Capacity() << " ["; if (include_data) *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length)); @@ -1915,7 +1929,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node)); } - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ReportError(root, node)); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, @@ -1927,13 +1941,13 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, worklist.push_back(node->concat()->right); worklist.push_back(node->concat()->left); } - } else if (node->tag >= FLAT) { + } else if (node->IsFlat()) { ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(), ReportError(root, node)); - } else if (node->tag == EXTERNAL) { + } else if (node->IsExternal()) { ABSL_INTERNAL_CHECK(node->external()->base != nullptr, ReportError(root, node)); - } else if (node->tag == SUBSTRING) { + } else if (node->IsSubstring()) { ABSL_INTERNAL_CHECK( node->substring()->start < node->substring()->child->length, ReportError(root, node)); @@ -1962,7 +1976,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, while (true) { const CordRep* next_node = nullptr; - if (cur_node->tag == CONCAT) { + if (cur_node->IsConcat()) { total_mem_usage += sizeof(CordRepConcat); const CordRep* left = cur_node->concat()->left; if (!RepMemoryUsageLeaf(left, &total_mem_usage)) { @@ -1976,12 +1990,12 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } next_node = right; } - } else if (cur_node->tag == BTREE) { + } else if (cur_node->IsBtree()) { total_mem_usage += sizeof(CordRepBtree); const CordRepBtree* node = cur_node->btree(); if (node->height() == 0) { for (const CordRep* edge : node->Edges()) { - RepMemoryUsageLeaf(edge, &total_mem_usage); + RepMemoryUsageDataEdge(edge, &total_mem_usage); } } else { for (const CordRep* edge : node->Edges()) { @@ -1990,7 +2004,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } } else { // Since cur_node is not a leaf or a concat node it must be a substring. - assert(cur_node->tag == SUBSTRING); + assert(cur_node->IsSubstring()); total_mem_usage += sizeof(CordRepSubstring); next_node = cur_node->substring()->child; if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) { diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 0c0a8a7a..d0296338 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -206,7 +206,32 @@ class CordTestPeer { ABSL_NAMESPACE_END } // namespace absl -TEST(Cord, AllFlatSizes) { +// The CordTest fixture runs all tests with and without Cord Btree enabled. +class CordTest : public testing::TestWithParam { + public: + CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) { + absl::cord_internal::cord_btree_enabled.store(UseBtree()); + } + ~CordTest() override { + absl::cord_internal::cord_btree_enabled.store(was_btree_); + } + + // Returns true if test is running with btree enabled. + bool UseBtree() const { return GetParam(); } + + // Returns human readable string representation of the test parameter. + static std::string ToString(testing::TestParamInfo param) { + return param.param ? "Btree" : "Concat"; + } + + private: + const bool was_btree_; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(), + CordTest::ToString); + +TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) { @@ -224,7 +249,7 @@ TEST(Cord, AllFlatSizes) { // We create a Cord at least 128GB in size using the fact that Cords can // internally reference-count; thus the Cord is enormous without actually // consuming very much memory. -TEST(GigabyteCord, FromExternal) { +TEST_P(CordTest, GigabyteCordFromExternal) { const size_t one_gig = 1024U * 1024U * 1024U; size_t max_size = 2 * one_gig; if (sizeof(max_size) > 4) max_size = 128 * one_gig; @@ -273,7 +298,7 @@ static absl::Cord MakeExternalCord(int size) { extern bool my_unique_true_boolean; bool my_unique_true_boolean = true; -TEST(Cord, Assignment) { +TEST_P(CordTest, Assignment) { absl::Cord x(absl::string_view("hi there")); absl::Cord y(x); ASSERT_EQ(std::string(x), "hi there"); @@ -327,7 +352,7 @@ TEST(Cord, Assignment) { } } -TEST(Cord, StartsEndsWith) { +TEST_P(CordTest, StartsEndsWith) { absl::Cord x(absl::string_view("abcde")); absl::Cord empty(""); @@ -360,7 +385,7 @@ TEST(Cord, StartsEndsWith) { ASSERT_TRUE(!empty.EndsWith("xyz")); } -TEST(Cord, Subcord) { +TEST_P(CordTest, Subcord) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); const std::string s = RandomLowercaseString(&rng, 1024); @@ -421,7 +446,7 @@ TEST(Cord, Subcord) { EXPECT_TRUE(sa.empty()); } -TEST(Cord, Swap) { +TEST_P(CordTest, Swap) { absl::string_view a("Dexter"); absl::string_view b("Mandark"); absl::Cord x(a); @@ -453,7 +478,7 @@ static void VerifyCopyToString(const absl::Cord& cord) { } } -TEST(Cord, CopyToString) { +TEST_P(CordTest, CopyToString) { VerifyCopyToString(absl::Cord()); VerifyCopyToString(absl::Cord("small cord")); VerifyCopyToString( @@ -461,45 +486,45 @@ TEST(Cord, CopyToString) { "copying ", "to ", "a ", "string."})); } -TEST(TryFlat, Empty) { +TEST_P(CordTest, TryFlatEmpty) { absl::Cord c; EXPECT_EQ(c.TryFlat(), ""); } -TEST(TryFlat, Flat) { +TEST_P(CordTest, TryFlatFlat) { absl::Cord c("hello"); EXPECT_EQ(c.TryFlat(), "hello"); } -TEST(TryFlat, SubstrInlined) { +TEST_P(CordTest, TryFlatSubstrInlined) { absl::Cord c("hello"); c.RemovePrefix(1); EXPECT_EQ(c.TryFlat(), "ello"); } -TEST(TryFlat, SubstrFlat) { +TEST_P(CordTest, TryFlatSubstrFlat) { absl::Cord c("longer than 15 bytes"); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes"); } -TEST(TryFlat, Concat) { +TEST_P(CordTest, TryFlatConcat) { absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"}); EXPECT_EQ(c.TryFlat(), absl::nullopt); } -TEST(TryFlat, External) { +TEST_P(CordTest, TryFlatExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); EXPECT_EQ(c.TryFlat(), "hell"); } -TEST(TryFlat, SubstrExternal) { +TEST_P(CordTest, TryFlatSubstrExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); EXPECT_EQ(sub.TryFlat(), "ell"); } -TEST(TryFlat, SubstrConcat) { +TEST_P(CordTest, TryFlatSubstrConcat) { absl::Cord c = absl::MakeFragmentedCord({"hello", " world"}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); EXPECT_EQ(sub.TryFlat(), absl::nullopt); @@ -507,7 +532,7 @@ TEST(TryFlat, SubstrConcat) { EXPECT_EQ(c.TryFlat(), absl::nullopt); } -TEST(TryFlat, CommonlyAssumedInvariants) { +TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) { // The behavior tested below is not part of the API contract of Cord, but it's // something we intend to be true in our current implementation. This test // exists to detect and prevent accidental breakage of the implementation. @@ -563,7 +588,7 @@ static void VerifyFlatten(absl::Cord c) { EXPECT_TRUE(IsFlat(c)); } -TEST(Cord, Flatten) { +TEST_P(CordTest, Flatten) { VerifyFlatten(absl::Cord()); VerifyFlatten(absl::Cord("small cord")); VerifyFlatten(absl::Cord("larger than small buffer optimization")); @@ -617,7 +642,7 @@ class TestData { }; } // namespace -TEST(Cord, MultipleLengths) { +TEST_P(CordTest, MultipleLengths) { TestData d; for (size_t i = 0; i < d.size(); i++) { std::string a = d.data(i); @@ -693,7 +718,7 @@ TEST(Cord, MultipleLengths) { namespace { -TEST(Cord, RemoveSuffixWithExternalOrSubstring) { +TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) { absl::Cord cord = absl::MakeCordFromExternal( "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); }); @@ -708,7 +733,7 @@ TEST(Cord, RemoveSuffixWithExternalOrSubstring) { EXPECT_EQ("foo", std::string(cord)); } -TEST(Cord, RemoveSuffixMakesZeroLengthNode) { +TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) { absl::Cord c; c.Append(absl::Cord(std::string(100, 'x'))); absl::Cord other_ref = c; // Prevent inplace appends @@ -735,7 +760,7 @@ absl::Cord CordWithZedBlock(size_t size) { } // Establish that ZedBlock does what we think it does. -TEST(CordSpliceTest, ZedBlock) { +TEST_P(CordTest, CordSpliceTestZedBlock) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); std::string s; @@ -743,7 +768,7 @@ TEST(CordSpliceTest, ZedBlock) { EXPECT_EQ("zzzzzzzzzz", s); } -TEST(CordSpliceTest, ZedBlock0) { +TEST_P(CordTest, CordSpliceTestZedBlock0) { absl::Cord blob = CordWithZedBlock(0); EXPECT_EQ(0, blob.size()); std::string s; @@ -751,7 +776,7 @@ TEST(CordSpliceTest, ZedBlock0) { EXPECT_EQ("", s); } -TEST(CordSpliceTest, ZedBlockSuffix1) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -763,7 +788,7 @@ TEST(CordSpliceTest, ZedBlockSuffix1) { } // Remove all of a prefix block -TEST(CordSpliceTest, ZedBlockSuffix0) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -795,7 +820,7 @@ absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset, } // Taking an empty suffix of a block breaks appending. -TEST(CordSpliceTest, RemoveEntireBlock1) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) { absl::Cord zero = CordWithZedBlock(10); absl::Cord suffix(zero); suffix.RemovePrefix(10); @@ -803,7 +828,7 @@ TEST(CordSpliceTest, RemoveEntireBlock1) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock2) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) { absl::Cord zero = CordWithZedBlock(10); absl::Cord prefix(zero); prefix.RemoveSuffix(10); @@ -813,7 +838,7 @@ TEST(CordSpliceTest, RemoveEntireBlock2) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock3) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) { absl::Cord blob = CordWithZedBlock(10); absl::Cord block = BigCord(10, 'b'); blob = SpliceCord(blob, 0, block); @@ -844,7 +869,7 @@ void VerifyComparison(const CordCompareTestCase& test_case) { << "LHS=" << rhs_string << "; RHS=" << lhs_string; } -TEST(Cord, Compare) { +TEST_P(CordTest, Compare) { absl::Cord subcord("aaaaaBBBBBcccccDDDDD"); subcord = subcord.Subcord(3, 10); @@ -907,7 +932,7 @@ TEST(Cord, Compare) { } } -TEST(Cord, CompareAfterAssign) { +TEST_P(CordTest, CompareAfterAssign) { absl::Cord a("aaaaaa1111111"); absl::Cord b("aaaaaa2222222"); a = "cccccc"; @@ -936,7 +961,7 @@ static void TestCompare(const absl::Cord& c, const absl::Cord& d, EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d; } -TEST(Compare, ComparisonIsUnsigned) { +TEST_P(CordTest, CompareComparisonIsUnsigned) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); std::uniform_int_distribution uniform_uint8(0, 255); char x = static_cast(uniform_uint8(rng)); @@ -945,7 +970,7 @@ TEST(Compare, ComparisonIsUnsigned) { absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng); } -TEST(Compare, RandomComparisons) { +TEST_P(CordTest, CompareRandomComparisons) { const int kIters = 5000; RandomEngine rng(GTEST_FLAG_GET(random_seed)); @@ -1003,43 +1028,43 @@ void CompareOperators() { EXPECT_FALSE(b <= a); } -TEST(ComparisonOperators, Cord_Cord) { +TEST_P(CordTest, ComparisonOperators_Cord_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_StringPiece) { +TEST_P(CordTest, ComparisonOperators_Cord_StringPiece) { CompareOperators(); } -TEST(ComparisonOperators, StringPiece_Cord) { +TEST_P(CordTest, ComparisonOperators_StringPiece_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_string) { +TEST_P(CordTest, ComparisonOperators_Cord_string) { CompareOperators(); } -TEST(ComparisonOperators, string_Cord) { +TEST_P(CordTest, ComparisonOperators_string_Cord) { CompareOperators(); } -TEST(ComparisonOperators, stdstring_Cord) { +TEST_P(CordTest, ComparisonOperators_stdstring_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_stdstring) { +TEST_P(CordTest, ComparisonOperators_Cord_stdstring) { CompareOperators(); } -TEST(ComparisonOperators, charstar_Cord) { +TEST_P(CordTest, ComparisonOperators_charstar_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_charstar) { +TEST_P(CordTest, ComparisonOperators_Cord_charstar) { CompareOperators(); } -TEST(ConstructFromExternal, ReleaserInvoked) { +TEST_P(CordTest, ConstructFromExternalReleaserInvoked) { // Empty external memory means the releaser should be called immediately. { bool invoked = false; @@ -1081,7 +1106,7 @@ TEST(ConstructFromExternal, ReleaserInvoked) { } } -TEST(ConstructFromExternal, CompareContents) { +TEST_P(CordTest, ConstructFromExternalCompareContents) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); for (int length = 1; length <= 2048; length *= 2) { @@ -1097,7 +1122,7 @@ TEST(ConstructFromExternal, CompareContents) { } } -TEST(ConstructFromExternal, LargeReleaser) { +TEST_P(CordTest, ConstructFromExternalLargeReleaser) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); constexpr size_t kLength = 256; std::string data = RandomLowercaseString(&rng, kLength); @@ -1112,7 +1137,7 @@ TEST(ConstructFromExternal, LargeReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, FunctionPointerReleaser) { +TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) { static absl::string_view data("hello world"); static bool invoked; auto* releaser = @@ -1129,7 +1154,7 @@ TEST(ConstructFromExternal, FunctionPointerReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, MoveOnlyReleaser) { +TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) { struct Releaser { explicit Releaser(bool* invoked) : invoked(invoked) {} Releaser(Releaser&& other) noexcept : invoked(other.invoked) {} @@ -1143,20 +1168,20 @@ TEST(ConstructFromExternal, MoveOnlyReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, NoArgLambda) { +TEST_P(CordTest, ConstructFromExternalNoArgLambda) { bool invoked = false; (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }); EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, StringViewArgLambda) { +TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) { bool invoked = false; (void)absl::MakeCordFromExternal( "dummy", [&invoked](absl::string_view) { invoked = true; }); EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { +TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) { struct Releaser { explicit Releaser(bool* destroyed) : destroyed(destroyed) {} ~Releaser() { *destroyed = true; } @@ -1171,7 +1196,7 @@ TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { EXPECT_TRUE(destroyed); } -TEST(ConstructFromExternal, ReferenceQualifierOverloads) { +TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) { struct Releaser { void operator()(absl::string_view) & { *lvalue_invoked = true; } void operator()(absl::string_view) && { *rvalue_invoked = true; } @@ -1199,7 +1224,7 @@ TEST(ConstructFromExternal, ReferenceQualifierOverloads) { EXPECT_TRUE(rvalue_invoked); } -TEST(ExternalMemory, BasicUsage) { +TEST_P(CordTest, ExternalMemoryBasicUsage) { static const char* strings[] = {"", "hello", "there"}; for (const char* str : strings) { absl::Cord dst("(prefix)"); @@ -1210,7 +1235,7 @@ TEST(ExternalMemory, BasicUsage) { } } -TEST(ExternalMemory, RemovePrefixSuffix) { +TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) { // Exhaustively try all sub-strings. absl::Cord cord = MakeComposite(); std::string s = std::string(cord); @@ -1225,7 +1250,7 @@ TEST(ExternalMemory, RemovePrefixSuffix) { } } -TEST(ExternalMemory, Get) { +TEST_P(CordTest, ExternalMemoryGet) { absl::Cord cord("hello"); AddExternalMemory(" world!", &cord); AddExternalMemory(" how are ", &cord); @@ -1244,16 +1269,16 @@ TEST(ExternalMemory, Get) { // Additionally we have some whiteboxed expectations based on our knowledge of // the layout and size of empty and inlined cords, and flat nodes. -TEST(CordMemoryUsage, Empty) { +TEST_P(CordTest, CordMemoryUsageEmpty) { EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage()); } -TEST(CordMemoryUsage, Embedded) { +TEST_P(CordTest, CordMemoryUsageEmbedded) { absl::Cord a("hello"); EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, EmbeddedAppend) { +TEST_P(CordTest, CordMemoryUsageEmbeddedAppend) { absl::Cord a("a"); absl::Cord b("bcd"); EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord)); @@ -1261,7 +1286,7 @@ TEST(CordMemoryUsage, EmbeddedAppend) { EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, ExternalMemory) { +TEST_P(CordTest, CordMemoryUsageExternalMemory) { static const int kLength = 1000; absl::Cord cord; AddExternalMemory(std::string(kLength, 'x'), &cord); @@ -1269,14 +1294,14 @@ TEST(CordMemoryUsage, ExternalMemory) { EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, Flat) { +TEST_P(CordTest, CordMemoryUsageFlat) { static const int kLength = 125; absl::Cord a(std::string(kLength, 'a')); EXPECT_GT(a.EstimatedMemoryUsage(), kLength); EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, AppendFlat) { +TEST_P(CordTest, CordMemoryUsageAppendFlat) { using absl::strings_internal::CordTestAccess; absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); size_t length = a.EstimatedMemoryUsage(); @@ -1286,9 +1311,32 @@ TEST(CordMemoryUsage, AppendFlat) { EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5); } +TEST_P(CordTest, CordMemoryUsageAppendExternal) { + static const int kLength = 1000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + size_t delta = a.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + +TEST_P(CordTest, CordMemoryUsageSubString) { + static const int kLength = 2000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(kLength, 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + absl::Cord b = a.Subcord(0, kLength + kLength / 2); + size_t delta = b.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + // Regtest for a change that had to be rolled back because it expanded out // of the InlineRep too soon, which was observable through MemoryUsage(). -TEST(CordMemoryUsage, InlineRep) { +TEST_P(CordTest, CordMemoryUsageInlineRep) { constexpr size_t kMaxInline = 15; // Cord::InlineRep::N const std::string small_string(kMaxInline, 'x'); absl::Cord c1(small_string); @@ -1302,7 +1350,7 @@ TEST(CordMemoryUsage, InlineRep) { } // namespace // Regtest for 7510292 (fix a bug introduced by 7465150) -TEST(Cord, Concat_Append) { +TEST_P(CordTest, Concat_Append) { // Create a rep of type CONCAT absl::Cord s1("foobarbarbarbarbar"); s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg"); @@ -1317,7 +1365,7 @@ TEST(Cord, Concat_Append) { EXPECT_EQ(s2.size(), size + 1); } -TEST(Cord, DiabolicalGrowth) { +TEST_P(CordTest, DiabolicalGrowth) { // This test exercises a diabolical Append() on a cord, making the // cord shared before each Append call resulting in a terribly fragmented // resulting cord. @@ -1337,7 +1385,7 @@ TEST(Cord, DiabolicalGrowth) { cord.EstimatedMemoryUsage()); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { +TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { absl::Cord fragmented = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); @@ -1357,7 +1405,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { +TEST_P(CordTest, MakeFragmentedCordFromVector) { std::vector chunks = {"A ", "fragmented ", "Cord"}; absl::Cord fragmented = absl::MakeFragmentedCord(chunks); @@ -1377,7 +1425,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(CordChunkIterator, Traits) { +TEST_P(CordTest, CordChunkIteratorTraits) { static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); @@ -1458,7 +1506,7 @@ static void VerifyChunkIterator(const absl::Cord& cord, EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT } -TEST(CordChunkIterator, Operations) { +TEST_P(CordTest, CordChunkIteratorOperations) { absl::Cord empty_cord; VerifyChunkIterator(empty_cord, 0); @@ -1628,7 +1676,7 @@ TEST(CordCharIterator, Operations) { VerifyCharIterator(subcords); } -TEST(Cord, StreamingOutput) { +TEST_P(CordTest, StreamingOutput) { absl::Cord c = absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); std::stringstream output; @@ -1636,7 +1684,7 @@ TEST(Cord, StreamingOutput) { EXPECT_EQ("A small fragmented Cord.", output.str()); } -TEST(Cord, ForEachChunk) { +TEST_P(CordTest, ForEachChunk) { for (int num_elements : {1, 10, 200}) { SCOPED_TRACE(num_elements); std::vector cord_chunks; @@ -1654,7 +1702,7 @@ TEST(Cord, ForEachChunk) { } } -TEST(Cord, SmallBufferAssignFromOwnData) { +TEST_P(CordTest, SmallBufferAssignFromOwnData) { constexpr size_t kMaxInline = 15; std::string contents = "small buff cord"; EXPECT_EQ(contents.size(), kMaxInline); @@ -1669,7 +1717,7 @@ TEST(Cord, SmallBufferAssignFromOwnData) { } } -TEST(Cord, Format) { +TEST_P(CordTest, Format) { absl::Cord c; absl::Format(&c, "There were %04d little %s.", 3, "pigs"); EXPECT_EQ(c, "There were 0003 little pigs."); @@ -1770,7 +1818,7 @@ struct LongView { }; -TEST(Cord, ConstinitConstructor) { +TEST_P(CordTest, ConstinitConstructor) { TestConstinitConstructor( absl::strings_internal::MakeStringConstant(ShortView{})); TestConstinitConstructor( diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 8ae644ba..b7f3f4c0 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -216,6 +216,14 @@ struct CordRep { // padding space from the base class (clang and gcc do, MSVC does not, etc) uint8_t storage[3]; + // Returns true if this instance's tag matches the requested type. + constexpr bool IsRing() const { return tag == RING; } + constexpr bool IsConcat() const { return tag == CONCAT; } + constexpr bool IsSubstring() const { return tag == SUBSTRING; } + constexpr bool IsExternal() const { return tag == EXTERNAL; } + constexpr bool IsFlat() const { return tag >= FLAT; } + constexpr bool IsBtree() const { return tag == BTREE; } + inline CordRepRing* ring(); inline const CordRepRing* ring() const; inline CordRepConcat* concat(); @@ -226,7 +234,6 @@ struct CordRep { inline const CordRepExternal* external() const; inline CordRepFlat* flat(); inline const CordRepFlat* flat() const; - inline CordRepBtree* btree(); inline const CordRepBtree* btree() const; @@ -277,7 +284,7 @@ struct CordRepExternal : public CordRep { ExternalReleaserInvoker releaser_invoker; // Deletes (releases) the external rep. - // Requires rep != nullptr and rep->tag == EXTERNAL + // Requires rep != nullptr and rep->IsExternal() static void Delete(CordRep* rep); }; @@ -320,7 +327,7 @@ struct CordRepExternalImpl }; inline void CordRepExternal::Delete(CordRep* rep) { - assert(rep != nullptr && rep->tag == EXTERNAL); + assert(rep != nullptr && rep->IsExternal()); auto* rep_external = static_cast(rep); assert(rep_external->releaser_invoker != nullptr); rep_external->releaser_invoker(rep_external); @@ -531,32 +538,32 @@ class InlineData { static_assert(sizeof(InlineData) == kMaxInline + 1, ""); inline CordRepConcat* CordRep::concat() { - assert(tag == CONCAT); + assert(IsConcat()); return static_cast(this); } inline const CordRepConcat* CordRep::concat() const { - assert(tag == CONCAT); + assert(IsConcat()); return static_cast(this); } inline CordRepSubstring* CordRep::substring() { - assert(tag == SUBSTRING); + assert(IsSubstring()); return static_cast(this); } inline const CordRepSubstring* CordRep::substring() const { - assert(tag == SUBSTRING); + assert(IsSubstring()); return static_cast(this); } inline CordRepExternal* CordRep::external() { - assert(tag == EXTERNAL); + assert(IsExternal()); return static_cast(this); } inline const CordRepExternal* CordRep::external() const { - assert(tag == EXTERNAL); + assert(IsExternal()); return static_cast(this); } diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index fd3a0045..8fe589fa 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -79,7 +79,7 @@ void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, // indented by two spaces per recursive depth. stream << std::string(depth * 2, ' ') << sharing << " (" << sptr << ") "; - if (rep->tag == BTREE) { + if (rep->IsBtree()) { const CordRepBtree* node = rep->btree(); std::string label = node->height() ? absl::StrCat("Node(", node->height(), ")") : "Leaf"; @@ -378,7 +378,7 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { } NODE_CHECK_VALID(tree != nullptr); - NODE_CHECK_EQ(tree->tag, BTREE); + NODE_CHECK_VALID(tree->IsBtree()); NODE_CHECK_VALID(tree->height() <= kMaxHeight); NODE_CHECK_VALID(tree->begin() < tree->capacity()); NODE_CHECK_VALID(tree->end() <= tree->capacity()); @@ -387,7 +387,7 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { for (CordRep* edge : tree->Edges()) { NODE_CHECK_VALID(edge != nullptr); if (tree->height() > 0) { - NODE_CHECK_VALID(edge->tag == BTREE); + NODE_CHECK_VALID(edge->IsBtree()); NODE_CHECK_VALID(edge->btree()->height() == tree->height() - 1); } else { NODE_CHECK_VALID(IsDataEdge(edge)); @@ -889,7 +889,7 @@ Span CordRepBtree::GetAppendBufferSlow(size_t size) { } CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { - if (rep->tag == BTREE) return rep->btree(); + if (rep->IsBtree()) return rep->btree(); CordRepBtree* node = nullptr; auto consume = [&node](CordRep* r, size_t offset, size_t length) { @@ -905,7 +905,7 @@ CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { } CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { - if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { return MergeTrees(tree, rep->btree()); } auto consume = [&tree](CordRep* r, size_t offset, size_t length) { @@ -917,7 +917,7 @@ CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { } CordRepBtree* CordRepBtree::PrependSlow(CordRepBtree* tree, CordRep* rep) { - if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { return MergeTrees(rep->btree(), tree); } auto consume = [&tree](CordRep* r, size_t offset, size_t length) { diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 56e1e4af..8f000cab 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -153,7 +153,7 @@ class CordRepBtree : public CordRep { }; // Creates a btree from the given input. Adopts a ref of `rep`. - // If the input `rep` is itself a btree, i.e., `tag == BTREE`, then this + // If the input `rep` is itself a btree, i.e., `IsBtree()`, then this // function immediately returns `rep->btree()`. If the input is a valid data // edge (see IsDataEdge()), then a new leaf node is returned containing `rep` // as the sole data edge. Else, the input is assumed to be a (legacy) concat @@ -514,12 +514,12 @@ class CordRepBtree : public CordRep { }; inline CordRepBtree* CordRep::btree() { - assert(tag == BTREE); + assert(IsBtree()); return static_cast(this); } inline const CordRepBtree* CordRep::btree() const { - assert(tag == BTREE); + assert(IsBtree()); return static_cast(this); } @@ -589,7 +589,7 @@ inline CordRepBtree* CordRepBtree::New(int height) { inline CordRepBtree* CordRepBtree::New(CordRep* rep) { CordRepBtree* tree = new CordRepBtree; - int height = rep->tag == BTREE ? rep->btree()->height() + 1 : 0; + int height = rep->IsBtree() ? rep->btree()->height() + 1 : 0; tree->length = rep->length; tree->InitInstance(height, /*begin=*/0, /*end=*/1); tree->edges_[0] = rep; diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index 20a6fc2b..db1f63fa 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -40,7 +40,7 @@ using index_type = CordRepRing::index_type; enum class Direction { kForward, kReversed }; inline bool IsFlatOrExternal(CordRep* rep) { - return rep->tag >= FLAT || rep->tag == EXTERNAL; + return rep->IsFlat() || rep->IsExternal(); } // Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise. @@ -229,7 +229,7 @@ void CordRepRing::SetCapacityForTesting(size_t capacity) { } void CordRepRing::Delete(CordRepRing* rep) { - assert(rep != nullptr && rep->tag == RING); + assert(rep != nullptr && rep->IsRing()); #if defined(__cpp_sized_deallocation) size_t size = AllocSize(rep->capacity_); rep->~CordRepRing(); @@ -360,7 +360,7 @@ CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { if (IsFlatOrExternal(child)) { return CreateFromLeaf(child, 0, length, extra); } - if (child->tag == RING) { + if (child->IsRing()) { return Mutable(child->ring(), extra); } return CreateSlow(child, extra); @@ -433,7 +433,7 @@ CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) { - if (child_arg->tag == RING) { + if (child_arg->IsRing()) { rep = AddRing(rep, child_arg->ring(), offset, len); } else { rep = AppendLeaf(rep, child_arg, offset, len); @@ -460,7 +460,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { if (IsFlatOrExternal(child)) { return AppendLeaf(rep, child, 0, length); } - if (child->tag == RING) { + if (child->IsRing()) { return AddRing(rep, child->ring(), 0, length); } return AppendSlow(rep, child); @@ -496,7 +496,7 @@ CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { if (IsFlatOrExternal(child)) { return PrependLeaf(rep, child, 0, length); } - if (child->tag == RING) { + if (child->IsRing()) { return AddRing(rep, child->ring(), 0, length); } return PrependSlow(rep, child); diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h index 2082a565..44db8494 100644 --- a/absl/strings/internal/cord_rep_ring.h +++ b/absl/strings/internal/cord_rep_ring.h @@ -570,12 +570,12 @@ inline CordRepRing::Position CordRepRing::FindTail(index_type head, // Now that CordRepRing is defined, we can define CordRep's helper casts: inline CordRepRing* CordRep::ring() { - assert(tag == RING); + assert(IsRing()); return static_cast(this); } inline const CordRepRing* CordRep::ring() const { - assert(tag == RING); + assert(IsRing()); return static_cast(this); }