goods_treasury_import_ffi/
lib.rs1use {
2 goods_treasury_import::{Importer, Registry},
3 std::path::Path,
4 uuid::Uuid,
5};
6
7const BUFFER_LEN: usize = 1024;
8
9#[repr(C)]
10#[derive(Clone, Copy)]
11pub struct ImporterOpaque {
12 _byte: u8,
13}
14
15#[repr(C)]
16pub struct ImporterFFI {
17 data: *const ImporterOpaque,
18 name: unsafe fn(*const ImporterOpaque, *mut u8, usize) -> usize,
19 source: unsafe fn(*const ImporterOpaque, *mut u8, usize) -> usize,
20 native: unsafe fn(*const ImporterOpaque, *mut u8, usize) -> usize,
21
22 #[cfg(any(unix, target_os = "wasi"))]
23 import: unsafe fn(
24 *const ImporterOpaque,
25 *const u8,
26 usize,
27 *const u8,
28 usize,
29 *mut u8,
30 usize,
31 ) -> isize,
32
33 #[cfg(windows)]
34 import: unsafe fn(
35 *const ImporterOpaque,
36 *const u16,
37 usize,
38 *const u16,
39 usize,
40 *mut u8,
41 usize,
42 ) -> isize,
43}
44
45impl ImporterFFI {
46 pub fn new<I>(importer: &'static I) -> Self
47 where
48 I: Importer,
49 {
50 ImporterFFI {
51 data: importer as *const I as *const ImporterOpaque,
52 name: |data, buf, len| unsafe {
53 let name = (*(data as *mut I)).name();
54 let buf = std::slice::from_raw_parts_mut(buf, len);
55 buf[..len.min(name.len())].copy_from_slice(&name.as_bytes());
56 name.len()
57 },
58 source: |data, buf, len| unsafe {
59 let source = (*(data as *mut I)).source();
60 let buf = std::slice::from_raw_parts_mut(buf, len);
61 buf[..len.min(source.len())].copy_from_slice(&source.as_bytes());
62 source.len()
63 },
64 native: |data, buf, len| unsafe {
65 let native = (*(data as *mut I)).native();
66 let buf = std::slice::from_raw_parts_mut(buf, len);
67 buf[..len.min(native.len())].copy_from_slice(&native.as_bytes());
68 native.len()
69 },
70 import: |data, source_ptr, source_len, native_ptr, native_len, error_ptr, error_len| unsafe {
71 #[cfg(any(unix, target_os = "wasi"))]
72 let (source, native) = {
73 use std::ffi::OsStr;
74
75 #[cfg(unix)]
76 use std::os::unix::ffi::OsStrExt;
77 #[cfg(target_os = "wasi")]
78 use std::os::wasi::ffi::OsStrExt;
79
80 let source =
81 OsStr::from_bytes(std::slice::from_raw_parts(source_ptr, source_len));
82
83 let native =
84 OsStr::from_bytes(std::slice::from_raw_parts(native_ptr, native_len));
85
86 (source, native)
87 };
88
89 #[cfg(windows)]
90 let (source, native) = {
91 use std::{ffi::OsString, os::windows::ffi::OsStringExt};
92
93 let source =
94 OsString::from_wide(std::slice::from_raw_parts(source_ptr, source_len));
95
96 let native =
97 OsString::from_wide(std::slice::from_raw_parts(native_ptr, native_len));
98
99 (source, native)
100 };
101
102 match (*(data as *const I)).import(
103 source.as_ref(),
104 native.as_ref(),
105 &mut RegistryFFI,
106 ) {
107 Ok(()) => 0,
108 Err(err) => {
109 use std::io::{Cursor, Write as _};
110 let error_slice = std::slice::from_raw_parts_mut(error_ptr, error_len);
111 let mut error_write = Cursor::new(error_slice);
112
113 let _ = write!(error_write, "{:#}", err);
114 -(error_write.position() as isize)
115 }
116 }
117 },
118 }
119 }
120}
121
122struct RegistryFFI;
123
124impl Registry for RegistryFFI {
125 fn store(
126 &mut self,
127 source: &Path,
128 source_format: &str,
129 native_format: &str,
130 tags: &[&str],
131 ) -> eyre::Result<Uuid> {
132 use std::ffi::OsStr;
133
134 #[cfg(unix)]
135 use std::os::unix::ffi::OsStrExt;
136 #[cfg(target_os = "wasi")]
137 use std::os::wasi::ffi::OsStrExt;
138 #[cfg(windows)]
139 use std::os::windows::ffi::OsStrExt;
140
141 let source: &OsStr = source.as_ref();
142
143 #[cfg(any(unix, target_os = "wasi"))]
144 let source = source.as_bytes();
145
146 #[cfg(windows)]
147 let source = source.encode_wide().collect::<Vec<u16>>();
148
149 #[cfg(windows)]
150 let source = String::from_utf16(&source[..]).unwrap();
151
152 let mut result_array = [0u8; BUFFER_LEN];
153
154 let tag_count = tags.len();
155 let tag_ptrs = tags.iter().map(|t| str::as_ptr(t)).collect::<Vec<_>>();
156 let tag_lens = tags.iter().map(|t| str::len(t)).collect::<Vec<_>>();
157
158 let result = unsafe {
159 treasury_registry_store(
160 source.as_ptr(),
161 source.len(),
162 source_format.as_ptr(),
163 source_format.len(),
164 native_format.as_ptr(),
165 native_format.len(),
166 tag_ptrs.as_ptr(),
167 tag_lens.as_ptr(),
168 tag_count,
169 result_array.as_mut_ptr(),
170 BUFFER_LEN,
171 )
172 };
173
174 if result < 0 {
175 let len = result.abs() as usize;
176 let error = std::str::from_utf8(&result_array[..len.min(BUFFER_LEN)]).unwrap();
177 Err(eyre::eyre!("{}", error))
178 } else {
179 debug_assert_eq!(result, 16);
181 let uuid = Uuid::from_slice(&result_array[..16]).unwrap();
182 Ok(uuid)
183 }
184 }
185
186 fn fetch(&mut self, asset: &Uuid) -> eyre::Result<Box<Path>> {
187 let asset = asset.as_bytes();
188
189 let mut path_array = [0; BUFFER_LEN];
190 let mut error_array = [0; BUFFER_LEN];
191
192 let result = unsafe {
193 treasury_registry_fetch(
194 asset.as_ptr(),
195 path_array.as_mut_ptr(),
196 BUFFER_LEN,
197 error_array.as_mut_ptr(),
198 BUFFER_LEN,
199 )
200 };
201
202 if result < 0 {
203 let len = result.abs() as usize;
204 let error = std::str::from_utf8(&error_array[..len.min(BUFFER_LEN)]).unwrap();
205 Err(eyre::eyre!("{}", error))
206 } else {
207 let len = result as usize;
208 debug_assert!(len <= BUFFER_LEN);
209
210 #[cfg(any(unix, target_os = "wasi"))]
211 {
212 use std::ffi::OsStr;
213
214 #[cfg(unix)]
215 use std::os::unix::ffi::OsStrExt;
216 #[cfg(target_os = "wasi")]
217 use std::os::wasi::ffi::OsStrExt;
218
219 let path = OsStr::from_bytes(&path_array[..len.min(BUFFER_LEN)]);
220
221 Ok(Path::new(path).into())
222 }
223
224 #[cfg(windows)]
225 {
226 use std::path::PathBuf;
227 let path = std::str::from_utf8(&path_array[..len.min(BUFFER_LEN)]).unwrap();
228 Ok(PathBuf::from(path).into_boxed_path())
229 }
230 }
231 }
232}
233
234extern "C" {
235 fn treasury_registry_store(
236 source_ptr: *const u8,
237 source_len: usize,
238 source_format_ptr: *const u8,
239 source_format_len: usize,
240 native_format_ptr: *const u8,
241 native_format_len: usize,
242 tag_ptrs: *const *const u8,
243 tag_lens: *const usize,
244 tag_count: usize,
245 result_ptr: *mut u8,
246 result_len: usize,
247 ) -> isize;
248
249 fn treasury_registry_fetch(
250 uuid: *const u8,
251 path_ptr: *mut u8,
252 path_len: usize,
253 error_ptr: *mut u8,
254 error_len: usize,
255 ) -> isize;
256}
257
258#[doc(hidden)]
259#[macro_export]
260macro_rules! count_tt {
261 () => { 0 };
262 ($head:tt $(, $tail:tt)*) => { 1 + $crate::count_tt!($($tail),*) };
263}
264
265#[macro_export]
267macro_rules! generate_imports_and_exports {
268 ($($importer:expr),* $(,)?) => {
269
270 #[no_mangle]
271 pub unsafe fn treasury_importer_enumerate_importers(importers: *mut $crate::ImporterFFI, count: usize) -> usize {
272 const COUNT: usize = $crate::count_tt!($(($importer)),*);
273
274 if count < COUNT {
275 COUNT
276 } else {
277 let mut ptr = importers;
278
279 $(
280 ::std::ptr::write(ptr, $crate::ImporterFFI::new($importer));
281 ptr = ptr.add(1);
282 )*
283
284 COUNT
285 }
286 }
287 };
288}
289
290#[cfg(target_os = "wasi")]
291#[no_mangle]
292pub unsafe fn treasury_importer_name_source_native_trampoline(
293 function: unsafe fn(*const ImporterOpaque, *mut u8, u32) -> u32,
294 ptr: *const ImporterOpaque,
295 result_ptr: *mut u8,
296 result_len: u32,
297) -> u32 {
298 function(ptr, result_ptr, result_len)
299}
300
301#[cfg(target_os = "wasi")]
302#[no_mangle]
303pub unsafe fn treasury_importer_import_trampoline(
304 function: unsafe fn(*const ImporterOpaque, *const u8, u32, *const u8, u32, *mut u8, u32) -> i32,
305 ptr: *const ImporterOpaque,
306 source_path_ptr: *const u8,
307 source_path_len: u32,
308 native_path_ptr: *const u8,
309 native_path_len: u32,
310 error_ptr: *mut u8,
311 error_len: u32,
312) -> i32 {
313 function(
314 ptr,
315 source_path_ptr,
316 source_path_len,
317 native_path_ptr,
318 native_path_len,
319 error_ptr,
320 error_len,
321 )
322}
323
324#[no_mangle]
329pub unsafe fn treasury_importer_alloc(size: usize, align: usize) -> *mut u8 {
330 let layout = std::alloc::Layout::from_size_align(size, align).unwrap();
331 std::alloc::alloc(layout)
332}
333
334#[no_mangle]
339pub unsafe fn treasury_importer_dealloc(ptr: *mut u8, size: usize, align: usize) {
340 let layout = std::alloc::Layout::from_size_align(size, align).unwrap();
341 std::alloc::dealloc(ptr, layout);
342}