use super::sparse::{check_consistency_raw, contains_raw, insert_raw, remove_raw};
use crate::{Libc, PtrNonNull, Slice, c_void, is};
use crate::{SparseSetError, SparseSetResult};
use core::slice::Iter as IterSlice;
#[doc = crate::_tags!(data_structure platform set)]
#[doc = crate::_doc_meta!{location("data/store/key")}]
#[derive(Debug)]
pub struct LinuxSparseSet {
dense: LinuxMmapUsize,
sparse: LinuxMmapUsize,
count: usize,
}
#[rustfmt::skip]
impl LinuxSparseSet {
pub fn new(dense: usize, sparse: usize) -> SparseSetResult<Self> {
Ok(Self {
dense: LinuxMmapUsize::new_zeroed(dense, false)?,
sparse: LinuxMmapUsize::new_zeroed(sparse, false)?,
count: 0,
})
}
pub fn new_noreserve(dense: usize, sparse: usize) -> SparseSetResult<Self> {
Ok(Self {
dense: LinuxMmapUsize::new_zeroed(dense, true)?,
sparse: LinuxMmapUsize::new_zeroed(sparse, true)?,
count: 0,
})
}
#[must_use]
pub const fn capacity(&self) -> usize { self.dense.len() }
#[must_use]
pub const fn universe(&self) -> usize { self.sparse.len() }
#[must_use]
pub const fn len(&self) -> usize { self.count }
#[must_use]
pub const fn is_empty(&self) -> bool { self.count == 0 }
#[must_use]
pub const fn is_full(&self) -> bool { self.count == self.dense.len() }
pub const fn clear(&mut self) { self.count = 0; }
#[must_use]
pub fn as_slice(&self) -> &[usize] { &self.dense.as_slice()[..self.count] }
pub fn iter(&self) -> IterSlice<'_, usize> { self.as_slice().iter() }
#[must_use]
pub fn contains(&self, value: usize) -> bool {
contains_raw(self.dense.as_slice(), self.sparse.as_slice(), self.count, value)
}
pub fn insert(&mut self, value: usize) -> SparseSetResult<bool> {
insert_raw(self.dense.as_mut_slice(), self.sparse.as_mut_slice(), &mut self.count, value)
}
pub fn remove(&mut self, value: usize) -> SparseSetResult<bool> {
remove_raw(self.dense.as_mut_slice(), self.sparse.as_mut_slice(), &mut self.count, value)
}
pub fn check_consistency(&self) {
check_consistency_raw(self.dense.as_slice(), self.sparse.as_slice(), self.count);
}
}
#[derive(Debug)]
struct LinuxMmapUsize {
ptr: PtrNonNull<usize>,
len: usize,
bytes: usize,
}
impl LinuxMmapUsize {
fn new_zeroed(len: usize, noreserve: bool) -> SparseSetResult<Self> {
let bytes =
len.checked_mul(size_of::<usize>()).ok_or(SparseSetError::AllocationTooLarge)?;
is! { bytes == 0, return Ok(Self { ptr: PtrNonNull::dangling(), len, bytes }) }
let mut flags = Libc::MAP_PRIVATE | Libc::MAP_ANONYMOUS;
is! { noreserve, flags |= Libc::MAP_NORESERVE }
let raw = unsafe {
Libc::mmap(
core::ptr::null_mut(),
bytes,
Libc::PROT_READ | Libc::PROT_WRITE,
flags,
-1,
0,
)
};
is! { Libc::is_map_failed(raw), return Err(SparseSetError::MapFailed) }
let ptr = PtrNonNull::new(raw.cast::<usize>()).ok_or(SparseSetError::MapFailed)?;
Ok(Self { ptr, len, bytes })
}
const fn len(&self) -> usize {
self.len
}
fn as_slice(&self) -> &[usize] {
unsafe { Slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
fn as_mut_slice(&mut self) -> &mut [usize] {
unsafe { Slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
impl Drop for LinuxMmapUsize {
fn drop(&mut self) {
if self.bytes != 0 {
let _ = unsafe { Libc::munmap(self.ptr.as_ptr().cast::<c_void>(), self.bytes) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn linux_sparse_set_basic() {
let mut set = LinuxSparseSet::new(5, 10_000).unwrap();
assert_eq!(set.insert(3), Ok(true));
assert_eq!(set.insert(5), Ok(true));
assert_eq!(set.insert(5), Ok(false));
assert!(set.contains(3));
assert!(set.contains(5));
assert!(!set.contains(2));
assert!(!set.contains(11_000));
assert_eq!(set.remove(3), Ok(true));
assert!(!set.contains(3));
assert_eq!(set.remove(500), Ok(false));
set.check_consistency();
}
#[test]
fn linux_sparse_set_clear() {
let mut set = LinuxSparseSet::new(8, 1024).unwrap();
assert_eq!(set.insert(10), Ok(true));
assert_eq!(set.insert(20), Ok(true));
set.clear();
assert!(!set.contains(10));
assert!(!set.contains(20));
assert_eq!(set.len(), 0);
assert_eq!(set.insert(20), Ok(true));
assert!(set.contains(20));
set.check_consistency();
}
}