mmtk/util/metadata/side_metadata/ranges.rs
1//! Data types for visiting metadata ranges at different granularities.
2//!
3//! Currently, the `break_bit_range` function can break a bit range into sub-ranges of whole bytes
4//! and in-byte bits.
5//!
6//! TODO:
7//!
8//! - Add a function to break a byte range into sub-ranges of whole words and in-word bytes.
9//! - And use it for searching side metadata for non-zero bits.
10//! - Add a function to break a byte range at chunk boundaries.
11//! - And use it for visiting discontiguous side metadata in bulk.
12
13use crate::util::Address;
14
15/// The type for bit offset in a byte.
16pub type BitOffset = u8;
17
18/// A range of bytes or bits within a byte. It is the unit of visiting a contiguous bit range of a
19/// side metadata.
20///
21/// In general, a bit range of a bitmap starts with multiple bits in the byte, followed by many
22/// whole bytes, and ends with multiple bits in the last byte.
23///
24/// A range is never empty.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum BitByteRange {
27 /// A range of whole bytes.
28 Bytes {
29 /// The starting address (inclusive) of the bytes.
30 start: Address,
31 /// The ending address (exclusive) of the bytes.
32 end: Address,
33 },
34 /// A range of bits within a byte.
35 BitsInByte {
36 /// The address of the byte.
37 addr: Address,
38 /// The starting bit index (inclusive), starting with zero from the low-order bit.
39 bit_start: BitOffset,
40 /// The ending bit index (exclusive), starting with zero from the low-order bit. This may
41 /// be 8 which means the range includes the highest bit. Be careful when shifting a `u8`
42 /// value because shifting an `u8` by 8 is considered an overflow in Rust.
43 bit_end: BitOffset,
44 },
45}
46
47/// Break a bit range into sub-ranges of whole bytes and in-byte bits.
48///
49/// This method is primarily used for iterating side metadata for a data address range. As we cannot
50/// guarantee that the data address range can be mapped to whole metadata bytes, we have to deal
51/// with visiting only a bit range in a metadata byte.
52///
53/// The bit range starts at the bit at index `start_bit` in the byte at address `start_addr`, and
54/// ends at (but does not include) the bit at index `end_bit` in the byte at address `end_addr`.
55///
56/// Arguments:
57/// * `forwards`: If true, we iterate forwards (from start/low address to end/high address).
58/// Otherwise, we iterate backwards (from end/high address to start/low address).
59/// * `visitor`: The callback that visits ranges of bits or bytes. It returns whether the
60/// itertion is early terminated.
61///
62/// Returns true if we iterate through every bits in the range. Return false if we abort iteration
63/// early.
64pub fn break_bit_range<V>(
65 start_addr: Address,
66 start_bit: BitOffset,
67 end_addr: Address,
68 end_bit: BitOffset,
69 forwards: bool,
70 visitor: &mut V,
71) -> bool
72where
73 V: FnMut(BitByteRange) -> bool,
74{
75 // The start and the end are the same, we don't need to do anything.
76 if start_addr == end_addr && start_bit == end_bit {
77 return false;
78 }
79
80 // If the range is already byte-aligned, visit the entire range as whole bytes.
81 if start_bit == 0 && end_bit == 0 {
82 return visitor(BitByteRange::Bytes {
83 start: start_addr,
84 end: end_addr,
85 });
86 }
87
88 // If the start and the end are within the same byte,
89 // visit the bit range within the byte.
90 if start_addr == end_addr {
91 return visitor(BitByteRange::BitsInByte {
92 addr: start_addr,
93 bit_start: start_bit,
94 bit_end: end_bit,
95 });
96 }
97
98 // If the end is the 0th bit of the next byte of the start,
99 // visit the bit range from the start to the end (bit 8) of the same byte.
100 if start_addr + 1usize == end_addr && end_bit == 0 {
101 return visitor(BitByteRange::BitsInByte {
102 addr: start_addr,
103 bit_start: start_bit,
104 bit_end: 8_u8,
105 });
106 }
107
108 // Otherwise, the range spans over multiple bytes, and is bit-unaligned at either the start or
109 // the end. Try to break it into (at most) three sub-ranges.
110
111 let start_aligned = start_bit == 0;
112 let end_aligned = end_bit == 0;
113
114 // We cannot let multiple closures capture `visitor` mutably at the same time, so we pass the
115 // visitor in as `v` every time.
116
117 // visit bits within the first byte
118 let visit_start = |v: &mut V| {
119 if !start_aligned {
120 v(BitByteRange::BitsInByte {
121 addr: start_addr,
122 bit_start: start_bit,
123 bit_end: 8_u8,
124 })
125 } else {
126 // The start is already aligned. No sub-byte range at the start.
127 false
128 }
129 };
130
131 // visit whole bytes in the middle
132 let visit_middle = |v: &mut V| {
133 let start = if start_aligned {
134 start_addr
135 } else {
136 // If the start is not aligned, the whole-byte range starts after the first byte.
137 start_addr + 1usize
138 };
139 let end = end_addr;
140 if start < end {
141 v(BitByteRange::Bytes { start, end })
142 } else {
143 // There are no whole bytes in the middle.
144 false
145 }
146 };
147
148 // visit bits within the last byte
149 let visit_end = |v: &mut V| {
150 if !end_aligned {
151 v(BitByteRange::BitsInByte {
152 addr: end_addr,
153 bit_start: 0_u8,
154 bit_end: end_bit,
155 })
156 } else {
157 // The end is aligned. No sub-byte range at the end.
158 false
159 }
160 };
161
162 // Visit the three segments forwards or backwards.
163 if forwards {
164 visit_start(visitor) || visit_middle(visitor) || visit_end(visitor)
165 } else {
166 visit_end(visitor) || visit_middle(visitor) || visit_start(visitor)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use crate::util::constants::BITS_IN_BYTE;
173
174 use super::*;
175
176 fn mk_addr(addr: usize) -> Address {
177 unsafe { Address::from_usize(addr) }
178 }
179
180 fn break_bit_range_wrapped(
181 start_addr: Address,
182 start_bit: usize,
183 end_addr: Address,
184 end_bit: usize,
185 ) -> Vec<BitByteRange> {
186 let mut vec = vec![];
187 break_bit_range(
188 start_addr,
189 start_bit as u8,
190 end_addr,
191 end_bit as u8,
192 true,
193 &mut |range| {
194 vec.push(range);
195 false
196 },
197 );
198 vec
199 }
200
201 #[test]
202 fn test_empty_range() {
203 let base = mk_addr(0x1000);
204 for bit in 0..BITS_IN_BYTE {
205 let result = break_bit_range_wrapped(base, bit, base, bit);
206 assert!(
207 result.is_empty(),
208 "Not empty. bit: {bit}, result: {result:?}"
209 );
210 }
211 }
212
213 #[test]
214 fn test_subbyte_range() {
215 let base = mk_addr(0x1000);
216 for bit0 in 0..BITS_IN_BYTE {
217 for bit1 in (bit0 + 1)..BITS_IN_BYTE {
218 let result = break_bit_range_wrapped(base, bit0, base, bit1);
219 assert_eq!(
220 result,
221 vec![BitByteRange::BitsInByte {
222 addr: base,
223 bit_start: bit0 as u8,
224 bit_end: bit1 as u8
225 }],
226 "Not equal. bit0: {bit0}, bit1: {bit1}",
227 );
228 }
229 }
230 }
231
232 #[test]
233 fn test_end_byte_range() {
234 let base = mk_addr(0x1000);
235 for bit0 in 1..BITS_IN_BYTE {
236 let result = break_bit_range_wrapped(base, bit0, base + 1usize, 0);
237 assert_eq!(
238 result,
239 vec![BitByteRange::BitsInByte {
240 addr: base,
241 bit_start: bit0 as u8,
242 bit_end: BITS_IN_BYTE as u8
243 }],
244 "Not equal. bit0: {bit0}",
245 );
246 }
247 }
248
249 #[test]
250 fn test_adjacent_grain_range() {
251 let base = mk_addr(0x1000);
252 for bit0 in 1..BITS_IN_BYTE {
253 for bit1 in 1..BITS_IN_BYTE {
254 let result = break_bit_range_wrapped(base, bit0, base + 1usize, bit1);
255 assert_eq!(
256 result,
257 vec![
258 BitByteRange::BitsInByte {
259 addr: base,
260 bit_start: bit0 as u8,
261 bit_end: BITS_IN_BYTE as u8,
262 },
263 BitByteRange::BitsInByte {
264 addr: base + 1usize,
265 bit_start: 0,
266 bit_end: bit1 as u8,
267 },
268 ],
269 "Not equal. bit0: {bit0}, bit1: {bit1}",
270 );
271 }
272 }
273 }
274
275 #[test]
276 fn test_left_and_whole_range() {
277 let base = mk_addr(0x1000);
278 for bit0 in 1..BITS_IN_BYTE {
279 for byte1 in 2usize..8 {
280 let result = break_bit_range_wrapped(base, bit0, base + byte1, 0);
281 assert_eq!(
282 result,
283 vec![
284 BitByteRange::BitsInByte {
285 addr: base,
286 bit_start: bit0 as u8,
287 bit_end: BITS_IN_BYTE as u8,
288 },
289 BitByteRange::Bytes {
290 start: base + 1usize,
291 end: base + byte1,
292 },
293 ],
294 "Not equal. bit0: {bit0}, byte1: {byte1}",
295 );
296 }
297 }
298 }
299
300 #[test]
301 fn test_whole_and_right_range() {
302 let base = mk_addr(0x1000);
303 for byte0 in 1..8 {
304 for bit1 in 1..BITS_IN_BYTE {
305 let result = break_bit_range_wrapped(base - byte0, 0, base, bit1);
306 assert_eq!(
307 result,
308 vec![
309 BitByteRange::Bytes {
310 start: base - byte0,
311 end: base,
312 },
313 BitByteRange::BitsInByte {
314 addr: base,
315 bit_start: 0,
316 bit_end: bit1 as u8,
317 },
318 ],
319 "Not equal. byte0: {byte0}, bit1: {bit1}",
320 );
321 }
322 }
323 }
324
325 #[test]
326 fn test_whole_range() {
327 let base = mk_addr(0x1000);
328 let result = break_bit_range_wrapped(base, 0, base + 42usize, 0);
329 assert_eq!(
330 result,
331 vec![BitByteRange::Bytes {
332 start: base,
333 end: base + 42usize,
334 },],
335 );
336 }
337
338 #[test]
339 fn test_left_whole_right_range() {
340 let base0 = mk_addr(0x1000);
341 let base1 = mk_addr(0x2000);
342
343 for bit0 in 1..BITS_IN_BYTE {
344 for bit1 in 1..BITS_IN_BYTE {
345 let result = break_bit_range_wrapped(base0 - 1usize, bit0, base1, bit1);
346 assert_eq!(
347 result,
348 vec![
349 BitByteRange::BitsInByte {
350 addr: base0 - 1usize,
351 bit_start: bit0 as u8,
352 bit_end: BITS_IN_BYTE as u8,
353 },
354 BitByteRange::Bytes {
355 start: base0,
356 end: base1,
357 },
358 BitByteRange::BitsInByte {
359 addr: base1,
360 bit_start: 0,
361 bit_end: bit1 as u8,
362 },
363 ],
364 "Not equal. bit0: {bit0}, bit1: {bit1}",
365 );
366 }
367 }
368 }
369}