// 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/options.h"
#include "absl/container/fixed_array.h"
#include "absl/functional/function_ref.h"
#include "absl/hash/hash.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/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 Cord Btree enabled,
// and with our without expected CRCs being set on the subject Cords.
class CordTest : public testing::TestWithParam<int> {
 public:
  // Returns true if test is running with btree enabled.
  bool UseCrc() const { return GetParam() == 2 || GetParam() == 3; }
  void MaybeHarden(absl::Cord& c) {
    if (UseCrc()) {
      c.SetExpectedChecksum(1);
    }
  }
  absl::Cord MaybeHardened(absl::Cord c) {
    MaybeHarden(c);
    return c;
  }

  // Returns human readable string representation of the test parameter.
  static std::string ToString(testing::TestParamInfo<int> param) {
    switch (param.param) {
      case 0:
        return "Btree";
      case 1:
        return "BtreeHardened";
      default:
        assert(false);
        return "???";
    }
  }
};

INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1),
                         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."})));
}

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
  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) {
  typedef std::basic_string<uint8_t> ustring;
  ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size());
  ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size());
  // ustring comparison is ideal because we expect Cord comparisons to be
  // based on unsigned byte comparisons regardless of whether char is signed.
  int expected = sign(cs.compare(ds));
  EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
}

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

  // If the size of the data is small enough, a future constructor
  // implementation may copy the bytes and immediately invoke the releaser
  // instead of creating an external node. We make a large dummy std::string to
  // make this test independent of such an optimization.
  std::string large_dummy(2048, 'c');
  {
    bool invoked = false;
    auto releaser = [&invoked](absl::string_view) { invoked = true; };
    {
      auto c = absl::MakeCordFromExternal(large_dummy, releaser);
      EXPECT_FALSE(invoked);
    }
    EXPECT_TRUE(invoked);
  }

  {
    bool invoked = false;
    auto releaser = [&invoked](absl::string_view) { invoked = true; };
    {
      absl::Cord copy;
      {
        auto c = absl::MakeCordFromExternal(large_dummy, releaser);
        copy = c;
        EXPECT_FALSE(invoked);
      }
      EXPECT_FALSE(invoked);
    }
    EXPECT_TRUE(invoked);
  }
}

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

// 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);
    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 : {true, false}) {
    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 : {true, false}) {
    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);

    EXPECT_EQ(&*pre_iter, pre_iter.operator->());

    const char* character_address = &*pre_iter;
    absl::Cord::CharIterator copy = pre_iter;
    ++copy;
    EXPECT_EQ(character_address, &*pre_iter);

    int n_equal_iterators = 0;
    for (absl::Cord::CharIterator it = range.begin(); it != range.end(); ++it) {
      n_equal_iterators += static_cast<int>(it == pre_iter);
    }
    EXPECT_EQ(n_equal_iterators, 1);

    absl::Cord::CharIterator advance_iter = range.begin();
    absl::Cord::Advance(&advance_iter, i);
    EXPECT_EQ(pre_iter, advance_iter);

    advance_iter = range.begin();
    EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, i), cord.Subcord(0, i));
    EXPECT_EQ(pre_iter, advance_iter);

    advance_iter = pre_iter;
    absl::Cord::Advance(&advance_iter, cord.size() - i);
    EXPECT_EQ(range.end(), advance_iter);

    advance_iter = pre_iter;
    EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, cord.size() - i),
              cord.Subcord(i, cord.size() - i));
    EXPECT_EQ(range.end(), advance_iter);

    ++i;
    ++pre_iter;
    post_iter++;
  }
  EXPECT_EQ(i, cord.size());
  EXPECT_TRUE(pre_iter == cord.char_end());   // NOLINT: explicitly test ==
  EXPECT_TRUE(post_iter == cord.char_end());  // NOLINT

  absl::Cord::CharIterator zero_advanced_end = cord.char_end();
  absl::Cord::Advance(&zero_advanced_end, 0);
  EXPECT_EQ(zero_advanced_end, cord.char_end());

  absl::Cord::CharIterator it = cord.char_begin();
  for (absl::string_view chunk : cord.Chunks()) {
    while (!chunk.empty()) {
      EXPECT_EQ(absl::Cord::ChunkRemaining(it), chunk);
      chunk.remove_prefix(1);
      ++it;
    }
  }
}

TEST_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_;
};

// Deliberately prevents the destructor for an absl::Cord from running. The cord
// is accessible via the cord member during the lifetime of the CordLeaker.
// After the CordLeaker is destroyed, pointers to the cord will remain valid
// until the CordLeaker's memory is deallocated.
struct CordLeaker {
  union {
    absl::Cord cord;
  };

  template <typename Str>
  constexpr explicit CordLeaker(const Str& str) : cord(str) {}

  ~CordLeaker() {
    // Don't do anything, including running cord's destructor. (cord's
    // destructor won't run automatically because cord is hidden inside a
    // union.)
  }
};

template <typename Str>
void TestConstinitConstructor(Str) {
  const auto expected = Str::value;
  // Defined before `cord` to be destroyed after it.
  static AfterExitCordTester exit_tester;  // NOLINT
  ABSL_CONST_INIT static CordLeaker cord_leaker(Str{});  // NOLINT
  // cord_leaker is static, so this reference will remain valid through the end
  // of program execution.
  static absl::Cord& cord = cord_leaker.cord;
  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, ConstinitConstructor) {
  TestConstinitConstructor(
      absl::strings_internal::MakeStringConstant(ShortView{}));
  TestConstinitConstructor(
      absl::strings_internal::MakeStringConstant(LongView{}));
}

namespace {

// Test helper that generates a populated cord for future manipulation.
//
// By test convention, all generated cords begin with the characters "abcde" at
// the start of the first chunk.
class PopulatedCordFactory {
 public:
  constexpr PopulatedCordFactory(absl::string_view name,
                                 absl::Cord (*generator)())
      : name_(name), generator_(generator) {}

  absl::string_view Name() const { return name_; }
  absl::Cord Generate() const { return generator_(); }

 private:
  absl::string_view name_;
  absl::Cord (*generator_)();
};

// clang-format off
// This array is constant-initialized in conformant compilers.
PopulatedCordFactory cord_factories[] = {
  {"sso", [] { return absl::Cord("abcde"); }},
  {"flat", [] {
    // Too large to live in SSO space, but small enough to be a simple FLAT.
    absl::Cord flat(absl::StrCat("abcde", std::string(1000, 'x')));
    flat.Flatten();
    return flat;
  }},
  {"external", [] {
    // A cheat: we are using a string literal as the external storage, so a
    // no-op releaser is correct here.
    return absl::MakeCordFromExternal("abcde External!", []{});
  }},
  {"external substring", [] {
    // A cheat: we are using a string literal as the external storage, so a
    // no-op releaser is correct here.
    absl::Cord ext = absl::MakeCordFromExternal("-abcde External!", []{});
    return absl::CordTestPeer::MakeSubstring(ext, 1, ext.size() - 1);
  }},
  {"substring", [] {
    absl::Cord flat(absl::StrCat("-abcde", std::string(1000, 'x')));
    flat.Flatten();
    return flat.Subcord(1, 998);
  }},
  {"fragmented", [] {
    std::string fragment = absl::StrCat("abcde", std::string(195, 'x'));
    std::vector<std::string> fragments(200, fragment);
    absl::Cord cord = absl::MakeFragmentedCord(fragments);
    assert(cord.size() == 40000);
    return cord;
  }},
};
// clang-format on

// Test helper that can mutate a cord, and possibly undo the mutation, for
// testing.
class CordMutator {
 public:
  constexpr CordMutator(absl::string_view name, void (*mutate)(absl::Cord&),
                        void (*undo)(absl::Cord&) = nullptr)
      : name_(name), mutate_(mutate), undo_(undo) {}

  absl::string_view Name() const { return name_; }
  void Mutate(absl::Cord& cord) const { mutate_(cord); }
  bool CanUndo() const { return undo_ != nullptr; }
  void Undo(absl::Cord& cord) const { undo_(cord); }

 private:
  absl::string_view name_;
  void (*mutate_)(absl::Cord&);
  void (*undo_)(absl::Cord&);
};

// clang-format off
// This array is constant-initialized in conformant compilers.
CordMutator cord_mutators[] = {
  {"clear", [](absl::Cord& c) { c.Clear(); }},
  {"overwrite", [](absl::Cord& c) { c = "overwritten"; }},
  {
    "append string",
    [](absl::Cord& c) { c.Append("0123456789"); },
    [](absl::Cord& c) { c.RemoveSuffix(10); }
  },
  {
    "append cord",
    [](absl::Cord& c) {
      c.Append(absl::MakeFragmentedCord({"12345", "67890"}));
    },
    [](absl::Cord& c) { c.RemoveSuffix(10); }
  },
  {
    "append checksummed cord",
    [](absl::Cord& c) {
      absl::Cord to_append = absl::MakeFragmentedCord({"12345", "67890"});
      to_append.SetExpectedChecksum(999);
      c.Append(to_append);
    },
    [](absl::Cord& c) { c.RemoveSuffix(10); }
  },
  {
    "append self",
    [](absl::Cord& c) { c.Append(c); },
    [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); }
  },
  {
    "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 : {false, true}) {
      SCOPED_TRACE(shared);

      absl::Cord shared_cord_source = factory.Generate();
      auto make_instance = [=] {
        return shared ? shared_cord_source : factory.Generate();
      };

      const absl::Cord base_value = factory.Generate();
      const std::string base_value_as_string(factory.Generate().Flatten());

      absl::Cord c1 = make_instance();
      EXPECT_FALSE(c1.ExpectedChecksum().has_value());

      // Setting an expected checksum works, and retains the cord's bytes
      c1.SetExpectedChecksum(12345);
      EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345);
      EXPECT_EQ(c1, base_value);

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

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

#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
