dungeon_cell/
buffer.rs

1//! Raw byte buffer to store other type's values.
2
3use core::mem::MaybeUninit;
4use core::ops::{Deref, DerefMut};
5use core::ptr::{addr_of, addr_of_mut};
6
7use crate::compile_time::{
8    const_transmute, const_usize_assert, SizeSmallerOrEqualTo,
9};
10use crate::marker_traits::IsSize;
11
12/// Array of maybe uninit bytes.
13///
14/// This type acts as a `[MaybeUninit<u8>; N]` where `N` is given by the generic `Size`.
15#[repr(transparent)]
16pub struct Buffer<Size: IsSize> {
17    /// Byte buffer of type `[MaybeUninit<u8>; Size::VALUE]`.
18    pub buffer: Size::Buffer,
19}
20
21impl<Size: IsSize> Copy for Buffer<Size> {}
22
23impl<Size: IsSize> Clone for Buffer<Size> {
24    fn clone(&self) -> Self {
25        *self
26    }
27}
28
29impl<Size: IsSize> Buffer<Size> {
30    /// Create uninitialized buffer.
31    ///
32    /// All bytes in the returned buffer will be in an uninitialized state
33    /// and cannot be read.
34    pub const fn uninit() -> Self {
35        Self {
36            buffer: Size::UNINIT_BUFFER,
37        }
38    }
39
40    /// Create buffer storing a value.
41    ///
42    /// This creates a uninitialized buffer then moves the value into it.
43    /// Bytes not needed to represent the value will be left in an uninitialized state.
44    ///
45    /// The returned buffer will very likely not be properly aligned to allow creating a
46    /// borrow/pointer to the value. To fix this, wrap the returned buffer in a
47    /// [`Aligned`][crate::align::Aligned].
48    pub const fn new<T>(value: T) -> Self {
49        const_usize_assert::<SizeSmallerOrEqualTo<T, Size>>();
50
51        // SAFETY: We know `Size::Buffer` is an array of maybe uninit bytes.
52        // And we know that it's size is equal to or larger than the size of `T`
53        // because of the above assert. Thus, its safe to transmute from `T`
54        // to the byte buffer.
55        let buffer = unsafe { const_transmute::<T, Size::Buffer>(value) };
56
57        Self { buffer }
58    }
59
60    /// Get a borrow of the byte buffer.
61    pub const fn get(this: &Self) -> &[MaybeUninit<u8>] {
62        // SAFETY: `self.buffer` is `Size::VALUE` bytes long because of the definition
63        // of `IsSize`. We don't need to worry about initialization because we
64        // make a slize of maybe uninit bytes. We don't mutate the data while the lifetime
65        // exists because `&self` is a borrow of the wrapper.
66        unsafe {
67            core::slice::from_raw_parts(
68                addr_of!(this.buffer) as *const MaybeUninit<u8>,
69                Size::VALUE,
70            )
71        }
72    }
73
74    /// Get a mutable borrow of the byte buffer.
75    pub fn get_mut(this: &mut Self) -> &mut [MaybeUninit<u8>] {
76        // SAFETY: `self.buffer` is `Size::VALUE` bytes long because of the definition
77        // of `IsSize`. We don't need to worry about initialization because we
78        // make a slize of maybe uninit bytes. We don't mutate the data while the lifetime
79        // exists because `&self` is a borrow of the wrapper.
80        unsafe {
81            core::slice::from_raw_parts_mut(
82                addr_of_mut!(this.buffer) as *mut MaybeUninit<u8>,
83                Size::VALUE,
84            )
85        }
86    }
87
88    /// Convert buffer into a value of type `T`.
89    ///
90    /// # Safety
91    /// The buffer must be storing a valid value of type `T`.
92    pub const unsafe fn into_value<T>(this: Self) -> T {
93        // Note: we don't have to worry about alignment here because the transmute
94        // (like the std one) automatically forces the alignment.
95
96        // SAFETY: By the safety requirement of the function we know the buffer
97        // is storing a value of `T`. So we can transmute from the buffer to `T`.
98        unsafe { const_transmute::<Size::Buffer, T>(this.buffer) }
99    }
100
101    /// Get pointer to first byte.
102    ///
103    /// This is useful for unsafe code.
104    pub const fn as_pointer_mut(buffer: *mut Self) -> *mut MaybeUninit<u8> {
105        // The struct is `repr(transparent)` so the pointer will be to the wrapped array.
106        buffer as _
107    }
108}
109
110impl<Size: IsSize> Deref for Buffer<Size> {
111    type Target = [MaybeUninit<u8>];
112
113    fn deref(&self) -> &Self::Target {
114        Self::get(self)
115    }
116}
117
118impl<Size: IsSize> DerefMut for Buffer<Size> {
119    fn deref_mut(&mut self) -> &mut Self::Target {
120        Self::get_mut(self)
121    }
122}