// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // StringRef and BlobRef semantics: equality against string_view, Key, // std::string, literals and other refs across the inline/heap boundary, // prefix-equal non-matches, ordering, implicit conversion, and blob element // alignment for 4- and 8-byte elements. import Crafter.DB; import std; using namespace Crafter; namespace fs = std::filesystem; namespace { int failures = 0; void Check(bool condition, std::string_view what) { if (!condition) { std::println("FAIL: {}", what); ++failures; } } struct Strings { std::string a; std::string b; std::vector words; std::vector doubles; std::vector bytes; static constexpr std::uint32_t version = 1; }; std::string_view V(std::string_view s) { return s; } } int main() { const fs::path dir = fs::temp_directory_path() / "crafter-db-stringref"; fs::remove_all(dir); DB::Table table(dir / "s.cdb"); const std::vector values = { "", "abc", "abcd", "abcde", "twelve_chars", "thirteen_char", "a much longer value that lives in the heap region" }; for (const std::string& v : values) table.Append({ v, v, { 1, 2, 3 }, { 1.5, 2.5 }, { std::byte{ 1 } } }); table.Append({ "abcd", "abcX", { 7, 8, 9, 10, 11 }, {}, {} }); // same length, same 3-byte prefix, differs in the 4th byte table.Append({ "thirteen_char", "thirteen_chaX", {}, { 3.5 }, {} }); for (std::size_t i = 0; i < values.size(); i++) { const DB::StringRef ref = table[i][&Strings::a]; const std::string& v = values[i]; Check(ref == V(v), std::format("== string_view for '{}'", v)); Check(ref == v, std::format("== std::string for '{}'", v)); Check(ref == DB::Key(v), std::format("== Key for '{}'", v)); Check(ref == table[i][&Strings::b], std::format("== StringRef for '{}'", v)); Check(!(ref != V(v)), std::format("!= false for '{}'", v)); Check(std::string_view(ref) == v, std::format("implicit string_view for '{}'", v)); Check(ref.Length() == v.size(), std::format("Length for '{}'", v)); Check(ref.Empty() == v.empty(), std::format("Empty for '{}'", v)); for (std::size_t j = 0; j < values.size(); j++) { if (j == i) continue; Check(!(ref == V(values[j])), std::format("'{}' != '{}'", v, values[j])); Check(!(ref == DB::Key(values[j])), std::format("'{}' != Key '{}'", v, values[j])); Check(!(ref == table[j][&Strings::a]), std::format("'{}' != ref '{}'", v, values[j])); } } Check(!(table[7][&Strings::a] == table[7][&Strings::b]), "prefix-equal inline strings differ in the tail"); Check(!(table[8][&Strings::a] == table[8][&Strings::b]), "prefix-equal heap strings differ in the last byte"); Check(table[8][&Strings::a] == V("thirteen_char") && !(table[8][&Strings::a] == V("thirteen_chaX")), "heap compare checks all bytes"); Check((table[1][&Strings::a] <=> V("abd")) == std::strong_ordering::less, "ordering less"); Check((table[1][&Strings::a] <=> V("abb")) == std::strong_ordering::greater, "ordering greater"); Check((table[1][&Strings::a] <=> V("abc")) == std::strong_ordering::equal, "ordering equal"); Check(table[1][&Strings::a] < V("abcd"), "prefix shorter sorts first"); Check(table[6][&Strings::a] < table[1][&Strings::a], "ref <=> ref (space sorts before b)"); Check(table[1][&Strings::a] == "abc", "== literal"); const DB::BlobRef words = table[0][&Strings::words]; const std::span wordSpan = words; Check(wordSpan.size() == 3 && wordSpan[0] == 1 && wordSpan[2] == 3, "u32 blob round trip (inline)"); Check(reinterpret_cast(wordSpan.data()) % alignof(std::uint32_t) == 0, "inline u32 blob is 4-aligned"); const std::span longWords = table[7][&Strings::words]; Check(longWords.size() == 5 && longWords[4] == 11, "u32 blob round trip (heap)"); Check(reinterpret_cast(longWords.data()) % alignof(std::uint32_t) == 0, "heap u32 blob is 4-aligned"); const std::span doubles = table[0][&Strings::doubles]; Check(doubles.size() == 2 && doubles[1] == 2.5, "double blob round trip"); Check(reinterpret_cast(doubles.data()) % alignof(double) == 0, "double blob is 8-aligned (never inline)"); const std::span one = table[8][&Strings::doubles]; Check(one.size() == 1 && one[0] == 3.5, "single double blob"); const std::span bytes = table[0][&Strings::bytes]; Check(bytes.size() == 1 && bytes[0] == std::byte{ 1 }, "byte blob round trip"); Check(table[7][&Strings::bytes].Size() == 0, "empty blob"); const Strings copy = table[7].Materialize(); Check(copy.words == std::vector{ 7, 8, 9, 10, 11 } && copy.doubles.empty(), "Materialize blobs"); fs::remove_all(dir); if (failures == 0) std::println("ok"); return failures == 0 ? 0 : 1; }