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 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}