1#![allow(
9 clippy::cast_possible_wrap,
10 clippy::cast_possible_truncation,
11 clippy::cast_sign_loss,
12 clippy::cast_precision_loss,
13 clippy::ptr_as_ptr
14)]
15
16use std::os::raw::{c_char, c_int, c_long, c_void};
17use std::ptr;
18
19use crate::err::ReadStatError;
20use crate::rs_parser::ReadStatParser;
21
22#[repr(C)]
27pub struct ReadStatBufferCtx {
28 data: *const u8,
29 len: usize,
30 pos: usize,
31}
32
33impl ReadStatBufferCtx {
34 pub const fn new(bytes: &[u8]) -> Self {
39 Self {
40 data: bytes.as_ptr(),
41 len: bytes.len(),
42 pos: 0,
43 }
44 }
45
46 pub fn configure_parser(
49 &mut self,
50 parser: ReadStatParser,
51 ) -> Result<ReadStatParser, ReadStatError> {
52 let ctx_ptr = std::ptr::from_mut::<Self>(self) as *mut c_void;
53 parser
54 .set_open_handler(Some(buffer_open))
55 .and_then(|p| p.set_close_handler(Some(buffer_close)))
56 .and_then(|p| p.set_seek_handler(Some(buffer_seek)))
57 .and_then(|p| p.set_read_handler(Some(buffer_read)))
58 .and_then(|p| p.set_update_handler(Some(buffer_update)))
59 .and_then(|p| p.set_io_ctx(ctx_ptr))
60 }
61}
62
63unsafe extern "C" fn buffer_open(_path: *const c_char, _io_ctx: *mut c_void) -> c_int {
65 0
66}
67
68unsafe extern "C" fn buffer_close(_io_ctx: *mut c_void) -> c_int {
70 0
71}
72
73unsafe extern "C" fn buffer_seek(
75 offset: readstat_sys::readstat_off_t,
76 whence: readstat_sys::readstat_io_flags_t,
77 io_ctx: *mut c_void,
78) -> readstat_sys::readstat_off_t {
79 let ctx = unsafe { &mut *(io_ctx as *mut ReadStatBufferCtx) };
80
81 let newpos: i64 = match whence {
82 readstat_sys::readstat_io_flags_e_READSTAT_SEEK_SET => offset,
83 readstat_sys::readstat_io_flags_e_READSTAT_SEEK_CUR => ctx.pos as i64 + offset,
84 readstat_sys::readstat_io_flags_e_READSTAT_SEEK_END => ctx.len as i64 + offset,
85 _ => return -1,
86 };
87
88 if newpos < 0 || newpos > ctx.len as i64 {
89 return -1;
90 }
91
92 ctx.pos = newpos as usize;
93 newpos
94}
95
96unsafe extern "C" fn buffer_read(buf: *mut c_void, nbytes: usize, io_ctx: *mut c_void) -> isize {
98 let ctx = unsafe { &mut *(io_ctx as *mut ReadStatBufferCtx) };
99 let bytes_left = ctx.len.saturating_sub(ctx.pos);
100
101 let to_copy = if nbytes <= bytes_left {
102 nbytes
103 } else if bytes_left > 0 {
104 bytes_left
105 } else {
106 return 0;
107 };
108
109 unsafe {
110 ptr::copy_nonoverlapping(ctx.data.add(ctx.pos), buf as *mut u8, to_copy);
111 }
112 ctx.pos += to_copy;
113 to_copy as isize
114}
115
116unsafe extern "C" fn buffer_update(
118 _file_size: c_long,
119 progress_handler: readstat_sys::readstat_progress_handler,
120 user_ctx: *mut c_void,
121 io_ctx: *mut c_void,
122) -> readstat_sys::readstat_error_t {
123 let Some(handler) = progress_handler else {
124 return readstat_sys::readstat_error_e_READSTAT_OK;
125 };
126
127 let ctx = unsafe { &*(io_ctx as *mut ReadStatBufferCtx) };
128 let progress = if ctx.len > 0 {
129 ctx.pos as f64 / ctx.len as f64
130 } else {
131 1.0
132 };
133
134 if unsafe { handler(progress, user_ctx) } != 0 {
135 return readstat_sys::readstat_error_e_READSTAT_ERROR_USER_ABORT;
136 }
137
138 readstat_sys::readstat_error_e_READSTAT_OK
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn buffer_ctx_new() {
147 let data = vec![1u8, 2, 3, 4, 5];
148 let ctx = ReadStatBufferCtx::new(&data);
149 assert_eq!(ctx.len, 5);
150 assert_eq!(ctx.pos, 0);
151 assert_eq!(ctx.data, data.as_ptr());
152 }
153
154 #[test]
155 fn buffer_ctx_empty() {
156 let data: Vec<u8> = vec![];
157 let ctx = ReadStatBufferCtx::new(&data);
158 assert_eq!(ctx.len, 0);
159 assert_eq!(ctx.pos, 0);
160 }
161}