safa_abi/ffi/
slice.rs

1use crate::{
2    errors::{ErrorStatus, IntoErr},
3    ffi::NotZeroable,
4};
5
6/// Represents an error that can occur when converting a RawSlice<T> to a Rust slice &[T].
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum InvalidSliceError {
9    PtrNotAligned,
10    PtrIsNull,
11    LenTooLarge,
12    Other,
13}
14
15impl IntoErr for InvalidSliceError {
16    fn into_err(self) -> ErrorStatus {
17        match self {
18            InvalidSliceError::LenTooLarge => ErrorStatus::StrTooLong,
19            _ => ErrorStatus::InvalidPtr,
20        }
21    }
22}
23
24#[repr(C)]
25#[derive(Debug, Clone, Copy)]
26/// A C compatible slice of type `T`
27pub struct Slice<T> {
28    pub(crate) ptr: *mut T,
29    pub(crate) len: usize,
30}
31
32impl<T> Slice<T> {
33    #[inline]
34    pub const unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
35        assert!(
36            !ptr.is_null() && len <= isize::MAX as usize,
37            "RawSlice::from_raw_parts requires ptr to be aligned and non-null, and len must be less than or equal to isize::MAX"
38        );
39        Self { ptr, len }
40    }
41
42    #[inline]
43    pub const fn from_slice_mut(slice: &mut [T]) -> Self {
44        Self {
45            ptr: slice.as_mut_ptr(),
46            len: slice.len(),
47        }
48    }
49
50    #[inline]
51    pub const fn from_slice(slice: &[T]) -> Self {
52        Self {
53            ptr: slice.as_ptr().cast_mut(),
54            len: slice.len(),
55        }
56    }
57
58    #[inline(always)]
59    pub const fn len(&self) -> usize {
60        self.len
61    }
62
63    #[inline(always)]
64    pub const fn as_ptr(&self) -> *const T {
65        self.ptr
66    }
67
68    /// Converts an FFI [`RawSlice<T>`] into a raw rust slice of type `T`
69    #[inline]
70    pub const unsafe fn as_slice_mut_unchecked<'a>(&self) -> &'a mut [T] {
71        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
72    }
73
74    #[inline]
75    pub const unsafe fn as_slice_unchecked<'a>(&self) -> &'a [T] {
76        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
77    }
78
79    /// Attempts to convert an FFI [`RawSlice<T>`] into a raw rust slice of type `T`, running the pointer into a custom validator function
80    ///
81    /// returns Err(InvalidSliceError) if the slice doesn't pass the rust slice requirements or returns Err(InvalidSliceError::Other) if the validator function returns false
82    #[inline]
83    pub unsafe fn try_as_slice_mut_custom<'a>(
84        &self,
85        validator: impl FnOnce(*const ()) -> bool,
86    ) -> Result<&'a mut [T], InvalidSliceError> {
87        if self.ptr.is_null() {
88            return Err(InvalidSliceError::PtrIsNull);
89        }
90
91        if !self.ptr.is_aligned() {
92            return Err(InvalidSliceError::PtrNotAligned);
93        }
94
95        if self.len > isize::MAX as usize {
96            return Err(InvalidSliceError::LenTooLarge);
97        }
98
99        if !validator(self.ptr.cast()) {
100            return Err(InvalidSliceError::Other);
101        }
102
103        Ok(unsafe { self.as_slice_mut_unchecked() })
104    }
105
106    /// Attempts to convert an FFI [`RawSlice<T>`] into a raw rust slice of type `T`, running the pointer into a custom validator function
107    ///
108    /// returns Err(InvalidSliceError) if the slice doesn't pass the rust slice requirements or returns Err(InvalidSliceError::Other) if the validator function returns false
109    #[inline]
110    pub unsafe fn try_as_slice_custom<'a>(
111        &self,
112        validator: impl Fn(*const ()) -> bool,
113    ) -> Result<&'a [T], InvalidSliceError> {
114        unsafe {
115            match self.try_as_slice_mut_custom(validator) {
116                Ok(slice) => Ok(slice),
117                Err(err) => Err(err),
118            }
119        }
120    }
121
122    pub unsafe fn try_as_slice_mut<'a>(&self) -> Result<&'a mut [T], InvalidSliceError> {
123        unsafe {
124            match self.try_as_slice_mut_custom(|_| true) {
125                Ok(slice) => Ok(slice),
126                Err(err) => Err(err),
127            }
128        }
129    }
130
131    pub unsafe fn try_as_slice<'a>(&self) -> Result<&'a [T], InvalidSliceError> {
132        unsafe {
133            match self.try_as_slice_custom(|_| true) {
134                Ok(slice) => Ok(slice),
135                Err(err) => Err(err),
136            }
137        }
138    }
139}
140
141impl<T> Slice<Slice<T>> {
142    /// Converts an FFI [Slice] of slices to a Rust slice of slices of type `T` *mut [*mut [T]].
143    ///
144    /// # Safety
145    ///
146    /// The given slice will be unsafely reused, for now the data will be left unchanged in the current rust version because the layout of [Slice<T>] is the same as &`[T]`,
147    /// However since the layout of slices isn't guaranteed yet by rust, this function may change the given buffer in the future, or by some obscure optimizations.
148    ///
149    /// this should be solved if this [RFC](https://github.com/rust-lang/rfcs/pull/3775) got accepted
150    #[inline]
151    pub const unsafe fn from_slices_ptr_mut(slices: *mut [*mut [T]]) -> Self {
152        let old_slices = unsafe { &mut *slices };
153        let raw_slices = unsafe { &mut *(slices as *mut [Slice<T>]) };
154
155        let mut i = 0;
156        while i < old_slices.len() {
157            let slice = old_slices[i];
158            raw_slices[i] = unsafe { Slice::from_slice(&*slice) };
159            i += 1;
160        }
161
162        Slice::from_slice(raw_slices)
163    }
164
165    /// Attempts to convert an FFI [Slice] of [Slice]s into a rust slice of slices *mut `[*mut [T]]`.
166    /// given an FFI [Slice] of [Slice]s
167    ///
168    /// # Safety
169    ///
170    /// The given FFI slice will be unsafely reused, for now the data will be left unchanged in the current rust version because the layout of [Slice<T>] is the same as &`[T]`,
171    /// However since the layout of slices isn't guaranteed yet by rust, this function may change the given buffer in the future, or by some obscure optimizations.
172    ///
173    /// this should be solved if this [RFC](https://github.com/rust-lang/rfcs/pull/3775) got accepted
174    #[inline]
175    pub unsafe fn try_into_slices_ptr_mut(
176        self,
177        custom_validate: impl Fn(*const ()) -> bool,
178    ) -> Result<*mut [*mut [T]], InvalidSliceError> {
179        let root = unsafe { self.try_as_slice_mut_custom(&custom_validate)? };
180        let root_ptr = root as *mut _;
181        let results = unsafe { &mut *(root_ptr as *mut [*mut [T]]) };
182
183        let mut i = 0;
184        while i < results.len() {
185            let slice = &root[i];
186            results[i] = unsafe { slice.try_as_slice_mut_custom(&custom_validate)? };
187            i += 1;
188        }
189
190        Ok(results)
191    }
192}
193
194impl<T> NotZeroable for Slice<T> {
195    #[inline(always)]
196    fn is_zero(&self) -> bool {
197        self.ptr.is_null()
198    }
199}