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}