use std::sync::RwLock;
pub struct VisibilityMap {
bits: RwLock<Vec<u64>>,
}
impl VisibilityMap {
pub fn new() -> Self {
Self {
bits: RwLock::new(Vec::new()),
}
}
pub fn with_capacity(pages: u32) -> Self {
let words = (pages as usize).div_ceil(64);
Self {
bits: RwLock::new(vec![0u64; words]),
}
}
pub fn is_all_visible(&self, page: u32) -> bool {
let bits = self.bits.read().expect("vmap rwlock poisoned");
let word_idx = page as usize / 64;
if word_idx >= bits.len() {
return false;
}
let bit_idx = page as usize % 64;
(bits[word_idx] >> bit_idx) & 1 == 1
}
pub fn mark_all_visible(&self, page: u32) {
let mut bits = self.bits.write().expect("vmap rwlock poisoned");
let word_idx = page as usize / 64;
if word_idx >= bits.len() {
bits.resize(word_idx + 1, 0);
}
let bit_idx = page as usize % 64;
bits[word_idx] |= 1u64 << bit_idx;
}
pub fn clear_all_visible(&self, page: u32) {
let mut bits = self.bits.write().expect("vmap rwlock poisoned");
let word_idx = page as usize / 64;
if word_idx >= bits.len() {
return;
}
let bit_idx = page as usize % 64;
bits[word_idx] &= !(1u64 << bit_idx);
}
pub fn all_visible_count(&self) -> u64 {
let bits = self.bits.read().expect("vmap rwlock poisoned");
bits.iter().map(|w| w.count_ones() as u64).sum()
}
pub fn capacity_pages(&self) -> u64 {
let bits = self.bits.read().expect("vmap rwlock poisoned");
(bits.len() as u64) * 64
}
pub fn clear(&self) {
let mut bits = self.bits.write().expect("vmap rwlock poisoned");
for w in bits.iter_mut() {
*w = 0;
}
}
pub fn mark_range_visible(&self, start: u32, end: u32) {
if start >= end {
return;
}
let mut bits = self.bits.write().expect("vmap rwlock poisoned");
let last_word = (end as usize - 1) / 64;
if last_word >= bits.len() {
bits.resize(last_word + 1, 0);
}
for page in start..end {
let word_idx = page as usize / 64;
let bit_idx = page as usize % 64;
bits[word_idx] |= 1u64 << bit_idx;
}
}
pub fn snapshot(&self, limit_pages: u32) -> Vec<(u32, bool)> {
let bits = self.bits.read().expect("vmap rwlock poisoned");
let mut out = Vec::with_capacity(limit_pages as usize);
for page in 0..limit_pages {
let word_idx = page as usize / 64;
let visible = if word_idx < bits.len() {
let bit_idx = page as usize % 64;
(bits[word_idx] >> bit_idx) & 1 == 1
} else {
false
};
out.push((page, visible));
}
out
}
}
impl Default for VisibilityMap {
fn default() -> Self {
Self::new()
}
}
pub fn page_of(entity_id: u64, rows_per_page: u32) -> u32 {
if rows_per_page == 0 {
return 0;
}
(entity_id / rows_per_page as u64) as u32
}
pub fn mark_dirty_after_write(vmap: &VisibilityMap, entity_id: u64, rows_per_page: u32) {
let page = page_of(entity_id, rows_per_page);
vmap.clear_all_visible(page);
}
pub fn mark_clean_after_gc(vmap: &VisibilityMap, start_page: u32, end_page: u32) {
vmap.mark_range_visible(start_page, end_page);
}