Skip to main content

native/fsst_nif/src/lib.rs

use fsst::Compressor;
use rustler::types::binary::{Binary, OwnedBinary};
use rustler::{Env, NifResult, Resource, ResourceArc};
use std::io::Write;

struct FsstTable {
    compressor: Compressor,
}

#[rustler::resource_impl]
impl Resource for FsstTable {}

#[rustler::nif]
fn train(samples: Vec<Binary>) -> NifResult<ResourceArc<FsstTable>> {
    if samples.is_empty() {
        return Err(rustler::Error::BadArg);
    }

    let values: Vec<&[u8]> = samples.iter().map(|sample| sample.as_slice()).collect();
    let compressor = Compressor::train(&values);

    Ok(ResourceArc::new(FsstTable { compressor }))
}

#[rustler::nif]
fn compress<'a>(env: Env<'a>, table: ResourceArc<FsstTable>, input: Binary) -> NifResult<Binary<'a>> {
    let compressed = table.compressor.compress(input.as_slice());
    Ok(binary_from_vec(env, compressed))
}

#[rustler::nif]
fn decompress<'a>(env: Env<'a>, table: ResourceArc<FsstTable>, input: Binary) -> NifResult<Binary<'a>> {
    let decompressed = table.compressor.decompressor().decompress(input.as_slice());
    Ok(binary_from_vec(env, decompressed))
}

fn binary_from_vec<'a>(env: Env<'a>, bytes: Vec<u8>) -> Binary<'a> {
    let mut binary = OwnedBinary::new(bytes.len()).unwrap();
    binary.as_mut_slice().write_all(&bytes).unwrap();
    binary.release(env)
}

rustler::init!("Elixir.FSST.Native");