use crate::ffi::{Buffer, Slice};
use crate::helpers::fields::{HeaderField, StaticIndex};
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Error {
Ok = 0,
IntegerOverflow = 1,
Incomplete = 2,
HuffmanInvalidPadding = 3,
HuffmanUnknownSymbol = 4,
Invalid = 5,
}
impl Error {
pub fn of(error: &crate::helpers::fields::Error) -> Self {
match error {
crate::helpers::fields::Error::IntegerOverflow => Self::IntegerOverflow,
crate::helpers::fields::Error::Incomplete => Self::Incomplete,
crate::helpers::fields::Error::Huffman(crate::helpers::huffman::DecodeError::InvalidPadding) => Self::HuffmanInvalidPadding,
crate::helpers::fields::Error::Huffman(crate::helpers::huffman::DecodeError::UnknownSymbol) => Self::HuffmanUnknownSymbol,
}
}
pub unsafe fn report(out: *mut Error, error: Self) {
if !out.is_null() {
unsafe { *out = error };
}
}
pub fn message(&self) -> &'static str {
match self {
Self::Ok => "ok",
Self::IntegerOverflow => "integer representation overflowed",
Self::Incomplete => "representation ends before the input does",
Self::HuffmanInvalidPadding => "huffman padding is not all one-bits",
Self::HuffmanUnknownSymbol => "huffman code does not map to a known symbol",
Self::Invalid => "invalid argument",
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_fields_error_message(error: Error) -> Slice {
Slice::text(error.message())
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Field {
pub name: Slice,
pub value: Slice,
}
impl Field {
pub unsafe fn parse_all(fields: *const Field, count: usize) -> Option<Vec<HeaderField>> {
if fields.is_null() {
return (count == 0).then(Vec::new);
}
let mut parsed = Vec::with_capacity(count);
for index in 0..count {
let field = unsafe { *fields.add(index) };
let name = unsafe { Slice::borrow_text(field.name.data, field.name.len) }?;
let value = unsafe { Slice::borrow_text(field.value.data, field.value.len) }?;
parsed.push(HeaderField::new(name, value));
}
Some(parsed)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_field_overhead() -> usize {
HeaderField::OVERHEAD
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_field_sensitive_count() -> usize {
HeaderField::SENSITIVE.len()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_field_sensitive_name(index: usize) -> Slice {
Slice::maybe(HeaderField::SENSITIVE.get(index).copied())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_field_size(name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> usize {
let (Some(name), Some(value)) = (unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return 0;
};
HeaderField::new(name, value).size()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_field_is_sensitive(name: *const u8, name_len: usize) -> bool {
let Some(name) = (unsafe { Slice::borrow_text(name, name_len) }) else {
return false;
};
HeaderField::new(name, "").sensitive()
}
pub struct Fields(pub Vec<HeaderField>);
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_fields_new() -> *mut Fields {
Box::into_raw(Box::new(Fields(Vec::new())))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_fields_append(fields: *mut Fields, name: *const u8, name_len: usize, value: *const u8, value_len: usize) -> bool {
let (Some(fields), Some(name), Some(value)) = (unsafe { fields.as_mut() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
fields.0.push(HeaderField::new(name, value));
true
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_fields_free(fields: *mut Fields) {
if !fields.is_null() {
drop(unsafe { Box::from_raw(fields) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_fields_count(fields: *const Fields) -> usize {
unsafe { fields.as_ref() }.map_or(0, |fields| fields.0.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_fields_name(fields: *const Fields, index: usize) -> Slice {
Slice::maybe(unsafe { fields.as_ref() }.and_then(|fields| fields.0.get(index)).map(|field| field.name.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_fields_value(fields: *const Fields, index: usize) -> Slice {
Slice::maybe(unsafe { fields.as_ref() }.and_then(|fields| fields.0.get(index)).map(|field| field.value.as_str()))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_integer_limit(prefix_bits: u8) -> u64 {
crate::helpers::fields::Integer::limit(prefix_bits)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_integer_encode(value: u64, prefix_bits: u8, flags: u8) -> Buffer {
let mut out = Vec::new();
crate::helpers::fields::Integer::encode(&mut out, value, prefix_bits, flags);
Buffer::new(out)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_integer_decode(data: *const u8, data_len: usize, prefix_bits: u8, out: *mut u64, read: *mut usize, error: *mut Error) -> bool {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
unsafe { Error::report(error, Error::Invalid) };
return false;
};
match crate::helpers::fields::Integer::decode(data, prefix_bits) {
Ok((consumed, value)) => {
if !out.is_null() {
unsafe { *out = value };
}
if !read.is_null() {
unsafe { *read = consumed };
}
unsafe { Error::report(error, Error::Ok) };
true
}
Err(failure) => {
unsafe { Error::report(error, Error::of(&failure)) };
false
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_string_prefers_huffman(data: *const u8, data_len: usize) -> bool {
crate::helpers::fields::StringLiteral::prefers_huffman(unsafe { Slice::borrow(data, data_len) }.unwrap_or_default())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_string_encode(data: *const u8, data_len: usize, prefix_bits: u8, flags: u8, huffman: bool) -> Buffer {
let data = unsafe { Slice::borrow(data, data_len) }.unwrap_or_default();
let mut out = Vec::new();
crate::helpers::fields::StringLiteral::encode(&mut out, data, prefix_bits, flags, huffman);
Buffer::new(out)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_string_encode_shorter(data: *const u8, data_len: usize, prefix_bits: u8, flags: u8) -> Buffer {
let data = unsafe { Slice::borrow(data, data_len) }.unwrap_or_default();
let mut out = Vec::new();
crate::helpers::fields::StringLiteral::encode_shorter(&mut out, data, prefix_bits, flags);
Buffer::new(out)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_string_decode(data: *const u8, data_len: usize, prefix_bits: u8, out: *mut Buffer, read: *mut usize, error: *mut Error) -> bool {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
unsafe { Error::report(error, Error::Invalid) };
return false;
};
match crate::helpers::fields::StringLiteral::decode(data, prefix_bits) {
Ok((consumed, octets)) => {
if !out.is_null() {
unsafe { *out = Buffer::new(octets) };
}
if !read.is_null() {
unsafe { *read = consumed };
}
unsafe { Error::report(error, Error::Ok) };
true
}
Err(failure) => {
unsafe { Error::report(error, Error::of(&failure)) };
false
}
}
}
pub type Index = StaticIndex;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_static_index_lookup(index: *const Index, name: *const u8, name_len: usize, value: *const u8, value_len: usize, name_index: *mut i64, exact: *mut i64) -> bool {
let (Some(index), Some(name), Some(value)) = (unsafe { index.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(value, value_len) }) else {
return false;
};
let (first, matched) = index.lookup(name, value);
if !name_index.is_null() {
unsafe { *name_index = first.map_or(-1, |first| first as i64) };
}
if !exact.is_null() {
unsafe { *exact = matched.map_or(-1, |matched| matched as i64) };
}
first.is_some()
}