1 //! Implements a map from allocation ranges to data.
2 //! This is somewhat similar to RangeMap, but the ranges
3 //! and data are discrete and non-splittable. An allocation in the
4 //! map will always have the same range until explicitly removed
6 use rustc_target::abi::Size;
7 use std::ops::{Index, IndexMut, Range};
9 use rustc_const_eval::interpret::AllocRange;
11 #[derive(Clone, Debug)]
13 /// The range covered by this element; never empty.
15 /// The data stored for this element.
19 /// Index of an allocation within the map
20 type Position = usize;
22 #[derive(Clone, Debug)]
23 pub struct AllocationMap<T> {
27 #[derive(Clone, Debug, PartialEq)]
29 /// The access perfectly overlaps (same offset and range) with the exsiting allocation
30 PerfectlyOverlapping(Position),
31 /// The access does not touch any exising allocation
33 /// The access overlaps with one or more existing allocations
34 ImperfectlyOverlapping(Range<Position>),
37 impl<T> AllocationMap<T> {
38 pub fn new() -> Self {
39 Self { v: Vec::new() }
42 /// Finds the position of the allocation containing the given offset. If the offset is not
43 /// in an existing allocation, then returns Err containing the position
44 /// where such allocation should be inserted
45 fn find_offset(&self, offset: Size) -> Result<Position, Position> {
46 // We do a binary search.
47 let mut left = 0usize; // inclusive
48 let mut right = self.v.len(); // exclusive
51 // No element contains the given offset. But the
52 // position is where such element should be placed at.
55 let candidate = left.checked_add(right).unwrap() / 2;
56 let elem = &self.v[candidate];
57 if offset < elem.range.start {
58 // We are too far right (offset is further left).
59 debug_assert!(candidate < right); // we are making progress
61 } else if offset >= elem.range.end() {
62 // We are too far left (offset is further right).
63 debug_assert!(candidate >= left); // we are making progress
72 /// Determines whether a given access on `range` overlaps with
73 /// an existing allocation
74 pub fn access_type(&self, range: AllocRange) -> AccessType {
75 match self.find_offset(range.start) {
77 // Start of the range belongs to an existing object, now let's check the overlapping situation
78 let elem = &self.v[pos];
79 // FIXME: derive Eq for AllocRange in rustc
80 if elem.range.start == range.start && elem.range.size == range.size {
81 // Happy case: perfectly overlapping access
82 AccessType::PerfectlyOverlapping(pos)
84 // FIXME: add a last() method to AllocRange that returns the last inclusive offset (end() is exclusive)
85 let end_pos = match self.find_offset(range.end() - Size::from_bytes(1)) {
86 // If the end lands in an existing object, add one to get the exclusive position
87 Ok(inclusive_pos) => inclusive_pos + 1,
88 Err(exclusive_pos) => exclusive_pos,
91 AccessType::ImperfectlyOverlapping(pos..end_pos)
95 // Start of the range doesn't belong to an existing object
96 match self.find_offset(range.end() - Size::from_bytes(1)) {
97 // Neither does the end
100 // There's nothing between the start and the end, so the range thing is empty
101 AccessType::Empty(pos)
103 // Otherwise we have entirely covered an existing object
104 AccessType::ImperfectlyOverlapping(pos..end_pos)
106 // Otherwise at least part of it overlaps with something else
107 Ok(end_pos) => AccessType::ImperfectlyOverlapping(pos..end_pos + 1),
113 /// Inserts an object and its occupied range at given position
114 // The Position can be calculated from AllocRange, but the only user of AllocationMap
115 // always calls access_type before calling insert/index/index_mut, and we don't
116 // want to repeat the binary search on each time, so we ask the caller to supply Position
117 pub fn insert_at_pos(&mut self, pos: Position, range: AllocRange, data: T) {
118 self.v.insert(pos, Elem { range, data });
119 // If we aren't the first element, then our start must be greater than the preivous element's end
121 debug_assert!(self.v[pos - 1].range.end() <= range.start);
123 // If we aren't the last element, then our end must be smaller than next element's start
124 if pos < self.v.len() - 1 {
125 debug_assert!(range.end() <= self.v[pos + 1].range.start);
129 pub fn remove_pos_range(&mut self, pos_range: Range<Position>) {
130 self.v.drain(pos_range);
133 pub fn remove_from_pos(&mut self, pos: Position) {
138 impl<T> Index<Position> for AllocationMap<T> {
141 fn index(&self, pos: Position) -> &Self::Output {
146 impl<T> IndexMut<Position> for AllocationMap<T> {
147 fn index_mut(&mut self, pos: Position) -> &mut Self::Output {
148 &mut self.v[pos].data
154 use rustc_const_eval::interpret::alloc_range;
160 // FIXME: make Size::from_bytes const
161 let four = Size::from_bytes(4);
162 let map = AllocationMap::<()>::new();
164 // Correctly tells where we should insert the first element (at position 0)
165 assert_eq!(map.find_offset(Size::from_bytes(3)), Err(0));
167 // Correctly tells the access type along with the supposed position
168 assert_eq!(map.access_type(alloc_range(Size::ZERO, four)), AccessType::Empty(0));
173 fn no_overlapping_inserts() {
174 let four = Size::from_bytes(4);
176 let mut map = AllocationMap::<&str>::new();
178 // |_|_|_|_|#|#|#|#|_|_|_|_|...
179 // 0 1 2 3 4 5 6 7 8 9 a b c d
180 map.insert_at_pos(0, alloc_range(four, four), "#");
181 // |_|_|_|_|#|#|#|#|_|_|_|_|...
182 // 0 ^ ^ ^ ^ 5 6 7 8 9 a b c d
183 map.insert_at_pos(0, alloc_range(Size::from_bytes(1), four), "@");
188 let four = Size::from_bytes(4);
190 let mut map = AllocationMap::<&str>::new();
194 map.insert_at_pos(0, alloc_range(Size::ZERO, four), "#");
197 assert_eq!(map.find_offset(four), Err(1));
198 // |#|#|#|#|_|_|_|_|_|...
200 assert_eq!(map.access_type(alloc_range(four, four)), AccessType::Empty(1));
202 let eight = Size::from_bytes(8);
203 // |#|#|#|#|_|_|_|_|@|@|@|@|_|_|...
204 // 0 1 2 3 4 5 6 7 8 9 a b c d
205 map.insert_at_pos(1, alloc_range(eight, four), "@");
206 // |#|#|#|#|_|_|_|_|@|@|@|@|_|_|...
207 // 0 1 2 3 4 5 6 ^ 8 9 a b c d
208 assert_eq!(map.find_offset(Size::from_bytes(7)), Err(1));
209 // |#|#|#|#|_|_|_|_|@|@|@|@|_|_|...
210 // 0 1 2 3 ^ ^ ^ ^ 8 9 a b c d
211 assert_eq!(map.access_type(alloc_range(four, four)), AccessType::Empty(1));
215 fn perfectly_overlapping() {
216 let four = Size::from_bytes(4);
218 let mut map = AllocationMap::<&str>::new();
222 map.insert_at_pos(0, alloc_range(Size::ZERO, four), "#");
225 assert_eq!(map.find_offset(Size::ZERO), Ok(0));
227 map.access_type(alloc_range(Size::ZERO, four)),
228 AccessType::PerfectlyOverlapping(0)
231 // |#|#|#|#|@|@|@|@|_|...
233 map.insert_at_pos(1, alloc_range(four, four), "@");
234 // |#|#|#|#|@|@|@|@|_|...
236 assert_eq!(map.find_offset(four), Ok(1));
237 assert_eq!(map.access_type(alloc_range(four, four)), AccessType::PerfectlyOverlapping(1));
242 let four = Size::from_bytes(4);
244 let mut map = AllocationMap::<&str>::new();
246 // |_|_|_|_|#|#|#|#|_|_|_|_|...
247 // 0 1 2 3 4 5 6 7 8 9 a b c d
248 map.insert_at_pos(0, alloc_range(four, four), "#");
249 // |_|_|_|_|#|#|#|#|_|_|_|_|...
250 // 0 1 ^ ^ ^ ^ 6 7 8 9 a b c d
252 map.access_type(alloc_range(Size::from_bytes(2), four)),
253 AccessType::ImperfectlyOverlapping(0..1)
255 // |_|_|_|_|#|#|#|#|_|_|_|_|...
256 // 0 1 2 3 4 5 ^ ^ ^ ^ a b c d
258 map.access_type(alloc_range(Size::from_bytes(6), four)),
259 AccessType::ImperfectlyOverlapping(0..1)
261 // |_|_|_|_|#|#|#|#|_|_|_|_|...
262 // 0 1 ^ ^ ^ ^ ^ ^ ^ ^ a b c d
264 map.access_type(alloc_range(Size::from_bytes(2), Size::from_bytes(8))),
265 AccessType::ImperfectlyOverlapping(0..1)
268 // |_|_|_|_|#|#|#|#|_|_|@|@|_|_|...
269 // 0 1 2 3 4 5 6 7 8 9 a b c d
270 map.insert_at_pos(1, alloc_range(Size::from_bytes(10), Size::from_bytes(2)), "@");
271 // |_|_|_|_|#|#|#|#|_|_|@|@|_|_|...
272 // 0 1 2 3 4 5 ^ ^ ^ ^ ^ ^ ^ ^
274 map.access_type(alloc_range(Size::from_bytes(6), Size::from_bytes(8))),
275 AccessType::ImperfectlyOverlapping(0..2)