Skip to main content

style_traits/
owned_array.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![allow(unsafe_code)]
6
7//! A replacement for `Box<[T; N]>` that cbindgen can understand.
8
9use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
10use std::marker::PhantomData;
11use std::ops::{Deref, DerefMut};
12use std::ptr::NonNull;
13use std::{fmt, mem};
14use to_shmem::{SharedMemoryBuilder, ToShmem};
15
16/// A struct that basically replaces a `Box<[T; N]>`, but which cbindgen can
17/// understand.
18///
19/// Every `OwnedArray` is allocation-backed with `N` fully-constructed
20/// elements; there is no empty or moved-from state.
21///
22/// We could rely on the struct layout of `Box<[T; N]>`. Per the `Box`
23/// documentation, `Box<T>` has the same ABI as `*mut T`, and since `[T; N]` is
24/// `Sized`, that's a thin pointer (unlike `Box<[T]>`, which is fat):
25///
26///   https://doc.rust-lang.org/std/boxed/index.html#memory-layout
27///
28/// But handling `Box` with cbindgen both in structs and argument positions
29/// more generally is a bit tricky.
30///
31/// This is useful when generated cbindgen code has circular references that
32/// can only be broken by indirection through boxed objects. When a type needs
33/// two, three, or a similar small number of such objects of the same kind,
34/// `OwnedArray<T, N>` is more ergonomic and efficient than holding `N`
35/// separate boxed values: it uses a single allocation and a single thin
36/// pointer instead of `N` of each.
37///
38/// Compared to `OwnedSlice<T>`, `OwnedArray<T, N>` does not store the length
39/// at runtime, `N` is part of the type, so the struct is a single
40/// pointer-word instead of two. For types used as enum variant payloads, this
41/// reduces the enclosing enum's footprint. The length can also be statically
42/// checked, wrong-length values are unrepresentable, so consumers don't need
43/// need runtime length assertions.
44///
45/// cbindgen:derive-eq=false
46/// cbindgen:derive-neq=false
47#[repr(C)]
48pub struct OwnedArray<T, const N: usize> {
49    ptr: NonNull<T>,
50    _phantom: PhantomData<T>,
51}
52
53impl<T, const N: usize> Drop for OwnedArray<T, N> {
54    #[inline]
55    fn drop(&mut self) {
56        unsafe {
57            drop(Box::from_raw(self.ptr.as_ptr() as *mut [T; N]));
58        }
59    }
60}
61
62unsafe impl<T: Send, const N: usize> Send for OwnedArray<T, N> {}
63unsafe impl<T: Sync, const N: usize> Sync for OwnedArray<T, N> {}
64
65impl<T: Clone, const N: usize> Clone for OwnedArray<T, N> {
66    #[inline]
67    fn clone(&self) -> Self {
68        std::array::from_fn(|i| self[i].clone()).into()
69    }
70}
71
72impl<T: fmt::Debug, const N: usize> fmt::Debug for OwnedArray<T, N> {
73    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
74        self.deref().fmt(formatter)
75    }
76}
77
78impl<T: PartialEq, const N: usize> PartialEq for OwnedArray<T, N> {
79    fn eq(&self, other: &Self) -> bool {
80        self.deref().eq(other.deref())
81    }
82}
83
84impl<T: Eq, const N: usize> Eq for OwnedArray<T, N> {}
85
86impl<T, const N: usize> OwnedArray<T, N> {
87    /// Convert the OwnedArray into a boxed array.
88    #[inline]
89    pub fn into_box(self) -> Box<[T; N]> {
90        let b = unsafe { Box::from_raw(self.ptr.as_ptr() as *mut [T; N]) };
91        mem::forget(self);
92        b
93    }
94
95    /// Convert the OwnedArray into an array.
96    #[inline]
97    pub fn into_array(self) -> [T; N] {
98        *self.into_box()
99    }
100}
101
102impl<T, const N: usize> Deref for OwnedArray<T, N> {
103    type Target = [T; N];
104
105    #[inline(always)]
106    fn deref(&self) -> &Self::Target {
107        unsafe { &*(self.ptr.as_ptr() as *const [T; N]) }
108    }
109}
110
111impl<T, const N: usize> DerefMut for OwnedArray<T, N> {
112    #[inline(always)]
113    fn deref_mut(&mut self) -> &mut Self::Target {
114        unsafe { &mut *(self.ptr.as_ptr() as *mut [T; N]) }
115    }
116}
117
118impl<T, const N: usize> From<[T; N]> for OwnedArray<T, N> {
119    #[inline]
120    fn from(values: [T; N]) -> Self {
121        Box::new(values).into()
122    }
123}
124
125impl<T, const N: usize> From<Box<[T; N]>> for OwnedArray<T, N> {
126    #[inline]
127    fn from(b: Box<[T; N]>) -> Self {
128        let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(b) as *mut T) };
129        Self {
130            ptr,
131            _phantom: PhantomData,
132        }
133    }
134}
135
136impl<T: Sized, const N: usize> MallocShallowSizeOf for OwnedArray<T, N> {
137    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
138        unsafe { ops.malloc_size_of(self.ptr.as_ptr()) }
139    }
140}
141
142impl<T: MallocSizeOf + Sized, const N: usize> MallocSizeOf for OwnedArray<T, N> {
143    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
144        self.shallow_size_of(ops) + (**self).size_of(ops)
145    }
146}
147
148impl<T: ToShmem + Sized, const N: usize> ToShmem for OwnedArray<T, N> {
149    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
150        unsafe {
151            let dest = to_shmem::to_shmem_slice(self.iter(), builder)?;
152            Ok(mem::ManuallyDrop::new(Self::from(Box::from_raw(
153                dest as *mut [T; N],
154            ))))
155        }
156    }
157}