Trait ffizz_passby::OpaqueStruct
source · [−]pub trait OpaqueStruct: Sized {
type CType: Sized;
unsafe fn with_ref<T, F: Fn(&Self) -> T>(cptr: *const Self::CType, f: F) -> T { ... }
unsafe fn with_mut_ref<T, F: Fn(&mut Self) -> T>(
cptr: *mut Self::CType,
f: F
) -> T { ... }
unsafe fn initialize(cptr: *mut Self::CType, rval: Self) { ... }
unsafe fn return_val(self) -> Self::CType { ... }
unsafe fn take(cptr: *mut Self::CType) -> Self { ... }
}Expand description
This trait supports structs allocated by C but managed by Rust.
This is useful for commonly-used data types that have a fixed size, to avoid allocating the data type itself on the heap.
This approach uses a “regular” Rust type in the Rust code, and a C type with a _reserved
field to reserve the space on the C stack. The tricky bit is to convince the C code to
allocate enough space to store the Rust value. There is no constant way to determine the space
required for a Rust value, but it is possible to make a conservative guess, possibly leaving
some unused space. The suggested C type is struct CType([u64; N]) for some N large enough to
contain the Rust type on the required platforms. In C, this type would be defined as struct ctype_t { _reserved uint64_t[N] } for the same N. The types must also have the same alignment.
This type contains debug assertions regarding the size of the Rust and C types, and will fail at runtime if the alignment or size of the two types is not as required.
This type provides two functions useful for initialization of a type: initialize takes an
“out arg” pointing to an uninitialized value, and initializes it; while return_val simply
returns a struct value that can be used to initialize a variable. Both function similarly,
so choose the one that makes the most senes for your API. For example, a constructor which
can also return an error may prefer to put the error in the return value and use initialize.
Safety
C allows uninitialized values, while Rust does not. Be careful in the documentation for the C API to ensure that values are properly initialized before they are used.
Required Associated Types
Provided Methods
Call the contained function with a shared reference to the data type.
Safety
- cptr must not be NULL and must point to a valid (initialized) CType value
- no other thread may mutate the value pointed to by CType until with_ref returns.
Call the contained function with an exclusive reference to the data type.
Safety
- cptr must not be NULL and must point to a valid (initialized) CType value
- no other thread may access the value pointed to by CType until with_ref_mut returns.
unsafe fn initialize(cptr: *mut Self::CType, rval: Self)
unsafe fn initialize(cptr: *mut Self::CType, rval: Self)
Initialize the value pointed to cptr with rval, “moving” rval into the pointer.
Safety
- cptr must not be NULL, must be aligned for CType, and must have enough space for CType.
- to avoid a leak, the value must eventually be moved out of *cptr and into a Rust value
to be dropped (see
OpaqueStruct::take)
unsafe fn return_val(self) -> Self::CType
unsafe fn return_val(self) -> Self::CType
Return a CType containing self, moving self in the process.
Safety
- to avoid a leak, the value must eventually be moved out of the return value
and into a Rust value to be dropped (see
OpaqueStruct::take)
Examples found in repository?
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
pub unsafe extern "C" fn byte_buffer_new() -> byte_buffer_t {
unsafe { ByteBuffer::return_val(ByteBuffer(Vec::new())) }
}
/// Initialize the given byte_buffer_t to an empty value.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_init(bb: *mut byte_buffer_t) {
unsafe { ByteBuffer::initialize(bb, ByteBuffer(Vec::new())) }
}
/// Free a byte_buffer_t.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_free(bb: *mut byte_buffer_t) {
let bb = unsafe { ByteBuffer::take(bb) };
drop(bb); // just to be explicit
}
/// Checksum a byte_buffer_t's contents by XOR'ing all bytes together.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_checksum(bb: *const byte_buffer_t) -> u8 {
unsafe {
ByteBuffer::with_ref(bb, |bb| {
// ok, not the most exciting "checksum"!
bb.0.iter().copied().reduce(|a, b| a ^ b).unwrap_or(0)
})
}
}
/// Add a byte to the byte buffer.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_push(bb: *mut byte_buffer_t, b: u8) {
unsafe { ByteBuffer::with_mut_ref(bb, |bb| bb.0.push(b)) }
}
/// Combine two byte buffers, returning a new byte buffer containing the bytes
/// from both inputs. This function consumes its inputs and they must not be
/// used after it returns.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_combine(
bb1: *mut byte_buffer_t,
bb2: *mut byte_buffer_t,
) -> byte_buffer_t {
let mut bb1 = unsafe { ByteBuffer::take(bb1) };
let bb2 = unsafe { ByteBuffer::take(bb2) };
// modify bb1 in place (but it's not in the caller's location anymore)
bb1.0.extend(&bb2.0[..]);
unsafe { ByteBuffer::return_val(bb1) }
}Take a CType and return an owned value.
This method is used for “xxx_free” functions, which simply drop the resulting owned value.
It is also used for functions which are documented in the C API as consuming the given value, saving the caller the trouble of separately freeing the value.
The memory pointed to by cptr is zeroed to prevent accidental re-use.
Safety
- cptr must not be NULL and must point to a valid (initialized) CType value
- the memory pointed to by cptr is uninitialized when this function returns
Examples found in repository?
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
pub unsafe extern "C" fn byte_buffer_free(bb: *mut byte_buffer_t) {
let bb = unsafe { ByteBuffer::take(bb) };
drop(bb); // just to be explicit
}
/// Checksum a byte_buffer_t's contents by XOR'ing all bytes together.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_checksum(bb: *const byte_buffer_t) -> u8 {
unsafe {
ByteBuffer::with_ref(bb, |bb| {
// ok, not the most exciting "checksum"!
bb.0.iter().copied().reduce(|a, b| a ^ b).unwrap_or(0)
})
}
}
/// Add a byte to the byte buffer.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_push(bb: *mut byte_buffer_t, b: u8) {
unsafe { ByteBuffer::with_mut_ref(bb, |bb| bb.0.push(b)) }
}
/// Combine two byte buffers, returning a new byte buffer containing the bytes
/// from both inputs. This function consumes its inputs and they must not be
/// used after it returns.
#[no_mangle]
pub unsafe extern "C" fn byte_buffer_combine(
bb1: *mut byte_buffer_t,
bb2: *mut byte_buffer_t,
) -> byte_buffer_t {
let mut bb1 = unsafe { ByteBuffer::take(bb1) };
let bb2 = unsafe { ByteBuffer::take(bb2) };
// modify bb1 in place (but it's not in the caller's location anymore)
bb1.0.extend(&bb2.0[..]);
unsafe { ByteBuffer::return_val(bb1) }
}