Skip to main content

osom_lib_boxed/
cbox.rs

1//! This module defines the [`CBox`] type.
2use core::borrow::{Borrow, BorrowMut};
3use core::marker::PhantomData;
4use core::ops::{Deref, DerefMut};
5use core::ptr::NonNull;
6
7use osom_lib_alloc::traits::Allocator;
8use osom_lib_reprc::traits::ReprC;
9use osom_lib_try_clone::TryClone;
10
11use crate::errors::CBoxTryCloneError;
12
13use super::errors::CBoxError;
14use super::layout::CBoxLayout;
15
16/// The ABI-stable box type. This type is equivalent to the standard `Box` type,
17/// except it is suitable for FFI and also accepts any allocator.
18#[repr(transparent)]
19#[must_use]
20#[derive(Debug)]
21pub struct CBox<T, TAllocator: Allocator> {
22    data: *mut u8,
23    _phantom: PhantomData<(T, TAllocator)>,
24}
25
26unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for CBox<T, TAllocator> {
27    const CHECK: () = const {
28        osom_lib_reprc::hidden::is_reprc::<T>();
29        osom_lib_reprc::hidden::is_reprc::<TAllocator>();
30    };
31}
32
33impl<T, TAllocator: Allocator> CBox<T, TAllocator> {
34    const LAYOUT: CBoxLayout<T, TAllocator> = CBoxLayout::new();
35
36    /// Creates a new [`CBox`] with the default allocator.
37    ///
38    /// # Errors
39    ///
40    /// For details see [`CBoxError`].
41    #[inline]
42    pub fn new(value: T) -> Result<Self, CBoxError>
43    where
44        TAllocator: Default,
45    {
46        Self::with_allocator(value, TAllocator::default())
47    }
48
49    /// Creates a new [`CBox`] with a custom allocator.
50    ///
51    /// # Errors
52    ///
53    /// For details see [`CBoxError`].
54    #[inline]
55    pub fn with_allocator(value: T, allocator: TAllocator) -> Result<Self, CBoxError> {
56        #[allow(unused_mut)]
57        let mut allocator = allocator;
58        let ptr = allocator
59            .allocate(Self::LAYOUT.total_layout)
60            .map_err(|_| CBoxError::AllocationError)?;
61        let mut result = Self {
62            data: ptr.as_ptr(),
63            _phantom: PhantomData,
64        };
65
66        unsafe {
67            CBox::allocator_ptr_mut(&mut result).write(allocator);
68            CBox::data_ptr_mut(&mut result).write(value);
69        }
70
71        Ok(result)
72    }
73
74    /// Returns a reference to the data stored in the [`CBox`].
75    #[inline(always)]
76    #[must_use]
77    pub const fn data(box_: &Self) -> &T {
78        unsafe { &*CBox::data_ptr(box_) }
79    }
80
81    /// Returns a mutable reference to the data stored in the [`CBox`].
82    #[inline(always)]
83    #[must_use]
84    pub const fn data_mut(box_: &mut Self) -> &mut T {
85        unsafe { &mut *CBox::data_ptr_mut(box_) }
86    }
87
88    /// Returns a raw pointer to the underlying data stored in the [`CBox`].
89    ///
90    /// The caller must ensure that the pointer does not outlive the [`CBox`].
91    #[inline(always)]
92    #[must_use]
93    pub const fn data_ptr(box_: &Self) -> *const T {
94        unsafe { box_.data.add(Self::LAYOUT.data_offset).cast::<T>() }
95    }
96
97    /// Converts the [`CBox`] into a raw pointer.
98    ///
99    /// # Notes
100    ///
101    /// * This method does not touch internal structure of the [`CBox`], `self` is simply forgotten.
102    /// * The caller must ensure that the pointer does not outlive the [`CBox`].
103    #[inline(always)]
104    #[must_use]
105    pub const fn into_raw_ptr(box_: Self) -> *mut u8 {
106        let ptr = box_.data;
107        core::mem::forget(box_);
108        ptr
109    }
110
111    /// Converts a raw pointer back to a [`CBox`] container.
112    ///
113    /// # Safety
114    ///
115    /// * The caller must ensure that the pointer came from the previous
116    ///   call to [`CBox::into_raw_ptr`].
117    /// * The caller must ensure that the raw pointer won't be used after
118    ///   the call.
119    ///
120    /// Otherwise the behavior is undefined.
121    #[inline(always)]
122    pub const unsafe fn from_raw_ptr(ptr: *mut u8) -> Self {
123        Self {
124            data: ptr,
125            _phantom: PhantomData,
126        }
127    }
128
129    /// Returns a raw mutable pointer to the underlying data stored in the [`CBox`].
130    ///
131    /// The caller must ensure that the pointer does not outlive the [`CBox`].
132    #[inline(always)]
133    #[must_use]
134    pub const fn data_ptr_mut(box_: &mut Self) -> *mut T {
135        unsafe { box_.data.add(Self::LAYOUT.data_offset).cast::<T>() }
136    }
137
138    /// Unpacks the [`CBox`] and returns the data.
139    /// The underlying memory gets deallocated.
140    #[inline]
141    #[must_use]
142    pub fn unpack(mut box_: Self) -> T {
143        let result = unsafe { CBox::drop(&mut box_) };
144        core::mem::forget(box_);
145        result
146    }
147
148    #[inline(always)]
149    const fn allocator_ref(box_: &Self) -> &TAllocator {
150        unsafe { &*CBox::allocator_ptr(box_) }
151    }
152
153    #[inline(always)]
154    #[must_use]
155    const fn allocator_ptr_mut(box_: &mut Self) -> *mut TAllocator {
156        unsafe { box_.data.add(Self::LAYOUT.allocator_offset).cast::<TAllocator>() }
157    }
158
159    #[inline(always)]
160    #[must_use]
161    const fn allocator_ptr(box_: &Self) -> *const TAllocator {
162        unsafe { box_.data.add(Self::LAYOUT.allocator_offset).cast::<TAllocator>() }
163    }
164
165    #[inline]
166    unsafe fn drop(box_: &mut Self) -> T {
167        unsafe {
168            let data = CBox::data_ptr(box_).read();
169            let mut allocator = CBox::allocator_ptr(box_).read();
170            allocator.deallocate(NonNull::new_unchecked(box_.data), Self::LAYOUT.total_layout);
171            data
172        }
173    }
174}
175
176impl<T, TAllocator: Allocator> Drop for CBox<T, TAllocator> {
177    fn drop(&mut self) {
178        let _ = unsafe { CBox::drop(self) };
179    }
180}
181
182impl<T, TAllocator: Allocator> AsRef<T> for CBox<T, TAllocator> {
183    fn as_ref(&self) -> &T {
184        CBox::data(self)
185    }
186}
187
188impl<T, TAllocator: Allocator> AsMut<T> for CBox<T, TAllocator> {
189    fn as_mut(&mut self) -> &mut T {
190        CBox::data_mut(self)
191    }
192}
193
194impl<T, TAllocator: Allocator> Borrow<T> for CBox<T, TAllocator> {
195    fn borrow(&self) -> &T {
196        CBox::data(self)
197    }
198}
199
200impl<T, TAllocator: Allocator> BorrowMut<T> for CBox<T, TAllocator> {
201    fn borrow_mut(&mut self) -> &mut T {
202        CBox::data_mut(self)
203    }
204}
205
206impl<T, TAllocator> TryClone for CBox<T, TAllocator>
207where
208    T: TryClone,
209    TAllocator: Allocator + TryClone,
210{
211    type Error = CBoxTryCloneError;
212
213    #[inline]
214    fn try_clone(&self) -> Result<Self, Self::Error> {
215        let data = CBox::data(self)
216            .try_clone()
217            .map_err(|_| CBoxTryCloneError::ItemCloningError)?;
218        let allocator = CBox::allocator_ref(self)
219            .try_clone()
220            .map_err(|_| CBoxTryCloneError::AllocatorCloningError)?;
221        Self::with_allocator(data, allocator).map_err(CBoxTryCloneError::from)
222    }
223}
224
225impl<T, TAllocator> Clone for CBox<T, TAllocator>
226where
227    T: TryClone,
228    TAllocator: Allocator + TryClone,
229{
230    fn clone(&self) -> Self {
231        self.try_clone().expect("Couldn't clone CBox")
232    }
233}
234
235impl<T, TAllocator: Allocator> Deref for CBox<T, TAllocator> {
236    type Target = T;
237
238    fn deref(&self) -> &Self::Target {
239        CBox::data(self)
240    }
241}
242
243impl<T, TAllocator: Allocator> DerefMut for CBox<T, TAllocator> {
244    fn deref_mut(&mut self) -> &mut Self::Target {
245        CBox::data_mut(self)
246    }
247}