Skip to content
iderex edited this page Jul 19, 2026 · 4 revisions

cudec

Open-source GPU decompression for the standard formats — batch-decode LZ4 (and, on the roadmap, Snappy, GDeflate, and Zstd) on an NVIDIA GPU. Decode-only, batch-oriented, auditable, fail-closed, and diff-tested against the reference implementations.

This wiki is a public reference. It tracks what cudec does today, not what it aims to do; anything not listed as implemented below is not implemented yet.

What cudec is

GPU decompression matters wherever decode throughput is the bottleneck: asset streaming, analytics scans, ML data loading, checkpoint restore. NVIDIA's nvCOMP is the production-grade library in this space, but it has been proprietary since v2.3; there is no maintained open-source library that decodes the standard formats on the GPU. cudec fills that gap — not on price (nvCOMP is free to use) but on the properties a closed binary cannot offer: auditability (every bounds check is readable, tested, and fuzz-diffed), portability (a HIP port is a planned milestone), and hackability.

The scope is deliberate: decode-only (compression stays on the CPU, where the encoders and the flexibility live) and batch-oriented (the GPU wins when thousands of independent chunks decode in parallel — a single small file over a cold PCIe bus loses to the CPU, and cudec does not claim otherwise).

Supported formats — implemented vs. planned

cudec is under active development. This table is the honest state of the tree and is the source of truth for what works today.

Format / capability Milestone Status ABI entry point
LZ4 block decode (batch, device-resident) M1 implemented cudec_lz4_decompress_batch
LZ4 frame decode (.lz4, block-independent subset) M2 implemented cudec_lz4f_decompress
LZ4 pinned-host streaming decode M2 implemented cudec_lz4_decompress_stream
Snappy decode M3 planned
GDeflate decode M4 planned
Zstd decode M5 planned
HIP portability (AMD) M6 planned

Notes on the implemented subset:

  • LZ4 frame decodes the block-independent subset. A valid frame that uses a feature cudec does not decode yet — block-linked mode (liblz4's frame-compressor default) or a dictionary id — returns CUDEC_ERR_UNSUPPORTED, which is distinct from a corrupt-input error. The header, block, and content checksums and the optional declared content size are verified fail-closed when present.
  • All three paths are fail-closed: a malformed, truncated, or hostile bitstream produces a defined error, never an out-of-bounds access and never partial output presented as success. Every reject path has a negative test, and decode output is diff-tested against liblz4 on real and mutated corpora.

The C ABI

The public surface is a single C header, include/cudec.h. It is C-compatible, requires no CUDA headers, never throws across the boundary, and never reports output it did not fully validate. Every function returns a cudec_status:

Status Meaning
CUDEC_OK success
CUDEC_ERR_INVALID_ARGUMENT a NULL/misaligned argument, empty batch, or over-limit batch — rejected, no launch
CUDEC_ERR_CORRUPT_INPUT the bitstream failed validation
CUDEC_ERR_OUTPUT_TOO_SMALL the destination capacity is too small for the decoded output
CUDEC_ERR_CUDA a device or host resource failure
CUDEC_ERR_NOT_IMPLEMENTED reserved; defined in the ABI, not currently returned by any entry point
CUDEC_ERR_UNSUPPORTED a valid frame using a feature outside cudec's supported subset

The header is the authoritative contract for each entry point — argument validation, the async/sync semantics of the batch call, and the fixed 16-byte cudec_chunk_result layout the device writes per chunk. The C API reference is a browsable, function-by-function summary of it, including the reusable streaming context (cudec_stream_ctx_create / cudec_lz4_decompress_stream_ctx / cudec_stream_ctx_destroy) that repeated-decode callers should prefer over the one-shot streaming entry point.

Building

Two builds. The host-only build needs only a C compiler and compiles the ABI and version surface — not the decoder. The CUDA build is the decoder: opt-in, fail-closed, and maintained through a digest-pinned dev container; a GPU is required only for the GPU-labeled tests, not for the build.

Host-only build (the ABI and version surface; CMake ≥ 3.24 and a C compiler, no CUDA toolchain):

cmake -B build && cmake --build build

CUDA build (the decoder) — build everything and run the host-side test subset (no GPU needed):

docker run --rm -v "$PWD:/w" -w /w \
  nvidia/cuda:12.6.2-devel-ubuntu24.04@sha256:738fba0fbdb225b7a2931c58a5c8f03a84d3cd2f6a84975826a157339ef750b8 \
  sh -c "apt-get update -q && apt-get install -yq cmake >/dev/null && \
         cmake -B build-cuda -DCUDEC_ENABLE_CUDA=ON && \
         cmake --build build-cuda -j && \
         ctest --test-dir build-cuda --no-tests=error -LE gpu --output-on-failure"

To run the full suite including the on-device tests, add --gpus all to the docker run line and drop -LE gpu. The CUDA engine is opt-in and fail-closed: -DCUDEC_ENABLE_CUDA=ON errors without a toolchain rather than silently falling back to host-only.

Security model

cudec decodes untrusted input by design — compressed bitstreams are classic attack surface, and a GPU decoder that trusts its input has a very wide blast radius. The posture is central, not incidental:

  • fail-closed decoding: a malformed, truncated, or hostile bitstream produces a defined error, never an out-of-bounds read or write and never partial output presented as success;
  • every reject path has an explicit negative test, and decode output is fuzz-diffed against the CPU reference implementations;
  • all size and offset arithmetic is overflow-checked before use;
  • the toolchain and all CI actions are pinned to exact hashes.

Report a suspected vulnerability (for example a crafted bitstream causing memory corruption) privately via GitHub private vulnerability reporting, not a public issue. See SECURITY.md.

Benchmarking discipline

A performance claim without its methodology is not a claim. Every number cudec publishes carries its full methodology block — GPU model, driver, CUDA version, corpus, chunk-size distribution, and run count — as emitted by the benchmark harness (bench/bench_lz4); a bare number cannot be produced, by construction. The Benchmarks page summarizes the measured LZ4 baselines (with methodology inseparable from each figure); the raw record lives in BENCHMARKS.md. Corpora are fetched hash-pinned via bench/get-corpora.sh and are never committed.

Contributing

cudec is issue-driven and gate-driven: every change starts as a triaged GitHub issue and lands as a gated PR off main. See CONTRIBUTING.md.