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