summaryrefslogtreecommitdiff
path: root/absl/strings/cord_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/cord_test.cc')
-rw-r--r--absl/strings/cord_test.cc529
1 files changed, 465 insertions, 64 deletions
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index cced9bba..188fbc2e 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -34,9 +34,11 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/container/fixed_array.h"
+#include "absl/hash/hash.h"
#include "absl/random/random.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
@@ -192,10 +194,13 @@ class CordTestPeer {
static Cord MakeSubstring(Cord src, size_t offset, size_t length) {
ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined");
+ ABSL_RAW_CHECK(src.ExpectedChecksum() == absl::nullopt,
+ "Can not be hardened");
Cord cord;
auto* rep = new cord_internal::CordRepSubstring;
rep->tag = cord_internal::SUBSTRING;
- rep->child = cord_internal::CordRep::Ref(src.contents_.tree());
+ rep->child = cord_internal::CordRep::Ref(
+ cord_internal::SkipCrcNode(src.contents_.tree()));
rep->start = offset;
rep->length = length;
cord.contents_.EmplaceTree(rep,
@@ -207,8 +212,9 @@ class CordTestPeer {
ABSL_NAMESPACE_END
} // namespace absl
-// The CordTest fixture runs all tests with and without Cord Btree enabled.
-class CordTest : public testing::TestWithParam<bool> {
+// The CordTest fixture runs all tests with and without Cord Btree enabled,
+// and with our without expected CRCs being set on the subject Cords.
+class CordTest : public testing::TestWithParam<int> {
public:
CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) {
absl::cord_internal::cord_btree_enabled.store(UseBtree());
@@ -218,18 +224,40 @@ class CordTest : public testing::TestWithParam<bool> {
}
// Returns true if test is running with btree enabled.
- bool UseBtree() const { return GetParam(); }
+ bool UseBtree() const { return GetParam() == 1 || GetParam() == 3; }
+ bool UseCrc() const { return GetParam() == 2 || GetParam() == 3; }
+ void MaybeHarden(absl::Cord& c) {
+ if (UseCrc()) {
+ c.SetExpectedChecksum(1);
+ }
+ }
+ absl::Cord MaybeHardened(absl::Cord c) {
+ MaybeHarden(c);
+ return c;
+ }
// Returns human readable string representation of the test parameter.
- static std::string ToString(testing::TestParamInfo<bool> param) {
- return param.param ? "Btree" : "Concat";
+ static std::string ToString(testing::TestParamInfo<int> param) {
+ switch (param.param) {
+ case 0:
+ return "Concat";
+ case 1:
+ return "Btree";
+ case 2:
+ return "ConcatHardened";
+ case 3:
+ return "BtreeHardened";
+ default:
+ assert(false);
+ return "???";
+ }
}
private:
const bool was_btree_;
};
-INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(),
+INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3),
CordTest::ToString);
TEST_P(CordTest, AllFlatSizes) {
@@ -243,6 +271,7 @@ TEST_P(CordTest, AllFlatSizes) {
}
absl::Cord dst(src);
+ MaybeHarden(dst);
EXPECT_EQ(std::string(dst), src) << s;
}
}
@@ -274,6 +303,7 @@ TEST_P(CordTest, GigabyteCordFromExternal) {
c.Append(from);
c.Append(from);
c.Append(from);
+ MaybeHarden(c);
}
for (int i = 0; i < 1024; ++i) {
@@ -302,6 +332,8 @@ bool my_unique_true_boolean = true;
TEST_P(CordTest, Assignment) {
absl::Cord x(absl::string_view("hi there"));
absl::Cord y(x);
+ MaybeHarden(y);
+ ASSERT_EQ(x.ExpectedChecksum(), absl::nullopt);
ASSERT_EQ(std::string(x), "hi there");
ASSERT_EQ(std::string(y), "hi there");
ASSERT_TRUE(x == y);
@@ -355,6 +387,7 @@ TEST_P(CordTest, Assignment) {
TEST_P(CordTest, StartsEndsWith) {
absl::Cord x(absl::string_view("abcde"));
+ MaybeHarden(x);
absl::Cord empty("");
ASSERT_TRUE(x.StartsWith(absl::Cord("abcde")));
@@ -392,6 +425,7 @@ TEST_P(CordTest, Subcord) {
absl::Cord a;
AppendWithFragments(s, &rng, &a);
+ MaybeHarden(a);
ASSERT_EQ(s, std::string(a));
// Check subcords of a, from a variety of interesting points.
@@ -413,6 +447,9 @@ TEST_P(CordTest, Subcord) {
ASSERT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
std::string(sa))
<< a;
+ if (pos != 0 || end_pos != a.size()) {
+ ASSERT_EQ(sa.ExpectedChecksum(), absl::nullopt);
+ }
}
}
@@ -452,10 +489,19 @@ TEST_P(CordTest, Swap) {
absl::string_view b("Mandark");
absl::Cord x(a);
absl::Cord y(b);
+ MaybeHarden(x);
swap(x, y);
+ if (UseCrc()) {
+ ASSERT_EQ(x.ExpectedChecksum(), absl::nullopt);
+ ASSERT_EQ(y.ExpectedChecksum(), 1);
+ }
ASSERT_EQ(x, absl::Cord(b));
ASSERT_EQ(y, absl::Cord(a));
x.swap(y);
+ if (UseCrc()) {
+ ASSERT_EQ(x.ExpectedChecksum(), 1);
+ ASSERT_EQ(y.ExpectedChecksum(), absl::nullopt);
+ }
ASSERT_EQ(x, absl::Cord(a));
ASSERT_EQ(y, absl::Cord(b));
}
@@ -480,11 +526,11 @@ static void VerifyCopyToString(const absl::Cord& cord) {
}
TEST_P(CordTest, CopyToString) {
- VerifyCopyToString(absl::Cord());
- VerifyCopyToString(absl::Cord("small cord"));
- VerifyCopyToString(
+ VerifyCopyToString(absl::Cord()); // empty cords cannot carry CRCs
+ VerifyCopyToString(MaybeHardened(absl::Cord("small cord")));
+ VerifyCopyToString(MaybeHardened(
absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
- "copying ", "to ", "a ", "string."}));
+ "copying ", "to ", "a ", "string."})));
}
TEST_P(CordTest, TryFlatEmpty) {
@@ -494,40 +540,47 @@ TEST_P(CordTest, TryFlatEmpty) {
TEST_P(CordTest, TryFlatFlat) {
absl::Cord c("hello");
+ MaybeHarden(c);
EXPECT_EQ(c.TryFlat(), "hello");
}
TEST_P(CordTest, TryFlatSubstrInlined) {
absl::Cord c("hello");
c.RemovePrefix(1);
+ MaybeHarden(c);
EXPECT_EQ(c.TryFlat(), "ello");
}
TEST_P(CordTest, TryFlatSubstrFlat) {
absl::Cord c("longer than 15 bytes");
absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ MaybeHarden(sub);
EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes");
}
TEST_P(CordTest, TryFlatConcat) {
absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
+ MaybeHarden(c);
EXPECT_EQ(c.TryFlat(), absl::nullopt);
}
TEST_P(CordTest, TryFlatExternal) {
absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
+ MaybeHarden(c);
EXPECT_EQ(c.TryFlat(), "hell");
}
TEST_P(CordTest, TryFlatSubstrExternal) {
absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ MaybeHarden(sub);
EXPECT_EQ(sub.TryFlat(), "ell");
}
TEST_P(CordTest, TryFlatSubstrConcat) {
absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1);
+ MaybeHarden(sub);
EXPECT_EQ(sub.TryFlat(), absl::nullopt);
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), absl::nullopt);
@@ -547,6 +600,7 @@ TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) {
"returned by the ",
"iterator"};
absl::Cord c = absl::MakeFragmentedCord(fragments);
+ MaybeHarden(c);
int fragment = 0;
int offset = 0;
absl::Cord::CharIterator itc = c.char_begin();
@@ -591,13 +645,15 @@ static void VerifyFlatten(absl::Cord c) {
TEST_P(CordTest, Flatten) {
VerifyFlatten(absl::Cord());
- VerifyFlatten(absl::Cord("small cord"));
- VerifyFlatten(absl::Cord("larger than small buffer optimization"));
- VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));
+ VerifyFlatten(MaybeHardened(absl::Cord("small cord")));
+ VerifyFlatten(
+ MaybeHardened(absl::Cord("larger than small buffer optimization")));
+ VerifyFlatten(MaybeHardened(
+ absl::MakeFragmentedCord({"small ", "fragmented ", "cord"})));
// Test with a cord that is longer than the largest flat buffer
RandomEngine rng(GTEST_FLAG_GET(random_seed));
- VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
+ VerifyFlatten(MaybeHardened(absl::Cord(RandomLowercaseString(&rng, 8192))));
}
// Test data
@@ -651,22 +707,26 @@ TEST_P(CordTest, MultipleLengths) {
{ // Construct from Cord
absl::Cord tmp(a);
absl::Cord x(tmp);
+ MaybeHarden(x);
EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
}
{ // Construct from absl::string_view
absl::Cord x(a);
+ MaybeHarden(x);
EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
}
{ // Append cord to self
absl::Cord self(a);
+ MaybeHarden(self);
self.Append(self);
EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
}
{ // Prepend cord to self
absl::Cord self(a);
+ MaybeHarden(self);
self.Prepend(self);
EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
}
@@ -678,12 +738,14 @@ TEST_P(CordTest, MultipleLengths) {
{ // CopyFrom Cord
absl::Cord x(a);
absl::Cord y(b);
+ MaybeHarden(x);
x = y;
EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // CopyFrom absl::string_view
absl::Cord x(a);
+ MaybeHarden(x);
x = b;
EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
@@ -691,12 +753,14 @@ TEST_P(CordTest, MultipleLengths) {
{ // Cord::Append(Cord)
absl::Cord x(a);
absl::Cord y(b);
+ MaybeHarden(x);
x.Append(y);
EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // Cord::Append(absl::string_view)
absl::Cord x(a);
+ MaybeHarden(x);
x.Append(b);
EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
@@ -704,12 +768,14 @@ TEST_P(CordTest, MultipleLengths) {
{ // Cord::Prepend(Cord)
absl::Cord x(a);
absl::Cord y(b);
+ MaybeHarden(x);
x.Prepend(y);
EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
}
{ // Cord::Prepend(absl::string_view)
absl::Cord x(a);
+ MaybeHarden(x);
x.Prepend(b);
EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
}
@@ -722,13 +788,16 @@ namespace {
TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) {
absl::Cord cord = absl::MakeCordFromExternal(
"foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });
-
EXPECT_EQ("foo bar baz", std::string(cord));
+ MaybeHarden(cord);
+
// This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node.
cord.RemoveSuffix(4);
EXPECT_EQ("foo bar", std::string(cord));
+ MaybeHarden(cord);
+
// This RemoveSuffix() will adjust the SUBSTRING node in-place.
cord.RemoveSuffix(4);
EXPECT_EQ("foo", std::string(cord));
@@ -738,6 +807,7 @@ TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) {
absl::Cord c;
c.Append(absl::Cord(std::string(100, 'x')));
absl::Cord other_ref = c; // Prevent inplace appends
+ MaybeHarden(c);
c.Append(absl::Cord(std::string(200, 'y')));
c.RemoveSuffix(200);
EXPECT_EQ(std::string(100, 'x'), std::string(c));
@@ -763,6 +833,7 @@ absl::Cord CordWithZedBlock(size_t size) {
// Establish that ZedBlock does what we think it does.
TEST_P(CordTest, CordSpliceTestZedBlock) {
absl::Cord blob = CordWithZedBlock(10);
+ MaybeHarden(blob);
EXPECT_EQ(10, blob.size());
std::string s;
absl::CopyCordToString(blob, &s);
@@ -771,6 +842,7 @@ TEST_P(CordTest, CordSpliceTestZedBlock) {
TEST_P(CordTest, CordSpliceTestZedBlock0) {
absl::Cord blob = CordWithZedBlock(0);
+ MaybeHarden(blob);
EXPECT_EQ(0, blob.size());
std::string s;
absl::CopyCordToString(blob, &s);
@@ -779,6 +851,7 @@ TEST_P(CordTest, CordSpliceTestZedBlock0) {
TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) {
absl::Cord blob = CordWithZedBlock(10);
+ MaybeHarden(blob);
EXPECT_EQ(10, blob.size());
absl::Cord suffix(blob);
suffix.RemovePrefix(9);
@@ -791,6 +864,7 @@ TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) {
// Remove all of a prefix block
TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) {
absl::Cord blob = CordWithZedBlock(10);
+ MaybeHarden(blob);
EXPECT_EQ(10, blob.size());
absl::Cord suffix(blob);
suffix.RemovePrefix(10);
@@ -823,6 +897,7 @@ absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset,
// Taking an empty suffix of a block breaks appending.
TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) {
absl::Cord zero = CordWithZedBlock(10);
+ MaybeHarden(zero);
absl::Cord suffix(zero);
suffix.RemovePrefix(10);
absl::Cord result;
@@ -831,6 +906,7 @@ TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) {
TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) {
absl::Cord zero = CordWithZedBlock(10);
+ MaybeHarden(zero);
absl::Cord prefix(zero);
prefix.RemoveSuffix(10);
absl::Cord suffix(zero);
@@ -842,13 +918,19 @@ TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) {
TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) {
absl::Cord blob = CordWithZedBlock(10);
absl::Cord block = BigCord(10, 'b');
+ MaybeHarden(blob);
+ MaybeHarden(block);
blob = SpliceCord(blob, 0, block);
}
struct CordCompareTestCase {
template <typename LHS, typename RHS>
- CordCompareTestCase(const LHS& lhs, const RHS& rhs)
- : lhs_cord(lhs), rhs_cord(rhs) {}
+ CordCompareTestCase(const LHS& lhs, const RHS& rhs, bool use_crc)
+ : lhs_cord(lhs), rhs_cord(rhs) {
+ if (use_crc) {
+ lhs_cord.SetExpectedChecksum(1);
+ }
+ }
absl::Cord lhs_cord;
absl::Cord rhs_cord;
@@ -885,47 +967,54 @@ TEST_P(CordTest, Compare) {
concat2.Append("cccccccccccDDDDDDDDDDDDDD");
concat2.Append("DD");
+ const bool use_crc = UseCrc();
+
std::vector<CordCompareTestCase> test_cases = {{
// Inline cords
- {"abcdef", "abcdef"},
- {"abcdef", "abcdee"},
- {"abcdef", "abcdeg"},
- {"bbcdef", "abcdef"},
- {"bbcdef", "abcdeg"},
- {"abcdefa", "abcdef"},
- {"abcdef", "abcdefa"},
+ {"abcdef", "abcdef", use_crc},
+ {"abcdef", "abcdee", use_crc},
+ {"abcdef", "abcdeg", use_crc},
+ {"bbcdef", "abcdef", use_crc},
+ {"bbcdef", "abcdeg", use_crc},
+ {"abcdefa", "abcdef", use_crc},
+ {"abcdef", "abcdefa", use_crc},
// Small flat cords
- {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"},
- {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"},
- {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"},
- {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"},
- {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"},
- {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"},
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD", use_crc},
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD", use_crc},
+ {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD", use_crc},
+ {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX", use_crc},
+ {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD", use_crc},
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa", use_crc},
// Subcords
- {subcord, subcord},
- {subcord, "aaBBBBBccc"},
- {subcord, "aaBBBBBccd"},
- {subcord, "aaBBBBBccb"},
- {subcord, "aaBBBBBxcb"},
- {subcord, "aaBBBBBccca"},
- {subcord, "aaBBBBBcc"},
+ {subcord, subcord, use_crc},
+ {subcord, "aaBBBBBccc", use_crc},
+ {subcord, "aaBBBBBccd", use_crc},
+ {subcord, "aaBBBBBccb", use_crc},
+ {subcord, "aaBBBBBxcb", use_crc},
+ {subcord, "aaBBBBBccca", use_crc},
+ {subcord, "aaBBBBBcc", use_crc},
// Concats
- {concat, concat},
+ {concat, concat, use_crc},
{concat,
- "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"},
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD",
+ use_crc},
{concat,
- "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"},
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD",
+ use_crc},
{concat,
- "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"},
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD",
+ use_crc},
{concat,
- "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"},
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD",
+ use_crc},
{concat,
- "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"},
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe",
+ use_crc},
- {concat, concat2},
+ {concat, concat2, use_crc},
}};
for (const auto& tc : test_cases) {
@@ -936,6 +1025,7 @@ TEST_P(CordTest, Compare) {
TEST_P(CordTest, CompareAfterAssign) {
absl::Cord a("aaaaaa1111111");
absl::Cord b("aaaaaa2222222");
+ MaybeHarden(a);
a = "cccccc";
b = "cccccc";
EXPECT_EQ(a, b);
@@ -994,6 +1084,8 @@ TEST_P(CordTest, CompareRandomComparisons) {
d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
}
std::bernoulli_distribution coin_flip(0.5);
+ MaybeHarden(c);
+ MaybeHarden(d);
TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)),
coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng);
}
@@ -1119,6 +1211,7 @@ TEST_P(CordTest, ConstructFromExternalCompareContents) {
EXPECT_EQ(external->size(), sv.size());
delete external;
});
+ MaybeHarden(cord);
EXPECT_EQ(data, cord);
}
}
@@ -1134,7 +1227,7 @@ TEST_P(CordTest, ConstructFromExternalLargeReleaser) {
EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size()));
invoked = true;
};
- (void)absl::MakeCordFromExternal(data, releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal(data, releaser));
EXPECT_TRUE(invoked);
}
@@ -1147,11 +1240,11 @@ TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) {
invoked = true;
});
invoked = false;
- (void)absl::MakeCordFromExternal(data, releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal(data, releaser));
EXPECT_TRUE(invoked);
invoked = false;
- (void)absl::MakeCordFromExternal(data, *releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal(data, *releaser));
EXPECT_TRUE(invoked);
}
@@ -1165,20 +1258,21 @@ TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) {
};
bool invoked = false;
- (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked));
+ (void)MaybeHardened(absl::MakeCordFromExternal("dummy", Releaser(&invoked)));
EXPECT_TRUE(invoked);
}
TEST_P(CordTest, ConstructFromExternalNoArgLambda) {
bool invoked = false;
- (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
+ (void)MaybeHardened(
+ absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }));
EXPECT_TRUE(invoked);
}
TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) {
bool invoked = false;
- (void)absl::MakeCordFromExternal(
- "dummy", [&invoked](absl::string_view) { invoked = true; });
+ (void)MaybeHardened(absl::MakeCordFromExternal(
+ "dummy", [&invoked](absl::string_view) { invoked = true; }));
EXPECT_TRUE(invoked);
}
@@ -1193,7 +1287,7 @@ TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) {
bool destroyed = false;
Releaser releaser(&destroyed);
- (void)absl::MakeCordFromExternal("dummy", releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser));
EXPECT_TRUE(destroyed);
}
@@ -1209,18 +1303,18 @@ TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) {
bool lvalue_invoked = false;
bool rvalue_invoked = false;
Releaser releaser = {&lvalue_invoked, &rvalue_invoked};
- (void)absl::MakeCordFromExternal("", releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal("", releaser));
EXPECT_FALSE(lvalue_invoked);
EXPECT_TRUE(rvalue_invoked);
rvalue_invoked = false;
- (void)absl::MakeCordFromExternal("dummy", releaser);
+ (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser));
EXPECT_FALSE(lvalue_invoked);
EXPECT_TRUE(rvalue_invoked);
rvalue_invoked = false;
// NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type.
- (void)absl::MakeCordFromExternal("dummy", std::move(releaser));
+ (void)MaybeHardened(absl::MakeCordFromExternal("dummy", std::move(releaser)));
EXPECT_FALSE(lvalue_invoked);
EXPECT_TRUE(rvalue_invoked);
}
@@ -1229,7 +1323,9 @@ TEST_P(CordTest, ExternalMemoryBasicUsage) {
static const char* strings[] = {"", "hello", "there"};
for (const char* str : strings) {
absl::Cord dst("(prefix)");
+ MaybeHarden(dst);
AddExternalMemory(str, &dst);
+ MaybeHarden(dst);
dst.Append("(suffix)");
EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")),
std::string(dst));
@@ -1243,7 +1339,9 @@ TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) {
for (int offset = 0; offset <= s.size(); offset++) {
for (int length = 0; length <= s.size() - offset; length++) {
absl::Cord result(cord);
+ MaybeHarden(result);
result.RemovePrefix(offset);
+ MaybeHarden(result);
result.RemoveSuffix(result.size() - length);
EXPECT_EQ(s.substr(offset, length), std::string(result))
<< offset << " " << length;
@@ -1254,8 +1352,10 @@ TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) {
TEST_P(CordTest, ExternalMemoryGet) {
absl::Cord cord("hello");
AddExternalMemory(" world!", &cord);
+ MaybeHarden(cord);
AddExternalMemory(" how are ", &cord);
cord.Append(" you?");
+ MaybeHarden(cord);
std::string s = std::string(cord);
for (int i = 0; i < s.size(); i++) {
EXPECT_EQ(s[i], cord[i]);
@@ -1354,11 +1454,13 @@ TEST_P(CordTest, CordMemoryUsageInlineRep) {
TEST_P(CordTest, Concat_Append) {
// Create a rep of type CONCAT
absl::Cord s1("foobarbarbarbarbar");
+ MaybeHarden(s1);
s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
size_t size = s1.size();
// Create a copy of s1 and append to it.
absl::Cord s2 = s1;
+ MaybeHarden(s2);
s2.Append("x");
// 7465150 modifies s1 when it shouldn't.
@@ -1378,6 +1480,7 @@ TEST_P(CordTest, DiabolicalGrowth) {
for (char c : expected) {
absl::Cord shared(cord);
cord.Append(absl::string_view(&c, 1));
+ MaybeHarden(cord);
}
std::string value;
absl::CopyCordToString(cord, &value);
@@ -1422,8 +1525,12 @@ static absl::Cord MakeHuge(absl::string_view prefix) {
TEST_P(CordTest, HugeCord) {
absl::Cord cord = MakeHuge("huge cord");
+ MaybeHarden(cord);
+
+ const size_t acceptable_delta =
+ 100 + (UseCrc() ? sizeof(absl::cord_internal::CordRepCrc) : 0);
EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage());
- EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage());
+ EXPECT_GE(cord.size() + acceptable_delta, cord.EstimatedMemoryUsage());
}
// Tests that Append() works ok when handed a self reference
@@ -1433,6 +1540,7 @@ TEST_P(CordTest, AppendSelf) {
std::string control_data = "Abc";
absl::Cord data(control_data);
while (control_data.length() < 0x4000) {
+ MaybeHarden(data);
data.Append(data);
control_data.append(control_data);
ASSERT_EQ(control_data, data);
@@ -1443,6 +1551,8 @@ TEST_P(CordTest, MakeFragmentedCordFromInitializerList) {
absl::Cord fragmented =
absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
+ MaybeHarden(fragmented);
+
EXPECT_EQ("A fragmented Cord", fragmented);
auto chunk_it = fragmented.chunk_begin();
@@ -1463,6 +1573,8 @@ TEST_P(CordTest, MakeFragmentedCordFromVector) {
std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
absl::Cord fragmented = absl::MakeFragmentedCord(chunks);
+ MaybeHarden(fragmented);
+
EXPECT_EQ("A fragmented Cord", fragmented);
auto chunk_it = fragmented.chunk_begin();
@@ -1565,22 +1677,26 @@ TEST_P(CordTest, CordChunkIteratorOperations) {
VerifyChunkIterator(empty_cord, 0);
absl::Cord small_buffer_cord("small cord");
+ MaybeHarden(small_buffer_cord);
VerifyChunkIterator(small_buffer_cord, 1);
absl::Cord flat_node_cord("larger than small buffer optimization");
+ MaybeHarden(flat_node_cord);
VerifyChunkIterator(flat_node_cord, 1);
- VerifyChunkIterator(
- absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
- "testing ", "chunk ", "iterations."}),
- 8);
+ VerifyChunkIterator(MaybeHardened(absl::MakeFragmentedCord(
+ {"a ", "small ", "fragmented ", "cord ", "for ",
+ "testing ", "chunk ", "iterations."})),
+ 8);
absl::Cord reused_nodes_cord(std::string(40, 'c'));
reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b')));
+ MaybeHarden(reused_nodes_cord);
reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a')));
size_t expected_chunks = 3;
for (int i = 0; i < 8; ++i) {
reused_nodes_cord.Prepend(reused_nodes_cord);
+ MaybeHarden(reused_nodes_cord);
expected_chunks *= 2;
VerifyChunkIterator(reused_nodes_cord, expected_chunks);
}
@@ -1706,27 +1822,33 @@ TEST_P(CordTest, CharIteratorOperations) {
VerifyCharIterator(empty_cord);
absl::Cord small_buffer_cord("small cord");
+ MaybeHarden(small_buffer_cord);
VerifyCharIterator(small_buffer_cord);
absl::Cord flat_node_cord("larger than small buffer optimization");
+ MaybeHarden(flat_node_cord);
VerifyCharIterator(flat_node_cord);
- VerifyCharIterator(
+ VerifyCharIterator(MaybeHardened(
absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
- "testing ", "character ", "iteration."}));
+ "testing ", "character ", "iteration."})));
absl::Cord reused_nodes_cord("ghi");
reused_nodes_cord.Prepend(absl::Cord("def"));
reused_nodes_cord.Prepend(absl::Cord("abc"));
for (int i = 0; i < 4; ++i) {
reused_nodes_cord.Prepend(reused_nodes_cord);
+ MaybeHarden(reused_nodes_cord);
VerifyCharIterator(reused_nodes_cord);
}
RandomEngine rng(GTEST_FLAG_GET(random_seed));
absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
absl::Cord subcords;
- for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128));
+ for (int i = 0; i < 4; ++i) {
+ subcords.Prepend(flat_cord.Subcord(16 * i, 128));
+ MaybeHarden(subcords);
+ }
VerifyCharIterator(subcords);
}
@@ -1751,6 +1873,8 @@ TEST_P(CordTest, CharIteratorAdvanceAndRead) {
cord.Append(absl::Cord(block));
}
+ MaybeHarden(cord);
+
for (size_t chunk_size :
{kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) {
absl::Cord::CharIterator it = cord.char_begin();
@@ -1768,6 +1892,7 @@ TEST_P(CordTest, CharIteratorAdvanceAndRead) {
TEST_P(CordTest, StreamingOutput) {
absl::Cord c =
absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
+ MaybeHarden(c);
std::stringstream output;
output << c;
EXPECT_EQ("A small fragmented Cord.", output.str());
@@ -1781,6 +1906,7 @@ TEST_P(CordTest, ForEachChunk) {
cord_chunks.push_back(absl::StrCat("[", i, "]"));
}
absl::Cord c = absl::MakeFragmentedCord(cord_chunks);
+ MaybeHarden(c);
std::vector<std::string> iterated_chunks;
absl::CordTestPeer::ForEachChunk(c,
@@ -1798,6 +1924,7 @@ TEST_P(CordTest, SmallBufferAssignFromOwnData) {
for (size_t pos = 0; pos < contents.size(); ++pos) {
for (size_t count = contents.size() - pos; count > 0; --count) {
absl::Cord c(contents);
+ MaybeHarden(c);
absl::string_view flat = c.Flatten();
c = flat.substr(pos, count);
EXPECT_EQ(c, contents.substr(pos, count))
@@ -1810,12 +1937,16 @@ 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.");
+ MaybeHarden(c);
absl::Format(&c, "And %-3llx bad wolf!", 1);
+ MaybeHarden(c);
EXPECT_EQ(c, "There were 0003 little pigs.And 1 bad wolf!");
}
TEST_P(CordTest, Hardening) {
absl::Cord cord("hello");
+ MaybeHarden(cord);
+
// These statement should abort the program in all builds modes.
EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), "");
EXPECT_DEATH_IF_SUPPORTED(cord.RemoveSuffix(6), "");
@@ -1855,6 +1986,7 @@ TEST_P(CordTest, BtreeHostileSplitInsertJoin) {
}
for (int j = 0; j < 1000; ++j) {
+ MaybeHarden(cord);
size_t offset = absl::Uniform(bitgen, 0u, cord.size());
size_t length = absl::Uniform(bitgen, 100u, data.size());
if (cord.size() == offset) {
@@ -1955,3 +2087,272 @@ TEST_P(CordTest, ConstinitConstructor) {
TestConstinitConstructor(
absl::strings_internal::MakeStringConstant(LongView{}));
}
+
+namespace {
+
+// Test helper that generates a populated cord for future manipulation.
+//
+// By test convention, all generated cords begin with the characters "abcde" at
+// the start of the first chunk.
+class PopulatedCordFactory {
+ public:
+ constexpr PopulatedCordFactory(absl::string_view name,
+ absl::Cord (*generator)())
+ : name_(name), generator_(generator) {}
+
+ absl::string_view Name() const { return name_; }
+ absl::Cord Generate() const { return generator_(); }
+
+ private:
+ absl::string_view name_;
+ absl::Cord (*generator_)();
+};
+
+// clang-format off
+// This array is constant-initialized in conformant compilers.
+PopulatedCordFactory cord_factories[] = {
+ {"sso", [] { return absl::Cord("abcde"); }},
+ {"flat", [] {
+ // Too large to live in SSO space, but small enough to be a simple FLAT.
+ absl::Cord flat(absl::StrCat("abcde", std::string(1000, 'x')));
+ flat.Flatten();
+ return flat;
+ }},
+ {"external", [] {
+ // A cheat: we are using a string literal as the external storage, so a
+ // no-op releaser is correct here.
+ return absl::MakeCordFromExternal("abcde External!", []{});
+ }},
+ {"external substring", [] {
+ // A cheat: we are using a string literal as the external storage, so a
+ // no-op releaser is correct here.
+ absl::Cord ext = absl::MakeCordFromExternal("-abcde External!", []{});
+ return absl::CordTestPeer::MakeSubstring(ext, 1, ext.size() - 1);
+ }},
+ {"substring", [] {
+ absl::Cord flat(absl::StrCat("-abcde", std::string(1000, 'x')));
+ flat.Flatten();
+ return flat.Subcord(1, 998);
+ }},
+ {"fragmented", [] {
+ std::string fragment = absl::StrCat("abcde", std::string(195, 'x'));
+ std::vector<std::string> fragments(200, fragment);
+ absl::Cord cord = absl::MakeFragmentedCord(fragments);
+ assert(cord.size() == 40000);
+ return cord;
+ }},
+};
+// clang-format on
+
+// Test helper that can mutate a cord, and possibly undo the mutation, for
+// testing.
+class CordMutator {
+ public:
+ constexpr CordMutator(absl::string_view name, void (*mutate)(absl::Cord&),
+ void (*undo)(absl::Cord&) = nullptr)
+ : name_(name), mutate_(mutate), undo_(undo) {}
+
+ absl::string_view Name() const { return name_; }
+ void Mutate(absl::Cord& cord) const { mutate_(cord); }
+ bool CanUndo() const { return undo_ != nullptr; }
+ void Undo(absl::Cord& cord) const { undo_(cord); }
+
+ private:
+ absl::string_view name_;
+ void (*mutate_)(absl::Cord&);
+ void (*undo_)(absl::Cord&);
+};
+
+// clang-format off
+// This array is constant-initialized in conformant compilers.
+CordMutator cord_mutators[] ={
+ {"clear", [](absl::Cord& c) { c.Clear(); }},
+ {"overwrite", [](absl::Cord& c) { c = "overwritten"; }},
+ {
+ "append string",
+ [](absl::Cord& c) { c.Append("0123456789"); },
+ [](absl::Cord& c) { c.RemoveSuffix(10); }
+ },
+ {
+ "append cord",
+ [](absl::Cord& c) {
+ c.Append(absl::MakeFragmentedCord({"12345", "67890"}));
+ },
+ [](absl::Cord& c) { c.RemoveSuffix(10); }
+ },
+ {
+ "append checksummed cord",
+ [](absl::Cord& c) {
+ absl::Cord to_append = absl::MakeFragmentedCord({"12345", "67890"});
+ to_append.SetExpectedChecksum(999);
+ c.Append(to_append);
+ },
+ [](absl::Cord& c) { c.RemoveSuffix(10); }
+ },
+ {
+ "append self",
+ [](absl::Cord& c) { c.Append(c); },
+ [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }
+ },
+ {
+ "prepend string",
+ [](absl::Cord& c) { c.Prepend("9876543210"); },
+ [](absl::Cord& c) { c.RemovePrefix(10); }
+ },
+ {
+ "prepend cord",
+ [](absl::Cord& c) {
+ c.Prepend(absl::MakeFragmentedCord({"98765", "43210"}));
+ },
+ [](absl::Cord& c) { c.RemovePrefix(10); }
+ },
+ {
+ "prepend checksummed cord",
+ [](absl::Cord& c) {
+ absl::Cord to_prepend = absl::MakeFragmentedCord({"98765", "43210"});
+ to_prepend.SetExpectedChecksum(999);
+ c.Prepend(to_prepend);
+ },
+ [](absl::Cord& c) { c.RemovePrefix(10); }
+ },
+ {
+ "prepend self",
+ [](absl::Cord& c) { c.Prepend(c); },
+ [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }
+ },
+ {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(2); }},
+ {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(2); }},
+ {"subcord", [](absl::Cord& c) { c = c.Subcord(1, c.size() - 2); }},
+ {
+ "swap inline",
+ [](absl::Cord& c) {
+ absl::Cord other("swap");
+ c.swap(other);
+ }
+ },
+ {
+ "swap tree",
+ [](absl::Cord& c) {
+ absl::Cord other(std::string(10000, 'x'));
+ c.swap(other);
+ }
+ },
+};
+// clang-format on
+} // namespace
+
+TEST_P(CordTest, ExpectedChecksum) {
+ for (const PopulatedCordFactory& factory : cord_factories) {
+ SCOPED_TRACE(factory.Name());
+ for (bool shared : {false, true}) {
+ SCOPED_TRACE(shared);
+
+ absl::Cord shared_cord_source = factory.Generate();
+ auto make_instance = [=] {
+ return shared ? shared_cord_source : factory.Generate();
+ };
+
+ const absl::Cord base_value = factory.Generate();
+ const std::string base_value_as_string(factory.Generate().Flatten());
+
+ absl::Cord c1 = make_instance();
+ EXPECT_FALSE(c1.ExpectedChecksum().has_value());
+
+ // Setting an expected checksum works, and retains the cord's bytes
+ c1.SetExpectedChecksum(12345);
+ EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+ EXPECT_EQ(c1, base_value);
+
+ // CRC persists through copies, assignments, and moves:
+ absl::Cord c1_copy_construct = c1;
+ EXPECT_EQ(c1_copy_construct.ExpectedChecksum().value_or(0), 12345);
+
+ absl::Cord c1_copy_assign;
+ c1_copy_assign = c1;
+ EXPECT_EQ(c1_copy_assign.ExpectedChecksum().value_or(0), 12345);
+
+ absl::Cord c1_move(std::move(c1_copy_assign));
+ EXPECT_EQ(c1_move.ExpectedChecksum().value_or(0), 12345);
+
+ EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
+
+ // A CRC Cord compares equal to its non-CRC value.
+ EXPECT_EQ(c1, make_instance());
+
+ for (const CordMutator& mutator : cord_mutators) {
+ SCOPED_TRACE(mutator.Name());
+
+ // Test that mutating a cord removes its stored checksum
+ absl::Cord c2 = make_instance();
+ c2.SetExpectedChecksum(24680);
+
+ mutator.Mutate(c2);
+ EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
+
+ if (mutator.CanUndo()) {
+ // Undoing an operation should not restore the checksum
+ mutator.Undo(c2);
+ EXPECT_EQ(c2, base_value);
+ EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);
+ }
+ }
+
+ absl::Cord c3 = make_instance();
+ c3.SetExpectedChecksum(999);
+ const absl::Cord& cc3 = c3;
+
+ // Test that all cord reading operations function in the face of an
+ // expected checksum.
+
+ // Test data precondition
+ ASSERT_TRUE(cc3.StartsWith("abcde"));
+
+ EXPECT_EQ(cc3.size(), base_value_as_string.size());
+ EXPECT_FALSE(cc3.empty());
+ EXPECT_EQ(cc3.Compare(base_value), 0);
+ EXPECT_EQ(cc3.Compare(base_value_as_string), 0);
+ EXPECT_EQ(cc3.Compare("wxyz"), -1);
+ EXPECT_EQ(cc3.Compare(absl::Cord("wxyz")), -1);
+ EXPECT_EQ(cc3.Compare("aaaa"), 1);
+ EXPECT_EQ(cc3.Compare(absl::Cord("aaaa")), 1);
+ EXPECT_EQ(absl::Cord("wxyz").Compare(cc3), 1);
+ EXPECT_EQ(absl::Cord("aaaa").Compare(cc3), -1);
+ EXPECT_TRUE(cc3.StartsWith("abcd"));
+ EXPECT_EQ(std::string(cc3), base_value_as_string);
+
+ std::string dest;
+ absl::CopyCordToString(cc3, &dest);
+ EXPECT_EQ(dest, base_value_as_string);
+
+ bool first_pass = true;
+ for (absl::string_view chunk : cc3.Chunks()) {
+ if (first_pass) {
+ EXPECT_TRUE(absl::StartsWith(chunk, "abcde"));
+ }
+ first_pass = false;
+ }
+ first_pass = true;
+ for (char ch : cc3.Chars()) {
+ if (first_pass) {
+ EXPECT_EQ(ch, 'a');
+ }
+ first_pass = false;
+ }
+ EXPECT_TRUE(absl::StartsWith(*cc3.chunk_begin(), "abcde"));
+ EXPECT_EQ(*cc3.char_begin(), 'a');
+
+ auto char_it = cc3.char_begin();
+ absl::Cord::Advance(&char_it, 2);
+ EXPECT_EQ(absl::Cord::AdvanceAndRead(&char_it, 2), "cd");
+ EXPECT_EQ(*char_it, 'e');
+ char_it = cc3.char_begin();
+ absl::Cord::Advance(&char_it, 2);
+ EXPECT_TRUE(absl::StartsWith(absl::Cord::ChunkRemaining(char_it), "cde"));
+
+ EXPECT_EQ(cc3[0], 'a');
+ EXPECT_EQ(cc3[4], 'e');
+ EXPECT_EQ(absl::HashOf(cc3), absl::HashOf(base_value));
+ EXPECT_EQ(absl::HashOf(cc3), absl::HashOf(base_value_as_string));
+ }
+ }
+}