//! Pack-time zstd ENCODER seam (offline `zit pack` only). Links C libzstd
//! (allyourcodebase/zstd) — confined to this module so the core `zit` library
//! stays C-free. DECODE is never here: the runtime read path uses pure-Zig
//! `std.compress.zstd`. The artifact hash is over UNCOMPRESSED bytes (§7.7) and
//! the wire re-emits the §4.1 zlib container, so this storage codec never
//! touches identity or interop. Frames are written WITHOUT a content checksum
//! (zit verifies §7.7 itself; 0.16 std's zstd checksum path is unimplemented).
const std = @import("std");
const c = @cImport(@cInclude("zstd.h"));
pub const Error = error{ ZstdCompress, OutOfMemory };
/// Compress `src` with libzstd at `level` (19 for pack). Simple one-shot API,
/// which emits no content checksum — exactly what std.compress.zstd decodes.
pub fn compress(gpa: std.mem.Allocator, src: []const u8, level: c_int) Error![]u8 {
const bound = c.ZSTD_compressBound(src.len);
const dst = try gpa.alloc(u8, bound);
const n = c.ZSTD_compress(dst.ptr, bound, src.ptr, src.len, level);
if (c.ZSTD_isError(n) != 0) {
gpa.free(dst);
return error.ZstdCompress;
}
// Trim the bound-sized buffer to the exact compressed length. resize shrinks
// in place when it can; otherwise copy into an exact allocation so the caller
// frees a slice whose length matches its allocation.
if (gpa.resize(dst, n)) return dst[0..n];
defer gpa.free(dst);
const out = try gpa.alloc(u8, n);
@memcpy(out, dst[0..n]);
return out;
}
test "zstd §storage: libzstd level-19 encode round-trips through pure-Zig std decode" {
const gpa = std.testing.allocator;
// ~256 KiB with long-distance redundancy (a solid-block stand-in).
const orig = try gpa.alloc(u8, 256 * 1024);
defer gpa.free(orig);
for (orig, 0..) |*b, i| b.* = @truncate((i / 61) *% 7 + i);
const comp = try compress(gpa, orig, 19);
defer gpa.free(comp);
try std.testing.expect(comp.len < orig.len); // it actually compressed
// Decode with pure-Zig std (verify_checksum=false — zit does §7.7 itself).
var r = std.Io.Reader.fixed(comp);
const buf = try gpa.alloc(u8, std.compress.zstd.default_window_len + std.compress.zstd.block_size_max);
defer gpa.free(buf);
var dec = std.compress.zstd.Decompress.init(&r, buf, .{ .verify_checksum = false });
const out = try dec.reader.allocRemaining(gpa, .unlimited);
defer gpa.free(out);
try std.testing.expectEqualSlices(u8, orig, out);
}
test "zstd §storage: encode is deterministic (same input, same bytes) for pack idempotence" {
const gpa = std.testing.allocator;
const src = "the quick brown fox " ** 4096;
const a = try compress(gpa, src, 19);
defer gpa.free(a);
const b = try compress(gpa, src, 19);
defer gpa.free(b);
try std.testing.expectEqualSlices(u8, a, b);
}