Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crates.io Actions Status Documentation codecov Dependency status

ricecomp

A Rust implementation of the Rice compression algorithm used by CFITSIO for the tile-compression of FITS images. It is a transpiled and refactored port of ricecomp.c, originally written by Richard White at STScI and contributed to CFITSIO in July 1999.

Rice coding is a fast, lossless entropy coder for integer data. It works best on images whose adjacent pixels are strongly correlated (astronomical frames, detector readouts, and similar), where the pixel-to-pixel differences are small and cluster near zero. For a full walk-through of how the coder works, see ALGORITHM.md.

Features

  • Lossless round-trip compression and decompression of i32, i16, and i8 pixel arrays.
  • Streaming encoder generic over any [std::io::Write] sink.
  • Decoder writes into a caller-supplied output buffer so allocations can be reused.
  • Graceful, Result-based error handling — a truncated or malformed stream returns an error instead of panicking.
  • No external runtime dependencies.
  • Bit-compatible with CFITSIO's fits_rcomp / fits_rdecomp.

Installation

[dependencies]
ricecomp = "0.5"

Usage

The API is split into two modules:

  • write::RCEncoder — compresses a slice of pixels into a byte sink.
  • read::RCDecoder — decompresses a byte slice back into pixels.

Both take a block size (nblock), the number of pixels that share a single Rice parameter. CFITSIO uses 32 for images; larger blocks adapt more slowly to local statistics, smaller blocks spend more overhead on per-block headers.

The number of pixels (nx) and the block size are not stored in the stream, so the decoder must be given the same values that were used to encode.

Round trip

use ricecomp::read::RCDecoder;
use ricecomp::write::RCEncoder;

let pixels: [i32; 8] = [100, 101, 103, 102, 104, 108, 110, 109];
let block_size = 32;

// Encode into any std::io::Write sink (here a Vec<u8>).
let mut compressed: Vec<u8> = Vec::new();
let mut encoder = RCEncoder::new(&mut compressed);
let written = encoder
    .encode(&pixels, pixels.len(), block_size)
    .expect("encoding failed");
println!("compressed {} pixels into {} bytes", pixels.len(), written);

// Decode into a caller-supplied buffer.
let decoder = RCDecoder::new();
let mut output = vec![0u32; pixels.len()];
decoder
    .decode(&compressed, pixels.len(), block_size, &mut output)
    .expect("decoding failed");

let recovered: Vec<i32> = output.iter().map(|&x| x as i32).collect();
assert_eq!(recovered, pixels);

RCEncoder::new accepts anything implementing std::io::Write, so you can compress straight into a Vec<u8>, a file, or any other writer. The decoder writes into the output slice you pass in, letting you reuse allocations across calls.

16-bit and 8-bit data

The same pattern applies to narrower integers via the dedicated variants:

Element type Encode Decode Decode output
i32 encode decode &mut [u32]
i16 encode_short decode_short &mut [u16]
i8 encode_byte decode_byte &mut [u8]

Each width uses Rice parameters tuned to its dynamic range, so the streams are not interchangeable — decode with the same variant you encoded with.

Error handling

  • write::EncodeErrorZeroSizeInput (empty input or zero block size) and EndOfBuffer (the writer could not accept more bytes).
  • read::DecodeErrorZeroSizeInput, NotProperlyAllocated (input shorter than the mandatory 4-byte header), and EndOfBuffer (the compressed stream ended before the requested number of pixels was produced).

References

License

Licensed under the MIT license. The bundled CFITSIO license, which covers the original C implementation this port derives from, is included under licenses/.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages