rkyv/ser/allocator/
alloc.rs1use core::{
2 alloc::Layout,
3 marker::PhantomData,
4 mem::{align_of, size_of, ManuallyDrop},
5 ptr::{slice_from_raw_parts_mut, NonNull},
6};
7
8use crate::{
9 alloc::alloc::{alloc, dealloc, handle_alloc_error},
10 ser::Allocator,
11};
12
13struct Block {
14 next_ptr: NonNull<Block>,
15 next_size: usize,
16}
17
18impl Block {
19 fn alloc(size: usize) -> NonNull<Self> {
20 debug_assert!(size >= size_of::<Self>());
21 let layout = Layout::from_size_align(size, align_of::<Self>()).unwrap();
22 let ptr = unsafe { alloc(layout).cast::<Self>() };
23 let Some(ptr) = NonNull::new(ptr) else {
24 handle_alloc_error(layout)
25 };
26
27 unsafe {
28 ptr.as_ptr().write(Self {
29 next_ptr: ptr,
30 next_size: layout.size(),
31 });
32 }
33
34 ptr
35 }
36
37 unsafe fn dealloc(ptr: NonNull<Self>, size: usize) {
38 let layout = unsafe {
39 Layout::from_size_align_unchecked(size, align_of::<Self>())
40 };
41 unsafe {
42 dealloc(ptr.as_ptr().cast(), layout);
43 }
44 }
45
46 unsafe fn push_next(
51 mut tail_ptr: NonNull<Self>,
52 mut new_ptr: NonNull<Self>,
53 ) {
54 let tail = unsafe { tail_ptr.as_mut() };
55 let new = unsafe { new_ptr.as_mut() };
56
57 debug_assert!(new.next_ptr == new_ptr);
58
59 let head = tail.next_ptr;
60 let head_cap = tail.next_size;
61 tail.next_ptr = new_ptr;
62 tail.next_size = new.next_size;
63 new.next_ptr = head;
64 new.next_size = head_cap;
65 }
66}
67
68pub struct Arena {
73 head_ptr: NonNull<Block>,
74}
75
76unsafe impl Send for Arena {}
78
79impl Drop for Arena {
80 fn drop(&mut self) {
81 self.shrink();
82 let head_size = unsafe { self.head_ptr.as_ref().next_size };
83 unsafe {
84 Block::dealloc(self.head_ptr, head_size);
85 }
86 }
87}
88
89impl Arena {
90 pub const DEFAULT_CAPACITY: usize = 1024;
92
93 pub fn new() -> Self {
95 Self::with_capacity(Self::DEFAULT_CAPACITY)
96 }
97
98 pub fn with_capacity(cap: usize) -> Self {
100 let head_size = cap
101 .checked_add(size_of::<Block>())
102 .and_then(|s| s.checked_next_power_of_two())
103 .expect("Arena capacity overflow");
104 let head_ptr = Block::alloc(head_size);
105 Self { head_ptr }
106 }
107
108 pub fn shrink(&mut self) -> usize {
112 let (mut current_ptr, mut current_size) = {
113 let head = unsafe { self.head_ptr.as_ref() };
114 (head.next_ptr, head.next_size)
115 };
116
117 loop {
118 let current = unsafe { current_ptr.as_mut() };
119
120 if current.next_ptr == current_ptr {
121 break;
123 }
124
125 let next_ptr = current.next_ptr;
126 let next_size = current.next_size;
127
128 if next_ptr == self.head_ptr {
129 unsafe {
131 Block::dealloc(next_ptr, next_size);
132 }
133
134 current.next_ptr = current_ptr;
136 current.next_size = current_size;
137 self.head_ptr = current_ptr;
138
139 break;
140 }
141
142 unsafe {
143 Block::dealloc(current_ptr, current_size);
144 }
145
146 current_ptr = next_ptr;
147 current_size = next_size;
148 }
149
150 current_size - size_of::<Block>()
151 }
152
153 pub fn capacity(&self) -> usize {
155 let mut current_ptr = self.head_ptr;
156 loop {
157 let current = unsafe { current_ptr.as_ref() };
158 if current.next_ptr == self.head_ptr {
159 break current.next_size - size_of::<Block>();
160 }
161 current_ptr = current.next_ptr;
162 }
163 }
164
165 pub fn acquire(&mut self) -> ArenaHandle<'_> {
169 self.shrink();
170
171 ArenaHandle {
172 tail_ptr: self.head_ptr,
173 tail_size: unsafe { self.head_ptr.as_ref().next_size },
174 used: size_of::<Block>(),
175 _phantom: PhantomData,
176 }
177 }
178
179 pub fn into_raw(self) -> NonNull<()> {
181 let this = ManuallyDrop::new(self);
182 this.head_ptr.cast()
183 }
184
185 pub unsafe fn from_raw(raw: NonNull<()>) -> Self {
193 Self {
194 head_ptr: raw.cast(),
195 }
196 }
197}
198
199impl Default for Arena {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205pub struct ArenaHandle<'a> {
207 tail_ptr: NonNull<Block>,
208 tail_size: usize,
209 used: usize,
210 _phantom: PhantomData<&'a mut Arena>,
211}
212
213unsafe impl Send for ArenaHandle<'_> {}
215
216unsafe impl<E> Allocator<E> for ArenaHandle<'_> {
217 unsafe fn push_alloc(
218 &mut self,
219 layout: Layout,
220 ) -> Result<NonNull<[u8]>, E> {
221 let pos = self.tail_ptr.as_ptr() as usize + self.used;
222 let pad = 0usize.wrapping_sub(pos) % layout.align();
223 if pad + layout.size() <= self.tail_size - self.used {
224 self.used += pad;
225 } else {
226 let size = usize::max(
228 2 * self.tail_size,
229 (size_of::<Block>() + layout.size() + layout.align())
230 .next_power_of_two(),
231 );
232 let next = Block::alloc(size);
233 unsafe {
234 Block::push_next(self.tail_ptr, next);
235 }
236 self.tail_ptr = next;
237 self.tail_size = size;
238 let pos = self.tail_ptr.as_ptr() as usize + size_of::<Block>();
239 let pad = 0usize.wrapping_sub(pos) % layout.align();
240 self.used = size_of::<Block>() + pad;
241 }
242
243 let ptr = unsafe { self.tail_ptr.as_ptr().cast::<u8>().add(self.used) };
246 let slice_ptr = slice_from_raw_parts_mut(ptr, layout.size());
247 let result = unsafe { NonNull::new_unchecked(slice_ptr) };
250 self.used += layout.size();
251 Ok(result)
252 }
253
254 unsafe fn pop_alloc(
255 &mut self,
256 ptr: NonNull<u8>,
257 _: Layout,
258 ) -> Result<(), E> {
259 let start = self.tail_ptr.as_ptr() as usize;
262 let end = start + self.tail_size;
263 let pos = ptr.as_ptr() as usize;
264 if (start..end).contains(&pos) {
265 self.used = pos - start;
266 }
267
268 Ok(())
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use core::alloc::Layout;
275
276 use rancor::{Panic, ResultExt};
277
278 use crate::{
279 alloc::{string::ToString, vec},
280 api::high::to_bytes_in_with_alloc,
281 ser::{allocator::Arena, Allocator},
282 util::AlignedVec,
283 };
284
285 #[test]
286 fn reuse_arena() {
287 let mut arena = Arena::with_capacity(2);
288
289 let value = vec![
290 "hello".to_string(),
291 "world".to_string(),
292 "foo".to_string(),
293 "bar".to_string(),
294 "baz".to_string(),
295 ];
296
297 for _ in 0..10 {
298 to_bytes_in_with_alloc::<_, _, Panic>(
299 &value,
300 AlignedVec::<16>::new(),
301 arena.acquire(),
302 )
303 .unwrap();
304 }
305 }
306
307 #[test]
308 fn pop_non_tail() {
309 let mut arena = Arena::new();
310 let mut handle = arena.acquire();
311
312 let layout =
313 Layout::from_size_align(Arena::DEFAULT_CAPACITY, 1).unwrap();
314
315 unsafe {
316 let a =
317 Allocator::<Panic>::push_alloc(&mut handle, layout).always_ok();
318 let b =
319 Allocator::<Panic>::push_alloc(&mut handle, layout).always_ok();
320 Allocator::<Panic>::pop_alloc(&mut handle, b.cast(), layout)
321 .always_ok();
322 Allocator::<Panic>::pop_alloc(&mut handle, a.cast(), layout)
323 .always_ok();
324 }
325 }
326}