use std::ops::Not;
use bitvec::view::BitView;
use crate::BitBuffer;
use crate::BufferAllocatorRef;
use crate::BufferMut;
use crate::ByteBufferMut;
use crate::bit::collect_bool_words;
use crate::bit::get_bit_unchecked;
use crate::bit::ops;
use crate::bit::pack::collect_bool_words_multiversioned;
use crate::bit::set_bit_unchecked;
use crate::bit::unset_bit_unchecked;
use crate::buffer_mut;
#[allow(clippy::inline_always)]
#[inline(always)]
pub(crate) fn fill_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
if start_bit >= end_bit {
return;
}
debug_assert!(end_bit <= slice.len() * 8);
let word_bits = slice.len() / 8 * 64;
if end_bit <= word_bits {
fill_word_bits(slice, start_bit, end_bit, value);
return;
}
if start_bit < word_bits {
fill_word_bits(slice, start_bit, word_bits, value);
}
fill_byte_bits(slice, start_bit.max(word_bits), end_bit, value);
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn fill_word_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
let words = slice.as_chunks_mut::<8>().0;
let (start_word, start_rem) = (start_bit / 64, start_bit % 64);
let (end_word, end_rem) = (end_bit / 64, end_bit % 64);
if start_word == end_word {
fill_word_mask(
&mut words[start_word],
low_bits(end_rem) & !low_bits(start_rem),
value,
);
return;
}
fill_word_mask(&mut words[start_word], !low_bits(start_rem), value);
words[start_word + 1..end_word].fill(if value { [0xFF; 8] } else { [0x00; 8] });
if end_rem != 0 {
fill_word_mask(&mut words[end_word], low_bits(end_rem), value);
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
const fn low_bits(n: usize) -> u64 {
(1u64 << n) - 1
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn fill_word_mask(word: &mut [u8; 8], mask: u64, value: bool) {
let current = u64::from_le_bytes(*word);
let updated = if value {
current | mask
} else {
current & !mask
};
*word = updated.to_le_bytes();
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn fill_byte_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
let fill_byte: u8 = if value { 0xFF } else { 0x00 };
let start_byte = start_bit / 8;
let start_rem = start_bit % 8;
let end_byte = end_bit / 8;
let end_rem = end_bit % 8;
if start_byte == end_byte {
let mask = ((1u8 << (end_rem - start_rem)) - 1) << start_rem;
if value {
slice[start_byte] |= mask;
} else {
slice[start_byte] &= !mask;
}
} else {
if start_rem != 0 {
let mask = !((1u8 << start_rem) - 1);
if value {
slice[start_byte] |= mask;
} else {
slice[start_byte] &= !mask;
}
}
let fill_start = if start_rem != 0 {
start_byte + 1
} else {
start_byte
};
if fill_start < end_byte {
slice[fill_start..end_byte].fill(fill_byte);
}
if end_rem != 0 {
let mask = (1u8 << end_rem) - 1;
if value {
slice[end_byte] |= mask;
} else {
slice[end_byte] &= !mask;
}
}
}
}
#[derive(Debug, Clone)]
pub struct BitBufferMut {
buffer: ByteBufferMut,
offset: usize,
len: usize,
}
impl BitBufferMut {
#[inline]
pub fn from_buffer(buffer: ByteBufferMut, offset: usize, len: usize) -> Self {
assert!(
len.saturating_add(offset) <= buffer.len().saturating_mul(8),
"Buffer len {} is too short for offset {offset} and length {len}",
buffer.len()
);
Self {
buffer,
offset,
len,
}
}
pub fn copy_from(bit_buffer: &BitBuffer) -> Self {
Self::copy_from_in(bit_buffer, bit_buffer.inner().allocator().clone())
}
pub fn copy_from_in(bit_buffer: &BitBuffer, allocator: BufferAllocatorRef) -> Self {
Self {
buffer: ByteBufferMut::copy_from_in(bit_buffer.inner(), allocator),
offset: bit_buffer.offset(),
len: bit_buffer.len(),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_in(capacity, BufferAllocatorRef::statically_allocated())
}
#[inline]
pub fn with_capacity_in(capacity: usize, allocator: BufferAllocatorRef) -> Self {
Self {
buffer: BufferMut::with_capacity_in(capacity.div_ceil(8), allocator),
offset: 0,
len: 0,
}
}
#[inline]
pub fn new_set(len: usize) -> Self {
Self {
buffer: buffer_mut![0xFF; len.div_ceil(8)],
offset: 0,
len,
}
}
#[inline]
pub fn new_unset(len: usize) -> Self {
Self {
buffer: BufferMut::zeroed(len.div_ceil(8)),
offset: 0,
len,
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn empty() -> Self {
Self::with_capacity(0)
}
pub fn empty_in(allocator: BufferAllocatorRef) -> Self {
Self::with_capacity_in(0, allocator)
}
#[cfg(test)]
pub(crate) fn allocator(&self) -> &BufferAllocatorRef {
self.buffer.allocator()
}
pub fn take(&mut self) -> Self {
Self {
buffer: self.buffer.take(),
offset: std::mem::take(&mut self.offset),
len: std::mem::take(&mut self.len),
}
}
#[inline]
pub fn full(value: bool, len: usize) -> Self {
if value {
Self::new_set(len)
} else {
Self::new_unset(len)
}
}
pub fn from_indices(len: usize, indices: impl IntoIterator<Item = usize>) -> BitBufferMut {
let mut buffer = BufferMut::<u64>::zeroed(len.div_ceil(64));
for idx in indices {
assert!(idx < len, "index {idx} exceeds len {len}");
buffer.as_mut_slice()[idx / 64] |= 1 << (idx % 64);
}
let mut buffer = buffer.into_byte_buffer();
buffer.truncate(len.div_ceil(8));
Self {
buffer,
offset: 0,
len,
}
}
#[inline]
pub fn collect_bool<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
Self::collect_bool_in(len, f, BufferAllocatorRef::statically_allocated())
}
#[inline]
pub fn collect_bool_in<F: FnMut(usize) -> bool>(
len: usize,
f: F,
allocator: BufferAllocatorRef,
) -> Self {
Self::collect_words_in(len, allocator, |words| collect_bool_words(words, len, f))
}
#[inline]
pub fn collect_bool_multiversioned<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
Self::collect_bool_multiversioned_in(len, f, BufferAllocatorRef::statically_allocated())
}
#[inline]
pub fn collect_bool_multiversioned_in<F: FnMut(usize) -> bool>(
len: usize,
f: F,
allocator: BufferAllocatorRef,
) -> Self {
Self::collect_words_in(len, allocator, |words| {
collect_bool_words_multiversioned(words, len, f)
})
}
#[inline]
fn collect_words_in(
len: usize,
allocator: BufferAllocatorRef,
fill: impl FnOnce(&mut [u64]),
) -> Self {
let num_words = len.div_ceil(64);
let mut buffer = BufferMut::<u64>::zeroed_in(num_words, allocator);
fill(buffer.as_mut_slice());
let mut bytes = buffer.into_byte_buffer();
bytes.truncate(len.div_ceil(8));
Self {
buffer: bytes,
offset: 0,
len,
}
}
#[inline]
pub fn inner(&self) -> &ByteBufferMut {
&self.buffer
}
#[inline]
pub fn into_inner(self) -> ByteBufferMut {
self.buffer
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn offset(&self) -> usize {
self.offset
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn value(&self, index: usize) -> bool {
assert!(index < self.len);
unsafe { self.value_unchecked(index) }
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub unsafe fn value_unchecked(&self, index: usize) -> bool {
unsafe { get_bit_unchecked(self.buffer.as_ptr(), self.offset + index) }
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn capacity(&self) -> usize {
(self.buffer.capacity() * 8) - self.offset
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
let required_bits = self.offset + self.len + additional;
let required_bytes = required_bits.div_ceil(8);
let additional_bytes = required_bytes.saturating_sub(self.buffer.len());
self.buffer.reserve(additional_bytes);
}
#[inline]
pub fn clear(&mut self) {
self.buffer.clear();
self.len = 0;
self.offset = 0;
}
#[inline]
pub fn set_to(&mut self, index: usize, value: bool) {
if value {
self.set(index);
} else {
self.unset(index);
}
}
#[inline]
pub unsafe fn set_to_unchecked(&mut self, index: usize, value: bool) {
if value {
unsafe { self.set_unchecked(index) }
} else {
unsafe { self.unset_unchecked(index) }
}
}
#[inline]
pub fn set(&mut self, index: usize) {
assert!(index < self.len, "index {index} exceeds len {}", self.len);
unsafe { self.set_unchecked(index) };
}
#[inline]
pub fn unset(&mut self, index: usize) {
assert!(index < self.len, "index {index} exceeds len {}", self.len);
unsafe { self.unset_unchecked(index) };
}
#[inline]
pub unsafe fn set_unchecked(&mut self, index: usize) {
unsafe { set_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
}
#[inline]
pub unsafe fn unset_unchecked(&mut self, index: usize) {
unsafe { unset_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(
new_len <= self.capacity(),
"`set_len` requires that new_len <= capacity()"
);
let bytes_len = (self.offset + new_len).div_ceil(8);
unsafe { self.buffer.set_len(bytes_len) };
self.len = new_len;
}
#[inline]
pub fn truncate(&mut self, len: usize) {
if len > self.len {
return;
}
assert!(
self.offset <= usize::MAX - len,
"Truncate on BitBufferMut overflowed"
);
let end_bit = self.offset + len;
let new_len_bytes = end_bit.div_ceil(8);
self.buffer.truncate(new_len_bytes);
self.len = len;
if !end_bit.is_multiple_of(8) {
let keep = (1u8 << (end_bit % 8)) - 1;
self.buffer.as_mut_slice()[new_len_bytes - 1] &= keep;
}
}
#[inline]
pub fn append(&mut self, value: bool) {
if value {
self.append_true()
} else {
self.append_false()
}
}
#[inline]
pub fn append_true(&mut self) {
let bit_pos = self.offset + self.len;
let byte_pos = bit_pos / 8;
let bit_in_byte = bit_pos % 8;
if byte_pos >= self.buffer.len() {
self.buffer.push(0u8);
}
self.buffer.as_mut_slice()[byte_pos] |= 1 << bit_in_byte;
self.len += 1;
}
#[inline]
pub fn append_false(&mut self) {
let bit_pos = self.offset + self.len;
let byte_pos = bit_pos / 8;
if byte_pos >= self.buffer.len() {
self.buffer.push(0u8);
}
self.len += 1;
}
#[inline]
pub fn append_n(&mut self, value: bool, n: usize) {
if n == 0 {
return;
}
assert!(
self.offset
.checked_add(self.len)
.and_then(|v| v.checked_add(n))
.is_some(),
"Append on BitBufferMut overflowed"
);
let end_bit_pos = self.offset + self.len + n;
let required_bytes = end_bit_pos.div_ceil(8);
if required_bytes > self.buffer.len() {
self.buffer.push_n(0x00, required_bytes - self.buffer.len());
}
let start = self.offset + self.len;
self.len += n;
fill_byte_bits(self.buffer.as_mut_slice(), start, end_bit_pos, value);
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub fn fill_range(&mut self, start: usize, end: usize, value: bool) {
assert!(end <= self.len, "end {end} exceeds len {}", self.len);
assert!(start <= end, "start {start} exceeds end {end}");
unsafe { self.fill_range_unchecked(start, end, value) }
}
#[allow(clippy::inline_always)]
#[inline(always)]
pub unsafe fn fill_range_unchecked(&mut self, start: usize, end: usize, value: bool) {
fill_bits(
self.buffer.as_mut_slice(),
self.offset + start,
self.offset + end,
value,
);
}
pub fn append_buffer(&mut self, buffer: &BitBuffer) {
let bit_len = buffer.len();
if bit_len == 0 {
return;
}
let start_bit_pos = self.offset + self.len;
let end_bit_pos = start_bit_pos + bit_len;
let required_bytes = end_bit_pos.div_ceil(8);
if required_bytes > self.buffer.len() {
self.buffer.push_n(0x00, required_bytes - self.buffer.len());
}
let dst_bit_offset = start_bit_pos % 8;
let src_bit_offset = buffer.offset();
if dst_bit_offset == 0 && src_bit_offset == 0 {
let dst_byte = start_bit_pos / 8;
let src_bytes = buffer.inner().as_slice();
let full_bytes = bit_len / 8;
self.buffer.as_mut_slice()[dst_byte..dst_byte + full_bytes]
.copy_from_slice(&src_bytes[..full_bytes]);
let rem = bit_len % 8;
if rem != 0 {
let mask = (1u8 << rem) - 1;
self.buffer.as_mut_slice()[dst_byte + full_bytes] |= src_bytes[full_bytes] & mask;
}
} else {
let self_slice = self
.buffer
.as_mut_slice()
.view_bits_mut::<bitvec::prelude::Lsb0>();
let other_slice = buffer
.inner()
.as_slice()
.view_bits::<bitvec::prelude::Lsb0>();
let source_range = src_bit_offset..src_bit_offset + bit_len;
self_slice[start_bit_pos..end_bit_pos].copy_from_bitslice(&other_slice[source_range]);
}
self.len += bit_len;
}
#[inline]
pub fn freeze(self) -> BitBuffer {
BitBuffer::new_with_offset(self.buffer.freeze(), self.len, self.offset)
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.buffer.as_slice()
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
self.buffer.as_mut_slice()
}
}
impl Default for BitBufferMut {
fn default() -> Self {
Self::with_capacity(0)
}
}
impl Not for BitBufferMut {
type Output = BitBufferMut;
#[inline]
fn not(mut self) -> Self::Output {
ops::bitwise_unary_op_mut(&mut self, |b| !b);
self
}
}
impl From<&[bool]> for BitBufferMut {
fn from(value: &[bool]) -> Self {
BitBufferMut::collect_bool_multiversioned(value.len(), |i| unsafe {
*value.get_unchecked(i)
})
}
}
impl From<&[u8]> for BitBufferMut {
fn from(value: &[u8]) -> Self {
BitBufferMut::collect_bool_multiversioned(
value.len(),
|i| unsafe { *value.get_unchecked(i) } > 0,
)
}
}
impl From<Vec<bool>> for BitBufferMut {
fn from(value: Vec<bool>) -> Self {
value.as_slice().into()
}
}
impl FromIterator<bool> for BitBufferMut {
#[inline]
fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
let mut iter = iter.into_iter();
let (lower_bound, _) = iter.size_hint();
let mut buf = BitBufferMut::new_unset(lower_bound);
assert_eq!(buf.offset, 0);
let ptr = buf.buffer.as_mut_ptr();
for i in 0..lower_bound {
let Some(v) = iter.next() else {
unsafe { buf.set_len(i) };
return buf;
};
if v {
unsafe { set_bit_unchecked(ptr, i) }
}
}
for v in iter {
buf.append(v);
}
buf
}
}
#[cfg(test)]
mod tests {
use allocator_api2::alloc::Global;
use rstest::rstest;
use crate::BufferAllocatorRef;
use crate::BufferMut;
use crate::bit::buf_mut::BitBufferMut;
use crate::bit::buf_mut::fill_bits;
use crate::bitbuffer;
use crate::bitbuffer_mut;
use crate::buffer_mut;
#[rstest]
fn fill_bits_matches_bitwise_fill(
#[values(1, 7, 8, 9, 16, 17)] slice_len: usize,
#[values(false, true)] value: bool,
) {
let pattern: Vec<u8> = (0u8..)
.take(slice_len)
.map(|i| i.wrapping_mul(0x5B) ^ 0xA6)
.collect();
for start in 0..=slice_len * 8 {
for end in start..=slice_len * 8 {
let mut actual = pattern.clone();
fill_bits(&mut actual, start, end, value);
let mut expected = pattern.clone();
for bit in start..end {
if value {
expected[bit / 8] |= 1 << (bit % 8);
} else {
expected[bit / 8] &= !(1 << (bit % 8));
}
}
assert_eq!(actual, expected, "range {start}..{end}");
}
}
}
#[test]
fn test_bits_mut() {
let mut bools = bitbuffer_mut![false; 10];
bools.set_to(0, true);
bools.set_to(9, true);
let bools = bools.freeze();
assert!(bools.value(0));
for i in 1..=8 {
assert!(!bools.value(i));
}
assert!(bools.value(9));
}
#[test]
fn take_preserves_allocator() {
let allocator = BufferAllocatorRef::new(Global);
let mut buffer = BitBufferMut::with_capacity_in(4, allocator.clone());
buffer.append(true);
buffer.append(false);
buffer.append(true);
let taken = buffer.take();
assert_eq!(
(0..taken.len())
.map(|index| taken.value(index))
.collect::<Vec<_>>(),
[true, false, true]
);
assert!(taken.allocator().ptr_eq(&allocator));
assert!(buffer.is_empty());
assert!(buffer.allocator().ptr_eq(&allocator));
}
#[test]
fn test_append_n() {
let mut bools = BitBufferMut::with_capacity(10);
assert_eq!(bools.len(), 0);
assert!(bools.is_empty());
bools.append(true);
bools.append_n(false, 8);
bools.append_n(true, 1);
let bools = bools.freeze();
assert_eq!(bools.true_count(), 2);
assert!(bools.value(0));
assert!(bools.value(9));
}
#[test]
fn append_false_after_truncate_reads_back_false() {
let mut bools = BitBufferMut::new_set(16);
bools.truncate(12);
bools.append_false();
bools.append_true();
let bools = bools.freeze();
assert_eq!(bools.len(), 14);
assert!(
!bools.value(12),
"appended false must read back false after truncate"
);
assert!(bools.value(13));
}
#[test]
fn test_reserve_ensures_len_plus_additional() {
let mut bits = BitBufferMut::with_capacity(10);
assert_eq!(bits.len(), 0);
bits.reserve(100);
assert!(bits.capacity() >= 100);
bits.append_n(true, 50);
assert_eq!(bits.len(), 50);
bits.reserve(100);
assert!(bits.capacity() >= 150);
}
#[test]
fn test_with_offset_zero() {
let buf = BufferMut::zeroed(2);
let mut bit_buf = BitBufferMut::from_buffer(buf, 0, 16);
bit_buf.set(0);
bit_buf.set(7);
bit_buf.set(8);
bit_buf.set(15);
assert!(bit_buf.value(0));
assert!(bit_buf.value(7));
assert!(bit_buf.value(8));
assert!(bit_buf.value(15));
assert!(!bit_buf.value(1));
assert!(!bit_buf.value(9));
assert_eq!(bit_buf.as_slice()[0], 0b10000001);
assert_eq!(bit_buf.as_slice()[1], 0b10000001);
}
#[test]
fn test_with_offset_within_byte() {
let buf = buffer_mut![0b11111111, 0b00000000, 0b00000000];
let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 10);
assert!(bit_buf.value(0)); assert!(bit_buf.value(4)); assert!(!bit_buf.value(5));
bit_buf.set(7);
assert!(bit_buf.value(7));
bit_buf.unset(0);
assert!(!bit_buf.value(0));
}
#[test]
fn test_with_offset_byte_boundary() {
let buf = buffer_mut![0xFF, 0x00, 0xFF];
let mut bit_buf = BitBufferMut::from_buffer(buf, 8, 16);
for i in 0..8 {
assert!(!bit_buf.value(i));
}
for i in 8..16 {
assert!(bit_buf.value(i));
}
bit_buf.set(0);
bit_buf.set(3);
assert!(bit_buf.value(0));
assert!(bit_buf.value(3));
}
#[test]
fn test_with_large_offset() {
let buf = buffer_mut![0xFF, 0xFF, 0xFF, 0xFF];
let mut bit_buf = BitBufferMut::from_buffer(buf, 13, 10);
for i in 0..10 {
assert!(bit_buf.value(i));
}
bit_buf.unset(0);
bit_buf.unset(5);
bit_buf.unset(9);
assert!(!bit_buf.value(0));
assert!(bit_buf.value(1));
assert!(!bit_buf.value(5));
assert!(!bit_buf.value(9));
}
#[test]
fn test_append_with_offset() {
let buf = buffer_mut![0b11100000]; let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0);
bit_buf.append(false); bit_buf.append(true); bit_buf.append(true);
assert_eq!(bit_buf.len(), 3);
assert!(!bit_buf.value(0));
assert!(bit_buf.value(1));
assert!(bit_buf.value(2));
}
#[test]
fn test_append_n_with_offset_crossing_boundary() {
let buf = BufferMut::zeroed(4);
let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
bit_buf.append_n(true, 10);
assert_eq!(bit_buf.len(), 10);
for i in 0..10 {
assert!(bit_buf.value(i));
}
assert_eq!(bit_buf.as_slice()[0], 0b11100000);
assert_eq!(bit_buf.as_slice()[1], 0b01111111);
}
#[test]
fn test_truncate_with_offset() {
let buf = buffer_mut![0xFF, 0xFF];
let mut bit_buf = BitBufferMut::from_buffer(buf, 4, 12);
assert_eq!(bit_buf.len(), 12);
bit_buf.truncate(8);
assert_eq!(bit_buf.len(), 8);
bit_buf.truncate(3);
assert_eq!(bit_buf.len(), 3);
bit_buf.truncate(10);
assert_eq!(bit_buf.len(), 3);
}
#[test]
fn test_capacity_with_offset() {
let buf = buffer_mut![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
assert!(bit_buf.capacity() >= 75);
assert_eq!(bit_buf.capacity() % 8, (80 - 5) % 8);
}
#[test]
fn test_reserve_with_offset() {
let buf = buffer_mut![0, 0]; let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0);
let initial_capacity = bit_buf.capacity();
assert!(initial_capacity >= 13);
bit_buf.reserve(20);
assert!(bit_buf.capacity() >= 20);
}
#[test]
fn test_freeze_with_offset() {
let buf = buffer_mut![0b11110000, 0b00001111];
let mut bit_buf = BitBufferMut::from_buffer(buf, 4, 8);
bit_buf.set(0);
bit_buf.set(7);
let frozen = bit_buf.freeze();
assert_eq!(frozen.offset(), 4);
assert_eq!(frozen.len(), 8);
assert!(frozen.value(0));
assert!(frozen.value(7));
}
#[cfg_attr(miri, ignore)] #[test]
fn append_after_clear_reads_back_false() {
let mut bools = BitBufferMut::new_set(16);
bools.clear();
bools.append_false();
bools.append_buffer(&crate::BitBuffer::new_unset(8));
let bools = bools.freeze();
assert_eq!(bools.len(), 9);
assert_eq!(bools.true_count(), 0);
}
#[cfg_attr(miri, ignore)] #[test]
fn test_append_buffer_after_truncate() {
let mut buf = BitBufferMut::new_set(16);
buf.truncate(3);
buf.append_buffer(&crate::BitBuffer::new_unset(8));
let frozen = buf.freeze();
assert_eq!(frozen.len(), 11);
for i in 0..3 {
assert!(frozen.value(i), "bit {i} should be set");
}
for i in 3..11 {
assert!(!frozen.value(i), "bit {i} should be unset");
}
}
#[rstest]
#[case::both_aligned(0, 0)]
#[case::dst_unaligned(3, 0)]
#[case::src_unaligned(0, 5)]
#[case::mismatched(3, 5)]
#[case::equal_nonzero(5, 5)]
#[cfg_attr(miri, ignore)] fn test_append_buffer_long(#[case] dst_prefix: usize, #[case] src_start: usize) {
let source = crate::BitBuffer::from_iter((0..301).map(|i| i % 3 == 0));
let source = source.slice(src_start..301);
let mut dest = BitBufferMut::with_capacity(512);
dest.append_n(true, dst_prefix);
dest.append_buffer(&source);
assert_eq!(dest.len(), dst_prefix + source.len());
for i in 0..dst_prefix {
assert!(dest.value(i), "prefix bit {i}");
}
for i in 0..source.len() {
assert_eq!(dest.value(dst_prefix + i), source.value(i), "bit {i}");
}
}
#[cfg_attr(miri, ignore)] #[test]
fn test_append_buffer_with_offsets() {
let source = bitbuffer![false, false, true, true, false, true];
let buf = BufferMut::zeroed(4);
let mut dest = BitBufferMut::from_buffer(buf, 3, 0);
dest.append(true);
dest.append(false);
dest.append_buffer(&source);
assert_eq!(dest.len(), 8);
assert!(dest.value(0)); assert!(!dest.value(1)); assert!(!dest.value(2)); assert!(!dest.value(3)); assert!(dest.value(4)); assert!(dest.value(5)); assert!(!dest.value(6)); assert!(dest.value(7)); }
#[test]
fn test_set_unset_unchecked_with_offset() {
let buf = BufferMut::zeroed(3);
let mut bit_buf = BitBufferMut::from_buffer(buf, 7, 10);
unsafe {
bit_buf.set_unchecked(0);
bit_buf.set_unchecked(5);
bit_buf.set_unchecked(9);
}
assert!(bit_buf.value(0));
assert!(bit_buf.value(5));
assert!(bit_buf.value(9));
unsafe {
bit_buf.unset_unchecked(5);
}
assert!(!bit_buf.value(5));
}
#[test]
fn test_value_unchecked_with_offset() {
let buf = buffer_mut![0b11110000, 0b00001111];
let bit_buf = BitBufferMut::from_buffer(buf, 4, 8);
unsafe {
assert!(bit_buf.value_unchecked(0));
assert!(bit_buf.value_unchecked(3));
assert!(bit_buf.value_unchecked(4));
assert!(bit_buf.value_unchecked(7));
}
}
#[test]
fn test_append_alternating_with_offset() {
let buf = BufferMut::zeroed(4);
let mut bit_buf = BitBufferMut::from_buffer(buf, 2, 0);
for i in 0..20 {
bit_buf.append(i % 2 == 0);
}
assert_eq!(bit_buf.len(), 20);
for i in 0..20 {
assert_eq!(bit_buf.value(i), i % 2 == 0);
}
}
#[test]
fn test_new_set_new_unset() {
let set_buf = bitbuffer_mut![true; 10];
let unset_buf = bitbuffer_mut![false; 10];
for i in 0..10 {
assert!(set_buf.value(i));
assert!(!unset_buf.value(i));
}
assert_eq!(set_buf.len(), 10);
assert_eq!(unset_buf.len(), 10);
}
#[test]
fn test_append_n_false_with_offset() {
let buf = BufferMut::zeroed(4);
let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
bit_buf.append_n(false, 15);
assert_eq!(bit_buf.len(), 15);
for i in 0..15 {
assert!(!bit_buf.value(i));
}
}
#[test]
fn test_append_n_true_with_offset() {
let buf = BufferMut::zeroed(4);
let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
bit_buf.append_n(true, 15);
assert_eq!(bit_buf.len(), 15);
for i in 0..15 {
assert!(bit_buf.value(i));
}
}
#[test]
fn test_mixed_operations_with_offset() {
let buf = BufferMut::zeroed(5);
let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0);
bit_buf.append_n(true, 5);
bit_buf.append_n(false, 3);
bit_buf.append(true);
assert_eq!(bit_buf.len(), 9);
bit_buf.set(6); bit_buf.unset(2);
assert!(bit_buf.value(0));
assert!(bit_buf.value(1));
assert!(!bit_buf.value(2)); assert!(bit_buf.value(3));
assert!(bit_buf.value(4));
assert!(!bit_buf.value(5));
assert!(bit_buf.value(6)); assert!(!bit_buf.value(7));
assert!(bit_buf.value(8));
bit_buf.truncate(6);
assert_eq!(bit_buf.len(), 6);
let frozen = bit_buf.freeze();
assert_eq!(frozen.offset(), 3);
assert_eq!(frozen.len(), 6);
}
#[test]
fn test_from_iterator_with_incorrect_size_hint() {
struct LyingIterator {
values: Vec<bool>,
index: usize,
}
impl Iterator for LyingIterator {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
(self.index < self.values.len()).then(|| {
let val = self.values[self.index];
self.index += 1;
val
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.values.len() - self.index;
let lower = remaining.min(5); let upper = Some(5); (lower, upper)
}
}
let lying_iter = LyingIterator {
values: vec![
true, false, true, false, true, false, true, false, true, false,
],
index: 0,
};
let bit_buf: BitBufferMut = lying_iter.collect();
assert_eq!(bit_buf.len(), 10);
for i in 0..10 {
assert_eq!(bit_buf.value(i), i % 2 == 0);
}
}
#[rstest]
#[case(0, 8)]
#[case(1, 7)]
#[case(7, 1)]
#[case(8, 0)]
fn from_buffer_accepts_offset_plus_len_within_the_buffer(
#[case] offset: usize,
#[case] len: usize,
) {
let bits = BitBufferMut::from_buffer(buffer_mut![0u8; 1], offset, len);
assert_eq!(bits.len(), len);
}
#[rstest]
#[case::offset_pushes_past_the_end(1, 8)]
#[case::offset_alone_past_the_end(9, 0)]
#[case::len_alone_past_the_end(0, 9)]
#[should_panic(expected = "is too short for offset")]
fn from_buffer_rejects_offset_plus_len_past_the_buffer(
#[case] offset: usize,
#[case] len: usize,
) {
BitBufferMut::from_buffer(buffer_mut![0u8; 1], offset, len);
}
#[test]
fn from_buffer_allows_offsets_beyond_one_byte() {
let bits = BitBufferMut::from_buffer(buffer_mut![0u8; 128], 1000, 24);
assert_eq!(bits.len(), 24);
}
#[test]
#[should_panic(expected = "is too short for offset")]
fn from_buffer_does_not_wrap_on_a_huge_offset() {
BitBufferMut::from_buffer(buffer_mut![0u8; 1], usize::MAX, 8);
}
}