extern crate spandsp_sys;
use std::ffi::CString;
use std::os::raw::{c_int, c_void};
use std::ptr::NonNull;
use crate::error::{Result, SpanDspError};
use crate::logging::LoggingState;
use crate::t4::{T4Compression, T4DecodeStatus, T4Stats};
type RowWriteCallback = Box<dyn FnMut(&[u8]) -> bool>;
unsafe extern "C" fn row_write_trampoline(
user_data: *mut c_void,
buf: *const u8,
len: usize,
) -> c_int {
unsafe {
if user_data.is_null() {
return 0;
}
let closure = &mut *(user_data as *mut RowWriteCallback);
let slice = if buf.is_null() || len == 0 {
&[]
} else {
std::slice::from_raw_parts(buf, len)
};
if closure(slice) { 0 } else { -1 }
}
}
pub struct T4Rx {
ptr: NonNull<spandsp_sys::t4_rx_state_t>,
}
impl T4Rx {
pub fn new(file: &str, compressions: T4Compression) -> Result<Self> {
let c_file = CString::new(file)
.map_err(|_| SpanDspError::InvalidInput("file path contains NUL byte".into()))?;
let ptr = unsafe {
spandsp_sys::t4_rx_init(
std::ptr::null_mut(),
c_file.as_ptr(),
compressions.bits() as c_int,
)
};
let ptr = NonNull::new(ptr).ok_or(SpanDspError::InitFailed)?;
Ok(Self { ptr })
}
pub fn start_page(&mut self) -> Result<()> {
let rc = unsafe { spandsp_sys::t4_rx_start_page(self.ptr.as_ptr()) };
if rc != 0 {
return Err(SpanDspError::ErrorCode(rc));
}
Ok(())
}
pub fn put(&mut self, buf: &[u8]) -> T4DecodeStatus {
let rc = unsafe { spandsp_sys::t4_rx_put(self.ptr.as_ptr(), buf.as_ptr(), buf.len()) };
T4DecodeStatus::try_from(rc).unwrap_or(T4DecodeStatus::InvalidData)
}
pub fn put_bit(&mut self, bit: i32) -> T4DecodeStatus {
let rc = unsafe { spandsp_sys::t4_rx_put_bit(self.ptr.as_ptr(), bit as c_int) };
T4DecodeStatus::try_from(rc).unwrap_or(T4DecodeStatus::InvalidData)
}
pub fn end_page(&mut self) -> Result<()> {
let rc = unsafe { spandsp_sys::t4_rx_end_page(self.ptr.as_ptr()) };
if rc != 0 {
return Err(SpanDspError::ErrorCode(rc));
}
Ok(())
}
pub fn set_rx_encoding(&mut self, encoding: T4Compression) -> Result<()> {
let rc = unsafe {
spandsp_sys::t4_rx_set_rx_encoding(self.ptr.as_ptr(), encoding.bits() as c_int)
};
if rc != 0 {
return Err(SpanDspError::ErrorCode(rc));
}
Ok(())
}
pub fn set_image_width(&mut self, width: i32) {
unsafe {
spandsp_sys::t4_rx_set_image_width(self.ptr.as_ptr(), width as c_int);
}
}
pub fn set_x_resolution(&mut self, resolution: i32) {
unsafe {
spandsp_sys::t4_rx_set_x_resolution(self.ptr.as_ptr(), resolution as c_int);
}
}
pub fn set_y_resolution(&mut self, resolution: i32) {
unsafe {
spandsp_sys::t4_rx_set_y_resolution(self.ptr.as_ptr(), resolution as c_int);
}
}
pub fn set_dcs(&mut self, dcs: &str) -> Result<()> {
let c_dcs = CString::new(dcs)
.map_err(|_| SpanDspError::InvalidInput("DCS contains NUL byte".into()))?;
unsafe {
spandsp_sys::t4_rx_set_dcs(self.ptr.as_ptr(), c_dcs.as_ptr());
}
Ok(())
}
pub fn set_sub_address(&mut self, sub_address: &str) -> Result<()> {
let c_sub = CString::new(sub_address)
.map_err(|_| SpanDspError::InvalidInput("sub-address contains NUL byte".into()))?;
unsafe {
spandsp_sys::t4_rx_set_sub_address(self.ptr.as_ptr(), c_sub.as_ptr());
}
Ok(())
}
pub fn set_far_ident(&mut self, ident: &str) -> Result<()> {
let c_ident = CString::new(ident)
.map_err(|_| SpanDspError::InvalidInput("far ident contains NUL byte".into()))?;
unsafe {
spandsp_sys::t4_rx_set_far_ident(self.ptr.as_ptr(), c_ident.as_ptr());
}
Ok(())
}
pub fn set_vendor(&mut self, vendor: &str) -> Result<()> {
let c_vendor = CString::new(vendor)
.map_err(|_| SpanDspError::InvalidInput("vendor contains NUL byte".into()))?;
unsafe {
spandsp_sys::t4_rx_set_vendor(self.ptr.as_ptr(), c_vendor.as_ptr());
}
Ok(())
}
pub fn set_model(&mut self, model: &str) -> Result<()> {
let c_model = CString::new(model)
.map_err(|_| SpanDspError::InvalidInput("model contains NUL byte".into()))?;
unsafe {
spandsp_sys::t4_rx_set_model(self.ptr.as_ptr(), c_model.as_ptr());
}
Ok(())
}
pub fn get_transfer_statistics(&self) -> T4Stats {
let mut stats = std::mem::MaybeUninit::<spandsp_sys::t4_stats_t>::zeroed();
unsafe {
spandsp_sys::t4_rx_get_transfer_statistics(self.ptr.as_ptr(), stats.as_mut_ptr());
T4Stats::from(stats.assume_init())
}
}
pub unsafe fn get_logging_state(&self) -> LoggingState {
let ptr = unsafe { spandsp_sys::t4_rx_get_logging_state(self.ptr.as_ptr()) };
let ptr = NonNull::new(ptr).expect("t4_rx_get_logging_state returned NULL");
unsafe { LoggingState::from_ptr_borrowed(ptr) }
}
pub fn as_ptr(&self) -> *mut spandsp_sys::t4_rx_state_t {
self.ptr.as_ptr()
}
}
impl Drop for T4Rx {
fn drop(&mut self) {
unsafe {
spandsp_sys::t4_rx_free(self.ptr.as_ptr());
}
}
}
pub struct T4T6Decoder {
ptr: NonNull<spandsp_sys::t4_t6_decode_state_t>,
_callback: Option<Box<RowWriteCallback>>,
}
impl T4T6Decoder {
pub fn new<F>(encoding: T4Compression, image_width: i32, handler: F) -> Result<Self>
where
F: FnMut(&[u8]) -> bool + 'static,
{
let boxed: Box<RowWriteCallback> = Box::new(Box::new(handler));
let user_data = &*boxed as *const RowWriteCallback as *mut c_void;
let ptr = unsafe {
spandsp_sys::t4_t6_decode_init(
std::ptr::null_mut(),
encoding.bits() as c_int,
image_width as c_int,
Some(row_write_trampoline),
user_data,
)
};
let ptr = NonNull::new(ptr).ok_or(SpanDspError::InitFailed)?;
Ok(Self {
ptr,
_callback: Some(boxed),
})
}
pub fn put(&mut self, buf: &[u8]) -> T4DecodeStatus {
let rc =
unsafe { spandsp_sys::t4_t6_decode_put(self.ptr.as_ptr(), buf.as_ptr(), buf.len()) };
T4DecodeStatus::try_from(rc).unwrap_or(T4DecodeStatus::InvalidData)
}
pub fn put_bit(&mut self, bit: i32) -> T4DecodeStatus {
let rc = unsafe { spandsp_sys::t4_t6_decode_put_bit(self.ptr.as_ptr(), bit as c_int) };
T4DecodeStatus::try_from(rc).unwrap_or(T4DecodeStatus::InvalidData)
}
pub fn restart(&mut self, image_width: i32) -> Result<()> {
let rc =
unsafe { spandsp_sys::t4_t6_decode_restart(self.ptr.as_ptr(), image_width as c_int) };
if rc != 0 {
return Err(SpanDspError::ErrorCode(rc));
}
Ok(())
}
pub fn set_encoding(&mut self, encoding: T4Compression) -> Result<()> {
let rc = unsafe {
spandsp_sys::t4_t6_decode_set_encoding(self.ptr.as_ptr(), encoding.bits() as c_int)
};
if rc != 0 {
return Err(SpanDspError::ErrorCode(rc));
}
Ok(())
}
pub fn image_width(&self) -> u32 {
unsafe { spandsp_sys::t4_t6_decode_get_image_width(self.ptr.as_ptr()) }
}
pub fn image_length(&self) -> u32 {
unsafe { spandsp_sys::t4_t6_decode_get_image_length(self.ptr.as_ptr()) }
}
pub fn compressed_image_size(&self) -> i32 {
unsafe { spandsp_sys::t4_t6_decode_get_compressed_image_size(self.ptr.as_ptr()) }
}
pub unsafe fn get_logging_state(&self) -> LoggingState {
let ptr = unsafe { spandsp_sys::t4_t6_decode_get_logging_state(self.ptr.as_ptr()) };
let ptr = NonNull::new(ptr).expect("t4_t6_decode_get_logging_state returned NULL");
unsafe { LoggingState::from_ptr_borrowed(ptr) }
}
pub fn as_ptr(&self) -> *mut spandsp_sys::t4_t6_decode_state_t {
self.ptr.as_ptr()
}
}
impl Drop for T4T6Decoder {
fn drop(&mut self) {
unsafe {
spandsp_sys::t4_t6_decode_free(self.ptr.as_ptr());
}
}
}