pb_arena/
lib.rs

1//! ID based arena for graph operations.
2
3use std::{alloc::{alloc, dealloc, Layout}, cell::UnsafeCell, ops::{Deref, DerefMut}, ptr::NonNull};
4
5pub mod sync;
6
7pub struct ArenaRefMut<'a, T: Sized> {
8    wr_counter: &'a UnsafeCell<i32>,
9    data: &'a UnsafeCell<T> 
10}
11
12impl<T: Sized> Deref for ArenaRefMut<'_, T> {
13    type Target = T;
14
15    fn deref(&self) -> &Self::Target {
16        unsafe {
17            self.data.get().as_ref().unwrap()
18        }
19    }
20}
21
22impl<T: Sized> DerefMut for ArenaRefMut<'_, T> {
23    fn deref_mut(&mut self) -> &mut Self::Target {
24        unsafe {
25            self.data.get().as_mut().unwrap()
26        }
27    }
28}
29
30
31impl<T: Sized> Drop for ArenaRefMut<'_, T> {
32    fn drop(&mut self) {
33        unsafe {
34            *self.wr_counter.get() += 1;
35        }
36    }
37}
38
39
40pub struct ArenaRef<'a, T: Sized> {
41    wr_counter: &'a UnsafeCell<i32>,
42    data: &'a UnsafeCell<T>
43}
44
45impl<T: Sized> Deref for ArenaRef<'_, T> {
46    type Target = T;
47
48    fn deref(&self) -> &Self::Target {
49        unsafe {
50            self.data.get().as_ref().unwrap()
51        }
52    }
53}
54
55impl<T: Sized> Drop for ArenaRef<'_, T> {
56    fn drop(&mut self) {
57        unsafe {
58            *self.wr_counter.get() -= 1;
59        }
60    }
61}
62
63struct Entry<T: Sized> {
64    wr_counter: UnsafeCell<i32>,
65    data: UnsafeCell<T>
66}
67
68impl<T: Sized> Entry<T> {
69    pub fn new(data: T) -> Self {
70        Self {
71            wr_counter: UnsafeCell::new(0),
72            data: UnsafeCell::new(data)
73        }
74    }
75    
76
77    pub fn borrow(&self) -> Option<ArenaRef<'_, T>> {
78        unsafe {
79            if *(self.wr_counter.get()) < 0 {
80                return None
81            }
82
83            *self.wr_counter.get() += 1;
84        }
85
86        return Some(ArenaRef {
87            wr_counter: &self.wr_counter,
88            data: &self.data
89        })
90    }
91
92    pub fn borrow_mut(&self) -> Option<ArenaRefMut<'_, T>> {
93        unsafe {
94            if *(self.wr_counter.get()) != 0 {
95                return None
96            }
97
98            *self.wr_counter.get() -= 1;
99        }
100
101        Some(ArenaRefMut { wr_counter: &self.wr_counter, data: &self.data })
102    }
103}
104
105struct Bucket<T: Sized> {
106    head: NonNull<Entry<T>>,
107    tail: NonNull<Entry<T>>,
108    last: Option<NonNull<Entry<T>>>,
109    layout: Layout
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
113struct ArenaCellId(usize);
114
115impl From<usize> for ArenaCellId {
116    fn from(value: usize) -> Self {
117        Self(value)
118    }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
122struct ArenaBucketId(usize);
123
124impl From<usize> for ArenaBucketId {
125    fn from(value: usize) -> Self {
126        Self(value)
127    }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd)]
131pub struct ArenaId {
132    block_id: ArenaBucketId,
133    cell_id: ArenaCellId
134}
135
136impl Ord for ArenaId {
137    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
138        if self.block_id != other.block_id {
139            return self.block_id.cmp(&other.block_id);
140        }
141
142        return self.cell_id.cmp(&other.cell_id);
143    }
144}
145
146impl ArenaId {
147    fn new<ABI, ACI>(block_id: ABI, cell_id: ACI) -> Self 
148    where ArenaBucketId: From<ABI>, ArenaCellId: From<ACI>
149    {
150        Self {block_id: block_id.into(), cell_id: cell_id.into()}
151    }
152    
153    fn next_block(&self) -> Self {
154        Self::new(self.block_id.0 + 1, 0)
155    }
156}
157
158impl<T: Sized> Drop for Bucket<T> {
159    fn drop(&mut self) {
160        unsafe {
161            if let Some(last) = self.last {
162                let mut cursor = self.head;
163                
164                while cursor <= last {
165                    cursor.drop_in_place();
166                    cursor = cursor.add(1);
167                }
168
169            }
170
171            dealloc(self.head.cast::<u8>().as_ptr(), self.layout);
172        }
173    }
174}
175
176impl<T: Sized> Bucket<T> {
177    fn new(size: usize) -> Self {
178        unsafe {
179            let layout = Layout::array::<Entry<T>>(size).unwrap();
180            let head = NonNull::new(alloc(layout) as *mut Entry<T>).unwrap();
181            let tail = head.add(size - 1);
182            let last = None;
183            Self {head, tail, last, layout}
184        }
185    }
186
187    fn first_id(&self) -> Option<ArenaCellId> {
188        if self.len() == 0 {
189            return None
190        }
191
192        return Some(ArenaCellId(0))
193    }
194
195    fn next_id(&self, id: ArenaCellId) -> Option<ArenaCellId> {
196        self.last.map(|last| {
197            unsafe {
198                let cursor = self.head.add(id.0 + 1);
199                (cursor <= last).then(|| ArenaCellId(id.0 + 1))
200            }
201        }).flatten()
202    }
203
204    fn alloc(&mut self, value: T) -> ArenaCellId {
205        if self.is_full() {
206            panic!("block is full")
207        }        
208
209        unsafe {
210            let mut last = self.last
211                .map(|last| last.add(1))
212                .unwrap_or_else(|| self.head);
213
214            *last.as_mut() = Entry::new(value);
215            self.last = Some(last);
216            return ArenaCellId(self.len() - 1)
217        }
218    }
219
220    fn len(&self) -> usize {
221        self.last.map(|last| {
222            unsafe {
223                let offset: usize = last.offset_from(self.head).try_into().unwrap();
224                offset + 1
225            }     
226        }).unwrap_or_default()       
227    }
228
229    // Returns the cell by its id.
230    fn get_cell(&self, cell_id: ArenaCellId) -> Option<&Entry<T>> {
231        self.last
232        .map(|last| {
233            unsafe {
234                let ptr = self.head.add(cell_id.0);
235                
236                (ptr <= last && ptr >= self.head)
237                .then(|| ptr.as_ref())
238            }
239        }).flatten()
240    }
241
242    pub fn is_full(&self) -> bool {
243        self.last
244            .map(|last| last >= self.tail)
245            .unwrap_or_else(|| false)
246    }
247}
248
249
250pub struct ArenaIter<'a, T: Sized> {
251    arena: &'a Arena<T>,
252    id: Option<ArenaId>
253}
254
255impl<'a, T: Sized> ArenaIter<'a, T> {
256    pub fn new(arena: &'a Arena<T>) -> Self {
257        Self {
258            arena,
259            id: None
260        }
261    }
262}
263
264impl<'a, T: Sized> Iterator for ArenaIter<'a, T> {
265    type Item = ArenaId;
266
267    fn next(&mut self) -> Option<Self::Item> {
268        if self.id.is_none() {
269            self.id = self.arena.first_id();
270            return self.id
271        }
272
273        let id = self.id.unwrap();
274        self.id = self.arena.next_id(id);
275        return self.id
276    }
277}
278
279//
280// An id-based arena dedicated to graph operations.
281// The arena allows to borrow/mut borrow each entry independently. 
282// 
283// # Examples
284// ```
285// let mut arena = Arena::<u32>::new(30);
286// let id_1 = arena.alloc(100);
287// let entry = arena.borrow_mut(id_1);
288// *entry = 20;
289// let id_2 = arena.alloc(200);
290// let entry_2 = arena.borrow_mut(id_2);
291// *entry_2 = 30;
292// ```
293//
294pub struct Arena<T: Sized> {
295    blocks: Vec<Bucket<T>>,
296    block_size: usize
297}
298
299impl<T: Sized> Arena<T> {
300    pub fn new(block_size: usize) -> Self {
301        Self {
302            blocks: Vec::default(),
303            block_size
304        }
305    }
306
307    pub fn iter(&self) -> ArenaIter<'_, T> {
308        ArenaIter::new(self)
309    }
310
311    pub fn borrow(&self, id: ArenaId) -> Option<ArenaRef<'_, T>> {
312        self.get_cell(id)
313        .map(Entry::borrow)
314        .flatten()
315    }
316    
317    pub fn borrow_mut(&self, id: ArenaId) -> Option<ArenaRefMut<'_, T>> {
318        self.get_cell(id).map(Entry::borrow_mut).flatten()
319    }
320
321    pub fn alloc(&mut self, data: T) -> ArenaId {
322        if let Some((block_id, block)) = self.find_free_block() {
323            let cell_id: ArenaCellId = block.alloc(data);
324            return ArenaId {block_id, cell_id}
325        }
326
327        let mut block = Bucket::<T>::new(self.block_size);
328        let cell_id = block.alloc(data);
329        self.blocks.push(block);
330        let block_id = ArenaBucketId(self.blocks.len() - 1);
331
332        return ArenaId { block_id, cell_id }
333    }
334
335    pub fn len(&self) -> usize {
336        self.blocks
337            .iter()
338            .map(|block| block.len())
339            .reduce(std::ops::Add::add)
340            .unwrap_or_default()
341    }
342   
343    fn get_cell(&self, id: ArenaId) -> Option<&Entry<T>> {
344        self.blocks
345        .get(id.block_id.0)
346        .map(|block| block.get_cell(id.cell_id))
347        .flatten()
348    }
349    
350    fn find_free_block(&mut self) -> Option<(ArenaBucketId, &mut Bucket<T>)> {
351        self.blocks
352            .iter_mut()
353            .enumerate()
354            .find(|(_, block)| !block.is_full())
355            .map(|(block_id, block)| (ArenaBucketId(block_id), block))
356    }
357
358    fn first_id(&self) -> Option<ArenaId> {
359        self.blocks
360            .get(0)
361            .map(|block| 
362                block.first_id().map(|cell_id| ArenaId::new(0, cell_id))
363            )
364            .flatten()
365    }
366
367    fn next_block_id(&self, id: ArenaId) -> Option<ArenaId> {
368        self.blocks
369            .get(id.block_id.0 + 1)
370            .map(|block| 
371                block.first_id()
372                    .map(|cell_id| ArenaId::new(
373                        id.block_id.0 + 1,
374                        cell_id
375                    ))
376            )
377            .flatten()
378    }
379
380    fn next_id(&self, id: ArenaId) -> Option<ArenaId> {
381        self.blocks
382        .get(id.block_id.0)
383        .map(|block| {
384            block
385            .next_id(id.cell_id)
386            .map(|cell_id| ArenaId::new(id.block_id, cell_id))
387        })
388        .flatten()
389        .or_else(|| self.next_block_id(id))
390        
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use crate::{Arena, ArenaId};
397
398    #[test]
399    fn test_alloc() {
400        let mut arena = Arena::<u32>::new(1);
401        let id = arena.alloc(10);
402
403        assert_eq!(arena.len(), 1);
404        assert!(arena.get_cell(ArenaId::new(0, 0)).is_some());
405
406        let cell = arena.get_cell(id).unwrap();
407        assert_eq!(*cell.borrow().unwrap(), 10);
408    }
409
410    #[test]
411    fn test_must_create_another_block_if_previous_is_full() {
412        let mut arena = Arena::<u32>::new(2);
413        arena.alloc(1);
414        arena.alloc(2);
415        let id = arena.alloc(3);
416        arena.alloc(4);
417
418        assert_eq!(arena.len(), 4);
419
420        let cell = arena.get_cell(id).unwrap();
421        assert_eq!(*cell.borrow().unwrap(), 3);
422    }
423
424    #[test]
425    fn test_iter() {
426        let mut arena = Arena::<u32>::new(2);
427        arena.alloc(1);
428        arena.alloc(2);
429        arena.alloc(3);
430        arena.alloc(4);  
431
432        assert_eq!(arena.next_block_id(ArenaId::new(0,1)), Some(ArenaId::new(1,0)));
433        assert_eq!(arena.next_id(ArenaId::new(0,1)), Some(ArenaId::new(1,0)));
434
435        let expected_ids = vec![
436            ArenaId::new(0, 0),
437            ArenaId::new(0, 1),
438            ArenaId::new(1, 0),
439            ArenaId::new(1, 1)
440        ];
441
442        let ids = arena.iter().collect::<Vec<_>>();
443        assert_eq!(expected_ids, ids)
444    }
445
446    #[test]
447    fn test_can_borrow_multiple_times() {
448        let mut arena = Arena::<u32>::new(1);
449        let id = arena.alloc(10);
450
451        let borrow_1 = arena.borrow(id);
452        let borrow_2 = arena.borrow(id);
453
454        assert!(borrow_1.is_some());
455        assert!(borrow_2.is_some())
456    }
457
458    #[test]
459    fn test_cannot_mut_borrow_if_already_borrowed() {
460        let mut arena = Arena::<u32>::new(1);
461        let id = arena.alloc(10);
462
463        let _ref = arena.borrow(id).unwrap();
464        assert_eq!(arena.borrow_mut(id).is_none(), true); // cannot mut borrow multiple times.
465    }
466
467    #[test]
468    fn test_cannot_mut_borrow_multiple_times() {
469        let mut arena = Arena::<u32>::new(1);
470        let id = arena.alloc(10);
471
472        let _mut_ref = arena.borrow_mut(id).unwrap();
473        assert_eq!(arena.borrow_mut(id).is_none(), true); // cannot mut borrow multiple times.
474    }
475
476    #[test]
477    fn test_can_mut_borrow_two_different_entries() {
478        let mut arena = Arena::<u32>::new(2);
479        let id_1 = arena.alloc(10);
480        let id_2 = arena.alloc(20);
481
482        let ref_1 = arena.borrow_mut(id_1).unwrap();
483        let ref_2 = arena.borrow_mut(id_2).unwrap();
484
485        assert_eq!(*ref_1, 10);
486        assert_eq!(*ref_2, 20);
487    }
488
489    pub struct DropCanary<'a> {
490        flag: Option<&'a mut bool>
491    }
492
493    impl<'a> Drop for DropCanary<'a> {
494        fn drop(&mut self) {
495            if let Some(f) = &mut self.flag {
496                **f = true;
497            }
498        }
499    }
500
501    #[test]
502    fn test_drop() {
503        let mut flag = false;
504        let mut arena = Arena::<DropCanary>::new(2);
505
506        let id = arena.alloc(DropCanary { flag: Some(&mut flag)});
507
508        assert_eq!(arena.borrow(id).unwrap().flag, Some(&mut false));
509        drop(arena);
510
511        assert_eq!(flag, true);
512    }
513}