aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/string.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore/string.cpp')
-rw-r--r--src/zencore/string.cpp144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/zencore/string.cpp b/src/zencore/string.cpp
index 4072aec56..8489841f8 100644
--- a/src/zencore/string.cpp
+++ b/src/zencore/string.cpp
@@ -1487,6 +1487,150 @@ TEST_CASE("CompactString.implicit_conversion")
CHECK(V == std::string_view("view"));
}
+TEST_CASE("SharedString.default")
+{
+ SharedString S;
+ CHECK(S.IsEmpty());
+ CHECK(S.Size() == 0);
+ CHECK(S.ToView() == std::string_view());
+ CHECK(S.c_str()[0] == '\0');
+ CHECK(S.UseCount() == 0);
+}
+
+TEST_CASE("SharedString.empty")
+{
+ // Empty input skips allocation: empty == default state.
+ SharedString S(std::string_view(""));
+ CHECK(S.IsEmpty());
+ CHECK(S.Size() == 0);
+ CHECK(S.UseCount() == 0);
+}
+
+TEST_CASE("SharedString.short")
+{
+ SharedString S(std::string_view("hello"));
+ CHECK(!S.IsEmpty());
+ CHECK(S.Size() == 5);
+ CHECK(S.ToView() == std::string_view("hello"));
+ CHECK(S.UseCount() == 1);
+}
+
+TEST_CASE("SharedString.sentinel_boundary")
+{
+ // 254 chars — largest value that fits in the prefix byte
+ std::string Str254(254, 'x');
+ std::string_view View254(Str254);
+ SharedString S(View254);
+ CHECK(S.Size() == 254);
+ CHECK(S.ToView() == View254);
+}
+
+TEST_CASE("SharedString.sentinel_exact")
+{
+ // 255 chars — hits the 0xFF sentinel, falls back to strlen
+ std::string Str255(255, 'y');
+ std::string_view View255(Str255);
+ SharedString S(View255);
+ CHECK(S.Size() == 255);
+ CHECK(S.ToView() == View255);
+}
+
+TEST_CASE("SharedString.long")
+{
+ std::string Str512(512, 'z');
+ std::string_view View512(Str512);
+ SharedString S(View512);
+ CHECK(S.Size() == 512);
+ CHECK(S.ToView() == View512);
+}
+
+TEST_CASE("SharedString.move")
+{
+ SharedString A(std::string_view("test"));
+ SharedString B(std::move(A));
+ CHECK(A.IsEmpty());
+ CHECK(B.ToView() == std::string_view("test"));
+ CHECK(B.UseCount() == 1);
+
+ SharedString C(std::string_view("first"));
+ SharedString D(std::string_view("second"));
+ D = std::move(C);
+ CHECK(C.IsEmpty());
+ CHECK(D.ToView() == std::string_view("first"));
+ CHECK(D.UseCount() == 1);
+}
+
+TEST_CASE("SharedString.copy_shares_buffer")
+{
+ SharedString A(std::string_view("shared"));
+ CHECK(A.UseCount() == 1);
+
+ SharedString B(A);
+ CHECK(A.UseCount() == 2);
+ CHECK(B.UseCount() == 2);
+ CHECK(A.ToView() == std::string_view("shared"));
+ CHECK(B.ToView() == std::string_view("shared"));
+ // Both views must point at the same underlying bytes.
+ CHECK(A.c_str() == B.c_str());
+
+ {
+ SharedString C = A;
+ CHECK(A.UseCount() == 3);
+ CHECK(C.c_str() == A.c_str());
+ }
+ // C has gone out of scope; refcount drops back.
+ CHECK(A.UseCount() == 2);
+}
+
+TEST_CASE("SharedString.copy_assign_releases_old")
+{
+ SharedString A(std::string_view("alpha"));
+ SharedString B(std::string_view("beta"));
+ CHECK(A.UseCount() == 1);
+ CHECK(B.UseCount() == 1);
+
+ B = A;
+ // B's old buffer is released; both now share A's buffer.
+ CHECK(A.UseCount() == 2);
+ CHECK(B.UseCount() == 2);
+ CHECK(B.ToView() == std::string_view("alpha"));
+ CHECK(A.c_str() == B.c_str());
+}
+
+TEST_CASE("SharedString.self_assign")
+{
+ SharedString A(std::string_view("self"));
+ CHECK(A.UseCount() == 1);
+
+ // Self copy-assignment must not release the buffer or change refcount.
+ SharedString& Aref = A;
+ A = Aref;
+ CHECK(A.UseCount() == 1);
+ CHECK(A.ToView() == std::string_view("self"));
+
+ // Self move-assignment must also be safe.
+ A = std::move(Aref);
+ CHECK(A.UseCount() == 1);
+ CHECK(A.ToView() == std::string_view("self"));
+}
+
+TEST_CASE("SharedString.empty_copy")
+{
+ SharedString A;
+ SharedString B(A);
+ CHECK(A.IsEmpty());
+ CHECK(B.IsEmpty());
+ CHECK(A.UseCount() == 0);
+ CHECK(B.UseCount() == 0);
+}
+
+TEST_CASE("SharedString.implicit_conversion")
+{
+ SharedString S(std::string_view("view"));
+ std::string_view V = S;
+ CHECK(V == std::string_view("view"));
+}
+
TEST_SUITE_END();
void