use crate::bit_chunk_iterator::BitChunks;
#[inline]
pub fn round_upto_multiple_of_64(num: usize) -> usize {
num.checked_next_multiple_of(64)
.expect("failed to round upto multiple of 64")
}
pub fn round_upto_power_of_2(num: usize, factor: usize) -> usize {
debug_assert!(factor > 0 && factor.is_power_of_two());
num.checked_add(factor - 1)
.expect("failed to round to next highest power of 2")
& !(factor - 1)
}
#[inline]
pub fn get_bit(data: &[u8], i: usize) -> bool {
data[i / 8] & (1 << (i % 8)) != 0
}
#[inline]
pub unsafe fn get_bit_raw(data: *const u8, i: usize) -> bool {
unsafe { (*data.add(i / 8) & (1 << (i % 8))) != 0 }
}
#[inline]
pub fn set_bit(data: &mut [u8], i: usize) {
data[i / 8] |= 1 << (i % 8);
}
#[inline]
pub unsafe fn set_bit_raw(data: *mut u8, i: usize) {
unsafe {
*data.add(i / 8) |= 1 << (i % 8);
}
}
#[inline]
pub fn unset_bit(data: &mut [u8], i: usize) {
data[i / 8] &= !(1 << (i % 8));
}
#[inline]
pub unsafe fn unset_bit_raw(data: *mut u8, i: usize) {
unsafe {
*data.add(i / 8) &= !(1 << (i % 8));
}
}
#[inline]
pub fn ceil(value: usize, divisor: usize) -> usize {
value.div_ceil(divisor)
}
#[inline]
pub(crate) fn read_up_to_byte_from_offset(
slice: &[u8],
number_of_bits_to_read: usize,
bit_offset: usize,
) -> u8 {
assert!(number_of_bits_to_read < 8, "can read up to 8 bits only");
assert!(bit_offset < 8, "bit offset must be less than 8");
assert_ne!(
number_of_bits_to_read, 0,
"number of bits to read must be greater than 0"
);
assert_ne!(slice.len(), 0, "slice must not be empty");
let number_of_bytes_to_read = ceil(number_of_bits_to_read + bit_offset, 8);
assert!(slice.len() >= number_of_bytes_to_read, "slice is too small");
let mut bits = slice[0] >> bit_offset;
for (i, &byte) in slice
.iter()
.take(number_of_bytes_to_read)
.enumerate()
.skip(1)
{
bits |= byte << (i * 8 - bit_offset);
}
bits & ((1 << number_of_bits_to_read) - 1)
}
pub fn apply_bitwise_binary_op<F>(
left: &mut [u8],
left_offset_in_bits: usize,
right: impl AsRef<[u8]>,
right_offset_in_bits: usize,
len_in_bits: usize,
mut op: F,
) where
F: FnMut(u64, u64) -> u64,
{
if len_in_bits == 0 {
return;
}
let bit_offset = left_offset_in_bits % 8;
let is_mutable_buffer_byte_aligned = bit_offset == 0;
if is_mutable_buffer_byte_aligned {
byte_aligned_bitwise_bin_op_helper(
left,
left_offset_in_bits,
right,
right_offset_in_bits,
len_in_bits,
op,
);
} else {
let bits_to_next_byte = (8 - bit_offset)
.min(len_in_bits);
{
let right_byte_offset = right_offset_in_bits / 8;
let right_first_byte: u8 = crate::util::bit_util::read_up_to_byte_from_offset(
&right.as_ref()[right_byte_offset..],
bits_to_next_byte,
right_offset_in_bits % 8,
);
align_to_byte(
left,
&mut |left| op(left, right_first_byte as u64),
left_offset_in_bits,
);
}
let offset_in_bits = left_offset_in_bits + bits_to_next_byte;
let right_offset_in_bits = right_offset_in_bits + bits_to_next_byte;
let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte);
if len_in_bits == 0 {
return;
}
byte_aligned_bitwise_bin_op_helper(
left,
offset_in_bits,
right,
right_offset_in_bits,
len_in_bits,
op,
);
}
}
pub fn apply_bitwise_unary_op<F>(
buffer: &mut [u8],
offset_in_bits: usize,
len_in_bits: usize,
mut op: F,
) where
F: FnMut(u64) -> u64,
{
if len_in_bits == 0 {
return;
}
let left_bit_offset = offset_in_bits % 8;
let is_mutable_buffer_byte_aligned = left_bit_offset == 0;
if is_mutable_buffer_byte_aligned {
byte_aligned_bitwise_unary_op_helper(buffer, offset_in_bits, len_in_bits, op);
} else {
align_to_byte(buffer, &mut op, offset_in_bits);
let bits_to_next_byte = 8 - left_bit_offset;
let offset_in_bits = offset_in_bits + bits_to_next_byte;
let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte);
if len_in_bits == 0 {
return;
}
byte_aligned_bitwise_unary_op_helper(buffer, offset_in_bits, len_in_bits, op);
}
}
#[inline]
fn byte_aligned_bitwise_bin_op_helper<F>(
left: &mut [u8],
left_offset_in_bits: usize,
right: impl AsRef<[u8]>,
right_offset_in_bits: usize,
len_in_bits: usize,
mut op: F,
) where
F: FnMut(u64, u64) -> u64,
{
assert_eq!(
left_offset_in_bits % 8,
0,
"offset_in_bits must be byte aligned"
);
let (complete_u64_chunks, remainder_bytes) =
U64UnalignedSlice::split(left, left_offset_in_bits, len_in_bits);
let right_chunks = BitChunks::new(right.as_ref(), right_offset_in_bits, len_in_bits);
assert_eq!(
self::ceil(right_chunks.remainder_len(), 8),
remainder_bytes.len()
);
let right_chunks_iter = right_chunks.iter();
assert_eq!(right_chunks_iter.len(), complete_u64_chunks.len());
complete_u64_chunks.zip_modify(right_chunks_iter, &mut op);
if right_chunks.remainder_len() > 0 {
handle_mutable_buffer_remainder(
&mut op,
remainder_bytes,
right_chunks.remainder_bits(),
right_chunks.remainder_len(),
)
}
}
#[inline]
fn byte_aligned_bitwise_unary_op_helper<F>(
buffer: &mut [u8],
offset_in_bits: usize,
len_in_bits: usize,
mut op: F,
) where
F: FnMut(u64) -> u64,
{
assert_eq!(offset_in_bits % 8, 0, "offset_in_bits must be byte aligned");
let remainder_len = len_in_bits % 64;
let (complete_u64_chunks, remainder_bytes) =
U64UnalignedSlice::split(buffer, offset_in_bits, len_in_bits);
assert_eq!(self::ceil(remainder_len, 8), remainder_bytes.len());
complete_u64_chunks.apply_unary_op(&mut op);
if remainder_len > 0 {
handle_mutable_buffer_remainder_unary(&mut op, remainder_bytes, remainder_len)
}
}
fn align_to_byte<F>(buffer: &mut [u8], op: &mut F, offset_in_bits: usize)
where
F: FnMut(u64) -> u64,
{
let byte_offset = offset_in_bits / 8;
let bit_offset = offset_in_bits % 8;
let first_byte: u8 = buffer[byte_offset];
let relevant_first_byte = first_byte >> bit_offset;
let result_first_byte = op(relevant_first_byte as u64) as u8;
let result_first_byte = result_first_byte << bit_offset;
let mask_for_first_bit_offset = (1 << bit_offset) - 1;
let result_first_byte =
(first_byte & mask_for_first_bit_offset) | (result_first_byte & !mask_for_first_bit_offset);
buffer[byte_offset] = result_first_byte;
}
struct U64UnalignedSlice<'a> {
ptr: *mut u64,
len: usize,
_marker: std::marker::PhantomData<&'a u8>,
}
impl<'a> U64UnalignedSlice<'a> {
fn split(
buffer: &'a mut [u8],
offset_in_bits: usize,
len_in_bits: usize,
) -> (Self, &'a mut [u8]) {
let left_buffer_mut: &mut [u8] = {
let last_offset = self::ceil(offset_in_bits + len_in_bits, 8);
assert!(last_offset <= buffer.len());
let byte_offset = offset_in_bits / 8;
&mut buffer[byte_offset..last_offset]
};
let number_of_u64_we_can_fit = len_in_bits / (u64::BITS as usize);
let u64_len_in_bytes = number_of_u64_we_can_fit * size_of::<u64>();
assert!(u64_len_in_bytes <= left_buffer_mut.len());
let (bytes_for_u64, remainder) = left_buffer_mut.split_at_mut(u64_len_in_bytes);
let ptr = bytes_for_u64.as_mut_ptr() as *mut u64;
let this = Self {
ptr,
len: number_of_u64_we_can_fit,
_marker: std::marker::PhantomData,
};
(this, remainder)
}
fn len(&self) -> usize {
self.len
}
fn zip_modify(
mut self,
mut zip_iter: impl ExactSizeIterator<Item = u64>,
mut map: impl FnMut(u64, u64) -> u64,
) {
assert_eq!(self.len, zip_iter.len());
if let Some(right) = zip_iter.next() {
unsafe {
self.apply_bin_op(right, &mut map);
}
}
for right in zip_iter {
self.ptr = unsafe { self.ptr.add(1) };
unsafe {
self.apply_bin_op(right, &mut map);
}
}
}
#[inline]
unsafe fn apply_bin_op(&mut self, right: u64, mut map: impl FnMut(u64, u64) -> u64) {
let current_input = unsafe {
self.ptr
.read_unaligned()
.to_le()
};
let combined = map(current_input, right);
unsafe { self.ptr.write_unaligned(combined) }
}
fn apply_unary_op(mut self, mut map: impl FnMut(u64) -> u64) {
if self.len == 0 {
return;
}
unsafe {
self.apply_bin_op(0, &mut |left, _| map(left));
}
for _ in 1..self.len {
self.ptr = unsafe { self.ptr.add(1) };
unsafe {
self.apply_bin_op(0, &mut |left, _| map(left));
}
}
}
}
#[inline]
fn handle_mutable_buffer_remainder<F>(
op: &mut F,
start_remainder_mut_slice: &mut [u8],
right_remainder_bits: u64,
remainder_len: usize,
) where
F: FnMut(u64, u64) -> u64,
{
let left_remainder_bits = get_remainder_bits(start_remainder_mut_slice, remainder_len);
let rem = op(left_remainder_bits, right_remainder_bits);
set_remainder_bits(start_remainder_mut_slice, rem, remainder_len);
}
#[inline]
fn set_remainder_bits(start_remainder_mut_slice: &mut [u8], rem: u64, remainder_len: usize) {
assert_ne!(
start_remainder_mut_slice.len(),
0,
"start_remainder_mut_slice must not be empty"
);
assert!(remainder_len < 64, "remainder_len must be less than 64");
assert_eq!(
start_remainder_mut_slice.len(),
self::ceil(remainder_len, 8),
"start_remainder_mut_slice length must be equal to ceil(remainder_len, 8)"
);
let rem = {
let current = start_remainder_mut_slice
.last()
.unwrap();
let current = *current as u64;
let inside_remainder_mask = (1 << remainder_len) - 1;
let outside_remainder_mask = !inside_remainder_mask;
let current = current & outside_remainder_mask;
let rem = rem & inside_remainder_mask;
current | rem
};
{
let remainder_bytes = self::ceil(remainder_len, 8);
let rem = &rem.to_le_bytes()[0..remainder_bytes];
let src = rem.as_ptr();
unsafe {
std::ptr::copy_nonoverlapping(
src,
start_remainder_mut_slice.as_mut_ptr(),
remainder_bytes,
)
};
}
}
#[inline]
fn get_remainder_bits(remainder: &[u8], remainder_len: usize) -> u64 {
assert!(remainder.len() < 64, "remainder_len must be less than 64");
assert_eq!(
remainder.len(),
self::ceil(remainder_len, 8),
"remainder and remainder len ceil must be the same"
);
let bits = remainder
.iter()
.enumerate()
.fold(0_u64, |acc, (index, &byte)| {
acc | (byte as u64) << (index * 8)
});
bits & ((1 << remainder_len) - 1)
}
#[inline]
fn handle_mutable_buffer_remainder_unary<F>(
op: &mut F,
start_remainder_mut: &mut [u8],
remainder_len: usize,
) where
F: FnMut(u64) -> u64,
{
let left_remainder_bits = get_remainder_bits(start_remainder_mut, remainder_len);
let rem = op(left_remainder_bits);
set_remainder_bits(start_remainder_mut, rem, remainder_len);
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
use crate::bit_iterator::BitIterator;
use crate::{BooleanBuffer, BooleanBufferBuilder, MutableBuffer};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
#[test]
fn test_round_upto_multiple_of_64() {
assert_eq!(0, round_upto_multiple_of_64(0));
assert_eq!(64, round_upto_multiple_of_64(1));
assert_eq!(64, round_upto_multiple_of_64(63));
assert_eq!(64, round_upto_multiple_of_64(64));
assert_eq!(128, round_upto_multiple_of_64(65));
assert_eq!(192, round_upto_multiple_of_64(129));
}
#[test]
#[should_panic(expected = "failed to round upto multiple of 64")]
fn test_round_upto_multiple_of_64_panic() {
let _ = round_upto_multiple_of_64(usize::MAX);
}
#[test]
#[should_panic(expected = "failed to round to next highest power of 2")]
fn test_round_upto_panic() {
let _ = round_upto_power_of_2(usize::MAX, 2);
}
#[test]
fn test_get_bit() {
assert!(get_bit(&[0b00001101], 0));
assert!(!get_bit(&[0b00001101], 1));
assert!(get_bit(&[0b00001101], 2));
assert!(get_bit(&[0b00001101], 3));
assert!(get_bit(&[0b01001001, 0b01010010], 0));
assert!(!get_bit(&[0b01001001, 0b01010010], 1));
assert!(!get_bit(&[0b01001001, 0b01010010], 2));
assert!(get_bit(&[0b01001001, 0b01010010], 3));
assert!(!get_bit(&[0b01001001, 0b01010010], 4));
assert!(!get_bit(&[0b01001001, 0b01010010], 5));
assert!(get_bit(&[0b01001001, 0b01010010], 6));
assert!(!get_bit(&[0b01001001, 0b01010010], 7));
assert!(!get_bit(&[0b01001001, 0b01010010], 8));
assert!(get_bit(&[0b01001001, 0b01010010], 9));
assert!(!get_bit(&[0b01001001, 0b01010010], 10));
assert!(!get_bit(&[0b01001001, 0b01010010], 11));
assert!(get_bit(&[0b01001001, 0b01010010], 12));
assert!(!get_bit(&[0b01001001, 0b01010010], 13));
assert!(get_bit(&[0b01001001, 0b01010010], 14));
assert!(!get_bit(&[0b01001001, 0b01010010], 15));
}
pub fn seedable_rng() -> StdRng {
StdRng::seed_from_u64(42)
}
#[test]
fn test_get_bit_raw() {
const NUM_BYTE: usize = 10;
let mut buf = [0; NUM_BYTE];
let mut expected = vec![];
let mut rng = seedable_rng();
for i in 0..8 * NUM_BYTE {
let b = rng.random_bool(0.5);
expected.push(b);
if b {
set_bit(&mut buf[..], i)
}
}
let raw_ptr = buf.as_ptr();
for (i, b) in expected.iter().enumerate() {
unsafe {
assert_eq!(*b, get_bit_raw(raw_ptr, i));
}
}
}
#[test]
fn test_set_bit() {
let mut b = [0b00000010];
set_bit(&mut b, 0);
assert_eq!([0b00000011], b);
set_bit(&mut b, 1);
assert_eq!([0b00000011], b);
set_bit(&mut b, 7);
assert_eq!([0b10000011], b);
}
#[test]
fn test_unset_bit() {
let mut b = [0b11111101];
unset_bit(&mut b, 0);
assert_eq!([0b11111100], b);
unset_bit(&mut b, 1);
assert_eq!([0b11111100], b);
unset_bit(&mut b, 7);
assert_eq!([0b01111100], b);
}
#[test]
fn test_set_bit_raw() {
const NUM_BYTE: usize = 10;
let mut buf = vec![0; NUM_BYTE];
let mut expected = vec![];
let mut rng = seedable_rng();
for i in 0..8 * NUM_BYTE {
let b = rng.random_bool(0.5);
expected.push(b);
if b {
unsafe {
set_bit_raw(buf.as_mut_ptr(), i);
}
}
}
let raw_ptr = buf.as_ptr();
for (i, b) in expected.iter().enumerate() {
unsafe {
assert_eq!(*b, get_bit_raw(raw_ptr, i));
}
}
}
#[test]
fn test_unset_bit_raw() {
const NUM_BYTE: usize = 10;
let mut buf = vec![255; NUM_BYTE];
let mut expected = vec![];
let mut rng = seedable_rng();
for i in 0..8 * NUM_BYTE {
let b = rng.random_bool(0.5);
expected.push(b);
if !b {
unsafe {
unset_bit_raw(buf.as_mut_ptr(), i);
}
}
}
let raw_ptr = buf.as_ptr();
for (i, b) in expected.iter().enumerate() {
unsafe {
assert_eq!(*b, get_bit_raw(raw_ptr, i));
}
}
}
#[test]
fn test_get_set_bit_roundtrip() {
const NUM_BYTES: usize = 10;
const NUM_SETS: usize = 10;
let mut buffer: [u8; NUM_BYTES * 8] = [0; NUM_BYTES * 8];
let mut v = HashSet::new();
let mut rng = seedable_rng();
for _ in 0..NUM_SETS {
let offset = rng.random_range(0..8 * NUM_BYTES);
v.insert(offset);
set_bit(&mut buffer[..], offset);
}
for i in 0..NUM_BYTES * 8 {
assert_eq!(v.contains(&i), get_bit(&buffer[..], i));
}
}
#[test]
fn test_ceil() {
assert_eq!(ceil(0, 1), 0);
assert_eq!(ceil(1, 1), 1);
assert_eq!(ceil(1, 2), 1);
assert_eq!(ceil(1, 8), 1);
assert_eq!(ceil(7, 8), 1);
assert_eq!(ceil(8, 8), 1);
assert_eq!(ceil(9, 8), 2);
assert_eq!(ceil(9, 9), 1);
assert_eq!(ceil(10000000000, 10), 1000000000);
assert_eq!(ceil(10, 10000000000), 1);
assert_eq!(ceil(10000000000, 1000000000), 10);
}
#[test]
fn test_read_up_to() {
let all_ones = &[0b10111001, 0b10001100];
for (bit_offset, expected) in [
(0, 0b00000001),
(1, 0b00000000),
(2, 0b00000000),
(3, 0b00000001),
(4, 0b00000001),
(5, 0b00000001),
(6, 0b00000000),
(7, 0b00000001),
] {
let result = read_up_to_byte_from_offset(all_ones, 1, bit_offset);
assert_eq!(
result, expected,
"failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
);
}
for (bit_offset, expected) in [
(0, 0b00000001),
(1, 0b00000000),
(2, 0b00000010),
(3, 0b00000011),
(4, 0b00000011),
(5, 0b00000001),
(6, 0b00000010),
(7, 0b00000001),
] {
let result = read_up_to_byte_from_offset(all_ones, 2, bit_offset);
assert_eq!(
result, expected,
"failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
);
}
for (bit_offset, expected) in [
(0, 0b00111001),
(1, 0b00011100),
(2, 0b00101110),
(3, 0b00010111),
(4, 0b00001011),
(5, 0b00100101),
(6, 0b00110010),
(7, 0b00011001),
] {
let result = read_up_to_byte_from_offset(all_ones, 6, bit_offset);
assert_eq!(
result, expected,
"failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
);
}
for (bit_offset, expected) in [
(0, 0b00111001),
(1, 0b01011100),
(2, 0b00101110),
(3, 0b00010111),
(4, 0b01001011),
(5, 0b01100101),
(6, 0b00110010),
(7, 0b00011001),
] {
let result = read_up_to_byte_from_offset(all_ones, 7, bit_offset);
assert_eq!(
result, expected,
"failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
);
}
}
fn test_mutable_buffer_bin_op_helper<F, G>(
left_data: &[bool],
right_data: &[bool],
left_offset_in_bits: usize,
right_offset_in_bits: usize,
len_in_bits: usize,
op: F,
mut expected_op: G,
) where
F: FnMut(u64, u64) -> u64,
G: FnMut(bool, bool) -> bool,
{
let mut left_buffer = BooleanBufferBuilder::new(len_in_bits);
left_buffer.append_slice(left_data);
let right_buffer = BooleanBuffer::from(right_data);
let expected: Vec<bool> = left_data
.iter()
.skip(left_offset_in_bits)
.zip(right_data.iter().skip(right_offset_in_bits))
.take(len_in_bits)
.map(|(l, r)| expected_op(*l, *r))
.collect();
apply_bitwise_binary_op(
left_buffer.as_slice_mut(),
left_offset_in_bits,
right_buffer.inner(),
right_offset_in_bits,
len_in_bits,
op,
);
let result: Vec<bool> =
BitIterator::new(left_buffer.as_slice(), left_offset_in_bits, len_in_bits).collect();
assert_eq!(
result, expected,
"Failed with left_offset={}, right_offset={}, len={}",
left_offset_in_bits, right_offset_in_bits, len_in_bits
);
}
fn test_mutable_buffer_unary_op_helper<F, G>(
data: &[bool],
offset_in_bits: usize,
len_in_bits: usize,
op: F,
mut expected_op: G,
) where
F: FnMut(u64) -> u64,
G: FnMut(bool) -> bool,
{
let mut buffer = BooleanBufferBuilder::new(len_in_bits);
buffer.append_slice(data);
let expected: Vec<bool> = data
.iter()
.skip(offset_in_bits)
.take(len_in_bits)
.map(|b| expected_op(*b))
.collect();
apply_bitwise_unary_op(buffer.as_slice_mut(), offset_in_bits, len_in_bits, op);
let result: Vec<bool> =
BitIterator::new(buffer.as_slice(), offset_in_bits, len_in_bits).collect();
assert_eq!(
result, expected,
"Failed with offset={}, len={}",
offset_in_bits, len_in_bits
);
}
fn create_test_data(len: usize) -> (Vec<bool>, Vec<bool>) {
let mut rng = rand::rng();
let left: Vec<bool> = (0..len).map(|_| rng.random_bool(0.5)).collect();
let right: Vec<bool> = (0..len).map(|_| rng.random_bool(0.5)).collect();
(left, right)
}
fn test_all_binary_ops(
left_data: &[bool],
right_data: &[bool],
left_offset_in_bits: usize,
right_offset_in_bits: usize,
len_in_bits: usize,
) {
test_mutable_buffer_bin_op_helper(
left_data,
right_data,
left_offset_in_bits,
right_offset_in_bits,
len_in_bits,
|a, b| a & b,
|a, b| a & b,
);
test_mutable_buffer_bin_op_helper(
left_data,
right_data,
left_offset_in_bits,
right_offset_in_bits,
len_in_bits,
|a, b| a | b,
|a, b| a | b,
);
test_mutable_buffer_bin_op_helper(
left_data,
right_data,
left_offset_in_bits,
right_offset_in_bits,
len_in_bits,
|a, b| a ^ b,
|a, b| a ^ b,
);
}
#[test]
fn test_binary_ops_less_than_byte() {
let (left, right) = create_test_data(4);
test_all_binary_ops(&left, &right, 0, 0, 4);
}
#[test]
fn test_binary_ops_less_than_byte_across_boundary() {
let (left, right) = create_test_data(16);
test_all_binary_ops(&left, &right, 6, 6, 4);
}
#[test]
fn test_binary_ops_exactly_byte() {
let (left, right) = create_test_data(16);
test_all_binary_ops(&left, &right, 0, 0, 8);
}
#[test]
fn test_binary_ops_more_than_byte_less_than_u64() {
let (left, right) = create_test_data(64);
test_all_binary_ops(&left, &right, 0, 0, 32);
}
#[test]
fn test_binary_ops_exactly_u64() {
let (left, right) = create_test_data(180);
test_all_binary_ops(&left, &right, 0, 0, 64);
test_all_binary_ops(&left, &right, 64, 9, 64);
test_all_binary_ops(&left, &right, 8, 100, 64);
test_all_binary_ops(&left, &right, 1, 15, 64);
test_all_binary_ops(&left, &right, 12, 10, 64);
test_all_binary_ops(&left, &right, 180 - 64, 2, 64);
}
#[test]
fn test_binary_ops_more_than_u64_not_multiple() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 0, 0, 100);
}
#[test]
fn test_binary_ops_exactly_multiple_u64() {
let (left, right) = create_test_data(256);
test_all_binary_ops(&left, &right, 0, 0, 128);
}
#[test]
fn test_binary_ops_more_than_multiple_u64() {
let (left, right) = create_test_data(300);
test_all_binary_ops(&left, &right, 0, 0, 200);
}
#[test]
fn test_binary_ops_byte_aligned_no_remainder() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 0, 0, 128);
}
#[test]
fn test_binary_ops_byte_aligned_with_remainder() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 0, 0, 100);
}
#[test]
fn test_binary_ops_not_byte_aligned_no_remainder() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 3, 3, 128);
}
#[test]
fn test_binary_ops_not_byte_aligned_with_remainder() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 5, 5, 100);
}
#[test]
fn test_binary_ops_different_offsets() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 3, 7, 50);
}
#[test]
fn test_binary_ops_offsets_greater_than_8_less_than_64() {
let (left, right) = create_test_data(200);
test_all_binary_ops(&left, &right, 13, 27, 100);
}
#[test]
fn test_not_less_than_byte() {
let data = vec![true, false, true, false];
test_mutable_buffer_unary_op_helper(&data, 0, 4, |a| !a, |a| !a);
}
#[test]
fn test_not_less_than_byte_across_boundary() {
let data: Vec<bool> = (0..16).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 6, 4, |a| !a, |a| !a);
}
#[test]
fn test_not_exactly_byte() {
let data: Vec<bool> = (0..16).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 8, |a| !a, |a| !a);
}
#[test]
fn test_not_more_than_byte_less_than_u64() {
let data: Vec<bool> = (0..64).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 32, |a| !a, |a| !a);
}
#[test]
fn test_not_exactly_u64() {
let data: Vec<bool> = (0..128).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 64, |a| !a, |a| !a);
}
#[test]
fn test_not_more_than_u64_not_multiple() {
let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 100, |a| !a, |a| !a);
}
#[test]
fn test_not_exactly_multiple_u64() {
let data: Vec<bool> = (0..256).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 128, |a| !a, |a| !a);
}
#[test]
fn test_not_more_than_multiple_u64() {
let data: Vec<bool> = (0..300).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 200, |a| !a, |a| !a);
}
#[test]
fn test_not_byte_aligned_no_remainder() {
let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 128, |a| !a, |a| !a);
}
#[test]
fn test_not_byte_aligned_with_remainder() {
let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 0, 100, |a| !a, |a| !a);
}
#[test]
fn test_not_not_byte_aligned_no_remainder() {
let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 3, 128, |a| !a, |a| !a);
}
#[test]
fn test_not_not_byte_aligned_with_remainder() {
let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
test_mutable_buffer_unary_op_helper(&data, 5, 100, |a| !a, |a| !a);
}
#[test]
fn test_empty_length() {
let (left, right) = create_test_data(16);
test_all_binary_ops(&left, &right, 0, 0, 0);
}
#[test]
fn test_single_bit() {
let (left, right) = create_test_data(16);
test_all_binary_ops(&left, &right, 0, 0, 1);
}
#[test]
fn test_single_bit_at_offset() {
let (left, right) = create_test_data(16);
test_all_binary_ops(&left, &right, 7, 7, 1);
}
#[test]
fn test_not_single_bit() {
let data = vec![true, false, true, false];
test_mutable_buffer_unary_op_helper(&data, 0, 1, |a| !a, |a| !a);
}
#[test]
fn test_not_empty_length() {
let data = vec![true, false, true, false];
test_mutable_buffer_unary_op_helper(&data, 0, 0, |a| !a, |a| !a);
}
#[test]
fn test_less_than_byte_unaligned_and_not_enough_bits() {
let left_offset_in_bits = 2;
let right_offset_in_bits = 4;
let len_in_bits = 1;
let right = (0..8).map(|i| (i / 2) % 2 == 0).collect::<Vec<_>>();
let left = (0..3).map(|i| i % 2 == 0).collect::<Vec<_>>();
test_all_binary_ops(
&left,
&right,
left_offset_in_bits,
right_offset_in_bits,
len_in_bits,
);
}
#[test]
fn test_bitwise_binary_op_offset_out_of_bounds() {
let input = vec![0b10101010u8, 0b01010101u8];
let mut buffer = MutableBuffer::new(2); buffer.extend_from_slice(&input); apply_bitwise_binary_op(
buffer.as_slice_mut(),
100, [0b11110000u8, 0b00001111u8],
0,
0,
|a, b| a & b,
);
assert_eq!(buffer.as_slice(), &input);
}
#[test]
#[should_panic(expected = "assertion failed: last_offset <= buffer.len()")]
fn test_bitwise_binary_op_length_out_of_bounds() {
let mut buffer = MutableBuffer::new(2); buffer.extend_from_slice(&[0b10101010u8, 0b01010101u8]); apply_bitwise_binary_op(
buffer.as_slice_mut(),
0, [0b11110000u8, 0b00001111u8],
0,
100,
|a, b| a & b,
);
assert_eq!(buffer.as_slice(), &[0b10101010u8, 0b01010101u8]);
}
#[test]
#[should_panic(expected = "offset + len out of bounds")]
fn test_bitwise_binary_op_right_len_out_of_bounds() {
let mut buffer = MutableBuffer::new(2); buffer.extend_from_slice(&[0b10101010u8, 0b01010101u8]); apply_bitwise_binary_op(
buffer.as_slice_mut(),
0, [0b11110000u8, 0b00001111u8],
1000,
16,
|a, b| a & b,
);
assert_eq!(buffer.as_slice(), &[0b10101010u8, 0b01010101u8]);
}
#[test]
#[should_panic(expected = "the len is 2 but the index is 12")]
fn test_bitwise_unary_op_offset_out_of_bounds() {
let input = vec![0b10101010u8, 0b01010101u8];
let mut buffer = MutableBuffer::new(2); buffer.extend_from_slice(&input); apply_bitwise_unary_op(
buffer.as_slice_mut(),
100, 8,
|a| !a,
);
assert_eq!(buffer.as_slice(), &input);
}
#[test]
#[should_panic(expected = "assertion failed: last_offset <= buffer.len()")]
fn test_bitwise_unary_op_length_out_of_bounds2() {
let input = vec![0b10101010u8, 0b01010101u8];
let mut buffer = MutableBuffer::new(2); buffer.extend_from_slice(&input); apply_bitwise_unary_op(
buffer.as_slice_mut(),
3, 100, |a| !a,
);
assert_eq!(buffer.as_slice(), &input);
}
}