1use std::{iter::repeat_with, ops::Range, sync::mpsc, time::Instant};
2
3use generational_arena::{Arena, Index};
4
5use crate::{
6 buddy::{self, is_pow_of_two, Block, BlockState},
7 pretty_print::prettify,
8};
9
10#[derive(Debug)]
12pub struct Allocation {
13 index: Index,
14 range: Range<usize>,
15 to_remove: mpsc::Sender<Index>,
16}
17
18impl Allocation {
19 pub fn range(&self) -> Range<usize> {
20 self.range.clone()
21 }
22}
23
24impl Drop for Allocation {
25 fn drop(&mut self) {
26 let _ = self.to_remove.send(self.index);
29 }
30}
31
32pub struct BuddyBookkeeping {
33 pub(crate) blocks: Arena<Block>,
34 pub(crate) root: Index,
35 to_remove_sender: mpsc::Sender<Index>,
36 to_remove_receiver: mpsc::Receiver<Index>,
37 min_block_size: usize,
38 max_block_size: usize,
39}
40
41impl BuddyBookkeeping {
42 pub fn new(size: usize, min_block_size: usize, max_block_size: usize) -> BuddyBookkeeping {
43 assert!(is_pow_of_two(size));
44 assert!(max_block_size <= size);
45 assert!(min_block_size <= max_block_size);
46
47 let mut new_arena = Arena::new();
48 let root = new_arena.insert(Block {
49 range: 0..size,
50 state: BlockState::Available,
51 });
52
53 let (sender, receiver) = mpsc::channel();
54
55 BuddyBookkeeping {
56 to_remove_sender: sender,
57 to_remove_receiver: receiver,
58 blocks: new_arena,
59 root: root,
60 min_block_size,
61 max_block_size,
62 }
63 }
64
65 pub fn alloc(&mut self, count: usize) -> Option<Allocation> {
66 let best_size = if 2_u32.pow(count.ilog2()) as usize == count {
67 count
68 } else {
69 2_u32.pow(count.ilog2() + 1) as usize
70 }
71 .max(self.min_block_size)
72 .min(self.max_block_size);
73
74 if best_size < count {
75 return None;
76 }
77
78 buddy::alloc(&mut self.blocks, self.root, best_size).map(|x| Allocation {
79 index: x,
80 range: (self.blocks[x].range.start)..(self.blocks[x].range.start + count),
81 to_remove: self.to_remove_sender.clone(),
82 })
83 }
84
85 pub fn tidy(&mut self) {
86 while let Ok(index) = self.to_remove_receiver.try_recv() {
87 buddy::dealloc(&mut self.blocks, index);
88 }
89
90 buddy::tidy(&mut self.blocks, self.root);
91 }
92
93 pub fn tidy_gas(&mut self, gas: usize) {
94 let mut gas = gas;
95
96 while let Ok(index) = self.to_remove_receiver.try_recv() {
97 if gas == 0 {
98 return;
99 }
100
101 buddy::dealloc(&mut self.blocks, index);
102
103 gas -= 1;
104 }
105
106 buddy::tidy_gas(&mut self.blocks, self.root, &mut gas);
107 }
108
109 pub fn tidy_timed(&mut self, deadline: Instant) {
110 while let Ok(index) = self.to_remove_receiver.try_recv() {
111 if Instant::now() >= deadline {
112 return;
113 }
114
115 buddy::dealloc(&mut self.blocks, index);
116 }
117
118 buddy::tidy_timed(&mut self.blocks, self.root, deadline);
119 }
120}
121
122pub struct BuddyArena<T> {
123 elements: Box<[T]>,
124 bookkeeping: BuddyBookkeeping,
125}
126
127impl<T> BuddyArena<T> {
128 pub fn new(size: usize, min_block_size: usize, max_block_size: usize) -> BuddyArena<T>
129 where
130 T: Default,
131 {
132 let elements_vec: Vec<T> = repeat_with(|| T::default()).take(size).collect();
133
134 BuddyArena {
135 elements: elements_vec.into(),
136 bookkeeping: BuddyBookkeeping::new(size, min_block_size, max_block_size),
137 }
138 }
139
140 pub fn bookkeeping(&self) -> &BuddyBookkeeping {
141 &self.bookkeeping
142 }
143
144 pub fn view(&self, a: &Allocation) -> &[T] {
145 &self.elements[a.range()]
146 }
147
148 pub fn view_mut(&mut self, a: &Allocation) -> &mut [T] {
149 &mut self.elements[a.range()]
150 }
151
152 pub fn alloc(&mut self, count: usize) -> Option<Allocation> {
153 self.bookkeeping.alloc(count)
154 }
155
156 pub fn tidy(&mut self) {
157 self.bookkeeping.tidy();
158 }
159
160 pub fn tidy_gas(&mut self, gas: usize) {
161 self.bookkeeping.tidy_gas(gas);
162 }
163
164 pub fn tidy_timed(&mut self, deadline: Instant) {
165 self.bookkeeping.tidy_timed(deadline);
166 }
167}
168
169#[test]
170fn test() {
171 let mut arena: BuddyArena<u8> = BuddyArena::new(2048, 8, 256);
172
173 let a1 = arena.alloc(64).unwrap();
174 let a2 = arena.alloc(24).unwrap();
175 let a3 = arena.alloc(2).unwrap();
176 let a4 = arena.alloc(7).unwrap();
177 let a5 = arena.alloc(31).unwrap();
178 let a6 = arena.alloc(60).unwrap();
179
180 println!("{:#?}", prettify(arena.bookkeeping()));
181
182 let string = "foobar";
183
184 let a = arena.alloc(string.bytes().len()).unwrap();
185 let view = arena.view_mut(&a);
186
187 view.copy_from_slice(string.as_bytes());
188
189 let str_view = std::str::from_utf8(view).unwrap();
190
191 dbg!(str_view);
192}