The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

#pragma once
#include "test.h"
#include <panda/string.h>
#include <panda/string_view.h>
namespace test {
template <typename T>
struct test_string {
using Allocator = typename test::Allocator<T>;
using String = panda::basic_string<T, std::char_traits<T>, Allocator>;
using String2 = panda::basic_string<T, std::char_traits<T>, test::Allocator<T,1>>;
using StdString = std::basic_string<T>;
using ExternalShared = typename String::ExternalShared;
template <class A> using AnyString = panda::basic_string<T, std::char_traits<T>, A>;
static constexpr const size_t MAX_SSO_CHARS = String::MAX_SSO_CHARS;
static constexpr const size_t BUF_CHARS = (sizeof(size_t) + sizeof(uint32_t)) / sizeof(T);
static constexpr const size_t EBUF_CHARS = 4*sizeof(void*)/sizeof(T);
static constexpr const size_t CHAR_SIZE = sizeof(T);
static constexpr const float GROW_RATE = 1.6;
static const T LITERAL[38];
static const size_t LITERAL_LEN = sizeof(LITERAL)/sizeof(T)-1;
static const T EMPTY[1];
static size_t slen (const T* src) {
size_t cnt = 0;
while (*src++) ++cnt;
return cnt;
}
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, const T* src, size_t len, size_t cap, size_t shcap) {
if (grow) {
cap *= GROW_RATE;
shcap *= GROW_RATE;
}
REQUIRE(str.length() == len);
CHECK(std::basic_string<T>(str.data(), len) == std::basic_string<T>(src, len));
CHECK(str.capacity() == cap);
CHECK(str.shared_capacity() == shcap);
}
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, const T* src, size_t len, size_t cap) { REQUIRE_STR<grow>(str, src, len, cap, cap); }
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, const T* src, size_t len) { REQUIRE_STR<grow>(str, src, len, len); }
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, const T* src) { REQUIRE_STR<grow>(str, src, slen(src)); }
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, StdString src, size_t cap, size_t shcap) { REQUIRE_STR<grow>(str, src.data(), src.size(), cap, shcap); }
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, StdString src, size_t cap) { REQUIRE_STR<grow>(str, src, cap, cap); }
template <bool grow = false, class A> static void REQUIRE_STR (const AnyString<A>& str, StdString src) { REQUIRE_STR<grow>(str, src, src.size()); }
template <bool grow = false, class A> static void REQUIRE_STRM (const AnyString<A>& str, StdString src) { REQUIRE_STR<grow>(str, src, str.capacity(), str.shared_capacity()); }
static void CHECK_ALLOCS (int allocated_cnt = 0, int allocated = 0, int deallocated_cnt = 0, int deallocated = 0, int reallocated_cnt = 0, int reallocated = 0, int ext_deallocated_cnt = 0, int ext_deallocated = 0, int ext_shbuf_deallocated = 0) {
auto stat = get_allocs();
CHECK(stat.allocated_cnt == allocated_cnt);
CHECK(stat.allocated == allocated);
CHECK(stat.deallocated_cnt == deallocated_cnt);
CHECK(stat.deallocated == deallocated);
CHECK(stat.reallocated_cnt == reallocated_cnt);
CHECK(stat.reallocated == reallocated);
CHECK(stat.ext_deallocated_cnt == ext_deallocated_cnt);
CHECK(stat.ext_deallocated == ext_deallocated);
CHECK(stat.ext_shbuf_deallocated == ext_shbuf_deallocated);
}
static StdString mstr (const char* data, size_t count = 1) {
StdString ret;
for (size_t i = 0; i < count; ++i) {
const char* ptr = data;
while (*ptr) ret += (T)*ptr++;
}
return ret;
}
// temporary return value, only immediate use, no assigments!
static const T* cstr (const char* data, size_t count = 1) {
static T dest[100000];
auto s = mstr(data, count);
s.copy(dest, s.size());
dest[s.size()] = (T)0;
return dest;
}
// temporary return value, only immediate use, no assigments!
static basic_string_view<T> svstr (const char* data, size_t count = 1) {
return basic_string_view<T>(cstr(data, count));
}
static T* extstr (StdString src, size_t cap = 0) {
if (cap < src.size()) cap = src.size();
T* ext = (T*)malloc(cap * sizeof(T));
std::memcpy(ext, src.data(), cap * sizeof(T));
return ext;
}
static ExternalShared* shared_buf_alloc () {
return (ExternalShared*)panda::DynamicMemoryPool::instance()->allocate(sizeof(ExternalShared));
}
template <class U = String> static U create_external (StdString exp, size_t cap) { return U(extstr(exp), exp.size(), cap, &Allocator::ext_free); }
template <class U = String> static U create_external (StdString exp) { return create_external<U>(exp, exp.size()); }
template <class U = String> static U create_external_cbuf (StdString exp, size_t cap) { return U(extstr(exp), exp.size(), cap, &Allocator::ext_free, shared_buf_alloc(), &Allocator::shared_buf_free); }
template <class U = String> static U create_external_cbuf (StdString exp) { return create_external_cbuf<U>(exp, exp.size()); }
static void assign_external (String& s, StdString exp, size_t cap) { s.assign(extstr(exp), exp.size(), cap, &Allocator::ext_free); }
static void assign_external (String& s, StdString exp) { assign_external(s, exp, exp.size()); }
static void assign_external_cbuf (String& s, StdString exp, size_t cap) { s.assign(extstr(exp), exp.size(), cap, &Allocator::ext_free, shared_buf_alloc(), &Allocator::shared_buf_free); }
static void assign_external_cbuf (String& s, StdString exp) { assign_external_cbuf(s, exp, exp.size()); }
static void test_ctor () {
auto defexp = mstr("this string is definitely longer than max sso chars");
auto defsz = BUF_CHARS + defexp.size();
SECTION("empty") {
{
String s;
REQUIRE_STR(s, EMPTY);
}
CHECK_ALLOCS();
};
SECTION("literal") {
{
String s(LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
}
CHECK_ALLOCS();
};
SECTION("sso") {
StdString cur;
while (cur.size() <= MAX_SSO_CHARS) {
String s(cur.data(), cur.size());
REQUIRE_STR(s, cur, MAX_SSO_CHARS);
if (cur.size() == defexp.size()) throw "should not happen";
cur += defexp[cur.size()];
}
CHECK_ALLOCS();
auto sz = BUF_CHARS + cur.size();
{
String s(cur.data(), cur.size());
REQUIRE_STR(s, cur);
CHECK_ALLOCS(1, sz);
}
CHECK_ALLOCS(0, 0, 1, sz);
}
SECTION("internal with len") {
{
String s(defexp.data(), defexp.size());
REQUIRE_STR(s, defexp);
}
CHECK_ALLOCS(1, defsz, 1, defsz);
}
SECTION("internal without len") {
{
String s(defexp.c_str());
REQUIRE_STR(s, defexp);
}
CHECK_ALLOCS(1, defsz, 1, defsz);
}
SECTION("external") {
{
String s(extstr(defexp), defexp.size(), defexp.size(), &Allocator::ext_free);
REQUIRE_STR(s, defexp);
}
CHECK_ALLOCS(1, EBUF_CHARS, 1, EBUF_CHARS, 0, 0, 1, defexp.size());
}
SECTION("external with custom buf") {
{
String s(extstr(defexp), defexp.size(), defexp.size(), &Allocator::ext_free, shared_buf_alloc(), &Allocator::shared_buf_free);
REQUIRE_STR(s, defexp);
CHECK_ALLOCS();
}
CHECK_ALLOCS(0, 0, 0, 0, 0, 0, 1, defexp.size(), 1);
}
SECTION("fill") {
SECTION("sso") {
{
auto exp = mstr("aa");
String s(exp.size(), (T)'a');
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
}
CHECK_ALLOCS();
};
SECTION("internal") {
auto exp = mstr("B", 50);
{
String s(exp.size(), (T)'B');
REQUIRE_STR(s, exp);
}
auto sz = BUF_CHARS + exp.size();
CHECK_ALLOCS(1, sz, 1, sz);
};
};
SECTION("new capacity") {
SECTION("sso") {
{
String s(2);
REQUIRE_STR(s, EMPTY, 0, MAX_SSO_CHARS);
}
CHECK_ALLOCS();
};
SECTION("internal") {
{
String s(50);
REQUIRE_STR(s, EMPTY, 0, 50);
}
auto sz = BUF_CHARS + 50;
CHECK_ALLOCS(1, sz, 1, sz);
};
};
}
template <class FString>
static void test_copy_ctor () {
SECTION("from empty") {
{
FString src;
String s(src);
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, EMPTY);
REQUIRE(src.use_count() == 1);
}
CHECK_ALLOCS();
}
SECTION("from literal") {
{
FString src(LITERAL);
String s(src);
REQUIRE_STR(src, LITERAL, LITERAL_LEN, 0);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
REQUIRE(src.use_count() == 1);
}
CHECK_ALLOCS();
}
SECTION("from sso") {
{
auto exp = mstr("bu");
FString src(exp.c_str());
String s(src);
REQUIRE_STR(src, exp, MAX_SSO_CHARS);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
REQUIRE(src.use_count() == 1);
}
CHECK_ALLOCS();
}
SECTION("from internal") {
auto exp = mstr("bu", 50);
auto sz = BUF_CHARS + exp.size();
{
FString src(exp.c_str());
CHECK_ALLOCS(1, sz);
{
String s(src);
REQUIRE_STR(src, exp, 0, exp.size());
REQUIRE_STR(s, exp, 0, exp.size());
CHECK_ALLOCS();
REQUIRE(src.use_count() == 2);
}
CHECK_ALLOCS();
}
CHECK_ALLOCS(0, 0, 1, sz);
}
SECTION("from external") {
auto exp = mstr("c", 50);
{
FString src(extstr(exp), exp.size(), exp.size(), &Allocator::ext_free);
CHECK_ALLOCS(1, EBUF_CHARS);
{
String s(src);
REQUIRE_STR(src, exp, 0, exp.size());
REQUIRE_STR(s, exp, 0, exp.size());
CHECK_ALLOCS();
REQUIRE(src.use_count() == 2);
}
CHECK_ALLOCS();
}
CHECK_ALLOCS(0, 0, 1, EBUF_CHARS, 0, 0, 1, exp.size());
}
}
template <class FString>
static void test_move_ctor () {
SECTION("from empty") {
{
FString src;
String s(std::move(src));
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, EMPTY);
}
CHECK_ALLOCS();
}
SECTION("from literal") {
{
FString src(LITERAL);
String s(std::move(src));
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
}
CHECK_ALLOCS();
}
SECTION("from sso") {
{
auto exp = mstr("bu");
FString src(exp.c_str());
String s(std::move(src));
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
}
CHECK_ALLOCS();
}
SECTION("from internal") {
auto exp = mstr("bu", 50);
auto sz = BUF_CHARS + exp.size();
{
FString src(exp.data(), exp.size());
CHECK_ALLOCS(1,sz);
String s(std::move(src));
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
}
CHECK_ALLOCS(0,0,1,sz);
}
SECTION("from external") {
auto exp = mstr("c", 50);
{
FString src(extstr(exp), exp.size(), exp.size(), &Allocator::ext_free);
CHECK_ALLOCS(1,EBUF_CHARS);
String s(std::move(src));
REQUIRE_STR(src, EMPTY);
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
}
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
}
}
template <class FString>
static void test_copy_ctor_offset () {
SECTION("from literal") {
{
auto exp = StdString(LITERAL).substr(2, 25);
FString src(LITERAL);
String s(src, 2, 25);
REQUIRE_STR(src, LITERAL, LITERAL_LEN, 0);
REQUIRE_STR(s, exp, 0);
}
CHECK_ALLOCS();
}
SECTION("from sso") {
{
auto exp = mstr("bu");
FString src(exp.c_str());
String s(src, 1, 1);
REQUIRE_STR(src, exp, MAX_SSO_CHARS);
REQUIRE_STR(s, mstr("u"), MAX_SSO_CHARS-1);
}
CHECK_ALLOCS();
}
SECTION("from internal") {
auto exp = mstr("bu", 50);
auto sz = BUF_CHARS + exp.size();
{
FString src(exp.c_str());
CHECK_ALLOCS(1, sz);
String s(src, 9, 5);
REQUIRE_STR(src, exp, 0, exp.size());
REQUIRE_STR(s, mstr("ububu"), 0, exp.size()-9);
CHECK_ALLOCS();
}
CHECK_ALLOCS(0,0,1,sz);
}
SECTION("from external") {
auto exp = mstr("c", 50);
{
FString src(extstr(exp), exp.size(), exp.size(), &Allocator::ext_free);
CHECK_ALLOCS(1, EBUF_CHARS);
String s(src, 10, 30);
REQUIRE_STR(src, exp, 0, exp.size());
REQUIRE_STR(s, mstr("c", 30), 0, exp.size()-10);
CHECK_ALLOCS();
}
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
}
SECTION("out of bounds") {
auto exp = mstr("hello");
FString src(exp.c_str());
SECTION("too big length acts as npos") {
String s(src, 3, 10);
REQUIRE_STRM(s, mstr("lo"));
}
SECTION("too big offset throws exception") {
REQUIRE_THROWS(String(src, 6, 10));
}
}
}
static void test_substr () {
auto exp = mstr("hello world, hello world!");
String src(exp.c_str());
String s = src.substr(0, 5);
REQUIRE_STR(s, mstr("hello"), 0, exp.size());
s = src.substr(6);
REQUIRE_STR(s, mstr("world, hello world!"), 0, exp.size()-6);
s = src.substr(4, 3);
REQUIRE_STR(s, mstr("o w"), 0, exp.size()-4);
REQUIRE_STR(src, exp, 0, exp.size());
}
static void test_clear () {
SECTION("literal") {
{
String s(LITERAL);
s.clear();
REQUIRE_STR(s, EMPTY);
}
CHECK_ALLOCS();
}
SECTION("sso") {
{
auto exp = mstr("bu");
String s(exp.c_str());
s.clear();
REQUIRE_STR(s, EMPTY, 0, MAX_SSO_CHARS);
}
CHECK_ALLOCS();
}
SECTION("internal") {
auto exp = mstr("bu", 50);
{
String s(exp.c_str());
get_allocs();
s.clear();
REQUIRE_STR(s, EMPTY, 0, exp.size());
}
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp.size());
}
SECTION("external") {
auto exp = mstr("c", 50);
{
String s(extstr(exp), exp.size(), exp.size(), &Allocator::ext_free);
get_allocs();
s.clear();
CHECK_ALLOCS();
REQUIRE_STR(s, EMPTY, 0, exp.size());
}
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
};
}
struct AssignLiteral { template <size_t U> static void op (String& s, const T (&val)[U]) { s.assign(val); } };
struct OpAssignLiteral { template <size_t U> static void op (String& s, const T (&val)[U]) { s = val; } };
struct AssignPtr { static void op (String& s, const T* val) { s.assign(val); } };
struct OpAssignPtr { static void op (String& s, const T* val) { s = val; } };
struct AssignCopy {
static void op (String& s, const String& val) { s.assign(val); }
static void op (String& s, const String2& val) { s.assign(val); }
};
struct OpAssignCopy {
static void op (String& s, const String& val) { s = val; }
static void op (String& s, const String2& val) { s = val; }
};
struct AssignMove {
static void op (String& s, String& val) { s.assign(std::move(val)); }
static void op (String& s, String2& val) { s.assign(std::move(val)); }
};
struct OpAssignMove {
static void op (String& s, String& val) { s = std::move(val); }
static void op (String& s, String2& val) { s = std::move(val); }
};
template <class U>
static void test_assign_literal () {
SECTION("from empty") {
{
String s;
U::op(s, LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
}
CHECK_ALLOCS();
}
SECTION("from literal") {
{
const T exp[] = {'h', 'e', 'l', 'l', 'o', 0};
const size_t exp_len = sizeof(exp) / sizeof(T) - 1;
String s(exp);
REQUIRE_STR(s, exp, exp_len, 0);
U::op(s, LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
}
CHECK_ALLOCS();
}
SECTION("from sso") {
{
auto exp = mstr("bu");
String s(exp.c_str());
U::op(s, LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
}
CHECK_ALLOCS();
}
SECTION("from internal") {
{
auto exp = mstr("a", 50);
auto sz = BUF_CHARS + exp.size();
String s(exp.c_str());
CHECK_ALLOCS(1,sz);
U::op(s, LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
CHECK_ALLOCS(0,0,1,sz);
}
CHECK_ALLOCS();
}
SECTION("from external") {
{
auto exp = mstr("c", 50);
String s(extstr(exp), exp.size(), exp.size(), &Allocator::ext_free);
CHECK_ALLOCS(1, EBUF_CHARS);
U::op(s, LITERAL);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
}
CHECK_ALLOCS();
}
}
template <class U>
static void test_assign_ptr () {
SECTION("from literal") {
auto exp = mstr("0", 50);
String s(LITERAL);
SECTION("no len") {
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1,BUF_CHARS+exp.size());
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp.size());
}
SECTION("with len") {
s.assign(exp.data(), 40);
CHECK_ALLOCS(1,BUF_CHARS+40);
REQUIRE_STR(s, exp.data(), 40, 40);
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+40);
}
}
SECTION("from sso") {
auto exp = mstr("1", 50);
String s(cstr("yt"));
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1,BUF_CHARS+exp.size());
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp.size());
}
SECTION("from internal") {
auto exp = mstr("1", 50);
String s(cstr("2", 50));
get_allocs();
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(); //no allocs for sufficient capacity
U::op(s, cstr("so"));
REQUIRE_STR(s, mstr("so"), 50); // didnt become sso
CHECK_ALLOCS();
exp = mstr("3", 60);
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, BUF_CHARS+60, 1, BUF_CHARS+50); //extended storage
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+60);
}
SECTION("from external") {
auto exp = mstr("4", 50);
String s(extstr(mstr("5", 50)), 50, 50, &Allocator::ext_free);
get_allocs();
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(); //no allocs for sufficient capacity
exp = mstr("bt");
U::op(s, exp.c_str());
REQUIRE_STR(s, exp, 50); // didnt become sso
exp = mstr("6", 70);
U::op(s, exp.c_str());
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, BUF_CHARS+70, 1, EBUF_CHARS, 0, 0, 1, 50); //extended storage moved to internal
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+70);
}
SECTION("from cow") {
auto exp = mstr("1", 40);
SECTION("from internal cow") {
String tmp(cstr("2", 50));
String s(tmp);
get_allocs();
U::op(s, exp.c_str());
REQUIRE_STR(s, exp); //string detached
CHECK_ALLOCS(1, BUF_CHARS+40); //string detached
s = tmp;
get_allocs();
U::op(s, cstr("qw"));
REQUIRE_STR(s, mstr("qw"), MAX_SSO_CHARS); //string detached to sso
}
SECTION("from external cow") {
String tmp(extstr(mstr("2", 50)), 50, 50, &Allocator::ext_free);
String s(tmp);
get_allocs();
U::op(s, exp.c_str());
REQUIRE_STR(s, exp); //string detached
CHECK_ALLOCS(1, BUF_CHARS+40); //string detached
s = tmp;
get_allocs();
U::op(s, cstr("qw"));
REQUIRE_STR(s, mstr("qw"), MAX_SSO_CHARS); //string detached to sso
}
}
get_allocs();
}
static void test_assign_external () {
SECTION("from literal") {
auto exp = mstr("0", 50);
String s(LITERAL);
assign_external(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, EBUF_CHARS);
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("from sso") {
auto exp = mstr("1", 50);
String s(cstr("yt"));
assign_external(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, EBUF_CHARS);
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("from internal") {
auto exp = mstr("1", 50);
String s(cstr("2", 60));
get_allocs();
assign_external(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, EBUF_CHARS, 1, BUF_CHARS + 60);
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("from external") {
auto exp = mstr("4", 50);
//refcnt = 1
String s = create_external<>(mstr("abcd"));
get_allocs();
assign_external(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(0,0,0,0,0,0,1,4); //this case is optimized to reuse ExternalBuffer instead of dropping and creating a new one
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
// refcnt = 2
s = create_external<>(mstr("abcd"));
String tmp(s);
get_allocs();
assign_external(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1,EBUF_CHARS);
tmp = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,4);
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("with custom buf") {
auto exp = mstr("4", 50);
String s = create_external<>(mstr("abcd"));
get_allocs();
assign_external_cbuf(s, exp);
REQUIRE_STR(s, exp);
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,4);
s = EMPTY;
CHECK_ALLOCS(0,0,0,0,0,0,1,50,1);
}
}
static void test_assign_fill () {
String s;
s.assign(2, (T)'a');
REQUIRE_STR(s, mstr("aa"), MAX_SSO_CHARS);
CHECK_ALLOCS();
s = String(cstr("a", 50));
get_allocs();
s.assign(10, (T)'b');
REQUIRE_STR(s, mstr("bbbbbbbbbb"), 50);
CHECK_ALLOCS();
s = EMPTY;
get_allocs();
}
static void test_assign_char () {
String s;
s = (T)'A';
REQUIRE_STR(s, mstr("A"), MAX_SSO_CHARS);
s = (T)'B';
REQUIRE_STR(s, mstr("B"), MAX_SSO_CHARS);
}
template <class U, class FString>
static void test_assign_copy () {
SECTION("literal<->literal") {
FString src(LITERAL);
String s;
U::op(s, src);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
REQUIRE_STR(src, LITERAL, LITERAL_LEN, 0);
s = EMPTY;
CHECK_ALLOCS();
}
SECTION("sso<->sso") {
auto exp = mstr("bu");
FString src(exp.c_str());
String s(cstr("du"));
CHECK_ALLOCS();
U::op(s, src);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
REQUIRE_STR(src, exp, MAX_SSO_CHARS);
CHECK_ALLOCS();
s = EMPTY;
src = EMPTY;
CHECK_ALLOCS();
}
SECTION("internal<->internal") {
auto exp = mstr("q", 50);
FString src(exp.c_str());
String s(cstr("d", 30));
get_allocs();
U::op(s, src);
REQUIRE_STR(s, exp, 0, exp.size());
REQUIRE_STR(src, exp, 0, exp.size());
CHECK_ALLOCS(0,0,1,BUF_CHARS+30);
s = EMPTY;
CHECK_ALLOCS();
src = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+50);
}
SECTION("external<->external") {
auto exp = mstr("q", 50);
FString src = create_external<FString>(exp);
String s = create_external<>(mstr("d", 30));
get_allocs();
U::op(s, src);
REQUIRE_STR(s, exp, 0, exp.size());
REQUIRE_STR(src, exp, 0, exp.size());
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,30);
s = EMPTY;
CHECK_ALLOCS();
src = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("same object") {
auto exp = mstr("q", 50);
String s(exp.c_str());
get_allocs();
U::op(s, s);
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+50);
}
}
template <class FString>
static void test_assign_offset () {
SECTION("internal<->internal") {
auto exp = mstr("q", 50);
FString src(exp.c_str());
String s(cstr("d", 30));
get_allocs();
s.assign(src, 10, 30);
REQUIRE_STR(s, mstr("q", 30), 0, 40);
CHECK_ALLOCS(0,0,1,BUF_CHARS+30);
s = EMPTY;
CHECK_ALLOCS();
src = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+50);
}
SECTION("same object") {
auto exp = mstr("x", 50);
String s = create_external<>(exp);
get_allocs();
s.assign(s, 10, 30);
REQUIRE_STR(s, mstr("x", 30), 40);
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
}
template <class U, class FString>
static void test_assign_move () {
SECTION("literal<->literal") {
FString src(LITERAL);
String s;
U::op(s, src);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
REQUIRE_STR(src, EMPTY);
s = EMPTY;
CHECK_ALLOCS();
}
SECTION("sso<->sso") {
auto exp = mstr("bu");
FString src(exp.c_str());
String s(cstr("du"));
CHECK_ALLOCS();
U::op(s, src);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
REQUIRE_STR(src, EMPTY);
CHECK_ALLOCS();
s = EMPTY;
src = EMPTY;
CHECK_ALLOCS();
}
SECTION("internal<->internal") {
auto exp = mstr("q", 50);
FString src(exp.c_str());
String s(cstr("d", 30));
get_allocs();
U::op(s, src);
REQUIRE_STR(s, exp);
REQUIRE_STR(src, EMPTY);
CHECK_ALLOCS(0,0,1,BUF_CHARS+30);
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+50);
src = EMPTY;
CHECK_ALLOCS();
}
SECTION("external<->external") {
auto exp = mstr("q", 50);
FString src = create_external<FString>(exp);
String s = create_external<>(mstr("d", 30));
get_allocs();
U::op(s, src);
REQUIRE_STR(s, exp);
REQUIRE_STR(src, EMPTY);
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,30);
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
src = EMPTY;
CHECK_ALLOCS();
}
SECTION("same object") {
auto exp = mstr("q", 50);
String s(exp.c_str());
get_allocs();
U::op(s, s);
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+50);
}
}
template <class FString>
static void test_assign () {
SECTION("literal") {
SECTION("method") { test_assign_literal<AssignLiteral>(); }
SECTION("operator") { test_assign_literal<OpAssignLiteral>(); }
}
SECTION("ptr") {
SECTION("method") { test_assign_ptr<AssignPtr>(); }
SECTION("operator") { test_assign_ptr<OpAssignPtr>(); }
}
SECTION("external") { test_assign_external(); }
SECTION("fill") { test_assign_fill(); }
SECTION("char") { test_assign_char(); }
SECTION("copy") {
SECTION("method") { test_assign_copy<AssignCopy, FString>(); }
SECTION("operator") { test_assign_copy<OpAssignCopy, FString>(); }
}
SECTION("offset") { test_assign_offset<FString>(); }
SECTION("move") {
SECTION("method") { test_assign_move<AssignMove, FString>(); }
SECTION("operator") { test_assign_move<OpAssignMove, FString>(); }
}
}
static void test_offset () {
SECTION("from literal") {
auto exp = StdString(LITERAL).substr(2, 25);
String s(LITERAL);
s.offset(2, 25);
REQUIRE_STR(s, exp, 0);
s = EMPTY;
CHECK_ALLOCS();
}
SECTION("from sso") {
auto exp = mstr("bu");
String s(exp.c_str());
s.offset(1, 1);
REQUIRE_STR(s, mstr("u"), MAX_SSO_CHARS-1);
s = EMPTY;
CHECK_ALLOCS();
}
SECTION("from internal") {
auto exp = mstr("bu", 50);
String s(exp.c_str());
s.offset(9, 5);
REQUIRE_STR(s, mstr("ububu"), 100-9);
s = EMPTY;
auto sz = BUF_CHARS + 100;
CHECK_ALLOCS(1,sz,1,sz);
}
SECTION("from external") {
auto exp = mstr("c", 50);
String s = create_external<>(exp);
s.offset(10, 30);
REQUIRE_STR(s, mstr("c", 30), exp.size()-10);
s = EMPTY;
CHECK_ALLOCS(1, EBUF_CHARS, 1, EBUF_CHARS, 0, 0, 1, exp.size());
}
SECTION("out of bounds") {
auto exp = mstr("hello");
String s(exp.c_str());
SECTION("too big length acts as npos") {
s.offset(3, 10);
REQUIRE_STRM(s, mstr("lo"));
};
SECTION("too big offset throws exception") {
REQUIRE_THROWS(s.offset(6,10));
}
}
}
template <class FString>
static void test_swap () {
SECTION("literal<->literal") {
String s1;
FString s2(LITERAL);
s1.swap(s2);
REQUIRE_STR(s1, LITERAL, LITERAL_LEN, 0);
REQUIRE_STR(s2, EMPTY);
s1 = EMPTY; s2 = EMPTY;
CHECK_ALLOCS();
}
SECTION("literal<->sso") {
auto exp = mstr("eb");
String s1(LITERAL);
FString s2(exp.c_str());
s1.swap(s2);
REQUIRE_STR(s1, exp, MAX_SSO_CHARS);
REQUIRE_STR(s2, LITERAL, LITERAL_LEN, 0);
s1 = EMPTY; s2 = EMPTY;
CHECK_ALLOCS();
}
SECTION("literal<->internal") {
auto exp = mstr("eb", 50);
String s1(LITERAL);
FString s2(exp.c_str());
get_allocs();
s1.swap(s2);
REQUIRE_STR(s1, exp);
REQUIRE_STR(s2, LITERAL, LITERAL_LEN, 0);
s2 = EMPTY;
CHECK_ALLOCS();
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp.size());
}
SECTION("literal<->external") {
auto exp = mstr("be", 50);
String s1(LITERAL);
FString s2 = create_external<FString>(exp);
get_allocs();
s1.swap(s2);
REQUIRE_STR(s1, exp);
REQUIRE_STR(s2, LITERAL, LITERAL_LEN, 0);
s2 = EMPTY;
CHECK_ALLOCS();
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
}
SECTION("sso<->sso") {
auto exp1 = mstr("eb");
auto exp2 = mstr("ta");
String s1(exp1.c_str());
FString s2(exp2.c_str());
s1.swap(s2);
REQUIRE_STR(s1, exp2, MAX_SSO_CHARS);
REQUIRE_STR(s2, exp1, MAX_SSO_CHARS);
s1 = EMPTY; s2 = EMPTY;
CHECK_ALLOCS();
}
SECTION("sso<->internal") {
auto exp1 = mstr("eb");
auto exp2 = mstr("ta", 50);
String s1(exp1.c_str());
FString s2(exp2.c_str());
get_allocs();
s1.swap(s2);
REQUIRE_STR(s1, exp2);
REQUIRE_STR(s2, exp1, MAX_SSO_CHARS);
s2 = EMPTY;
CHECK_ALLOCS();
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp2.size());
}
SECTION("sso<->external") {
auto exp1 = mstr("eb");
auto exp2 = mstr("ta", 50);
String s1(exp1.c_str());
FString s2 = create_external<FString>(exp2);
get_allocs();
s1.swap(s2);
REQUIRE_STR(s1, exp2);
REQUIRE_STR(s2, exp1, MAX_SSO_CHARS);
s2 = EMPTY;
CHECK_ALLOCS();
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp2.size());
}
SECTION("internal<->internal") {
auto exp1 = mstr("eb", 100);
auto exp2 = mstr("ta", 50);
String s1(exp1.c_str());
FString s2(exp2.c_str());
CHECK_ALLOCS(2, BUF_CHARS*2 + exp1.size() + exp2.size());
s1.swap(s2);
CHECK_ALLOCS();
REQUIRE_STR(s1, exp2);
REQUIRE_STR(s2, exp1);
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp2.size());
s2 = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp1.size());
}
SECTION("internal<->external") {
auto exp1 = mstr("eb", 100);
auto exp2 = mstr("ta", 50);
String s1(exp1.c_str());
FString s2 = create_external<FString>(exp2);
CHECK_ALLOCS(2, EBUF_CHARS + BUF_CHARS + exp1.size());
s1.swap(s2);
REQUIRE_STR(s1, exp2);
REQUIRE_STR(s2, exp1);
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp2.size());
s2 = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp1.size());
}
SECTION("external<->external") {
auto exp1 = mstr("eb", 100);
auto exp2 = mstr("ta", 50);
String s1 = create_external<>(exp1);
FString s2 = create_external<FString>(exp2);
CHECK_ALLOCS(2, EBUF_CHARS*2);
s1.swap(s2);
REQUIRE_STR(s1, exp2);
REQUIRE_STR(s2, exp1);
s1 = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp2.size());
s2 = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp1.size());
}
}
static void test_copy () {
String s(cstr("the password for my bank account is w74mnds320ft but i won't tell you the login)"));
T t[500];
size_t cnt;
cnt = s.copy(t, 0);
REQUIRE(cnt == 0);
cnt = s.copy(t, 10);
REQUIRE(cnt == 10);
REQUIRE_STRM(String(t, cnt), mstr("the passwo"));
cnt = s.copy(t, 20, 15);
REQUIRE(cnt == 20);
REQUIRE_STRM(String(t, cnt), mstr("r my bank account is"));
cnt = s.copy(t, 20, 70); //too much count
REQUIRE(cnt == 10);
REQUIRE_STRM(String(t, cnt), mstr("the login)"));
cnt = s.copy(t, String::npos, 60); //count=npos
REQUIRE(cnt == 20);
REQUIRE_STRM(String(t, cnt), mstr(" tell you the login)"));
REQUIRE_THROWS(s.copy(t, 10, 90)); //too much offset
}
static void test_bool_empty () {
String s;
REQUIRE(s.empty());
REQUIRE(!s);
s = 'a';
REQUIRE(!s.empty());
REQUIRE(s);
}
static void test_use_count () {
String s1(LITERAL);
String s2(s1);
REQUIRE(s1.use_count() == 1);
REQUIRE(s2.use_count() == 1);
s1 = cstr("ab");
s2 = s1;
REQUIRE(s1.use_count() == 1);
REQUIRE(s2.use_count() == 1);
s1 = cstr("a", 50);
REQUIRE(s1.use_count() == 1);
s2 = s1;
REQUIRE(s1.use_count() == 2);
REQUIRE(s2.use_count() == 2);
s1 = create_external<>(mstr("b", 50));
REQUIRE(s1.use_count() == 1);
s2 = s1;
REQUIRE(s1.use_count() == 2);
REQUIRE(s2.use_count() == 2);
}
static void test_detach () {
get_allocs();
SECTION("literal") {
String s(LITERAL);
s.buf();
REQUIRE_STR(s, LITERAL, LITERAL_LEN, LITERAL_LEN);
CHECK_ALLOCS(1, BUF_CHARS + LITERAL_LEN);
};
SECTION("sso") {
String s(cstr("ab"));
s.buf();
CHECK_ALLOCS();
REQUIRE_STR(s, mstr("ab"), MAX_SSO_CHARS);
};
SECTION("internal") {
auto exp = mstr("q", 50);
String s(exp.c_str());
get_allocs();
s.buf();
CHECK_ALLOCS();
String s2(s);
REQUIRE(s.use_count() == 2);
REQUIRE(s2.use_count() == 2);
s.buf();
CHECK_ALLOCS(1, BUF_CHARS + exp.size()); //cow - detached
REQUIRE_STR(s, exp);
REQUIRE(s.use_count() == 1);
REQUIRE(s2.use_count() == 1);
};
SECTION("external") {
auto exp = mstr("q", 50);
String s = create_external<>(exp);
get_allocs();
s.buf();
CHECK_ALLOCS();
String s2(s);
REQUIRE(s.use_count() == 2);
REQUIRE(s2.use_count() == 2);
s.buf();
CHECK_ALLOCS(1, BUF_CHARS + exp.size()); //cow - detached
REQUIRE_STR(s, exp);
REQUIRE(s.use_count() == 1);
REQUIRE(s2.use_count() == 1);
};
}
static void test_at_front_back () {
String s(cstr("0123456789", 5));
String tmp(s);
get_allocs();
SECTION("front") {
REQUIRE(s.front() == (T)'0');
REQUIRE(s.use_count() == 2); // not detached
s.front() = (T)'9';
REQUIRE_STRM(String(s, 0, 10), mstr("9123456789"));
REQUIRE(s.use_count() == 1); //string detached
}
SECTION("at") {
REQUIRE(s.at(1) == (T)'1');
REQUIRE(s.at(2) == (T)'2');
REQUIRE_THROWS(s.at(1000));
REQUIRE(s.use_count() == 2); // not detached
s.at(1) = (T)'8';
REQUIRE_STRM(String(s, 0, 10), mstr("0823456789"));
REQUIRE(s.use_count() == 1); //string detached
s.at(0) = s.at(1);
REQUIRE_STRM(String(s, 0, 10), mstr("8823456789"));
}
SECTION("op[]") {
REQUIRE(s[3] == (T)'3');
REQUIRE(s.use_count() == 2); // not detached
s[2] = (T)'7';
REQUIRE_STRM(String(s, 0, 10), mstr("0173456789"));
REQUIRE(s.use_count() == 1); //string detached
}
SECTION("back") {
REQUIRE(s.back() == (T)'9');
REQUIRE(s.use_count() == 2); // not detached
s.back() = (T)'0';
REQUIRE_STRM(String(s, 40, 10), mstr("0123456780"));
REQUIRE(s.use_count() == 1); //string detached
}
SECTION("pop_back") {
auto exp = StdString(LITERAL).substr(0, LITERAL_LEN-1);
String s(LITERAL);
s.pop_back();
REQUIRE_STR(s, exp, 0);
s = EMPTY;
CHECK_ALLOCS();
}
}
static void test_iterator () {
String s(cstr("0123456789", 5));
String tmp(s);
get_allocs();
SECTION("begin + mutations") {
auto it = s.begin();
REQUIRE(*it++ == (T)'0');
REQUIRE(*it++ == (T)'1');
REQUIRE(*(it += 2) == (T)'4');
REQUIRE(*(it -= 1) == (T)'3');
REQUIRE(s.use_count() == 2); // not detached
*it = (T)'x';
REQUIRE(s.use_count() == 1); // detached
REQUIRE_STRM(String(s, 0, 10), mstr("012x456789"));
}
SECTION("end") {
auto it = s.end();
REQUIRE(*(--it) == (T)'9');
REQUIRE(*(--it) == (T)'8');
std::advance(it, -2);
REQUIRE(*it == (T)'6');
}
SECTION("diffence & eq & ne") {
auto b = s.begin();
auto e = s.end();
REQUIRE(b == b);
REQUIRE(e == e);
REQUIRE(b != e);
REQUIRE(size_t(e - b) == s.length());
}
SECTION("ordening relations") {
auto b = s.begin();
auto e = s.end();
REQUIRE(b >= b);
REQUIRE(!(b > b));
REQUIRE(e > b);
REQUIRE(b < e);
REQUIRE(b <= e);
REQUIRE(e <= e);
REQUIRE(!(e < e ));
}
SECTION("global -+ operators") {
auto b = s.begin();
auto e = s.end();
REQUIRE(*(b + 1) == (T)'1');
REQUIRE(*(2 + b) == (T)'2');
REQUIRE(*(e - 1) == (T)'9');
REQUIRE(*(2 - e) == (T)'8');
}
SECTION("as const iterator") {
auto b = s.begin();
auto cb = (const T*)b;
REQUIRE(*cb++ == (T)'0');
REQUIRE(*cb++ == (T)'1');
}
SECTION("plus and as const iterator") {
auto b = s.begin() + 2;
auto cb = (const T*)b;
REQUIRE(*cb++ == (T)'2');
REQUIRE(*cb++ == (T)'3');
}
}
static void test_erase () {
get_allocs();
SECTION("literal") {
String s(LITERAL);
s.erase(11);
REQUIRE_STR(s, mstr("hello world"), 0);
CHECK_ALLOCS();
s.erase(0, 6);
REQUIRE_STR(s, mstr("world"), 0);
CHECK_ALLOCS();
s.erase(1, 3);
REQUIRE_STR(s, mstr("wd"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
if (CHAR_SIZE == 1) {
SECTION("sso") {
String s(cstr("motherfuck"));
s.erase(8);
REQUIRE_STR(s, mstr("motherfu"), MAX_SSO_CHARS);
s.erase(0, 2);
REQUIRE_STR(s, mstr("therfu"), MAX_SSO_CHARS-2);
s.erase(1, 2);
REQUIRE_STR(s, mstr("trfu"), MAX_SSO_CHARS-4);
s = EMPTY;
CHECK_ALLOCS();
}
}
SECTION("internal") {
auto exp = mstr("0123456789", 7);
String s(exp.c_str());
get_allocs();
s.erase(65);
REQUIRE_STR(s, mstr("01234567890123456789012345678901234567890123456789012345678901234"), 70);
s.erase(0, 5);
REQUIRE_STR(s, mstr("567890123456789012345678901234567890123456789012345678901234"), 65);
s.erase(5, 5);
REQUIRE_STR(s, mstr("5678956789012345678901234567890123456789012345678901234"), 60); //head moved
s.erase(45, 5);
REQUIRE_STR(s, mstr("56789567890123456789012345678901234567890123401234"), 60); //tail moved
CHECK_ALLOCS();
String s2(s);
REQUIRE_STR(s2, mstr("56789567890123456789012345678901234567890123401234"), 0, 60);
s.erase(45);
REQUIRE_STR(s, mstr("567895678901234567890123456789012345678901234"), 0, 60); // still cow
s.erase(0, 5);
REQUIRE_STR(s, mstr("5678901234567890123456789012345678901234"), 0, 55); // still cow
CHECK_ALLOCS();
REQUIRE(s.use_count() == 2); // cow
s.erase(5, 5);
REQUIRE_STR(s, mstr("56789567890123456789012345678901234")); // detached
REQUIRE(s.use_count() == 1);
CHECK_ALLOCS(1,BUF_CHARS+35);
}
SECTION("external") {
auto exp = mstr("0123456789", 5);
String s(exp.c_str());
get_allocs();
s.erase(45);
REQUIRE_STR(s, mstr("012345678901234567890123456789012345678901234"), 50);
s.erase(0, 5);
REQUIRE_STR(s, mstr("5678901234567890123456789012345678901234"), 45);
s.erase(5, 5);
REQUIRE_STR(s, mstr("56789567890123456789012345678901234"), 40); //head moved
s.erase(25, 5);
REQUIRE_STR(s, mstr("567895678901234567890123401234"), 40); //tail moved
CHECK_ALLOCS();
String s2(s);
REQUIRE_STR(s2, mstr("567895678901234567890123401234"), 0, 40);
s.erase(25);
REQUIRE_STR(s, mstr("5678956789012345678901234"), 0, 40); // still cow
s.erase(0, 5);
REQUIRE_STR(s, mstr("56789012345678901234"), 0, 35); // still cow
CHECK_ALLOCS();
REQUIRE(s.use_count() == 2);
s.erase(1, 18);
REQUIRE_STR(s, mstr("54"), MAX_SSO_CHARS); // detached
REQUIRE(s.use_count() == 1);
}
SECTION("offset exceed") {
String s(LITERAL);
REQUIRE_THROWS(s.erase(100));
}
}
template <class FString>
static void test_compare () {
String s1(cstr("keyword"));
FString s2(cstr("abcword"));
FString s3(cstr("keyword1"));
FString s4(cstr("keyword"));
FString s5(cstr("word"));
get_allocs();
REQUIRE(s1.compare(0, 7, s2, 0, 7) > 0);
REQUIRE(s1.compare(0, 7, s3, 0, 8) < 0);
REQUIRE(s1.compare(0, 7, s4, 0, 7) == 0);
REQUIRE(s1.compare(0, 7, s5, 0, 4) < 0);
REQUIRE(s1.compare(0, 7, s3, 0, 7) == 0);
REQUIRE(s1.compare(3, 4, s5, 0, 4) == 0);
REQUIRE(s1.compare(3, 4, s2, 3, 4) == 0);
REQUIRE_THROWS(s1.compare(8, 7, s2, 0, 7)); //offset exceeded
REQUIRE_THROWS(s1.compare(0, 7, s2, 8, 7)); //offset exceeded
REQUIRE(s1.compare(0, 10, s4, 0, 7) == 0); //len exceeded
REQUIRE(s1.compare(0, 10, s4, 0, 11) == 0); //len exceeded
REQUIRE_FALSE(s1 == s3);
REQUIRE(s1 == s4);
REQUIRE(s1 != s3);
REQUIRE_FALSE(s1 != s4);
REQUIRE(s1 > s2);
REQUIRE_FALSE(s1 > s3);
REQUIRE_FALSE(s1 > s4);
REQUIRE(s1 >= s2);
REQUIRE_FALSE(s1 >= s3);
REQUIRE(s1 >= s4);
REQUIRE_FALSE(s1 < s2);
REQUIRE(s1 < s3);
REQUIRE_FALSE(s1 < s4);
REQUIRE_FALSE(s1 <= s2);
REQUIRE(s1 <= s3);
REQUIRE(s1 <= s4);
CHECK_ALLOCS();
}
template <class FString>
static void test_find () {
auto npos = String::npos;
String s(cstr("jopa noviy god"));
SECTION("find") {
REQUIRE(s.find(FString(cstr("o"))) == 1);
REQUIRE(s.find(FString(cstr("jopa"))) == 0);
REQUIRE(s.find(FString(cstr("noviy"))) == 5);
REQUIRE(s.find(FString(cstr("god"))) == 11);
REQUIRE(s.find(FString(cstr("o")), 2) == 6);
REQUIRE(s.find(FString(EMPTY), 0) == 0);
REQUIRE(s.find(FString(EMPTY), 13) == 13);
REQUIRE(s.find(FString(EMPTY), 14) == 14);
REQUIRE(s.find(FString(EMPTY), 15) == npos);
REQUIRE(s.find(FString(cstr("o")), 14) == npos);
REQUIRE(s.find(FString(cstr("god")), 11) == 11);
REQUIRE(s.find(FString(cstr("god")), 12) == npos);
}
SECTION("rfind") {
REQUIRE(s.rfind(FString(cstr("o"))) == 12);
REQUIRE(s.rfind(FString(cstr("o")), 99999) == 12);
REQUIRE(s.rfind(FString(cstr("jopa"))) == 0);
REQUIRE(s.rfind(FString(cstr("jopa")), 0) == 0);
REQUIRE(s.rfind(FString(cstr("noviy"))) == 5);
REQUIRE(s.rfind(FString(cstr("o")), 11) == 6);
REQUIRE(s.rfind(FString(EMPTY), 0) == 0);
REQUIRE(s.rfind(FString(EMPTY), 13) == 13);
REQUIRE(s.rfind(FString(EMPTY), 14) == 14);
REQUIRE(s.rfind(FString(EMPTY), 15) == 14);
REQUIRE(s.rfind(FString(cstr("o")), 0) == npos);
REQUIRE(s.rfind(FString(cstr("god")), 11) == 11);
REQUIRE(s.rfind(FString(cstr("god")), 10) == npos);
}
SECTION("find_first_of") {
REQUIRE(s.find_first_of(FString(cstr("o"))) == 1);
REQUIRE(s.find_first_of(FString(cstr("o")), 2) == 6);
REQUIRE(s.find_first_of(FString(cstr("o")), 14) == npos);
REQUIRE(s.find_first_of(FString(EMPTY), 0) == npos);
REQUIRE(s.find_first_of(FString(EMPTY), 15) == npos);
REQUIRE(s.find_first_of(FString(cstr("pnv"))) == 2);
REQUIRE(s.find_first_of(FString(cstr("pnv")), 3) == 5);
REQUIRE(s.find_first_of(FString(cstr("pnv")), 6) == 7);
REQUIRE(s.find_first_of(FString(cstr("pnv")), 8) == npos);
}
SECTION("find_first_not_of") {
REQUIRE(s.find_first_not_of(FString(cstr("o"))) == 0);
REQUIRE(s.find_first_not_of(FString(cstr("j"))) == 1);
REQUIRE(s.find_first_not_of(FString(cstr("o")), 1) == 2);
REQUIRE(s.find_first_not_of(FString(cstr("d")), 13) == npos);
REQUIRE(s.find_first_not_of(FString(EMPTY), 0) == 0);
REQUIRE(s.find_first_not_of(FString(EMPTY), 15) == npos);
REQUIRE(s.find_first_not_of(FString(cstr("jopa nviy"))) == 11);
REQUIRE(s.find_first_not_of(FString(cstr("og ")), 10) == 13);
REQUIRE(s.find_first_not_of(FString(cstr("ogd ")), 10) == npos);
}
SECTION("find_last_of") {
REQUIRE(s.find_last_of(FString(cstr("o"))) == 12);
REQUIRE(s.find_last_of(FString(cstr("o")), 9999) == 12);
REQUIRE(s.find_last_of(FString(cstr("o")), 10) == 6);
REQUIRE(s.find_last_of(FString(cstr("o")), 1) == 1);
REQUIRE(s.find_last_of(FString(cstr("o")), 0) == npos);
REQUIRE(s.find_last_of(FString(EMPTY), 0) == npos);
REQUIRE(s.find_last_of(FString(EMPTY), 15) == npos);
REQUIRE(s.find_last_of(FString(cstr("pnv"))) == 7);
REQUIRE(s.find_last_of(FString(cstr("pnv")), 6) == 5);
REQUIRE(s.find_last_of(FString(cstr("pnv")), 4) == 2);
REQUIRE(s.find_last_of(FString(cstr("pnv")), 1) == npos);
}
SECTION("find_last_not_of") {
REQUIRE(s.find_last_not_of(FString(cstr("o"))) == 13);
REQUIRE(s.find_last_not_of(FString(cstr("d"))) == 12);
REQUIRE(s.find_last_not_of(FString(cstr("d")), 9999) == 12);
REQUIRE(s.find_last_not_of(FString(cstr("d")), 12) == 12);
REQUIRE(s.find_last_not_of(FString(cstr("o")), 12) == 11);
REQUIRE(s.find_last_not_of(FString(cstr("j")), 0) == npos);
REQUIRE(s.find_last_not_of(FString(EMPTY), 0) == 0);
REQUIRE(s.find_last_not_of(FString(EMPTY), 13) == 13);
REQUIRE(s.find_last_not_of(FString(EMPTY), 14) == 13);
REQUIRE(s.find_last_not_of(FString(EMPTY), 15) == 13);
REQUIRE(s.find_last_not_of(FString(cstr("nviy god"))) == 3);
REQUIRE(s.find_last_not_of(FString(cstr("jpa ")), 4) == 1);
REQUIRE(s.find_last_not_of(FString(cstr("jopa ")), 4) == npos);
}
}
static void test_reserve () {
get_allocs();
SECTION("literal") {
get_allocs();
String s(LITERAL);
SECTION(">len") {
s.reserve(100);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 100);
CHECK_ALLOCS(1, BUF_CHARS+100);
}
SECTION("<len") {
s.reserve(LITERAL_LEN-1);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, LITERAL_LEN);
CHECK_ALLOCS(1, BUF_CHARS+LITERAL_LEN);
}
SECTION("=0") {
s.reserve(0);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, LITERAL_LEN);
CHECK_ALLOCS(1, BUF_CHARS+LITERAL_LEN);
}
}
if (CHAR_SIZE == 1) {
SECTION("sso") {
get_allocs();
auto exp = mstr("hello");
SECTION("<= max sso") {
String s(exp.c_str());
s.reserve(0);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
CHECK_ALLOCS();
s.reserve(MAX_SSO_CHARS);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("> max sso") {
String s(exp.c_str());
s.reserve(MAX_SSO_CHARS+1);
REQUIRE_STR(s, exp, MAX_SSO_CHARS+1);
CHECK_ALLOCS(1, BUF_CHARS+MAX_SSO_CHARS+1);
}
SECTION("offset, <= capacity") {
String s((mstr("hi")+exp).c_str());
s.offset(2);
REQUIRE_STR(s, exp, MAX_SSO_CHARS-2);
s.reserve(MAX_SSO_CHARS-2);
REQUIRE_STR(s, exp, MAX_SSO_CHARS-2);
CHECK_ALLOCS();
}
SECTION("offset, > capacity, <= max sso") {
String s((mstr("hi")+exp).c_str());
s.offset(2);
REQUIRE_STR(s, exp, MAX_SSO_CHARS-2);
s.reserve(MAX_SSO_CHARS);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
CHECK_ALLOCS(); // string should has been moved to the beginning, no allocs
}
}
}
SECTION("internal") {
auto exp = mstr("abcde", 10);
String s(exp.c_str());
get_allocs();
SECTION("detach cow") {
String s2(s);
s.reserve(exp.size()-1);
REQUIRE_STR(s, exp);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+exp.size());
s = s2;
get_allocs();
s.offset(10, 30);
auto tmp = exp.substr(10, 30);
s.reserve(0);
REQUIRE_STR(s, tmp);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+tmp.size()); // detached with minimum required capacity
s = s2;
get_allocs();
s.offset(10, 30);
s.reserve(100);
REQUIRE_STR(s, tmp, 100);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+100);
}
SECTION("<= max capacity") {
s.reserve(0);
s.reserve(exp.size());
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
}
SECTION("> max capacity") {
s.reserve(exp.size()*2);
REQUIRE_STR(s, exp, exp.size()*2);
CHECK_ALLOCS(0,0,0,0,1,exp.size());
}
SECTION("offset, <= capacity") {
s.offset(10);
s.reserve(40);
REQUIRE_STR(s, exp.substr(10));
CHECK_ALLOCS();
}
SECTION("offset, > capacity, <= max capacity") {
s.offset(10);
s.reserve(50);
REQUIRE_STR(s, exp.substr(10), 50);
CHECK_ALLOCS(); // str has been moved to the beginning
}
SECTION("offset, > max capacity") {
s.offset(20);
s.reserve(70);
REQUIRE_STR(s, exp.substr(20), 70);
CHECK_ALLOCS(1, BUF_CHARS+70, 1, BUF_CHARS+50);
}
SECTION("reserve to sso") {
String s2(s);
s.offset(0, 2);
s.reserve(2);
REQUIRE_STR(s, exp.substr(0,2), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
}
SECTION("external") {
auto exp = mstr("abcde", 10);
String s = create_external<>(exp);
get_allocs();
SECTION("detach cow") {
String s2(s);
s.reserve(exp.size()-1);
REQUIRE_STR(s, exp);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+exp.size());
s = s2;
get_allocs();
s.offset(10, 30);
s.reserve(0);
auto tmp = exp.substr(10,30);
REQUIRE_STR(s, tmp);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+30); // detached with minimum required capacity
s = s2;
get_allocs();
s.offset(10, 30);
s.reserve(100);
REQUIRE_STR(s, tmp, 100);
REQUIRE(s.use_count() == 1); // detached
CHECK_ALLOCS(1, BUF_CHARS+100);
}
SECTION("<= max capacity") {
s.reserve(0);
s.reserve(exp.size());
REQUIRE_STR(s, exp);
CHECK_ALLOCS();
}
SECTION("> max capacity") {
s.reserve(exp.size()*2);
REQUIRE_STR(s, exp, exp.size()*2);
CHECK_ALLOCS(1, BUF_CHARS+exp.size()*2, 1, EBUF_CHARS, 0, 0, 1, exp.size());
}
SECTION("offset, <= capacity") {
s.offset(10);
s.reserve(40);
REQUIRE_STR(s, exp.substr(10));
CHECK_ALLOCS();
}
SECTION("offset, > capacity, <= max capacity") {
s.offset(10);
s.reserve(50);
REQUIRE_STR(s, exp.substr(10), 50);
CHECK_ALLOCS(); // str has been moved to the beginning
}
SECTION("offset, > max capacity") {
s.offset(20);
s.reserve(70);
REQUIRE_STR(s, exp.substr(20), 70);
CHECK_ALLOCS(1, BUF_CHARS+70, 1, EBUF_CHARS, 0,0, 1, exp.size());
}
SECTION("reserve to sso") {
String s2(s);
s.offset(0, 2);
s.reserve(2);
REQUIRE_STR(s, exp.substr(0, 2), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
}
}
static void test_resize () {
get_allocs();
SECTION("literal") {
String s(LITERAL);
SECTION("less") {
s.resize(1);
REQUIRE_STR(s, StdString(LITERAL, 1), 0);
CHECK_ALLOCS();
}
SECTION("more") {
s.resize(LITERAL_LEN+10);
REQUIRE_STR(s, StdString(LITERAL) + StdString(10, (T)'\0'));
CHECK_ALLOCS(1, BUF_CHARS + LITERAL_LEN + 10);
}
}
if (CHAR_SIZE == 1) {
SECTION("sso") {
auto exp = mstr("world");
String s(exp.c_str());
SECTION("less") {
s.resize(2);
REQUIRE_STR(s, mstr("wo"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("more") {
s.resize(7, (T)'!');
REQUIRE_STR(s, mstr("world!!"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
}
}
SECTION("internal") {
auto exp = mstr("a", 50);
String s(exp.c_str());
get_allocs();
SECTION("less") {
s.resize(10);
REQUIRE_STR(s, mstr("a", 10), exp.size());
CHECK_ALLOCS();
}
SECTION("more") {
s.resize(70, (T)'b');
REQUIRE_STR(s, exp + mstr("b", 20));
CHECK_ALLOCS(0,0,0,0,1,20);
}
}
SECTION("external") {
auto exp = mstr("a", 50);
String s = create_external<>(exp);
get_allocs();
SECTION("less") {
s.resize(10);
REQUIRE_STR(s, mstr("a", 10), exp.size());
CHECK_ALLOCS();
}
SECTION("more") {
s.offset(40);
s.resize(20, (T)'b');
REQUIRE_STR(s, mstr("a", 10) + mstr("b", 10), 50);
CHECK_ALLOCS(); // because offset has been eliminated
}
}
}
static void test_shrink_to_fit () {
get_allocs();
SECTION("literal") {
auto s = String(LITERAL).substr(2,5);
s.shrink_to_fit();
REQUIRE(s.capacity() == 0); // noop
CHECK_ALLOCS();
}
SECTION("sso") {
String s(cstr("ab"));
s.pop_back();
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("internal owner") {
String s(cstr("a", 50));
get_allocs();
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 50));
CHECK_ALLOCS();
s.offset(0, 40);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 40));
CHECK_ALLOCS(0,0,0,0,1,-10); //realloced
s.offset(10);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 30));
CHECK_ALLOCS(1,BUF_CHARS+30,1,BUF_CHARS+40); // dealloc+alloc
s.offset(0,2);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("aa"), MAX_SSO_CHARS); // shrinked to sso
CHECK_ALLOCS(0,0,1,BUF_CHARS+30);
}
SECTION("internal cow") {
String s(cstr("a", 50));
String tmp(s);
get_allocs();
s.offset(10, 30);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 30), 0, 40);
CHECK_ALLOCS();
s.offset(0, 2);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("aa"), MAX_SSO_CHARS); // shrinked to sso
CHECK_ALLOCS();
}
SECTION("external owner") {
String s = create_external<>(mstr("a", 50));
get_allocs();
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 50));
CHECK_ALLOCS();
s.offset(0, 40);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 40)); // shrinked
CHECK_ALLOCS(1, BUF_CHARS+40, 1, EBUF_CHARS, 0, 0, 1, 50); // moved to internal
s = create_external<>(mstr("a", 50));
get_allocs();
s.offset(0,2);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("aa"), MAX_SSO_CHARS); // shrinked to sso
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,50);
}
SECTION("external cow") {
String s = create_external<>(mstr("a", 50));
get_allocs();
String tmp(s);
s.offset(10, 30);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("a", 30), 0, 40);
CHECK_ALLOCS();
s = create_external<>(mstr("a", 50));
tmp = s;
get_allocs();
s.offset(0, 2);
s.shrink_to_fit();
REQUIRE_STR(s, mstr("aa"), MAX_SSO_CHARS); // shrinked to sso
CHECK_ALLOCS();
}
}
static void test_append () {
String s(cstr("abcd"));
SECTION("char") {
s.append(5, (T)'x');
REQUIRE_STRM(s, mstr("abcdxxxxx"));
}
SECTION("same class string") {
s.append(String(cstr("1234")));
REQUIRE_STRM(s, mstr("abcd1234"));
s.append(String(cstr("qwerty")), 3);
REQUIRE_STRM(s, mstr("abcd1234rty"));
s.append(String(cstr("hello world")), 5, 4);
REQUIRE_STRM(s, mstr("abcd1234rty wor"));
}
SECTION("foreign class string") {
s.append(String2(cstr("1234")));
REQUIRE_STRM(s, mstr("abcd1234"));
s.append(String2(cstr("qwerty")), 3);
REQUIRE_STRM(s, mstr("abcd1234rty"));
s.append(String2(cstr("hello world")), 5, 4);
REQUIRE_STRM(s, mstr("abcd1234rty wor"));
}
SECTION("ptr") {
s.append(cstr("1234"));
REQUIRE_STRM(s, mstr("abcd1234"));
s.append(cstr("qwerty"), 3);
REQUIRE_STRM(s, mstr("abcd1234qwe"));
}
SECTION("literal") {
s.append(LITERAL);
REQUIRE_STRM(s, mstr("abcd") + StdString(LITERAL));
}
SECTION("initializer list") {
s.append({'1', '2', '3', '4'});
REQUIRE_STRM(s, mstr("abcd1234"));
}
SECTION("string_view") {
s.append(svstr("1234"));
REQUIRE_STRM(s, mstr("abcd1234"));
}
SECTION("self append") {
s = cstr("12345678901234567890");
s.append(s);
REQUIRE_STRM(s, mstr("1234567890123456789012345678901234567890"));
s.append(s, 1, 6);
REQUIRE_STRM(s, mstr("1234567890123456789012345678901234567890234567"));
}
SECTION("preserve_allocated_when_empty_but_reserved") {
String s;
String s2(cstr("a", 50));
String s3(cstr("b", 10));
get_allocs();
s.reserve(100);
CHECK_ALLOCS(1, BUF_CHARS+100);
s.append(s2);
CHECK_ALLOCS();
s.append(s3);
CHECK_ALLOCS();
REQUIRE_STR(s, mstr("a",50)+mstr("b",10), 100);
}
SECTION("operator +=") {
String s(cstr("abcd"));
s += String(cstr("1234"));
REQUIRE_STRM(s, mstr("abcd1234"));
s += String2(cstr("qwerty"));
REQUIRE_STRM(s, mstr("abcd1234qwerty"));
s += cstr("hello world");
REQUIRE_STRM(s, mstr("abcd1234qwertyhello world"));
s += (T)'x';
REQUIRE_STRM(s, mstr("abcd1234qwertyhello worldx"));
s += s;
REQUIRE_STRM(s, mstr("abcd1234qwertyhello worldxabcd1234qwertyhello worldx"));
}
}
template <class FString>
static void test_op_plus () {
get_allocs();
auto lexp = mstr("x", 30);
auto rexp = mstr("y", 40);
String empty;
SECTION("str-str") {
String lhs(lexp.c_str());
FString rhs(rexp.c_str());
get_allocs();
String s = lhs + rhs;
REQUIRE_STR(s, lexp+rexp);
REQUIRE(lhs.use_count() == 1); REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1); // no cows
CHECK_ALLOCS(1, BUF_CHARS+70);
s = EMPTY; get_allocs();
s = lhs + empty;
REQUIRE(lhs.use_count() == 2); REQUIRE(s.use_count() == 2); // cow
REQUIRE_STR(s, lexp, 0, lexp.size());
CHECK_ALLOCS();
s = EMPTY;
s = empty + rhs;
REQUIRE(rhs.use_count() == 2); REQUIRE(s.use_count() == 2); // cow
REQUIRE_STR(s, rexp, 0, rexp.size());
CHECK_ALLOCS();
}
SECTION("ptr-str") {
auto lhs = lexp.c_str();
String rhs(rexp.c_str());
get_allocs();
String s = lhs + rhs;
REQUIRE_STR(s, lexp+rexp);
REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1);
CHECK_ALLOCS(1, BUF_CHARS+lexp.size()+rexp.size());
s = EMPTY; get_allocs();
s = lhs + empty;
REQUIRE_STR(s, lexp);
CHECK_ALLOCS(1, BUF_CHARS+lexp.size());
REQUIRE(s.use_count() == 1);
s = EMPTY; get_allocs();
s = cstr("") + rhs;
REQUIRE_STR(s, rexp, 0, rexp.size());
REQUIRE(rhs.use_count() == 2); REQUIRE(s.use_count() == 2);
CHECK_ALLOCS();
}
SECTION("char-str") {
T lhs = (T)'x';
String rhs(rexp.c_str());
get_allocs();
String s = lhs + rhs;
REQUIRE_STR(s, lhs+rexp);
REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1);
CHECK_ALLOCS(1, BUF_CHARS + rexp.size() + 1);
s = EMPTY; get_allocs();
s = lhs + empty;
REQUIRE_STR(s, mstr("x"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("str-ptr") {
String lhs(lexp.c_str());
auto rhs = rexp.c_str();
get_allocs();
String s = lhs + rhs;
REQUIRE_STR(s, lexp+rexp);
REQUIRE(lhs.use_count() == 1); REQUIRE(s.use_count() == 1);
CHECK_ALLOCS(1, BUF_CHARS + lexp.size() + rexp.size());
s = EMPTY; get_allocs();
s = empty + rhs;
REQUIRE_STR(s, rexp);
REQUIRE(s.use_count() == 1);
s = EMPTY; get_allocs();
s = lhs + cstr("");
REQUIRE_STR(s, lexp, 0, lexp.size());
REQUIRE(lhs.use_count() == 2); REQUIRE(s.use_count() == 2);
CHECK_ALLOCS();
}
SECTION("str-char") {
String lhs(lexp.c_str());
T rhs = (T)'y';
get_allocs();
String s = lhs + rhs;
REQUIRE_STR(s, lexp+rhs);
REQUIRE(lhs.use_count() == 1); REQUIRE(s.use_count() == 1);
CHECK_ALLOCS(1, BUF_CHARS + lhs.size() + 1);
s = EMPTY; get_allocs();
s = empty + rhs;
REQUIRE_STR(s, mstr("y"), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("mstr-str") {
String lhs(lexp.c_str());
FString rhs(rexp.c_str());
lhs.reserve(200);
get_allocs();
String s = std::move(lhs) + rhs;
REQUIRE_STR(s, lexp+rexp, 200);
REQUIRE(lhs.use_count() == 1); REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1); // no cows
REQUIRE(!lhs); // lhs moved
REQUIRE_STR(rhs, rexp);
CHECK_ALLOCS();
}
SECTION("str-mstr") {
String lhs(lexp.c_str());
FString rhs(rexp.c_str());
rhs.reserve(250);
get_allocs();
String s = lhs + std::move(rhs);
REQUIRE_STR(s, lexp+rexp, 250);
REQUIRE(lhs.use_count() == 1); REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1); // no cows
REQUIRE_STR(lhs, lexp);
REQUIRE(!rhs);
CHECK_ALLOCS();
}
SECTION("mstr-mstr") { // for now it's just lhs.append(rhs), i.e. the same as mstr-str
String lhs(lexp.c_str());
FString rhs(rexp.c_str());
lhs.reserve(150);
get_allocs();
String s = std::move(lhs) + std::move(rhs);
REQUIRE_STR(s, lexp+rexp, 150);
REQUIRE(lhs.use_count() == 1); REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1); // no cows
REQUIRE(!lhs); // lhs moved
REQUIRE_STR(rhs, rexp);
CHECK_ALLOCS();
}
SECTION("ptr-mstr") {
auto lhs = lexp.c_str();
String rhs(rexp.c_str());
rhs.reserve(100);
get_allocs();
String s = lhs + std::move(rhs);
REQUIRE_STR(s, lexp+rexp, 100);
REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1);
REQUIRE(!rhs);
CHECK_ALLOCS();
}
SECTION("char-mstr") {
T lhs = (T)'x';
String rhs(rexp.c_str());
rhs.reserve(120);
get_allocs();
String s = lhs + std::move(rhs);
REQUIRE_STR(s, lhs+rexp, 120);
REQUIRE(rhs.use_count() == 1); REQUIRE(s.use_count() == 1);
REQUIRE(!rhs);
CHECK_ALLOCS();
}
SECTION("mstr-ptr") {
String lhs(lexp.c_str());
auto rhs = rexp.c_str();
lhs.reserve(300);
get_allocs();
String s = std::move(lhs) + rhs;
REQUIRE_STR(s, lexp+rexp, 300);
REQUIRE(lhs.use_count() == 1); REQUIRE(s.use_count() == 1);
REQUIRE(!lhs);
CHECK_ALLOCS();
}
SECTION("mstr-char") {
String lhs(lexp.c_str());
T rhs = (T)'y';
lhs.reserve(400);
get_allocs();
String s = std::move(lhs) + rhs;
REQUIRE_STR(s, lexp+rhs, 400);
REQUIRE(lhs.use_count() == 1); REQUIRE(s.use_count() == 1);
REQUIRE(!lhs);
CHECK_ALLOCS();
}
}
static void test_insert_impl (int cnt, bool is_external) {
auto exp = mstr("a", cnt);
String s = is_external ? create_external<>(exp) : String(exp.c_str());
get_allocs();
SECTION("end") {
SECTION("has end space") {
s.length(s.length() - 6);
s.insert(s.length(), cstr(" world"));
REQUIRE_STR(s, mstr("a", cnt-6)+mstr(" world"), cnt);
CHECK_ALLOCS();
}
SECTION("has head space") {
s.offset(cnt - 3);
s.insert(s.length(), cstr(" world"));
REQUIRE_STR(s, mstr("aaa world"), cnt);
CHECK_ALLOCS();
}
SECTION("has both space") {
s.offset(4, 3);
REQUIRE(s.capacity() >= 7);
s.insert(s.length(), cstr(" XX"));
REQUIRE_STR(s, mstr("aaa XX"), cnt-4);
CHECK_ALLOCS();
}
SECTION("has summary space") {
s.offset(4, cnt-8); // 4 free from head and tail
s.insert(s.length(), cstr("world")); // 5 inserted
REQUIRE_STR(s, mstr("a", cnt-8)+mstr("world"), cnt); // moved to the beginning
CHECK_ALLOCS();
}
SECTION("has no space") {
s.offset(2, cnt-4); // 2 free from head and tail
s.insert(s.length(), cstr("world")); // 5 inserted
REQUIRE_STR<true>(s, mstr("a", cnt-4)+mstr("world"), cnt+1); // moved to the beginning
auto stat = get_allocs();
REQUIRE(stat.allocated_cnt == 1);
REQUIRE(stat.allocated == (int)(BUF_CHARS+(cnt+1)*GROW_RATE));
}
}
SECTION("begin") {
SECTION("has end space") {
s.length(s.length() - 6);
s.insert(0, cstr("world "));
REQUIRE_STR(s, mstr("world ")+mstr("a",cnt-6), cnt);
CHECK_ALLOCS();
}
SECTION("has head space") {
s.offset(cnt - 3);
s.insert(0, cstr("world "));
REQUIRE_STR(s, mstr("world aaa"), 9);
CHECK_ALLOCS();
}
SECTION("has both space") {
s.offset(3, 4);
REQUIRE(s.capacity() >= 8);
s.insert(0, cstr("X "));
REQUIRE_STR(s, mstr("X aaaa"), cnt-1);
CHECK_ALLOCS();
}
SECTION("has summary space") {
s.offset(4, cnt-8); // 4 free from head and tail
s.insert(0, cstr("world")); // 5 insterted
REQUIRE_STR(s, mstr("world")+mstr("a",cnt-8), cnt); // moved to the beginning
CHECK_ALLOCS();
}
SECTION("has no space") {
s.offset(2, cnt-4); // 2 free from head and tail
s.insert(0, cstr("world")); // 5 insterted
REQUIRE_STR(s, mstr("world")+mstr("a",cnt-4), cnt+1); // moved to the beginning
auto stat = get_allocs();
REQUIRE(stat.allocated_cnt == 1);
REQUIRE(stat.allocated == (int)BUF_CHARS+cnt+1);
}
};
SECTION("middle") {
SECTION("has end space") {
s.length(s.length() - 6);
s.insert(2, cstr("world "));
REQUIRE_STR(s, mstr("aaworld ")+mstr("a",cnt-8), cnt);
CHECK_ALLOCS();
}
SECTION("has head space") {
s.offset(cnt - 3);
s.insert(2, cstr("world "));
REQUIRE_STR(s, mstr("aaworld a"), 9);
CHECK_ALLOCS();
}
SECTION("has both space, head is shorter") {
s.offset(4, 3);
REQUIRE(s.capacity() >= 7);
s.insert(1, cstr(" X ")); // head is moved (3 bytes left)
REQUIRE_STR(s, mstr("a X aa"), cnt-1);
CHECK_ALLOCS();
}
SECTION("has both space, tail is shorter") {
s.offset(4, 3);
s.insert(2, cstr(" X ")); // tail is moved (3 bytes right)
REQUIRE_STR(s, mstr("aa X a"), cnt-4);
CHECK_ALLOCS();
}
SECTION("has summary space") {
s.offset(4, cnt-8); // 4 free from head and tail
s.insert(2, cstr("world")); // 5 insterted
REQUIRE_STR(s, mstr("aaworld")+mstr("a",cnt-10), cnt); // moved to the beginning
CHECK_ALLOCS();
}
SECTION("has no space") {
s.offset(2, cnt-4); // 2 free from head and tail
s.insert(2, cstr("world")); // 5 insterted
REQUIRE_STR(s, mstr("aaworld")+mstr("a",cnt-6), cnt+1); // moved to the beginning
auto stat = get_allocs();
REQUIRE(stat.allocated_cnt == 1);
REQUIRE(stat.allocated == (int)BUF_CHARS+cnt+1);
}
};
}
static void test_insert_cow (bool is_external) {
auto exp = mstr("a", 50);
String s = is_external ? create_external<>(exp) : String(exp.c_str());
get_allocs();
String tmp(s);
SECTION("end") {
s.length(s.length() - 10);
s.insert(s.length(), cstr("hello"));
REQUIRE_STR<true>(s, mstr("a", 40)+mstr("hello"), 45);
CHECK_ALLOCS(1, BUF_CHARS+45*GROW_RATE);
};
SECTION("begin") {
s.offset(10, 30);
s.insert(0, cstr("hello"));
REQUIRE_STR(s, mstr("hello")+mstr("a",30), 35);
CHECK_ALLOCS(1, BUF_CHARS+35);
};
SECTION("middle") {
s.offset(10, 30);
s.insert(5, cstr("hello"));
REQUIRE_STR(s, mstr("aaaaahello")+mstr("a",25), 35);
CHECK_ALLOCS(1, BUF_CHARS+35);
};
}
static void test_insert () {
get_allocs();
SECTION("literal") {
get_allocs();
SECTION("end") {
auto exp = mstr(" hello");
String s(LITERAL);
s.insert(s.length(), exp.c_str());
REQUIRE_STR<true>(s, StdString(LITERAL)+exp);
CHECK_ALLOCS(1, BUF_CHARS + (LITERAL_LEN + exp.size())*GROW_RATE);
}
SECTION("begin") {
auto exp = mstr("hello ");
String s(LITERAL);
s.insert(0, exp.c_str());
REQUIRE_STR(s, exp + StdString(LITERAL));
CHECK_ALLOCS(1, BUF_CHARS + LITERAL_LEN + exp.size());
}
SECTION("middle") {
auto exp = mstr("epta");
String s(LITERAL);
s.insert(5, exp.c_str());
auto tmp = StdString(LITERAL);
tmp.insert(5, exp);
REQUIRE_STR(s, tmp);
CHECK_ALLOCS(1, BUF_CHARS+tmp.size());
}
}
if (CHAR_SIZE == 1) SECTION("sso") { test_insert_impl(MAX_SSO_CHARS, false); }
SECTION("internal") { test_insert_impl(50, false); }
SECTION("external") { test_insert_impl(50, true); }
String s (cstr("hello, world"));
String a (cstr(" suka"));
String2 a2(cstr(" suka"));
SECTION("str") {
s.insert(5, a);
REQUIRE_STRM(s, mstr("hello suka, world"));
REQUIRE_THROWS(s.insert(1000, a));
}
SECTION("fstr") {
s.insert(6, a2);
REQUIRE_STRM(s, mstr("hello, suka world"));
}
SECTION("str&pos") {
s.insert(5, a, 2);
REQUIRE_STRM(s, mstr("hellouka, world"));
}
SECTION("str&pos/len") {
s.insert(5, a, 2, 2);
REQUIRE_STRM(s, mstr("hellouk, world"));
}
SECTION("fstr&pos/len") {
s.insert(6, a2, 1, 2);
REQUIRE_STRM(s, mstr("hello,su world"));
}
SECTION("ptr") {
s.insert(10, cstr("123"));
REQUIRE_STRM(s, mstr("hello, wor123ld"));
}
SECTION("ptr&len") {
s.insert(10, cstr("123"), 2);
REQUIRE_STRM(s, mstr("hello, wor12ld"));
}
SECTION("=literal=") {
s.insert(7, LITERAL);
REQUIRE_STRM(s, mstr("hello, ")+StdString(LITERAL)+mstr("world"));
}
SECTION("count char") {
s.insert(2, 5, (T)'x');
REQUIRE_STRM(s, mstr("hexxxxxllo, world"));
}
SECTION("iterator count char") {
auto it = s.insert(s.cbegin()+3, 5, (T)'y');
REQUIRE(*it == (T)'y');
REQUIRE(*(it-1) == (T)'l');
REQUIRE_STRM(s, mstr("helyyyyylo, world"));
}
SECTION("iterator char") {
auto it = s.insert(s.cbegin()+5, (T)'z');
REQUIRE(*it == (T)'z');
REQUIRE(*(it-1) == (T)'o');
REQUIRE_STRM(s, mstr("helloz, world"));
}
SECTION("initializer list") {
s.insert(s.cbegin()+1, {(T)'h', (T)'i'});
REQUIRE_STRM(s, mstr("hhiello, world"));
}
SECTION("string_view") {
s.insert(9, svstr("x", 5));
REQUIRE_STRM(s, mstr("hello, woxxxxxrld"));
}
SECTION("internal cow") { test_insert_cow(false); }
SECTION("external cow") { test_insert_cow(true); }
SECTION("self") {
s = cstr("a", 20);
s.insert(10, s);
REQUIRE_STRM(s, mstr("a", 40));
}
SECTION("self&pos/len") {
s = cstr("a", 20);
s.insert(10, s, 5, 10);
REQUIRE_STRM(s, mstr("a", 30));
}
}
// all tests should keep in mind that cnt could be 11 bytes on 32bit
static void test_replace_impl (int cnt, bool is_external) {
auto exp = mstr("a", cnt);
String s = is_external ? create_external<>(exp) : String(exp.c_str());
get_allocs();
SECTION("shrink") {
s.replace(3, 4, cstr("hi"));
REQUIRE_STR(s, mstr("a",3)+mstr("hi")+mstr("a",cnt-7), cnt);
CHECK_ALLOCS();
s.replace(0, 4, EMPTY);
REQUIRE_STR(s, mstr("i")+mstr("a",cnt-7), cnt-4);
CHECK_ALLOCS();
}
SECTION("grow") {
SECTION("has end space") {
s.length(s.length() - 3);
s.replace(3, 3, cstr("world "));
REQUIRE_STR(s, mstr("aaaworld ")+mstr("a",cnt-9), cnt);
CHECK_ALLOCS();
};
SECTION("has head space") {
s.offset(4);
s.replace(2, 2, cstr(" XX "));
REQUIRE_STR(s, mstr("aa XX ")+mstr("a",cnt-8), cnt-2);
CHECK_ALLOCS();
};
SECTION("has both space, head is shorter") {
s.offset(3, 4);
REQUIRE(s.capacity() >= 7);
s.replace(1,2, cstr(" XX ")); // head is moved
REQUIRE_STR(s, mstr("a XX a"), cnt-1);
CHECK_ALLOCS();
};
SECTION("has both space, tail is shorter") {
s.offset(3, 4);
s.replace(2,1, cstr(" XX ")); // tail is moved
REQUIRE_STR(s, mstr("aa XX a"), cnt-3);
CHECK_ALLOCS();
};
SECTION("has summary space") {
s.offset(3, cnt-6); // 3 free from head and tail
s.replace(2,2, cstr("XXXXXXX")); // 5 inserted
REQUIRE_STR(s, mstr("aaXXXXXXX")+mstr("a",cnt-10), cnt); // moved to the beginning
CHECK_ALLOCS();
};
SECTION("has no space") {
s.offset(2, cnt-4); // 2 free from head and tail
s.replace(3,2, cstr("XXXXXXX")); // 5 insterted
REQUIRE_STR(s, mstr("aaaXXXXXXX")+mstr("a",cnt-9), cnt+1); // moved to the beginning
auto stat = get_allocs();
REQUIRE(stat.allocated_cnt == 1);
REQUIRE(stat.allocated == (int)BUF_CHARS+cnt+1);
};
}
}
static void test_replace () {
SECTION("literal") {
String s (LITERAL);
StdString exp(LITERAL);
SECTION("shrink") {
exp.replace(5, 5, cstr("hi"));
s.replace(5, 5, cstr("hi"));
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, BUF_CHARS + exp.size());
}
SECTION("grow") {
exp.replace(5, 10, cstr("epta"));
s.replace(5, 10, cstr("epta"));
REQUIRE_STR(s, exp);
CHECK_ALLOCS(1, BUF_CHARS + exp.size());
}
}
if (CHAR_SIZE == 1) SECTION("sso") { test_replace_impl(MAX_SSO_CHARS, false); }
SECTION("internal") { test_replace_impl(50, false); }
SECTION("external") { test_replace_impl(50, true); }
String s (cstr("hello, world"));
String a (cstr(" suka"));
String2 a2(cstr(" suka"));
SECTION("str") {
s.replace(6, 6, a);
REQUIRE_STRM(s, mstr("hello, suka"));
s.replace(5, 1000, a);
REQUIRE_STRM(s, mstr("hello suka"));
REQUIRE_THROWS(s.replace(1000, 1, a));
}
SECTION("fstr") {
s.replace(1, String::npos, a2);
REQUIRE_STRM(s, mstr("h suka"));
}
SECTION("it/str") {
s.replace(s.cbegin()+6, s.cbegin()+12, a);
REQUIRE_STRM(s, mstr("hello, suka"));
s.replace(s.cbegin()+5, s.cend()+100, a);
REQUIRE_STRM(s, mstr("hello suka"));
REQUIRE_THROWS(s.replace(s.cend()+1, s.end(), a));
}
SECTION("it/fstr") {
s.replace(s.cbegin()+1, s.cend(), a2);
REQUIRE_STRM(s, mstr("h suka"));
}
SECTION("str&pos/len") {
s.replace(6, 6, a, 1, 2);
REQUIRE_STRM(s, mstr("hello,su"));
s.replace(6, 6, a, 1, 20);
REQUIRE_STRM(s, mstr("hello,suka"));
s.replace(6, 6, a, 1);
REQUIRE_STRM(s, mstr("hello,suka"));
REQUIRE_THROWS(s.replace(6,6,a,10));
}
SECTION("fstr&pos/len") {
s.replace(6, 6, a2, 1, 2);
REQUIRE_STRM(s, mstr("hello,su"));
}
SECTION("ptr") {
s.replace(6, 6, cstr(" guy"));
REQUIRE_STRM(s, mstr("hello, guy"));
}
SECTION("ptr&len") {
s.replace(6, 6, cstr(" guy"), 3);
REQUIRE_STRM(s, mstr("hello, gu"));
}
SECTION("=literal=") {
s.replace(6,6, LITERAL);
REQUIRE_STRM(s, mstr("hello,")+StdString(LITERAL));
}
SECTION("it/ptr") {
s.replace(s.cbegin()+6, s.cbegin()+12, cstr(" guy"));
REQUIRE_STRM(s, mstr("hello, guy"));
}
SECTION("it/ptr&len") {
s.replace(s.cbegin()+6, s.cbegin()+12, cstr(" guy"), 3);
REQUIRE_STRM(s, mstr("hello, gu"));
}
SECTION("it/=literal=") {
s.replace(s.cbegin()+6, s.cbegin()+12, LITERAL);
REQUIRE_STRM(s, mstr("hello,")+StdString(LITERAL));
}
SECTION("count char") {
s.replace(6, 5, 10, (T)'x');
REQUIRE_STRM(s, mstr("hello,xxxxxxxxxxd"));
}
SECTION("it/count char") {
s.replace(s.cbegin()+6, s.cbegin()+11, 10, (T)'x');
REQUIRE_STRM(s, mstr("hello,xxxxxxxxxxd"));
}
SECTION("initializer list") {
s.replace(s.cbegin()+6, s.cbegin()+11, {(T)'h', (T)'i'});
REQUIRE_STRM(s, mstr("hello,hid"));
}
SECTION("string_view") {
s.replace(6, 6, svstr(" guy"));
REQUIRE_STRM(s, mstr("hello, guy"));
}
SECTION("it/string_view") {
s.replace(s.cbegin()+6, s.cbegin()+12, svstr(" guy"));
REQUIRE_STRM(s, mstr("hello, guy"));
}
SECTION("self") {
s = cstr("1234567890", 2);
s.replace(3, 5, s);
REQUIRE_STRM(s, mstr("123")+mstr("1234567890",2)+mstr("90")+mstr("1234567890"));
}
SECTION("self&pos/len") {
s = cstr("1234567890", 2);
s.replace(3, 5, s, 2, 15);
REQUIRE_STRM(s, mstr("123")+mstr("345678901234567")+mstr("90")+mstr("1234567890"));
}
SECTION("it/self") {
s = cstr("1234567890", 2);
s.replace(s.cbegin()+3, s.cbegin()+8, s);
REQUIRE_STRM(s, mstr("123")+mstr("1234567890",2)+mstr("90")+mstr("1234567890"));
}
}
static void test_shared_detach () {
get_allocs();
SECTION("literal") {
auto llen = LITERAL_LEN;
String s(LITERAL);
s.shared_buf();
REQUIRE(s.capacity() == llen); // detached
CHECK_ALLOCS(1, BUF_CHARS + LITERAL_LEN);
}
SECTION("sso") {
auto msc = MAX_SSO_CHARS;
String s(cstr("ab"));
s.shared_buf();
REQUIRE(s.capacity() == msc); // noop
CHECK_ALLOCS();
}
SECTION("internal") {
String s(cstr("a", 50));
get_allocs();
s.offset(0, 10);
s.shared_buf();
REQUIRE(s.capacity() == 50); // noop
CHECK_ALLOCS();
String tmp(s);
s.shared_buf();
REQUIRE(s.shared_capacity() == 50); // noop
CHECK_ALLOCS();
}
SECTION("external") {
String s = create_external(mstr("a",50));
get_allocs();
s.offset(0, 10);
s.shared_buf();
REQUIRE(s.capacity() == 50); // noop
CHECK_ALLOCS();
String tmp(s);
s.shared_buf();
REQUIRE(s.shared_capacity() == 50); // noop
CHECK_ALLOCS();
}
}
static void test_to_from_number () {
if (CHAR_SIZE > 1) return;
SECTION("to number") {
string s(" 1020asd");
uint64_t val;
auto res = s.to_number(val);
REQUIRE(!res.ec);
REQUIRE(val == 1020);
res = s.to_number(val, (size_t)3);
REQUIRE(!res.ec);
REQUIRE(val == 20);
res = s.to_number(val, 4, 1);
REQUIRE(!res.ec);
REQUIRE(val == 2);
res = s.to_number(val, 0, 999, 16);
REQUIRE(!res.ec);
REQUIRE(val == 66058);
res = s.to_number(val, 2, 9, 2);
REQUIRE(!res.ec);
REQUIRE(val == 2);
res = s.to_number(val, (size_t)5);
REQUIRE(!res.ec);
REQUIRE(val == 0);
REQUIRE(s.to_number(val, (size_t)6).ec);
};
SECTION("from number") {
string s;
REQUIRE(string::from_number(10) == "10");
REQUIRE(string::from_number(10,8) == "12");
REQUIRE(string::from_number(10, 16) == "a");
CHECK_ALLOCS();
};
}
static void test_foreign_allocator () {
SECTION("from literal") {
String2 src(LITERAL);
String s(src);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
s = EMPTY; src = EMPTY;
CHECK_ALLOCS();
};
SECTION("from sso") {
auto exp = mstr("ab");
String2 src(exp.c_str());
String s(src);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
s = EMPTY; src = EMPTY;
CHECK_ALLOCS();
};
SECTION("from internal") {
auto exp = mstr("a", 50);
String2 src(exp.c_str());
CHECK_ALLOCS(1, BUF_CHARS+exp.size());
String s(src);
CHECK_ALLOCS();
REQUIRE_STR(s, exp, 0, exp.size());
src = EMPTY;
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,1,BUF_CHARS+exp.size());
};
SECTION("from external") {
auto exp = mstr("b", 50);
String2 src = create_external<String2>(exp);
CHECK_ALLOCS(1, EBUF_CHARS);
String s(src);
CHECK_ALLOCS();
REQUIRE_STR(s, exp, 0, exp.size());
src = EMPTY;
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,1,EBUF_CHARS,0,0,1,exp.size());
};
SECTION("from external with custom buf") {
auto exp = mstr("b", 50);
String2 src = create_external_cbuf<String2>(exp);
String s(src);
CHECK_ALLOCS();
REQUIRE_STR(s, exp, 0, exp.size());
src = EMPTY;
CHECK_ALLOCS();
s = EMPTY;
CHECK_ALLOCS(0,0,0,0,0,0,1,exp.size(),1);
};
}
static void test_cstr () {
SECTION("empty") {
String s;
REQUIRE(s.c_str()[0] == 0);
REQUIRE_STR(s, EMPTY);
CHECK_ALLOCS();
}
SECTION("literal") {
String s(LITERAL);
REQUIRE(s.c_str()[LITERAL_LEN] == 0);
REQUIRE_STR(s, LITERAL, LITERAL_LEN, 0);
CHECK_ALLOCS();
}
SECTION("sso") {
SECTION("self") {
auto exp = mstr("a", MAX_SSO_CHARS-1);
String s(exp.c_str());
REQUIRE(s.c_str()[exp.length()] == 0);
REQUIRE_STR(s, exp, MAX_SSO_CHARS);
CHECK_ALLOCS();
}
SECTION("detach") {
auto exp = mstr("a", 50);
String src(exp.c_str());
get_allocs();
String s = src.substr(0, MAX_SSO_CHARS-1);
CHECK(s.c_str()[MAX_SSO_CHARS-1] == 0);
REQUIRE_STR(s, mstr("a", MAX_SSO_CHARS-1), MAX_SSO_CHARS);
CHECK_ALLOCS();
}
}
SECTION("internal") {
SECTION("self") {
auto exp = mstr("ab", 50);
String s(exp.c_str());
get_allocs();
REQUIRE(s.c_str()[exp.length()] == 0);
REQUIRE_STR(s, exp, exp.length() + 1);
CHECK_ALLOCS(0,0,0,0,1,1);
s.c_str();
CHECK_ALLOCS();
}
SECTION("detach") {
auto exp = mstr("ab", 50);
String s(exp.c_str());
get_allocs();
String s2(s);
REQUIRE(s.c_str()[exp.length()] == 0);
REQUIRE_STR(s, exp, exp.length() + 1);
CHECK_ALLOCS(1,BUF_CHARS+exp.size()+1,0,0,0,0);
s.c_str();
CHECK_ALLOCS();
}
}
}
static void run () {
get_allocs();
SECTION("ctor") { test_ctor(); }
SECTION("copy ctor") {
SECTION("local") { test_copy_ctor<String>(); }
SECTION("foreign") { test_copy_ctor<String2>(); }
}
SECTION("move ctor") {
SECTION("local") { test_move_ctor<String>(); }
SECTION("foreign") { test_move_ctor<String2>(); }
}
SECTION("copy ctor with offset") {
SECTION("local") { test_copy_ctor_offset<String>(); }
SECTION("foreign") { test_copy_ctor_offset<String2>(); }
}
SECTION("substr") { test_substr(); }
SECTION("clear") { test_clear(); }
SECTION("assign") {
SECTION("local") { test_assign<String>(); }
SECTION("foreign") { test_assign<String2>(); }
}
SECTION("offset") { test_offset(); }
SECTION("swap") {
SECTION("local") { test_swap<String>(); }
SECTION("foreign") { test_swap<String2>(); }
}
SECTION("copy") { test_copy(); }
SECTION("to_bool, empty") { test_bool_empty(); }
SECTION("use_count") { test_use_count(); }
SECTION("detach") { test_detach(); }
SECTION("at/op[]/front/[pop_]back") { test_at_front_back(); }
SECTION("iterator") { test_iterator(); }
SECTION("erase") { test_erase(); }
SECTION("compare") {
SECTION("local") { test_compare<String>(); }
SECTION("foreign") { test_compare<String2>(); }
}
SECTION("find") {
SECTION("local") { test_find<String>(); }
SECTION("foreign") { test_find<String2>(); }
}
SECTION("reserve") { test_reserve(); }
SECTION("resize") { test_resize(); }
SECTION("shrink_to_fit") { test_shrink_to_fit(); }
SECTION("append") { test_append(); }
SECTION("operator +") {
SECTION("local") { test_op_plus<String>(); }
SECTION("foreign") { test_op_plus<String2>(); }
}
SECTION("insert") { test_insert(); }
SECTION("replace") { test_replace(); }
SECTION("shared_detach") { test_shared_detach(); }
SECTION("to/from_number") { test_to_from_number(); }
SECTION("from foreign allocator") { test_foreign_allocator(); }
SECTION("c_str") { test_cstr(); }
}
};
template <class T> const T test_string<T>::LITERAL[38] = {'h','e','l','l','o',' ','w','o','r','l','d',',',' ','t','h','i','s',' ','i','s',' ','a',' ','l','i','t','e','r','a','l',' ','s','t','r','i','n','g',0};
template <class T> const T test_string<T>::EMPTY[1] = {0};
}