about summary refs log blame commit diff
path: root/third_party/abseil_cpp/absl/strings/cord_test.cc
blob: 4443c8289658140181070a754e7b2f8362541a17 (plain) (tree)
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587



















                                           
                             


                                           
                                    
















































                                                                            

                                                                  





































































































                                                                              
                                 


























































































































































































































                                                                                


                              














                                                                
                                                            












                                                                       











































                                                                              






















































































                                                                                
                             




                                                      
                                          



                                                      
                             




                                                                             
                              








                                                                             
                         





                                                                        
                                      




                                                                        
                              





                                                                            
                                           




                                                                            
                               





                                                                            
                                            
















































































































































































































































                                                                            
                                        






















































                                                                                
                                                                              

































































































































































                                                                                












                                                                              











































                                                                              
                                                        






























































































































































































































































































































































































































































                                                                                
 







                                                                


















                                                                              
#include "absl/strings/cord.h"

#include <algorithm>
#include <climits>
#include <cstdio>
#include <iterator>
#include <map>
#include <numeric>
#include <random>
#include <sstream>
#include <type_traits>
#include <utility>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/container/fixed_array.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"

typedef std::mt19937_64 RandomEngine;

static std::string RandomLowercaseString(RandomEngine* rng);
static std::string RandomLowercaseString(RandomEngine* rng, size_t length);

static int GetUniformRandomUpTo(RandomEngine* rng, int upper_bound) {
  if (upper_bound > 0) {
    std::uniform_int_distribution<int> uniform(0, upper_bound - 1);
    return uniform(*rng);
  } else {
    return 0;
  }
}

static size_t GetUniformRandomUpTo(RandomEngine* rng, size_t upper_bound) {
  if (upper_bound > 0) {
    std::uniform_int_distribution<size_t> uniform(0, upper_bound - 1);
    return uniform(*rng);
  } else {
    return 0;
  }
}

static int32_t GenerateSkewedRandom(RandomEngine* rng, int max_log) {
  const uint32_t base = (*rng)() % (max_log + 1);
  const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
  return (*rng)() & mask;
}

static std::string RandomLowercaseString(RandomEngine* rng) {
  int length;
  std::bernoulli_distribution one_in_1k(0.001);
  std::bernoulli_distribution one_in_10k(0.0001);
  // With low probability, make a large fragment
  if (one_in_10k(*rng)) {
    length = GetUniformRandomUpTo(rng, 1048576);
  } else if (one_in_1k(*rng)) {
    length = GetUniformRandomUpTo(rng, 10000);
  } else {
    length = GenerateSkewedRandom(rng, 10);
  }
  return RandomLowercaseString(rng, length);
}

static std::string RandomLowercaseString(RandomEngine* rng, size_t length) {
  std::string result(length, '\0');
  std::uniform_int_distribution<int> chars('a', 'z');
  std::generate(result.begin(), result.end(),
                [&]() { return static_cast<char>(chars(*rng)); });
  return result;
}

static void DoNothing(absl::string_view /* data */, void* /* arg */) {}

static void DeleteExternalString(absl::string_view data, void* arg) {
  std::string* s = reinterpret_cast<std::string*>(arg);
  EXPECT_EQ(data, *s);
  delete s;
}

// Add "s" to *dst via `MakeCordFromExternal`
static void AddExternalMemory(absl::string_view s, absl::Cord* dst) {
  std::string* str = new std::string(s.data(), s.size());
  dst->Append(absl::MakeCordFromExternal(*str, [str](absl::string_view data) {
    DeleteExternalString(data, str);
  }));
}

static void DumpGrowth() {
  absl::Cord str;
  for (int i = 0; i < 1000; i++) {
    char c = 'a' + i % 26;
    str.Append(absl::string_view(&c, 1));
  }
}

// Make a Cord with some number of fragments.  Return the size (in bytes)
// of the smallest fragment.
static size_t AppendWithFragments(const std::string& s, RandomEngine* rng,
                                  absl::Cord* cord) {
  size_t j = 0;
  const size_t max_size = s.size() / 5;  // Make approx. 10 fragments
  size_t min_size = max_size;            // size of smallest fragment
  while (j < s.size()) {
    size_t N = 1 + GetUniformRandomUpTo(rng, max_size);
    if (N > (s.size() - j)) {
      N = s.size() - j;
    }
    if (N < min_size) {
      min_size = N;
    }

    std::bernoulli_distribution coin_flip(0.5);
    if (coin_flip(*rng)) {
      // Grow by adding an external-memory.
      AddExternalMemory(absl::string_view(s.data() + j, N), cord);
    } else {
      cord->Append(absl::string_view(s.data() + j, N));
    }
    j += N;
  }
  return min_size;
}

// Add an external memory that contains the specified std::string to cord
static void AddNewStringBlock(const std::string& str, absl::Cord* dst) {
  char* data = new char[str.size()];
  memcpy(data, str.data(), str.size());
  dst->Append(absl::MakeCordFromExternal(
      absl::string_view(data, str.size()),
      [](absl::string_view s) { delete[] s.data(); }));
}

// Make a Cord out of many different types of nodes.
static absl::Cord MakeComposite() {
  absl::Cord cord;
  cord.Append("the");
  AddExternalMemory(" quick brown", &cord);
  AddExternalMemory(" fox jumped", &cord);

  absl::Cord full(" over");
  AddExternalMemory(" the lazy", &full);
  AddNewStringBlock(" dog slept the whole day away", &full);
  absl::Cord substring = full.Subcord(0, 18);

  // Make substring long enough to defeat the copying fast path in Append.
  substring.Append(std::string(1000, '.'));
  cord.Append(substring);
  cord = cord.Subcord(0, cord.size() - 998);  // Remove most of extra junk

  return cord;
}

namespace absl {
ABSL_NAMESPACE_BEGIN

class CordTestPeer {
 public:
  static void ForEachChunk(
      const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) {
    c.ForEachChunk(callback);
  }
};

ABSL_NAMESPACE_END
}  // namespace absl

TEST(Cord, AllFlatSizes) {
  using absl::strings_internal::CordTestAccess;

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

    absl::Cord dst(src);
    EXPECT_EQ(std::string(dst), src) << s;
  }
}

// We create a Cord at least 128GB in size using the fact that Cords can
// internally reference-count; thus the Cord is enormous without actually
// consuming very much memory.
TEST(GigabyteCord, FromExternal) {
  const size_t one_gig = 1024U * 1024U * 1024U;
  size_t max_size = 2 * one_gig;
  if (sizeof(max_size) > 4) max_size = 128 * one_gig;

  size_t length = 128 * 1024;
  char* data = new char[length];
  absl::Cord from = absl::MakeCordFromExternal(
      absl::string_view(data, length),
      [](absl::string_view sv) { delete[] sv.data(); });

  // This loop may seem odd due to its combination of exponential doubling of
  // size and incremental size increases.  We do it incrementally to be sure the
  // Cord will need rebalancing and will exercise code that, in the past, has
  // caused crashes in production.  We grow exponentially so that the code will
  // execute in a reasonable amount of time.
  absl::Cord c;
  ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
  c.Append(from);
  while (c.size() < max_size) {
    c.Append(c);
    c.Append(from);
    c.Append(from);
    c.Append(from);
    c.Append(from);
  }

  for (int i = 0; i < 1024; ++i) {
    c.Append(from);
  }
  ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
  // Note: on a 32-bit build, this comes out to   2,818,048,000 bytes.
  // Note: on a 64-bit build, this comes out to 171,932,385,280 bytes.
}

static absl::Cord MakeExternalCord(int size) {
  char* buffer = new char[size];
  memset(buffer, 'x', size);
  absl::Cord cord;
  cord.Append(absl::MakeCordFromExternal(
      absl::string_view(buffer, size),
      [](absl::string_view s) { delete[] s.data(); }));
  return cord;
}

// Extern to fool clang that this is not constant. Needed to suppress
// a warning of unsafe code we want to test.
extern bool my_unique_true_boolean;
bool my_unique_true_boolean = true;

TEST(Cord, Assignment) {
  absl::Cord x(absl::string_view("hi there"));
  absl::Cord y(x);
  ASSERT_EQ(std::string(x), "hi there");
  ASSERT_EQ(std::string(y), "hi there");
  ASSERT_TRUE(x == y);
  ASSERT_TRUE(x <= y);
  ASSERT_TRUE(y <= x);

  x = absl::string_view("foo");
  ASSERT_EQ(std::string(x), "foo");
  ASSERT_EQ(std::string(y), "hi there");
  ASSERT_TRUE(x < y);
  ASSERT_TRUE(y > x);
  ASSERT_TRUE(x != y);
  ASSERT_TRUE(x <= y);
  ASSERT_TRUE(y >= x);

  x = "foo";
  ASSERT_EQ(x, "foo");

  // Test that going from inline rep to tree we don't leak memory.
  std::vector<std::pair<absl::string_view, absl::string_view>>
      test_string_pairs = {{"hi there", "foo"},
                           {"loooooong coooooord", "short cord"},
                           {"short cord", "loooooong coooooord"},
                           {"loooooong coooooord1", "loooooong coooooord2"}};
  for (std::pair<absl::string_view, absl::string_view> test_strings :
       test_string_pairs) {
    absl::Cord tmp(test_strings.first);
    absl::Cord z(std::move(tmp));
    ASSERT_EQ(std::string(z), test_strings.first);
    tmp = test_strings.second;
    z = std::move(tmp);
    ASSERT_EQ(std::string(z), test_strings.second);
  }
  {
    // Test that self-move assignment doesn't crash/leak.
    // Do not write such code!
    absl::Cord my_small_cord("foo");
    absl::Cord my_big_cord("loooooong coooooord");
    // Bypass clang's warning on self move-assignment.
    absl::Cord* my_small_alias =
        my_unique_true_boolean ? &my_small_cord : &my_big_cord;
    absl::Cord* my_big_alias =
        !my_unique_true_boolean ? &my_small_cord : &my_big_cord;

    *my_small_alias = std::move(my_small_cord);
    *my_big_alias = std::move(my_big_cord);
    // my_small_cord and my_big_cord are in an unspecified but valid
    // state, and will be correctly destroyed here.
  }
}

TEST(Cord, StartsEndsWith) {
  absl::Cord x(absl::string_view("abcde"));
  absl::Cord empty("");

  ASSERT_TRUE(x.StartsWith(absl::Cord("abcde")));
  ASSERT_TRUE(x.StartsWith(absl::Cord("abc")));
  ASSERT_TRUE(x.StartsWith(absl::Cord("")));
  ASSERT_TRUE(empty.StartsWith(absl::Cord("")));
  ASSERT_TRUE(x.EndsWith(absl::Cord("abcde")));
  ASSERT_TRUE(x.EndsWith(absl::Cord("cde")));
  ASSERT_TRUE(x.EndsWith(absl::Cord("")));
  ASSERT_TRUE(empty.EndsWith(absl::Cord("")));

  ASSERT_TRUE(!x.StartsWith(absl::Cord("xyz")));
  ASSERT_TRUE(!empty.StartsWith(absl::Cord("xyz")));
  ASSERT_TRUE(!x.EndsWith(absl::Cord("xyz")));
  ASSERT_TRUE(!empty.EndsWith(absl::Cord("xyz")));

  ASSERT_TRUE(x.StartsWith("abcde"));
  ASSERT_TRUE(x.StartsWith("abc"));
  ASSERT_TRUE(x.StartsWith(""));
  ASSERT_TRUE(empty.StartsWith(""));
  ASSERT_TRUE(x.EndsWith("abcde"));
  ASSERT_TRUE(x.EndsWith("cde"));
  ASSERT_TRUE(x.EndsWith(""));
  ASSERT_TRUE(empty.EndsWith(""));

  ASSERT_TRUE(!x.StartsWith("xyz"));
  ASSERT_TRUE(!empty.StartsWith("xyz"));
  ASSERT_TRUE(!x.EndsWith("xyz"));
  ASSERT_TRUE(!empty.EndsWith("xyz"));
}

TEST(Cord, Subcord) {
  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  const std::string s = RandomLowercaseString(&rng, 1024);

  absl::Cord a;
  AppendWithFragments(s, &rng, &a);
  ASSERT_EQ(s.size(), a.size());

  // Check subcords of a, from a variety of interesting points.
  std::set<size_t> positions;
  for (int i = 0; i <= 32; ++i) {
    positions.insert(i);
    positions.insert(i * 32 - 1);
    positions.insert(i * 32);
    positions.insert(i * 32 + 1);
    positions.insert(a.size() - i);
  }
  positions.insert(237);
  positions.insert(732);
  for (size_t pos : positions) {
    if (pos > a.size()) continue;
    for (size_t end_pos : positions) {
      if (end_pos < pos || end_pos > a.size()) continue;
      absl::Cord sa = a.Subcord(pos, end_pos - pos);
      EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
                std::string(sa))
          << a;
    }
  }

  // Do the same thing for an inline cord.
  const std::string sh = "short";
  absl::Cord c(sh);
  for (size_t pos = 0; pos <= sh.size(); ++pos) {
    for (size_t n = 0; n <= sh.size() - pos; ++n) {
      absl::Cord sc = c.Subcord(pos, n);
      EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c;
    }
  }

  // Check subcords of subcords.
  absl::Cord sa = a.Subcord(0, a.size());
  std::string ss = s.substr(0, s.size());
  while (sa.size() > 1) {
    sa = sa.Subcord(1, sa.size() - 2);
    ss = ss.substr(1, ss.size() - 2);
    EXPECT_EQ(ss, std::string(sa)) << a;
    if (HasFailure()) break;  // halt cascade
  }

  // It is OK to ask for too much.
  sa = a.Subcord(0, a.size() + 1);
  EXPECT_EQ(s, std::string(sa));

  // It is OK to ask for something beyond the end.
  sa = a.Subcord(a.size() + 1, 0);
  EXPECT_TRUE(sa.empty());
  sa = a.Subcord(a.size() + 1, 1);
  EXPECT_TRUE(sa.empty());
}

TEST(Cord, Swap) {
  absl::string_view a("Dexter");
  absl::string_view b("Mandark");
  absl::Cord x(a);
  absl::Cord y(b);
  swap(x, y);
  ASSERT_EQ(x, absl::Cord(b));
  ASSERT_EQ(y, absl::Cord(a));
  x.swap(y);
  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(Cord, CopyToString) {
  VerifyCopyToString(absl::Cord());
  VerifyCopyToString(absl::Cord("small cord"));
  VerifyCopyToString(
      absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
                                "copying ", "to ", "a ", "string."}));
}

TEST(TryFlat, Empty) {
  absl::Cord c;
  EXPECT_EQ(c.TryFlat(), "");
}

TEST(TryFlat, Flat) {
  absl::Cord c("hello");
  EXPECT_EQ(c.TryFlat(), "hello");
}

TEST(TryFlat, SubstrInlined) {
  absl::Cord c("hello");
  c.RemovePrefix(1);
  EXPECT_EQ(c.TryFlat(), "ello");
}

TEST(TryFlat, SubstrFlat) {
  absl::Cord c("longer than 15 bytes");
  c.RemovePrefix(1);
  EXPECT_EQ(c.TryFlat(), "onger than 15 bytes");
}

TEST(TryFlat, Concat) {
  absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"});
  EXPECT_EQ(c.TryFlat(), absl::nullopt);
}

TEST(TryFlat, External) {
  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
  EXPECT_EQ(c.TryFlat(), "hell");
}

TEST(TryFlat, SubstrExternal) {
  absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {});
  c.RemovePrefix(1);
  EXPECT_EQ(c.TryFlat(), "ell");
}

TEST(TryFlat, SubstrConcat) {
  absl::Cord c = absl::MakeFragmentedCord({"hello", " world"});
  c.RemovePrefix(1);
  EXPECT_EQ(c.TryFlat(), absl::nullopt);
}

static bool IsFlat(const absl::Cord& c) {
  return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
}

static void VerifyFlatten(absl::Cord c) {
  std::string old_contents(c);
  absl::string_view old_flat;
  bool already_flat_and_non_empty = IsFlat(c) && !c.empty();
  if (already_flat_and_non_empty) {
    old_flat = *c.chunk_begin();
  }
  absl::string_view new_flat = c.Flatten();

  // Verify that the contents of the flattened Cord are correct.
  EXPECT_EQ(new_flat, old_contents);
  EXPECT_EQ(std::string(c), old_contents);

  // If the Cord contained data and was already flat, verify that the data
  // wasn't copied.
  if (already_flat_and_non_empty) {
    EXPECT_EQ(old_flat.data(), new_flat.data())
        << "Allocated new memory even though the Cord was already flat.";
  }

  // Verify that the flattened Cord is in fact flat.
  EXPECT_TRUE(IsFlat(c));
}

TEST(Cord, Flatten) {
  VerifyFlatten(absl::Cord());
  VerifyFlatten(absl::Cord("small cord"));
  VerifyFlatten(absl::Cord("larger than small buffer optimization"));
  VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));

  // Test with a cord that is longer than the largest flat buffer
  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
}

// Test data
namespace {
class TestData {
 private:
  std::vector<std::string> data_;

  // Return a std::string of the specified length.
  static std::string MakeString(int length) {
    std::string result;
    char buf[30];
    snprintf(buf, sizeof(buf), "(%d)", length);
    while (result.size() < length) {
      result += buf;
    }
    result.resize(length);
    return result;
  }

 public:
  TestData() {
    // short strings increasing in length by one
    for (int i = 0; i < 30; i++) {
      data_.push_back(MakeString(i));
    }

    // strings around half kMaxFlatLength
    static const int kMaxFlatLength = 4096 - 9;
    static const int kHalf = kMaxFlatLength / 2;

    for (int i = -10; i <= +10; i++) {
      data_.push_back(MakeString(kHalf + i));
    }

    for (int i = -10; i <= +10; i++) {
      data_.push_back(MakeString(kMaxFlatLength + i));
    }
  }

  size_t size() const { return data_.size(); }
  const std::string& data(size_t i) const { return data_[i]; }
};
}  // namespace

TEST(Cord, MultipleLengths) {
  TestData d;
  for (size_t i = 0; i < d.size(); i++) {
    std::string a = d.data(i);

    {  // Construct from Cord
      absl::Cord tmp(a);
      absl::Cord x(tmp);
      EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
    }

    {  // Construct from absl::string_view
      absl::Cord x(a);
      EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
    }

    {  // Append cord to self
      absl::Cord self(a);
      self.Append(self);
      EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
    }

    {  // Prepend cord to self
      absl::Cord self(a);
      self.Prepend(self);
      EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
    }

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

      {  // CopyFrom Cord
        absl::Cord x(a);
        absl::Cord y(b);
        x = y;
        EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
      }

      {  // CopyFrom absl::string_view
        absl::Cord x(a);
        x = b;
        EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
      }

      {  // Cord::Append(Cord)
        absl::Cord x(a);
        absl::Cord y(b);
        x.Append(y);
        EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
      }

      {  // Cord::Append(absl::string_view)
        absl::Cord x(a);
        x.Append(b);
        EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
      }

      {  // Cord::Prepend(Cord)
        absl::Cord x(a);
        absl::Cord y(b);
        x.Prepend(y);
        EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
      }

      {  // Cord::Prepend(absl::string_view)
        absl::Cord x(a);
        x.Prepend(b);
        EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
      }
    }
  }
}

namespace {

TEST(Cord, RemoveSuffixWithExternalOrSubstring) {
  absl::Cord cord = absl::MakeCordFromExternal(
      "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });

  EXPECT_EQ("foo bar baz", std::string(cord));

  // This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node.
  cord.RemoveSuffix(4);
  EXPECT_EQ("foo bar", std::string(cord));

  // This RemoveSuffix() will adjust the SUBSTRING node in-place.
  cord.RemoveSuffix(4);
  EXPECT_EQ("foo", std::string(cord));
}

TEST(Cord, RemoveSuffixMakesZeroLengthNode) {
  absl::Cord c;
  c.Append(absl::Cord(std::string(100, 'x')));
  absl::Cord other_ref = c;  // Prevent inplace appends
  c.Append(absl::Cord(std::string(200, 'y')));
  c.RemoveSuffix(200);
  EXPECT_EQ(std::string(100, 'x'), std::string(c));
}

}  // namespace

// CordSpliceTest contributed by hendrie.
namespace {

// Create a cord with an external memory block filled with 'z'
absl::Cord CordWithZedBlock(size_t size) {
  char* data = new char[size];
  if (size > 0) {
    memset(data, 'z', size);
  }
  absl::Cord cord = absl::MakeCordFromExternal(
      absl::string_view(data, size),
      [](absl::string_view s) { delete[] s.data(); });
  return cord;
}

// Establish that ZedBlock does what we think it does.
TEST(CordSpliceTest, ZedBlock) {
  absl::Cord blob = CordWithZedBlock(10);
  EXPECT_EQ(10, blob.size());
  std::string s;
  absl::CopyCordToString(blob, &s);
  EXPECT_EQ("zzzzzzzzzz", s);
}

TEST(CordSpliceTest, ZedBlock0) {
  absl::Cord blob = CordWithZedBlock(0);
  EXPECT_EQ(0, blob.size());
  std::string s;
  absl::CopyCordToString(blob, &s);
  EXPECT_EQ("", s);
}

TEST(CordSpliceTest, ZedBlockSuffix1) {
  absl::Cord blob = CordWithZedBlock(10);
  EXPECT_EQ(10, blob.size());
  absl::Cord suffix(blob);
  suffix.RemovePrefix(9);
  EXPECT_EQ(1, suffix.size());
  std::string s;
  absl::CopyCordToString(suffix, &s);
  EXPECT_EQ("z", s);
}

// Remove all of a prefix block
TEST(CordSpliceTest, ZedBlockSuffix0) {
  absl::Cord blob = CordWithZedBlock(10);
  EXPECT_EQ(10, blob.size());
  absl::Cord suffix(blob);
  suffix.RemovePrefix(10);
  EXPECT_EQ(0, suffix.size());
  std::string s;
  absl::CopyCordToString(suffix, &s);
  EXPECT_EQ("", s);
}

absl::Cord BigCord(size_t len, char v) {
  std::string s(len, v);
  return absl::Cord(s);
}

// Splice block into cord.
absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset,
                      const absl::Cord& block) {
  ABSL_RAW_CHECK(offset >= 0, "");
  ABSL_RAW_CHECK(offset + block.size() <= blob.size(), "");
  absl::Cord result(blob);
  result.RemoveSuffix(blob.size() - offset);
  result.Append(block);
  absl::Cord suffix(blob);
  suffix.RemovePrefix(offset + block.size());
  result.Append(suffix);
  ABSL_RAW_CHECK(blob.size() == result.size(), "");
  return result;
}

// Taking an empty suffix of a block breaks appending.
TEST(CordSpliceTest, RemoveEntireBlock1) {
  absl::Cord zero = CordWithZedBlock(10);
  absl::Cord suffix(zero);
  suffix.RemovePrefix(10);
  absl::Cord result;
  result.Append(suffix);
}

TEST(CordSpliceTest, RemoveEntireBlock2) {
  absl::Cord zero = CordWithZedBlock(10);
  absl::Cord prefix(zero);
  prefix.RemoveSuffix(10);
  absl::Cord suffix(zero);
  suffix.RemovePrefix(10);
  absl::Cord result(prefix);
  result.Append(suffix);
}

TEST(CordSpliceTest, RemoveEntireBlock3) {
  absl::Cord blob = CordWithZedBlock(10);
  absl::Cord block = BigCord(10, 'b');
  blob = SpliceCord(blob, 0, block);
}

struct CordCompareTestCase {
  template <typename LHS, typename RHS>
  CordCompareTestCase(const LHS& lhs, const RHS& rhs)
      : lhs_cord(lhs), rhs_cord(rhs) {}

  absl::Cord lhs_cord;
  absl::Cord rhs_cord;
};

const auto sign = [](int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); };

void VerifyComparison(const CordCompareTestCase& test_case) {
  std::string lhs_string(test_case.lhs_cord);
  std::string rhs_string(test_case.rhs_cord);
  int expected = sign(lhs_string.compare(rhs_string));
  EXPECT_EQ(expected, test_case.lhs_cord.Compare(test_case.rhs_cord))
      << "LHS=" << lhs_string << "; RHS=" << rhs_string;
  EXPECT_EQ(expected, test_case.lhs_cord.Compare(rhs_string))
      << "LHS=" << lhs_string << "; RHS=" << rhs_string;
  EXPECT_EQ(-expected, test_case.rhs_cord.Compare(test_case.lhs_cord))
      << "LHS=" << rhs_string << "; RHS=" << lhs_string;
  EXPECT_EQ(-expected, test_case.rhs_cord.Compare(lhs_string))
      << "LHS=" << rhs_string << "; RHS=" << lhs_string;
}

TEST(Cord, Compare) {
  absl::Cord subcord("aaaaaBBBBBcccccDDDDD");
  subcord = subcord.Subcord(3, 10);

  absl::Cord tmp("aaaaaaaaaaaaaaaa");
  tmp.Append("BBBBBBBBBBBBBBBB");
  absl::Cord concat = absl::Cord("cccccccccccccccc");
  concat.Append("DDDDDDDDDDDDDDDD");
  concat.Prepend(tmp);

  absl::Cord concat2("aaaaaaaaaaaaa");
  concat2.Append("aaaBBBBBBBBBBBBBBBBccccc");
  concat2.Append("cccccccccccDDDDDDDDDDDDDD");
  concat2.Append("DD");

  std::vector<CordCompareTestCase> test_cases = {{
      // Inline cords
      {"abcdef", "abcdef"},
      {"abcdef", "abcdee"},
      {"abcdef", "abcdeg"},
      {"bbcdef", "abcdef"},
      {"bbcdef", "abcdeg"},
      {"abcdefa", "abcdef"},
      {"abcdef", "abcdefa"},

      // Small flat cords
      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"},
      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"},
      {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"},
      {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"},
      {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"},
      {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"},

      // Subcords
      {subcord, subcord},
      {subcord, "aaBBBBBccc"},
      {subcord, "aaBBBBBccd"},
      {subcord, "aaBBBBBccb"},
      {subcord, "aaBBBBBxcb"},
      {subcord, "aaBBBBBccca"},
      {subcord, "aaBBBBBcc"},

      // Concats
      {concat, concat},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"},
      {concat,
       "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"},

      {concat, concat2},
  }};

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

TEST(Cord, CompareAfterAssign) {
  absl::Cord a("aaaaaa1111111");
  absl::Cord b("aaaaaa2222222");
  a = "cccccc";
  b = "cccccc";
  EXPECT_EQ(a, b);
  EXPECT_FALSE(a < b);

  a = "aaaa";
  b = "bbbbb";
  a = "";
  b = "";
  EXPECT_EQ(a, b);
  EXPECT_FALSE(a < b);
}

// Test CompareTo() and ComparePrefix() against string and substring
// comparison methods from basic_string.
static void TestCompare(const absl::Cord& c, const absl::Cord& d,
                        RandomEngine* rng) {
  typedef std::basic_string<uint8_t> ustring;
  ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size());
  ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size());
  // ustring comparison is ideal because we expect Cord comparisons to be
  // based on unsigned byte comparisons regardless of whether char is signed.
  int expected = sign(cs.compare(ds));
  EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
}

TEST(Compare, ComparisonIsUnsigned) {
  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255);
  char x = static_cast<char>(uniform_uint8(rng));
  TestCompare(
      absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x)),
      absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng);
}

TEST(Compare, RandomComparisons) {
  const int kIters = 5000;
  RandomEngine rng(testing::GTEST_FLAG(random_seed));

  int n = GetUniformRandomUpTo(&rng, 5000);
  absl::Cord a[] = {MakeExternalCord(n),
                    absl::Cord("ant"),
                    absl::Cord("elephant"),
                    absl::Cord("giraffe"),
                    absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100),
                                           GetUniformRandomUpTo(&rng, 100))),
                    absl::Cord(""),
                    absl::Cord("x"),
                    absl::Cord("A"),
                    absl::Cord("B"),
                    absl::Cord("C")};
  for (int i = 0; i < kIters; i++) {
    absl::Cord c, d;
    for (int j = 0; j < (i % 7) + 1; j++) {
      c.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
      d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
    }
    std::bernoulli_distribution coin_flip(0.5);
    TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)),
                coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng);
  }
}

template <typename T1, typename T2>
void CompareOperators() {
  const T1 a("a");
  const T2 b("b");

  EXPECT_TRUE(a == a);
  // For pointer type (i.e. `const char*`), operator== compares the address
  // instead of the string, so `a == const char*("a")` isn't necessarily true.
  EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a"));
  EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a"));
  EXPECT_FALSE(a == b);

  EXPECT_TRUE(a != b);
  EXPECT_FALSE(a != a);

  EXPECT_TRUE(a < b);
  EXPECT_FALSE(b < a);

  EXPECT_TRUE(b > a);
  EXPECT_FALSE(a > b);

  EXPECT_TRUE(a >= a);
  EXPECT_TRUE(b >= a);
  EXPECT_FALSE(a >= b);

  EXPECT_TRUE(a <= a);
  EXPECT_TRUE(a <= b);
  EXPECT_FALSE(b <= a);
}

TEST(ComparisonOperators, Cord_Cord) {
  CompareOperators<absl::Cord, absl::Cord>();
}

TEST(ComparisonOperators, Cord_StringPiece) {
  CompareOperators<absl::Cord, absl::string_view>();
}

TEST(ComparisonOperators, StringPiece_Cord) {
  CompareOperators<absl::string_view, absl::Cord>();
}

TEST(ComparisonOperators, Cord_string) {
  CompareOperators<absl::Cord, std::string>();
}

TEST(ComparisonOperators, string_Cord) {
  CompareOperators<std::string, absl::Cord>();
}

TEST(ComparisonOperators, stdstring_Cord) {
  CompareOperators<std::string, absl::Cord>();
}

TEST(ComparisonOperators, Cord_stdstring) {
  CompareOperators<absl::Cord, std::string>();
}

TEST(ComparisonOperators, charstar_Cord) {
  CompareOperators<const char*, absl::Cord>();
}

TEST(ComparisonOperators, Cord_charstar) {
  CompareOperators<absl::Cord, const char*>();
}

TEST(ConstructFromExternal, ReleaserInvoked) {
  // Empty external memory means the releaser should be called immediately.
  {
    bool invoked = false;
    auto releaser = [&invoked](absl::string_view) { invoked = true; };
    {
      auto c = absl::MakeCordFromExternal("", releaser);
      EXPECT_TRUE(invoked);
    }
  }

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

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

TEST(ConstructFromExternal, CompareContents) {
  RandomEngine rng(testing::GTEST_FLAG(random_seed));

  for (int length = 1; length <= 2048; length *= 2) {
    std::string data = RandomLowercaseString(&rng, length);
    auto* external = new std::string(data);
    auto cord =
        absl::MakeCordFromExternal(*external, [external](absl::string_view sv) {
          EXPECT_EQ(external->data(), sv.data());
          EXPECT_EQ(external->size(), sv.size());
          delete external;
        });
    EXPECT_EQ(data, cord);
  }
}

TEST(ConstructFromExternal, LargeReleaser) {
  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  constexpr size_t kLength = 256;
  std::string data = RandomLowercaseString(&rng, kLength);
  std::array<char, kLength> data_array;
  for (size_t i = 0; i < kLength; ++i) data_array[i] = data[i];
  bool invoked = false;
  auto releaser = [data_array, &invoked](absl::string_view data) {
    EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size()));
    invoked = true;
  };
  (void)absl::MakeCordFromExternal(data, releaser);
  EXPECT_TRUE(invoked);
}

TEST(ConstructFromExternal, FunctionPointerReleaser) {
  static absl::string_view data("hello world");
  static bool invoked;
  auto* releaser =
      static_cast<void (*)(absl::string_view)>([](absl::string_view sv) {
        EXPECT_EQ(data, sv);
        invoked = true;
      });
  invoked = false;
  (void)absl::MakeCordFromExternal(data, releaser);
  EXPECT_TRUE(invoked);

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

TEST(ConstructFromExternal, MoveOnlyReleaser) {
  struct Releaser {
    explicit Releaser(bool* invoked) : invoked(invoked) {}
    Releaser(Releaser&& other) noexcept : invoked(other.invoked) {}
    void operator()(absl::string_view) const { *invoked = true; }

    bool* invoked;
  };

  bool invoked = false;
  (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked));
  EXPECT_TRUE(invoked);
}

TEST(ConstructFromExternal, NoArgLambda) {
  bool invoked = false;
  (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; });
  EXPECT_TRUE(invoked);
}

TEST(ConstructFromExternal, StringViewArgLambda) {
  bool invoked = false;
  (void)absl::MakeCordFromExternal(
      "dummy", [&invoked](absl::string_view) { invoked = true; });
  EXPECT_TRUE(invoked);
}

TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
  struct Releaser {
    explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
    ~Releaser() { *destroyed = true; }
    void operator()(absl::string_view) const {}

    bool* destroyed;
  };

  bool destroyed = false;
  Releaser releaser(&destroyed);
  (void)absl::MakeCordFromExternal("dummy", releaser);
  EXPECT_TRUE(destroyed);
}

TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
  struct Releaser {
    void operator()(absl::string_view) & { *lvalue_invoked = true; }
    void operator()(absl::string_view) && { *rvalue_invoked = true; }

    bool* lvalue_invoked;
    bool* rvalue_invoked;
  };

  bool lvalue_invoked = false;
  bool rvalue_invoked = false;
  Releaser releaser = {&lvalue_invoked, &rvalue_invoked};
  (void)absl::MakeCordFromExternal("", releaser);
  EXPECT_FALSE(lvalue_invoked);
  EXPECT_TRUE(rvalue_invoked);
  rvalue_invoked = false;

  (void)absl::MakeCordFromExternal("dummy", releaser);
  EXPECT_FALSE(lvalue_invoked);
  EXPECT_TRUE(rvalue_invoked);
  rvalue_invoked = false;

  // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type.
  (void)absl::MakeCordFromExternal("dummy", std::move(releaser));
  EXPECT_FALSE(lvalue_invoked);
  EXPECT_TRUE(rvalue_invoked);
}

TEST(ExternalMemory, BasicUsage) {
  static const char* strings[] = {"", "hello", "there"};
  for (const char* str : strings) {
    absl::Cord dst("(prefix)");
    AddExternalMemory(str, &dst);
    dst.Append("(suffix)");
    EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")),
              std::string(dst));
  }
}

TEST(ExternalMemory, RemovePrefixSuffix) {
  // Exhaustively try all sub-strings.
  absl::Cord cord = MakeComposite();
  std::string s = std::string(cord);
  for (int offset = 0; offset <= s.size(); offset++) {
    for (int length = 0; length <= s.size() - offset; length++) {
      absl::Cord result(cord);
      result.RemovePrefix(offset);
      result.RemoveSuffix(result.size() - length);
      EXPECT_EQ(s.substr(offset, length), std::string(result))
          << offset << " " << length;
    }
  }
}

TEST(ExternalMemory, Get) {
  absl::Cord cord("hello");
  AddExternalMemory(" world!", &cord);
  AddExternalMemory(" how are ", &cord);
  cord.Append(" you?");
  std::string s = std::string(cord);
  for (int i = 0; i < s.size(); i++) {
    EXPECT_EQ(s[i], cord[i]);
  }
}

// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage()
// These tests take into account that the reported memory usage is approximate
// and non-deterministic. For all tests, We verify that the reported memory
// usage is larger than `size()`, and less than `size() * 1.5` as a cord should
// never reserve more 'extra' capacity than half of its size as it grows.
// Additionally we have some whiteboxed expectations based on our knowledge of
// the layout and size of empty and inlined cords, and flat nodes.

TEST(CordMemoryUsage, Empty) {
  EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage());
}

TEST(CordMemoryUsage, Embedded) {
  absl::Cord a("hello");
  EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
}

TEST(CordMemoryUsage, EmbeddedAppend) {
  absl::Cord a("a");
  absl::Cord b("bcd");
  EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord));
  a.Append(b);
  EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
}

TEST(CordMemoryUsage, ExternalMemory) {
  static const int kLength = 1000;
  absl::Cord cord;
  AddExternalMemory(std::string(kLength, 'x'), &cord);
  EXPECT_GT(cord.EstimatedMemoryUsage(), kLength);
  EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5);
}

TEST(CordMemoryUsage, Flat) {
  static const int kLength = 125;
  absl::Cord a(std::string(kLength, 'a'));
  EXPECT_GT(a.EstimatedMemoryUsage(), kLength);
  EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5);
}

TEST(CordMemoryUsage, AppendFlat) {
  using absl::strings_internal::CordTestAccess;
  absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
  size_t length = a.EstimatedMemoryUsage();
  a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b'));
  size_t delta = a.EstimatedMemoryUsage() - length;
  EXPECT_GT(delta, CordTestAccess::MaxFlatLength());
  EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5);
}

// Regtest for a change that had to be rolled back because it expanded out
// of the InlineRep too soon, which was observable through MemoryUsage().
TEST(CordMemoryUsage, InlineRep) {
  constexpr size_t kMaxInline = 15;  // Cord::InlineRep::N
  const std::string small_string(kMaxInline, 'x');
  absl::Cord c1(small_string);

  absl::Cord c2;
  c2.Append(small_string);
  EXPECT_EQ(c1, c2);
  EXPECT_EQ(c1.EstimatedMemoryUsage(), c2.EstimatedMemoryUsage());
}

}  // namespace

// Regtest for 7510292 (fix a bug introduced by 7465150)
TEST(Cord, Concat_Append) {
  // Create a rep of type CONCAT
  absl::Cord s1("foobarbarbarbarbar");
  s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
  size_t size = s1.size();

  // Create a copy of s1 and append to it.
  absl::Cord s2 = s1;
  s2.Append("x");

  // 7465150 modifies s1 when it shouldn't.
  EXPECT_EQ(s1.size(), size);
  EXPECT_EQ(s2.size(), size + 1);
}

TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
  absl::Cord fragmented =
      absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});

  EXPECT_EQ("A fragmented Cord", fragmented);

  auto chunk_it = fragmented.chunk_begin();

  ASSERT_TRUE(chunk_it != fragmented.chunk_end());
  EXPECT_EQ("A ", *chunk_it);

  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
  EXPECT_EQ("fragmented ", *chunk_it);

  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
  EXPECT_EQ("Cord", *chunk_it);

  ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
}

TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) {
  std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
  absl::Cord fragmented = absl::MakeFragmentedCord(chunks);

  EXPECT_EQ("A fragmented Cord", fragmented);

  auto chunk_it = fragmented.chunk_begin();

  ASSERT_TRUE(chunk_it != fragmented.chunk_end());
  EXPECT_EQ("A ", *chunk_it);

  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
  EXPECT_EQ("fragmented ", *chunk_it);

  ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
  EXPECT_EQ("Cord", *chunk_it);

  ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
}

TEST(CordChunkIterator, Traits) {
  static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value,
                "");
  static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, "");

  // Move semantics to satisfy swappable via std::swap
  static_assert(std::is_move_constructible<absl::Cord::ChunkIterator>::value,
                "");
  static_assert(std::is_move_assignable<absl::Cord::ChunkIterator>::value, "");

  static_assert(
      std::is_same<
          std::iterator_traits<absl::Cord::ChunkIterator>::iterator_category,
          std::input_iterator_tag>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::value_type,
                   absl::string_view>::value,
      "");
  static_assert(
      std::is_same<
          std::iterator_traits<absl::Cord::ChunkIterator>::difference_type,
          ptrdiff_t>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::pointer,
                   const absl::string_view*>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::reference,
                   absl::string_view>::value,
      "");
}

static void VerifyChunkIterator(const absl::Cord& cord,
                                size_t expected_chunks) {
  EXPECT_EQ(cord.chunk_begin() == cord.chunk_end(), cord.empty()) << cord;
  EXPECT_EQ(cord.chunk_begin() != cord.chunk_end(), !cord.empty());

  absl::Cord::ChunkRange range = cord.Chunks();
  EXPECT_EQ(range.begin() == range.end(), cord.empty());
  EXPECT_EQ(range.begin() != range.end(), !cord.empty());

  std::string content(cord);
  size_t pos = 0;
  auto pre_iter = cord.chunk_begin(), post_iter = cord.chunk_begin();
  size_t n_chunks = 0;
  while (pre_iter != cord.chunk_end() && post_iter != cord.chunk_end()) {
    EXPECT_FALSE(pre_iter == cord.chunk_end());   // NOLINT: explicitly test ==
    EXPECT_FALSE(post_iter == cord.chunk_end());  // NOLINT

    EXPECT_EQ(pre_iter, post_iter);
    EXPECT_EQ(*pre_iter, *post_iter);

    EXPECT_EQ(pre_iter->data(), (*pre_iter).data());
    EXPECT_EQ(pre_iter->size(), (*pre_iter).size());

    absl::string_view chunk = *pre_iter;
    EXPECT_FALSE(chunk.empty());
    EXPECT_LE(pos + chunk.size(), content.size());
    EXPECT_EQ(absl::string_view(content.c_str() + pos, chunk.size()), chunk);

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

    ++pre_iter;
    EXPECT_EQ(*post_iter++, chunk);

    pos += chunk.size();
    ++n_chunks;
  }
  EXPECT_EQ(expected_chunks, n_chunks);
  EXPECT_EQ(pos, content.size());
  EXPECT_TRUE(pre_iter == cord.chunk_end());   // NOLINT: explicitly test ==
  EXPECT_TRUE(post_iter == cord.chunk_end());  // NOLINT
}

TEST(CordChunkIterator, Operations) {
  absl::Cord empty_cord;
  VerifyChunkIterator(empty_cord, 0);

  absl::Cord small_buffer_cord("small cord");
  VerifyChunkIterator(small_buffer_cord, 1);

  absl::Cord flat_node_cord("larger than small buffer optimization");
  VerifyChunkIterator(flat_node_cord, 1);

  VerifyChunkIterator(
      absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
                                "testing ", "chunk ", "iterations."}),
      8);

  absl::Cord reused_nodes_cord(std::string(40, 'c'));
  reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b')));
  reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a')));
  size_t expected_chunks = 3;
  for (int i = 0; i < 8; ++i) {
    reused_nodes_cord.Prepend(reused_nodes_cord);
    expected_chunks *= 2;
    VerifyChunkIterator(reused_nodes_cord, expected_chunks);
  }

  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
  absl::Cord subcords;
  for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128));
  VerifyChunkIterator(subcords, 128);
}

TEST(CordCharIterator, Traits) {
  static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
                "");
  static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, "");

  // Move semantics to satisfy swappable via std::swap
  static_assert(std::is_move_constructible<absl::Cord::CharIterator>::value,
                "");
  static_assert(std::is_move_assignable<absl::Cord::CharIterator>::value, "");

  static_assert(
      std::is_same<
          std::iterator_traits<absl::Cord::CharIterator>::iterator_category,
          std::input_iterator_tag>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::value_type,
                   char>::value,
      "");
  static_assert(
      std::is_same<
          std::iterator_traits<absl::Cord::CharIterator>::difference_type,
          ptrdiff_t>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::pointer,
                   const char*>::value,
      "");
  static_assert(
      std::is_same<std::iterator_traits<absl::Cord::CharIterator>::reference,
                   const char&>::value,
      "");
}

static void VerifyCharIterator(const absl::Cord& cord) {
  EXPECT_EQ(cord.char_begin() == cord.char_end(), cord.empty());
  EXPECT_EQ(cord.char_begin() != cord.char_end(), !cord.empty());

  absl::Cord::CharRange range = cord.Chars();
  EXPECT_EQ(range.begin() == range.end(), cord.empty());
  EXPECT_EQ(range.begin() != range.end(), !cord.empty());

  size_t i = 0;
  absl::Cord::CharIterator pre_iter = cord.char_begin();
  absl::Cord::CharIterator post_iter = cord.char_begin();
  std::string content(cord);
  while (pre_iter != cord.char_end() && post_iter != cord.char_end()) {
    EXPECT_FALSE(pre_iter == cord.char_end());   // NOLINT: explicitly test ==
    EXPECT_FALSE(post_iter == cord.char_end());  // NOLINT

    EXPECT_LT(i, cord.size());
    EXPECT_EQ(content[i], *pre_iter);

    EXPECT_EQ(pre_iter, post_iter);
    EXPECT_EQ(*pre_iter, *post_iter);
    EXPECT_EQ(&*pre_iter, &*post_iter);

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

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

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

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

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

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

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

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

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

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

TEST(CordCharIterator, Operations) {
  absl::Cord empty_cord;
  VerifyCharIterator(empty_cord);

  absl::Cord small_buffer_cord("small cord");
  VerifyCharIterator(small_buffer_cord);

  absl::Cord flat_node_cord("larger than small buffer optimization");
  VerifyCharIterator(flat_node_cord);

  VerifyCharIterator(
      absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
                                "testing ", "character ", "iteration."}));

  absl::Cord reused_nodes_cord("ghi");
  reused_nodes_cord.Prepend(absl::Cord("def"));
  reused_nodes_cord.Prepend(absl::Cord("abc"));
  for (int i = 0; i < 4; ++i) {
    reused_nodes_cord.Prepend(reused_nodes_cord);
    VerifyCharIterator(reused_nodes_cord);
  }

  RandomEngine rng(testing::GTEST_FLAG(random_seed));
  absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
  absl::Cord subcords;
  for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128));
  VerifyCharIterator(subcords);
}

TEST(Cord, StreamingOutput) {
  absl::Cord c =
      absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
  std::stringstream output;
  output << c;
  EXPECT_EQ("A small fragmented Cord.", output.str());
}

TEST(Cord, ForEachChunk) {
  for (int num_elements : {1, 10, 200}) {
    SCOPED_TRACE(num_elements);
    std::vector<std::string> cord_chunks;
    for (int i = 0; i < num_elements; ++i) {
      cord_chunks.push_back(absl::StrCat("[", i, "]"));
    }
    absl::Cord c = absl::MakeFragmentedCord(cord_chunks);

    std::vector<std::string> iterated_chunks;
    absl::CordTestPeer::ForEachChunk(c,
                                     [&iterated_chunks](absl::string_view sv) {
                                       iterated_chunks.emplace_back(sv);
                                     });
    EXPECT_EQ(iterated_chunks, cord_chunks);
  }
}

TEST(Cord, SmallBufferAssignFromOwnData) {
  constexpr size_t kMaxInline = 15;
  std::string contents = "small buff cord";
  EXPECT_EQ(contents.size(), kMaxInline);
  for (size_t pos = 0; pos < contents.size(); ++pos) {
    for (size_t count = contents.size() - pos; count > 0; --count) {
      absl::Cord c(contents);
      absl::string_view flat = c.Flatten();
      c = flat.substr(pos, count);
      EXPECT_EQ(c, contents.substr(pos, count))
          << "pos = " << pos << "; count = " << count;
    }
  }
}

TEST(Cord, Format) {
  absl::Cord c;
  absl::Format(&c, "There were %04d little %s.", 3, "pigs");
  EXPECT_EQ(c, "There were 0003 little pigs.");
  absl::Format(&c, "And %-3llx bad wolf!", 1);
  EXPECT_EQ(c, "There were 0003 little pigs.And 1   bad wolf!");
}

TEST(CordDeathTest, Hardening) {
  absl::Cord cord("hello");
  // 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(), "");
}