reusable_memory/
base.rs

1use std::{mem, num::NonZeroUsize, ptr};
2
3use super::borrow::ReusableMemoryBorrow;
4
5/// `align_up(base, align)` returns the smallest greater integer than `base` aligned to `align`.
6///
7/// More formally:
8/// ```norun
9/// f_d(x) =
10///     x, if x mod d = 0
11///     x + d - x mod d, otherwise
12/// ```
13/// simplifies to `x - 1 + d - (x - 1) mod d`
14/// assuming `d = 2^N`, can also be written in code like: `(x - 1 + d) & !(d - 1)`
15/// where `x = base` and `d = align`
16///
17/// Similar code to `std::alloc::Layout::padding_needed_for`, but without the `- base`
18const fn align_up(base: usize, align: usize) -> usize {
19	base.wrapping_add(align.wrapping_sub(1)) & !align.wrapping_sub(1)
20}
21macro_rules! impl_borrow_mut_X_as {
22	(
23		pub fn $capacity_name: ident;
24		pub fn $name: ident<$($gen_name: ident),+>[$count: literal];
25	) => {
26		pub fn $capacity_name<$($gen_name),+>(
27			&self, capacity: [NonZeroUsize; $count]
28		) -> usize {
29			let align_of: [usize; $count] = [$(mem::align_of::<$gen_name>()),+];
30
31			$(
32				assert_ne!(mem::size_of::<$gen_name>(), 0);
33			)+
34
35			let needed_bytes = 0;
36			let counter = 0;
37
38			$(
39				// where the block for $gen_name starts, in bytes, and the index
40				#[allow(non_snake_case)]
41				let $gen_name: (usize, usize) = (align_up(needed_bytes, mem::align_of::<$gen_name>()), counter);
42				// where the block from $gen_name ends
43				let needed_bytes = $gen_name.0 + mem::size_of::<$gen_name>() * capacity[counter].get();
44
45				#[allow(unused_variables)]
46				let counter = counter + 1;
47			)+
48
49			// Add `align - 1` to `needed_bytes` if align of `T` is more than align of `B`.
50			let align_bump = if mem::align_of::<B>() >= mem::align_of::<T>() {
51				0
52			} else {
53				align_of[0] - 1
54			};
55			// Add `align_bump` afterwards so that $gen_name starts are correct
56			let needed_bytes = needed_bytes + align_bump;
57			let needed_length = (needed_bytes + mem::size_of::<B>() - 1) / mem::size_of::<B>();
58
59			needed_length
60		}
61
62		pub fn $name<'mem, $($gen_name),+>(
63			&'mem mut self, capacity: [NonZeroUsize; $count]
64		) ->( $(ReusableMemoryBorrow<'mem, $gen_name>),+ ) {
65			let align_of: [usize; $count] = [$(mem::align_of::<$gen_name>()),+];
66
67			$(
68				assert_ne!(mem::size_of::<$gen_name>(), 0);
69			)+
70
71			let needed_bytes = 0;
72			let counter = 0;
73
74			$(
75				// where the block for $gen_name starts, in bytes, and the index
76				#[allow(non_snake_case)]
77				let $gen_name: (usize, usize) = (align_up(needed_bytes, mem::align_of::<$gen_name>()), counter);
78				// where the block from $gen_name ends
79				let needed_bytes = $gen_name.0 + mem::size_of::<$gen_name>() * capacity[counter].get();
80
81				#[allow(unused_variables)]
82				let counter = counter + 1;
83			)+
84
85			// Add `align - 1` to `needed_bytes` if align of `T` is more than align of `B`.
86			let align_bump = if mem::align_of::<B>() >= mem::align_of::<T>() {
87				0
88			} else {
89				align_of[0] - 1
90			};
91			// Add `align_bump` afterwards so that $gen_name starts are correct
92			let needed_bytes = needed_bytes + align_bump;
93			let needed_length = (needed_bytes + mem::size_of::<B>() - 1) / mem::size_of::<B>();
94
95			// Reserve the memory
96			self.vec.reserve(needed_length);
97			let memory_ptr = self.vec.as_mut_ptr();
98
99			// Compute the offset we need from the vec pointer to have the proper alignment.
100			let align_offset = memory_ptr.align_offset(align_of[0]);
101			if align_offset == std::usize::MAX {
102				panic!("Could not align pointer");
103			}
104
105			unsafe {
106				(
107					$(
108						ReusableMemoryBorrow::from_raw_parts(
109							ptr::NonNull::new_unchecked(
110								(memory_ptr.add(align_offset) as *mut u8).add($gen_name.0) as *mut $gen_name
111							),
112							capacity[$gen_name.1]
113						)
114					),+
115				)
116			}
117		}
118	}
119}
120
121/// Reusable memory struct.
122///
123/// This struct keeps previously allocated memory and can mutably reborrow it as a different type on demand.
124///
125/// The generic type `B` can be used to control the alignment of the base memory, but it must not be zero sized.
126/// Using a zero sized `B` returns an error in constructor.
127#[derive(Debug, Clone)]
128pub struct ReusableMemory<B = u8> {
129	vec: Vec<B>
130}
131impl<B> ReusableMemory<B> {
132	impl_borrow_mut_X_as!(
133		pub fn needed_capacity_for_two;
134		pub fn borrow_mut_two_as<T, U>[2];
135	);
136
137	impl_borrow_mut_X_as!(
138		pub fn needed_capacity_for_three;
139		pub fn borrow_mut_three_as<T, U, V>[3];
140	);
141
142	impl_borrow_mut_X_as!(
143		pub fn needed_capacity_for_four;
144		pub fn borrow_mut_four_as<T, U, V, W>[4];
145	);
146
147	impl_borrow_mut_X_as!(
148		pub fn needed_capacity_for_five;
149		pub fn borrow_mut_five_as<T, U, V, W, X>[5];
150	);
151
152	/// Creates new reusable memory without checking the size of `B`.
153	///
154	/// Can be used in const context.
155	///
156	/// ### Safety
157	///
158	/// * `std::mem::size_of::<B>()` must not be zero.
159	pub const unsafe fn new_unchecked() -> Self { ReusableMemory { vec: Vec::new() } }
160
161	/// Panics if `size_of::<B>() == 0`
162	pub fn new() -> Self { Self::with_capacity(0) }
163
164	/// Counted in the capacity of `B`.
165	///
166	/// Panics if `size_of::<B>() == 0`
167	pub fn with_capacity(len: usize) -> Self {
168		assert_ne!(mem::size_of::<B>(), 0);
169
170		ReusableMemory { vec: Vec::with_capacity(len) }
171	}
172
173	pub fn needed_capacity_for<T>(&self, count: NonZeroUsize) -> usize {
174		assert_ne!(mem::size_of::<T>(), 0);
175
176		// Add `align - 1` to `needed_bytes` if align of `T` is more than align of `B`.
177		let align_bump =
178			if mem::align_of::<B>() >= mem::align_of::<T>() { 0 } else { mem::align_of::<T>() - 1 };
179
180		// Needed length in bytes.
181		let needed_length = {
182			let needed_bytes = mem::size_of::<T>() * count.get() + align_bump;
183
184			// Needed length divided by the size of `B`, or the number of `B`s needed rounded up.
185			(needed_bytes + mem::size_of::<B>() - 1) / mem::size_of::<B>()
186		};
187
188		needed_length
189	}
190
191	/// Borrows the reusable memory as a different type.
192	///
193	/// This borrow is properly aligned and has at least the requested capacity.
194	///
195	/// Returns an error if `size_of::<T>() == 0`.
196	/// Also returns an error when the pointer could not be aligned properly for `T`.
197	pub fn borrow_mut_as<'mem, T>(
198		&'mem mut self, capacity: NonZeroUsize
199	) -> ReusableMemoryBorrow<'mem, T> {
200		let needed_length = self.needed_capacity_for::<T>(capacity);
201
202		// Reserve so at least `capacity` of `T`s fit, plus possible align offset.
203		self.vec.reserve(needed_length);
204		let memory_ptr = self.vec.as_mut_ptr();
205
206		// Compute the offset we need from the vec pointer to have the proper alignment.
207		let align_offset = memory_ptr.align_offset(mem::align_of::<T>());
208		if align_offset == std::usize::MAX {
209			panic!("Could not align pointer");
210		}
211
212		unsafe {
213			ReusableMemoryBorrow::from_raw_parts(
214				ptr::NonNull::new_unchecked(memory_ptr.add(align_offset) as *mut T),
215				capacity
216			)
217		}
218	}
219}