tvl-depot/absl/strings/cord_test.cc

1626 lines
49 KiB
C++
Raw Normal View History

Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
#include "absl/strings/cord.h"
#include <algorithm>
#include <climits>
#include <cstdio>
#include <iterator>
#include <map>
#include <numeric>
#include <random>
#include <sstream>
#include <type_traits>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/container/fixed_array.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
typedef std::mt19937_64 RandomEngine;
static std::string RandomLowercaseString(RandomEngine* rng);
static std::string RandomLowercaseString(RandomEngine* rng, size_t length);
static int GetUniformRandomUpTo(RandomEngine* rng, int upper_bound) {
if (upper_bound > 0) {
std::uniform_int_distribution<int> uniform(0, upper_bound - 1);
return uniform(*rng);
} else {
return 0;
}
}
static size_t GetUniformRandomUpTo(RandomEngine* rng, size_t upper_bound) {
if (upper_bound > 0) {
std::uniform_int_distribution<size_t> uniform(0, upper_bound - 1);
return uniform(*rng);
} else {
return 0;
}
}
static int32_t GenerateSkewedRandom(RandomEngine* rng, int max_log) {
const uint32_t base = (*rng)() % (max_log + 1);
const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
return (*rng)() & mask;
}
static std::string RandomLowercaseString(RandomEngine* rng) {
int length;
std::bernoulli_distribution one_in_1k(0.001);
std::bernoulli_distribution one_in_10k(0.0001);
// With low probability, make a large fragment
if (one_in_10k(*rng)) {
length = GetUniformRandomUpTo(rng, 1048576);
} else if (one_in_1k(*rng)) {
length = GetUniformRandomUpTo(rng, 10000);
} else {
length = GenerateSkewedRandom(rng, 10);
}
return RandomLowercaseString(rng, length);
}
static std::string RandomLowercaseString(RandomEngine* rng, size_t length) {
std::string result(length, '\0');
std::uniform_int_distribution<int> chars('a', 'z');
std::generate(result.begin(), result.end(),
[&]() { return static_cast<char>(chars(*rng)); });
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
return result;
}
static void DoNothing(absl::string_view /* data */, void* /* arg */) {}
static void DeleteExternalString(absl::string_view data, void* arg) {
std::string* s = reinterpret_cast<std::string*>(arg);
EXPECT_EQ(data, *s);
delete s;
}
// Add "s" to *dst via `MakeCordFromExternal`
static void AddExternalMemory(absl::string_view s, absl::Cord* dst) {
std::string* str = new std::string(s.data(), s.size());
dst->Append(absl::MakeCordFromExternal(*str, [str](absl::string_view data) {
DeleteExternalString(data, str);
}));
}
static void DumpGrowth() {
absl::Cord str;
for (int i = 0; i < 1000; i++) {
char c = 'a' + i % 26;
str.Append(absl::string_view(&c, 1));
}
}
// Make a Cord with some number of fragments. Return the size (in bytes)
// of the smallest fragment.
static size_t AppendWithFragments(const std::string& s, RandomEngine* rng,
absl::Cord* cord) {
size_t j = 0;
const size_t max_size = s.size() / 5; // Make approx. 10 fragments
size_t min_size = max_size; // size of smallest fragment
while (j < s.size()) {
size_t N = 1 + GetUniformRandomUpTo(rng, max_size);
if (N > (s.size() - j)) {
N = s.size() - j;
}
if (N < min_size) {
min_size = N;
}
std::bernoulli_distribution coin_flip(0.5);
if (coin_flip(*rng)) {
// Grow by adding an external-memory.
AddExternalMemory(absl::string_view(s.data() + j, N), cord);
} else {
cord->Append(absl::string_view(s.data() + j, N));
}
j += N;
}
return min_size;
}
// Add an external memory that contains the specified std::string to cord
static void AddNewStringBlock(const std::string& str, absl::Cord* dst) {
char* data = new char[str.size()];
memcpy(data, str.data(), str.size());
dst->Append(absl::MakeCordFromExternal(
absl::string_view(data, str.size()),
[](absl::string_view s) { delete[] s.data(); }));
}
// Make a Cord out of many different types of nodes.
static absl::Cord MakeComposite() {
absl::Cord cord;
cord.Append("the");
AddExternalMemory(" quick brown", &cord);
AddExternalMemory(" fox jumped", &cord);
absl::Cord full(" over");
AddExternalMemory(" the lazy", &full);
AddNewStringBlock(" dog slept the whole day away", &full);
absl::Cord substring = full.Subcord(0, 18);
// Make substring long enough to defeat the copying fast path in Append.
substring.Append(std::string(1000, '.'));
cord.Append(substring);
cord = cord.Subcord(0, cord.size() - 998); // Remove most of extra junk
return cord;
}
namespace absl {
ABSL_NAMESPACE_BEGIN
class CordTestPeer {
public:
static void ForEachChunk(
const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) {
c.ForEachChunk(callback);
}
};
ABSL_NAMESPACE_END
} // namespace absl
TEST(Cord, AllFlatSizes) {
using absl::strings_internal::CordTestAccess;
for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) {
// Make a string of length s.
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
std::string src;
while (src.size() < s) {
src.push_back('a' + (src.size() % 26));
}
absl::Cord dst(src);
EXPECT_EQ(std::string(dst), src) << s;
}
}
// 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) {
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;
size_t length = 128 * 1024;
char* data = new char[length];
absl::Cord from = absl::MakeCordFromExternal(
absl::string_view(data, length),
[](absl::string_view sv) { delete[] sv.data(); });
// This loop may seem odd due to its combination of exponential doubling of
// size and incremental size increases. We do it incrementally to be sure the
// Cord will need rebalancing and will exercise code that, in the past, has
// caused crashes in production. We grow exponentially so that the code will
// execute in a reasonable amount of time.
absl::Cord c;
ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
c.Append(from);
while (c.size() < max_size) {
c.Append(c);
c.Append(from);
c.Append(from);
c.Append(from);
c.Append(from);
}
for (int i = 0; i < 1024; ++i) {
c.Append(from);
}
ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
// Note: on a 32-bit build, this comes out to 2,818,048,000 bytes.
// Note: on a 64-bit build, this comes out to 171,932,385,280 bytes.
}
static absl::Cord MakeExternalCord(int size) {
char* buffer = new char[size];
memset(buffer, 'x', size);
absl::Cord cord;
cord.Append(absl::MakeCordFromExternal(
absl::string_view(buffer, size),
[](absl::string_view s) { delete[] s.data(); }));
return cord;
}
// Extern to fool clang that this is not constant. Needed to suppress
// a warning of unsafe code we want to test.
extern bool my_unique_true_boolean;
bool my_unique_true_boolean = true;
TEST(Cord, Assignment) {
absl::Cord x(absl::string_view("hi there"));
absl::Cord y(x);
ASSERT_EQ(std::string(x), "hi there");
ASSERT_EQ(std::string(y), "hi there");
ASSERT_TRUE(x == y);
ASSERT_TRUE(x <= y);
ASSERT_TRUE(y <= x);
x = absl::string_view("foo");
ASSERT_EQ(std::string(x), "foo");
ASSERT_EQ(std::string(y), "hi there");
ASSERT_TRUE(x < y);
ASSERT_TRUE(y > x);
ASSERT_TRUE(x != y);
ASSERT_TRUE(x <= y);
ASSERT_TRUE(y >= x);
x = "foo";
ASSERT_EQ(x, "foo");
// Test that going from inline rep to tree we don't leak memory.
std::vector<std::pair<absl::string_view, absl::string_view>>
test_string_pairs = {{"hi there", "foo"},
{"loooooong coooooord", "short cord"},
{"short cord", "loooooong coooooord"},
{"loooooong coooooord1", "loooooong coooooord2"}};
for (std::pair<absl::string_view, absl::string_view> test_strings :
test_string_pairs) {
absl::Cord tmp(test_strings.first);
absl::Cord z(std::move(tmp));
ASSERT_EQ(std::string(z), test_strings.first);
tmp = test_strings.second;
z = std::move(tmp);
ASSERT_EQ(std::string(z), test_strings.second);
}
{
// Test that self-move assignment doesn't crash/leak.
// Do not write such code!
absl::Cord my_small_cord("foo");
absl::Cord my_big_cord("loooooong coooooord");
// Bypass clang's warning on self move-assignment.
absl::Cord* my_small_alias =
my_unique_true_boolean ? &my_small_cord : &my_big_cord;
absl::Cord* my_big_alias =
!my_unique_true_boolean ? &my_small_cord : &my_big_cord;
*my_small_alias = std::move(my_small_cord);
*my_big_alias = std::move(my_big_cord);
// my_small_cord and my_big_cord are in an unspecified but valid
// state, and will be correctly destroyed here.
}
}
TEST(Cord, StartsEndsWith) {
absl::Cord x(absl::string_view("abcde"));
absl::Cord empty("");
ASSERT_TRUE(x.StartsWith(absl::Cord("abcde")));
ASSERT_TRUE(x.StartsWith(absl::Cord("abc")));
ASSERT_TRUE(x.StartsWith(absl::Cord("")));
ASSERT_TRUE(empty.StartsWith(absl::Cord("")));
ASSERT_TRUE(x.EndsWith(absl::Cord("abcde")));
ASSERT_TRUE(x.EndsWith(absl::Cord("cde")));
ASSERT_TRUE(x.EndsWith(absl::Cord("")));
ASSERT_TRUE(empty.EndsWith(absl::Cord("")));
ASSERT_TRUE(!x.StartsWith(absl::Cord("xyz")));
ASSERT_TRUE(!empty.StartsWith(absl::Cord("xyz")));
ASSERT_TRUE(!x.EndsWith(absl::Cord("xyz")));
ASSERT_TRUE(!empty.EndsWith(absl::Cord("xyz")));
ASSERT_TRUE(x.StartsWith("abcde"));
ASSERT_TRUE(x.StartsWith("abc"));
ASSERT_TRUE(x.StartsWith(""));
ASSERT_TRUE(empty.StartsWith(""));
ASSERT_TRUE(x.EndsWith("abcde"));
ASSERT_TRUE(x.EndsWith("cde"));
ASSERT_TRUE(x.EndsWith(""));
ASSERT_TRUE(empty.EndsWith(""));
ASSERT_TRUE(!x.StartsWith("xyz"));
ASSERT_TRUE(!empty.StartsWith("xyz"));
ASSERT_TRUE(!x.EndsWith("xyz"));
ASSERT_TRUE(!empty.EndsWith("xyz"));
}
TEST(Cord, Subcord) {
RandomEngine rng(testing::GTEST_FLAG(random_seed));
const std::string s = RandomLowercaseString(&rng, 1024);
absl::Cord a;
AppendWithFragments(s, &rng, &a);
ASSERT_EQ(s.size(), a.size());
// Check subcords of a, from a variety of interesting points.
std::set<size_t> positions;
for (int i = 0; i <= 32; ++i) {
positions.insert(i);
positions.insert(i * 32 - 1);
positions.insert(i * 32);
positions.insert(i * 32 + 1);
positions.insert(a.size() - i);
}
positions.insert(237);
positions.insert(732);
for (size_t pos : positions) {
if (pos > a.size()) continue;
for (size_t end_pos : positions) {
if (end_pos < pos || end_pos > a.size()) continue;
absl::Cord sa = a.Subcord(pos, end_pos - pos);
EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
std::string(sa))
<< a;
}
}
// Do the same thing for an inline cord.
const std::string sh = "short";
absl::Cord c(sh);
for (size_t pos = 0; pos <= sh.size(); ++pos) {
for (size_t n = 0; n <= sh.size() - pos; ++n) {
absl::Cord sc = c.Subcord(pos, n);
EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c;
}
}
// Check subcords of subcords.
absl::Cord sa = a.Subcord(0, a.size());
std::string ss = s.substr(0, s.size());
while (sa.size() > 1) {
sa = sa.Subcord(1, sa.size() - 2);
ss = ss.substr(1, ss.size() - 2);
EXPECT_EQ(ss, std::string(sa)) << a;
if (HasFailure()) break; // halt cascade
}
// It is OK to ask for too much.
sa = a.Subcord(0, a.size() + 1);
EXPECT_EQ(s, std::string(sa));
// It is OK to ask for something beyond the end.
sa = a.Subcord(a.size() + 1, 0);
EXPECT_TRUE(sa.empty());
sa = a.Subcord(a.size() + 1, 1);
EXPECT_TRUE(sa.empty());
}
TEST(Cord, Swap) {
absl::string_view a("Dexter");
absl::string_view b("Mandark");
absl::Cord x(a);
absl::Cord y(b);
swap(x, y);
ASSERT_EQ(x, absl::Cord(b));
ASSERT_EQ(y, absl::Cord(a));
}
static void VerifyCopyToString(const absl::Cord& cord) {
std::string initially_empty;
absl::CopyCordToString(cord, &initially_empty);
EXPECT_EQ(initially_empty, cord);
constexpr size_t kInitialLength = 1024;
std::string has_initial_contents(kInitialLength, 'x');
const char* address_before_copy = has_initial_contents.data();
absl::CopyCordToString(cord, &has_initial_contents);
EXPECT_EQ(has_initial_contents, cord);
if (cord.size() <= kInitialLength) {
EXPECT_EQ(has_initial_contents.data(), address_before_copy)
<< "CopyCordToString allocated new string storage; "
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
"has_initial_contents = \""
<< has_initial_contents << "\"";
}
}
TEST(Cord, CopyToString) {
VerifyCopyToString(absl::Cord());
VerifyCopyToString(absl::Cord("small cord"));
VerifyCopyToString(
absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
"copying ", "to ", "a ", "string."}));
}
TEST(TryFlat, Empty) {
absl::Cord c;
EXPECT_EQ(c.TryFlat(), "");
}
TEST(TryFlat, Flat) {
absl::Cord c("hello");
EXPECT_EQ(c.TryFlat(), "hello");
}
TEST(TryFlat, SubstrInlined) {
absl::Cord c("hello");
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), "ello");
}
TEST(TryFlat, SubstrFlat) {
absl::Cord c("longer than 15 bytes");
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
}
TEST(TryFlat, Concat) {
absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
EXPECT_EQ(c.TryFlat(), absl::nullopt);
}
TEST(TryFlat, External) {
absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
EXPECT_EQ(c.TryFlat(), "hell");
}
TEST(TryFlat, SubstrExternal) {
absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), "ell");
}
TEST(TryFlat, SubstrConcat) {
absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
c.RemovePrefix(1);
EXPECT_EQ(c.TryFlat(), absl::nullopt);
}
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
static bool IsFlat(const absl::Cord& c) {
return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
}
static void VerifyFlatten(absl::Cord c) {
std::string old_contents(c);
absl::string_view old_flat;
bool already_flat_and_non_empty = IsFlat(c) && !c.empty();
if (already_flat_and_non_empty) {
old_flat = *c.chunk_begin();
}
absl::string_view new_flat = c.Flatten();
// Verify that the contents of the flattened Cord are correct.
EXPECT_EQ(new_flat, old_contents);
EXPECT_EQ(std::string(c), old_contents);
// If the Cord contained data and was already flat, verify that the data
// wasn't copied.
if (already_flat_and_non_empty) {
EXPECT_EQ(old_flat.data(), new_flat.data())
<< "Allocated new memory even though the Cord was already flat.";
}
// Verify that the flattened Cord is in fact flat.
EXPECT_TRUE(IsFlat(c));
}
TEST(Cord, Flatten) {
VerifyFlatten(absl::Cord());
VerifyFlatten(absl::Cord("small cord"));
VerifyFlatten(absl::Cord("larger than small buffer optimization"));
VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));
// Test with a cord that is longer than the largest flat buffer
RandomEngine rng(testing::GTEST_FLAG(random_seed));
VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
}
// Test data
namespace {
class TestData {
private:
std::vector<std::string> data_;
// Return a std::string of the specified length.
static std::string MakeString(int length) {
std::string result;
char buf[30];
snprintf(buf, sizeof(buf), "(%d)", length);
while (result.size() < length) {
result += buf;
}
result.resize(length);
return result;
}
public:
TestData() {
// short strings increasing in length by one
for (int i = 0; i < 30; i++) {
data_.push_back(MakeString(i));
}
// strings around half kMaxFlatLength
static const int kMaxFlatLength = 4096 - 9;
static const int kHalf = kMaxFlatLength / 2;
for (int i = -10; i <= +10; i++) {
data_.push_back(MakeString(kHalf + i));
}
for (int i = -10; i <= +10; i++) {
data_.push_back(MakeString(kMaxFlatLength + i));
}
}
size_t size() const { return data_.size(); }
const std::string& data(size_t i) const { return data_[i]; }
};
} // namespace
TEST(Cord, MultipleLengths) {
TestData d;
for (size_t i = 0; i < d.size(); i++) {
std::string a = d.data(i);
{ // Construct from Cord
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord tmp(a);
absl::Cord x(tmp);
EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
}
{ // Construct from absl::string_view
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
}
{ // Append cord to self
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord self(a);
self.Append(self);
EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
}
{ // Prepend cord to self
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord self(a);
self.Prepend(self);
EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
}
// Try to append/prepend others
for (size_t j = 0; j < d.size(); j++) {
std::string b = d.data(j);
{ // CopyFrom Cord
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
absl::Cord y(b);
x = y;
EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // CopyFrom absl::string_view
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
x = b;
EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // Cord::Append(Cord)
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
absl::Cord y(b);
x.Append(y);
EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // Cord::Append(absl::string_view)
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
x.Append(b);
EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
}
{ // Cord::Prepend(Cord)
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
absl::Cord y(b);
x.Prepend(y);
EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
}
{ // Cord::Prepend(absl::string_view)
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
absl::Cord x(a);
x.Prepend(b);
EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
}
}
}
}
namespace {
TEST(Cord, RemoveSuffixWithExternalOrSubstring) {
absl::Cord cord = absl::MakeCordFromExternal(
"foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });
EXPECT_EQ("foo bar baz", std::string(cord));
// This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node.
cord.RemoveSuffix(4);
EXPECT_EQ("foo bar", std::string(cord));
// This RemoveSuffix() will adjust the SUBSTRING node in-place.
cord.RemoveSuffix(4);
EXPECT_EQ("foo", std::string(cord));
}
TEST(Cord, RemoveSuffixMakesZeroLengthNode) {
absl::Cord c;
c.Append(absl::Cord(std::string(100, 'x')));
absl::Cord other_ref = c; // Prevent inplace appends
c.Append(absl::Cord(std::string(200, 'y')));
c.RemoveSuffix(200);
EXPECT_EQ(std::string(100, 'x'), std::string(c));
}
} // namespace
// CordSpliceTest contributed by hendrie.
namespace {
// Create a cord with an external memory block filled with 'z'
absl::Cord CordWithZedBlock(size_t size) {
char* data = new char[size];
if (size > 0) {
memset(data, 'z', size);
}
absl::Cord cord = absl::MakeCordFromExternal(
absl::string_view(data, size),
[](absl::string_view s) { delete[] s.data(); });
return cord;
}
// Establish that ZedBlock does what we think it does.
TEST(CordSpliceTest, ZedBlock) {
absl::Cord blob = CordWithZedBlock(10);
EXPECT_EQ(10, blob.size());
std::string s;
absl::CopyCordToString(blob, &s);
EXPECT_EQ("zzzzzzzzzz", s);
}
TEST(CordSpliceTest, ZedBlock0) {
absl::Cord blob = CordWithZedBlock(0);
EXPECT_EQ(0, blob.size());
std::string s;
absl::CopyCordToString(blob, &s);
EXPECT_EQ("", s);
}
TEST(CordSpliceTest, ZedBlockSuffix1) {
absl::Cord blob = CordWithZedBlock(10);
EXPECT_EQ(10, blob.size());
absl::Cord suffix(blob);
suffix.RemovePrefix(9);
EXPECT_EQ(1, suffix.size());
std::string s;
absl::CopyCordToString(suffix, &s);
EXPECT_EQ("z", s);
}
// Remove all of a prefix block
TEST(CordSpliceTest, ZedBlockSuffix0) {
absl::Cord blob = CordWithZedBlock(10);
EXPECT_EQ(10, blob.size());
absl::Cord suffix(blob);
suffix.RemovePrefix(10);
EXPECT_EQ(0, suffix.size());
std::string s;
absl::CopyCordToString(suffix, &s);
EXPECT_EQ("", s);
}
absl::Cord BigCord(size_t len, char v) {
std::string s(len, v);
return absl::Cord(s);
}
// Splice block into cord.
absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset,
const absl::Cord& block) {
ABSL_RAW_CHECK(offset >= 0, "");
ABSL_RAW_CHECK(offset + block.size() <= blob.size(), "");
absl::Cord result(blob);
result.RemoveSuffix(blob.size() - offset);
result.Append(block);
absl::Cord suffix(blob);
suffix.RemovePrefix(offset + block.size());
result.Append(suffix);
ABSL_RAW_CHECK(blob.size() == result.size(), "");
return result;
}
// Taking an empty suffix of a block breaks appending.
TEST(CordSpliceTest, RemoveEntireBlock1) {
absl::Cord zero = CordWithZedBlock(10);
absl::Cord suffix(zero);
suffix.RemovePrefix(10);
absl::Cord result;
result.Append(suffix);
}
TEST(CordSpliceTest, RemoveEntireBlock2) {
absl::Cord zero = CordWithZedBlock(10);
absl::Cord prefix(zero);
prefix.RemoveSuffix(10);
absl::Cord suffix(zero);
suffix.RemovePrefix(10);
absl::Cord result(prefix);
result.Append(suffix);
}
TEST(CordSpliceTest, RemoveEntireBlock3) {
absl::Cord blob = CordWithZedBlock(10);
absl::Cord block = BigCord(10, 'b');
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) {}
absl::Cord lhs_cord;
absl::Cord rhs_cord;
};
const auto sign = [](int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); };
void VerifyComparison(const CordCompareTestCase& test_case) {
std::string lhs_string(test_case.lhs_cord);
std::string rhs_string(test_case.rhs_cord);
int expected = sign(lhs_string.compare(rhs_string));
EXPECT_EQ(expected, test_case.lhs_cord.Compare(test_case.rhs_cord))
<< "LHS=" << lhs_string << "; RHS=" << rhs_string;
EXPECT_EQ(expected, test_case.lhs_cord.Compare(rhs_string))
<< "LHS=" << lhs_string << "; RHS=" << rhs_string;
EXPECT_EQ(-expected, test_case.rhs_cord.Compare(test_case.lhs_cord))
<< "LHS=" << rhs_string << "; RHS=" << lhs_string;
EXPECT_EQ(-expected, test_case.rhs_cord.Compare(lhs_string))
<< "LHS=" << rhs_string << "; RHS=" << lhs_string;
}
TEST(Cord, Compare) {
absl::Cord subcord("aaaaaBBBBBcccccDDDDD");
subcord = subcord.Subcord(3, 10);
absl::Cord tmp("aaaaaaaaaaaaaaaa");
tmp.Append("BBBBBBBBBBBBBBBB");
absl::Cord concat = absl::Cord("cccccccccccccccc");
concat.Append("DDDDDDDDDDDDDDDD");
concat.Prepend(tmp);
absl::Cord concat2("aaaaaaaaaaaaa");
concat2.Append("aaaBBBBBBBBBBBBBBBBccccc");
concat2.Append("cccccccccccDDDDDDDDDDDDDD");
concat2.Append("DD");
std::vector<CordCompareTestCase> test_cases = {{
// Inline cords
{"abcdef", "abcdef"},
{"abcdef", "abcdee"},
{"abcdef", "abcdeg"},
{"bbcdef", "abcdef"},
{"bbcdef", "abcdeg"},
{"abcdefa", "abcdef"},
{"abcdef", "abcdefa"},
// Small flat cords
{"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"},
{"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"},
{"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"},
{"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"},
{"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"},
{"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"},
// Subcords
{subcord, subcord},
{subcord, "aaBBBBBccc"},
{subcord, "aaBBBBBccd"},
{subcord, "aaBBBBBccb"},
{subcord, "aaBBBBBxcb"},
{subcord, "aaBBBBBccca"},
{subcord, "aaBBBBBcc"},
// Concats
{concat, concat},
{concat,
"aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"},
{concat,
"aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"},
{concat,
"aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"},
{concat,
"aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"},
{concat,
"aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"},
{concat, concat2},
}};
for (const auto& tc : test_cases) {
VerifyComparison(tc);
}
}
TEST(Cord, CompareAfterAssign) {
absl::Cord a("aaaaaa1111111");
absl::Cord b("aaaaaa2222222");
a = "cccccc";
b = "cccccc";
EXPECT_EQ(a, b);
EXPECT_FALSE(a < b);
a = "aaaa";
b = "bbbbb";
a = "";
b = "";
EXPECT_EQ(a, b);
EXPECT_FALSE(a < b);
}
// Test CompareTo() and ComparePrefix() against string and substring
// comparison methods from basic_string.
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
static void TestCompare(const absl::Cord& c, const absl::Cord& d,
RandomEngine* rng) {
typedef std::basic_string<uint8_t> ustring;
ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size());
ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size());
// ustring comparison is ideal because we expect Cord comparisons to be
// based on unsigned byte comparisons regardless of whether char is signed.
int expected = sign(cs.compare(ds));
EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
}
TEST(Compare, ComparisonIsUnsigned) {
RandomEngine rng(testing::GTEST_FLAG(random_seed));
std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255);
char x = static_cast<char>(uniform_uint8(rng));
TestCompare(
absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x)),
absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng);
}
TEST(Compare, RandomComparisons) {
const int kIters = 5000;
RandomEngine rng(testing::GTEST_FLAG(random_seed));
int n = GetUniformRandomUpTo(&rng, 5000);
absl::Cord a[] = {MakeExternalCord(n),
absl::Cord("ant"),
absl::Cord("elephant"),
absl::Cord("giraffe"),
absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100),
GetUniformRandomUpTo(&rng, 100))),
absl::Cord(""),
absl::Cord("x"),
absl::Cord("A"),
absl::Cord("B"),
absl::Cord("C")};
for (int i = 0; i < kIters; i++) {
absl::Cord c, d;
for (int j = 0; j < (i % 7) + 1; j++) {
c.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
}
std::bernoulli_distribution coin_flip(0.5);
TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)),
coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng);
}
}
template <typename T1, typename T2>
void CompareOperators() {
const T1 a("a");
const T2 b("b");
EXPECT_TRUE(a == a);
// For pointer type (i.e. `const char*`), operator== compares the address
// instead of the string, so `a == const char*("a")` isn't necessarily true.
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a"));
EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a"));
EXPECT_FALSE(a == b);
EXPECT_TRUE(a != b);
EXPECT_FALSE(a != a);
EXPECT_TRUE(a < b);
EXPECT_FALSE(b < a);
EXPECT_TRUE(b > a);
EXPECT_FALSE(a > b);
EXPECT_TRUE(a >= a);
EXPECT_TRUE(b >= a);
EXPECT_FALSE(a >= b);
EXPECT_TRUE(a <= a);
EXPECT_TRUE(a <= b);
EXPECT_FALSE(b <= a);
}
TEST(ComparisonOperators, Cord_Cord) {
CompareOperators<absl::Cord, absl::Cord>();
}
TEST(ComparisonOperators, Cord_StringPiece) {
CompareOperators<absl::Cord, absl::string_view>();
}
TEST(ComparisonOperators, StringPiece_Cord) {
CompareOperators<absl::string_view, absl::Cord>();
}
TEST(ComparisonOperators, Cord_string) {
CompareOperators<absl::Cord, std::string>();
}
TEST(ComparisonOperators, string_Cord) {
CompareOperators<std::string, absl::Cord>();
}
TEST(ComparisonOperators, stdstring_Cord) {
CompareOperators<std::string, absl::Cord>();
}
TEST(ComparisonOperators, Cord_stdstring) {
CompareOperators<absl::Cord, std::string>();
}
TEST(ComparisonOperators, charstar_Cord) {
CompareOperators<const char*, absl::Cord>();
}
TEST(ComparisonOperators, Cord_charstar) {
CompareOperators<absl::Cord, const char*>();
}
TEST(ConstructFromExternal, ReleaserInvoked) {
// Empty external memory means the releaser should be called immediately.
{
bool invoked = false;
auto releaser = [&invoked](absl::string_view) { invoked = true; };
{
auto c = absl::MakeCordFromExternal("", releaser);
EXPECT_TRUE(invoked);
}
}
// If the size of the data is small enough, a future constructor
// implementation may copy the bytes and immediately invoke the releaser
// instead of creating an external node. We make a large dummy std::string to
// make this test independent of such an optimization.
std::string large_dummy(2048, 'c');
{
bool invoked = false;
auto releaser = [&invoked](absl::string_view) { invoked = true; };
{
auto c = absl::MakeCordFromExternal(large_dummy, releaser);
EXPECT_FALSE(invoked);
}
EXPECT_TRUE(invoked);
}
{
bool invoked = false;
auto releaser = [&invoked](absl::string_view) { invoked = true; };
{
absl::Cord copy;
{
auto c = absl::MakeCordFromExternal(large_dummy, releaser);
copy = c;
EXPECT_FALSE(invoked);
}
EXPECT_FALSE(invoked);
}
EXPECT_TRUE(invoked);
}
}
TEST(ConstructFromExternal, CompareContents) {
RandomEngine rng(testing::GTEST_FLAG(random_seed));
for (int length = 1; length <= 2048; length *= 2) {
std::string data = RandomLowercaseString(&rng, length);
auto* external = new std::string(data);
auto cord =
absl::MakeCordFromExternal(*external, [external](absl::string_view sv) {
EXPECT_EQ(external->data(), sv.data());
EXPECT_EQ(external->size(), sv.size());
delete external;
});
EXPECT_EQ(data, cord);
}
}
TEST(ConstructFromExternal, LargeReleaser) {
RandomEngine rng(testing::GTEST_FLAG(random_seed));
constexpr size_t kLength = 256;
std::string data = RandomLowercaseString(&rng, kLength);
std::array<char, kLength> data_array;
for (size_t i = 0; i < kLength; ++i) data_array[i] = data[i];
bool invoked = false;
auto releaser = [data_array, &invoked](absl::string_view data) {
EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size()));
invoked = true;
};
(void)absl::MakeCordFromExternal(data, releaser);
EXPECT_TRUE(invoked);
}
TEST(ConstructFromExternal, FunctionPointerReleaser) {
static absl::string_view data("hello world");
static bool invoked;
auto* releaser =
static_cast<void (*)(absl::string_view)>([](absl::string_view sv) {
EXPECT_EQ(data, sv);
invoked = true;
});
invoked = false;
(void)absl::MakeCordFromExternal(data, releaser);
EXPECT_TRUE(invoked);
invoked = false;
(void)absl::MakeCordFromExternal(data, *releaser);
EXPECT_TRUE(invoked);
}
TEST(ConstructFromExternal, MoveOnlyReleaser) {
struct Releaser {
explicit Releaser(bool* invoked) : invoked(invoked) {}
Releaser(Releaser&& other) noexcept : invoked(other.invoked) {}
void operator()(absl::string_view) const { *invoked = true; }
bool* invoked;
};
bool invoked = false;
(void)absl::MakeCordFromExternal("dummy", Releaser(&invoked));
EXPECT_TRUE(invoked);
}
Export of internal Abseil changes -- a3e58c1870a9626039f4d178d2d599319bd9f8a8 by Matt Kulukundis <kfm@google.com>: Allow MakeCordFromExternal to take a zero arg releaser. PiperOrigin-RevId: 298650274 -- 01897c4a9bb99f3dc329a794019498ad345ddebd by Samuel Benzaquen <sbenza@google.com>: Reduce library bloat for absl::Flag by moving the definition of base virtual functions to a .cc file. This removes the duplicate symbols in user translation units and has the side effect of moving the vtable definition too (re key function) PiperOrigin-RevId: 298617920 -- 190f0d3782c63aed01046886d7fbc1be5bca2de9 by Derek Mauro <dmauro@google.com>: Import GitHub #596: Unbreak stacktrace code for UWP apps PiperOrigin-RevId: 298600834 -- cd5cf6f8c87b35b85a9584e94da2a99057345b73 by Gennadiy Rozental <rogeeff@google.com>: Use union of heap allocated pointer, one word atomic and two word atomic to represent flags value. Any type T, which is trivially copy-able and with with sizeof(T) <= 8, will be stored in atomic int64_t. Any type T, which is trivially copy-able and with with 8 < sizeof(T) <= 16, will be stored in atomic AlignedTwoWords. We also introducing value storage type to distinguish these cases. PiperOrigin-RevId: 298497200 -- f8fe7bd53bfed601f002f521e34ab4bc083fc28b by Matthew Brown <matthewbr@google.com>: Ensure a deep copy and proper equality on absl::Status::ErasePayload PiperOrigin-RevId: 298482742 -- a5c9ccddf4b04f444e3f7e27dbc14faf1fcb5373 by Gennadiy Rozental <rogeeff@google.com>: Change ChunkIterator implementation to use fixed capacity collection of CordRep*. We can now assume that depth never exceeds 91. That makes comparison operator exception safe. I've tested that with this CL we do not observe an overhead of chunk_end. Compiler optimized this iterator completely. PiperOrigin-RevId: 298458472 -- 327ea5e8910bc388b03389c730763f9823abfce5 by Abseil Team <absl-team@google.com>: Minor cleanups in b-tree code: - Rename some variables: fix issues of different param names between definition/declaration, move away from `x` as a default meaningless variable name. - Make init_leaf/init_internal be non-static methods (they already take the node as the first parameter). - In internal_emplace/try_shrink, update root/rightmost the same way as in insert_unique/insert_multi. - Replace a TODO with a comment. PiperOrigin-RevId: 298432836 -- 8020ce9ec8558ee712d9733ae3d660ac1d3ffe1a by Abseil Team <absl-team@google.com>: Guard against unnecessary copy in case the buffer is empty. This is important in cases were the user is explicitly tuning their chunks to match PiecewiseChunkSize(). PiperOrigin-RevId: 298366044 -- 89324441d1c0c697c90ba7d8fc63639805fcaa9d by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 298219363 GitOrigin-RevId: a3e58c1870a9626039f4d178d2d599319bd9f8a8 Change-Id: I28dffc684b6fd0292b94807b88ec6664d5d0e183
2020-03-03 20:22:10 +01:00
TEST(ConstructFromExternal, NoArgLambda) {
bool invoked = false;
(void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
EXPECT_TRUE(invoked);
}
TEST(ConstructFromExternal, StringViewArgLambda) {
bool invoked = false;
(void)absl::MakeCordFromExternal(
"dummy", [&invoked](absl::string_view) { invoked = true; });
EXPECT_TRUE(invoked);
}
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
struct Releaser {
explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
~Releaser() { *destroyed = true; }
void operator()(absl::string_view) const {}
bool* destroyed;
};
bool destroyed = false;
Releaser releaser(&destroyed);
(void)absl::MakeCordFromExternal("dummy", releaser);
EXPECT_TRUE(destroyed);
}
TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
struct Releaser {
void operator()(absl::string_view) & { *lvalue_invoked = true; }
void operator()(absl::string_view) && { *rvalue_invoked = true; }
bool* lvalue_invoked;
bool* rvalue_invoked;
};
bool lvalue_invoked = false;
bool rvalue_invoked = false;
Releaser releaser = {&lvalue_invoked, &rvalue_invoked};
(void)absl::MakeCordFromExternal("", releaser);
EXPECT_FALSE(lvalue_invoked);
EXPECT_TRUE(rvalue_invoked);
rvalue_invoked = false;
(void)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));
EXPECT_FALSE(lvalue_invoked);
EXPECT_TRUE(rvalue_invoked);
}
TEST(ExternalMemory, BasicUsage) {
static const char* strings[] = {"", "hello", "there"};
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
for (const char* str : strings) {
absl::Cord dst("(prefix)");
AddExternalMemory(str, &dst);
dst.Append("(suffix)");
EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")),
std::string(dst));
}
}
TEST(ExternalMemory, RemovePrefixSuffix) {
// Exhaustively try all sub-strings.
absl::Cord cord = MakeComposite();
std::string s = std::string(cord);
for (int offset = 0; offset <= s.size(); offset++) {
for (int length = 0; length <= s.size() - offset; length++) {
absl::Cord result(cord);
result.RemovePrefix(offset);
result.RemoveSuffix(result.size() - length);
EXPECT_EQ(s.substr(offset, length), std::string(result))
<< offset << " " << length;
}
}
}
TEST(ExternalMemory, Get) {
absl::Cord cord("hello");
AddExternalMemory(" world!", &cord);
AddExternalMemory(" how are ", &cord);
cord.Append(" you?");
std::string s = std::string(cord);
for (int i = 0; i < s.size(); i++) {
EXPECT_EQ(s[i], cord[i]);
}
}
// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage()
// These tests take into account that the reported memory usage is approximate
// and non-deterministic. For all tests, We verify that the reported memory
// usage is larger than `size()`, and less than `size() * 1.5` as a cord should
// never reserve more 'extra' capacity than half of its size as it grows.
// 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) {
EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage());
}
TEST(CordMemoryUsage, Embedded) {
absl::Cord a("hello");
EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
}
TEST(CordMemoryUsage, EmbeddedAppend) {
absl::Cord a("a");
absl::Cord b("bcd");
EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord));
a.Append(b);
EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
}
TEST(CordMemoryUsage, ExternalMemory) {
static const int kLength = 1000;
absl::Cord cord;
AddExternalMemory(std::string(kLength, 'x'), &cord);
EXPECT_GT(cord.EstimatedMemoryUsage(), kLength);
EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5);
}
TEST(CordMemoryUsage, Flat) {
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) {
using absl::strings_internal::CordTestAccess;
absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
size_t length = a.EstimatedMemoryUsage();
a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b'));
size_t delta = a.EstimatedMemoryUsage() - length;
EXPECT_GT(delta, CordTestAccess::MaxFlatLength());
EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 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) {
constexpr size_t kMaxInline = 15; // Cord::InlineRep::N
const std::string small_string(kMaxInline, 'x');
absl::Cord c1(small_string);
absl::Cord c2;
c2.Append(small_string);
EXPECT_EQ(c1, c2);
EXPECT_EQ(c1.EstimatedMemoryUsage(), c2.EstimatedMemoryUsage());
}
} // namespace
// Regtest for 7510292 (fix a bug introduced by 7465150)
TEST(Cord, Concat_Append) {
// Create a rep of type CONCAT
absl::Cord s1("foobarbarbarbarbar");
s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
size_t size = s1.size();
// Create a copy of s1 and append to it.
absl::Cord s2 = s1;
s2.Append("x");
// 7465150 modifies s1 when it shouldn't.
EXPECT_EQ(s1.size(), size);
EXPECT_EQ(s2.size(), size + 1);
}
TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
absl::Cord fragmented =
absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
EXPECT_EQ("A fragmented Cord", fragmented);
auto chunk_it = fragmented.chunk_begin();
ASSERT_TRUE(chunk_it != fragmented.chunk_end());
EXPECT_EQ("A ", *chunk_it);
ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
EXPECT_EQ("fragmented ", *chunk_it);
ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
EXPECT_EQ("Cord", *chunk_it);
ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
}
TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) {
std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
absl::Cord fragmented = absl::MakeFragmentedCord(chunks);
EXPECT_EQ("A fragmented Cord", fragmented);
auto chunk_it = fragmented.chunk_begin();
ASSERT_TRUE(chunk_it != fragmented.chunk_end());
EXPECT_EQ("A ", *chunk_it);
ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
EXPECT_EQ("fragmented ", *chunk_it);
ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
EXPECT_EQ("Cord", *chunk_it);
ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
}
TEST(CordChunkIterator, Traits) {
static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value,
"");
static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, "");
// Move semantics to satisfy swappable via std::swap
static_assert(std::is_move_constructible<absl::Cord::ChunkIterator>::value,
"");
static_assert(std::is_move_assignable<absl::Cord::ChunkIterator>::value, "");
static_assert(
std::is_same<
std::iterator_traits<absl::Cord::ChunkIterator>::iterator_category,
std::input_iterator_tag>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::value_type,
absl::string_view>::value,
"");
static_assert(
std::is_same<
std::iterator_traits<absl::Cord::ChunkIterator>::difference_type,
ptrdiff_t>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::pointer,
const absl::string_view*>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::reference,
absl::string_view>::value,
"");
}
static void VerifyChunkIterator(const absl::Cord& cord,
size_t expected_chunks) {
EXPECT_EQ(cord.chunk_begin() == cord.chunk_end(), cord.empty()) << cord;
EXPECT_EQ(cord.chunk_begin() != cord.chunk_end(), !cord.empty());
absl::Cord::ChunkRange range = cord.Chunks();
EXPECT_EQ(range.begin() == range.end(), cord.empty());
EXPECT_EQ(range.begin() != range.end(), !cord.empty());
std::string content(cord);
size_t pos = 0;
auto pre_iter = cord.chunk_begin(), post_iter = cord.chunk_begin();
size_t n_chunks = 0;
while (pre_iter != cord.chunk_end() && post_iter != cord.chunk_end()) {
EXPECT_FALSE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test ==
EXPECT_FALSE(post_iter == cord.chunk_end()); // NOLINT
EXPECT_EQ(pre_iter, post_iter);
EXPECT_EQ(*pre_iter, *post_iter);
EXPECT_EQ(pre_iter->data(), (*pre_iter).data());
EXPECT_EQ(pre_iter->size(), (*pre_iter).size());
absl::string_view chunk = *pre_iter;
EXPECT_FALSE(chunk.empty());
EXPECT_LE(pos + chunk.size(), content.size());
EXPECT_EQ(absl::string_view(content.c_str() + pos, chunk.size()), chunk);
int n_equal_iterators = 0;
for (absl::Cord::ChunkIterator it = range.begin(); it != range.end();
++it) {
n_equal_iterators += static_cast<int>(it == pre_iter);
}
EXPECT_EQ(n_equal_iterators, 1);
++pre_iter;
EXPECT_EQ(*post_iter++, chunk);
pos += chunk.size();
++n_chunks;
}
EXPECT_EQ(expected_chunks, n_chunks);
EXPECT_EQ(pos, content.size());
EXPECT_TRUE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test ==
EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT
}
TEST(CordChunkIterator, Operations) {
absl::Cord empty_cord;
VerifyChunkIterator(empty_cord, 0);
absl::Cord small_buffer_cord("small cord");
VerifyChunkIterator(small_buffer_cord, 1);
absl::Cord flat_node_cord("larger than small buffer optimization");
VerifyChunkIterator(flat_node_cord, 1);
VerifyChunkIterator(
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')));
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);
expected_chunks *= 2;
VerifyChunkIterator(reused_nodes_cord, expected_chunks);
}
RandomEngine rng(testing::GTEST_FLAG(random_seed));
absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
absl::Cord subcords;
for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128));
VerifyChunkIterator(subcords, 128);
}
Export of internal Abseil changes -- a3e58c1870a9626039f4d178d2d599319bd9f8a8 by Matt Kulukundis <kfm@google.com>: Allow MakeCordFromExternal to take a zero arg releaser. PiperOrigin-RevId: 298650274 -- 01897c4a9bb99f3dc329a794019498ad345ddebd by Samuel Benzaquen <sbenza@google.com>: Reduce library bloat for absl::Flag by moving the definition of base virtual functions to a .cc file. This removes the duplicate symbols in user translation units and has the side effect of moving the vtable definition too (re key function) PiperOrigin-RevId: 298617920 -- 190f0d3782c63aed01046886d7fbc1be5bca2de9 by Derek Mauro <dmauro@google.com>: Import GitHub #596: Unbreak stacktrace code for UWP apps PiperOrigin-RevId: 298600834 -- cd5cf6f8c87b35b85a9584e94da2a99057345b73 by Gennadiy Rozental <rogeeff@google.com>: Use union of heap allocated pointer, one word atomic and two word atomic to represent flags value. Any type T, which is trivially copy-able and with with sizeof(T) <= 8, will be stored in atomic int64_t. Any type T, which is trivially copy-able and with with 8 < sizeof(T) <= 16, will be stored in atomic AlignedTwoWords. We also introducing value storage type to distinguish these cases. PiperOrigin-RevId: 298497200 -- f8fe7bd53bfed601f002f521e34ab4bc083fc28b by Matthew Brown <matthewbr@google.com>: Ensure a deep copy and proper equality on absl::Status::ErasePayload PiperOrigin-RevId: 298482742 -- a5c9ccddf4b04f444e3f7e27dbc14faf1fcb5373 by Gennadiy Rozental <rogeeff@google.com>: Change ChunkIterator implementation to use fixed capacity collection of CordRep*. We can now assume that depth never exceeds 91. That makes comparison operator exception safe. I've tested that with this CL we do not observe an overhead of chunk_end. Compiler optimized this iterator completely. PiperOrigin-RevId: 298458472 -- 327ea5e8910bc388b03389c730763f9823abfce5 by Abseil Team <absl-team@google.com>: Minor cleanups in b-tree code: - Rename some variables: fix issues of different param names between definition/declaration, move away from `x` as a default meaningless variable name. - Make init_leaf/init_internal be non-static methods (they already take the node as the first parameter). - In internal_emplace/try_shrink, update root/rightmost the same way as in insert_unique/insert_multi. - Replace a TODO with a comment. PiperOrigin-RevId: 298432836 -- 8020ce9ec8558ee712d9733ae3d660ac1d3ffe1a by Abseil Team <absl-team@google.com>: Guard against unnecessary copy in case the buffer is empty. This is important in cases were the user is explicitly tuning their chunks to match PiecewiseChunkSize(). PiperOrigin-RevId: 298366044 -- 89324441d1c0c697c90ba7d8fc63639805fcaa9d by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 298219363 GitOrigin-RevId: a3e58c1870a9626039f4d178d2d599319bd9f8a8 Change-Id: I28dffc684b6fd0292b94807b88ec6664d5d0e183
2020-03-03 20:22:10 +01:00
TEST(CordChunkIterator, MaxLengthFullTree) {
absl::Cord cord;
size_t size = 1;
AddExternalMemory("x", &cord);
EXPECT_EQ(cord.size(), size);
for (int i = 0; i < 63; ++i) {
cord.Prepend(absl::Cord(cord));
size <<= 1;
EXPECT_EQ(cord.size(), size);
auto chunk_it = cord.chunk_begin();
EXPECT_EQ(*chunk_it, "x");
}
EXPECT_DEATH_IF_SUPPORTED(
(cord.Prepend(absl::Cord(cord)), *cord.chunk_begin()),
"Cord is too long");
}
TEST(CordChunkIterator, MaxDepth) {
// By reusing nodes, it's possible in pathological cases to build a Cord that
// exceeds both the maximum permissible length and depth. In this case, the
// violation of the maximum depth is reported.
absl::Cord left_child;
AddExternalMemory("x", &left_child);
absl::Cord root = left_child;
for (int i = 0; i < 91; ++i) {
size_t new_size = left_child.size() + root.size();
root.Prepend(left_child);
EXPECT_EQ(root.size(), new_size);
auto chunk_it = root.chunk_begin();
EXPECT_EQ(*chunk_it, "x");
std::swap(left_child, root);
}
EXPECT_DEATH_IF_SUPPORTED(root.Prepend(left_child), "Cord depth exceeds max");
}
Export of internal Abseil changes -- 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 by Abseil Team <absl-team@google.com>: Internal changes PiperOrigin-RevId: 295164378 -- 74990f100b3f4172c770ef8c76c05c8e99febdde by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Cord`. PiperOrigin-RevId: 295161959 -- 6018c57f43c45c31dc1a61c0cd75fa2aa9be8dab by Gennadiy Rozental <rogeeff@google.com>: Introduce independent notion of FlagStaticTypeID. This change separates static flag value type identification from the type specific "vtable" with all the operations specific to value type. This change allows us to do the following: * We can move most of "vtable" implementation from handle header, which will become public soon, into implementation details of Abseil Flag. * We can combine back marshalling ops and general ops into a single vtable routine. They were split previously to facilitate type identification without requiring marshalling routines to be exposed in header. * We do not need to store two vtable pointers. We can now store only one. The static type id can be deduced on request. Overall we are saving 24 bytes per flag according to size_tester run. PiperOrigin-RevId: 295149687 -- 986b78e9ba571aa85154e70bda4580edd45bb7bf by Abseil Team <absl-team@google.com>: Update internal comments. PiperOrigin-RevId: 295030681 -- 825412b29fd6015027bbc3e5f802706eee0d2837 by Matthew Brown <matthewbr@google.com>: Change str_format_internal::ConversionChar to an enum (from a struct-wrapped enum). PiperOrigin-RevId: 294987462 -- f9f88d91809d2cc33fc129df70fa93e7a2c35c69 by Derek Mauro <dmauro@google.com>: Use more precise wording in the question on live-at-head PiperOrigin-RevId: 294957679 GitOrigin-RevId: 97faa5fdfa4cd5d7a74cd9332cddd8a7c1e67b89 Change-Id: I081e70d148ffac7296d65e2a2f775f643eaf70bf
2020-02-14 18:41:25 +01:00
TEST(CordCharIterator, Traits) {
static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
"");
static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, "");
// Move semantics to satisfy swappable via std::swap
static_assert(std::is_move_constructible<absl::Cord::CharIterator>::value,
"");
static_assert(std::is_move_assignable<absl::Cord::CharIterator>::value, "");
static_assert(
std::is_same<
std::iterator_traits<absl::Cord::CharIterator>::iterator_category,
std::input_iterator_tag>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::CharIterator>::value_type,
char>::value,
"");
static_assert(
std::is_same<
std::iterator_traits<absl::Cord::CharIterator>::difference_type,
ptrdiff_t>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::CharIterator>::pointer,
const char*>::value,
"");
static_assert(
std::is_same<std::iterator_traits<absl::Cord::CharIterator>::reference,
const char&>::value,
"");
}
static void VerifyCharIterator(const absl::Cord& cord) {
EXPECT_EQ(cord.char_begin() == cord.char_end(), cord.empty());
EXPECT_EQ(cord.char_begin() != cord.char_end(), !cord.empty());
absl::Cord::CharRange range = cord.Chars();
EXPECT_EQ(range.begin() == range.end(), cord.empty());
EXPECT_EQ(range.begin() != range.end(), !cord.empty());
size_t i = 0;
absl::Cord::CharIterator pre_iter = cord.char_begin();
absl::Cord::CharIterator post_iter = cord.char_begin();
std::string content(cord);
while (pre_iter != cord.char_end() && post_iter != cord.char_end()) {
EXPECT_FALSE(pre_iter == cord.char_end()); // NOLINT: explicitly test ==
EXPECT_FALSE(post_iter == cord.char_end()); // NOLINT
EXPECT_LT(i, cord.size());
EXPECT_EQ(content[i], *pre_iter);
EXPECT_EQ(pre_iter, post_iter);
EXPECT_EQ(*pre_iter, *post_iter);
EXPECT_EQ(&*pre_iter, &*post_iter);
EXPECT_EQ(&*pre_iter, pre_iter.operator->());
const char* character_address = &*pre_iter;
absl::Cord::CharIterator copy = pre_iter;
++copy;
EXPECT_EQ(character_address, &*pre_iter);
int n_equal_iterators = 0;
for (absl::Cord::CharIterator it = range.begin(); it != range.end(); ++it) {
n_equal_iterators += static_cast<int>(it == pre_iter);
}
EXPECT_EQ(n_equal_iterators, 1);
absl::Cord::CharIterator advance_iter = range.begin();
absl::Cord::Advance(&advance_iter, i);
EXPECT_EQ(pre_iter, advance_iter);
advance_iter = range.begin();
EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, i), cord.Subcord(0, i));
EXPECT_EQ(pre_iter, advance_iter);
advance_iter = pre_iter;
absl::Cord::Advance(&advance_iter, cord.size() - i);
EXPECT_EQ(range.end(), advance_iter);
advance_iter = pre_iter;
EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, cord.size() - i),
cord.Subcord(i, cord.size() - i));
EXPECT_EQ(range.end(), advance_iter);
++i;
++pre_iter;
post_iter++;
}
EXPECT_EQ(i, cord.size());
EXPECT_TRUE(pre_iter == cord.char_end()); // NOLINT: explicitly test ==
EXPECT_TRUE(post_iter == cord.char_end()); // NOLINT
absl::Cord::CharIterator zero_advanced_end = cord.char_end();
absl::Cord::Advance(&zero_advanced_end, 0);
EXPECT_EQ(zero_advanced_end, cord.char_end());
absl::Cord::CharIterator it = cord.char_begin();
for (absl::string_view chunk : cord.Chunks()) {
while (!chunk.empty()) {
EXPECT_EQ(absl::Cord::ChunkRemaining(it), chunk);
chunk.remove_prefix(1);
++it;
}
}
}
TEST(CordCharIterator, Operations) {
absl::Cord empty_cord;
VerifyCharIterator(empty_cord);
absl::Cord small_buffer_cord("small cord");
VerifyCharIterator(small_buffer_cord);
absl::Cord flat_node_cord("larger than small buffer optimization");
VerifyCharIterator(flat_node_cord);
VerifyCharIterator(
absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
"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);
VerifyCharIterator(reused_nodes_cord);
}
RandomEngine rng(testing::GTEST_FLAG(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));
VerifyCharIterator(subcords);
}
TEST(Cord, StreamingOutput) {
absl::Cord c =
absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
std::stringstream output;
output << c;
EXPECT_EQ("A small fragmented Cord.", output.str());
}
TEST(Cord, ForEachChunk) {
for (int num_elements : {1, 10, 200}) {
SCOPED_TRACE(num_elements);
std::vector<std::string> cord_chunks;
for (int i = 0; i < num_elements; ++i) {
cord_chunks.push_back(absl::StrCat("[", i, "]"));
}
absl::Cord c = absl::MakeFragmentedCord(cord_chunks);
std::vector<std::string> iterated_chunks;
absl::CordTestPeer::ForEachChunk(c,
[&iterated_chunks](absl::string_view sv) {
iterated_chunks.emplace_back(sv);
});
EXPECT_EQ(iterated_chunks, cord_chunks);
}
}
TEST(Cord, SmallBufferAssignFromOwnData) {
constexpr size_t kMaxInline = 15;
std::string contents = "small buff cord";
EXPECT_EQ(contents.size(), kMaxInline);
for (size_t pos = 0; pos < contents.size(); ++pos) {
for (size_t count = contents.size() - pos; count > 0; --count) {
absl::Cord c(contents);
absl::string_view flat = c.Flatten();
c = flat.substr(pos, count);
EXPECT_EQ(c, contents.substr(pos, count))
<< "pos = " << pos << "; count = " << count;
}
}
}