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}