vm_allocator/
id_allocator.rs1use crate::{Error, Result};
11use alloc::collections::BTreeSet;
12
13#[derive(Debug, Clone)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct IdAllocator {
23 range_base: u32,
25 next_id: Option<u32>,
27 range_end: u32,
29 freed_ids: BTreeSet<u32>,
31}
32
33impl IdAllocator {
34 pub fn new(range_base: u32, range_end: u32) -> Result<Self> {
38 if range_end < range_base {
39 return Err(Error::InvalidRange(range_base.into(), range_end.into()));
40 }
41 Ok(IdAllocator {
42 range_base,
43 next_id: Some(range_base),
44 range_end,
45 freed_ids: BTreeSet::<u32>::new(),
46 })
47 }
48
49 fn id_in_range(&self, id: u32) -> bool {
50 self.range_base <= id && id <= self.range_end
52 }
53
54 pub fn allocate_id(&mut self) -> Result<u32> {
58 if !self.freed_ids.is_empty() {
61 let ret_value = *self.freed_ids.iter().next().unwrap();
62 self.freed_ids.remove(&ret_value);
63 return Ok(ret_value);
64 }
65 if let Some(next_id) = self.next_id {
67 if next_id > self.range_end {
68 return Err(Error::ResourceNotAvailable);
69 }
70 self.next_id = next_id.checked_add(1);
74 return Ok(next_id);
75 }
76 Err(Error::Overflow)
77 }
78
79 pub fn is_allocated(&self, id: u32) -> bool {
86 if !self.id_in_range(id) || self.freed_ids.contains(&id) {
87 return false;
88 }
89 match self.next_id {
93 Some(next) => id < next,
94 None => true,
95 }
96 }
97
98 pub fn free_id(&mut self, id: u32) -> Result<u32> {
100 if !self.id_in_range(id) {
103 return Err(Error::OutOfRange(id));
104 }
105 if let Some(next_id) = self.next_id {
106 if next_id <= id {
107 return Err(Error::NeverAllocated(id));
108 }
109 }
110
111 self.freed_ids
114 .insert(id)
115 .then_some(id)
116 .ok_or(Error::AlreadyReleased(id))
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use crate::id_allocator::IdAllocator;
123 use crate::Error;
124
125 #[test]
126 fn test_slot_id_allocation() {
127 let faulty_allocator = IdAllocator::new(23, 5);
128 assert_eq!(faulty_allocator.unwrap_err(), Error::InvalidRange(23, 5));
129 let mut legacy_irq_allocator = IdAllocator::new(5, 23).unwrap();
130 assert_eq!(legacy_irq_allocator.range_base, 5);
131 assert_eq!(legacy_irq_allocator.range_end, 23);
132
133 let id = legacy_irq_allocator.allocate_id().unwrap();
134 assert_eq!(id, 5);
135 assert_eq!(legacy_irq_allocator.next_id.unwrap(), 6);
136
137 for _ in 1..19 {
138 assert!(legacy_irq_allocator.allocate_id().is_ok());
139 }
140
141 assert_eq!(
142 legacy_irq_allocator.allocate_id().unwrap_err(),
143 Error::ResourceNotAvailable
144 );
145 }
146
147 #[test]
148 fn test_u32_overflow() {
149 let mut allocator = IdAllocator::new(u32::MAX - 1, u32::MAX).unwrap();
150 assert_eq!(allocator.allocate_id().unwrap(), u32::MAX - 1);
151 assert_eq!(allocator.allocate_id().unwrap(), u32::MAX);
152 let res = allocator.allocate_id();
153 assert!(res.is_err());
154 assert_eq!(res.unwrap_err(), Error::Overflow);
155 }
156
157 #[test]
158 fn test_slot_id_free() {
159 let mut legacy_irq_allocator = IdAllocator::new(5, 23).unwrap();
160 assert_eq!(
161 legacy_irq_allocator.free_id(3).unwrap_err(),
162 Error::OutOfRange(3)
163 );
164 assert_eq!(legacy_irq_allocator.freed_ids.len(), 0);
165
166 for _ in 1..10 {
167 let _id = legacy_irq_allocator.allocate_id().unwrap();
168 }
169
170 let irq = 10;
171 legacy_irq_allocator.free_id(irq).unwrap();
172 assert!(legacy_irq_allocator.freed_ids.contains(&irq));
173 assert_eq!(
174 legacy_irq_allocator.free_id(10).unwrap_err(),
175 Error::AlreadyReleased(10)
176 );
177 let irq = 9;
178 legacy_irq_allocator.free_id(irq).unwrap();
179 assert_eq!(legacy_irq_allocator.freed_ids.len(), 2);
180 assert_eq!(*legacy_irq_allocator.freed_ids.iter().next().unwrap(), 9);
181
182 let irq = legacy_irq_allocator.allocate_id().unwrap();
183 assert_eq!(irq, 9);
184 assert!(!legacy_irq_allocator.freed_ids.contains(&irq));
185 assert_eq!(legacy_irq_allocator.freed_ids.len(), 1);
186 assert_eq!(
187 legacy_irq_allocator.free_id(21).unwrap_err(),
188 Error::NeverAllocated(21)
189 );
190 }
191
192 #[test]
193 fn test_free_id_never_allocated_boundary() {
194 let mut allocator = IdAllocator::new(5, 23).unwrap();
195
196 assert_eq!(allocator.next_id.unwrap(), 5);
197 assert_eq!(allocator.free_id(5).unwrap_err(), Error::NeverAllocated(5));
198 assert_eq!(allocator.freed_ids.len(), 0);
199
200 for _ in 0..3 {
201 allocator.allocate_id().unwrap();
202 }
203 assert_eq!(allocator.next_id.unwrap(), 8);
204 assert_eq!(allocator.free_id(8).unwrap_err(), Error::NeverAllocated(8));
205 assert_eq!(allocator.freed_ids.len(), 0);
206 }
207
208 #[test]
209 fn test_is_allocated() {
210 let mut allocator = IdAllocator::new(5, 23).unwrap();
211
212 assert!(!allocator.is_allocated(4));
213 assert!(!allocator.is_allocated(24));
214
215 assert!(!allocator.is_allocated(10));
216
217 let id = allocator.allocate_id().unwrap();
218 assert_eq!(id, 5);
219 assert!(allocator.is_allocated(5));
220 assert!(!allocator.is_allocated(6));
221
222 allocator.free_id(5).unwrap();
223 assert!(!allocator.is_allocated(5));
224
225 let id = allocator.allocate_id().unwrap();
226 assert_eq!(id, 5);
227 assert!(allocator.is_allocated(5));
228 }
229
230 #[test]
231 fn test_is_allocated_full_range() {
232 let mut allocator = IdAllocator::new(u32::MAX - 1, u32::MAX).unwrap();
233
234 assert!(!allocator.is_allocated(u32::MAX - 1));
235 assert!(!allocator.is_allocated(u32::MAX));
236
237 assert_eq!(allocator.allocate_id().unwrap(), u32::MAX - 1);
238 assert!(allocator.is_allocated(u32::MAX - 1));
239 assert!(!allocator.is_allocated(u32::MAX));
240
241 assert_eq!(allocator.allocate_id().unwrap(), u32::MAX);
242 assert!(allocator.next_id.is_none());
243 assert!(allocator.is_allocated(u32::MAX));
244
245 allocator.free_id(u32::MAX).unwrap();
246 assert!(!allocator.is_allocated(u32::MAX));
247 assert!(allocator.is_allocated(u32::MAX - 1));
248 }
249
250 #[test]
251 fn test_is_allocated_with_freed() {
252 let mut allocator = IdAllocator::new(5, 23).unwrap();
253 allocator.allocate_id().unwrap();
254 allocator.allocate_id().unwrap();
255
256 assert_eq!(allocator.next_id, Some(7));
257
258 assert!(allocator.is_allocated(6));
259 assert!(!allocator.is_allocated(7));
260
261 allocator.free_id(5).unwrap();
262 assert!(!allocator.is_allocated(5));
263 assert!(allocator.is_allocated(6));
264 assert!(!allocator.is_allocated(7));
265 }
266
267 #[test]
268 fn test_id_sanity_checks() {
269 let legacy_irq_allocator = IdAllocator::new(5, 23).unwrap();
270
271 assert!(!legacy_irq_allocator.id_in_range(4));
272 assert!(legacy_irq_allocator.id_in_range(6));
273 assert!(!legacy_irq_allocator.id_in_range(25));
274 }
275}