• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 ///! C-bindings for the rutabaga_gfx crate
6 extern crate rutabaga_gfx;
7 
8 use std::cell::RefCell;
9 use std::convert::TryInto;
10 use std::ffi::CStr;
11 use std::ffi::CString;
12 use std::fs::File;
13 use std::io::IoSliceMut;
14 use std::os::raw::c_char;
15 use std::os::raw::c_void;
16 use std::panic::catch_unwind;
17 use std::panic::AssertUnwindSafe;
18 use std::path::Path;
19 use std::path::PathBuf;
20 use std::ptr::copy_nonoverlapping;
21 use std::ptr::null;
22 use std::ptr::null_mut;
23 use std::slice::from_raw_parts;
24 use std::slice::from_raw_parts_mut;
25 
26 #[cfg(unix)]
27 use libc::iovec;
28 use libc::EINVAL;
29 use libc::ESRCH;
30 use rutabaga_gfx::*;
31 
32 #[cfg(not(unix))]
33 #[repr(C)]
34 pub struct iovec {
35     pub iov_base: *mut c_void,
36     pub iov_len: usize,
37 }
38 
39 const NO_ERROR: i32 = 0;
40 const RUTABAGA_WSI_SURFACELESS: u64 = 1;
41 
42 thread_local! {
43     static S_DEBUG_HANDLER: RefCell<Option<RutabagaDebugHandler>> = const { RefCell::new(None) };
44 }
45 
log_error(debug_string: String)46 fn log_error(debug_string: String) {
47     S_DEBUG_HANDLER.with(|handler_cell| {
48         if let Some(handler) = &*handler_cell.borrow() {
49             let cstring = CString::new(debug_string.as_str()).expect("CString creation failed");
50 
51             let debug = RutabagaDebug {
52                 debug_type: RUTABAGA_DEBUG_ERROR,
53                 message: cstring.as_ptr(),
54             };
55 
56             handler.call(debug);
57         }
58     });
59 }
60 
return_result<T>(result: RutabagaResult<T>) -> i3261 fn return_result<T>(result: RutabagaResult<T>) -> i32 {
62     if let Err(e) = result {
63         log_error(e.to_string());
64         -EINVAL
65     } else {
66         NO_ERROR
67     }
68 }
69 
70 macro_rules! return_on_error {
71     ($result:expr) => {
72         match $result {
73             Ok(t) => t,
74             Err(e) => {
75                 log_error(e.to_string());
76                 return -EINVAL;
77             }
78         }
79     };
80 }
81 
82 macro_rules! return_on_io_error {
83     ($result:expr) => {
84         match $result {
85             Ok(t) => t,
86             Err(e) => {
87                 log_error(e.to_string());
88                 return -e.raw_os_error().unwrap_or(EINVAL);
89             }
90         }
91     };
92 }
93 
94 #[allow(non_camel_case_types)]
95 type rutabaga = Rutabaga;
96 
97 #[allow(non_camel_case_types)]
98 type rutabaga_create_blob = ResourceCreateBlob;
99 
100 #[allow(non_camel_case_types)]
101 type rutabaga_create_3d = ResourceCreate3D;
102 
103 #[allow(non_camel_case_types)]
104 type rutabaga_transfer = Transfer3D;
105 
106 #[allow(non_camel_case_types)]
107 type rutabaga_fence = RutabagaFence;
108 
109 #[allow(non_camel_case_types)]
110 type rutabaga_debug = RutabagaDebug;
111 
112 #[repr(C)]
113 #[derive(Copy, Clone)]
114 pub struct rutabaga_iovecs {
115     pub iovecs: *mut iovec,
116     pub num_iovecs: usize,
117 }
118 
119 #[repr(C)]
120 #[derive(Copy, Clone)]
121 pub struct rutabaga_handle {
122     pub os_handle: i64,
123     pub handle_type: u32,
124 }
125 
126 #[repr(C)]
127 pub struct rutabaga_mapping {
128     pub ptr: *mut c_void,
129     pub size: u64,
130 }
131 
132 #[repr(C)]
133 pub struct rutabaga_channel {
134     pub channel_name: *const c_char,
135     pub channel_type: u32,
136 }
137 
138 #[repr(C)]
139 pub struct rutabaga_channels {
140     pub channels: *const rutabaga_channel,
141     pub num_channels: usize,
142 }
143 
144 #[repr(C)]
145 pub struct rutabaga_command {
146     pub ctx_id: u32,
147     pub cmd_size: u32,
148     pub cmd: *mut u8,
149     pub num_in_fences: u32,
150     pub fence_ids: *mut u64,
151 }
152 
153 #[allow(non_camel_case_types)]
154 pub type rutabaga_fence_callback = extern "C" fn(user_data: u64, fence: &rutabaga_fence);
155 
156 #[allow(non_camel_case_types)]
157 pub type rutabaga_debug_callback = extern "C" fn(user_data: u64, debug: &rutabaga_debug);
158 
159 #[repr(C)]
160 pub struct rutabaga_builder<'a> {
161     pub user_data: u64,
162     pub capset_mask: u64,
163     pub wsi: u64,
164     pub fence_cb: rutabaga_fence_callback,
165     pub debug_cb: Option<rutabaga_debug_callback>,
166     pub channels: Option<&'a rutabaga_channels>,
167     pub renderer_features: *const c_char,
168 }
169 
create_ffi_fence_handler( user_data: u64, fence_cb: rutabaga_fence_callback, ) -> RutabagaFenceHandler170 fn create_ffi_fence_handler(
171     user_data: u64,
172     fence_cb: rutabaga_fence_callback,
173 ) -> RutabagaFenceHandler {
174     RutabagaFenceHandler::new(move |completed_fence| fence_cb(user_data, &completed_fence))
175 }
176 
create_ffi_debug_handler( user_data: u64, debug_cb: rutabaga_debug_callback, ) -> RutabagaDebugHandler177 fn create_ffi_debug_handler(
178     user_data: u64,
179     debug_cb: rutabaga_debug_callback,
180 ) -> RutabagaDebugHandler {
181     RutabagaDebugHandler::new(move |rutabaga_debug| debug_cb(user_data, &rutabaga_debug))
182 }
183 
184 #[no_mangle]
185 /// # Safety
186 /// - `capset_names` must be a null-terminated C-string.
rutabaga_calculate_capset_mask( capset_names: *const c_char, capset_mask: &mut u64, ) -> i32187 pub unsafe extern "C" fn rutabaga_calculate_capset_mask(
188     capset_names: *const c_char,
189     capset_mask: &mut u64,
190 ) -> i32 {
191     catch_unwind(AssertUnwindSafe(|| {
192         if capset_names == null() {
193             return -EINVAL;
194         }
195 
196         let c_str_slice = CStr::from_ptr(capset_names);
197         let result = c_str_slice.to_str();
198         let str_slice = return_on_error!(result);
199         *capset_mask = rutabaga_gfx::calculate_capset_mask(str_slice.split(':'));
200         NO_ERROR
201     }))
202     .unwrap_or(-ESRCH)
203 }
204 
205 /// # Safety
206 /// - If `(*builder).channels` is not null, the caller must ensure `(*channels).channels` points to
207 ///   a valid array of `struct rutabaga_channel` of size `(*channels).num_channels`.
208 /// - The `channel_name` field of `struct rutabaga_channel` must be a null-terminated C-string.
209 #[no_mangle]
rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32210 pub unsafe extern "C" fn rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32 {
211     catch_unwind(AssertUnwindSafe(|| {
212         let fence_handler = create_ffi_fence_handler((*builder).user_data, (*builder).fence_cb);
213         let mut debug_handler_opt: Option<RutabagaDebugHandler> = None;
214 
215         if let Some(func) = (*builder).debug_cb {
216             let debug_handler = create_ffi_debug_handler((*builder).user_data, func);
217             S_DEBUG_HANDLER.with(|handler_cell| {
218                 *handler_cell.borrow_mut() = Some(debug_handler.clone());
219             });
220             debug_handler_opt = Some(debug_handler);
221         }
222 
223         let mut rutabaga_channels_opt = None;
224         if let Some(channels) = (*builder).channels {
225             let mut rutabaga_channels: Vec<RutabagaChannel> = Vec::new();
226             let channels_slice = from_raw_parts(channels.channels, channels.num_channels);
227 
228             for channel in channels_slice {
229                 let c_str_slice = CStr::from_ptr(channel.channel_name);
230                 let result = c_str_slice.to_str();
231                 let str_slice = return_on_error!(result);
232                 let string = str_slice.to_owned();
233                 let path = PathBuf::from(&string);
234 
235                 rutabaga_channels.push(RutabagaChannel {
236                     base_channel: path,
237                     channel_type: channel.channel_type,
238                 });
239             }
240 
241             rutabaga_channels_opt = Some(rutabaga_channels);
242         }
243 
244         let mut renderer_features_opt = None;
245         let renderer_features_ptr = (*builder).renderer_features;
246         if !renderer_features_ptr.is_null() {
247             let c_str_slice = CStr::from_ptr(renderer_features_ptr);
248             let result = c_str_slice.to_str();
249             let str_slice = return_on_error!(result);
250             let string = str_slice.to_owned();
251             renderer_features_opt = Some(string);
252         }
253 
254         let mut component_type = RutabagaComponentType::CrossDomain;
255         if (*builder).capset_mask == 0 {
256             component_type = RutabagaComponentType::Rutabaga2D;
257         }
258 
259         let rutabaga_wsi = match (*builder).wsi {
260             RUTABAGA_WSI_SURFACELESS => RutabagaWsi::Surfaceless,
261             _ => return -EINVAL,
262         };
263 
264         let result = RutabagaBuilder::new(component_type, (*builder).capset_mask)
265             .set_use_external_blob(false)
266             .set_use_egl(true)
267             .set_wsi(rutabaga_wsi)
268             .set_debug_handler(debug_handler_opt)
269             .set_rutabaga_channels(rutabaga_channels_opt)
270             .set_renderer_features(renderer_features_opt)
271             .build(fence_handler, None);
272 
273         let rtbg = return_on_error!(result);
274         *ptr = Box::into_raw(Box::new(rtbg)) as _;
275         NO_ERROR
276     }))
277     .unwrap_or(-ESRCH)
278 }
279 
280 /// # Safety
281 /// - `ptr` must have been created by `rutabaga_init`.
282 #[no_mangle]
rutabaga_finish(ptr: &mut *mut rutabaga) -> i32283 pub extern "C" fn rutabaga_finish(ptr: &mut *mut rutabaga) -> i32 {
284     catch_unwind(AssertUnwindSafe(|| {
285         unsafe { Box::from_raw(*ptr) };
286         *ptr = null_mut();
287         NO_ERROR
288     }))
289     .unwrap_or(-ESRCH)
290 }
291 
292 #[no_mangle]
rutabaga_get_num_capsets(ptr: &mut rutabaga, num_capsets: &mut u32) -> i32293 pub extern "C" fn rutabaga_get_num_capsets(ptr: &mut rutabaga, num_capsets: &mut u32) -> i32 {
294     catch_unwind(AssertUnwindSafe(|| {
295         *num_capsets = ptr.get_num_capsets();
296         NO_ERROR
297     }))
298     .unwrap_or(-ESRCH)
299 }
300 
301 #[no_mangle]
rutabaga_get_capset_info( ptr: &mut rutabaga, capset_index: u32, capset_id: &mut u32, capset_version: &mut u32, capset_size: &mut u32, ) -> i32302 pub extern "C" fn rutabaga_get_capset_info(
303     ptr: &mut rutabaga,
304     capset_index: u32,
305     capset_id: &mut u32,
306     capset_version: &mut u32,
307     capset_size: &mut u32,
308 ) -> i32 {
309     catch_unwind(AssertUnwindSafe(|| {
310         let result = ptr.get_capset_info(capset_index);
311         let info = return_on_error!(result);
312         *capset_id = info.0;
313         *capset_version = info.1;
314         *capset_size = info.2;
315         NO_ERROR
316     }))
317     .unwrap_or(-ESRCH)
318 }
319 
320 /// # Safety
321 /// - `capset` must point an array of bytes of size `capset_size`.
322 #[no_mangle]
rutabaga_get_capset( ptr: &mut rutabaga, capset_id: u32, version: u32, capset: *mut u8, capset_size: u32, ) -> i32323 pub unsafe extern "C" fn rutabaga_get_capset(
324     ptr: &mut rutabaga,
325     capset_id: u32,
326     version: u32,
327     capset: *mut u8,
328     capset_size: u32,
329 ) -> i32 {
330     catch_unwind(AssertUnwindSafe(|| {
331         let size: usize = capset_size.try_into().map_err(|_e| -EINVAL).unwrap();
332         let result = ptr.get_capset(capset_id, version);
333         let vec = return_on_error!(result);
334         copy_nonoverlapping(vec.as_ptr(), capset, size);
335         NO_ERROR
336     }))
337     .unwrap_or(-ESRCH)
338 }
339 
340 #[no_mangle]
rutabaga_context_create( ptr: &mut rutabaga, ctx_id: u32, context_init: u32, context_name: *const c_char, context_name_len: u32, ) -> i32341 pub extern "C" fn rutabaga_context_create(
342     ptr: &mut rutabaga,
343     ctx_id: u32,
344     context_init: u32,
345     context_name: *const c_char,
346     context_name_len: u32,
347 ) -> i32 {
348     let mut name: Option<&str> = None;
349     if !context_name.is_null() && context_name_len > 0 {
350         // Safe because context_name is not NULL and len is a positive integer, so the caller
351         // is expected to provide a valid pointer to an array of bytes at least as long as the
352         // passed length. If the provided byte array doesn't contain valid utf-8, name will be
353         // None.
354         let view = unsafe {
355             std::slice::from_raw_parts(context_name as *const u8, context_name_len as usize)
356         };
357         name = std::str::from_utf8(view).ok();
358     }
359 
360     catch_unwind(AssertUnwindSafe(|| {
361         let result = ptr.create_context(ctx_id, context_init, name);
362         return_result(result)
363     }))
364     .unwrap_or(-ESRCH)
365 }
366 
367 #[no_mangle]
rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32368 pub extern "C" fn rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32 {
369     catch_unwind(AssertUnwindSafe(|| {
370         let result = ptr.destroy_context(ctx_id);
371         return_result(result)
372     }))
373     .unwrap_or(-ESRCH)
374 }
375 
376 #[no_mangle]
rutabaga_context_attach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32377 pub extern "C" fn rutabaga_context_attach_resource(
378     ptr: &mut rutabaga,
379     ctx_id: u32,
380     resource_id: u32,
381 ) -> i32 {
382     catch_unwind(AssertUnwindSafe(|| {
383         let result = ptr.context_attach_resource(ctx_id, resource_id);
384         return_result(result)
385     }))
386     .unwrap_or(-ESRCH)
387 }
388 
389 #[no_mangle]
rutabaga_context_detach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32390 pub extern "C" fn rutabaga_context_detach_resource(
391     ptr: &mut rutabaga,
392     ctx_id: u32,
393     resource_id: u32,
394 ) -> i32 {
395     catch_unwind(AssertUnwindSafe(|| {
396         let result = ptr.context_detach_resource(ctx_id, resource_id);
397         return_result(result)
398     }))
399     .unwrap_or(-ESRCH)
400 }
401 
402 #[no_mangle]
rutabaga_resource_create_3d( ptr: &mut rutabaga, resource_id: u32, create_3d: &rutabaga_create_3d, ) -> i32403 pub extern "C" fn rutabaga_resource_create_3d(
404     ptr: &mut rutabaga,
405     resource_id: u32,
406     create_3d: &rutabaga_create_3d,
407 ) -> i32 {
408     catch_unwind(AssertUnwindSafe(|| {
409         let result = ptr.resource_create_3d(resource_id, *create_3d);
410         return_result(result)
411     }))
412     .unwrap_or(-ESRCH)
413 }
414 
415 /// # Safety
416 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
417 ///   iovecs of size `(*iovecs).num_iovecs`.
418 /// - Each iovec must point to valid memory starting at `iov_base` with length `iov_len`.
419 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is is
420 ///   unreferenced.
421 #[no_mangle]
rutabaga_resource_attach_backing( ptr: &mut rutabaga, resource_id: u32, iovecs: &rutabaga_iovecs, ) -> i32422 pub unsafe extern "C" fn rutabaga_resource_attach_backing(
423     ptr: &mut rutabaga,
424     resource_id: u32,
425     iovecs: &rutabaga_iovecs,
426 ) -> i32 {
427     catch_unwind(AssertUnwindSafe(|| {
428         let slice = from_raw_parts((*iovecs).iovecs, (*iovecs).num_iovecs);
429         let vecs = slice
430             .iter()
431             .map(|iov| RutabagaIovec {
432                 base: iov.iov_base,
433                 len: iov.iov_len,
434             })
435             .collect();
436 
437         let result = ptr.attach_backing(resource_id, vecs);
438         return_result(result)
439     }))
440     .unwrap_or(-ESRCH)
441 }
442 
443 #[no_mangle]
rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32444 pub extern "C" fn rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32 {
445     catch_unwind(AssertUnwindSafe(|| {
446         let result = ptr.detach_backing(resource_id);
447         return_result(result)
448     }))
449     .unwrap_or(-ESRCH)
450 }
451 
452 /// # Safety
453 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
454 ///   iovecs of size `(*iovecs).num_iovecs`.
455 #[no_mangle]
rutabaga_resource_transfer_read( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, buf: Option<&iovec>, ) -> i32456 pub unsafe extern "C" fn rutabaga_resource_transfer_read(
457     ptr: &mut rutabaga,
458     ctx_id: u32,
459     resource_id: u32,
460     transfer: &rutabaga_transfer,
461     buf: Option<&iovec>,
462 ) -> i32 {
463     catch_unwind(AssertUnwindSafe(|| {
464         let mut slice_opt = None;
465         if let Some(iovec) = buf {
466             slice_opt = Some(IoSliceMut::new(std::slice::from_raw_parts_mut(
467                 iovec.iov_base as *mut u8,
468                 iovec.iov_len,
469             )));
470         }
471 
472         let result = ptr.transfer_read(ctx_id, resource_id, *transfer, slice_opt);
473         return_result(result)
474     }))
475     .unwrap_or(-ESRCH)
476 }
477 
478 #[no_mangle]
rutabaga_resource_transfer_write( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, ) -> i32479 pub extern "C" fn rutabaga_resource_transfer_write(
480     ptr: &mut rutabaga,
481     ctx_id: u32,
482     resource_id: u32,
483     transfer: &rutabaga_transfer,
484 ) -> i32 {
485     catch_unwind(AssertUnwindSafe(|| {
486         let result = ptr.transfer_write(ctx_id, resource_id, *transfer);
487         return_result(result)
488     }))
489     .unwrap_or(-ESRCH)
490 }
491 
492 /// # Safety
493 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
494 ///   iovecs of size `(*iovecs).num_iovecs`.
495 /// - If `handle` is not null, the caller must ensure it is a valid OS-descriptor.  Ownership is
496 ///   transfered to rutabaga.
497 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is is
498 ///   unreferenced.
499 #[no_mangle]
rutabaga_resource_create_blob( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, create_blob: &rutabaga_create_blob, iovecs: Option<&rutabaga_iovecs>, handle: Option<&rutabaga_handle>, ) -> i32500 pub unsafe extern "C" fn rutabaga_resource_create_blob(
501     ptr: &mut rutabaga,
502     ctx_id: u32,
503     resource_id: u32,
504     create_blob: &rutabaga_create_blob,
505     iovecs: Option<&rutabaga_iovecs>,
506     handle: Option<&rutabaga_handle>,
507 ) -> i32 {
508     catch_unwind(AssertUnwindSafe(|| {
509         let mut iovecs_opt: Option<Vec<RutabagaIovec>> = None;
510         if let Some(iovs) = iovecs {
511             let slice = from_raw_parts((*iovs).iovecs, (*iovs).num_iovecs);
512             let vecs = slice
513                 .iter()
514                 .map(|iov| RutabagaIovec {
515                     base: iov.iov_base,
516                     len: iov.iov_len,
517                 })
518                 .collect();
519             iovecs_opt = Some(vecs);
520         }
521 
522         let mut handle_opt: Option<RutabagaHandle> = None;
523 
524         // Only needed on Unix, since there is no way to create a handle from guest memory on
525         // Windows.
526         #[cfg(unix)]
527         if let Some(hnd) = handle {
528             handle_opt = Some(RutabagaHandle {
529                 os_handle: RutabagaDescriptor::from_raw_descriptor(
530                     (*hnd).os_handle.try_into().unwrap(),
531                 ),
532                 handle_type: (*hnd).handle_type,
533             });
534         }
535 
536         let result =
537             ptr.resource_create_blob(ctx_id, resource_id, *create_blob, iovecs_opt, handle_opt);
538 
539         return_result(result)
540     }))
541     .unwrap_or(-ESRCH)
542 }
543 
544 #[no_mangle]
rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32545 pub extern "C" fn rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32 {
546     catch_unwind(AssertUnwindSafe(|| {
547         let result = ptr.unref_resource(resource_id);
548         return_result(result)
549     }))
550     .unwrap_or(-ESRCH)
551 }
552 
553 /// # Safety
554 /// Caller owns raw descriptor on success and is responsible for closing it.
555 #[no_mangle]
rutabaga_resource_export_blob( ptr: &mut rutabaga, resource_id: u32, handle: &mut rutabaga_handle, ) -> i32556 pub extern "C" fn rutabaga_resource_export_blob(
557     ptr: &mut rutabaga,
558     resource_id: u32,
559     handle: &mut rutabaga_handle,
560 ) -> i32 {
561     catch_unwind(AssertUnwindSafe(|| {
562         let result = ptr.export_blob(resource_id);
563         let hnd = return_on_error!(result);
564 
565         (*handle).handle_type = hnd.handle_type;
566         (*handle).os_handle = hnd.os_handle.into_raw_descriptor() as i64;
567         NO_ERROR
568     }))
569     .unwrap_or(-ESRCH)
570 }
571 
572 #[no_mangle]
rutabaga_resource_map( ptr: &mut rutabaga, resource_id: u32, mapping: &mut rutabaga_mapping, ) -> i32573 pub extern "C" fn rutabaga_resource_map(
574     ptr: &mut rutabaga,
575     resource_id: u32,
576     mapping: &mut rutabaga_mapping,
577 ) -> i32 {
578     catch_unwind(AssertUnwindSafe(|| {
579         let result = ptr.map(resource_id);
580         let internal_map = return_on_error!(result);
581         (*mapping).ptr = internal_map.ptr as *mut c_void;
582         (*mapping).size = internal_map.size;
583         NO_ERROR
584     }))
585     .unwrap_or(-ESRCH)
586 }
587 
588 #[no_mangle]
rutabaga_resource_unmap(ptr: &mut rutabaga, resource_id: u32) -> i32589 pub extern "C" fn rutabaga_resource_unmap(ptr: &mut rutabaga, resource_id: u32) -> i32 {
590     catch_unwind(AssertUnwindSafe(|| {
591         let result = ptr.unmap(resource_id);
592         return_result(result)
593     }))
594     .unwrap_or(-ESRCH)
595 }
596 
597 #[no_mangle]
rutabaga_resource_map_info( ptr: &mut rutabaga, resource_id: u32, map_info: &mut u32, ) -> i32598 pub extern "C" fn rutabaga_resource_map_info(
599     ptr: &mut rutabaga,
600     resource_id: u32,
601     map_info: &mut u32,
602 ) -> i32 {
603     catch_unwind(AssertUnwindSafe(|| {
604         let result = ptr.map_info(resource_id);
605         *map_info = return_on_error!(result);
606         NO_ERROR
607     }))
608     .unwrap_or(-ESRCH)
609 }
610 
611 /// # Safety
612 /// - `commands` must point to a contiguous memory region of `size` bytes.
613 #[no_mangle]
rutabaga_submit_command( ptr: &mut rutabaga, cmd: &rutabaga_command, ) -> i32614 pub unsafe extern "C" fn rutabaga_submit_command(
615     ptr: &mut rutabaga,
616     cmd: &rutabaga_command,
617 ) -> i32 {
618     catch_unwind(AssertUnwindSafe(|| {
619         let cmd_slice = from_raw_parts_mut(cmd.cmd, cmd.cmd_size as usize);
620         let fence_ids = from_raw_parts(cmd.fence_ids, cmd.num_in_fences as usize);
621         let result = ptr.submit_command(cmd.ctx_id, cmd_slice, fence_ids);
622         return_result(result)
623     }))
624     .unwrap_or(-ESRCH)
625 }
626 
627 #[no_mangle]
rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32628 pub extern "C" fn rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32 {
629     catch_unwind(AssertUnwindSafe(|| {
630         let result = ptr.create_fence(*fence);
631         return_result(result)
632     }))
633     .unwrap_or(-ESRCH)
634 }
635 
636 /// # Safety
637 /// - `dir` must be a null-terminated C-string.
638 #[no_mangle]
rutabaga_snapshot(ptr: &mut rutabaga, dir: *const c_char) -> i32639 pub unsafe extern "C" fn rutabaga_snapshot(ptr: &mut rutabaga, dir: *const c_char) -> i32 {
640     catch_unwind(AssertUnwindSafe(|| {
641         let c_str_slice = CStr::from_ptr(dir);
642 
643         let result = c_str_slice.to_str();
644         let directory = return_on_error!(result);
645 
646         let file = return_on_io_error!(File::create(Path::new(directory).join("snapshot")));
647         let result = ptr.snapshot(&mut std::io::BufWriter::new(file), directory);
648         return_result(result)
649     }))
650     .unwrap_or(-ESRCH)
651 }
652 
653 /// # Safety
654 /// - `dir` must be a null-terminated C-string.
655 #[no_mangle]
rutabaga_restore(ptr: &mut rutabaga, dir: *const c_char) -> i32656 pub unsafe extern "C" fn rutabaga_restore(ptr: &mut rutabaga, dir: *const c_char) -> i32 {
657     catch_unwind(AssertUnwindSafe(|| {
658         let c_str_slice = CStr::from_ptr(dir);
659 
660         let result = c_str_slice.to_str();
661         let directory = return_on_error!(result);
662 
663         let file = return_on_io_error!(File::open(Path::new(directory).join("snapshot")));
664         let result = ptr.restore(&mut std::io::BufReader::new(file), directory);
665         return_result(result)
666     }))
667     .unwrap_or(-ESRCH)
668 }
669