use crate::{
BitStore,
Unsigned,
};
#[derive(PartialEq, Eq)]
enum BitSlicePtr<T: Unsigned = usize> {
Mutable(*mut T),
Const(*const T),
}
#[doc = include_str!("../docs/slice.md")]
#[derive(PartialEq, Eq)]
pub struct BitSlice<'a, Word: Unsigned> {
m_store: BitSlicePtr<Word>,
m_offset: u32,
m_len: usize,
m_words: usize,
_marker: core::marker::PhantomData<&'a Word>,
}
impl<'a, Word: Unsigned> BitSlice<'a, Word> {
#[inline]
pub fn new(words: &'a [Word], start: usize, end: usize) -> Self {
debug_assert!(!words.is_empty(), "cannot create a bit-slice from an empty vector of words");
debug_assert!(start < end, "start: {start} should be < end: {end}");
debug_assert!(end <= words.len() * Word::UBITS, "bit range extends beyond the end of the vector of words");
let m_len = end - start;
let m_words = Word::words_needed(m_len);
let (m_store, m_offset) = Word::index_and_offset(start);
Self {
m_store: BitSlicePtr::Const(unsafe { words.as_ptr().add(m_store) }),
m_offset,
m_len,
m_words,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn new_mut(words: &'a mut [Word], start: usize, end: usize) -> Self {
debug_assert!(!words.is_empty(), "cannot create a bit-slice from an empty vector of words");
debug_assert!(start < end, "start: {start} should be < end: {end}");
debug_assert!(end <= words.len() * Word::UBITS, "bit range extends beyond the end of the vector of words");
let m_len = end - start;
let m_words = Word::words_needed(m_len);
let (m_store, m_offset) = Word::index_and_offset(start);
Self {
m_store: BitSlicePtr::Mutable(unsafe { words.as_mut_ptr().add(m_store) }),
m_offset,
m_len,
m_words,
_marker: core::marker::PhantomData,
}
}
}
impl<Word: Unsigned> BitStore<Word> for BitSlice<'_, Word> {
#[inline]
fn len(&self) -> usize { self.m_len }
fn store(&self) -> &[Word] {
let backing_words = Word::words_needed(self.m_len + self.m_offset as usize);
unsafe {
match self.m_store {
BitSlicePtr::Const(ptr) => std::slice::from_raw_parts(ptr, backing_words),
BitSlicePtr::Mutable(ptr) => std::slice::from_raw_parts(ptr.cast_const(), backing_words),
}
}
}
fn store_mut(&mut self) -> &mut [Word] {
let backing_words = Word::words_needed(self.m_len + self.m_offset as usize);
unsafe {
match self.m_store {
BitSlicePtr::Mutable(ptr) => std::slice::from_raw_parts_mut(ptr, backing_words),
BitSlicePtr::Const(_) => panic!("cannot mutably access data of immutable BitSlice"),
}
}
}
fn offset(&self) -> u32 { self.m_offset }
#[inline]
fn words(&self) -> usize { self.m_words }
#[inline]
fn word(&self, i: usize) -> Word {
let (u0_bits, u1_bits) = self.recipe_for_word(i);
let u0_ptr: *const Word = match self.m_store {
BitSlicePtr::Const(ptr) => unsafe { ptr.add(i) },
BitSlicePtr::Mutable(ptr) => unsafe { ptr.add(i) },
};
let mut result = Word::ZERO;
let u0 = unsafe { *u0_ptr };
result.replace_bits(0..u0_bits, u0.unbounded_shr(self.m_offset));
if u1_bits != 0 {
let u1 = unsafe { *u0_ptr.add(1) };
result.replace_bits(u0_bits..u0_bits + u1_bits, u1.unbounded_shl(u0_bits));
}
result
}
#[inline]
fn set_word(&mut self, i: usize, value: Word) {
let (u0_bits, u1_bits) = self.recipe_for_word(i);
let u0_ptr: *mut Word = match self.m_store {
BitSlicePtr::Mutable(ptr) => unsafe { ptr.add(i) },
BitSlicePtr::Const(_) => {
panic!("This should never happen -- cannot set word on immutable slice");
},
};
let shift = self.m_offset;
let u0 = unsafe { &mut *u0_ptr };
u0.replace_bits(shift..shift + u0_bits, value.unbounded_shl(shift));
if u1_bits != 0 {
let u1 = unsafe { &mut *u0_ptr.add(1) };
u1.replace_bits(0..u1_bits, value.unbounded_shr(u0_bits));
}
}
}
impl<Word: Unsigned> BitSlice<'_, Word> {
#[inline]
fn recipe_for_word(&self, i: usize) -> (u32, u32) {
debug_assert!(i < self.m_words, "Index {i} should be less than {}", self.m_words);
let mut u0_bits = Word::BITS - self.m_offset;
let mut u1_bits = self.m_offset;
#[allow(clippy::cast_possible_truncation)]
if i == self.m_words - 1 {
let last = self.m_offset as usize + self.m_len - 1;
let last_offset = (last % Word::UBITS) as u32;
if last_offset < self.m_offset {
u1_bits = last_offset + 1;
}
else {
u0_bits = last_offset - self.m_offset + 1;
u1_bits = 0;
}
}
(u0_bits, u1_bits)
}
}