gemachain_runtime/
sorted_storages.rs

1use crate::accounts_db::SnapshotStorage;
2use log::*;
3use gemachain_measure::measure::Measure;
4use gemachain_sdk::clock::Slot;
5use std::ops::Range;
6
7pub struct SortedStorages<'a> {
8    range: Range<Slot>,
9    storages: Vec<Option<&'a SnapshotStorage>>,
10    slot_count: usize,
11}
12
13impl<'a> SortedStorages<'a> {
14    pub fn get(&self, slot: Slot) -> Option<&SnapshotStorage> {
15        if !self.range.contains(&slot) {
16            None
17        } else {
18            let index = (slot - self.range.start) as usize;
19            self.storages[index]
20        }
21    }
22
23    pub fn range_width(&self) -> Slot {
24        self.range.end - self.range.start
25    }
26
27    pub fn range(&self) -> &Range<Slot> {
28        &self.range
29    }
30
31    pub fn slot_count(&self) -> usize {
32        self.slot_count
33    }
34
35    // assumptions:
36    // 1. each SnapshotStorage.!is_empty()
37    // 2. SnapshotStorage.first().unwrap().get_slot() is unique from all other SnapshotStorage items.
38    pub fn new(source: &'a [SnapshotStorage]) -> Self {
39        let slots = source
40            .iter()
41            .map(|storages| {
42                let first = storages.first();
43                assert!(first.is_some(), "SnapshotStorage.is_empty()");
44                let storage = first.unwrap();
45                storage.slot() // this must be unique. Will be enforced in new_with_slots
46            })
47            .collect::<Vec<_>>();
48        Self::new_with_slots(source.iter().zip(slots.iter()), None, None)
49    }
50
51    // source[i] is in slot slots[i]
52    // assumptions:
53    // 1. slots vector contains unique slot #s.
54    // 2. slots and source are the same len
55    pub fn new_with_slots<'b>(
56        source: impl Iterator<Item = (&'a SnapshotStorage, &'b Slot)> + Clone,
57        // A slot used as a lower bound, but potentially smaller than the smallest slot in the given 'source' iterator
58        min_slot: Option<Slot>,
59        // highest valid slot. Only matters if source array does not contain a slot >= max_slot_inclusive.
60        // An example is a slot that has accounts in the write cache at slots <= 'max_slot_inclusive' but no storages at those slots.
61        // None => self.range.end = source.1.max() + 1
62        // Some(slot) => self.range.end = std::cmp::max(slot, source.1.max())
63        max_slot_inclusive: Option<Slot>,
64    ) -> Self {
65        let mut min = Slot::MAX;
66        let mut max = Slot::MIN;
67        let mut adjust_min_max = |slot| {
68            min = std::cmp::min(slot, min);
69            max = std::cmp::max(slot + 1, max);
70        };
71        // none, either, or both of min/max could be specified
72        if let Some(slot) = min_slot {
73            adjust_min_max(slot);
74        }
75        if let Some(slot) = max_slot_inclusive {
76            adjust_min_max(slot);
77        }
78
79        let mut slot_count = 0;
80        let mut time = Measure::start("get slot");
81        let source_ = source.clone();
82        source_.for_each(|(_, slot)| {
83            slot_count += 1;
84            adjust_min_max(*slot);
85        });
86        time.stop();
87        let mut time2 = Measure::start("sort");
88        let range;
89        let mut storages;
90        if min > max {
91            range = Range::default();
92            storages = vec![];
93        } else {
94            range = Range {
95                start: min,
96                end: max,
97            };
98            let len = max - min;
99            storages = vec![None; len as usize];
100            source.for_each(|(original_storages, slot)| {
101                let index = (slot - min) as usize;
102                assert!(storages[index].is_none(), "slots are not unique"); // we should not encounter the same slot twice
103                storages[index] = Some(original_storages);
104            });
105        }
106        time2.stop();
107        debug!("SortedStorages, times: {}, {}", time.as_us(), time2.as_us());
108        Self {
109            range,
110            storages,
111            slot_count,
112        }
113    }
114}
115
116#[cfg(test)]
117pub mod tests {
118    use super::*;
119    impl<'a> SortedStorages<'a> {
120        pub fn new_debug(source: &[(&'a SnapshotStorage, Slot)], min: Slot, len: usize) -> Self {
121            let mut storages = vec![None; len];
122            let range = Range {
123                start: min,
124                end: min + len as Slot,
125            };
126            let slot_count = source.len();
127            for (storage, slot) in source {
128                storages[*slot as usize] = Some(*storage);
129            }
130
131            Self {
132                range,
133                storages,
134                slot_count,
135            }
136        }
137    }
138
139    #[test]
140    #[should_panic(expected = "SnapshotStorage.is_empty()")]
141    fn test_sorted_storages_empty() {
142        SortedStorages::new(&[Vec::new()]);
143    }
144
145    #[test]
146    #[should_panic(expected = "slots are not unique")]
147    fn test_sorted_storages_duplicate_slots() {
148        SortedStorages::new_with_slots(
149            [Vec::new(), Vec::new()].iter().zip([0, 0].iter()),
150            None,
151            None,
152        );
153    }
154
155    #[test]
156    fn test_sorted_storages_none() {
157        let result = SortedStorages::new_with_slots([].iter().zip([].iter()), None, None);
158        assert_eq!(result.range, Range::default());
159        assert_eq!(result.slot_count, 0);
160        assert_eq!(result.storages.len(), 0);
161        assert!(result.get(0).is_none());
162    }
163
164    #[test]
165    fn test_sorted_storages_1() {
166        let vec = vec![];
167        let vec_check = vec.clone();
168        let slot = 4;
169        let vecs = [vec];
170        let result = SortedStorages::new_with_slots(vecs.iter().zip([slot].iter()), None, None);
171        assert_eq!(
172            result.range,
173            Range {
174                start: slot,
175                end: slot + 1
176            }
177        );
178        assert_eq!(result.slot_count, 1);
179        assert_eq!(result.storages.len(), 1);
180        assert_eq!(result.get(slot).unwrap().len(), vec_check.len());
181    }
182
183    #[test]
184    fn test_sorted_storages_2() {
185        let vec = vec![];
186        let vec_check = vec.clone();
187        let slots = [4, 7];
188        let vecs = [vec.clone(), vec];
189        let result = SortedStorages::new_with_slots(vecs.iter().zip(slots.iter()), None, None);
190        assert_eq!(
191            result.range,
192            Range {
193                start: slots[0],
194                end: slots[1] + 1,
195            }
196        );
197        assert_eq!(result.slot_count, 2);
198        assert_eq!(result.storages.len() as Slot, slots[1] - slots[0] + 1);
199        assert!(result.get(0).is_none());
200        assert!(result.get(3).is_none());
201        assert!(result.get(5).is_none());
202        assert!(result.get(6).is_none());
203        assert!(result.get(8).is_none());
204        assert_eq!(result.get(slots[0]).unwrap().len(), vec_check.len());
205        assert_eq!(result.get(slots[1]).unwrap().len(), vec_check.len());
206    }
207}