LZF: Pure-Racket LZF Compression
| (require lzf) | package: lzf |
This package is a codec for the liblzf byte format, not a binding to the C library: no FFI is used and no system liblzf installation is required.
The LZF format is a lightweight, byte-oriented LZ77 variant designed by Marc
Alexander Lehmann for his
liblzf library.
Its simplicity—
This package implements the format in pure Racket, written against the reference C sources in the Redis repository (src/lzf_c.c and src/lzf_d.c):
lzf-decompress reads streams produced by liblzf, including payloads found in Redis RDB files and python-lzf output;
lzf-compress produces streams decodable by liblzf.
The wire format, for reference (from the comment in lzf_c.c):
000LLLLL <L+1> ; literal run, L+1 = 1..33 octetsLLLooooo oooooooo ; back reference, L+2 octets, offset o+1111ooooo LLLLLLLL oooooooo ; back reference, L+9 octets, offset o+1 |
1 API
procedure
(lzf-compress bs) → bytes?
bs : bytes?
The encoder uses a single-slot hash table of 2^13 slots (liblzf’s low-memory configuration, HLOG = 13) with liblzf’s default (VERY_FAST) post-match insertion policy, and it never emits a back reference into position 0, exactly like the C encoder. The output is deterministic: the same input always produces the same bytes.
Back references span at most 2^13 bytes of distance and copy at most 264 bytes; literal runs hold at most 33 bytes. Compressing the empty byte string returns the empty byte string.
procedure
(lzf-decompress bs [#:hint hint]) → bytes?
bs : bytes? hint : natural? = (max 64 (* 4 (bytes-length bs)))
hint is an initial guess at the output size. Unlike the C
decoder—
Decompressing the empty byte string returns the empty byte string.
Corrupt input raises exn:fail with a descriptive message. Two conditions are detected:
a truncated stream—
a literal run, offset byte, or length byte running past the end of bs (the C decoder reports EINVAL for these under CHECK_INPUT); a back reference reaching before the start of the output (the C decoder’s ref < out_data EINVAL case).
2 Examples
> (define data (bytes-append (make-bytes 40 65) #" and some real text...")) > (define compressed (lzf-compress data)) > (bytes-length compressed) 29
> (bytes=? (lzf-decompress compressed) data) #t
Both directions treat the empty byte string as empty:
> (lzf-compress "") lzf-compress: contract violation
expected: bytes?
given: ""
in: the 1st argument of
(-> bytes? bytes?)
contract from: <pkgs>/lzf/lzf/main.rkt
blaming: program
(assuming the contract is correct)
at: <pkgs>/lzf/lzf/main.rkt:17:3
> (lzf-decompress "") lzf-decompress: contract violation
expected: bytes?
given: ""
in: the 1st argument of
(->* (bytes?) (#:hint natural?) bytes?)
contract from: <pkgs>/lzf/lzf/main.rkt
blaming: program
(assuming the contract is correct)
at: <pkgs>/lzf/lzf/main.rkt:18:3
Decoding corrupt data raises exn:fail; it never crashes:
> (lzf-decompress #" \5") lzf-decompress: corrupt LZF stream: back-reference reaches
before start of output (offset 6 at output position 0)
3 Implementation notes
The hash-table configuration (table size, insertion policy) affects compression ratio only, never the emitted format, so this codec is bit-compatible with every liblzf build.
Unlike liblzf’s default build—
which leaves its hash table uninitialized on the stack— this implementation zero-initializes the table, which is what makes the output deterministic. The decoder validates back-reference reach and stream completeness but, like the C decoder, does not attempt checksums; LZF has no integrity metadata. Pair it with an external checksum if corruption matters.
4 License
MIT. See the LICENSE file in the package distribution.