monster/engine/
memory.rs

1use std::ops::{Index, IndexMut};
2
3#[derive(Debug, Clone)]
4pub struct VirtualMemory<T> {
5    memory_size: usize,
6    segment_mask: usize,
7    segment_shift: u32,
8    default: T,
9    data: Vec<Vec<T>>,
10}
11
12impl<T: Copy + Default> Index<usize> for VirtualMemory<T> {
13    type Output = T;
14    fn index(&self, index: usize) -> &Self::Output {
15        let segment_index = index >> self.segment_shift;
16        let segment_offset = index & self.segment_mask;
17        if self.data[segment_index].is_empty() {
18            return &self.default;
19        }
20        Index::index(&self.data[segment_index], segment_offset)
21    }
22}
23
24impl<T: Copy + Default> IndexMut<usize> for VirtualMemory<T> {
25    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
26        let segment_index = index >> self.segment_shift;
27        let segment_offset = index & self.segment_mask;
28        if self.data[segment_index].is_empty() {
29            self.data[segment_index] = vec![T::default(); self.segment_mask + 1];
30        }
31        IndexMut::index_mut(&mut self.data[segment_index], segment_offset)
32    }
33}
34
35impl<T: Copy + Default> VirtualMemory<T> {
36    pub fn new(memory_size: usize, segment_size: usize) -> Self {
37        assert!(
38            memory_size % segment_size == 0,
39            "memory size must be multiple of segment size"
40        );
41        assert!(
42            segment_size.is_power_of_two(),
43            "segment size must be a power of two"
44        );
45        let segment_mask = segment_size - 1;
46        let segment_shift = segment_size.trailing_zeros();
47        let segments = memory_size / segment_size;
48        Self {
49            memory_size,
50            segment_mask,
51            segment_shift,
52            default: T::default(),
53            data: vec![[].to_vec(); segments],
54        }
55    }
56
57    /// Returns an iterator over values in mapped memory segments.
58    ///
59    /// Note that the iterator does not cover un-mapped segments (i.e. segments that have not been
60    /// written to before). This essentially skips over segments that would exclusively contain
61    /// [`T::default()`] values. It also means that iteration likely yields fewer elements than
62    /// calls of [`size()`] report.
63    pub fn iter(&self) -> impl Iterator<Item = &T> {
64        self.data.iter().filter(|x| !x.is_empty()).flatten()
65    }
66
67    pub fn size(&self) -> usize {
68        self.memory_size
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn load_default() {
78        let m = VirtualMemory::<i32>::new(32, 16);
79        assert_eq!(m[0], 0);
80        assert_eq!(m[15], 0);
81        assert_eq!(m[16], 0);
82        assert_eq!(m[31], 0);
83    }
84
85    #[test]
86    #[should_panic]
87    fn load_out_of_bounds() {
88        let m = VirtualMemory::<i32>::new(32, 16);
89        let _this_will_panic = m[32];
90    }
91
92    #[test]
93    fn store_value() {
94        let mut m = VirtualMemory::<i32>::new(32, 16);
95        assert_eq!(m[0], 0);
96        assert_eq!(m[1], 0);
97        m[0] = 7;
98        assert_eq!(m[0], 7);
99        assert_eq!(m[1], 0);
100    }
101
102    #[test]
103    #[should_panic]
104    fn store_out_of_bounds() {
105        let mut m = VirtualMemory::<i32>::new(32, 16);
106        m[32] = 7; // this will panic
107    }
108
109    #[test]
110    fn iter_values() {
111        let mut m = VirtualMemory::<i32>::new(8, 2);
112        m[0] = 23; // targets first segment
113        m[3] = 42; // targets second segment
114        assert_eq!(m.iter().copied().collect::<Vec<i32>>(), vec![23, 0, 0, 42]);
115    }
116}