Skip to main content

range_alloc_arceos/
lib.rs

1//! A simple, fast range allocator for managing contiguous ranges of resources.
2//!
3//! This crate provides a [`RangeAllocator`] that efficiently allocates and frees
4//! contiguous ranges from an initial range. It uses a best-fit allocation strategy
5//! to minimize memory fragmentation.
6//!
7//! # Example
8//!
9//! ```
10//! use range_alloc_arceos::RangeAllocator;
11//!
12//! let mut allocator = RangeAllocator::new(0..100);
13//!
14//! // Allocate a range of length 10
15//! let range = allocator.allocate_range(10).unwrap();
16//! assert_eq!(range, 0..10);
17//!
18//! // Free the range when done
19//! allocator.free_range(range);
20//! ```
21
22#![no_std]
23
24extern crate alloc;
25
26use alloc::vec;
27use alloc::vec::Vec;
28use core::{
29    fmt::Debug,
30    iter::Sum,
31    ops::{Add, AddAssign, Range, Sub},
32};
33
34/// A range allocator that manages allocation and deallocation of contiguous ranges.
35///
36/// The allocator starts with an initial range and maintains a list of free ranges.
37/// It uses a best-fit allocation strategy to minimize fragmentation when allocating
38/// new ranges.
39///
40/// # Type Parameters
41///
42/// * `T` - The type used for range bounds. Must support arithmetic operations and ordering.
43#[derive(Debug)]
44pub struct RangeAllocator<T> {
45    /// The range this allocator covers.
46    initial_range: Range<T>,
47    /// A Vec of ranges in this heap which are unused.
48    /// Must be ordered with ascending range start to permit short circuiting allocation.
49    /// No two ranges in this vec may overlap.
50    free_ranges: Vec<Range<T>>,
51}
52
53/// Error type returned when a range allocation fails.
54///
55/// This error indicates that there is not enough contiguous space available
56/// to satisfy the allocation request, although there may be enough total free
57/// space if it were defragmented.
58#[derive(Clone, Debug, PartialEq)]
59pub struct RangeAllocationError<T> {
60    /// The total length of all free ranges combined.
61    ///
62    /// This value represents how much space would be available if all fragmented
63    /// free ranges could be combined into one contiguous range.
64    pub fragmented_free_length: T,
65}
66
67impl<T> RangeAllocator<T>
68where
69    T: Clone + Copy + Add<Output = T> + AddAssign + Sub<Output = T> + Eq + PartialOrd + Debug,
70{
71    /// Creates a new range allocator with the specified initial range.
72    ///
73    /// The entire initial range is marked as free and available for allocation.
74    ///
75    /// # Arguments
76    ///
77    /// * `range` - The initial range that this allocator will manage.
78    ///
79    /// # Example
80    ///
81    /// ```
82    /// use range_alloc_arceos::RangeAllocator;
83    ///
84    /// let allocator = RangeAllocator::new(0..1024);
85    /// ```
86    pub fn new(range: Range<T>) -> Self {
87        RangeAllocator {
88            initial_range: range.clone(),
89            free_ranges: vec![range],
90        }
91    }
92
93    /// Returns a reference to the initial range managed by this allocator.
94    ///
95    /// This is the range that was provided when the allocator was created,
96    /// or the expanded range if [`grow_to`](Self::grow_to) was called.
97    pub fn initial_range(&self) -> &Range<T> {
98        &self.initial_range
99    }
100
101    /// Grows the allocator's range to a new end point.
102    ///
103    /// This extends the upper bound of the initial range and makes the new space
104    /// available for allocation. If the last free range ends at the current upper
105    /// bound, it is extended; otherwise, a new free range is added.
106    ///
107    /// # Arguments
108    ///
109    /// * `new_end` - The new end point for the range (must be greater than the current end).
110    pub fn grow_to(&mut self, new_end: T) {
111        let initial_range_end = self.initial_range.end;
112        if let Some(last_range) = self
113            .free_ranges
114            .last_mut()
115            .filter(|last_range| last_range.end == initial_range_end)
116        {
117            last_range.end = new_end;
118        } else {
119            self.free_ranges.push(self.initial_range.end..new_end);
120        }
121
122        self.initial_range.end = new_end;
123    }
124
125    /// Allocates a contiguous range of the specified length.
126    ///
127    /// This method uses a best-fit allocation strategy to find the smallest free range
128    /// that can satisfy the request, minimizing fragmentation. If no single contiguous
129    /// range is large enough, it returns an error with information about the total
130    /// fragmented free space.
131    ///
132    /// # Arguments
133    ///
134    /// * `length` - The length of the range to allocate.
135    ///
136    /// # Returns
137    ///
138    /// * `Ok(Range<T>)` - The allocated range if successful.
139    /// * `Err(RangeAllocationError<T>)` - If allocation fails, containing information
140    ///   about the total fragmented free space available.
141    ///
142    /// # Example
143    ///
144    /// ```
145    /// use range_alloc_arceos::RangeAllocator;
146    ///
147    /// let mut allocator = RangeAllocator::new(0..100);
148    /// let range = allocator.allocate_range(20).unwrap();
149    /// assert_eq!(range, 0..20);
150    /// ```
151    pub fn allocate_range(&mut self, length: T) -> Result<Range<T>, RangeAllocationError<T>> {
152        assert_ne!(length + length, length);
153        let mut best_fit: Option<(usize, Range<T>)> = None;
154
155        // This is actually correct. With the trait bound as it is, we have
156        // no way to summon a value of 0 directly, so we make one by subtracting
157        // something from itself. Once the trait bound can be changed, this can
158        // be fixed.
159        #[allow(clippy::eq_op)]
160        let mut fragmented_free_length = length - length;
161        for (index, range) in self.free_ranges.iter().cloned().enumerate() {
162            let range_length = range.end - range.start;
163            fragmented_free_length += range_length;
164            if range_length < length {
165                continue;
166            } else if range_length == length {
167                // Found a perfect fit, so stop looking.
168                best_fit = Some((index, range));
169                break;
170            }
171            best_fit = Some(match best_fit {
172                Some((best_index, best_range)) => {
173                    // Find best fit for this allocation to reduce memory fragmentation.
174                    if range_length < best_range.end - best_range.start {
175                        (index, range)
176                    } else {
177                        (best_index, best_range.clone())
178                    }
179                }
180                None => (index, range),
181            });
182        }
183        match best_fit {
184            Some((index, range)) => {
185                if range.end - range.start == length {
186                    self.free_ranges.remove(index);
187                } else {
188                    self.free_ranges[index].start += length;
189                }
190                Ok(range.start..(range.start + length))
191            }
192            None => Err(RangeAllocationError {
193                fragmented_free_length,
194            }),
195        }
196    }
197
198    /// Frees a previously allocated range, making it available for future allocations.
199    ///
200    /// This method attempts to merge the freed range with adjacent free ranges to
201    /// reduce fragmentation. The freed range must be within the initial range and
202    /// must not be empty.
203    ///
204    /// # Arguments
205    ///
206    /// * `range` - The range to free. Must be within the allocator's initial range.
207    ///
208    /// # Panics
209    ///
210    /// Panics if the range is outside the initial range or if the range is empty
211    /// (start >= end).
212    ///
213    /// # Example
214    ///
215    /// ```
216    /// use range_alloc_arceos::RangeAllocator;
217    ///
218    /// let mut allocator = RangeAllocator::new(0..100);
219    /// let range = allocator.allocate_range(20).unwrap();
220    /// allocator.free_range(range);
221    /// ```
222    pub fn free_range(&mut self, range: Range<T>) {
223        assert!(self.initial_range.start <= range.start && range.end <= self.initial_range.end);
224        assert!(range.start < range.end);
225
226        // Get insertion position.
227        let i = self
228            .free_ranges
229            .iter()
230            .position(|r| r.start > range.start)
231            .unwrap_or(self.free_ranges.len());
232
233        // Try merging with neighboring ranges in the free list.
234        // Before: |left|-(range)-|right|
235        if i > 0 && range.start == self.free_ranges[i - 1].end {
236            // Merge with |left|.
237            self.free_ranges[i - 1].end =
238                if i < self.free_ranges.len() && range.end == self.free_ranges[i].start {
239                    // Check for possible merge with |left| and |right|.
240                    let right = self.free_ranges.remove(i);
241                    right.end
242                } else {
243                    range.end
244                };
245
246            return;
247        } else if i < self.free_ranges.len() && range.end == self.free_ranges[i].start {
248            // Merge with |right|.
249            self.free_ranges[i].start = if i > 0 && range.start == self.free_ranges[i - 1].end {
250                // Check for possible merge with |left| and |right|.
251                let left = self.free_ranges.remove(i - 1);
252                left.start
253            } else {
254                range.start
255            };
256
257            return;
258        }
259
260        // Debug checks
261        assert!(
262            (i == 0 || self.free_ranges[i - 1].end < range.start)
263                && (i >= self.free_ranges.len() || range.end < self.free_ranges[i].start)
264        );
265
266        self.free_ranges.insert(i, range);
267    }
268
269    /// Returns an iterator over allocated non-empty ranges
270    pub fn allocated_ranges(&self) -> impl Iterator<Item = Range<T>> + '_ {
271        let first = match self.free_ranges.first() {
272            Some(Range { ref start, .. }) if *start > self.initial_range.start => {
273                Some(self.initial_range.start..*start)
274            }
275            None => Some(self.initial_range.clone()),
276            _ => None,
277        };
278
279        let last = match self.free_ranges.last() {
280            Some(Range { end, .. }) if *end < self.initial_range.end => {
281                Some(*end..self.initial_range.end)
282            }
283            _ => None,
284        };
285
286        let mid = self
287            .free_ranges
288            .iter()
289            .zip(self.free_ranges.iter().skip(1))
290            .map(|(ra, rb)| ra.end..rb.start);
291
292        first.into_iter().chain(mid).chain(last)
293    }
294
295    /// Resets the allocator to its initial state.
296    ///
297    /// This marks the entire initial range as free, effectively deallocating
298    /// all previously allocated ranges.
299    pub fn reset(&mut self) {
300        self.free_ranges.clear();
301        self.free_ranges.push(self.initial_range.clone());
302    }
303
304    /// Returns `true` if no ranges have been allocated.
305    ///
306    /// This checks whether the allocator is in its initial state with all space free.
307    pub fn is_empty(&self) -> bool {
308        self.free_ranges.len() == 1 && self.free_ranges[0] == self.initial_range
309    }
310}
311
312impl<T: Copy + Sub<Output = T> + Sum> RangeAllocator<T> {
313    /// Returns the total amount of free space available across all free ranges.
314    ///
315    /// This sums the lengths of all free ranges, giving the total amount of space
316    /// that could be allocated if fragmentation is not an issue.
317    pub fn total_available(&self) -> T {
318        self.free_ranges
319            .iter()
320            .map(|range| range.end - range.start)
321            .sum()
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn test_basic_allocation() {
331        let mut alloc = RangeAllocator::new(0..10);
332        // Test if an allocation works
333        assert_eq!(alloc.allocate_range(4), Ok(0..4));
334        assert!(alloc.allocated_ranges().eq(core::iter::once(0..4)));
335        // Free the prior allocation
336        alloc.free_range(0..4);
337        // Make sure the free actually worked
338        assert_eq!(alloc.free_ranges, vec![0..10]);
339        assert!(alloc.allocated_ranges().eq(core::iter::empty()));
340    }
341
342    #[test]
343    fn test_out_of_space() {
344        let mut alloc = RangeAllocator::new(0..10);
345        // Test if the allocator runs out of space correctly
346        assert_eq!(alloc.allocate_range(10), Ok(0..10));
347        assert!(alloc.allocated_ranges().eq(core::iter::once(0..10)));
348        assert!(alloc.allocate_range(4).is_err());
349        alloc.free_range(0..10);
350    }
351
352    #[test]
353    fn test_grow() {
354        let mut alloc = RangeAllocator::new(0..11);
355        // Test if the allocator runs out of space correctly
356        assert_eq!(alloc.allocate_range(10), Ok(0..10));
357        assert!(alloc.allocated_ranges().eq(core::iter::once(0..10)));
358        assert!(alloc.allocate_range(4).is_err());
359        alloc.grow_to(20);
360        assert_eq!(alloc.allocate_range(4), Ok(10..14));
361        alloc.free_range(0..14);
362    }
363
364    #[test]
365    fn test_grow_with_hole_at_start() {
366        let mut alloc = RangeAllocator::new(0..6);
367
368        assert_eq!(alloc.allocate_range(3), Ok(0..3));
369        assert_eq!(alloc.allocate_range(3), Ok(3..6));
370        alloc.free_range(0..3);
371
372        alloc.grow_to(9);
373        assert_eq!(alloc.allocated_ranges().collect::<Vec<_>>(), [3..6]);
374    }
375    #[test]
376    fn test_grow_with_hole_in_middle() {
377        let mut alloc = RangeAllocator::new(0..6);
378
379        assert_eq!(alloc.allocate_range(2), Ok(0..2));
380        assert_eq!(alloc.allocate_range(2), Ok(2..4));
381        assert_eq!(alloc.allocate_range(2), Ok(4..6));
382        alloc.free_range(2..4);
383
384        alloc.grow_to(9);
385        assert_eq!(alloc.allocated_ranges().collect::<Vec<_>>(), [0..2, 4..6]);
386    }
387
388    #[test]
389    fn test_dont_use_block_that_is_too_small() {
390        let mut alloc = RangeAllocator::new(0..10);
391        // Allocate three blocks then free the middle one and check for correct state
392        assert_eq!(alloc.allocate_range(3), Ok(0..3));
393        assert_eq!(alloc.allocate_range(3), Ok(3..6));
394        assert_eq!(alloc.allocate_range(3), Ok(6..9));
395        alloc.free_range(3..6);
396        assert_eq!(alloc.free_ranges, vec![3..6, 9..10]);
397        assert_eq!(
398            alloc.allocated_ranges().collect::<Vec<Range<i32>>>(),
399            vec![0..3, 6..9]
400        );
401        // Now request space that the middle block can fill, but the end one can't.
402        assert_eq!(alloc.allocate_range(3), Ok(3..6));
403    }
404
405    #[test]
406    fn test_free_blocks_in_middle() {
407        let mut alloc = RangeAllocator::new(0..100);
408        // Allocate many blocks then free every other block.
409        assert_eq!(alloc.allocate_range(10), Ok(0..10));
410        assert_eq!(alloc.allocate_range(10), Ok(10..20));
411        assert_eq!(alloc.allocate_range(10), Ok(20..30));
412        assert_eq!(alloc.allocate_range(10), Ok(30..40));
413        assert_eq!(alloc.allocate_range(10), Ok(40..50));
414        assert_eq!(alloc.allocate_range(10), Ok(50..60));
415        assert_eq!(alloc.allocate_range(10), Ok(60..70));
416        assert_eq!(alloc.allocate_range(10), Ok(70..80));
417        assert_eq!(alloc.allocate_range(10), Ok(80..90));
418        assert_eq!(alloc.allocate_range(10), Ok(90..100));
419        assert_eq!(alloc.free_ranges, vec![]);
420        assert!(alloc.allocated_ranges().eq(core::iter::once(0..100)));
421        alloc.free_range(10..20);
422        alloc.free_range(30..40);
423        alloc.free_range(50..60);
424        alloc.free_range(70..80);
425        alloc.free_range(90..100);
426        // Check that the right blocks were freed.
427        assert_eq!(
428            alloc.free_ranges,
429            vec![10..20, 30..40, 50..60, 70..80, 90..100]
430        );
431        assert_eq!(
432            alloc.allocated_ranges().collect::<Vec<Range<i32>>>(),
433            vec![0..10, 20..30, 40..50, 60..70, 80..90]
434        );
435        // Fragment the memory on purpose a bit.
436        assert_eq!(alloc.allocate_range(6), Ok(10..16));
437        assert_eq!(alloc.allocate_range(6), Ok(30..36));
438        assert_eq!(alloc.allocate_range(6), Ok(50..56));
439        assert_eq!(alloc.allocate_range(6), Ok(70..76));
440        assert_eq!(alloc.allocate_range(6), Ok(90..96));
441        // Check for fragmentation.
442        assert_eq!(
443            alloc.free_ranges,
444            vec![16..20, 36..40, 56..60, 76..80, 96..100]
445        );
446        assert_eq!(
447            alloc.allocated_ranges().collect::<Vec<Range<i32>>>(),
448            vec![0..16, 20..36, 40..56, 60..76, 80..96]
449        );
450        // Fill up the fragmentation
451        assert_eq!(alloc.allocate_range(4), Ok(16..20));
452        assert_eq!(alloc.allocate_range(4), Ok(36..40));
453        assert_eq!(alloc.allocate_range(4), Ok(56..60));
454        assert_eq!(alloc.allocate_range(4), Ok(76..80));
455        assert_eq!(alloc.allocate_range(4), Ok(96..100));
456        // Check that nothing is free.
457        assert_eq!(alloc.free_ranges, vec![]);
458        assert!(alloc.allocated_ranges().eq(core::iter::once(0..100)));
459    }
460
461    #[test]
462    fn test_ignore_block_if_another_fits_better() {
463        let mut alloc = RangeAllocator::new(0..10);
464        // Allocate blocks such that the only free spaces available are 3..6 and 9..10
465        // in order to prepare for the next test.
466        assert_eq!(alloc.allocate_range(3), Ok(0..3));
467        assert_eq!(alloc.allocate_range(3), Ok(3..6));
468        assert_eq!(alloc.allocate_range(3), Ok(6..9));
469        alloc.free_range(3..6);
470        assert_eq!(alloc.free_ranges, vec![3..6, 9..10]);
471        assert_eq!(
472            alloc.allocated_ranges().collect::<Vec<Range<i32>>>(),
473            vec![0..3, 6..9]
474        );
475        // Now request space that can be filled by 3..6 but should be filled by 9..10
476        // because 9..10 is a perfect fit.
477        assert_eq!(alloc.allocate_range(1), Ok(9..10));
478    }
479
480    #[test]
481    fn test_merge_neighbors() {
482        let mut alloc = RangeAllocator::new(0..9);
483        assert_eq!(alloc.allocate_range(3), Ok(0..3));
484        assert_eq!(alloc.allocate_range(3), Ok(3..6));
485        assert_eq!(alloc.allocate_range(3), Ok(6..9));
486        alloc.free_range(0..3);
487        alloc.free_range(6..9);
488        alloc.free_range(3..6);
489        assert_eq!(alloc.free_ranges, vec![0..9]);
490        assert!(alloc.allocated_ranges().eq(core::iter::empty()));
491    }
492}