1use super::PstreamFormat;
2use crate::{catch_r_error, error::Error, error::Result, robj::Robj, throw_r_error};
3use extendr_ffi::{R_NilValue, R_Unserialize, R_inpstream_st, R_inpstream_t, SEXP};
4use std::io::Read;
5
6pub struct ReadHook {
7 func: Option<unsafe extern "C" fn(arg1: SEXP, arg2: SEXP) -> SEXP>,
8 data: SEXP,
9}
10
11pub trait Load {
12 fn load<P: AsRef<std::path::Path>>(
15 path: &P,
16 format: PstreamFormat,
17 hook: Option<ReadHook>,
18 ) -> Result<Robj> {
19 let mut reader = std::fs::File::open(path)
20 .map_err(|_| Error::Other(format!("could not open file {:?}", path.as_ref())))?;
21 Self::from_reader(&mut reader, format, hook)
22 }
23
24 fn from_reader<R: Read>(
27 reader: &mut R,
28 format: PstreamFormat,
29 hook: Option<ReadHook>,
30 ) -> Result<Robj> {
31 unsafe extern "C" fn inchar<R: Read>(arg1: R_inpstream_t) -> ::std::os::raw::c_int {
32 let reader = &mut *((*arg1).data as *mut R);
33 let buf: &mut [u8] = &mut [0_u8];
34 reader.read_exact(buf).map(|_| buf[0].into()).unwrap_or(-1)
35 }
36
37 unsafe extern "C" fn inbytes<R: Read>(
38 arg1: R_inpstream_t,
39 arg2: *mut ::std::os::raw::c_void,
40 arg3: ::std::os::raw::c_int,
41 ) {
42 let reader = &mut *((*arg1).data as *mut R);
43 let buf = std::slice::from_raw_parts_mut(arg2 as *mut u8, arg3 as usize);
44 if let Err(e) = reader.read_exact(buf) {
45 throw_r_error(format!("failed to read R serialization stream: {e}"));
46 }
47 }
48
49 let read_ptr: *mut R = reader as &mut R;
50 let data = read_ptr.cast::<std::ffi::c_void>();
51
52 let (hook_func, hook_data) = if let Some(hook) = hook {
53 (hook.func, hook.data)
54 } else {
55 (None, unsafe { R_NilValue })
56 };
57
58 let mut state = R_inpstream_st {
61 data,
62 type_: format,
63 InChar: Some(inchar::<R>),
64 InBytes: Some(inbytes::<R>),
65 InPersistHookFunc: hook_func,
66 InPersistHookData: hook_data,
67 native_encoding: [0; 64],
68 nat2nat_obj: std::ptr::null_mut(),
69 nat2utf8_obj: std::ptr::null_mut(),
70 };
71
72 Ok(unsafe {
73 Robj::from_sexp(catch_r_error(move || {
74 R_Unserialize(&mut state as R_inpstream_t)
75 })?)
76 })
77 }
78}
79
80impl Load for Robj {}