1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use std::ops::RangeInclusive;
use crate::AddressType;

/// Describes a region in memory.
///
/// The `MemoryRegion.upper_bound` is **inclusive**.
///
/// Biggest possible memory address: [`AddressType::MAX`](AddressType).
#[derive(PartialEq, Eq)]
#[derive(Debug)]
pub struct MemoryRegion<D: Clone> {
    lower_bound: AddressType,
    upper_bound: AddressType,
    description: D,
}

impl<D: Clone> MemoryRegion<D> {
    pub(crate) fn new(lower_bound: AddressType, upper_bound: AddressType, description: D) -> Result<Self, String> {
        if lower_bound >= upper_bound {
            return Err(format!("lower bound ({}) is bigger than or equal to upper bound ({})", lower_bound, upper_bound));
        }
        Ok(Self {
            lower_bound,
            upper_bound,
            description,
        })
    }

    /// Returns `true` if a address is inside of the `MemoryRegion`.
    pub fn address_in_region(&self, address: AddressType) -> bool {
        self.lower_bound <= address && address <= self.upper_bound
    }

    /// Returns `true` if a address comes after the upper bound of the `MemoryRegion`.
    pub fn address_comes_after(&self, address: AddressType) -> bool {
        self.upper_bound < address
    }

    /// Returns `true` if the given `MemoryRegion` overlaps with another `MemoryRegion`.
    pub fn overlaps(&self, other: &MemoryRegion<D>) -> bool {
        self.upper_bound >= other.lower_bound && self.lower_bound <= other.upper_bound
    }

    /// Returns `true` if the given `MemoryRegion`s lower bound is bigger than the other
    /// `MemoryRegion`s upper bound.
    pub fn comes_after(&self, other: &MemoryRegion<D>) -> bool {
        self.lower_bound > other.upper_bound
    }

    /// Returns `true` if the `MemoryRegion` starts at the beginning of memory.
    pub fn starts_at_zero(&self) -> bool {
        self.lower_bound == 0
    }

    /// Returns `true` if the given `MemoryRegion` is **directly** next to another `MemoryRegion`.
    pub fn is_adjacent_to(&self, other: &MemoryRegion<D>) -> bool {
        self.upper_bound.wrapping_add(1) == other.lower_bound || self.lower_bound == other.upper_bound.wrapping_add(1)
    }

    pub(crate) fn region_between_zero_and_self(&self, description: &D) -> Result<Option<MemoryRegion<D>>, String> {
        Ok(
            if self.starts_at_zero() {
                None
            } else {
                Some(Self::new(0, self.lower_bound.wrapping_sub(1), description.clone())?)
            }
        )
    }

    pub(crate) fn region_between(&self, other: &MemoryRegion<D>, description: &D) -> Result<Option<MemoryRegion<D>>, String> {
        Ok(
            if self.is_adjacent_to(other) {
                None
            } else {
                Some(Self::new(self.upper_bound, other.lower_bound.wrapping_sub(1), description.clone())?)
            }
        )
    }

    /// Lower bound of the `MemoryRegion`.
    pub fn lower_bound(&self) -> AddressType {
        self.lower_bound
    }

    /// Upper bound of the `MemoryRegion`.
    pub fn upper_bound(&self) -> AddressType {
        self.upper_bound
    }

    /// Description of the `MemoryRegion`.
    pub fn description(&self) -> D {
        self.description.clone()
    }

    /// Returns the `MemoryRegion` as a range.
    pub fn to_range(&self) -> RangeInclusive<AddressType> {
        self.lower_bound..=self.upper_bound
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const MR_1: MemoryRegion<&str> = MemoryRegion { lower_bound: 0, upper_bound: 255, description: "test" };
    const MR_2: MemoryRegion<&str> = MemoryRegion { lower_bound: 16, upper_bound: 31, description: "test" };
    const MR_3: MemoryRegion<&str> = MemoryRegion { lower_bound: 16, upper_bound: 32, description: "test" };
    const MR_4: MemoryRegion<&str> = MemoryRegion { lower_bound: 32, upper_bound: 64, description: "test" };

    #[test]
    fn memory_region_construction() {
        assert_eq!(
            MemoryRegion::new(0, 6, "test"),
            Ok(MemoryRegion {
                lower_bound: 0,
                upper_bound: 6,
                description: "test"
            })
        );

        assert!(MemoryRegion::new(6, 0, "test").is_err());
    }

    #[test]
    fn memory_region_overlap() {
        assert!(MR_1.overlaps(&MR_2));
        assert!(MR_1.overlaps(&MR_4));
        assert!(!MR_2.overlaps(&MR_4));
        assert!(MR_3.overlaps(&MR_4));

        // Reversed
        assert!(MR_2.overlaps(&MR_1));
        assert!(MR_4.overlaps(&MR_1));
        assert!(!MR_4.overlaps(&MR_2));
        assert!(MR_4.overlaps(&MR_3));

        assert!(MR_1.overlaps(&MR_1));
    }

    #[test]
    fn memory_region_adjacency() {
        assert!(!MR_1.is_adjacent_to(&MR_2));
        assert!(MR_2.is_adjacent_to(&MR_4));
        assert!(!MR_3.is_adjacent_to(&MR_4));
    }
}