Artifact eed987a99da37419e28d30c9aa6e71cc8a042dbe04167e12c4ffe2fbfcabbf30:
- File
src/storage_benchmark.zig
— part of check-in
[c9bea85ebc]
at
2026-07-16 15:32:21
on branch probedict
— Add a read-only --probe-dict projection to the storage bench to gate dictionary-frame work.
Before building dictionary-compressed full-form frames, measure the win. The new 'zig build bench -- storage --probe-dict' mode (optional .zit path) samples a deterministic content-addressed dictionary from the corpus: up to 64 evenly spaced other-class artifacts plus 16 manifests, capped at 256 KiB. For every full-form zlib frame it then projects storing the artifact as a fossil delta against that dictionary: it deltas the content, verifies delta.apply reconstructs the bytes exactly, zlib-wraps the delta as a section 4.1 container, and keeps the projection only when it strictly beats the current frame. The one-time dictionary cost is included and every non-reconstructing or unprofitable frame is counted at its current size, so the number is honest, not a best case. It stays read-only: the store is opened through openReadOnly and never written. A clear PROCEED/DEFER line reports the projected reduction against a 15 percent gate.
Technique after Yann Collet's zstd dictionary training (https://github.com/facebook/zstd) and the RFC 1950 section 2.2 FDICT preset- dictionary concept (https://www.rfc-editor.org/rfc/rfc1950), with results validated by Chrome's shared-dictionary transport deployment (https://developer.chrome.com/blog/shared-dictionary-compression). The matcher is Fossil's own delta format, so the projected bytes at rest stay a section 4.1 zlib container and no new codec is introduced. (user: dmestas, size: 53526) [annotate] [blame] [check-ins using]
//! Storage measurement harness (storage-efficiency survey, step 1: bytes at //! rest per frame class + serve CPU). //! //! `zig build bench -- storage [<.zit path>]`. Reads a `.zit` (a real store //! passed by path, or a seeded synthetic corpus built in a temp file) and //! reports, over the physical file, WITHOUT modifying it: //! //! 1. bytes at rest by `FrameKind` (raw/zlib/delta/config): frame count, //! on-disk bytes, % of store, avg bytes/frame, and the header+padding //! overhead per kind (so "frame overhead is ~1%" is a measured fact); //! 2. bytes by artifact TYPE — each content artifact is classified as a //! check-in/manifest (its bytes parse as a §4/§5 structural artifact) or //! a file blob (they do not), so "how much of the byte budget is //! manifests" is a number, not a guess; //! 3. a compression-headroom probe over a sample of full-form (`.zlib`) //! frames — uncompressed vs the current zlib-compressed size — quantifying //! the delta-miss population a global codec/dictionary could target; //! 4. serve CPU — time to produce the §4.1/§7.2 wire form for each artifact //! (verbatim `wire()` for a stored container, on-the-fly `container.encode` //! for a raw frame, mirroring the serve loop), in us/artifact and MB/s. //! //! There is no RFC oracle behind a benchmark, so this file implements no //! normative clause and asserts no comparative claim: it is measurement //! INFRASTRUCTURE — it makes no performance claim itself, it lets the survey //! ideas be weighed honestly. The one testable property is exact accounting: //! `header + raw + zlib + delta + config == file size`, checked by the frame //! walk reaching the file end exactly. //! //! Everything effectful goes through the injected `std.Io`: the monotonic clock //! (`Io.Timestamp.now(io, .awake)`) and the backing file. The store is read //! ONLY through `LogStore.openReadOnly` (which never creates, rewrites, or //! truncates) plus positional reads for the frame walk; a diagnostic must not //! mutate the store it measures. Synthetic corpus bytes derive solely from the //! `--seed`, so a run replays exactly. const std = @import("std"); const Io = std.Io; const zit = @import("zit"); const artifact = zit.artifact; const codec = zit.container; const delta = zit.delta; const storage = @import("zit_storage"); const logstore = @import("zit_logstore"); /// Closed harness error set. `Storage`/`Corrupt` propagate from LogStore, `Io` /// is a failed file syscall (open/stat/read), `BadArgs` is a rejected flag. /// Printing adds the writer's own error set; never `anyerror`. pub const Error = error{ OutOfMemory, Storage, Corrupt, Io, BadArgs }; pub const RunError = Error || Io.Writer.Error; // --------------------------------------------------------------------------- // Explicit fixed caps (TigerStyle bounds); each overflow -> BadArgs. // --------------------------------------------------------------------------- const max_count: u64 = 1_000_000; const min_blob: u64 = 32; // room for the 8-byte index prefix + mutations. const max_blob: u64 = 16 << 20; const max_frac: u64 = 100; /// Full-form frames sampled for the compression-headroom probe. const headroom_sample_cap: usize = 1024; /// SHA-256 content-address width the corpus synthesizes for every artifact. const hash_len: usize = 32; // Dictionary-frame projection (survey §2.2, Phase-0 probe). These bounds mirror // the real `buildDict` sampling rule the projection models — so the number the // probe prints is the number the eventual mechanism would deliver, not a // looser upper bound. Deterministic: same store -> same dictionary -> same // projection. /// Dictionary blob byte cap (explicit bound; the concatenated sample stops here). const dict_max: usize = 256 << 10; /// Evenly-spaced "other"-class artifacts drawn into the dictionary. const dict_max_others: usize = 64; /// Evenly-spaced manifest artifacts drawn into the dictionary. const dict_max_manifests: usize = 16; /// One synthetic-corpus shape (all inputs arrive from argv only). Defaults give /// a mixed store: many small check-in manifests plus larger, chained file /// blobs, so every frame kind and both artifact types are exercised. const Config = struct { seed: u64 = 1234, count: u64 = 500, blob: u64 = 2048, /// Percent of artifacts emitted as check-in manifests (the rest are blobs). manifest_frac: u64 = 45, }; /// What the harness does with the acquired `.zit`. /// `.measure` — the frame-kind / artifact-type / serve-CPU report (default). /// `.probe_dict` — the read-only Phase-0 dictionary-frame projection. const Mode = enum { measure, probe_dict }; /// Where the measured `.zit` comes from. const Backing = union(enum) { path: []const u8, synthetic: Config }; /// A parsed invocation: a store to acquire plus what to do with it. const Source = struct { backing: Backing, mode: Mode }; // --------------------------------------------------------------------------- // Entry point + argument parsing. // --------------------------------------------------------------------------- /// Dispatched from logstore_benchmark's `run` when argv[1] == "storage". `args` /// is the whole process argv; a bare `[<path>]` and the synthetic flags begin /// at index 2. pub fn run(gpa: std.mem.Allocator, io: Io, args: []const []const u8, out: *Io.Writer) RunError!void { const source = try parseArgs(args); switch (source.backing) { .path => |p| try measurePath(gpa, io, p, source.mode, out), .synthetic => |cfg| try measureSynthetic(gpa, io, cfg, source.mode, out), } try out.flush(); } /// A leading non-`--` token is a `.zit` path (measure/probe a real store); /// otherwise the `--flag value` pairs configure a synthetic corpus. The /// valueless `--probe-dict` flag selects the dictionary projection and may /// accompany either form. fn parseArgs(args: []const []const u8) Error!Source { std.debug.assert(args.len >= 2); var mode: Mode = .measure; var path: ?[]const u8 = null; var have_flags = false; var cfg = Config{}; var i: usize = 2; while (i < args.len) : (i += 1) { const arg = args[i]; if (std.mem.eql(u8, arg, "--probe-dict")) { mode = .probe_dict; // valueless continue; } if (!std.mem.startsWith(u8, arg, "--")) { if (path != null) return error.BadArgs; // one positional only path = arg; continue; } // A valued synthetic-corpus flag. have_flags = true; i += 1; if (i >= args.len) return error.BadArgs; // flag with no value const val = std.fmt.parseInt(u64, args[i], 0) catch return error.BadArgs; if (std.mem.eql(u8, arg, "--seed")) { cfg.seed = val; } else if (std.mem.eql(u8, arg, "--count")) { cfg.count = val; } else if (std.mem.eql(u8, arg, "--blob")) { cfg.blob = val; } else if (std.mem.eql(u8, arg, "--manifest-frac")) { cfg.manifest_frac = val; } else return error.BadArgs; } if (path) |p| { // A real store: synthetic knobs do not apply to it. if (have_flags) return error.BadArgs; return .{ .backing = .{ .path = p }, .mode = mode }; } if (cfg.count < 1 or cfg.count > max_count) return error.BadArgs; if (cfg.blob < min_blob or cfg.blob > max_blob) return error.BadArgs; if (cfg.manifest_frac > max_frac) return error.BadArgs; return .{ .backing = .{ .synthetic = cfg }, .mode = mode }; } // --------------------------------------------------------------------------- // Source acquisition // --------------------------------------------------------------------------- /// Open a real `.zit` read-only and analyze it. Filesystem access is confined /// here; the store is never written, in either mode. fn measurePath(gpa: std.mem.Allocator, io: Io, path: []const u8, mode: Mode, out: *Io.Writer) RunError!void { var file = Io.Dir.openFileAbsolute(io, path, .{}) catch return error.Io; defer file.close(io); const st = file.stat(io) catch return error.Io; var fs = storage.FileStorage.init(io, file, st.size); try analyze(gpa, io, fs.storageFile(), st.size, path, mode, out); } /// Dispatch an acquired read-only store to the requested analysis. Both arms /// read only — `measure` and `probeDict` open through `openReadOnly` and never /// mutate the backing file. fn analyze( gpa: std.mem.Allocator, io: Io, sfile: storage.StorageFile, file_size: u64, label: []const u8, mode: Mode, out: *Io.Writer, ) RunError!void { switch (mode) { .measure => try measure(gpa, io, sfile, file_size, label, out), .probe_dict => try probeDict(gpa, io, sfile, file_size, label, out), } } /// Build a seeded synthetic corpus in a throwaway temp file, measure it, and /// unlink it. Same seed -> byte-identical file -> identical numbers. fn measureSynthetic(gpa: std.mem.Allocator, io: Io, cfg: Config, mode: Mode, out: *Io.Writer) RunError!void { var temp = try Temp.create(io); defer temp.cleanup(io); var fs = storage.FileStorage.init(io, temp.file, 0); try buildCorpus(gpa, fs.storageFile(), cfg); const file_size = fs.storageFile().size() catch return error.Storage; var name_buf: [96]u8 = undefined; const label = std.fmt.bufPrint(&name_buf, "synthetic corpus seed={d} count={d} blob={d} manifest_frac={d}%", .{ cfg.seed, cfg.count, cfg.blob, cfg.manifest_frac, }) catch return error.BadArgs; // Re-open over the SAME file at its post-build size; both modes read only. var fs2 = storage.FileStorage.init(io, temp.file, file_size); try analyze(gpa, io, fs2.storageFile(), file_size, label, mode, out); } // --------------------------------------------------------------------------- // The measurement (read-only over the store) // --------------------------------------------------------------------------- fn measure( gpa: std.mem.Allocator, io: Io, sfile: storage.StorageFile, file_size: u64, label: []const u8, out: *Io.Writer, ) RunError!void { // openReadOnly walks and whole-frame-CRC-verifies every frame, so the // subsequent lightweight prefix-only frame walk reads trustworthy bytes. var store = logstore.LogStore.openReadOnly(gpa, sfile) catch |e| return mapOpen(e); defer store.deinit(gpa); const bd = try walkFrames(sfile, file_size); try reportFrameKinds(out, label, bd, file_size); const rids = try store.ridsInPersistedOrder(gpa); defer gpa.free(rids); try reportArtifactTypes(gpa, &store, rids, file_size, out); try reportHeadroom(gpa, &store, rids, out); try reportServeCpu(gpa, io, &store, rids, out); try out.writeAll("# zit-vs-fossil ratio: skipped (needs a cloneable fossil repo; network-dependent)\n"); } // --------------------------------------------------------------------------- // 1. Bytes at rest, by FrameKind — an independent physical frame walk. // --------------------------------------------------------------------------- /// Per-kind byte accounting. Indexed by `@intFromEnum(FrameKind)` (raw=0, /// zlib=1, delta=2, config=3). `on_disk` is the aligned frame span; `payload` /// is the on-disk (raw/container) payload length; `overhead` is header+padding /// (`on_disk - payload`). const Breakdown = struct { count: [4]u64 = .{ 0, 0, 0, 0 }, on_disk: [4]u64 = .{ 0, 0, 0, 0 }, payload: [4]u64 = .{ 0, 0, 0, 0 }, overhead: [4]u64 = .{ 0, 0, 0, 0 }, fn total_on_disk(self: Breakdown) u64 { return self.on_disk[0] + self.on_disk[1] + self.on_disk[2] + self.on_disk[3]; } fn total_overhead(self: Breakdown) u64 { return self.overhead[0] + self.overhead[1] + self.overhead[2] + self.overhead[3]; } }; /// A frame's on-disk span (logstore.zig frame layout): the fixed header /// (`len|flags|hash_len|prefix_crc|hash|delta_of|header_crc` = 22 + hash_len), /// the payload, and the 4-byte whole-frame CRC, padded up to an 8-byte /// boundary — i.e. `align8(26 + hash_len + payload_len)`. fn frameSpan(hash_len_b: u8, payload_len: u64) u64 { return alignUp8(26 + @as(u64, hash_len_b) + payload_len); } fn alignUp8(n: u64) u64 { return (n + 7) / 8 * 8; } /// Walk every frame from its physical bytes, classifying by the prefix flag /// byte and summing spans by kind. Reads only the 10-byte fixed prefix /// (`payload_len|flags|hash_len|prefix_crc`) of each frame — no payload, no /// decode — so it is a cheap structural scan. The store was CRC-verified by /// `openReadOnly` before this runs, so the prefix bytes are trustworthy. /// Reaching `file_size` exactly is the exact-accounting invariant. fn walkFrames(sfile: storage.StorageFile, file_size: u64) Error!Breakdown { std.debug.assert(file_size >= logstore.header_len); var bd = Breakdown{}; var offset: u64 = logstore.header_len; // Each frame spans >= 8 bytes; the count can never exceed file_size/8. const max_frames: u64 = file_size / logstore.frame_align + 1; var seen: u64 = 0; while (offset < file_size) { seen += 1; if (seen > max_frames) return error.Corrupt; var prefix: [10]u8 = undefined; const n = sfile.pread(offset, &prefix) catch return error.Storage; if (n < prefix.len) return error.Corrupt; // a known frame is never short const payload_len = std.mem.readInt(u32, prefix[0..4], .little); const flags = prefix[4]; const hlen = prefix[5]; if (flags > @intFromEnum(logstore.FrameKind.config)) return error.Corrupt; if (hlen < logstore.min_hash_len or hlen > logstore.max_hash_len) return error.Corrupt; const span = frameSpan(hlen, payload_len); const k: usize = flags; bd.count[k] += 1; bd.on_disk[k] += span; bd.payload[k] += payload_len; bd.overhead[k] += span - payload_len; offset += span; } if (offset != file_size) return error.Corrupt; // frame boundaries must tile the file exactly std.debug.assert(logstore.header_len + bd.total_on_disk() == file_size); // exact accounting return bd; } const kind_names = [4][]const u8{ "raw", "zlib", "delta", "config" }; fn reportFrameKinds(out: *Io.Writer, label: []const u8, bd: Breakdown, file_size: u64) RunError!void { try out.print("# storage bench: bytes at rest [{s}]\n\n", .{label}); try out.writeAll("frame-kind breakdown\n"); try out.writeAll("kind frames on-disk %store avg/frame overhead ovhd%\n"); for (0..4) |k| { if (bd.count[k] == 0 and bd.on_disk[k] == 0) continue; const avg = if (bd.count[k] == 0) 0 else bd.on_disk[k] / bd.count[k]; try out.print("{s:<8} {d:>6} {d:>10} {d:>5.1}% {d:>9} {d:>8} {d:>4.1}%\n", .{ kind_names[k], bd.count[k], bd.on_disk[k], pct(bd.on_disk[k], file_size), avg, bd.overhead[k], pct(bd.overhead[k], bd.on_disk[k]), }); } try out.print("{s:<8} {d:>6} {d:>10}\n", .{ "header", @as(u64, 0), logstore.header_len }); try out.print("{s:<8} {d:>6} {d:>10} {d:>5.1}% {s:>9} {d:>8} {d:>4.1}%\n", .{ "total", bd.count[0] + bd.count[1] + bd.count[2] + bd.count[3], file_size, @as(f64, 100.0), "", bd.total_overhead(), pct(bd.total_overhead() + logstore.header_len, file_size), }); const accounted = logstore.header_len + bd.total_on_disk(); try out.print("# accounting: header(64) + raw + zlib + delta + config = {d} = file size ({s})\n\n", .{ accounted, if (accounted == file_size) "exact" else "MISMATCH", }); } // --------------------------------------------------------------------------- // 2. Bytes by artifact TYPE — parse each content artifact's card grammar. // --------------------------------------------------------------------------- /// A content artifact is a check-in/manifest when its uncompressed bytes parse /// as a §4/§5 structural artifact of type `manifest`; another structural type /// (tag, cluster, wiki, ...) is `other`; anything that fails to parse is a file /// blob (source content). Bytes are the on-disk frame span, so the totals here /// reconcile with the raw+zlib+delta on-disk totals from the frame walk. const TypeClass = enum(usize) { manifest = 0, other = 1, blob = 2 }; fn reportArtifactTypes( gpa: std.mem.Allocator, store: *logstore.LogStore, rids: []const logstore.Rid, file_size: u64, out: *Io.Writer, ) RunError!void { var count = [_]u64{ 0, 0, 0 }; var on_disk = [_]u64{ 0, 0, 0 }; for (rids) |rid| { const span = frameSpan(@intCast(store.hashOf(rid).len), store.payloadLenOf(rid)); const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse continue; defer gpa.free(content); const cls = classify(gpa, content) catch |e| return switch (e) { error.OutOfMemory => error.OutOfMemory, }; const i = @intFromEnum(cls); count[i] += 1; on_disk[i] += span; } try out.writeAll("artifact-type breakdown (content frames, on-disk bytes)\n"); try out.writeAll("type count on-disk %store\n"); const type_names = [_][]const u8{ "checkin/manifest", "other-artifact", "file-blob" }; for (0..3) |i| { if (count[i] == 0) continue; try out.print("{s:<16} {d:>6} {d:>10} {d:>5.1}%\n", .{ type_names[i], count[i], on_disk[i], pct(on_disk[i], file_size), }); } try out.print("# manifests are {d:.1}% of the store by bytes at rest (survey §2.5: the manifest-transform budget)\n\n", .{ pct(on_disk[@intFromEnum(TypeClass.manifest)], file_size), }); } /// Classify uncompressed artifact bytes. `artifact.parse` is the card-grammar /// oracle: success means a structural artifact (its `kind` splits manifest from /// other), a parse failure means a file blob. Only OOM escapes. fn classify(gpa: std.mem.Allocator, content: []const u8) error{OutOfMemory}!TypeClass { var m = artifact.parse(gpa, content) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, else => return .blob, // not a structural artifact -> a file blob }; defer m.deinit(); return if (m.kind == .manifest) .manifest else .other; } // --------------------------------------------------------------------------- // 3. Compression-headroom probe over full-form (.zlib) frames. // --------------------------------------------------------------------------- fn reportHeadroom( gpa: std.mem.Allocator, store: *logstore.LogStore, rids: []const logstore.Rid, out: *Io.Writer, ) RunError!void { var sampled: u64 = 0; var uncompressed: u64 = 0; var compressed: u64 = 0; for (rids) |rid| { if (sampled >= headroom_sample_cap) break; if (store.kindOf(rid) != .zlib) continue; // full-form only; deltas are already differential const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse continue; defer gpa.free(content); uncompressed += content.len; compressed += store.payloadLenOf(rid); // the stored §4.1 container length sampled += 1; } try out.writeAll("full-form (.zlib) compression headroom\n"); if (sampled == 0) { try out.writeAll("# no full-form frames sampled (store holds only raw/delta content)\n\n"); return; } try out.print("sampled {d} frames: uncompressed={d} compressed={d} ratio={d:.1}% (compressed/uncompressed)\n", .{ sampled, uncompressed, compressed, pct(compressed, uncompressed), }); try out.writeAll("# informational (no claim): the delta-miss population a global codec / preset dictionary (survey §2.2) could target\n\n"); } // --------------------------------------------------------------------------- // 4. Serve CPU — time to produce the §4.1/§7.2 wire form per artifact. // --------------------------------------------------------------------------- fn reportServeCpu( gpa: std.mem.Allocator, io: Io, store: *logstore.LogStore, rids: []const logstore.Rid, out: *Io.Writer, ) RunError!void { var served: u64 = 0; var wire_bytes: u64 = 0; const t0 = now(io); for (rids) |rid| { wire_bytes += try produceWire(gpa, store, rid); served += 1; } const elapsed_ns = elapsed(t0, now(io)); try out.writeAll("serve CPU (produce §4.1/§7.2 wire form)\n"); const us_each = if (served == 0) 0 else nsToUs(elapsed_ns) / @as(f64, @floatFromInt(served)); try out.print("artifacts={d} total={d:.2}ms {d:.3}us/artifact {d:.1} MB/s (wire bytes out)\n\n", .{ served, nsToMs(elapsed_ns), us_each, mbPerSec(wire_bytes, elapsed_ns), }); } /// Produce and free `rid`'s served wire bytes, returning their length. A stored /// container is served verbatim via `wire()` (§4.1/§7.2 — the stored bytes ARE /// the wire payload); a raw frame has no stored container, so the serve loop /// compresses its content on the fly (`container.encode`, matching serve.zig). fn produceWire(gpa: std.mem.Allocator, store: *logstore.LogStore, rid: logstore.Rid) Error!u64 { if (store.wire(gpa, rid) catch |e| return mapContent(e)) |w| { var wm = w; const len = wm.container().len; wm.deinit(gpa); return len; } // Raw (or phantom): compress the reconstructed content the way serve does. const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse return 0; defer gpa.free(content); const container = codec.encode(gpa, content, .default) catch |e| return switch (e) { error.OutOfMemory => error.OutOfMemory, error.InputTooLarge => error.Corrupt, }; defer gpa.free(container); return container.len; } // --------------------------------------------------------------------------- // Phase-0 dictionary-frame projection (survey §2.2) — a READ-ONLY diagnostic. // // It PROJECTS, without implementing, the win of storing near-identical // full-form artifacts as a fossil delta against a shared, content-addressed // dictionary blob sampled from the corpus. This is a PROJECTION, not a claim // and not a stored format: nothing is written, no frame kind is added. The // projection is the gate — the real dictionary-frame work is built only if it // clears the ≥15% total-store-reduction bar. // // Technique after Yann Collet's zstd dictionary training // (https://github.com/facebook/zstd) and the RFC 1950 §2.2 FDICT preset- // dictionary concept (https://www.rfc-editor.org/rfc/rfc1950), with results // validated by Chrome's shared-dictionary transport deployment // (https://developer.chrome.com/blog/shared-dictionary-compression). The // matcher here is Fossil's own delta format (src/delta.zig), so the projected // bytes at rest stay a §4.1 zlib container — no codec beyond the one the store // already uses is introduced. // --------------------------------------------------------------------------- /// Everything the projection reports, computed purely from the store's bytes in /// a fixed order — so the same store yields an identical `Projection`. `_current` /// / `_projected` are on-disk frame spans (header+payload+CRC, 8-byte aligned), /// so they reconcile directly with the frame-walk totals and with `file_size`. const Projection = struct { /// Artifacts drawn into the dictionary sample. dict_samples: u64 = 0, /// Uncompressed dictionary bytes (the sampled, capped concatenation). dict_len: u64 = 0, /// One-time on-disk cost of the dictionary as a §4.1 container frame. dict_ondisk: u64 = 0, /// The store's content-address width (bytes), used for projected spans. hash_w: u64 = 0, /// Full-form (`.zlib`) frames considered. fullform_frames: u64 = 0, /// Their current on-disk total. fullform_current: u64 = 0, /// Their projected on-disk total (dict_delta where it wins, else the /// unchanged full form) — the one-time `dict_ondisk` is NOT folded in here. fullform_projected: u64 = 0, /// Frames a profitable, reconstructing dict_delta improved. improved: u64 = 0, /// Frames counted at their current size (unprofitable OR non-reconstructing). fell_back: u64 = 0, /// Subset of `fell_back` whose delta did NOT reconstruct the content — a /// projection that would be a lie, so it is honestly excluded from the win. non_reconstruct: u64 = 0, }; /// Sample a deterministic candidate dictionary from the store, mirroring the /// real `buildDict` rule the projection models: walk artifacts in persisted /// (offset) order, classify each with the bench's own `classify`, take up to /// `dict_max_others` evenly-spaced "other"-class artifacts and up to /// `dict_max_manifests` evenly-spaced manifests, then concatenate the selected /// artifacts back in offset order, capping the blob at `dict_max`. Same store -> /// identical bytes -> identical hash. Caller owns the returned slice. fn buildProbeDict(gpa: std.mem.Allocator, store: *logstore.LogStore, rids: []const logstore.Rid, samples_out: *u64) Error![]u8 { // Pass 1: classify every artifact (content decoded, classified, freed). const classes = gpa.alloc(TypeClass, rids.len) catch return error.OutOfMemory; defer gpa.free(classes); for (rids, 0..) |rid, i| { const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse { classes[i] = .blob; // phantom/raw-absent: never sampled continue; }; defer gpa.free(content); classes[i] = classify(gpa, content) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, }; } // Pass 2: mark an evenly-spaced sample of each class as selected. const selected = gpa.alloc(bool, rids.len) catch return error.OutOfMemory; defer gpa.free(selected); @memset(selected, false); try markEvenlySpaced(gpa, classes, .other, dict_max_others, selected); try markEvenlySpaced(gpa, classes, .manifest, dict_max_manifests, selected); // Pass 3: concatenate the selected artifacts in offset order, capped. var samples: u64 = 0; var dict: std.ArrayList(u8) = .empty; errdefer dict.deinit(gpa); for (rids, 0..) |rid, i| { if (!selected[i]) continue; if (dict.items.len >= dict_max) break; const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse continue; defer gpa.free(content); const room = dict_max - dict.items.len; const take = @min(room, content.len); dict.appendSlice(gpa, content[0..take]) catch return error.OutOfMemory; samples += 1; } samples_out.* = samples; return dict.toOwnedSlice(gpa) catch return error.OutOfMemory; } /// Mark up to `cap` evenly-spaced members of `class` as selected. Evenly-spaced /// = ranks `⌊i·n/k⌋` for `i` in `0..k` (k = min(cap, n)) — the same fixed rule /// for a given population, so the sample is deterministic. fn markEvenlySpaced( gpa: std.mem.Allocator, classes: []const TypeClass, class: TypeClass, cap: usize, selected: []bool, ) Error!void { var ranks: std.ArrayList(usize) = .empty; defer ranks.deinit(gpa); for (classes, 0..) |c, i| { if (c == class) ranks.append(gpa, i) catch return error.OutOfMemory; } const n = ranks.items.len; if (n == 0) return; const k = @min(cap, n); std.debug.assert(k >= 1); for (0..k) |i| { const r = i * n / k; // < n since i < k <= n std.debug.assert(r < n); selected[ranks.items[r]] = true; } } /// Compute the full projection: build the dictionary, price its one-time frame, /// then for every full-form frame project a dict_delta and VERIFY it /// reconstructs the content byte-for-byte before counting the saving. A frame /// whose delta does not reconstruct, or does not beat the full form, is counted /// at its CURRENT size — no cherry-picking. Pure (no clock/IO), so it is the /// deterministic core the read-only report wraps with timing. fn computeProjection(gpa: std.mem.Allocator, store: *logstore.LogStore, rids: []const logstore.Rid) Error!Projection { var pj = Projection{}; if (rids.len == 0) return pj; pj.hash_w = store.hashOf(rids[0]).len; // the store's content-address width var samples: u64 = 0; const dict = try buildProbeDict(gpa, store, rids, &samples); defer gpa.free(dict); pj.dict_samples = samples; pj.dict_len = dict.len; const dict_container = codec.encode(gpa, dict, .default) catch |e| return mapEncode(e); defer gpa.free(dict_container); pj.dict_ondisk = frameSpan(@intCast(pj.hash_w), dict_container.len); for (rids) |rid| { if (store.kindOf(rid) != .zlib) continue; // full-form only const content = (store.content(gpa, rid) catch |e| return mapContent(e)) orelse continue; defer gpa.free(content); pj.fullform_frames += 1; const cur_span = frameSpan(@intCast(store.hashOf(rid).len), store.payloadLenOf(rid)); pj.fullform_current += cur_span; const d = delta.create(gpa, dict, content) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, error.TargetTooLarge => { // unrepresentable -> real impl stores plain zlib pj.fell_back += 1; pj.non_reconstruct += 1; pj.fullform_projected += cur_span; continue; }, }; defer gpa.free(d); // Honesty bar: a projection built on a delta that does not reconstruct // is a lie. Verify apply(dict, delta) == content, byte-exact. const ok = verifyReconstruct(gpa, dict, d, content) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, }; if (!ok) { pj.fell_back += 1; pj.non_reconstruct += 1; pj.fullform_projected += cur_span; continue; } // The stored dict_delta would be a §4.1 container of the raw delta. const dd_container = codec.encode(gpa, d, .default) catch |e| return mapEncode(e); defer gpa.free(dd_container); const dd_span = frameSpan(@intCast(store.hashOf(rid).len), dd_container.len); // Offline pack can afford the exact comparison: keep the dict_delta iff // strictly smaller than the full form, else store the full form. if (dd_span < cur_span) { pj.improved += 1; pj.fullform_projected += dd_span; } else { pj.fell_back += 1; pj.fullform_projected += cur_span; } } return pj; } /// True iff `apply(dict, d)` reconstructs `content` byte-for-byte. A malformed /// or checksum-mismatched delta is `false` (it would fall back), never fatal. fn verifyReconstruct(gpa: std.mem.Allocator, dict: []const u8, d: []const u8, content: []const u8) error{OutOfMemory}!bool { const back = delta.apply(gpa, dict, d) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, else => return false, // MalformedDelta / ChecksumMismatch -> not reconstructable }; defer gpa.free(back); return std.mem.eql(u8, back, content); } /// Open the store read-only, compute the projection, and print it. Timing wraps /// only the pure projection (delta create + verify + container encode over every /// full-form frame), so the reported µs/frame is the pack-time cost the real /// mechanism would pay. fn probeDict( gpa: std.mem.Allocator, io: Io, sfile: storage.StorageFile, file_size: u64, label: []const u8, out: *Io.Writer, ) RunError!void { var store = logstore.LogStore.openReadOnly(gpa, sfile) catch |e| return mapOpen(e); defer store.deinit(gpa); const rids = try store.ridsInPersistedOrder(gpa); defer gpa.free(rids); const t0 = now(io); const pj = try computeProjection(gpa, &store, rids); const elapsed_ns = elapsed(t0, now(io)); try reportProjection(out, label, pj, file_size, elapsed_ns); } fn reportProjection(out: *Io.Writer, label: []const u8, pj: Projection, file_size: u64, elapsed_ns: u64) RunError!void { try out.print("# storage bench: dictionary-frame PROJECTION (read-only; survey §2.2) [{s}]\n\n", .{label}); try out.writeAll("# This is a PROJECTION, not a claim and not a stored format: nothing was\n"); try out.writeAll("# written. It models storing full-form frames as a fossil delta against a\n"); try out.writeAll("# content-addressed dictionary sampled from the corpus; the one-time\n"); try out.writeAll("# dictionary cost is included and non-reconstructing/unprofitable frames are\n"); try out.writeAll("# counted at their CURRENT size (no cherry-picking).\n\n"); if (pj.fullform_frames == 0) { try out.writeAll("no full-form (.zlib) frames — nothing for a dictionary to target\n"); try out.writeAll("verdict: DEFER (projected reduction 0.0% < 15% gate)\n"); return; } // Net full-form saving folds in the one-time dictionary cost (honest). const proj_fullform_total = pj.fullform_projected + pj.dict_ondisk; const saved: i64 = @as(i64, @intCast(pj.fullform_current)) - @as(i64, @intCast(proj_fullform_total)); const full_pct = pctSigned(saved, pj.fullform_current); const whole_pct = pctSigned(saved, file_size); try out.print("dictionary: {d} artifacts sampled -> {d} B uncompressed, {d} B on-disk (one-time §4.1 frame)\n", .{ pj.dict_samples, pj.dict_len, pj.dict_ondisk, }); try out.print("full-form frames: {d} (improved={d}, fell-back={d}, of which non-reconstructing={d})\n", .{ pj.fullform_frames, pj.improved, pj.fell_back, pj.non_reconstruct, }); try out.print("full-form bytes: current={d} projected={d} (dict {d} + dict_delta {d})\n", .{ pj.fullform_current, proj_fullform_total, pj.dict_ondisk, pj.fullform_projected, }); try out.print("projected reduction: {d:.1}% on the full-form population, {d:.1}% on the whole store ({d} B)\n", .{ full_pct, whole_pct, file_size, }); const us_each = nsToUs(elapsed_ns) / @as(f64, @floatFromInt(pj.fullform_frames)); try out.print("pack-time cost: {d:.2}ms total, {d:.3}us/frame (delta create + verify + container encode)\n", .{ nsToMs(elapsed_ns), us_each, }); // The gate: proceed to real frame work only at >= 15% total store reduction. if (whole_pct >= 15.0) { try out.print("verdict: PROCEED (projected {d:.1}% >= 15% gate)\n", .{whole_pct}); } else { try out.print("verdict: DEFER (projected {d:.1}% < 15% gate)\n", .{whole_pct}); } } /// Percent of a possibly-negative part over a whole (the dictionary's one-time /// cost can exceed the frame saving on a corpus with no shared structure). fn pctSigned(part: i64, whole: u64) f64 { if (whole == 0) return 0; return @as(f64, @floatFromInt(part)) * 100.0 / @as(f64, @floatFromInt(whole)); } fn mapEncode(e: codec.EncodeError) Error { return switch (e) { error.OutOfMemory => error.OutOfMemory, error.InputTooLarge => error.Corrupt, }; } // --------------------------------------------------------------------------- // Synthetic corpus (pure — bytes derive from the seed alone). // --------------------------------------------------------------------------- /// Build a mixed store: check-in manifests (chained so many delta-encode) and /// file blobs (some stored raw, the rest a chained delta lineage), plus one /// config frame. Every frame kind and both artifact types appear. All /// randomness is one seeded PRNG with a fixed draw order, so the file is /// byte-identical for a given `(seed, count, blob, manifest_frac)`. fn buildCorpus(gpa: std.mem.Allocator, sfile: storage.StorageFile, cfg: Config) Error!void { var store = logstore.LogStore.open(gpa, sfile, 0) catch |e| return mapOpen(e); defer store.deinit(gpa); var prng = std.Random.DefaultPrng.init(cfg.seed); const rand = prng.random(); const blob = gpa.alloc(u8, @intCast(cfg.blob)) catch return error.OutOfMemory; defer gpa.free(blob); const prev = gpa.alloc(u8, @intCast(cfg.blob)) catch return error.OutOfMemory; defer gpa.free(prev); var man_buf: [256]u8 = undefined; var last_manifest: ?logstore.Rid = null; var last_blob: ?logstore.Rid = null; var have_prev_blob = false; const n: usize = @intCast(cfg.count); for (0..n) |i| { const draw = rand.int(u64); if (draw % 100 < cfg.manifest_frac) { const bytes = try buildManifest(&man_buf, i); const h = hashBlob(bytes); last_manifest = store.putArtifact(&h, bytes, last_manifest) catch |e| return mapPut(e); } else { fillBlob(rand, blob, prev, have_prev_blob, i); @memcpy(prev, blob); have_prev_blob = true; const h = hashBlob(blob); // Every 4th blob is stored raw (populates the raw bucket, and gives // later deltas a raw base — legal); the rest chain as zlib/delta. if ((draw / 100) % 4 == 0) { last_blob = store.put(&h, blob) catch |e| return mapPut(e); } else { last_blob = store.putArtifact(&h, blob, last_blob) catch |e| return mapPut(e); } } if ((i + 1) % 64 == 0) store.commit() catch return error.Storage; } store.putConfig(.{ .hash_policy = 0, .default_branch = "trunk", .project_id = "zit-storage-bench", .project_code = "0000000000000000", }) catch |e| return mapPut(e); store.commit() catch return error.Storage; } /// A minimal, valid §4/§5 check-in manifest (card set C,D,P,R,U,Z, matching the /// RFC A.1.1 worked example), distinct per `index` via its comment. `R` is the /// §6.3 aggregate of zero tracked files (md5 of the empty string); `Z` is the /// §6.2 self-checksum, so the bytes parse and classify as `manifest`. Successive /// indices differ only in the comment + Z line, so consecutive manifests are /// similar enough for a profitable delta. fn buildManifest(buf: []u8, index: u64) Error![]const u8 { const body = std.fmt.bufPrint(buf, "C checkin-{d}\nD 2007-07-21T14:09:59\nP\nR d41d8cd98f00b204e9800998ecf8427e\nU bench\n", .{index}) catch return error.BadArgs; var digest: [32]u8 = undefined; _ = artifact.selfChecksum(body, &digest); // Append "Z <32-hex>\n" after the body. const zoff = body.len; std.debug.assert(zoff + 35 <= buf.len); buf[zoff] = 'Z'; buf[zoff + 1] = ' '; @memcpy(buf[zoff + 2 ..][0..32], &digest); buf[zoff + 34] = '\n'; return buf[0 .. zoff + 35]; } /// A tiny word vocabulary so synthetic file blobs read like compressible /// source text (real file blobs compress ~3-4x), making the full-form /// compression-headroom probe representative rather than measuring random /// noise. Fixed at comptime; no ambient data. const vocab = [_][]const u8{ "fn ", "return ", "const ", "value ", "buffer ", "index ", "store ", "frame ", "hash =", "payload", "= self.", "len);\n", "if (x) ", "loop {\n", "assert(", "offset ", }; /// Fill `blob` for artifact `index`: pseudo-source text (compressible) for the /// first blob, else a ~2% mutation of the previous blob (a delta-able chain). /// The first 8 bytes always carry `index`, so every blob's content — and hash — /// is distinct. fn fillBlob(rand: std.Random, blob: []u8, prev: []const u8, have_prev: bool, index: u64) void { std.debug.assert(blob.len >= 8); if (!have_prev) { var w: usize = 0; while (w < blob.len) { const tok = vocab[rand.uintLessThan(usize, vocab.len)]; const n = @min(tok.len, blob.len - w); @memcpy(blob[w..][0..n], tok[0..n]); w += n; } } else { @memcpy(blob, prev); const mutations = blob.len / 50 + 1; // ~2% of tokens rewritten for (0..mutations) |_| { const at = rand.uintLessThan(usize, blob.len); const tok = vocab[rand.uintLessThan(usize, vocab.len)]; const n = @min(tok.len, blob.len - at); @memcpy(blob[at..][0..n], tok[0..n]); } } std.mem.writeInt(u64, blob[0..8], index, .little); } fn hashBlob(bytes: []const u8) [hash_len]u8 { var h: [hash_len]u8 = undefined; std.crypto.hash.sha2.Sha256.hash(bytes, &h, .{}); return h; } // --------------------------------------------------------------------------- // Temp backing file (real file, reached only through injected Io). // --------------------------------------------------------------------------- /// A throwaway `/tmp` file, named from the monotonic clock so concurrent runs /// never collide, unlinked on cleanup. const Temp = struct { file: Io.File, path_buf: [64]u8, path_len: usize, fn create(io: Io) Error!Temp { var t: Temp = undefined; const nonce = Io.Clock.Timestamp.now(io, .awake).raw.nanoseconds; const p = std.fmt.bufPrint(&t.path_buf, "/tmp/zit-sbench-{d}.zit", .{nonce}) catch return error.BadArgs; t.path_len = p.len; t.file = Io.Dir.createFileAbsolute(io, t.path_buf[0..t.path_len], .{ .read = true }) catch return error.Io; return t; } fn cleanup(self: *Temp, io: Io) void { self.file.close(io); Io.Dir.deleteFileAbsolute(io, self.path_buf[0..self.path_len]) catch {}; } }; // --------------------------------------------------------------------------- // Timing, formatting, error mapping. // --------------------------------------------------------------------------- fn now(io: Io) Io.Timestamp { return Io.Timestamp.now(io, .awake); } fn elapsed(t0: Io.Timestamp, t1: Io.Timestamp) u64 { const ns = t0.durationTo(t1).nanoseconds; // i96, monotonic so >= 0 return if (ns < 0) 0 else @intCast(ns); } fn pct(part: u64, whole: u64) f64 { if (whole == 0) return 0; return @as(f64, @floatFromInt(part)) * 100.0 / @as(f64, @floatFromInt(whole)); } fn mbPerSec(bytes: u64, ns: u64) f64 { if (ns == 0 or bytes == 0) return 0; const secs = @as(f64, @floatFromInt(ns)) / 1_000_000_000.0; const mib = @as(f64, @floatFromInt(bytes)) / (1024.0 * 1024.0); return mib / secs; } fn nsToUs(ns: u64) f64 { return @as(f64, @floatFromInt(ns)) / 1000.0; } fn nsToMs(ns: u64) f64 { return @as(f64, @floatFromInt(ns)) / 1_000_000.0; } fn mapOpen(e: logstore.OpenError) Error { return switch (e) { error.OutOfMemory => error.OutOfMemory, error.Storage => error.Storage, error.Corrupt => error.Corrupt, }; } fn mapContent(e: logstore.ContentError) Error { return switch (e) { error.OutOfMemory => error.OutOfMemory, error.Storage => error.Storage, error.Corrupt => error.Corrupt, }; } fn mapPut(e: logstore.PutError) Error { return switch (e) { error.OutOfMemory => error.OutOfMemory, error.Storage => error.Storage, }; } // --------------------------------------------------------------------------- // Tests (Debug, wired into `zig build test`): a benchmark has no RFC oracle, so // the only testable properties are the harness's own invariants — exact // accounting, deterministic classification, and read-only measurement. No // timing loop runs under `test`. // --------------------------------------------------------------------------- const testing = std.testing; /// Build a small corpus into a tmp file and return the opened read-only handle /// plus its size. Caller closes the file and deinits the store. const Fixture = struct { file: Io.File, size: u64, fn build(io: Io, dir: Io.Dir, cfg: Config) !Fixture { const file = try dir.createFile(io, ".zit", .{ .read = true }); var fs = storage.FileStorage.init(io, file, 0); try buildCorpus(testing.allocator, fs.storageFile(), cfg); const size = try fs.storageFile().size(); return .{ .file = file, .size = size }; } }; test "storage bench: frame-kind breakdown sums to the total store size (exact accounting)" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .count = 120, .blob = 512 }); var file = fx.file; defer file.close(io); var fs = storage.FileStorage.init(io, file, fx.size); const bd = try walkFrames(fs.storageFile(), fx.size); // The whole file is the header plus every frame span, nothing unaccounted. try testing.expectEqual(fx.size, logstore.header_len + bd.total_on_disk()); // The corpus exercises every kind we depend on: raw, zlib/delta content, // and exactly one config frame. try testing.expect(bd.count[@intFromEnum(logstore.FrameKind.raw)] > 0); try testing.expect(bd.count[@intFromEnum(logstore.FrameKind.zlib)] + bd.count[@intFromEnum(logstore.FrameKind.delta)] > 0); try testing.expectEqual(@as(u64, 1), bd.count[@intFromEnum(logstore.FrameKind.config)]); // Per-kind overhead is header+padding only: on_disk == payload + overhead. for (0..4) |k| { try testing.expectEqual(bd.on_disk[k], bd.payload[k] + bd.overhead[k]); } } test "storage bench: artifact-frame spans reconcile with the store's own per-rid view" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .count = 120, .blob = 512 }); var file = fx.file; defer file.close(io); var fs = storage.FileStorage.init(io, file, fx.size); const bd = try walkFrames(fs.storageFile(), fx.size); var fs2 = storage.FileStorage.init(io, file, fx.size); var store = try logstore.LogStore.openReadOnly(testing.allocator, fs2.storageFile()); defer store.deinit(testing.allocator); const rids = try store.ridsInPersistedOrder(testing.allocator); defer testing.allocator.free(rids); // The frame walk's non-config on-disk total must equal the sum of every // artifact rid's span computed from the public API — the two independent // views of the same frames agree. var api_total: u64 = 0; for (rids) |rid| api_total += frameSpan(@intCast(store.hashOf(rid).len), store.payloadLenOf(rid)); const walk_content = bd.on_disk[@intFromEnum(logstore.FrameKind.raw)] + bd.on_disk[@intFromEnum(logstore.FrameKind.zlib)] + bd.on_disk[@intFromEnum(logstore.FrameKind.delta)]; try testing.expectEqual(walk_content, api_total); try testing.expectEqual(@as(usize, bd.count[0] + bd.count[1] + bd.count[2]), rids.len); } test "storage bench: classification is deterministic and splits manifests from blobs" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .count = 120, .blob = 512 }); var file = fx.file; defer file.close(io); var fs = storage.FileStorage.init(io, file, fx.size); var store = try logstore.LogStore.openReadOnly(testing.allocator, fs.storageFile()); defer store.deinit(testing.allocator); const rids = try store.ridsInPersistedOrder(testing.allocator); defer testing.allocator.free(rids); var manifests: u64 = 0; var blobs: u64 = 0; for (rids) |rid| { const content = (try store.content(testing.allocator, rid)) orelse continue; defer testing.allocator.free(content); const cls = try classify(testing.allocator, content); // Determinism: the same bytes classify identically on a repeat call. try testing.expectEqual(cls, try classify(testing.allocator, content)); switch (cls) { .manifest => manifests += 1, .blob => blobs += 1, .other => {}, } } // The corpus mixes both, so both buckets are non-empty. try testing.expect(manifests > 0); try testing.expect(blobs > 0); } test "storage bench: a synthetic manifest parses and classifies as a check-in" { var buf: [256]u8 = undefined; const bytes = try buildManifest(&buf, 7); var m = try artifact.parse(testing.allocator, bytes); defer m.deinit(); try testing.expectEqual(artifact.ArtifactType.manifest, m.kind); try testing.expectEqual(TypeClass.manifest, try classify(testing.allocator, bytes)); } test "storage bench: measuring is read-only — the store is byte-identical afterward" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .count = 120, .blob = 512 }); var file = fx.file; defer file.close(io); const before = try testing.allocator.alloc(u8, @intCast(fx.size)); defer testing.allocator.free(before); try testing.expectEqual(@as(usize, @intCast(fx.size)), try file.readPositionalAll(io, before, 0)); // Run the full measurement into a discarding writer. var sink: [512]u8 = undefined; var dw: Io.Writer.Discarding = .init(&sink); var fs = storage.FileStorage.init(io, file, fx.size); try measure(testing.allocator, io, fs.storageFile(), fx.size, "test", &dw.writer); const after = try testing.allocator.alloc(u8, @intCast(fx.size)); defer testing.allocator.free(after); try testing.expectEqual(@as(usize, @intCast(fx.size)), try file.readPositionalAll(io, after, 0)); try testing.expectEqualSlices(u8, before, after); } test "storage bench: same seed reproduces a byte-identical corpus" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const cfg = Config{ .seed = 99, .count = 80, .blob = 256 }; const a = try Fixture.build(io, tmp.dir, cfg); var fa = a.file; defer fa.close(io); var fb = try tmp.dir.createFile(io, ".zit-b", .{ .read = true }); defer fb.close(io); var fs = storage.FileStorage.init(io, fb, 0); try buildCorpus(testing.allocator, fs.storageFile(), cfg); const b_size = try fs.storageFile().size(); try testing.expectEqual(a.size, b_size); const ba = try testing.allocator.alloc(u8, @intCast(a.size)); defer testing.allocator.free(ba); const bb = try testing.allocator.alloc(u8, @intCast(b_size)); defer testing.allocator.free(bb); _ = try fa.readPositionalAll(io, ba, 0); _ = try fb.readPositionalAll(io, bb, 0); try testing.expectEqualSlices(u8, ba, bb); } test "storage bench: dictionary projection is read-only — the store is byte-identical afterward" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .count = 160, .blob = 512 }); var file = fx.file; defer file.close(io); const before = try testing.allocator.alloc(u8, @intCast(fx.size)); defer testing.allocator.free(before); try testing.expectEqual(@as(usize, @intCast(fx.size)), try file.readPositionalAll(io, before, 0)); var sink: [512]u8 = undefined; var dw: Io.Writer.Discarding = .init(&sink); var fs = storage.FileStorage.init(io, file, fx.size); try probeDict(testing.allocator, io, fs.storageFile(), fx.size, "test", &dw.writer); const after = try testing.allocator.alloc(u8, @intCast(fx.size)); defer testing.allocator.free(after); try testing.expectEqual(@as(usize, @intCast(fx.size)), try file.readPositionalAll(io, after, 0)); try testing.expectEqualSlices(u8, before, after); } test "storage bench: the dictionary projection is deterministic and internally consistent" { var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; const fx = try Fixture.build(io, tmp.dir, .{ .seed = 7, .count = 160, .blob = 512 }); var file = fx.file; defer file.close(io); var fs = storage.FileStorage.init(io, file, fx.size); var store = try logstore.LogStore.openReadOnly(testing.allocator, fs.storageFile()); defer store.deinit(testing.allocator); const rids = try store.ridsInPersistedOrder(testing.allocator); defer testing.allocator.free(rids); const a = try computeProjection(testing.allocator, &store, rids); const b = try computeProjection(testing.allocator, &store, rids); // Same store -> byte-identical projection (no clock, no randomness). try testing.expectEqual(a, b); // Every full-form frame is accounted exactly once, either improved or // fell-back; the non-reconstructing set is a subset of the fell-back set. try testing.expectEqual(a.fullform_frames, a.improved + a.fell_back); try testing.expect(a.non_reconstruct <= a.fell_back); // Honesty: a projection that does not shrink a frame never counts it below // its current size, so the projected full-form total can never undershoot // the sum of the improved frames' current sizes — a coarse floor check is // that projected <= current + dict only if something improved. if (a.improved == 0) try testing.expect(a.fullform_projected == a.fullform_current); }