use std::sync::atomic::{AtomicU64, Ordering};
use crate::bitmap::{Bitmap, RefSlice, WithBitmapSlice};
#[cfg(feature = "backend-mmap")]
use crate::mmap::NewBitmap;
#[derive(Debug)]
pub struct AtomicBitmap {
map: Vec<AtomicU64>,
size: usize,
page_size: usize,
}
#[allow(clippy::len_without_is_empty)]
impl AtomicBitmap {
pub fn new(byte_size: usize, page_size: usize) -> Self {
let mut num_pages = byte_size / page_size;
if byte_size % page_size > 0 {
num_pages += 1;
}
let map_size = num_pages / 64 + 1;
let map: Vec<AtomicU64> = (0..map_size).map(|_| AtomicU64::new(0)).collect();
AtomicBitmap {
map,
size: num_pages,
page_size,
}
}
pub fn is_bit_set(&self, index: usize) -> bool {
if index < self.size {
(self.map[index >> 6].load(Ordering::Acquire) & (1 << (index & 63))) != 0
} else {
false
}
}
pub fn is_addr_set(&self, addr: usize) -> bool {
self.is_bit_set(addr / self.page_size)
}
pub fn set_addr_range(&self, start_addr: usize, len: usize) {
if len == 0 {
return;
}
let first_bit = start_addr / self.page_size;
let last_bit = start_addr.saturating_add(len - 1) / self.page_size;
for n in first_bit..=last_bit {
if n >= self.size {
break;
}
self.map[n >> 6].fetch_or(1 << (n & 63), Ordering::SeqCst);
}
}
pub fn len(&self) -> usize {
self.size
}
pub fn get_and_reset(&self) -> Vec<u64> {
self.map
.iter()
.map(|u| u.fetch_and(0, Ordering::SeqCst))
.collect()
}
pub fn reset(&self) {
for it in self.map.iter() {
it.store(0, Ordering::Release);
}
}
}
impl Clone for AtomicBitmap {
fn clone(&self) -> Self {
let map = self
.map
.iter()
.map(|i| i.load(Ordering::Acquire))
.map(AtomicU64::new)
.collect();
AtomicBitmap {
map,
size: self.size,
page_size: self.page_size,
}
}
}
impl<'a> WithBitmapSlice<'a> for AtomicBitmap {
type S = RefSlice<'a, Self>;
}
impl Bitmap for AtomicBitmap {
fn mark_dirty(&self, offset: usize, len: usize) {
self.set_addr_range(offset, len)
}
fn dirty_at(&self, offset: usize) -> bool {
self.is_addr_set(offset)
}
fn slice_at(&self, offset: usize) -> <Self as WithBitmapSlice>::S {
RefSlice::new(self, offset)
}
}
impl Default for AtomicBitmap {
fn default() -> Self {
AtomicBitmap::new(0, 0x1000)
}
}
#[cfg(feature = "backend-mmap")]
impl NewBitmap for AtomicBitmap {
fn with_len(len: usize) -> Self {
let page_size;
#[cfg(unix)]
{
page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) };
}
#[cfg(windows)]
{
use winapi::um::sysinfoapi::{GetSystemInfo, LPSYSTEM_INFO, SYSTEM_INFO};
let mut sysinfo: SYSTEM_INFO = unsafe { std::mem::zeroed() };
unsafe { GetSystemInfo(&mut sysinfo as LPSYSTEM_INFO) };
page_size = sysinfo.dwPageSize;
}
AtomicBitmap::new(len, usize::try_from(page_size).unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitmap::tests::test_bitmap;
#[test]
fn test_bitmap_basic() {
let a = AtomicBitmap::new(1025, 128);
assert_eq!(a.len(), 9);
let b = AtomicBitmap::new(1024, 128);
assert_eq!(b.len(), 8);
b.set_addr_range(128, 129);
assert!(!b.is_addr_set(0));
assert!(b.is_addr_set(128));
assert!(b.is_addr_set(256));
assert!(!b.is_addr_set(384));
let copy_b = b.clone();
assert!(copy_b.is_addr_set(256));
assert!(!copy_b.is_addr_set(384));
b.reset();
assert!(!b.is_addr_set(128));
assert!(!b.is_addr_set(256));
assert!(!b.is_addr_set(384));
b.set_addr_range(128, 129);
let v = b.get_and_reset();
assert!(!b.is_addr_set(128));
assert!(!b.is_addr_set(256));
assert!(!b.is_addr_set(384));
assert_eq!(v.len(), 1);
assert_eq!(v[0], 0b110);
}
#[test]
fn test_bitmap_out_of_range() {
let b = AtomicBitmap::new(1024, 1);
b.set_addr_range(768, 512);
assert!(b.is_addr_set(768));
assert!(!b.is_addr_set(1024));
assert!(!b.is_addr_set(1152));
}
#[test]
fn test_bitmap_impl() {
let b = AtomicBitmap::new(0x2000, 128);
test_bitmap(&b);
}
}