36#include <gtest/gtest.h>
51#if defined(__unix__) || defined(__APPLE__)
60 namespace fs = std::filesystem;
72 static std::atomic<unsigned long long>
counter{0};
73 const auto now = std::chrono::steady_clock::now().time_since_epoch().count();
74#if defined(__unix__) || defined(__APPLE__)
75 const auto pid =
static_cast<long long>(
::getpid());
79 const auto id = std::to_string(pid) +
"_" +
80 std::to_string(
now) +
"_" +
82 const fs::path dir = fs::temp_directory_path() /
"aleph_file_btree_tests";
83 fs::create_directories(dir);
84 return dir / (
id +
".idx");
91 explicit TempFile(fs::path p) : path(
std::move(p)) {}
97 fs::remove(path.string() +
".wal",
ec);
98 fs::remove(path.string() +
".wal.tmp",
ec);
99 fs::remove(path.string() +
".journal",
ec);
100 fs::remove(path.string() +
".journal.tmp",
ec);
101 fs::remove(path.string() +
".lock",
ec);
102 fs::remove(path.string() +
".old",
ec);
103 fs::remove(path.string() +
".new",
ec);
107 template <
typename T>
112 for (
size_t i = 0; i < arr.
size(); ++i)
113 ret.push_back(arr[i]);
117#if defined(__unix__) || defined(__APPLE__)
120# if defined(__SANITIZE_THREAD__)
122# elif defined(__has_feature)
123# if __has_feature(thread_sanitizer)
133 template <
typename F>
180 const std::uint64_t
size,
181 const std::uint64_t root_page,
182 const std::uint64_t page_count,
183 const std::uint64_t free_page_head,
184 const std::uint64_t checkpoint_sequence,
205 const size_t page_bytes,
209 for (std::uint64_t page_id = 1; page_id <= page_count; ++page_id)
213 crc,
page_blob.data() + (page_id - 1) * page_bytes, page_bytes);
228 constexpr size_t page_bytes =
229 sizeof(std::uint8_t) +
sizeof(std::uint16_t) * 2 + 3
230 +
sizeof(std::uint64_t) +
sizeof(std::uint64_t) +
sizeof(
int) *
max_keys
231 +
sizeof(std::uint64_t) *
max_children +
sizeof(std::uint32_t);
236 std::array<char, Aleph::detail::Ordered_Tree_Snapshot_Magic_Size> magic = {};
237 in.read(magic.data(), magic.size());
242 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
247 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
252 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
256 std::uint32_t version = 0;
275 std::uint64_t
size = 0;
276 std::uint64_t root_page = 0;
277 std::uint64_t page_count = 0;
278 std::uint64_t free_page_head = 0;
279 std::uint64_t checkpoint_sequence = 0;
288 std::vector<char>
page_blob(page_bytes * page_count);
292 std::ofstream
out(
wal_path, std::ios::binary | std::ios::trunc);
295 const auto wal_magic =
299 out.write(wal_magic.data(), wal_magic.size());
301 out.write(
reinterpret_cast<const char *
>(&key_size),
sizeof(key_size));
302 out.write(
reinterpret_cast<const char *
>(&min_degree),
sizeof(min_degree));
305 out.write(
reinterpret_cast<const char *
>(&
size),
sizeof(
size));
306 out.write(
reinterpret_cast<const char *
>(&root_page),
sizeof(root_page));
307 out.write(
reinterpret_cast<const char *
>(&page_count),
sizeof(page_count));
308 out.write(
reinterpret_cast<const char *
>(&free_page_head),
sizeof(free_page_head));
309 out.write(
reinterpret_cast<const char *
>(&checkpoint_sequence),
sizeof(checkpoint_sequence));
310 out.write(
reinterpret_cast<const char *
>(&page_count),
sizeof(page_count));
313 checkpoint_sequence, page_count);
316 for (std::uint64_t page_id = 1; page_id <= page_count; ++page_id)
318 out.write(
reinterpret_cast<const char *
>(&page_id),
sizeof(page_id));
319 out.write(
page_blob.data() + (page_id - 1) * page_bytes, page_bytes);
325 out.write(
reinterpret_cast<const char *
>(&checkpoint_sequence),
326 sizeof(checkpoint_sequence));
327 out.write(
reinterpret_cast<const char *
>(&page_count),
sizeof(page_count));
342 +
sizeof(std::uint32_t) * 2 +
sizeof(std::uint64_t)
343 +
sizeof(std::uint8_t) + 7 +
sizeof(std::uint64_t) * 2);
345 std::uint64_t page_count = 0;
346 in.read(
reinterpret_cast<char *
>(&page_count),
sizeof(page_count));
353 constexpr size_t header_bytes =
355 +
sizeof(std::uint64_t) * 5 +
sizeof(std::uint8_t) + 7;
356 constexpr size_t page_bytes =
357 sizeof(std::uint8_t) +
sizeof(std::uint16_t) * 2 + 3
358 +
sizeof(std::uint64_t) +
sizeof(std::uint64_t) +
sizeof(
int) * 5
359 +
sizeof(std::uint64_t) * 6 +
sizeof(std::uint32_t);
367 +
sizeof(std::uint32_t) * 2 +
sizeof(std::uint64_t)
368 +
sizeof(std::uint8_t) + 7 +
sizeof(std::uint64_t));
371 std::uint64_t root_page = 0;
372 in.read(
reinterpret_cast<char *
>(&root_page),
sizeof(root_page));
375 const auto offset =
static_cast<std::streamoff
>(
376 header_bytes + (root_page - 1) * page_bytes +
sizeof(std::uint8_t)
377 +
sizeof(std::uint16_t) * 2 + 3 +
sizeof(std::uint64_t) * 2);
381 std::array<unsigned char, Aleph::detail::Paged_Value_Codec<int>::encoded_size>
390 constexpr size_t page_bytes =
391 sizeof(std::uint8_t) +
sizeof(std::uint16_t) * 2 + 3
392 +
sizeof(std::uint64_t) +
sizeof(std::uint64_t) +
sizeof(
int) * 5
393 +
sizeof(std::uint64_t) * 6 +
sizeof(std::uint32_t);
397 std::vector<std::uint64_t>
page_ids;
403 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
408 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
413 in.read(
reinterpret_cast<char *
>(&
value),
sizeof(
value));
417 std::array<char, Aleph::detail::Ordered_Tree_Snapshot_Magic_Size> magic = {};
419 std::array<char, Aleph::detail::Ordered_Tree_Snapshot_Magic_Size>
422 std::uint32_t key_size = 0;
423 std::uint64_t min_degree = 0;
429 in.read(magic.data(), magic.size());
448 std::uint64_t page_id = 0;
451 in.seekg(
static_cast<std::streamoff
>(page_bytes), std::ios::cur);
461 const std::streamoff
offset,
const size_t size)
463 std::ifstream
in(src, std::ios::binary);
465 std::fstream
out(
dst, std::ios::binary | std::ios::in | std::ios::out);
468 std::vector<char> buffer(
size);
471 in.read(buffer.data(), buffer.size());
476 out.write(buffer.data(), buffer.size());
487 for (
int value : {40, 10, 90, 20, 70, 60, 30})
492 (std::vector<int>{10, 20, 30, 40, 60, 70, 90}));
498 (std::vector<int>{10, 20, 30, 40, 60, 70, 90}));
535 std::uint64_t first = 0;
536 std::uint64_t second = 0;
560 const std::vector<std::string>
expected = {
561 "alfa",
"beta",
"delta",
"epsilon",
"gama"
566 tree(
tmp.path.string(),
false);
583 std::optional<std::string>(
"delta"));
652 std::ofstream
out(
tmp.path.string() +
".wal",
653 std::ios::binary | std::ios::trunc);
655 out <<
"pending recovery";
666#if defined(__unix__) || defined(__APPLE__)
668 GTEST_SKIP() <<
"TSAN does not support these fork-based lock checks";
706 catch (
const std::runtime_error &)
719 GTEST_SKIP() <<
"fork-based lock validation is only available on Unix-like systems";
728 std::ofstream
out(
tmp.path.string() +
".lock",
729 std::ios::binary | std::ios::trunc);
767 std::ofstream
out(
tmp.path, std::ios::binary | std::ios::trunc);
768 out <<
"not a valid Aleph snapshot";
781 for (
int value : {10, 20, 30, 40, 50})
785 std::fstream
io(
tmp.path, std::ios::binary | std::ios::in | std::ios::out);
787 io.seekg(0, std::ios::end);
788 const auto end =
io.tellg();
790 io.seekg(end - std::streamoff(1));
795 byte ^=
static_cast<char>(0x5A);
796 io.seekp(end - std::streamoff(1));
808 const auto journal =
tmp.path.string() +
".journal";
812 for (
int value : {12, 6, 18, 3, 9, 15, 21})
818 fs::copy_file(
tmp.path,
journal, fs::copy_options::overwrite_existing,
ec);
822 std::ofstream
out(
tmp.path, std::ios::binary | std::ios::trunc);
830 (std::vector<int>{3, 6, 9, 12, 15, 18, 21}));
837 const auto wal =
tmp.path.string() +
".wal";
841 for (
int value : {12, 6, 18, 3, 9, 15, 21})
848 std::ofstream
out(
tmp.path, std::ios::binary | std::ios::trunc);
856 (std::vector<int>{3, 6, 9, 12, 15, 18, 21}));
863 const auto wal =
tmp.path.string() +
".wal";
867 for (
int value : {12, 6, 18, 3, 9, 15, 21})
873 std::fstream
wal_io(
wal, std::ios::binary | std::ios::in | std::ios::out);
875 wal_io.seekg(0, std::ios::end);
883 byte ^=
static_cast<char>(0x11);
890 std::ofstream
out(
tmp.path, std::ios::binary | std::ios::trunc);
901 constexpr size_t header_bytes =
903 +
sizeof(std::uint64_t) * 5 +
sizeof(std::uint8_t) + 7;
904 constexpr size_t page_bytes =
905 sizeof(std::uint8_t) +
sizeof(std::uint16_t) * 2 + 3
906 +
sizeof(std::uint64_t) +
sizeof(std::uint64_t) +
sizeof(
int) * 5
907 +
sizeof(std::uint64_t) * 6 +
sizeof(std::uint32_t);
910 const auto wal =
tmp.path.string() +
".wal";
945 static_cast<std::streamoff
>(header_bytes
959 const auto wal =
tmp.path.string() +
".wal";
964 for (
int value : {12, 6, 18, 3, 9, 15, 21})
986 (std::vector<int>{3, 6, 9, 12, 15, 18, 21, 24, 27}));
1001 std::runtime_error);
1013 for (
int value : {1, 2, 3, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 80})
1026 4, 5, 6, 10, 11, 12, 13, 14, 18, 19, 20, 21, 22, 23, 24, 25,
1027 26, 27, 28, 29, 30, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
1028 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
1029 61, 62, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79
size_t size_t int32_t value
size_t size_t int32_t * out
Simple dynamic array with automatic resizing and functional operations.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Persistent page-managed B+ Tree stored in a single binary file.
bool insert(const Key &key)
Insert a key if it is not already present.
Persistent page-managed B-Tree stored in a single binary file.
bool verify() const
Verify structural B-Tree invariants across cached pages.
void set_auto_sync(const bool enabled) noexcept
Enable or disable automatic flushing of dirty pages.
bool contains(const Key &key) const
Return whether a key is present.
bool auto_sync_enabled() const noexcept
Return whether automatic synchronization is enabled.
Array< Key > keys() const
Materialize the tree contents in sorted order.
void checkpoint() const
Synonym for sync().
void sync() const
Flush the current tree image using redo WAL or full-image fallback.
bool remove(const Key &key)
Remove a key if present.
void reload()
Discard unsynchronized changes and reload pages from disk.
std::uint64_t checkpoint_sequence() const noexcept
Return the durable checkpoint sequence stored in the backing file.
bool insert(const Key &key)
Insert a key if it is not already present.
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().
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
std::uint32_t crc32_add_bytes(std::uint32_t crc, const void *data, const size_t size) noexcept
constexpr std::array< char, Ordered_Tree_Snapshot_Magic_Size > ordered_tree_snapshot_magic(const char(&text)[N]) noexcept
std::uint32_t crc32_begin() noexcept
std::uint32_t crc32_add(std::uint32_t crc, const T &value) noexcept
constexpr size_t Ordered_Tree_Snapshot_Magic_Size
std::uint32_t crc32_finish(const std::uint32_t crc) noexcept
Main namespace for Aleph-w library functions.
size_t size(Node *root) noexcept
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
std::vector< typename C::Item_Type > to_vector(const C &c)
Convert a container to a std::vector.
Page-managed persistent B-Tree with file-backed storage.
Page-managed persistent B+ Tree with file-backed storage.