Skip to content

Commit 78934ae

Browse files
simongdaviesludfjig
authored andcommitted
Add bitmap module with helper functions for page management
Signed-off-by: Simon Davies <[email protected]>
1 parent a8dad96 commit 78934ae

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
Copyright 2025 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
use std::cmp::Ordering;
17+
18+
use hyperlight_common::mem::{PAGE_SIZE_USIZE, PAGES_IN_BLOCK};
19+
20+
use crate::{Result, log_then_return};
21+
22+
// Contains various helper functions for dealing with bitmaps.
23+
24+
/// Returns a new bitmap of pages. If `init_dirty` is true, all pages are marked as dirty, otherwise all pages are clean.
25+
/// Will return an error if given size is 0.
26+
pub fn new_page_bitmap(size_in_bytes: usize, init_dirty: bool) -> Result<Vec<u64>> {
27+
if size_in_bytes == 0 {
28+
log_then_return!("Tried to create a bitmap with size 0.");
29+
}
30+
let num_pages = size_in_bytes.div_ceil(PAGE_SIZE_USIZE);
31+
let num_blocks = num_pages.div_ceil(PAGES_IN_BLOCK);
32+
match init_dirty {
33+
false => Ok(vec![0; num_blocks]),
34+
true => {
35+
let mut bitmap = vec![!0u64; num_blocks]; // all pages are dirty
36+
let num_unused_bits = num_blocks * PAGES_IN_BLOCK - num_pages;
37+
// set the unused bits to 0, could cause problems otherwise
38+
#[allow(clippy::unwrap_used)]
39+
let last_block = bitmap.last_mut().unwrap(); // unwrap is safe since size_in_bytes>0
40+
*last_block >>= num_unused_bits;
41+
Ok(bitmap)
42+
}
43+
}
44+
}
45+
46+
/// Returns the union (bitwise OR) of two bitmaps. The resulting bitmap will have the same length
47+
/// as the longer of the two input bitmaps.
48+
pub(crate) fn bitmap_union(bitmap: &[u64], other_bitmap: &[u64]) -> Vec<u64> {
49+
let min_len = bitmap.len().min(other_bitmap.len());
50+
let max_len = bitmap.len().max(other_bitmap.len());
51+
52+
let mut result = vec![0; max_len];
53+
54+
for i in 0..min_len {
55+
result[i] = bitmap[i] | other_bitmap[i];
56+
}
57+
58+
match bitmap.len().cmp(&other_bitmap.len()) {
59+
Ordering::Greater => {
60+
result[min_len..].copy_from_slice(&bitmap[min_len..]);
61+
}
62+
Ordering::Less => {
63+
result[min_len..].copy_from_slice(&other_bitmap[min_len..]);
64+
}
65+
Ordering::Equal => {}
66+
}
67+
68+
result
69+
}
70+
71+
// Used as a helper struct to implement an iterator on.
72+
struct SetBitIndices<'a> {
73+
bitmap: &'a [u64],
74+
block_index: usize, // one block is 1 u64, which is 64 pages
75+
current: u64, // the current block we are iterating over, or 0 if first iteration
76+
}
77+
78+
/// Iterates over the zero-based indices of the set bits in the given bitmap.
79+
pub(crate) fn bit_index_iterator(bitmap: &[u64]) -> impl Iterator<Item = usize> + '_ {
80+
SetBitIndices {
81+
bitmap,
82+
block_index: 0,
83+
current: 0,
84+
}
85+
}
86+
87+
impl Iterator for SetBitIndices<'_> {
88+
type Item = usize;
89+
90+
fn next(&mut self) -> Option<Self::Item> {
91+
while self.current == 0 {
92+
// will always enter this on first iteration because current is initialized to 0
93+
if self.block_index >= self.bitmap.len() {
94+
// no more blocks to iterate over
95+
return None;
96+
}
97+
self.current = self.bitmap[self.block_index];
98+
self.block_index += 1;
99+
}
100+
let trailing_zeros = self.current.trailing_zeros();
101+
self.current &= self.current - 1; // Clear the least significant set bit
102+
Some((self.block_index - 1) * 64 + trailing_zeros as usize) // block_index guaranteed to be > 0 at this point
103+
}
104+
}
105+
106+
#[cfg(test)]
107+
mod tests {
108+
use hyperlight_common::mem::PAGE_SIZE_USIZE;
109+
110+
use crate::Result;
111+
use crate::mem::bitmap::{bit_index_iterator, bitmap_union, new_page_bitmap};
112+
113+
#[test]
114+
fn new_page_bitmap_test() -> Result<()> {
115+
let bitmap = new_page_bitmap(1, false)?;
116+
assert_eq!(bitmap.len(), 1);
117+
assert_eq!(bitmap[0], 0);
118+
119+
let bitmap = new_page_bitmap(1, true)?;
120+
assert_eq!(bitmap.len(), 1);
121+
assert_eq!(bitmap[0], 1);
122+
123+
let bitmap = new_page_bitmap(32 * PAGE_SIZE_USIZE, false)?;
124+
assert_eq!(bitmap.len(), 1);
125+
assert_eq!(bitmap[0], 0);
126+
127+
let bitmap = new_page_bitmap(32 * PAGE_SIZE_USIZE, true)?;
128+
assert_eq!(bitmap.len(), 1);
129+
assert_eq!(bitmap[0], 0x0000_0000_FFFF_FFFF);
130+
Ok(())
131+
}
132+
133+
#[test]
134+
fn page_iterator() {
135+
let data = vec![0b1000010100, 0b01, 0b100000000000000011];
136+
let mut iter = bit_index_iterator(&data);
137+
assert_eq!(iter.next(), Some(2));
138+
assert_eq!(iter.next(), Some(4));
139+
assert_eq!(iter.next(), Some(9));
140+
assert_eq!(iter.next(), Some(64));
141+
assert_eq!(iter.next(), Some(128));
142+
assert_eq!(iter.next(), Some(129));
143+
assert_eq!(iter.next(), Some(145));
144+
assert_eq!(iter.next(), None);
145+
146+
let data_2 = vec![0, 0, 0];
147+
let mut iter_2 = bit_index_iterator(&data_2);
148+
assert_eq!(iter_2.next(), None);
149+
150+
let data_3 = vec![0, 0, 0b1, 1 << 63];
151+
let mut iter_3 = bit_index_iterator(&data_3);
152+
assert_eq!(iter_3.next(), Some(128));
153+
assert_eq!(iter_3.next(), Some(255));
154+
assert_eq!(iter_3.next(), None);
155+
156+
let data_4 = vec![];
157+
let mut iter_4 = bit_index_iterator(&data_4);
158+
assert_eq!(iter_4.next(), None);
159+
}
160+
161+
#[test]
162+
fn union() -> Result<()> {
163+
let a = 0b1000010100;
164+
let b = 0b01;
165+
let c = 0b100000000000000011;
166+
let d = 0b101010100000011000000011;
167+
let e = 0b000000000000001000000000000000000000;
168+
let f = 0b100000000000000001010000000001010100000000000;
169+
let bitmap = vec![a, b, c];
170+
let other_bitmap = vec![d, e, f];
171+
let union = bitmap_union(&bitmap, &other_bitmap);
172+
assert_eq!(union, vec![a | d, b | e, c | f]);
173+
174+
// different length
175+
let union = bitmap_union(&[a], &[d, e, f]);
176+
assert_eq!(union, vec![a | d, e, f]);
177+
178+
let union = bitmap_union(&[a, b, c], &[d]);
179+
assert_eq!(union, vec![a | d, b, c]);
180+
181+
let union = bitmap_union(&[], &[d, e]);
182+
assert_eq!(union, vec![d, e]);
183+
184+
let union = bitmap_union(&[a, b, c], &[]);
185+
assert_eq!(union, vec![a, b, c]);
186+
187+
let union = bitmap_union(&[], &[]);
188+
let empty: Vec<u64> = vec![];
189+
assert_eq!(union, empty);
190+
191+
Ok(())
192+
}
193+
}

0 commit comments

Comments
 (0)