Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/abseil-cpp/absl/strings/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 110 kB image not shown  

Quelle  cord_test.cc

  Sprache: C
 

// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "absl/strings/cord.h"

#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <iterator>
#include <limits>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/macros.h"
#include "absl/base/no_destructor.h"
#include "absl/base/options.h"
#include "absl/container/fixed_array.h"
#include "absl/functional/function_ref.h"
#include "absl/hash/hash.h"
#include "absl/hash/hash_testing.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/random/random.h"
#include "absl/strings/cord_buffer.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/cordz_test_helpers.h"
#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_crc.h"
#include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/internal/cordz_statistics.h"
#include "absl/strings/internal/cordz_update_tracker.h"
#include "absl/strings/internal/string_constant.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/compare.h"
#include "absl/types/optional.h"

// convenience local constants
static constexpr auto FLAT = absl::cord_internal::FLAT;
static constexpr auto MAX_FLAT_TAG = absl::cord_internal::MAX_FLAT_TAG;

typedef std::mt19937_64 RandomEngine;

using absl::cord_internal::CordRep;
using absl::cord_internal::CordRepBtree;
using absl::cord_internal::CordRepConcat;
using absl::cord_internal::CordRepCrc;
using absl::cord_internal::CordRepExternal;
using absl::cord_internal::CordRepFlat;
using absl::cord_internal::CordRepSubstring;
using absl::cord_internal::CordzUpdateTracker;
using absl::cord_internal::kFlatOverhead;
using absl::cord_internal::kMaxFlatLength;
using ::testing::ElementsAre;
using ::testing::Le;

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)); });
  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);
  }

  static bool IsTree(const Cord& c) { return c.contents_.is_tree(); }
  static CordRep* Tree(const Cord& c) { return c.contents_.tree(); }

  static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) {
    return c.contents_.cordz_info();
  }

  static Cord MakeSubstring(Cord src, size_t offset, size_t length) {
    CHECK(src.contents_.is_tree()) << "Can not be inlined";
    CHECK(!src.ExpectedChecksum().has_value()) << "Can not be hardened";
    Cord cord;
    auto* tree = cord_internal::SkipCrcNode(src.contents_.tree());
    auto* rep = CordRepSubstring::Create(CordRep::Ref(tree), offset, length);
    cord.contents_.EmplaceTree(rep, CordzUpdateTracker::kSubCord);
    return cord;
  }
};

ABSL_NAMESPACE_END
}  // namespace absl



// The CordTest fixture runs all tests with and without expected CRCs being set
// on the subject Cords.
class CordTest : public testing::TestWithParam<bool /*useCrc*/> {
 public:
  // Returns true if test is running with Crc enabled.
  bool UseCrc() const { return GetParam(); }
  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> useCrc) {
    if (useCrc.param) {
      return "BtreeHardened";
    } else {
      return "Btree";
    }
  }
};

INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(),
                         CordTest::ToString);

TEST(CordRepFlat, AllFlatCapacities) {
  // Explicitly and redundantly assert built-in min/max limits
  static_assert(absl::cord_internal::kFlatOverhead < 32, "");
  static_assert(absl::cord_internal::kMinFlatSize == 32, "");
  static_assert(absl::cord_internal::kMaxLargeFlatSize == 256 << 10, "");
  EXPECT_EQ(absl::cord_internal::TagToAllocatedSize(FLAT), 32);
  EXPECT_EQ(absl::cord_internal::TagToAllocatedSize(MAX_FLAT_TAG), 256 << 10);

  // Verify all tags to map perfectly back and forth, and
  // that sizes are monotonically increasing.
  size_t last_size = 0;
  for (int tag = FLAT; tag <= MAX_FLAT_TAG; ++tag) {
    size_t size = absl::cord_internal::TagToAllocatedSize(tag);
    ASSERT_GT(size, last_size);
    ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size);
    last_size = size;
  }

  // All flat size from 32 - 512 are 8 byte granularity
  for (size_t size = 32; size <= 512; size += 8) {
    ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size);
    uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size);
    ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size);
  }

  // All flat sizes from 512 - 8192 are 64 byte granularity
  for (size_t size = 512; size <= 8192; size += 64) {
    ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size);
    uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size);
    ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size);
  }

  // All flat sizes from 8KB to 256KB are 4KB granularity
  for (size_t size = 8192; size <= 256 * 1024; size += 4 * 1024) {
    ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size);
    uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size);
    ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size);
  }
}

TEST(CordRepFlat, MaxFlatSize) {
  CordRepFlat* flat = CordRepFlat::New(kMaxFlatLength);
  EXPECT_EQ(flat->Capacity(), kMaxFlatLength);
  CordRep::Unref(flat);

  flat = CordRepFlat::New(kMaxFlatLength * 4);
  EXPECT_EQ(flat->Capacity(), kMaxFlatLength);
  CordRep::Unref(flat);
}

TEST(CordRepFlat, MaxLargeFlatSize) {
  const size_t size = 256 * 1024 - kFlatOverhead;
  CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), size);
  EXPECT_GE(flat->Capacity(), size);
  CordRep::Unref(flat);
}

TEST(CordRepFlat, AllFlatSizes) {
  const size_t kMaxSize = 256 * 1024;
  for (size_t size = 32; size <= kMaxSize; size *=2) {
    const size_t length = size - kFlatOverhead - 1;
    CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), length);
    EXPECT_GE(flat->Capacity(), length);
    memset(flat->Data(), 0xCD, flat->Capacity());
    CordRep::Unref(flat);
  }
}

TEST_P(CordTest, AllFlatSizes) {
  using absl::strings_internal::CordTestAccess;

  for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) {
    // Make a string of length s.
    std::string src;
    while (src.size() < s) {
      src.push_back('a' + (src.size() % 26));
    }

    absl::Cord dst(src);
    MaybeHarden(dst);
    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_P(CordTest, GigabyteCordFromExternal) {
  const size_t one_gig = 1024U * 1024U * 1024U;
  size_t max_size = 2 * one_gig;
  if (sizeof(max_size) > 4) max_size = 128 * one_gig;

  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;
  c.Append(from);
  while (c.size() < max_size) {
    c.Append(c);
    c.Append(from);
    c.Append(from);
    c.Append(from);
    c.Append(from);
    MaybeHarden(c);
  }

  for (int i = 0; i < 1024; ++i) {
    c.Append(from);
  }
  LOG(INFO) << "Made a Cord with " << c.size() << " bytes!";
  // 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_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);
  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_P(CordTest, StartsEndsWith) {
  absl::Cord x(absl::string_view("abcde"));
  MaybeHarden(x);
  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_P(CordTest, Contains) {
  auto flat_haystack = absl::Cord("this is a flat cord");
  auto fragmented_haystack = absl::MakeFragmentedCord(
      {"this"" ""is"" ""a"" ""fragmented"" ""cord"});

  EXPECT_TRUE(flat_haystack.Contains(""));
  EXPECT_TRUE(fragmented_haystack.Contains(""));
  EXPECT_TRUE(flat_haystack.Contains(absl::Cord("")));
  EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("")));
  EXPECT_TRUE(absl::Cord("").Contains(""));
  EXPECT_TRUE(absl::Cord("").Contains(absl::Cord("")));
  EXPECT_FALSE(absl::Cord("").Contains(flat_haystack));
  EXPECT_FALSE(absl::Cord("").Contains(fragmented_haystack));

  EXPECT_FALSE(flat_haystack.Contains("z"));
  EXPECT_FALSE(fragmented_haystack.Contains("z"));
  EXPECT_FALSE(flat_haystack.Contains(absl::Cord("z")));
  EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("z")));

  EXPECT_FALSE(flat_haystack.Contains("is an"));
  EXPECT_FALSE(fragmented_haystack.Contains("is an"));
  EXPECT_FALSE(flat_haystack.Contains(absl::Cord("is an")));
  EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("is an")));
  EXPECT_FALSE(
      flat_haystack.Contains(absl::MakeFragmentedCord({"is"" ""an"})));
  EXPECT_FALSE(fragmented_haystack.Contains(
      absl::MakeFragmentedCord({"is"" ""an"})));

  EXPECT_TRUE(flat_haystack.Contains("is a"));
  EXPECT_TRUE(fragmented_haystack.Contains("is a"));
  EXPECT_TRUE(flat_haystack.Contains(absl::Cord("is a")));
  EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("is a")));
  EXPECT_TRUE(
      flat_haystack.Contains(absl::MakeFragmentedCord({"is"" ""a"})));
  EXPECT_TRUE(
      fragmented_haystack.Contains(absl::MakeFragmentedCord({"is"" ""a"})));
}

TEST_P(CordTest, Find) {
  auto flat_haystack = absl::Cord("this is a flat cord");
  auto fragmented_haystack = absl::MakeFragmentedCord(
      {"this"" ""is"" ""a"" ""fragmented"" ""cord"});
  auto empty_haystack = absl::Cord("");

  EXPECT_EQ(flat_haystack.Find(""), flat_haystack.char_begin());
  EXPECT_EQ(fragmented_haystack.Find(""), fragmented_haystack.char_begin());
  EXPECT_EQ(flat_haystack.Find(absl::Cord("")), flat_haystack.char_begin());
  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("")),
            fragmented_haystack.char_begin());
  EXPECT_EQ(empty_haystack.Find(""), empty_haystack.char_begin());
  EXPECT_EQ(empty_haystack.Find(absl::Cord("")), empty_haystack.char_begin());
  EXPECT_EQ(empty_haystack.Find(flat_haystack), empty_haystack.char_end());
  EXPECT_EQ(empty_haystack.Find(fragmented_haystack),
            empty_haystack.char_end());

  EXPECT_EQ(flat_haystack.Find("z"), flat_haystack.char_end());
  EXPECT_EQ(fragmented_haystack.Find("z"), fragmented_haystack.char_end());
  EXPECT_EQ(flat_haystack.Find(absl::Cord("z")), flat_haystack.char_end());
  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("z")),
            fragmented_haystack.char_end());

  EXPECT_EQ(flat_haystack.Find("is an"), flat_haystack.char_end());
  EXPECT_EQ(fragmented_haystack.Find("is an"), fragmented_haystack.char_end());
  EXPECT_EQ(flat_haystack.Find(absl::Cord("is an")), flat_haystack.char_end());
  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is an")),
            fragmented_haystack.char_end());
  EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is"" ""an"})),
            flat_haystack.char_end());
  EXPECT_EQ(
      fragmented_haystack.Find(absl::MakeFragmentedCord({"is"" ""an"})),
      fragmented_haystack.char_end());

  EXPECT_EQ(flat_haystack.Find("is a"),
            std::next(flat_haystack.char_begin(), 5));
  EXPECT_EQ(fragmented_haystack.Find("is a"),
            std::next(fragmented_haystack.char_begin(), 5));
  EXPECT_EQ(flat_haystack.Find(absl::Cord("is a")),
            std::next(flat_haystack.char_begin(), 5));
  EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is a")),
            std::next(fragmented_haystack.char_begin(), 5));
  EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is"" ""a"})),
            std::next(flat_haystack.char_begin(), 5));
  EXPECT_EQ(
      fragmented_haystack.Find(absl::MakeFragmentedCord({"is"" ""a"})),
      std::next(fragmented_haystack.char_begin(), 5));
}

TEST_P(CordTest, Subcord) {
  RandomEngine rng(GTEST_FLAG_GET(random_seed));
  const std::string s = RandomLowercaseString(&rng, 1024);

  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.
  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);
      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);
      }
    }
  }

  // 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);
      ASSERT_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);
    ASSERT_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_P(CordTest, Swap) {
  absl::string_view a("Dexter");
  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));
}

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; "
           "has_initial_contents = \""
        << has_initial_contents << "\"";
  }
}

TEST_P(CordTest, CopyToString) {
  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."})));
}

static void VerifyAppendCordToString(const absl::Cord& cord) {
  std::string initially_empty;
  absl::AppendCordToString(cord, &initially_empty);
  EXPECT_EQ(initially_empty, cord);

  const absl::string_view kInitialContents = "initial contents.";
  std::string expected_after_append =
      absl::StrCat(kInitialContents, std::string(cord));

  std::string no_reserve(kInitialContents);
  absl::AppendCordToString(cord, &no_reserve);
  EXPECT_EQ(no_reserve, expected_after_append);

  std::string has_reserved_capacity(kInitialContents);
  has_reserved_capacity.reserve(has_reserved_capacity.size() + cord.size());
  const char* address_before_copy = has_reserved_capacity.data();
  absl::AppendCordToString(cord, &has_reserved_capacity);
  EXPECT_EQ(has_reserved_capacity, expected_after_append);
  EXPECT_EQ(has_reserved_capacity.data(), address_before_copy)
      << "AppendCordToString allocated new string storage; "
         "has_reserved_capacity = \""
      << has_reserved_capacity << "\"";
}

TEST_P(CordTest, AppendToString) {
  VerifyAppendCordToString(absl::Cord());  // empty cords cannot carry CRCs
  VerifyAppendCordToString(MaybeHardened(absl::Cord("small cord")));
  VerifyAppendCordToString(MaybeHardened(
      absl::MakeFragmentedCord({"fragmented ""cord ""to ""test ",
                                "appending ""to ""a ""string."})));
}

TEST_P(CordTest, AppendEmptyBuffer) {
  absl::Cord cord;
  cord.Append(absl::CordBuffer());
  cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000));
}

TEST_P(CordTest, AppendEmptyBufferToFlat) {
  absl::Cord cord(std::string(2000, 'x'));
  cord.Append(absl::CordBuffer());
  cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000));
}

TEST_P(CordTest, AppendEmptyBufferToTree) {
  absl::Cord cord(std::string(2000, 'x'));
  cord.Append(std::string(2000, 'y'));
  cord.Append(absl::CordBuffer());
  cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000));
}

TEST_P(CordTest, AppendSmallBuffer) {
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  ASSERT_THAT(buffer.capacity(), Le(15));
  memcpy(buffer.data(), "Abc", 3);
  buffer.SetLength(3);
  cord.Append(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  memcpy(buffer.data(), "defgh", 5);
  buffer.SetLength(5);
  cord.Append(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  EXPECT_THAT(cord.Chunks(), ElementsAre("Abcdefgh"));
}

TEST_P(CordTest, AppendAndPrependBufferArePrecise) {
  // Create a cord large enough to force 40KB flats.
  std::string test_data(absl::cord_internal::kMaxFlatLength * 10, 'x');
  absl::Cord cord1(test_data);
  absl::Cord cord2(test_data);
  const size_t size1 = cord1.EstimatedMemoryUsage();
  const size_t size2 = cord2.EstimatedMemoryUsage();

  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  memcpy(buffer.data(), "Abc", 3);
  buffer.SetLength(3);
  cord1.Append(std::move(buffer));

  buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  memcpy(buffer.data(), "Abc", 3);
  buffer.SetLength(3);
  cord2.Prepend(std::move(buffer));

#ifndef NDEBUG
  // Allow 32 bytes new CordRepFlat, and 128 bytes for 'glue nodes'
  constexpr size_t kMaxDelta = 128 + 32;
#else
  // Allow 256 bytes extra for 'allocation debug overhead'
  constexpr size_t kMaxDelta = 128 + 32 + 256;
#endif

  EXPECT_LE(cord1.EstimatedMemoryUsage() - size1, kMaxDelta);
  EXPECT_LE(cord2.EstimatedMemoryUsage() - size2, kMaxDelta);

  EXPECT_EQ(cord1, absl::StrCat(test_data, "Abc"));
  EXPECT_EQ(cord2, absl::StrCat("Abc", test_data));
}

TEST_P(CordTest, PrependSmallBuffer) {
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  ASSERT_THAT(buffer.capacity(), Le(15));
  memcpy(buffer.data(), "Abc", 3);
  buffer.SetLength(3);
  cord.Prepend(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  buffer = absl::CordBuffer::CreateWithDefaultLimit(3);
  memcpy(buffer.data(), "defgh", 5);
  buffer.SetLength(5);
  cord.Prepend(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  EXPECT_THAT(cord.Chunks(), ElementsAre("defghAbc"));
}

TEST_P(CordTest, AppendLargeBuffer) {
  absl::Cord cord;

  std::string s1(700, '1');
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(s1.size());
  memcpy(buffer.data(), s1.data(), s1.size());
  buffer.SetLength(s1.size());
  cord.Append(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  std::string s2(1000, '2');
  buffer = absl::CordBuffer::CreateWithDefaultLimit(s2.size());
  memcpy(buffer.data(), s2.data(), s2.size());
  buffer.SetLength(s2.size());
  cord.Append(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  EXPECT_THAT(cord.Chunks(), ElementsAre(s1, s2));
}

TEST_P(CordTest, PrependLargeBuffer) {
  absl::Cord cord;

  std::string s1(700, '1');
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(s1.size());
  memcpy(buffer.data(), s1.data(), s1.size());
  buffer.SetLength(s1.size());
  cord.Prepend(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  std::string s2(1000, '2');
  buffer = absl::CordBuffer::CreateWithDefaultLimit(s2.size());
  memcpy(buffer.data(), s2.data(), s2.size());
  buffer.SetLength(s2.size());
  cord.Prepend(std::move(buffer));
  EXPECT_EQ(buffer.length(), 0);    // NOLINT
  EXPECT_GT(buffer.capacity(), 0);  // NOLINT

  EXPECT_THAT(cord.Chunks(), ElementsAre(s2, s1));
}

class CordAppendBufferTest : public testing::TestWithParam<bool> {
 public:
  size_t is_default() const { return GetParam(); }

  // Returns human readable string representation of the test parameter.
  static std::string ToString(testing::TestParamInfo<bool> param) {
    return param.param ? "DefaultLimit" : "CustomLimit";
  }

  size_t limit() const {
    return is_default() ? absl::CordBuffer::kDefaultLimit
                        : absl::CordBuffer::kCustomLimit;
  }

  size_t maximum_payload() const {
    return is_default() ? absl::CordBuffer::MaximumPayload()
                        : absl::CordBuffer::MaximumPayload(limit());
  }

  absl::CordBuffer GetAppendBuffer(absl::Cord& cord, size_t capacity,
                                   size_t min_capacity = 16) {
    return is_default()
               ? cord.GetAppendBuffer(capacity, min_capacity)
               : cord.GetCustomAppendBuffer(limit(), capacity, min_capacity);
  }
};

INSTANTIATE_TEST_SUITE_P(WithParam, CordAppendBufferTest, testing::Bool(),
                         CordAppendBufferTest::ToString);

TEST_P(CordAppendBufferTest, GetAppendBufferOnEmptyCord) {
  absl::Cord cord;
  absl::CordBuffer buffer = GetAppendBuffer(cord, 1000);
  EXPECT_GE(buffer.capacity(), 1000);
  EXPECT_EQ(buffer.length(), 0);
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnInlinedCord) {
  static constexpr int kInlinedSize = sizeof(absl::CordBuffer) - 1;
  for (int size : {6, kInlinedSize - 3, kInlinedSize - 2, 1000}) {
    absl::Cord cord("Abc");
    absl::CordBuffer buffer = GetAppendBuffer(cord, size, 1);
    EXPECT_GE(buffer.capacity(), 3 + size);
    EXPECT_EQ(buffer.length(), 3);
    EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc");
    EXPECT_TRUE(cord.empty());
  }
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnInlinedCordCapacityCloseToMax) {
  // Cover the use case where we have a non empty inlined cord with some size
  // 'n', and ask for something like 'uint64_max - k', assuming internal logic
  // could overflow on 'uint64_max - k + size', and return a valid, but
  // inefficiently smaller buffer if it would provide is the max allowed size.
  for (size_t dist_from_max = 0; dist_from_max <= 4; ++dist_from_max) {
    absl::Cord cord("Abc");
    size_t size = std::numeric_limits<size_t>::max() - dist_from_max;
    absl::CordBuffer buffer = GetAppendBuffer(cord, size, 1);
    EXPECT_GE(buffer.capacity(), maximum_payload());
    EXPECT_EQ(buffer.length(), 3);
    EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc");
    EXPECT_TRUE(cord.empty());
  }
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnFlat) {
  // Create a cord with a single flat and extra capacity
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
  const size_t expected_capacity = buffer.capacity();
  buffer.SetLength(3);
  memcpy(buffer.data(), "Abc", 3);
  cord.Append(std::move(buffer));

  buffer = GetAppendBuffer(cord, 6);
  EXPECT_EQ(buffer.capacity(), expected_capacity);
  EXPECT_EQ(buffer.length(), 3);
  EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc");
  EXPECT_TRUE(cord.empty());
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnFlatWithoutMinCapacity) {
  // Create a cord with a single flat and extra capacity
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
  buffer.SetLength(30);
  memset(buffer.data(), 'x', 30);
  cord.Append(std::move(buffer));

  buffer = GetAppendBuffer(cord, 1000, 900);
  EXPECT_GE(buffer.capacity(), 1000);
  EXPECT_EQ(buffer.length(), 0);
  EXPECT_EQ(cord, std::string(30, 'x'));
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnTree) {
  RandomEngine rng;
  for (int num_flats : {2, 3, 100}) {
    // Create a cord with `num_flats` flats and extra capacity
    absl::Cord cord;
    std::string prefix;
    std::string last;
    for (int i = 0; i < num_flats - 1; ++i) {
      prefix += last;
      last = RandomLowercaseString(&rng, 10);
      absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
      buffer.SetLength(10);
      memcpy(buffer.data(), last.data(), 10);
      cord.Append(std::move(buffer));
    }
    absl::CordBuffer buffer = GetAppendBuffer(cord, 6);
    EXPECT_GE(buffer.capacity(), 500);
    EXPECT_EQ(buffer.length(), 10);
    EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), last);
    EXPECT_EQ(cord, prefix);
  }
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnTreeWithoutMinCapacity) {
  absl::Cord cord;
  for (int i = 0; i < 2; ++i) {
    absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
    buffer.SetLength(3);
    memcpy(buffer.data(), i ? "def" : "Abc", 3);
    cord.Append(std::move(buffer));
  }
  absl::CordBuffer buffer = GetAppendBuffer(cord, 1000, 900);
  EXPECT_GE(buffer.capacity(), 1000);
  EXPECT_EQ(buffer.length(), 0);
  EXPECT_EQ(cord, "Abcdef");
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnSubstring) {
  // Create a large cord with a single flat and some extra capacity
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
  buffer.SetLength(450);
  memset(buffer.data(), 'x', 450);
  cord.Append(std::move(buffer));
  cord.RemovePrefix(1);

  // Deny on substring
  buffer = GetAppendBuffer(cord, 6);
  EXPECT_EQ(buffer.length(), 0);
  EXPECT_EQ(cord, std::string(449, 'x'));
}

TEST_P(CordAppendBufferTest, GetAppendBufferOnSharedCord) {
  // Create a shared cord with a single flat and extra capacity
  absl::Cord cord;
  absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
  buffer.SetLength(3);
  memcpy(buffer.data(), "Abc", 3);
  cord.Append(std::move(buffer));
  absl::Cord shared_cord = cord;

  // Deny on flat
  buffer = GetAppendBuffer(cord, 6);
  EXPECT_EQ(buffer.length(), 0);
  EXPECT_EQ(cord, "Abc");

  buffer = absl::CordBuffer::CreateWithDefaultLimit(500);
  buffer.SetLength(3);
  memcpy(buffer.data(), "def", 3);
  cord.Append(std::move(buffer));
  shared_cord = cord;

  // Deny on tree
  buffer = GetAppendBuffer(cord, 6);
  EXPECT_EQ(buffer.length(), 0);
  EXPECT_EQ(cord, "Abcdef");
}

TEST_P(CordTest, TryFlatEmpty) {
  absl::Cord c;
  EXPECT_EQ(c.TryFlat(), "");
}

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, TryFlatCommonlyAssumedInvariants) {
  // The behavior tested below is not part of the API contract of Cord, but it's
  // something we intend to be true in our current implementation.  This test
  // exists to detect and prevent accidental breakage of the implementation.
  absl::string_view fragments[] = {"A fragmented test",
                                   " cord",
                                   " to test subcords",
                                   " of ",
                                   "a",
                                   " cord for",
                                   " each chunk "
                                   "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();
  for (absl::string_view sv : c.Chunks()) {
    absl::string_view expected = fragments[fragment];
    absl::Cord subcord1 = c.Subcord(offset, sv.length());
    absl::Cord subcord2 = absl::Cord::AdvanceAndRead(&itc, sv.size());
    EXPECT_EQ(subcord1.TryFlat(), expected);
    EXPECT_EQ(subcord2.TryFlat(), expected);
    ++fragment;
    offset += sv.length();
  }
}

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_P(CordTest, Flatten) {
  VerifyFlatten(absl::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(MaybeHardened(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_P(CordTest, MultipleLengths) {
  TestData d;
  for (size_t i = 0; i < d.size(); i++) {
    std::string a = d.data(i);

    {  // 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 << "'";
    }

    // Try to append/prepend others
    for (size_t j = 0; j < d.size(); j++) {
      std::string b = d.data(j);

      {  // 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 << "'";
      }

      {  // 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 << "'";
      }

      {  // 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 << "'";
      }
    }
  }
}

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));
}

TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) {
  absl::Cord c;
  c.Append(absl::Cord(std::string(100, 'x')));
  absl::Cord other_ref = c;  // Prevent inplace appends
  EXPECT_THAT(other_ref, testing::Eq(c));
  MaybeHarden(c);
  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_P(CordTest, CordSpliceTestZedBlock) {
  absl::Cord blob = CordWithZedBlock(10);
  MaybeHarden(blob);
  EXPECT_EQ(10, blob.size());
  std::string s;
  absl::CopyCordToString(blob, &s);
  EXPECT_EQ("zzzzzzzzzz", s);
}

TEST_P(CordTest, CordSpliceTestZedBlock0) {
  absl::Cord blob = CordWithZedBlock(0);
  MaybeHarden(blob);
  EXPECT_EQ(0, blob.size());
  std::string s;
  absl::CopyCordToString(blob, &s);
  EXPECT_EQ("", s);
}

TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) {
  absl::Cord blob = CordWithZedBlock(10);
  MaybeHarden(blob);
  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_P(CordTest, CordSpliceTestZedBlockSuffix0) {
  absl::Cord blob = CordWithZedBlock(10);
  MaybeHarden(blob);
  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) {
  CHECK_GE(offset, 0);
  CHECK_LE(static_cast<size_t>(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);
  CHECK_EQ(blob.size(), result.size());
  return result;
}

// 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;
  result.Append(suffix);
}

TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) {
  absl::Cord zero = CordWithZedBlock(10);
  MaybeHarden(zero);
  absl::Cord prefix(zero);
  prefix.RemoveSuffix(10);
  absl::Cord suffix(zero);
  suffix.RemovePrefix(10);
  absl::Cord result(prefix);
  result.Append(suffix);
}

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, bool use_crc)
      : lhs_cord(lhs), rhs_cord(rhs) {
    if (use_crc) {
      lhs_cord.SetExpectedChecksum(1);
    }
  }

  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_P(CordTest, 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");

  const bool use_crc = UseCrc();

  std::vector<CordCompareTestCase> test_cases = {{
      // Inline cords
      {"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", 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, 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, use_crc},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD",
       use_crc},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD",
       use_crc},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD",
       use_crc},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD",
       use_crc},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe",
       use_crc},

      {concat, concat2, use_crc},
  }};

  for (const auto& tc : test_cases) {
    VerifyComparison(tc);
  }
}

TEST_P(CordTest, CompareAfterAssign) {
  absl::Cord a("aaaaaa1111111");
  absl::Cord b("aaaaaa2222222");
  MaybeHarden(a);
  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.
static void TestCompare(const absl::Cord& c, const absl::Cord& d,
                        RandomEngine* rng) {
  // char_traits<char>::lt is guaranteed to do an unsigned comparison:
  // https://en.cppreference.com/w/cpp/string/char_traits/cmp. We also expect
  // Cord comparisons to be based on unsigned byte comparisons regardless of
  // whether char is signed.
  int expected = sign(std::string(c).compare(std::string(d)));
  EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
}

TEST_P(CordTest, CompareComparisonIsUnsigned) {
  RandomEngine rng(GTEST_FLAG_GET(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_P(CordTest, CompareRandomComparisons) {
  const int kIters = 5000;
  RandomEngine rng(GTEST_FLAG_GET(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);
    MaybeHarden(c);
    MaybeHarden(d);
    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.
  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_P(CordTest, ComparisonOperators_Cord_Cord) {
  CompareOperators<absl::Cord, absl::Cord>();
}

TEST_P(CordTest, ComparisonOperators_Cord_StringPiece) {
  CompareOperators<absl::Cord, absl::string_view>();
}

TEST_P(CordTest, ComparisonOperators_StringPiece_Cord) {
  CompareOperators<absl::string_view, absl::Cord>();
}

TEST_P(CordTest, ComparisonOperators_Cord_string) {
  CompareOperators<absl::Cord, std::string>();
}

TEST_P(CordTest, ComparisonOperators_string_Cord) {
  CompareOperators<std::string, absl::Cord>();
}

TEST_P(CordTest, ComparisonOperators_stdstring_Cord) {
  CompareOperators<std::string, absl::Cord>();
}

TEST_P(CordTest, ComparisonOperators_Cord_stdstring) {
  CompareOperators<absl::Cord, std::string>();
}

TEST_P(CordTest, ComparisonOperators_charstar_Cord) {
  CompareOperators<const char*, absl::Cord>();
}

TEST_P(CordTest, ComparisonOperators_Cord_charstar) {
  CompareOperators<absl::Cord, const char*>();
}

TEST_P(CordTest, ConstructFromExternalReleaserInvoked) {
  // 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_THAT(c, testing::Eq(""));
      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_THAT(c, testing::Eq(large_dummy));
      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_P(CordTest, ConstructFromExternalCompareContents) {
  RandomEngine rng(GTEST_FLAG_GET(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;
        });
    MaybeHarden(cord);
    EXPECT_EQ(data, cord);
  }
}

TEST_P(CordTest, ConstructFromExternalLargeReleaser) {
  RandomEngine rng(GTEST_FLAG_GET(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)MaybeHardened(absl::MakeCordFromExternal(data, releaser));
  EXPECT_TRUE(invoked);
}

TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) {
  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)MaybeHardened(absl::MakeCordFromExternal(data, releaser));
  EXPECT_TRUE(invoked);

  invoked = false;
  (void)MaybeHardened(absl::MakeCordFromExternal(data, *releaser));
  EXPECT_TRUE(invoked);
}

TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) {
  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)MaybeHardened(absl::MakeCordFromExternal("dummy", Releaser(&invoked)));
  EXPECT_TRUE(invoked);
}

TEST_P(CordTest, ConstructFromExternalNoArgLambda) {
  bool invoked = false;
  (void)MaybeHardened(
      absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }));
  EXPECT_TRUE(invoked);
}

TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) {
  bool invoked = false;
  (void)MaybeHardened(absl::MakeCordFromExternal(
      "dummy", [&invoked](absl::string_view) { invoked = true; }));
  EXPECT_TRUE(invoked);
}

TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) {
  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)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser));
  EXPECT_TRUE(destroyed);
}

TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) {
  enum InvokedAs { kMissing, kLValue, kRValue };
  enum CopiedAs { kNone, kMove, kCopy };
  struct Tracker {
    CopiedAs copied_as = kNone;
    InvokedAs invoked_as = kMissing;

    void Record(InvokedAs rhs) {
      ASSERT_EQ(invoked_as, kMissing);
      invoked_as = rhs;
    }

    void Record(CopiedAs rhs) {
      if (copied_as == kNone || rhs == kCopy) copied_as = rhs;
    }
  } tracker;

  class Releaser {
   public:
    explicit Releaser(Tracker* tracker) : tr_(tracker) { *tracker = Tracker(); }
    Releaser(Releaser&& rhs) : tr_(rhs.tr_) { tr_->Record(kMove); }
    Releaser(const Releaser& rhs) : tr_(rhs.tr_) { tr_->Record(kCopy); }

    void operator()(absl::string_view) & { tr_->Record(kLValue); }
    void operator()(absl::string_view) && { tr_->Record(kRValue); }

   private:
    Tracker* tr_;
  };

  const Releaser releaser1(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("", releaser1));
  EXPECT_EQ(tracker.copied_as, kCopy);
  EXPECT_EQ(tracker.invoked_as, kRValue);

  const Releaser releaser2(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("", releaser2));
  EXPECT_EQ(tracker.copied_as, kCopy);
  EXPECT_EQ(tracker.invoked_as, kRValue);

  Releaser releaser3(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("", std::move(releaser3)));
  EXPECT_EQ(tracker.copied_as, kMove);
  EXPECT_EQ(tracker.invoked_as, kRValue);

  Releaser releaser4(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser4));
  EXPECT_EQ(tracker.copied_as, kCopy);
  EXPECT_EQ(tracker.invoked_as, kRValue);

  const Releaser releaser5(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser5));
  EXPECT_EQ(tracker.copied_as, kCopy);
  EXPECT_EQ(tracker.invoked_as, kRValue);

  Releaser releaser6(&tracker);
  (void)MaybeHardened(absl::MakeCordFromExternal("foo", std::move(releaser6)));
  EXPECT_EQ(tracker.copied_as, kMove);
  EXPECT_EQ(tracker.invoked_as, kRValue);
}

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));
  }
}

TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) {
  // 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);
      MaybeHarden(result);
      result.RemovePrefix(offset);
      MaybeHarden(result);
      result.RemoveSuffix(result.size() - length);
      EXPECT_EQ(s.substr(offset, length), std::string(result))
          << offset << " " << length;
    }
  }
}

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]);
  }
}

// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage()
// We use whiteboxed expectations based on our knowledge of the layout and size
// of empty and inlined cords, and flat nodes.

constexpr auto kFairShare = absl::CordMemoryAccounting::kFairShare;
constexpr auto kTotalMorePrecise =
    absl::CordMemoryAccounting::kTotalMorePrecise;

// Creates a cord of `n` `c` values, making sure no string stealing occurs.
absl::Cord MakeCord(size_t n, char c) {
  const std::string s(n, c);
  return absl::Cord(s);
}

TEST(CordTest, CordMemoryUsageEmpty) {
  absl::Cord cord;
  EXPECT_EQ(sizeof(absl::Cord), cord.EstimatedMemoryUsage());
  EXPECT_EQ(sizeof(absl::Cord), cord.EstimatedMemoryUsage(kFairShare));
  EXPECT_EQ(sizeof(absl::Cord), cord.EstimatedMemoryUsage(kTotalMorePrecise));
}

TEST(CordTest, CordMemoryUsageInlined) {
  absl::Cord a("hello");
  EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
  EXPECT_EQ(a.EstimatedMemoryUsage(kFairShare), sizeof(absl::Cord));
  EXPECT_EQ(a.EstimatedMemoryUsage(kTotalMorePrecise), sizeof(absl::Cord));
}

TEST(CordTest, CordMemoryUsageExternalMemory) {
  absl::Cord cord;
  AddExternalMemory(std::string(1000, 'x'), &cord);
  const size_t expected =
      sizeof(absl::Cord) + 1000 + sizeof(CordRepExternal) + sizeof(intptr_t);
  EXPECT_EQ(cord.EstimatedMemoryUsage(), expected);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), expected);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kTotalMorePrecise), expected);
}

TEST(CordTest, CordMemoryUsageFlat) {
  absl::Cord cord = MakeCord(1000, 'a');
  const size_t flat_size =
      absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize();
  EXPECT_EQ(cord.EstimatedMemoryUsage(), sizeof(absl::Cord) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + flat_size);
}

TEST(CordTest, CordMemoryUsageSubStringSharedFlat) {
  absl::Cord flat = MakeCord(2000, 'a');
  const size_t flat_size =
      absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize();
  absl::Cord cord = flat.Subcord(500, 1000);
  EXPECT_EQ(cord.EstimatedMemoryUsage(),
            sizeof(absl::Cord) + sizeof(CordRepSubstring) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + sizeof(CordRepSubstring) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + sizeof(CordRepSubstring) + flat_size / 2);
}

TEST(CordTest, CordMemoryUsageFlatShared) {
  absl::Cord shared = MakeCord(1000, 'a');
  absl::Cord cord(shared);
  const size_t flat_size =
      absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize();
  EXPECT_EQ(cord.EstimatedMemoryUsage(), sizeof(absl::Cord) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + flat_size / 2);
}

TEST(CordTest, CordMemoryUsageFlatHardenedAndShared) {
  absl::Cord shared = MakeCord(1000, 'a');
  absl::Cord cord(shared);
  const size_t flat_size =
      absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize();
  cord.SetExpectedChecksum(1);
  EXPECT_EQ(cord.EstimatedMemoryUsage(),
            sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size / 2);

  absl::Cord cord2(cord);
  EXPECT_EQ(cord2.EstimatedMemoryUsage(),
            sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size);
  EXPECT_EQ(cord2.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size);
  EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + (sizeof(CordRepCrc) + flat_size / 2) / 2);
}

TEST(CordTest, CordMemoryUsageBTree) {
  absl::Cord cord1;
  size_t flats1_size = 0;
  absl::Cord flats1[4] = {MakeCord(1000, 'a'), MakeCord(1100, 'a'),
                          MakeCord(1200, 'a'), MakeCord(1300, 'a')};
  for (absl::Cord flat : flats1) {
    flats1_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize();
    cord1.Append(std::move(flat));
  }

  // Make sure the created cord is a BTREE tree. Under some builds such as
  // windows DLL, we may have ODR like effects on the flag, meaning the DLL
  // code will run with the picked up default.
  if (!absl::CordTestPeer::Tree(cord1)->IsBtree()) {
    LOG(WARNING) << "Cord library code not respecting btree flag";
    return;
  }

  size_t rep1_size = sizeof(CordRepBtree) + flats1_size;
  size_t rep1_shared_size = sizeof(CordRepBtree) + flats1_size / 2;

  EXPECT_EQ(cord1.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep1_size);
  EXPECT_EQ(cord1.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + rep1_size);
  EXPECT_EQ(cord1.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + rep1_shared_size);

  absl::Cord cord2;
  size_t flats2_size = 0;
  absl::Cord flats2[4] = {MakeCord(600, 'a'), MakeCord(700, 'a'),
                          MakeCord(800, 'a'), MakeCord(900, 'a')};
  for (absl::Cord& flat : flats2) {
    flats2_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize();
    cord2.Append(std::move(flat));
  }
  size_t rep2_size = sizeof(CordRepBtree) + flats2_size;

  EXPECT_EQ(cord2.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep2_size);
  EXPECT_EQ(cord2.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + rep2_size);
  EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + rep2_size);

  absl::Cord cord(cord1);
  cord.Append(std::move(cord2));

  EXPECT_EQ(cord.EstimatedMemoryUsage(),
            sizeof(absl::Cord) + sizeof(CordRepBtree) + rep1_size + rep2_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) + sizeof(CordRepBtree) + rep1_size + rep2_size);
  EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare),
            sizeof(absl::Cord) + sizeof(CordRepBtree) + rep1_shared_size / 2 +
                rep2_size);
}

TEST(CordTest, TestHashFragmentation) {
  // Make sure we hit these boundary cases precisely.
  EXPECT_EQ(1024, absl::hash_internal::PiecewiseChunkSize());
  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
      absl::Cord(),
      absl::MakeFragmentedCord({std::string(600, 'a'), std::string(600, 'a')}),
      absl::MakeFragmentedCord({std::string(1200, 'a')}),
      absl::MakeFragmentedCord({std::string(900, 'b'), std::string(900, 'b')}),
      absl::MakeFragmentedCord({std::string(1800, 'b')}),
      absl::MakeFragmentedCord(
          {std::string(2000, 'c'), std::string(2000, 'c')}),
      absl::MakeFragmentedCord({std::string(4000, 'c')}),
      absl::MakeFragmentedCord({std::string(1024, 'd')}),
      absl::MakeFragmentedCord({std::string(1023, 'd'), "d"}),
      absl::MakeFragmentedCord({std::string(1025, 'e')}),
      absl::MakeFragmentedCord({std::string(1024, 'e'), "e"}),
      absl::MakeFragmentedCord({std::string(1023, 'e'), "e""e"}),
  }));
}

// 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_P(CordTest, CordMemoryUsageInlineRep) {
  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());
}

TEST_P(CordTest, CordMemoryUsageTotalMorePreciseMode) {
  constexpr size_t kChunkSize = 2000;
  std::string tmp_str(kChunkSize, 'x');
  const absl::Cord flat(std::move(tmp_str));

  // Construct `fragmented` with two references into the same
  // underlying buffer shared with `flat`:
  absl::Cord fragmented(flat);
  fragmented.Append(flat);

  // Memory usage of `flat`, minus the top-level Cord object:
  const size_t flat_internal_usage =
      flat.EstimatedMemoryUsage() - sizeof(absl::Cord);

  // `fragmented` holds a Cord and a CordRepBtree. That tree points to two
  // copies of flat's internals, which we expect to dedup:
  EXPECT_EQ(fragmented.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) +
            sizeof(CordRepBtree) +
            flat_internal_usage);

  // This is a case where kTotal produces an overestimate:
  EXPECT_EQ(fragmented.EstimatedMemoryUsage(),
            sizeof(absl::Cord) +
            sizeof(CordRepBtree) +
            2 * flat_internal_usage);
}

TEST_P(CordTest, CordMemoryUsageTotalMorePreciseModeWithSubstring) {
  constexpr size_t kChunkSize = 2000;
  std::string tmp_str(kChunkSize, 'x');
  const absl::Cord flat(std::move(tmp_str));

  // Construct `fragmented` with two references into the same
  // underlying buffer shared with `flat`.
  //
  // This time, each reference is through a Subcord():
  absl::Cord fragmented;
  fragmented.Append(flat.Subcord(1, kChunkSize - 2));
  fragmented.Append(flat.Subcord(1, kChunkSize - 2));

  // Memory usage of `flat`, minus the top-level Cord object:
  const size_t flat_internal_usage =
      flat.EstimatedMemoryUsage() - sizeof(absl::Cord);

  // `fragmented` holds a Cord and a CordRepBtree. That tree points to two
  // CordRepSubstrings, each pointing at flat's internals.
  EXPECT_EQ(fragmented.EstimatedMemoryUsage(kTotalMorePrecise),
            sizeof(absl::Cord) +
            sizeof(CordRepBtree) +
            2 * sizeof(CordRepSubstring) +
            flat_internal_usage);

  // This is a case where kTotal produces an overestimate:
  EXPECT_EQ(fragmented.EstimatedMemoryUsage(),
            sizeof(absl::Cord) +
            sizeof(CordRepBtree) +
            2 * sizeof(CordRepSubstring) +
            2 * flat_internal_usage);
}
}  // namespace

// Regtest for 7510292 (fix a bug introduced by 7465150)
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.
  EXPECT_EQ(s1.size(), size);
  EXPECT_EQ(s2.size(), size + 1);
}

TEST_P(CordTest, DiabolicalGrowth) {
  // This test exercises a diabolical Append(<one char>) on a cord, making the
  // cord shared before each Append call resulting in a terribly fragmented
  // resulting cord.
  RandomEngine rng(GTEST_FLAG_GET(random_seed));
  const std::string expected = RandomLowercaseString(&rng, 5000);
  absl::Cord cord;
  for (char c : expected) {
    absl::Cord shared(cord);
    EXPECT_THAT(cord, testing::Eq(shared));
    cord.Append(absl::string_view(&c, 1));
    MaybeHarden(cord);
  }
  std::string value;
  absl::CopyCordToString(cord, &value);
  EXPECT_EQ(value, expected);
  LOG(INFO) << "Diabolical size allocated = " << cord.EstimatedMemoryUsage();
}

// The following tests check support for >4GB cords in 64-bit binaries, and
// 2GB-4GB cords in 32-bit binaries.  This function returns the large cord size
// that's appropriate for the binary.

// Construct a huge cord with the specified valid prefix.
static absl::Cord MakeHuge(absl::string_view prefix) {
  absl::Cord cord;
  if (sizeof(size_t) > 4) {
    // In 64-bit binaries, test 64-bit Cord support.
    const size_t size =
        static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 314;
    cord.Append(absl::MakeCordFromExternal(
        absl::string_view(prefix.data(), size),
        [](absl::string_view s) { DoNothing(s, nullptr); }));
  } else {
    // Cords are limited to 32-bit lengths in 32-bit binaries.  The following
    // tests check for use of "signed int" to represent Cord length/offset.
    // However absl::string_view does not allow lengths >= (1u<<31), so we need
    // to append in two parts;
    const size_t s1 = (1u << 31) - 1;
    // For shorter cord, `Append` copies the data rather than allocating a new
    // node. The threshold is currently set to 511, so `s2` needs to be bigger
    // to not trigger the copy.
    const size_t s2 = 600;
    cord.Append(absl::MakeCordFromExternal(
        absl::string_view(prefix.data(), s1),
        [](absl::string_view s) { DoNothing(s, nullptr); }));
    cord.Append(absl::MakeCordFromExternal(
        absl::string_view("", s2),
        [](absl::string_view s) { DoNothing(s, nullptr); }));
  }
  return cord;
}

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() + acceptable_delta, cord.EstimatedMemoryUsage());
}

// Tests that Append() works ok when handed a self reference
TEST_P(CordTest, AppendSelf) {
  // Test the empty case.
  absl::Cord empty;
  MaybeHarden(empty);
  empty.Append(empty);
  ASSERT_EQ(empty, "");

  // We run the test until data is ~16K
  // This guarantees it covers small, medium and large data.
  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);
  }
}

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();

  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_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();

  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_P(CordTest, CordChunkIteratorTraits) {
  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_P(CordTest, CordChunkIteratorOperations) {
  absl::Cord empty_cord;
  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(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);
  }

  RandomEngine rng(GTEST_FLAG_GET(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);
}


TEST_P(CordTest, AdvanceAndReadOnDataEdge) {
  RandomEngine rng(GTEST_FLAG_GET(random_seed));
  const std::string data = RandomLowercaseString(&rng, 2000);
  for (bool as_flat : {truefalse}) {
    SCOPED_TRACE(as_flat ? "Flat" : "External");

    absl::Cord cord =
        as_flat ? absl::Cord(data)
                : absl::MakeCordFromExternal(data, [](absl::string_view) {});
    auto it = cord.Chars().begin();
#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
    EXPECT_DEATH_IF_SUPPORTED(cord.AdvanceAndRead(&it, 2001), ".*");
#endif

    it = cord.Chars().begin();
    absl::Cord frag = cord.AdvanceAndRead(&it, 2000);
    EXPECT_EQ(frag, data);
    EXPECT_TRUE(it == cord.Chars().end());

    it = cord.Chars().begin();
    frag = cord.AdvanceAndRead(&it, 200);
    EXPECT_EQ(frag, data.substr(0, 200));
    EXPECT_FALSE(it == cord.Chars().end());

    frag = cord.AdvanceAndRead(&it, 1500);
    EXPECT_EQ(frag, data.substr(200, 1500));
    EXPECT_FALSE(it == cord.Chars().end());

    frag = cord.AdvanceAndRead(&it, 300);
    EXPECT_EQ(frag, data.substr(1700, 300));
    EXPECT_TRUE(it == cord.Chars().end());
  }
}

TEST_P(CordTest, AdvanceAndReadOnSubstringDataEdge) {
  RandomEngine rng(GTEST_FLAG_GET(random_seed));
  const std::string data = RandomLowercaseString(&rng, 2500);
  for (bool as_flat : {truefalse}) {
    SCOPED_TRACE(as_flat ? "Flat" : "External");

    absl::Cord cord =
        as_flat ? absl::Cord(data)
                : absl::MakeCordFromExternal(data, [](absl::string_view) {});
    cord = cord.Subcord(200, 2000);
    const std::string substr = data.substr(200, 2000);

    auto it = cord.Chars().begin();
#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
    EXPECT_DEATH_IF_SUPPORTED(cord.AdvanceAndRead(&it, 2001), ".*");
#endif

    it = cord.Chars().begin();
    absl::Cord frag = cord.AdvanceAndRead(&it, 2000);
    EXPECT_EQ(frag, substr);
    EXPECT_TRUE(it == cord.Chars().end());

    it = cord.Chars().begin();
    frag = cord.AdvanceAndRead(&it, 200);
    EXPECT_EQ(frag, substr.substr(0, 200));
    EXPECT_FALSE(it == cord.Chars().end());

    frag = cord.AdvanceAndRead(&it, 1500);
    EXPECT_EQ(frag, substr.substr(200, 1500));
    EXPECT_FALSE(it == cord.Chars().end());

    frag = cord.AdvanceAndRead(&it, 300);
    EXPECT_EQ(frag, substr.substr(1700, 300));
    EXPECT_TRUE(it == cord.Chars().end());
  }
}

TEST_P(CordTest, CharIteratorTraits) {
  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);

    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_P(CordTest, CharIteratorOperations) {
  absl::Cord empty_cord;
  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(MaybeHardened(
      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);
    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));
    MaybeHarden(subcords);
  }
  VerifyCharIterator(subcords);
}

TEST_P(CordTest, CharIteratorAdvanceAndRead) {
  // Create a Cord holding 6 flats of 2500 bytes each, and then iterate over it
  // reading 150, 1500, 2500 and 3000 bytes. This will result in all possible
  // partial, full and straddled read combinations including reads below
  // kMaxBytesToCopy. b/197776822 surfaced a bug for a specific partial, small
  // read 'at end' on Cord which caused a failure on attempting to read past the
  // end in CordRepBtreeReader which was not covered by any existing test.
  constexpr int kBlocks = 6;
  constexpr size_t kBlockSize = 2500;
  constexpr size_t kChunkSize1 = 1500;
  constexpr size_t kChunkSize2 = 2500;
  constexpr size_t kChunkSize3 = 3000;
  constexpr size_t kChunkSize4 = 150;
  RandomEngine rng;
  std::string data = RandomLowercaseString(&rng, kBlocks * kBlockSize);
  absl::Cord cord;
  for (int i = 0; i < kBlocks; ++i) {
    const std::string block = data.substr(i * kBlockSize, kBlockSize);
    cord.Append(absl::Cord(block));
  }

  MaybeHarden(cord);

  for (size_t chunk_size :
       {kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) {
    absl::Cord::CharIterator it = cord.char_begin();
    size_t offset = 0;
    while (offset < data.length()) {
      const size_t n = std::min<size_t>(data.length() - offset, chunk_size);
      absl::Cord chunk = cord.AdvanceAndRead(&it, n);
      ASSERT_EQ(chunk.size(), n);
      ASSERT_EQ(chunk.Compare(data.substr(offset, n)), 0);
      offset += n;
    }
  }
}

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());
}

TEST_P(CordTest, 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);
    MaybeHarden(c);

    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_P(CordTest, 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);
      MaybeHarden(c);
      absl::string_view flat = c.Flatten();
      c = flat.substr(pos, count);
      EXPECT_EQ(c, contents.substr(pos, count))
          << "pos = " << pos << "; count = " << count;
    }
  }
}

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, Stringify) {
  absl::Cord c =
      absl::MakeFragmentedCord({"A ""small ""fragmented ""Cord""."});
  MaybeHarden(c);
  EXPECT_EQ(absl::StrCat(c), "A small fragmented Cord.");
}

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), "");

  bool test_hardening = false;
  ABSL_HARDENING_ASSERT([&]() {
    // This only runs when ABSL_HARDENING_ASSERT is active.
    test_hardening = true;
    return true;
  }());
  if (!test_hardening) return;

  EXPECT_DEATH_IF_SUPPORTED(cord[5], "");
  EXPECT_DEATH_IF_SUPPORTED(*cord.chunk_end(), "");
  EXPECT_DEATH_IF_SUPPORTED(static_cast<void>(cord.chunk_end()->empty()), "");
  EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), "");
}

// This test mimics a specific (and rare) application repeatedly splitting a
// cord, inserting (overwriting) a string value, and composing a new cord from
// the three pieces. This is hostile towards a Btree implementation: A split of
// a node at any level is likely to have the right-most edge of the left split,
// and the left-most edge of the right split shared. For example, splitting a
// leaf node with 6 edges will result likely in a 1-6, 2-5, 3-4, etc. split,
// sharing the 'split node'. When recomposing such nodes, we 'injected' an edge
// in that node. As this happens with some probability on each level of the
// tree, this will quickly grow the tree until it reaches maximum height.
TEST_P(CordTest, BtreeHostileSplitInsertJoin) {
  absl::BitGen bitgen;

  // Start with about 1GB of data
  std::string data(1 << 10, 'x');
  absl::Cord buffer(data);
  absl::Cord cord;
  for (int i = 0; i < 1000000; ++i) {
    cord.Append(buffer);
  }

  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) {
      cord.Append(absl::string_view(data.data(), length));
    } else {
      absl::Cord suffix;
      if (offset + length < cord.size()) {
        suffix = cord;
        suffix.RemovePrefix(offset + length);
      }
      if (cord.size() > offset) {
        cord.RemoveSuffix(cord.size() - offset);
      }
      cord.Append(absl::string_view(data.data(), length));
      if (!suffix.empty()) {
        cord.Append(suffix);
      }
    }
  }
}

class AfterExitCordTester {
 public:
  bool Set(absl::Cord* cord, absl::string_view expected) {
    cord_ = cord;
    expected_ = expected;
    return true;
  }

  ~AfterExitCordTester() {
    EXPECT_EQ(*cord_, expected_);
  }
 private:
  absl::Cord* cord_;
  absl::string_view expected_;
};

template <typename Str>
void TestAfterExit(Str) {
  const auto expected = Str::value;
  // Defined before `cord` to be destroyed after it.
  static AfterExitCordTester exit_tester;  // NOLINT
  static absl::NoDestructor<absl::Cord> cord_leaker(Str{});
  // cord_leaker is static, so this reference will remain valid through the end
  // of program execution.
  static absl::Cord& cord = *cord_leaker;
  static bool init_exit_tester = exit_tester.Set(&cord, expected);
  (void)init_exit_tester;

  EXPECT_EQ(cord, expected);
  // Copy the object and test the copy, and the original.
  {
    absl::Cord copy = cord;
    EXPECT_EQ(copy, expected);
  }
  // The original still works
  EXPECT_EQ(cord, expected);

  // Try making adding more structure to the tree.
  {
    absl::Cord copy = cord;
    std::string expected_copy(expected);
    for (int i = 0; i < 10; ++i) {
      copy.Append(cord);
      absl::StrAppend(&expected_copy, expected);
      EXPECT_EQ(copy, expected_copy);
    }
  }

  // Make sure we are using the right branch during constant evaluation.
  EXPECT_EQ(absl::CordTestPeer::IsTree(cord), cord.size() >= 16);

  for (int i = 0; i < 10; ++i) {
    // Make a few more Cords from the same global rep.
    // This tests what happens when the refcount for it gets below 1.
    EXPECT_EQ(expected, absl::Cord(Str{}));
  }
}

constexpr int SimpleStrlen(const char* p) {
  return *p ? 1 + SimpleStrlen(p + 1) : 0;
}

struct ShortView {
  constexpr absl::string_view operator()() const {
    return absl::string_view("SSO string", SimpleStrlen("SSO string"));
  }
};

struct LongView {
  constexpr absl::string_view operator()() const {
    return absl::string_view("String that does not fit SSO.",
                             SimpleStrlen("String that does not fit SSO."));
  }
};


TEST_P(CordTest, AfterExit) {
  TestAfterExit(absl::strings_internal::MakeStringConstant(ShortView{}));
  TestAfterExit(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); }
  },
  {
    "append empty string",
    [](absl::Cord& c) { c.Append(""); },
    [](absl::Cord& c) { }
  },
  {
    "append empty cord",
    [](absl::Cord& c) { c.Append(absl::Cord()); },
    [](absl::Cord& c) { }
  },
  {
    "append empty checksummed cord",
    [](absl::Cord& c) {
      absl::Cord to_append;
      to_append.SetExpectedChecksum(999);
      c.Append(to_append);
    },
    [](absl::Cord& c) { }
  },
  {
    "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 empty string",
    [](absl::Cord& c) { c.Prepend(""); },
    [](absl::Cord& c) { }
  },
  {
    "prepend empty cord",
    [](absl::Cord& c) { c.Prepend(absl::Cord()); },
    [](absl::Cord& c) { }
  },
  {
    "prepend empty checksummed cord",
    [](absl::Cord& c) {
      absl::Cord to_prepend;
      to_prepend.SetExpectedChecksum(999);
      c.Prepend(to_prepend);
    },
    [](absl::Cord& c) { }
  },
  {
    "prepend self",
    [](absl::Cord& c) { c.Prepend(c); },
    [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }
  },
  {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); }},
  {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }},
  {"remove 0-prefix", [](absl::Cord& c) { c.RemovePrefix(0); }},
  {"remove 0-suffix", [](absl::Cord& c) { c.RemoveSuffix(0); }},
  {"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 : {falsetrue}) {
      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);

      // Test that setting an expected checksum again doesn't crash or leak
      // memory.
      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);

        if (c1 == c2) {
          // Not a mutation (for example, appending the empty string).
          // Whether the checksum is removed is not defined.
          continue;
        }

        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));
    }
  }
}

// Test the special cases encountered with an empty checksummed cord.
TEST_P(CordTest, ChecksummedEmptyCord) {
  absl::Cord c1;
  EXPECT_FALSE(c1.ExpectedChecksum().has_value());

  // Setting an expected checksum works.
  c1.SetExpectedChecksum(12345);
  EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
  EXPECT_EQ(c1, "");
  EXPECT_TRUE(c1.empty());

  // Test that setting an expected checksum again doesn't crash or leak memory.
  c1.SetExpectedChecksum(12345);
  EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
  EXPECT_EQ(c1, "");
  EXPECT_TRUE(c1.empty());

  // 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, absl::Cord());

  for (const CordMutator& mutator : cord_mutators) {
    SCOPED_TRACE(mutator.Name());

    // Exercise mutating an empty checksummed cord to catch crashes and exercise
    // memory sanitizers.
    absl::Cord c2;
    c2.SetExpectedChecksum(24680);
    mutator.Mutate(c2);

    if (c2.empty()) {
      // Not a mutation
      continue;
    }
    EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt);

    if (mutator.CanUndo()) {
      mutator.Undo(c2);
    }
  }

  absl::Cord c3;
  c3.SetExpectedChecksum(999);
  const absl::Cord& cc3 = c3;

  // Test that all cord reading operations function in the face of an
  // expected checksum.
  EXPECT_TRUE(cc3.StartsWith(""));
  EXPECT_TRUE(cc3.EndsWith(""));
  EXPECT_TRUE(cc3.empty());
  EXPECT_EQ(cc3, "");
  EXPECT_EQ(cc3, absl::Cord());
  EXPECT_EQ(cc3.size(), 0);
  EXPECT_EQ(cc3.Compare(absl::Cord()), 0);
  EXPECT_EQ(cc3.Compare(c1), 0);
  EXPECT_EQ(cc3.Compare(cc3), 0);
  EXPECT_EQ(cc3.Compare(""), 0);
  EXPECT_EQ(cc3.Compare("wxyz"), -1);
  EXPECT_EQ(cc3.Compare(absl::Cord("wxyz")), -1);
  EXPECT_EQ(absl::Cord("wxyz").Compare(cc3), 1);
  EXPECT_EQ(std::string(cc3), "");

  std::string dest;
  absl::CopyCordToString(cc3, &dest);
  EXPECT_EQ(dest, "");

  for (absl::string_view chunk : cc3.Chunks()) {  // NOLINT(unreachable loop)
    static_cast<void>(chunk);
    GTEST_FAIL() << "no chunks expected";
  }
  EXPECT_TRUE(cc3.chunk_begin() == cc3.chunk_end());

  for (char ch : cc3.Chars()) {  // NOLINT(unreachable loop)
    static_cast<void>(ch);
    GTEST_FAIL() << "no chars expected";
  }
  EXPECT_TRUE(cc3.char_begin() == cc3.char_end());

  EXPECT_EQ(cc3.TryFlat(), "");
  EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::Cord()));
  EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::string_view()));
}

// This must not be static to avoid aggressive optimizations.
ABSL_ATTRIBUTE_WEAK
size_t FalseReport(const absl::Cord& a, bool f);

ABSL_ATTRIBUTE_NOINLINE
size_t FalseReport(const absl::Cord& a, bool f) {
  absl::Cord b;
  const absl::Cord& ref = f ? b : a;
  // Test that sanitizers report nothing here. Without
  // InlineData::Rep::annotated_this() compiler can unconditionally load
  // poisoned parts, assuming that local variable is fully accessible.
  return ref.size();
}

TEST(CordSanitizerTest, SanitizesCordFalseReport) {
  absl::Cord c;
  for (int i = 0; i < 1000; ++i) c.Append("a");
  FalseReport(c, false);
}

TEST(CrcCordTest, ChecksummedEmptyCordEstimateMemoryUsage) {
  absl::Cord cord;
  cord.SetExpectedChecksum(0);
  EXPECT_NE(cord.EstimatedMemoryUsage(), 0);
}

TEST(CordThreeWayComparisonTest, CompareCords) {
#ifndef __cpp_impl_three_way_comparison
  GTEST_SKIP() << "C++20 three-way <=> comparison not supported";
#else
  EXPECT_EQ(absl::Cord("a") <=> absl::Cord("a"), std::strong_ordering::equal);
  EXPECT_EQ(absl::Cord("aaaa") <=> absl::Cord("aaab"),
            std::strong_ordering::less);
  EXPECT_EQ(absl::Cord("baaa") <=> absl::Cord("a"),
            std::strong_ordering::greater);
#endif
}

TEST(CordThreeWayComparisonTest, CompareCordsAndStringViews) {
#ifndef __cpp_impl_three_way_comparison
  GTEST_SKIP() << "C++20 three-way <=> comparison not supported";
#else
  EXPECT_EQ(absl::string_view("a") <=> absl::Cord("a"),
            std::strong_ordering::equal);
  EXPECT_EQ(absl::Cord("a") <=> absl::string_view("b"),
            std::strong_ordering::less);
  EXPECT_EQ(absl::string_view("b") <=> absl::Cord("a"),
            std::strong_ordering::greater);
#endif
}

#if defined(GTEST_HAS_DEATH_TEST) && defined(ABSL_INTERNAL_CORD_HAVE_SANITIZER)

// Returns an expected poison / uninitialized death message expression.
const char* MASanDeathExpr() {
  return "(use-after-poison|use-of-uninitialized-value)";
}

TEST(CordSanitizerTest, SanitizesEmptyCord) {
  absl::Cord cord;
  const char* data = cord.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[0], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesSmallCord) {
  absl::Cord cord("Hello");
  const char* data = cord.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnSetSSOValue) {
  absl::Cord cord("String that is too big to be an SSO value");
  cord = "Hello";
  const char* data = cord.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnCopyCtor) {
  absl::Cord src("hello");
  absl::Cord dst(src);
  const char* data = dst.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnMoveCtor) {
  absl::Cord src("hello");
  absl::Cord dst(std::move(src));
  const char* data = dst.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnAssign) {
  absl::Cord src("hello");
  absl::Cord dst;
  dst = src;
  const char* data = dst.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnMoveAssign) {
  absl::Cord src("hello");
  absl::Cord dst;
  dst = std::move(src);
  const char* data = dst.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

TEST(CordSanitizerTest, SanitizesCordOnSsoAssign) {
  absl::Cord src("hello");
  absl::Cord dst("String that is too big to be an SSO value");
  dst = src;
  const char* data = dst.Flatten().data();
  EXPECT_DEATH(EXPECT_EQ(data[5], 0), MASanDeathExpr());
}

#endif  // GTEST_HAS_DEATH_TEST && ABSL_INTERNAL_CORD_HAVE_SANITIZER

Messung V0.5 in Prozent
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.101 Sekunden  (vorverarbeitet am  2026-04-25) ¤

*© Formatika GbR, Deutschland






vorgegebene Sprache:

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.