36#include <gtest/gtest.h>
61#if defined(__SANITIZE_THREAD__)
62# define ALEPH_ROPE_TEST_UNDER_TSAN 1
63#elif defined(__has_feature)
64# if __has_feature(thread_sanitizer)
65# define ALEPH_ROPE_TEST_UNDER_TSAN 1
68#ifndef ALEPH_ROPE_TEST_UNDER_TSAN
69# define ALEPH_ROPE_TEST_UNDER_TSAN 0
78#if !ALEPH_ROPE_TEST_UNDER_TSAN
82 while (remaining >= 0)
87 remaining, remaining - 1, std::memory_order_relaxed))
96 throw std::bad_alloc();
101 void * ptr = std::malloc(
size);
103 throw std::bad_alloc();
129 class AllocationFailureScope
137 std::memory_order_relaxed);
153#if !ALEPH_ROPE_TEST_UNDER_TSAN
155void *
operator new(std::size_t
size)
160void *
operator new[](std::size_t
size)
165void *
operator new(std::size_t
size,
const std::nothrow_t &)
noexcept
177void *
operator new[](std::size_t
size,
const std::nothrow_t &)
noexcept
189void operator delete(
void * ptr)
noexcept
194void operator delete[](
void * ptr)
noexcept
199void operator delete(
void * ptr, std::size_t)
noexcept
204void operator delete[](
void * ptr, std::size_t)
noexcept
209void operator delete(
void * ptr,
const std::nothrow_t &)
noexcept
214void operator delete[](
void * ptr,
const std::nothrow_t &)
noexcept
221using namespace Aleph;
297 EXPECT_EQ(a.concat(empty).to_string(),
"abc");
305 EXPECT_EQ(
r.substr(0, 5).to_string(),
"hello");
306 EXPECT_EQ(
r.substr(6, 5).to_string(),
"world");
307 EXPECT_EQ(
r.substr(0, 11).to_string(),
"hello world");
327 const std::string_view text =
"the quick brown fox jumps over the lazy dog";
363 Rope<char> r{std::string_view(
"hello brave new world")};
367 EXPECT_EQ(
r.to_string(),
"hello brave new world");
385#if ALEPH_ROPE_TEST_UNDER_TSAN
386 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
387 "new/delete, which conflicts with TSan's own at link time.";
426 const char expected[] = {
'a',
'b',
'c',
'd',
'e',
'f'};
427 for (
size_t i = 0; i < 6; ++i)
454 .concat(
TinyRope{std::string_view(
"cde")})
465 .concat(
TinyRope{std::string_view(
"cde")})
496 TinyRope r{std::string_view(
"the quick brown fox jumps over the lazy dog")};
498 EXPECT_EQ(
r.to_string(),
"the quick brown fox jumps over the lazy dog");
501 auto sub =
r.substr(4, 5);
505 auto ins =
r.insert(10,
TinyRope{std::string_view(
"very ")});
507 "the quick very brown fox jumps over the lazy dog");
510 auto er =
r.erase(4, 6);
511 EXPECT_EQ(
er.to_string(),
"the brown fox jumps over the lazy dog");
523 for (
int i = 0; i < 500; ++i)
525 const char c =
static_cast<char>(
'a' + (i % 26));
526 r =
r.concat(
TinyRope{std::string_view(&c, 1)});
533 for (
size_t i = 0; i <
expected.size(); ++i)
545 for (
int i = 0; i < 500; ++i)
547 const char c =
static_cast<char>(
'a' + (i % 26));
548 r =
TinyRope{std::string_view(&c, 1)}.concat(
r);
555 for (
size_t i = 0; i <
expected.size(); ++i)
564 TinyRope r{std::string_view(
"abcdefghijkl")};
565 EXPECT_EQ(
r.substr(0, 4).to_string(),
"abcd");
566 EXPECT_EQ(
r.substr(4, 4).to_string(),
"efgh");
567 EXPECT_EQ(
r.substr(2, 4).to_string(),
"cdef");
568 EXPECT_EQ(
r.substr(0, 12).to_string(),
"abcdefghijkl");
576 std::mt19937
rng(0xC0FFEEu);
577 std::uniform_int_distribution<int>
op_dist(0, 3);
578 std::uniform_int_distribution<int>
char_dist(
'a',
'z');
583 std::string reference;
588 for (
int i = 0; i < len; ++i)
593 std::uniform_int_distribution<int>
len_dist(1, 6);
597 const int op = reference.empty() ? 0 :
op_dist(
rng);
609 std::uniform_int_distribution<size_t>
pos_dist(0, reference.size());
613 reference.insert(pos, chunk);
618 std::uniform_int_distribution<size_t>
pos_dist(0, reference.size());
620 std::uniform_int_distribution<size_t>
len_d(0, reference.size() - pos);
623 reference.erase(pos, len);
629 std::uniform_int_distribution<size_t>
pos_dist(0, reference.size());
631 std::uniform_int_distribution<size_t>
len_d(0, reference.size() - pos);
634 <<
"substr disagreement at iter " <<
iter;
652 if (
not std::getenv(
"ENABLE_PERF_TESTS"))
653 GTEST_SKIP() <<
"Skipping Rope performance regression (set "
654 "ENABLE_PERF_TESTS=1 to enable)";
668 constexpr int N = 20000;
670 const auto start = std::chrono::steady_clock::now();
672 for (
int i = 0; i <
N; ++i)
674 const char c =
static_cast<char>(
'a' + (i % 26));
677 const auto elapsed = std::chrono::steady_clock::now() - start;
690 if (
const char *
env_ms = std::getenv(
"ROPE_ABSORPTION_MAX_MS"))
691 max_ms = std::atol(
env_ms);
692 EXPECT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).
count(), max_ms)
693 <<
N <<
" single-character concats took too long -- the leaf-absorption "
694 "fast path may have regressed (see tpl_rope.H's \"Leaf absorption\" note).";
703 if (
not std::getenv(
"ENABLE_PERF_TESTS"))
704 GTEST_SKIP() <<
"Skipping Rope performance regression (set "
705 "ENABLE_PERF_TESTS=1 to enable)";
718 const std::string
big_text(200000,
'x');
722 constexpr int N = 5000;
723 const auto start = std::chrono::steady_clock::now();
724 for (
int i = 0; i <
N; ++i)
730 const auto elapsed = std::chrono::steady_clock::now() - start;
735 if (
const char *
env_ms = std::getenv(
"ROPE_SHARING_MAX_MS"))
736 max_ms = std::atol(
env_ms);
737 EXPECT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).
count(), max_ms)
738 <<
N <<
" copy+small-concat derivations from a " <<
big_text.size()
739 <<
"-character rope took too long -- copy() or concat() may no longer be "
740 "O(1)/structurally sharing (see tpl_rope.H's \"Structure\" note).";
747#if ALEPH_ROPE_TEST_UNDER_TSAN
748 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
749 "new/delete, which conflicts with TSan's own at link time.";
756 TinyRope{std::string_view(
"abcd")}.concat(
TinyRope{std::string_view(
"ef")});
757 const TinyRope right{std::string_view(
"g")};
781 catch (
const std::bad_alloc &)
818 if (
not std::getenv(
"ENABLE_PERF_TESTS"))
819 GTEST_SKIP() <<
"Skipping Rope performance regression (set "
820 "ENABLE_PERF_TESTS=1 to enable)";
831 for (
const int total : {100000, 1000000, 5000000})
833 std::string text(
static_cast<size_t>(
total),
'x');
834 std::mt19937
rng(42);
835 for (
auto & c : text)
836 c =
static_cast<char>(
'a' + (
rng() % 26));
837 R
r{std::string_view(text)};
840 const size_t pos =
static_cast<size_t>(
total) / 2;
841 const auto start = std::chrono::steady_clock::now();
842 for (
int i = 0; i < 200; ++i)
848 const double ms = std::chrono::duration<double, std::milli>(
849 std::chrono::steady_clock::now() - start)
855 if (
const char *
env_ms = std::getenv(
"ROPE_SLICE_MAX_MS"))
856 max_ms = std::atol(
env_ms);
859 <<
"-character rope took too long -- see tpl_rope.H substr()'s "
860 "@note on the straddle-rebalance worst case.";
878 size_t iterations = 0;
881 for (; iterations < 100; ++iterations)
884 std::overflow_error);
static string random_string(std::mt19937 &rng, size_t len)
Immutable, structurally-shared rope over a sequence of Char.
Rope erase(const size_t pos, const size_t len) const
Return a new rope with [pos, pos+len) removed.
Rope insert(const size_t pos, const Rope &other) const
Return a new rope with other inserted at pos.
Array< Char > flatten() const
Return every character of this rope as an independent Array.
Rope concat(const Rope &other) const
Return a new rope that is *this followed by other.
std::basic_string< Char > to_string() const
Return every character of this rope as a std::basic_string.
size_t size() const noexcept
Return the number of characters in this rope.
bool verify() const noexcept
Check this rope's internal structural invariants.
Char at(const size_t pos) const
Return the character at pos.
Minimal std::expected-style result type for C++20.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Main namespace for Aleph-w library functions.
size_t size(Node *root) noexcept
std::string concat(const Args &...args)
Concatenate multiple arguments into a single std::string.
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Immutable, structurally-shared rope (Aleph::Rope) for large character sequences.