use std::ffi::c_void;
use std::io::{Read, Write};
use std::os::raw::c_int;
use std::sync::Mutex;
pub(crate) type DropFn = unsafe fn(*mut c_void);
struct ReadCallbackState<R: Read> {
reader: R,
buffer: Vec<u8>,
}
struct WriteCallbackState<W: Write> {
writer: W,
}
pub trait ProgressCallback {
fn on_progress(&mut self, bytes_processed: u64, total_bytes: u64);
}
unsafe extern "C" fn read_callback_impl<R: Read>(
_archive: *mut libarchive2_sys::archive,
client_data: *mut c_void,
buffer: *mut *const c_void,
) -> isize {
if client_data.is_null() || buffer.is_null() {
return -1;
}
unsafe {
let state = &*(client_data as *mut Mutex<ReadCallbackState<R>>);
let mut guard = match state.lock() {
Ok(g) => g,
Err(_) => return -1, };
let ReadCallbackState {
reader,
buffer: buf,
} = &mut *guard;
let result = reader.read(buf);
match result {
Ok(n) => {
*buffer = buf.as_ptr() as *const c_void;
n as isize
}
Err(_) => -1,
}
}
}
unsafe extern "C" fn write_callback_impl<W: Write>(
_archive: *mut libarchive2_sys::archive,
client_data: *mut c_void,
buffer: *const c_void,
length: usize,
) -> isize {
if client_data.is_null() || buffer.is_null() {
return -1;
}
unsafe {
let state = &*(client_data as *mut Mutex<WriteCallbackState<W>>);
let mut guard = match state.lock() {
Ok(g) => g,
Err(_) => return -1, };
let data = std::slice::from_raw_parts(buffer as *const u8, length);
match guard.writer.write_all(data) {
Ok(()) => length as isize,
Err(_) => -1,
}
}
}
unsafe extern "C" fn close_callback_impl(
_archive: *mut libarchive2_sys::archive,
_client_data: *mut c_void,
) -> c_int {
0
}
pub struct CallbackReader<R: Read> {
state: Box<Mutex<ReadCallbackState<R>>>,
}
impl<R: Read> CallbackReader<R> {
pub fn new(reader: R) -> Self {
const BUFFER_SIZE: usize = 65536; CallbackReader {
state: Box::new(Mutex::new(ReadCallbackState {
reader,
buffer: vec![0u8; BUFFER_SIZE],
})),
}
}
pub(crate) fn into_raw_parts(self) -> (*mut c_void, *const c_void, *const c_void, DropFn) {
let ptr = Box::into_raw(self.state) as *mut c_void;
unsafe fn drop_fn<R: Read>(ptr: *mut c_void) {
unsafe {
let _ = Box::from_raw(ptr as *mut Mutex<ReadCallbackState<R>>);
}
}
(
ptr,
read_callback_impl::<R> as *const c_void,
close_callback_impl as *const c_void,
drop_fn::<R>,
)
}
}
pub struct CallbackWriter<W: Write> {
state: Box<Mutex<WriteCallbackState<W>>>,
}
impl<W: Write> CallbackWriter<W> {
pub fn new(writer: W) -> Self {
CallbackWriter {
state: Box::new(Mutex::new(WriteCallbackState { writer })),
}
}
pub(crate) fn into_raw_parts(self) -> (*mut c_void, *const c_void, *const c_void, DropFn) {
let ptr = Box::into_raw(self.state) as *mut c_void;
unsafe fn drop_fn<W: Write>(ptr: *mut c_void) {
unsafe {
let _ = Box::from_raw(ptr as *mut Mutex<WriteCallbackState<W>>);
}
}
(
ptr,
write_callback_impl::<W> as *const c_void,
close_callback_impl as *const c_void,
drop_fn::<W>,
)
}
}
pub struct ProgressTracker {
callback: Box<dyn ProgressCallback>,
bytes_processed: u64,
total_bytes: u64,
}
impl ProgressTracker {
pub fn new<C: ProgressCallback + 'static>(callback: C) -> Self {
ProgressTracker {
callback: Box::new(callback),
bytes_processed: 0,
total_bytes: 0,
}
}
pub fn update(&mut self, bytes: u64) {
self.bytes_processed += bytes;
self.callback
.on_progress(self.bytes_processed, self.total_bytes);
}
pub fn set_total(&mut self, total: u64) {
self.total_bytes = total;
}
pub fn reset(&mut self) {
self.bytes_processed = 0;
}
}