1use std::cell::RefCell;
2use std::fmt::Display;
3use std::mem::ManuallyDrop;
4use std::ptr;
5
6#[repr(C)]
7pub struct ByteBuffer {
8 pub ptr: *mut u8,
9 pub length: i32,
10 pub capacity: i32,
11}
12
13impl ByteBuffer {
14 #[must_use]
15 pub fn from_vec(bytes: Vec<u8>) -> Self {
16 let Ok(length) = i32::try_from(bytes.len()) else {
17 set_last_error("buffer length exceeds i32::MAX");
18 return Self::null();
19 };
20 let Ok(capacity) = i32::try_from(bytes.capacity()) else {
21 set_last_error("buffer capacity exceeds i32::MAX");
22 return Self::null();
23 };
24
25 let mut bytes = ManuallyDrop::new(bytes);
26 Self {
27 ptr: bytes.as_mut_ptr(),
28 length,
29 capacity,
30 }
31 }
32
33 pub fn destroy(self) {
34 if self.ptr.is_null() {
35 return;
36 }
37
38 if self.length < 0 || self.capacity < 0 || self.length > self.capacity {
39 return;
40 }
41
42 let Ok(length) = usize::try_from(self.length) else {
43 return;
44 };
45 let Ok(capacity) = usize::try_from(self.capacity) else {
46 return;
47 };
48
49 unsafe {
50 let _ = Vec::from_raw_parts(self.ptr, length, capacity);
51 }
52 }
53
54 #[must_use]
55 pub const fn null() -> Self {
56 Self {
57 ptr: ptr::null_mut(),
58 length: 0,
59 capacity: 0,
60 }
61 }
62}
63
64thread_local! {
65 static LAST_ERROR: RefCell<Option<String>> = const { RefCell::new(None) };
66}
67
68pub fn set_last_error(err: impl Display) {
69 LAST_ERROR.with(|cell| {
70 *cell.borrow_mut() = Some(err.to_string());
71 });
72}
73
74pub fn clear_last_error() {
75 LAST_ERROR.with(|cell| {
76 *cell.borrow_mut() = None;
77 });
78}
79
80#[must_use]
81pub fn last_error_message() -> Option<String> {
82 LAST_ERROR.with(|cell| cell.borrow().clone())
83}
84
85pub fn into_ffi_result(result: crate::Result<Vec<u8>>) -> *mut ByteBuffer {
86 match result {
87 Ok(bytes) => {
88 clear_last_error();
89 Box::into_raw(Box::new(ByteBuffer::from_vec(bytes)))
90 }
91 Err(err) => {
92 set_last_error(err);
93 ptr::null_mut()
94 }
95 }
96}
97
98pub fn into_ffi_ptr_result<T>(result: crate::Result<T>) -> *mut T {
99 match result {
100 Ok(value) => {
101 clear_last_error();
102 Box::into_raw(Box::new(value))
103 }
104 Err(err) => {
105 set_last_error(err);
106 ptr::null_mut()
107 }
108 }
109}
110
111pub fn into_ffi_i32_result(result: crate::Result<i32>, error_value: i32) -> i32 {
112 match result {
113 Ok(value) => {
114 clear_last_error();
115 value
116 }
117 Err(err) => {
118 set_last_error(err);
119 error_value
120 }
121 }
122}
123
124pub fn run_on_large_stack<F, T>(f: F) -> crate::Result<T>
136where
137 F: FnOnce() -> crate::Result<T> + Send,
138 T: Send,
139{
140 const STACK_SIZE: usize = 64 * 1024 * 1024; std::thread::scope(|scope| {
143 std::thread::Builder::new()
144 .stack_size(STACK_SIZE)
145 .spawn_scoped(scope, f)
146 .map_err(|e| {
147 crate::error::Error::Conversion(format!("failed to spawn worker thread: {e}"))
148 })?
149 .join()
150 .unwrap_or_else(|payload| {
151 let msg = payload
152 .downcast_ref::<&str>()
153 .copied()
154 .or_else(|| payload.downcast_ref::<String>().map(String::as_str))
155 .unwrap_or("unknown panic");
156 Err(crate::error::Error::Conversion(format!(
157 "worker thread panicked: {msg}"
158 )))
159 })
160 })
161}