#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![allow(clippy::missing_inline_in_public_items)]
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::integer_arithmetic)]
#![allow(clippy::integer_division)]
#![allow(clippy::implicit_return)]
mod iter;
mod fmt;
pub mod aligned;
pub mod packed;
pub type AlignedUnthBuf = UnthBuf<aligned::AlignedLayout>;
pub type PackedUnthBuf = UnthBuf<packed::PackedLayout>;
pub use iter::UnthBufIter;
mod tests;
pub(crate) const BITS_PER_CELL: u8 = usize::BITS as u8;
pub type Bits = core::num::NonZeroU8;
#[derive(Clone)]
pub struct UnthBuf<CL: CellLayout> {
pub(crate) capacity: usize,
pub(crate) data: Box<[usize]>,
pub(crate) bits: Bits,
pub(crate) mask: usize,
pub(crate) elpc: u8,
pub(crate) cell_layout: core::marker::PhantomData<CL>
}
pub trait CellLayout: Sized + Clone + Copy {
type Location;
fn get_cell_count(capacity: usize, bits: Bits) -> usize;
fn get_exact_bit_count(buf: &UnthBuf<Self>) -> usize;
fn location_of(buf: &UnthBuf<Self>, index: usize) -> Self::Location;
unsafe fn set_unchecked(buf: &mut UnthBuf<Self>, index: usize, value: usize);
unsafe fn get_unchecked(buf: &UnthBuf<Self>, index: usize) -> usize;
}
impl<CL: CellLayout> UnthBuf<CL> {
pub fn new_from_sized_iter<I>(bits: Bits, iter: I) -> Self
where I: Iterator<Item = usize> + core::iter::ExactSizeIterator
{
let mut new = Self::new(bits, iter.len());
new.fill_from(iter);
new
}
pub fn new_from_capacity_and_iter<I>(bits: Bits, capacity: usize, iter: I) -> Self
where I: Iterator<Item = usize>
{
let mut new = Self::new(bits, capacity);
new.fill_from(iter);
new
}
pub fn new_with_default(bits: Bits, capacity: usize, default_value: usize) -> Self {
let mut new = Self::new(bits, capacity);
assert!(new.can_element_fit(default_value), "given default value (0x{default_value:X}) does not fit into {bits} bits");
if default_value != 0 {
new.fill_with(default_value);
}
new
}
pub fn new(bits: Bits, capacity: usize) -> Self {
assert!(capacity != 0, "cannot create buffer of 0 capacity");
let size = CL::get_cell_count(capacity, bits);
let data = vec![0; size].into_boxed_slice();
let mask = Self::mask_from_bits(bits.get());
let elpc = BITS_PER_CELL.checked_div(bits.get()).unwrap_or(0);
Self {
capacity,
data,
bits,
mask,
elpc,
cell_layout: core::marker::PhantomData,
}
}
#[inline(always)]
pub fn get_capacity(&self) -> usize {
self.capacity
}
#[inline(always)]
pub fn get_indices(&self) -> core::ops::Range<usize> {
0..self.capacity
}
#[inline(always)]
pub fn get_total_bit_count(&self) -> usize {
self.data.len() * BITS_PER_CELL as usize
}
#[inline(always)]
pub fn get_exact_bit_count(&self) -> usize {
CL::get_exact_bit_count(self)
}
#[inline(always)]
pub fn get_padding_bit_count(&self) -> usize {
self.get_total_bit_count() - CL::get_exact_bit_count(self)
}
#[inline(always)]
pub fn raw(&self) -> &[usize] {
&self.data
}
#[inline(always)]
pub fn raw_mut(&mut self) -> &mut [usize] {
&mut self.data
}
#[inline(always)]
pub fn raw_len(&self) -> usize {
self.data.len()
}
#[inline(always)]
pub fn raw_byte_len(&self) -> usize {
self.data.len() * (usize::BITS/8) as usize
}
#[inline(always)]
pub fn can_element_fit(&self, value: usize) -> bool {
(value & self.mask) == value
}
#[inline(always)]
pub fn get_element_mask(&self) -> usize {
self.mask
}
#[inline(always)]
pub fn get_element_bits(&self) -> Bits {
self.bits
}
#[inline(always)]
pub fn is_index(&self, index: usize) -> bool {
index < self.capacity
}
#[inline(always)]
pub fn is_cell(&self, cell: usize) -> bool {
cell < self.data.len()
}
pub fn location_of(&self, index: usize) -> CL::Location {
CL::location_of(self, index)
}
pub fn fill_with(&mut self, value: usize) {
assert!(self.can_element_fit(value), "given value does not fit");
if value == 0 {
return self.fill_with_default()
}
for index in self.get_indices() {
unsafe {self.set_unchecked(index, value)};
}
}
pub fn fill_with_default(&mut self) {
self.data.fill(0);
}
pub fn fill_from(&mut self, iter: impl Iterator<Item = usize>) {
for (index, value) in self.get_indices().zip(iter).fuse() {
unsafe {self.set_unchecked(index, value)};
}
}
#[inline]
pub fn set(&mut self, index: usize, value: usize) -> Result<(),&'static str> {
if !self.can_element_fit(value) {return Err("value does not fit")}
if !self.is_index(index) {return Err("index is out-of-bounds")}
unsafe {self.set_unchecked(index, value);}
Ok(())
}
#[inline(always)]
pub unsafe fn set_unchecked(&mut self, index: usize, value: usize) {
CL::set_unchecked(self, index, value);
}
#[inline]
pub fn get(&self, index: usize) -> Option<usize> {
if !self.is_index(index) {return None}
Some(unsafe {self.get_unchecked(index)})
}
#[inline(always)]
pub unsafe fn get_unchecked(&self, index: usize) -> usize {
CL::get_unchecked(self, index)
}
pub(crate) fn mask_from_bits(bits: u8) -> usize {
if bits == 0 {return 0}
if bits as u32 == usize::BITS {return usize::MAX}
if bits as u32 > usize::BITS {
panic!("cannot store {bits} bits in usize ({})", usize::BITS)
}
2_usize.pow(bits.into()) - 1
}
}