radlang — Compression (compress)
The compress namespace provides gzip and zlib(deflate) stream compression, plus
ZIP archive create/extract. It is a compiler-intrinsic namespace — available
everywhere with no import — backed by a vendored, statically-linked miniz (no external zlib dependency).
For language fundamentals see the Overview. The HTTP server uses this same codec to gzip responses automatically; see HTTP.
Overview
| Method | Returns | Description |
|---|---|---|
compress.gzip(string\|byte[] input [, map meta]) | byte[] | Compress to a gzip stream (RFC 1952) |
compress.gunzip(byte[] data) | string | byte[] | Decompress a gzip stream |
compress.deflate(string\|byte[] input) | byte[] | Compress to a zlib stream (RFC 1950) |
compress.inflate(byte[] data) | string | byte[] | Decompress a zlib stream |
compress.gzipInfo(byte[] data) | string | Read a gzip stream’s metadata as JSON |
compress.unzip(string\|byte[] src, string destDir) | int | Extract a ZIP archive to a directory; returns file count |
compress.zip(string destPath, string[] files) | int | Create a ZIP archive from files; returns count added |
gzip/gunzip produce and consume the gzip container (magic header + CRC-32 +
size trailer) that HTTP labels Content-Encoding: gzip. deflate/inflate use
the zlib container HTTP labels Content-Encoding: deflate.
Text and binary (input + output)
Compression accepts either a string or a byte[] — the same method handles
both:
byte[] a = compress.gzip("some text") // from a string
byte[] b = compress.gzip(fs.readBytesSync("photo.raw")) // from bytes Decompression’s output type is chosen by the declaration target — declare the
result string for text, or byte[] for binary-safe bytes:
string text = compress.gunzip(a) // text (NUL-terminated)
byte[] raw = compress.gunzip(b) // raw bytes, safe for binary payloads Prefer the byte[] form for any payload that may contain NUL bytes (images,
audio, serialized binary) — the string form is meant for text and stops at the
first NUL.
Round-trip a string:
fn main() {
string original = "the quick brown fox jumps over the lazy dog"
byte[] packed = compress.gzip(original)
string restored = compress.gunzip(packed)
sys.output(restored) // the quick brown fox jumps over the lazy dog
} Persist a compressed payload with the fs byte APIs:
fn main() {
byte[] packed = compress.gzip(report)
fs.writeBytesSync("report.json.gz", packed)
byte[] onDisk = fs.readBytesSync("report.json.gz")
string report = compress.gunzip(onDisk)
} deflate / inflate behave the same over the zlib container.
gzip metadata
By default gzip writes a minimal, deterministic header (no timestamp, no
filename) so the same input always yields the same bytes — useful for caching and
ETags. Pass an optional metadata map to embed a filename, modification time, or
comment in the gzip header:
byte[] gz = compress.gzip(body, {
"name": "report.json",
"mtime": "1730400000", // unix seconds
"comment": "nightly export"
}) Read a gzip stream’s metadata back as a JSON object with gzipInfo (missing
fields come back as "" / 0):
string info = compress.gzipInfo(gz)
// {"name":"report.json","mtime":1730400000,"comment":"nightly export"}
// Parse it into a struct you define, or navigate it dynamically with json.*
type GzipMeta { string name, long mtime, string comment }
GzipMeta m = json.parse<GzipMeta>(info)
sys.output(m.name) // report.json deflate (zlib) has no metadata slot, so there’s nothing to set there.
ZIP archives
Create a ZIP from a list of file paths (each stored under its basename), and
extract an archive to a directory. Both zip and unzip return the number of
files processed.
fn main() {
int added = compress.zip("bundle.zip", ["a.txt", "assets/logo.png"])
// bundle.zip now contains "a.txt" and "logo.png"
int files = compress.unzip("bundle.zip", "out/")
sys.output(files) // 2
} unzip accepts the archive as a path or as byte[], so you can extract one
you already hold in memory (e.g. a download):
byte[] archive = http.getSync("https://example.com/pkg.zip").body // (bytes)
int files = compress.unzip(archive, "pkg/") Extraction is hardened against zip-slip: entries with absolute paths or .. components are skipped rather than written outside destDir. Parent directories
are created as needed.
Notes
- Compression level is the balanced default (zlib level 6); it is not currently configurable.
- Decompression is size-safe: a malformed stream yields an empty result rather than crashing.
- HTTP is automatic. You rarely need
compressfor web responses — the server gzips eligible bodies and the client transparently inflates them. Reach for this namespace to compress files, cache entries, message payloads, or to pack/unpack archives outside the HTTP path. - ZIP scope. The archive API is intentionally minimal (extract-all and create-from-paths). In-memory per-entry read/listing and richer writing are a planned enhancement.