retour\alloc/
search.rs

1use crate::error::{Error, Result};
2use std::ops::Range;
3
4/// Returns an iterator for free after the specified address.
5pub fn after(
6  origin: *const (),
7  range: Option<Range<usize>>,
8) -> impl Iterator<Item = Result<*const ()>> {
9  FreeRegionIter::new(origin, range, SearchDirection::After)
10}
11
12/// Returns an iterator for free before the specified address.
13pub fn before(
14  origin: *const (),
15  range: Option<Range<usize>>,
16) -> impl Iterator<Item = Result<*const ()>> {
17  FreeRegionIter::new(origin, range, SearchDirection::Before)
18}
19
20/// Direction for the region search.
21enum SearchDirection {
22  Before,
23  After,
24}
25
26/// An iterator searching for free regions.
27struct FreeRegionIter {
28  range: Range<usize>,
29  search: SearchDirection,
30  current: usize,
31}
32
33impl FreeRegionIter {
34  /// Creates a new iterator for free regions.
35  fn new(origin: *const (), range: Option<Range<usize>>, search: SearchDirection) -> Self {
36    FreeRegionIter {
37      range: range.unwrap_or(0..usize::max_value()),
38      current: origin as usize,
39      search,
40    }
41  }
42}
43
44impl Iterator for FreeRegionIter {
45  type Item = Result<*const ()>;
46
47  /// Returns the closest free region for the current address.
48  fn next(&mut self) -> Option<Self::Item> {
49    let page_size = region::page::size();
50
51    while self.current > 0 && self.range.contains(&self.current) {
52      match region::query(self.current as *const usize) {
53        Ok(region) => {
54          let range = region.as_range();
55          self.current = match self.search {
56            SearchDirection::Before => range.start.saturating_sub(page_size),
57            SearchDirection::After => range.end,
58          }
59        },
60        Err(error) => {
61          // Check whether the region is free, otherwise return the error
62          let result = Some(match error {
63            region::Error::UnmappedRegion => Ok(self.current as *const _),
64            inner => Err(Error::RegionFailure(inner)),
65          });
66
67          // Adjust the offset for repeated calls.
68          self.current = match self.search {
69            SearchDirection::Before => self.current.saturating_sub(page_size),
70            SearchDirection::After => self.current + page_size,
71          };
72
73          return result;
74        },
75      }
76    }
77
78    None
79  }
80}