memory_spec/
region.rs

1#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)]
2pub struct Region {
3    origin: u64,
4    end: u64,
5}
6
7impl Region {
8    pub fn new(
9        origin: Option<u64>,
10        length: Option<u64>,
11        end: Option<u64>,
12    ) -> Result<Self, NewRegionError> {
13        let (origin, end) = match (origin, length, end) {
14            (Some(origin), Some(length), Some(end)) => (origin.checked_add(length) == Some(end))
15                .then_some((origin, end))
16                .ok_or_else(|| NewRegionError::inconsistent("origin + length != end")),
17            (Some(origin), Some(length), None) => origin
18                .checked_add(length)
19                .map(|end| (origin, end))
20                .ok_or_else(|| NewRegionError::overflow("overflow evaluating origin + length")),
21            (Some(origin), None, Some(end)) => (origin <= end)
22                .then_some((origin, end))
23                .ok_or_else(|| NewRegionError::inconsistent("origin > end")),
24            (None, Some(length), Some(end)) => end
25                .checked_sub(length)
26                .map(|origin| (origin, end))
27                .ok_or_else(|| NewRegionError::overflow("overflow evaluating end - length")),
28            (Some(_), None, None) => Err(NewRegionError::underspecified("missing length")),
29            (None, Some(_), None) | (None, None, Some(_)) | (None, None, None) => {
30                Err(NewRegionError::underspecified("missing origin"))
31            }
32        }?;
33        Ok(Region { origin, end })
34    }
35
36    pub fn origin(&self) -> u64 {
37        self.origin
38    }
39
40    pub fn length(&self) -> u64 {
41        self.end - self.origin
42    }
43
44    pub fn end(&self) -> u64 {
45        self.end
46    }
47
48    /// Whether `other` is fully contained by this region.
49    pub fn contains(&self, other: &Region) -> bool {
50        self.origin <= other.origin && self.end >= other.end
51    }
52
53    pub fn intersection(&self, other: &Region) -> Option<Region> {
54        let origin = self.origin.max(other.origin);
55        let end = self.end.min(other.end);
56        Region::new(Some(origin), None, Some(end)).ok()
57    }
58
59    pub fn overlaps(&self, other: &Region) -> bool {
60        let Some(overlap) = self.intersection(other) else {
61            return false;
62        };
63        overlap.length() != 0
64    }
65}
66
67#[derive(Copy, Clone, Debug, PartialEq, Eq)]
68pub struct NewRegionError {
69    message: &'static str,
70    kind: NewRegionErrorKind,
71}
72
73impl NewRegionError {
74    fn overflow(message: &'static str) -> Self {
75        Self {
76            message,
77            kind: NewRegionErrorKind::Overflow,
78        }
79    }
80
81    fn inconsistent(message: &'static str) -> Self {
82        Self {
83            message,
84            kind: NewRegionErrorKind::Inconsistent,
85        }
86    }
87
88    fn underspecified(message: &'static str) -> Self {
89        Self {
90            message,
91            kind: NewRegionErrorKind::Underspecified,
92        }
93    }
94}
95
96#[derive(Copy, Clone, Debug, PartialEq, Eq)]
97pub enum NewRegionErrorKind {
98    Overflow,
99    Inconsistent,
100    Underspecified,
101}
102
103impl std::fmt::Display for NewRegionError {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.write_str(self.message)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn it_works() {
115        let region1: Region = Region::new(Some(1), Some(1), None).unwrap();
116        let region2: Region = Region::new(Some(1), None, Some(2)).unwrap();
117        let region3: Region = Region::new(None, Some(1), Some(2)).unwrap();
118        let region4: Region = Region::new(Some(1), Some(1), Some(2)).unwrap();
119        assert_eq!(region1, Region { origin: 1, end: 2 });
120        assert_eq!(region1, region2);
121        assert_eq!(region2, region3);
122        assert_eq!(region3, region4);
123    }
124
125    #[test]
126    fn it_errors() {
127        let err1 = Region::new(Some(1), Some(1), Some(3)).unwrap_err();
128        let err2 = Region::new(Some(0xFFFFFFFF_FFFFFFFF), Some(1), None).unwrap_err();
129        let err3 = Region::new(Some(2), None, Some(1)).unwrap_err();
130        let _err4 = Region::new(Some(1), None, None).unwrap_err();
131        let _err5 = Region::new(None, None, Some(1)).unwrap_err();
132
133        assert_eq!(err1.message, "origin + length != end");
134        assert_eq!(err2.message, "overflow evaluating origin + length");
135        assert_eq!(err3.message, "origin > end");
136    }
137}