use std::ffi::c_void;
use fidius_core::status::{
STATUS_BUFFER_TOO_SMALL, STATUS_OK, STATUS_SERIALIZATION_ERROR, STATUS_STREAM_END,
};
use fidius_core::stream_ffi::FidiusStreamHandle;
type NextEncoded = Box<dyn FnMut() -> Option<Result<Vec<u8>, ()>> + Send>;
struct ProducerState {
next_encoded: NextEncoded,
pending: Option<Vec<u8>>,
}
unsafe extern "C" fn producer_next(
h: *mut FidiusStreamHandle,
buf: *mut u8,
cap: u32,
out_len: *mut u32,
) -> i32 {
let st = &mut *((*h).state as *mut ProducerState);
if st.pending.is_none() {
match (st.next_encoded)() {
Some(Ok(bytes)) => st.pending = Some(bytes),
Some(Err(())) => return STATUS_SERIALIZATION_ERROR,
None => return STATUS_STREAM_END,
}
}
let bytes = st.pending.as_ref().unwrap();
if bytes.len() > cap as usize {
*out_len = bytes.len() as u32;
return STATUS_BUFFER_TOO_SMALL;
}
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
*out_len = bytes.len() as u32;
st.pending = None;
STATUS_OK
}
unsafe extern "C" fn producer_drop(h: *mut FidiusStreamHandle) {
drop(Box::from_raw((*h).state as *mut ProducerState));
drop(Box::from_raw(h));
}
fn build_handle(next_encoded: NextEncoded) -> *mut FidiusStreamHandle {
let st = Box::into_raw(Box::new(ProducerState {
next_encoded,
pending: None,
}));
Box::into_raw(Box::new(FidiusStreamHandle {
next: producer_next,
drop_fn: producer_drop,
state: st as *mut c_void,
}))
}
pub fn host_producer_handle(
items: impl Iterator<Item = Vec<u8>> + Send + 'static,
) -> *mut FidiusStreamHandle {
let mut items = items;
build_handle(Box::new(move || items.next().map(Ok)))
}
pub fn host_producer_handle_typed<I: serde::Serialize + 'static>(
items: impl Iterator<Item = I> + Send + 'static,
) -> *mut FidiusStreamHandle {
let mut items = items;
build_handle(Box::new(move || {
items
.next()
.map(|i| fidius_core::wire::serialize(&i).map_err(|_| ()))
}))
}
#[cfg(test)]
mod tests {
use super::*;
use fidius_core::stream_ffi::HostStream;
#[test]
fn host_producer_feeds_guest_consumer() {
let items: Vec<u64> = vec![1, 2, 3, 4];
let encoded: Vec<Vec<u8>> = items
.iter()
.map(|i| fidius_core::wire::serialize(i).unwrap())
.collect();
let handle = host_producer_handle(encoded.into_iter());
let consumer = unsafe { HostStream::<u64>::from_handle(handle) };
let got: Vec<u64> = consumer.collect();
assert_eq!(got, items);
}
}