use crate::ffi::{Buffer, Slice};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Symbol {
pub code: u32,
pub length: u8,
}
impl Symbol {
pub fn build(symbol: &crate::helpers::huffman::Symbol) -> Self {
Self { code: symbol.code, length: symbol.length }
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DecodeError {
Ok = 0,
InvalidPadding = 1,
UnknownSymbol = 2,
Invalid = 3,
}
impl DecodeError {
pub fn from_code(code: i32) -> Option<Self> {
Some(match code {
0 => Self::Ok,
1 => Self::InvalidPadding,
2 => Self::UnknownSymbol,
3 => Self::Invalid,
_ => return None,
})
}
pub fn of(error: &crate::helpers::huffman::DecodeError) -> Self {
match error {
crate::helpers::huffman::DecodeError::InvalidPadding => Self::InvalidPadding,
crate::helpers::huffman::DecodeError::UnknownSymbol => Self::UnknownSymbol,
}
}
pub unsafe fn report(out: *mut DecodeError, error: Self) {
if !out.is_null() {
unsafe { *out = error };
}
}
pub fn message(&self) -> &'static str {
match self {
Self::Ok => "ok",
Self::InvalidPadding => "huffman padding is not all one-bits",
Self::UnknownSymbol => "huffman code does not map to a known symbol",
Self::Invalid => "invalid argument",
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_error_message(error: i32) -> Slice {
Slice::maybe(DecodeError::from_code(error).map(|error| error.message()))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_eos() -> u16 {
crate::helpers::huffman::EOS
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_table_len() -> usize {
crate::helpers::huffman::TABLE.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_symbol(index: usize) -> Symbol {
match crate::helpers::huffman::table().get(index) {
Some(symbol) => Symbol::build(symbol),
None => Symbol { code: 0, length: 0 },
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_length(index: usize) -> u8 {
crate::helpers::huffman::LENGTHS.get(index).copied().unwrap_or(0)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_nibble() -> usize {
crate::helpers::huffman::NIBBLE
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_emit() -> u8 {
crate::helpers::huffman::EMIT
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_fail() -> u8 {
crate::helpers::huffman::FAIL
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_ended() -> u8 {
crate::helpers::huffman::ENDED
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Transition {
pub next: u16,
pub symbol: u8,
pub flags: u8,
}
impl Transition {
pub fn build(transition: &crate::helpers::huffman::Transition) -> Self {
Self { next: transition.next, symbol: transition.symbol, flags: transition.flags }
}
}
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Branch {
None = 0,
Node = 1,
Symbol = 2,
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_states() -> usize {
crate::helpers::huffman::decode_table().rows.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_nodes() -> usize {
crate::helpers::huffman::decode_table().branches.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_transition(state: usize, nibble: u8) -> Transition {
match crate::helpers::huffman::decode_table().rows.get(state) {
Some(row) => Transition::build(&row[nibble as usize & (crate::helpers::huffman::NIBBLE - 1)]),
None => Transition::build(&crate::helpers::huffman::Transition::STUCK),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_huffman_accepting(state: usize) -> bool {
crate::helpers::huffman::decode_table().accepting.get(state).copied().unwrap_or(false)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_huffman_step(node: usize, bit: bool, value: *mut u32) -> Branch {
let (branch, reached) = match crate::helpers::huffman::decode_table().step(node, bit) {
Some(crate::helpers::huffman::Branch::Node(index)) => (Branch::Node, index as u32),
Some(crate::helpers::huffman::Branch::Symbol(symbol)) => (Branch::Symbol, symbol as u32),
None => (Branch::None, 0),
};
if !value.is_null() {
unsafe { *value = reached };
}
branch
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_huffman_encoded_len(data: *const u8, data_len: usize) -> usize {
crate::helpers::huffman::encoded_len(unsafe { Slice::borrow(data, data_len) }.unwrap_or_default())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_huffman_encode(data: *const u8, data_len: usize) -> Buffer {
match unsafe { Slice::borrow(data, data_len) } {
Some(data) => Buffer::new(crate::helpers::huffman::encode(data).to_vec()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_huffman_decode(data: *const u8, data_len: usize, out: *mut Buffer, error: *mut DecodeError) -> bool {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
unsafe { DecodeError::report(error, DecodeError::Invalid) };
return false;
};
match crate::helpers::huffman::decode(data) {
Ok(octets) => {
if !out.is_null() {
unsafe { *out = Buffer::new(octets.to_vec()) };
}
unsafe { DecodeError::report(error, DecodeError::Ok) };
true
}
Err(failure) => {
unsafe { DecodeError::report(error, DecodeError::of(&failure)) };
false
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_huffman_decode_ascii(data: *const u8, data_len: usize, out: *mut Buffer, ascii: *mut bool, error: *mut DecodeError) -> bool {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
unsafe { DecodeError::report(error, DecodeError::Invalid) };
return false;
};
let mut octets = Vec::new();
match crate::helpers::huffman::decode_into_ascii(data, &mut octets) {
Ok(printable) => {
if !out.is_null() {
unsafe { *out = Buffer::new(octets) };
}
if !ascii.is_null() {
unsafe { *ascii = printable };
}
unsafe { DecodeError::report(error, DecodeError::Ok) };
true
}
Err(failure) => {
unsafe { DecodeError::report(error, DecodeError::of(&failure)) };
false
}
}
}