Docs
What it’s for
lz4 binds liblz4 through a small C shim it loads at run time, and covers the three LZ4 formats data lakes use: bare blocks for Parquet’s LZ4_RAW pages, self-describing frames for Iceberg Puffin blobs, and the legacy Hadoop framing older Parquet files still use for LZ4. parquet-full and iceberg use it. It is a binding, not an implementation: the speed is liblz4’s.
Install
A new project needs Modular’s channel, pixi’s build preview and the compiler before the tin:
pixi init app -c https://conda.modular.com/max -c conda-forge && cd app
sed -i.bak 's/^\[workspace\]/[workspace]\npreview = ["pixi-build"]/' pixi.toml
pixi add "mojo-compiler==1.2.0"
pixi shelf add lz4-mojo # no shelf dependencies; builds the shim against conda-forge lz4-c
pixi builds the shim, liblz4mojo.dylib on macOS and liblz4mojo.so on Linux, into the environment’s lib. You need no link flags.
Quick start
Compress a page as a bare block and as a frame, and decompress both:
from lz4 import compress_block, compress_frame, decompress_block, decompress_frame
def main() raises:
var page = List[UInt8]()
for i in range(100_000):
page.append(UInt8(i % 7))
# Parquet LZ4_RAW: a bare block; the page header keeps the length.
var block = compress_block(page)
var back = decompress_block(block, len(page))
print("block:", len(block), "bytes,", back == page)
# Iceberg Puffin: a self-describing LZ4 frame, content size recorded.
var frame = compress_frame(page)
print("frame:", len(frame), "bytes,", decompress_frame(frame) == page)
API
Everything is exported from lz4.
Blocks (LZ4_RAW), which carry no size of their own:
compress_block(data: Span[UInt8, _], acceleration: Int = 1) raises -> List[UInt8]: the fast encoder; a higheraccelerationtrades ratio for speed.compress_block_hc(data: Span[UInt8, _], level: Int = 9) raises -> List[UInt8]: the high-compression encoder, level 1 to 12; same wire format.decompress_block(data: Span[UInt8, _], uncompressed_size: Int) raises -> List[UInt8]: needs the exact decoded size.decompress_block_into(data: Span[UInt8, _], dst: Span[mut=True, UInt8, _]) raises -> Int: into a buffer you own; returns the bytes written.
Frames (LZ4F):
compress_frame(data: Span[UInt8, _], level: Int = 0, content_size: Bool = True) raises -> List[UInt8]: level 0 is the fast encoder, 1 to 12 the HC one. The content size is recorded by default, as Puffin requires.decompress_frame(data: Span[UInt8, _]) raises -> List[UInt8]: sized from the header when it has a size; otherwise it grows the buffer and retries.frame_content_size(data: Span[UInt8, _]) raises -> Optional[Int]: the size in the header, or None.is_lz4_frame(data: Span[UInt8, _]) -> Bool: checks the magic number, without loading the library.
Legacy Parquet LZ4, read only:
decompress_hadoop(data: Span[UInt8, _], uncompressed_size: Int) raises -> List[UInt8]: the[BE u32 uncompressed][BE u32 compressed][block]chunksparquet-mrwrote.
Errors and limits
- Errors name the call:
lz4.decompress_block failed: corrupt input or destination too small (rc=<n>),lz4.decompress_frame failed: corrupt or truncated input,lz4.decompress_hadoop: truncated block.frame_content_sizeraises on bytes that aren’t a frame header. - Sizes are C
inton both sides, so one call handles at most about 2 GiB. - LZ4F stores “no size” and “size 0” the same way, so
frame_content_sizereturns None for both. Content and block checksums in a frame are verified by liblz4. - The shim is looked for in
$CONDA_PREFIX/lib, then next to the executable and in its../lib, then on the binary’s rpath. If none has it, the first call raiseslz4.mojo: cannot load liblz4mojo.dylib; looked in: ...and says to setCONDA_PREFIX. - The shim is opened once per process and never closed, so page-sized calls stay cheap.
Tests
pixi run test round-trips blocks and frames from 0 B to 1 MiB, decodes vectors produced by CPython’s lz4 package and baked in as constants, checks the corrupt-input errors and decodes a Hadoop-framed stream it builds itself. It runs again with CONDA_PREFIX unset to test the library search. CI runs it on Linux and macOS with Mojo 1.2.0 and nightly.
The full reference is the README on GitHub.