Simplify implementation of arrays

This commit is contained in:
Dustin J. Mitchell 2022-02-07 00:15:09 +00:00
parent e11506ee6a
commit a270b6c254
5 changed files with 130 additions and 54 deletions

View file

@ -1,3 +1,6 @@
use crate::util::vec_into_raw_parts;
use std::ptr::NonNull;
/// Support for values passed to Rust by value. These are represented as full structs in C. Such
/// values are implicitly copyable, via C's struct assignment.
///
@ -142,3 +145,86 @@ pub(crate) trait PassByPointer: Sized {
}
}
}
/// Support for arrays of objects referenced by pointer.
///
/// The underlying C type should have three fields, containing items, length, and capacity. The
/// required trait functions just fetch and set these fields. The PassByValue trait will be
/// implemented automatically, converting between the C type and `Vec<NonNull<Element>>`. For most
/// cases, it is only necessary to implement `tc_.._free` that first calls
/// `PassByValue::take_from_arg(arg, PointerArray::null_value())` to take the existing value and
/// replace it with the null value; then `PointerArray::drop_pointer_vector(..)` to drop the
/// resulting vector and all of the objects it points to.
///
/// # Safety
///
/// The C type must be documented as read-only. None of the fields may be modified, nor anything
/// in the `items` array.
///
/// This class guarantees that the items pointer is non-NULL for any valid array (even when len=0),
/// and that all pointers at indexes 0..len-1 are non-NULL.
pub(crate) trait PointerArray: Sized {
type Element: 'static + PassByPointer;
/// Create a new PointerArray from the given items, len, and capacity.
///
/// # Safety
///
/// The arguments must either:
/// - be NULL, 0, and 0, respectively; or
/// - be valid for Vec::from_raw_parts
unsafe fn from_raw_parts(items: *const NonNull<Self::Element>, len: usize, cap: usize) -> Self;
/// Get the items, len, and capacity (in that order) for this instance. These must be
/// precisely the same values passed tearlier to `from_raw_parts`.
fn into_raw_parts(self) -> (*const NonNull<Self::Element>, usize, usize);
/// Generate a NULL value. By default this is a NULL items pointer with zero length and
/// capacity.
fn null_value() -> Self {
// SAFETY:
// - satisfies the first case in from_raw_parts' safety documentation
unsafe { Self::from_raw_parts(std::ptr::null(), 0, 0) }
}
/// Drop a vector of element pointers. This is a convenience function for implementing
/// tc_.._free functions.
fn drop_pointer_vector(mut vec: Vec<NonNull<Self::Element>>) {
// first, drop each of the elements in turn
for p in vec.drain(..) {
// SAFETY:
// - p is not NULL (NonNull)
// - p was generated by Rust (true for all arrays)
// - p was not modified (all arrays are immutable from C)
// - caller will not use this pointer again (promised by caller; p has been drain'd from
// the vector)
drop(unsafe { PassByPointer::take_from_arg(p.as_ptr()) });
}
drop(vec);
}
}
impl<A> PassByValue for A
where
A: PointerArray,
{
type RustType = Vec<NonNull<A::Element>>;
unsafe fn from_ctype(self) -> Self::RustType {
let (items, len, cap) = self.into_raw_parts();
debug_assert!(!items.is_null());
// SAFETY:
// - PointerArray::from_raw_parts requires that items, len, and cap be valid for
// Vec::from_raw_parts if not NULL, and they are not NULL (as promised by caller)
// - PointerArray::into_raw_parts returns precisely the values passed to from_raw_parts.
// - those parts are passed to Vec::from_raw_parts here.
unsafe { Vec::from_raw_parts(items as *mut _, len, cap) }
}
fn as_ctype(arg: Self::RustType) -> Self {
let (items, len, cap) = vec_into_raw_parts(arg);
// SAFETY:
// - satisfies the second case in from_raw_parts' safety documentation
unsafe { Self::from_raw_parts(items, len, cap) }
}
}