Skip to content

Add DEFLATE inflate/deflate builtins via system zlib (fixes #684)#693

Merged
InauguralPhysicist merged 1 commit into
InauguralSystems:mainfrom
Nitjsefnie-OSC:inflate-684
Jul 22, 2026
Merged

Add DEFLATE inflate/deflate builtins via system zlib (fixes #684)#693
InauguralPhysicist merged 1 commit into
InauguralSystems:mainfrom
Nitjsefnie-OSC:inflate-684

Conversation

@Nitjsefnie

Copy link
Copy Markdown
Contributor

Implements #684: DEFLATE decompression (and its write-side duals) as thin wrappers over the system zlib, so scripts can read real-world .xlsx/.ods ZIP entries and .gz streams.

The builtins

Builtin windowBits Role
inflate −15 raw DEFLATE decompression (ZIP member format — .xlsx/.ods entries)
deflate −15 raw DEFLATE compression, dual of inflate
zlib_inflate 15+32 wrapped decompression, auto-detects zlib and gzip headers — makes plain .gz readable
zlib_deflate 15 zlib-wrapped compression, dual (default level)

Byte representation mirrors read_bytes/write_bytes exactly (list of 0-255 ints or a buffer in, fresh list out). Corrupt/truncated input raises a catchable value error, never a crash. Decompressed output is capped at 256 MiB (zip-bomb bound, matching the sandbox_run default budget) — a design choice not in the issue text; tune or drop freely. All four are in SANDBOX_ALLOW (pure bytes→bytes, no fs/proc/net/env access).

Gating (zero-dependency build preserved)

EIGENSCRIPT_EXT_ZLIB macro + a new make zlib target, mirroring the make http precedent. Default OFF: every existing build target is byte-for-byte unchanged and build.sh is untouched.

One disclosed deviation from the extension precedent (deliberate): disabled extensions normally leave their builtins unregistered ("undefined variable"). Here the four builtins register unconditionally and the gated-out bodies raise a catchable "<name>: compiled without zlib support (rebuild with make zlib)" — a clear, actionable error for scripts probing capability. Happy to switch to the unregistered convention if you prefer strict consistency. (Error kind is value — the closed ErrKind set has no capability kind.)

Tests + verification

  • tests/test_inflate.eigs (22 checks), wired as section [123] probe-gated exactly like [44] HTTP: minimal builds run a 1-check stub-contract test, zlib builds run the full 22. The gating can't misfire — the stub string only exists in the !EIGENSCRIPT_EXT_ZLIB compilation branch.
  • Coverage: round-trips (incl. empty + 8000-byte payloads), fixed known-vectors for raw, zlib, and gzip (pins inflate independently of our own deflate), buffer inputs, RFC 1950 header pin, corrupt/truncated streams, wrong-type shapes.
  • Four suite legs green: minimal 3131/3131, zlib 3152/3152, ASan 3135/3135, ASan+UBSan+zlib 3156/3156 — no sanitizer/leak reports. stdlib_index_check (+selftest), doc_drift_check, freestanding_check all green.
  • Independently cross-checked against Python's zlib on payloads the tests don't use, both directions (our inflate ← python's deflate; python's decompress ← our deflate), plus gzip auto-detect. Mutation-verified: flipping inflate's windowBits −15→15 fails all 22 checks.
  • A 300 MiB-of-zeros bomb (305 KB compressed) hits the cap with a catchable limit error in <1 s.

Maintainer-facing notes

  • make full does not gain zlib (kept strictly to the opt-in mirror of make http) — say the word and I'll add -DEIGENSCRIPT_EXT_ZLIB=1 -lz there.
  • CI as-is has no make zlib leg, so [123] exercises only the stub contract in CI until one is added.
  • Drive-by observation, left untouched: docs/BUILTINS.md:3 says "199 core" builtins but src/builtins.c registered 228 before this change (232 after) — stale count, not mechanically gated.

Generated by Claude Fable 5 (brief, review), Kimi K3 (implementation)

…s#684)

Four codecs as thin wrappers over the system zlib (-lz), two dual pairs
in the InauguralSystems#677 sense:

- inflate / deflate: raw DEFLATE (windowBits -15) — the ZIP member
  format, so .xlsx/.ods entries are readable from script.
- zlib_inflate / zlib_deflate: zlib-wrapped; inflate uses windowBits
  15+32, auto-detecting zlib AND gzip headers — that is what makes
  plain .gz files readable.

Byte representation mirrors read_bytes/write_bytes exactly: a list of
ints 0-255 (mod 256) or a buffer in, a fresh list of ints 0-255 out.
Corrupt/truncated input raises a catchable `value` error, never a
crash; decompressed output is capped at 256 MiB (`limit`, zip-bomb
bound, matching the sandbox_run default budget).

Gating preserves the zero-dependency minimal build via the existing
EIGENSCRIPT_EXT_* mechanism (same pattern as `make http`): default OFF,
`make zlib` opts in. Compiled without zlib the four names stay
registered — so scripts feature-detect with try/catch and the sandbox
fail-closed allowlist can name real builtins — but every call raises
"compiled without zlib support". All four are pure bytes<->bytes and
join SANDBOX_ALLOW.

Tests: tests/test_inflate.eigs (22 checks) — round-trip identity for
both pairs incl. empty and 8 KB inputs, fixed known-vectors generated
by python's zlib (inflate pinned independently of our deflate), gzip
auto-detect, buffer input, corrupt/truncated catchable errors, and the
wrong-type error shape. run_all_tests.sh [123] probe-gates the section
like [44] HTTP: the real suite runs only under `make zlib`; on minimal
builds it instead asserts the stub raises the documented error.

Suites: release minimal 3131/3131, release zlib 3152/3152,
asan minimal 3135/3135, asan+zlib 3156/3156 (leaks on, all zero-fail).

Co-Authored-By: Kimi K3 <noreply@kimi.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants