68#include <gtest/gtest.h>
92 return static_cast<long long>(
_getpid());
94 return static_cast<long long>(
getpid());
98std::filesystem::path
tmp_dir(
const std::string &name)
107 static std::atomic<unsigned long long>
counter{0};
108 const auto tick = std::chrono::steady_clock::now().time_since_epoch().count();
109 auto p = std::filesystem::temp_directory_path()
110 / (
"aleph_ca_ckpt_safety_" + name +
"_"
111 + std::to_string(
static_cast<long long>(
tick))
112 +
"_" + std::to_string(process_id())
113 +
"_" + std::to_string(
counter++));
114 std::filesystem::create_directories(p);
121 std::uniform_real_distribution<double> u(0.0, 1.0);
125 g.set({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)},
132 if (a.extents() != b.extents())
134 for (
ca_size_t i = 0; i < a.size(0); ++i)
135 for (
ca_size_t j = 0; j < a.size(1); ++j)
136 if (a.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)})
137 != b.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)}))
142std::size_t
byte_count(
const std::filesystem::path &p)
145 const auto sz = std::filesystem::file_size(p,
ec);
146 return ec ? 0 :
static_cast<std::size_t
>(sz);
152 const auto dir = target.parent_path();
153 const auto stem = target.filename().string();
156 const auto name = entry.path().filename().string();
157 if (name.rfind(
stem +
".tmp.", 0) == 0)
171 const auto dir =
tmp_dir(
"compress_rt");
172 const auto raw_path = dir /
"raw.bin";
173 const auto cmp_path = dir /
"compressed.bin";
206 std::filesystem::remove_all(dir);
218 const auto dir =
tmp_dir(
"crash_preserve");
219 const auto target = dir /
"snap.bin";
223 std::ofstream f(target, std::ios::binary);
226 const std::string
before = [&]
228 std::ifstream
in(target);
229 std::string s((std::istreambuf_iterator<char>(
in)),
230 std::istreambuf_iterator<char>());
244 const auto tmp = make_tmp_path(target);
246 std::ofstream
out(
tmp, std::ios::binary | std::ios::trunc);
247 std::vector<char>
dummy(4096,
'X');
260 std::ifstream
in(target);
261 const std::string
after((std::istreambuf_iterator<char>(
in)),
262 std::istreambuf_iterator<char>());
264 <<
"target must keep its previous contents when the writer crashes "
270 std::filesystem::remove_all(dir);
275 const auto dir =
tmp_dir(
"crash_new");
276 const auto target = dir /
"snap.bin";
284 const auto tmp = make_tmp_path(target);
286 std::ofstream
out(
tmp, std::ios::binary | std::ios::trunc);
287 std::vector<char>
dummy(2048,
'Y');
298 <<
"no target file must exist when the writer crashes before rename";
299 std::filesystem::remove_all(dir);
310 const auto dir =
tmp_dir(
"async_persist");
316 constexpr std::size_t
N = 6;
317 std::vector<std::filesystem::path> paths;
319 for (std::size_t
k = 0;
k <
N; ++
k)
322 const auto p = dir / (
"async_" + std::to_string(
k) +
".bin");
328 for (
const auto &p : paths)
329 EXPECT_TRUE(std::filesystem::exists(p)) <<
"missing async snapshot: " << p;
334 for (
const auto &p : paths)
337 std::filesystem::remove_all(dir);
346 const auto dir =
tmp_dir(
"async_drop");
355 writer.set_before_write_hook([] {
356 std::this_thread::sleep_for(std::chrono::milliseconds(20));
359 constexpr std::size_t
N = 32;
360 for (std::size_t
k = 0;
k <
N; ++
k)
363 writer.submit(
eng, dir / (
"snap_" + std::to_string(
k) +
".bin"));
368 <<
"Drop_Oldest must drop at least one task under sustained pressure";
371 std::filesystem::remove_all(dir);
380 const auto dir =
tmp_dir(
"compress_ratio");
381 const auto raw_path = dir /
"raw.bin";
382 const auto cmp_path = dir /
"compressed.bin";
386 std::mt19937
rng(0x600D);
387 std::bernoulli_distribution
coin(0.01);
391 g.set({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)},
405 const double ratio =
static_cast<double>(
raw_size) /
static_cast<double>(
cmp_size);
407 <<
"Sparse 1024^2 grid should compress > 4x (raw=" <<
raw_size
408 <<
", compressed=" <<
cmp_size <<
", ratio=" << ratio <<
")";
410 std::filesystem::remove_all(dir);
422template <
typename Engine>
425 std::uint64_t step_count,
426 const std::vector<std::int32_t> &cells)
429 using Lattice =
typename Engine::lattice_type;
430 std::ofstream
out(path, std::ios::binary | std::ios::trunc);
433 const std::uint32_t v1 = 1;
434 out.write(
reinterpret_cast<const char *
>(&v1),
sizeof(v1));
436 out.write(
reinterpret_cast<const char *
>(&
th),
sizeof(
th));
437 const std::uint32_t
st_size =
static_cast<std::uint32_t
>(
sizeof(std::int32_t));
439 const std::uint32_t rank =
static_cast<std::uint32_t
>(
Lattice::rank);
440 out.write(
reinterpret_cast<const char *
>(&rank),
sizeof(rank));
441 std::array<std::uint64_t, 4>
ext{};
442 const auto &e = reference.frame().extents();
444 ext[d] =
static_cast<std::uint64_t
>(e[d]);
445 for (std::size_t d = 0; d < 4; ++d)
446 out.write(
reinterpret_cast<const char *
>(&
ext[d]),
sizeof(std::uint64_t));
447 out.write(
reinterpret_cast<const char *
>(&step_count),
sizeof(step_count));
448 const std::uint8_t has_rng = 0;
449 out.write(
reinterpret_cast<const char *
>(&has_rng),
sizeof(has_rng));
450 const char pad[7] = {0, 0, 0, 0, 0, 0, 0};
452 const std::uint64_t master_seed = 0;
453 out.write(
reinterpret_cast<const char *
>(&master_seed),
sizeof(master_seed));
454 const std::uint64_t
cell_count = cells.size();
455 out.write(
reinterpret_cast<const char *
>(&cell_count),
sizeof(
cell_count));
456 out.write(
reinterpret_cast<const char *
>(cells.data()),
457 static_cast<std::streamsize
>(cells.size() *
sizeof(std::int32_t)));
465 const auto dir =
tmp_dir(
"v1_legacy");
466 const auto path = dir /
"legacy.bin";
469 std::vector<std::int32_t> cells(64);
470 for (std::size_t i = 0; i < cells.size(); ++i)
471 cells[i] =
static_cast<std::int32_t
>((i * 37) % 5);
481 EXPECT_EQ(
h.payload_size, 64u *
sizeof(std::int32_t));
492 std::filesystem::remove_all(dir);
501 const auto dir =
tmp_dir(
"delta");
513 const auto base_step =
static_cast<std::uint64_t
>(
eng.steps_run());
538 std::filesystem::remove_all(dir);
562void patch_u64(
const std::filesystem::path &path, std::streamoff
offset,
565 std::fstream f(path, std::ios::binary | std::ios::in | std::ios::out);
567 f.seekp(
offset, std::ios::beg);
568 unsigned char bytes[8];
569 for (
int i = 0; i < 8; ++i)
570 bytes[i] =
static_cast<unsigned char>((
value >> (8 * i)) & 0xFF);
571 f.write(
reinterpret_cast<const char *
>(bytes),
sizeof(bytes));
579 const auto dir =
tmp_dir(
"hostile_cellcount");
580 const auto path = dir /
"snap.bin";
591 std::filesystem::remove_all(dir);
596 const auto dir =
tmp_dir(
"hostile_payload");
597 const auto path = dir /
"snap.bin";
611 std::filesystem::remove_all(dir);
size_t size_t int32_t value
size_t size_t int32_t * out
Internal compress / decompress helpers used by ca-checkpoint.H (Phase 17).
Binary checkpoint format for Aleph::CA engines (Phase 15 + Phase 17 crash-safe / compress / async wri...
Background writer pumping snapshots onto a dedicated thread.
Lattice that adds boundary-aware access on top of a storage.
static constexpr std::size_t rank
Moore (Chebyshev) neighborhood of radius R in N dimensions.
Synchronous double-buffered engine.
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)
constexpr std::array< char, 8 > magic_bytes
Magic prefix identifying an Aleph CA checkpoint stream.
std::vector< std::uint8_t > snapshot_frame_bytes(const Engine &engine)
Flatten an engine frame into a row-major byte buffer.
constexpr std::uint32_t compressed
Frame payload was written through DEFLATE (miniz).
constexpr std::uint32_t delta
File contains only the cells that changed relative to a baseline.
ca_size_t cell_count(const Lattice &lat) noexcept
Total cell count of a lattice (product of extents).
constexpr Game_Of_Life_Rule make_game_of_life_rule() noexcept
Build the canonical Game of Life rule.
Checkpoint_Header inspect_checkpoint(const std::filesystem::path &path)
Read just the header from a checkpoint file.
Resume_Token apply_delta_checkpoint(Engine &engine, const std::filesystem::path &path)
Apply a delta checkpoint to the engine's current state.
void save_delta_checkpoint(Engine &engine, const std::vector< std::uint8_t > &baseline, const std::uint64_t base_step, const std::filesystem::path &path, const Checkpoint_Options &options={})
Write a delta checkpoint relative to a previous payload.
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Resume_Token load_checkpoint_into(Engine &engine, const std::filesystem::path &path)
Restore an engine's state in-place from a checkpoint file.
void save_checkpoint(Engine &engine, const std::filesystem::path &path, const Checkpoint_Options &options={})
Write a complete engine snapshot to disk atomically.
Main namespace for Aleph-w library functions.
User-tunable knobs for save_checkpoint.
bool compress
Enable DEFLATE compression of the frame payload.
Resume handle returned by load_checkpoint_into.
The lattice wraps around on every axis.
Synchronous double-buffered engine for cellular automata.
Cellular automata lattice with pluggable boundary policies.
Neighborhoods catalogue for Aleph::CA.
Rule mechanisms for Aleph::CA.
Reproducible stochastic CA rules (Phase 8).
Dense, contiguous storage for cellular automata cells (1D/2D/3D).