qoi-rs is a minimal, auditable, safe Rust port of the pinned QOI reference
encoder and decoder. The production library exposes a small in-memory API for
encoding raw RGB/RGBA pixels to QOI bytes and decoding QOI bytes back to raw
pixels, with C-reference compatibility tests, fuzzing evidence and a
reproducible benchmark report.
- Entirely safe production Rust, enforced with
#![forbid(unsafe_code)]. - Byte-for-byte encoder agreement with the pinned C implementation across deterministic and fuzz-generated test inputs.
- Strict malformed-input validation for headers, chunk bounds, pending runs, trailing data and the exact end marker.
- Deterministic C/Rust differential tests plus differential and arbitrary-input fuzz targets.
- Reproducible benchmark report comparing the Rust codec with the pinned C implementation under equivalent in-memory workloads.
git clone --recurse-submodules \
https://github.com/the-last-working-build/qoi-rs.git
cd qoi-rs
./scripts/verify.shFor normal development:
cargo build
cargo testuse qoi_rs::{
Channels, ColorSpace, ImageDesc, decode, encode,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let pixels = vec![255, 0, 0, 255];
let desc = ImageDesc {
width: 1,
height: 1,
channels: Channels::Rgba,
colorspace: ColorSpace::SrgbWithLinearAlpha,
};
let encoded = encode(&pixels, desc)?;
let decoded = decode(&encoded, None)?;
assert_eq!(decoded.pixels, pixels);
assert_eq!(decoded.desc, desc);
Ok(())
}decode(input, None) returns the channel count declared in the QOI header.
Passing Some(Channels::Rgb) or Some(Channels::Rgba) requests that output
layout while preserving the original header metadata in DecodedImage::desc.
The source of truth is the pinned upstream QOI repository:
97bacc86a9c4abf5a2d452102dc26546c4c670b9
Pinned source hashes are recorded in SOURCE_HASHES.txt and
checked by ./scripts/verify.sh.
For valid inputs covered by deterministic and fuzz testing:
- Rust encoding matches the pinned C encoder byte-for-byte.
- Rust decodes C-produced streams to the original pixels.
- C decodes Rust-produced streams to the original pixels.
- Rust decodes Rust-produced streams to the original pixels.
- Requested RGB/RGBA output agrees between C and Rust.
The Rust decoder is stricter for malformed streams. It rejects:
- Missing or incorrect eight-byte end markers.
- Multi-byte chunks whose operands would cross the logical chunk boundary.
- Final RUN chunks that describe more pixels than the header permits.
- Extra unused chunk bytes before the end marker.
These checks are intentional safety validations and do not change behavior for valid QOI streams. See DECISIONS.md and SPEC.md.
Deterministic integration tests compile a small C reference executable from the
pinned reference/qoi/qoi.h and compare C and Rust codecs across RGB/RGBA
fixtures, repeated pixels, INDEX reuse, small and large deltas, alpha changes
and edge byte values.
Fuzzing evidence from fuzz/log.txt:
- Differential fuzz target: 1,502,450 executions.
- Arbitrary Rust decoder target: 1,917,549 executions.
- Combined executions: 3,419,999.
- Crashes: 0.
The fuzz-only crate links the C implementation for differential testing. The released Rust library does not link to C.
Corrected benchmark summary from bench/results/results.txt:
| Fixture | Operation | C median | Rust median | Rust/C |
|---|---|---|---|---|
| Flat RGBA | Encode | 1.123 ms | 4.391 ms | 3.91x |
| Flat RGBA | Decode | 1.401 ms | 5.714 ms | 4.08x |
| Gradient RGB | Encode | 6.770 ms | 8.685 ms | 1.28x |
| Gradient RGB | Decode | 3.913 ms | 8.086 ms | 2.07x |
| Noise RGBA | Encode | 4.325 ms | 8.782 ms | 2.03x |
| Noise RGBA | Decode | 4.137 ms | 9.432 ms | 2.28x |
These results were collected on one machine and are not universal performance claims. The fair benchmark design is documented in bench/methodology.md.
The port prioritizes memory safety, explicit validation and auditable equivalence over initial optimization.
The Rust implementation is currently slower than the pinned C reference,
especially for run-heavy data. Likely optimization areas include output-buffer
initialization, per-pixel bounds checks, state-machine structure and reducing
individual Vec::push operations.
These optimizations were intentionally deferred until after correctness was established through deterministic differential testing and fuzzing.
The safe Rust port produced byte-for-byte compatible encodings and equivalent decoding results across the deterministic and fuzz-generated valid inputs tested.
The Rust implementation is currently approximately 1.28x-4.08x slower than the pinned C implementation across the selected benchmark workloads.
Run the complete verification suite:
./scripts/verify.shRun the benchmark:
cargo run --release --manifest-path bench/Cargo.tomlRun the five-minute fuzz targets locally with nightly Rust:
cargo +nightly fuzz run differential -- -max_total_time=300
cargo +nightly fuzz run decode_arbitrary -- -max_total_time=300src/: safe Rust production encoder, decoder, errors and public types.tests/differential/: deterministic C/Rust integration harness.tools/c-reference/: small C reference executable used by tests.fuzz/: cargo-fuzz package and fuzz-only C reference wrapper.bench/: standalone benchmark package, methodology and recorded results.reference/qoi/: pinned upstream C implementation as a submodule.SPEC.md: implemented QOI behavior and strict-validation rules.DECISIONS.md: architectural decisions and compatibility rationale.
The design record is in DECISIONS.md. The implemented behavior
is in SPEC.md. Together they document why the production codec is
safe Rust, why outputs are owned Vec<u8> values, how source and output channel
counts are represented, and where malformed-input validation is stricter than C.
This project is a Rust port of QOI - The Quite OK Image Format, originally created by Dominic Szablewski and licensed under the MIT License.
The Rust port is licensed under the terms in LICENSE.
qoi-rs is a small, auditable Rust port of the pinned QOI C reference
implementation.
The project aims to:
- Preserve the reference encoder's deterministic output.
- Decode valid QOI streams equivalently to the reference implementation.
- Use safe Rust throughout the production library.
- Reject malformed inputs without out-of-bounds reads or unchecked arithmetic.
- Make compatibility decisions explicit and testable.
- Maintain reproducible differential, fuzzing and benchmark evidence.
The project does not currently aim to:
- Be the fastest available QOI implementation.
- Provide streaming I/O APIs.
- Support
no_std. - Replace more mature general-purpose Rust QOI crates.
- Preserve the C decoder's permissive behavior for malformed streams.
The QOI encoder and decoder are feature-complete for the implemented QOI 1.0 format operations.
Current status:
- All six QOI chunk operations are supported.
- The encoder is compared byte-for-byte with the pinned C encoder.
- The decoder is cross-checked against C-produced streams.
- The production library forbids unsafe Rust.
- Strict malformed-input checks are intentional and documented.
- The public API should still be considered experimental until version 1.0.
This project began as a time-bounded C-to-Rust porting exercise in August 2026. It is now maintained as an independent Rust port and codec-engineering project.