diff options
Diffstat (limited to 'src/zencore/string.cpp')
| -rw-r--r-- | src/zencore/string.cpp | 144 |
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 |