#![cfg_attr(not(feature = "std"), no_std)]
use shadow_nft_common::array_from_fn;
type LenType = u32;
const LEN_SIZE: usize = ::core::mem::size_of::<LenType>();
const BITS_PER_BYTE: usize = 8;
pub struct ZeroCopyBitSlice<'a> {
bit_bytes: &'a mut [u8],
bit_len: LenType,
}
pub struct ZeroCopyBitSliceRead<'a> {
bit_bytes: &'a [u8],
bit_len: LenType,
}
impl<'a> ZeroCopyBitSlice<'a> {
pub fn intialize_in<'o>(
num_bits: LenType,
bytes: &'o mut &'a mut [u8],
) -> ZeroCopyBitSlice<'a> {
let byte_len = (num_bits as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let required_size = byte_len + LEN_SIZE;
if bytes.len() < required_size {
panic!("buffer provided for initialization is not large enough")
}
bytes[0..LEN_SIZE].copy_from_slice(num_bits.to_le_bytes().as_ref());
let (bit_bytes, rest) = unsafe {
::core::mem::transmute::<(&'o mut [u8], &'o mut [u8]), (&'a mut [u8], &'a mut [u8])>(
bytes.split_at_mut(LEN_SIZE + byte_len),
)
};
*bytes = rest;
ZeroCopyBitSlice {
bit_bytes: &mut bit_bytes[LEN_SIZE..LEN_SIZE + byte_len],
bit_len: num_bits,
}
}
pub fn from_bytes(bytes: &'a mut [u8]) -> ZeroCopyBitSlice<'a> {
let bit_len: LenType = LenType::from_le_bytes(array_from_fn::from_fn(|i| bytes[i]));
let byte_len = (bit_len as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let bit_bytes: &mut [u8] = &mut bytes[LEN_SIZE..LEN_SIZE + byte_len];
ZeroCopyBitSlice { bit_bytes, bit_len }
}
pub fn from_bytes_update<'o>(bytes: &'o mut &'a mut [u8]) -> ZeroCopyBitSlice<'a> {
let bit_len: LenType = LenType::from_le_bytes(array_from_fn::from_fn(|i| bytes[i]));
let byte_len = (bit_len as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let (bit_bytes, rest) = unsafe {
::core::mem::transmute::<(&'o mut [u8], &'o mut [u8]), (&'a mut [u8], &'a mut [u8])>(
bytes[LEN_SIZE..].split_at_mut(byte_len),
)
};
*bytes = rest;
ZeroCopyBitSlice { bit_bytes, bit_len }
}
pub fn bit_len(&self) -> LenType {
self.bit_len
}
pub fn byte_len(&self) -> usize {
self.bit_bytes.len()
}
pub fn set(&mut self, idx: LenType, value: bool) {
if idx >= self.bit_len {
panic!("bit index is out of bounds");
}
let byte_idx: usize = idx as usize / BITS_PER_BYTE;
let rel_bit_idx: u8 = idx as u8 % BITS_PER_BYTE as u8;
if value {
self.bit_bytes[byte_idx] |= 1 << rel_bit_idx;
} else {
self.bit_bytes[byte_idx] &= !(1 << rel_bit_idx);
}
}
pub fn get(&self, idx: LenType) -> bool {
if idx as LenType >= self.bit_len {
panic!("bit index is out of bounds");
}
let byte_idx: usize = (idx as usize) / BITS_PER_BYTE;
let rel_bit_idx: u8 = (idx as u8) % (BITS_PER_BYTE as u8);
(self.bit_bytes[byte_idx] & (1 << rel_bit_idx)) != 0
}
#[cfg(feature = "choose-random-zero")]
pub fn choose_random_zero(&mut self, seed: impl AsRef<[u8]>) -> Option<LenType> {
use sha2::{Digest, Sha256};
let num_zeros: LenType = self.num_zeros();
if num_zeros == 0 {
return None;
}
let seed = {
let mut hasher = Sha256::new();
hasher.update(seed.as_ref());
hasher.finalize()
};
let maybe_oob_index: LenType = LenType::from_le_bytes(array_from_fn::from_fn(|i| seed[i]));
let zero_index = maybe_oob_index % num_zeros;
find_and_set_nth_zero_bit(self.bit_bytes, zero_index, self.bit_len)
}
pub fn num_zeros(&self) -> LenType {
if self.byte_len() == 0 {
return 0;
}
let num_complete_bytes = (self.bit_len as usize)
.checked_div(BITS_PER_BYTE)
.expect("already handled zero byte_len case");
let mut zeros = 0;
for byte in &self.bit_bytes[0..num_complete_bytes] {
zeros += byte.count_zeros();
}
if num_complete_bytes < self.byte_len() {
let last_byte = *self
.bit_bytes
.last()
.expect("already handled zero byte_len case");
let up_to = (self.bit_len() % 8) as u8 - 1;
zeros += count_zero_bits_in_byte_up_to_bit(last_byte, up_to);
}
zeros
}
pub const fn required_space(num_bits: LenType) -> usize {
LEN_SIZE + (num_bits as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE
}
}
fn find_and_set_nth_zero_bit(bytes: &mut [u8], nth: LenType, bit_len: LenType) -> Option<LenType> {
let mut zero_count: LenType = 0;
for (&byte, byte_idx) in bytes.iter().zip(0..) {
let byte_zero_count = byte.count_zeros() as LenType;
if zero_count + byte_zero_count > nth {
for rel_bit_idx in 0..8 {
if byte_idx * 8 + rel_bit_idx == bit_len {
return None;
}
if (byte & (1 << rel_bit_idx)) == 0 {
if zero_count == nth {
bytes[byte_idx as usize] |= 1 << rel_bit_idx;
return Some(byte_idx * 8 + rel_bit_idx);
}
zero_count += 1;
}
}
} else {
zero_count += byte_zero_count;
}
}
None
}
impl<'a> ZeroCopyBitSliceRead<'a> {
pub unsafe fn from_bytes(bytes: &'a [u8]) -> ZeroCopyBitSliceRead<'a> {
let bit_len: LenType = LenType::from_le_bytes(array_from_fn::from_fn(|i| bytes[i]));
let byte_len = (bit_len as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let bit_bytes: &[u8] = &bytes[LEN_SIZE..LEN_SIZE + byte_len];
ZeroCopyBitSliceRead { bit_bytes, bit_len }
}
pub fn num_zeros(&self) -> LenType {
if self.byte_len() == 0 {
return 0;
}
let num_complete_bytes = (self.bit_len as usize)
.checked_div(BITS_PER_BYTE)
.expect("already handled zero byte_len case");
let mut zeros = 0;
for byte in &self.bit_bytes[0..num_complete_bytes] {
zeros += byte.count_zeros();
}
if num_complete_bytes < self.byte_len() {
let last_byte = *self
.bit_bytes
.last()
.expect("already handled zero byte_len case");
let up_to = (self.bit_len() % 8) as u8 - 1;
zeros += count_zero_bits_in_byte_up_to_bit(last_byte, up_to);
}
zeros
}
pub fn bit_len(&self) -> LenType {
self.bit_len
}
pub fn byte_len(&self) -> usize {
self.bit_bytes.len()
}
}
#[test]
fn test_find_and_set_bit() {
let mut bytes = [0b_0000_0010, 0b_1010_1010];
let expected_ = [0b_0100_1010, 0b_1110_1010];
let exp_idxs = [3, 6, 14];
let idx1 = find_and_set_nth_zero_bit(&mut bytes, 2, 16).unwrap();
let idx2 = find_and_set_nth_zero_bit(&mut bytes, 4, 16).unwrap();
let later_test = bytes;
let idx3 = find_and_set_nth_zero_bit(&mut bytes, 8, 16).unwrap();
assert_eq!(idx1, exp_idxs[0]);
assert_eq!(idx2, exp_idxs[1]);
assert_eq!(idx3, exp_idxs[2]);
assert_eq!(bytes, expected_);
let idx3_2 = find_and_set_nth_zero_bit(&mut later_test.clone(), 8, 15).unwrap();
assert_eq!(idx3, idx3_2);
assert!(find_and_set_nth_zero_bit(&mut later_test.clone(), 8, 14).is_none());
}
#[inline(always)]
fn count_zero_bits_in_byte_up_to_bit(byte: u8, up_to_bit: u8) -> LenType {
(!byte << (7 - up_to_bit)).count_ones()
}
#[test]
fn test_count_zero_up_to() {
for up_to_bit in 0..=7 {
for byte in 0..=u8::MAX {
let count = count_zero_bits_in_byte_up_to_bit(byte, up_to_bit);
assert!(
count <= up_to_bit as u32 + 1,
"got {count} <= {up_to_bit} for {byte:08b} up to {up_to_bit}"
);
}
}
let few_cases = [0b_0000_0100, 0b_0010_0010, 0b_1010_1010, 0b_1111_0111];
let expected_3 = [3, 3, 2, 1];
let expected_5 = [5, 4, 3, 1];
for i in 0..4 {
assert_eq!(
count_zero_bits_in_byte_up_to_bit(few_cases[i], 3),
expected_3[i]
);
assert_eq!(
count_zero_bits_in_byte_up_to_bit(few_cases[i], 5),
expected_5[i]
);
}
}
#[test]
fn test_deserialization() {
const BIT_LEN: LenType = 9;
const BYTE_LEN: usize = (BIT_LEN as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let [d1, d2] = [7, 3];
let [l1, l2, l3, l4] = LenType::to_le_bytes(BIT_LEN);
let [flags1, flags2] = [0b10101010, 0b10000000];
let [d3, d4] = [4, 5];
let mut bytes: Vec<u8> = vec![d1, d2, l1, l2, l3, l4, flags1, flags2, d3, d4];
let zcbs = ZeroCopyBitSlice::from_bytes(&mut bytes[2..]);
assert_eq!(zcbs.bit_len(), BIT_LEN);
assert_eq!(zcbs.byte_len(), BYTE_LEN);
assert_eq!(zcbs.bit_bytes, &[flags1, flags2]);
}
#[test]
fn test_initialization() {
const BIT_LEN: LenType = 9;
const BYTE_LEN: usize = (BIT_LEN as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let mut buffer = [0; BYTE_LEN + LEN_SIZE];
let mut buf_slice = buffer.as_mut_slice();
let zcbs = ZeroCopyBitSlice::intialize_in(BIT_LEN, &mut buf_slice);
assert_eq!(zcbs.bit_len(), BIT_LEN);
assert_eq!(zcbs.byte_len(), BYTE_LEN);
drop(zcbs);
assert_eq!(buf_slice.len(), 0);
let bit_len: LenType = LenType::from_le_bytes(array_from_fn::from_fn(|i| buffer[i]));
assert_eq!(bit_len, BIT_LEN);
}
#[test]
fn test_reads_and_writes() {
const BIT_LEN: LenType = 9;
const BYTE_LEN: usize = (BIT_LEN as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let mut zcbs = ZeroCopyBitSlice {
bit_bytes: &mut [0; BYTE_LEN],
bit_len: BIT_LEN,
};
for bit_idx in 0..BIT_LEN {
assert!(!zcbs.get(bit_idx));
}
for bit_idx in 0..BIT_LEN {
zcbs.set(bit_idx, bit_idx % 2 == 0);
}
for bit_idx in 0..BIT_LEN {
assert_eq!(zcbs.get(bit_idx), bit_idx % 2 == 0);
}
for bit_idx in 0..BIT_LEN {
zcbs.set(bit_idx, bit_idx % 2 != 0);
}
for bit_idx in 0..BIT_LEN {
assert_eq!(zcbs.get(bit_idx), bit_idx % 2 != 0);
}
}
#[test]
#[cfg(feature = "choose-random-zero")]
fn test_get_random() {
const BIT_LEN: LenType = 9;
const BYTE_LEN: usize = (BIT_LEN as usize + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let expected_sorted = array_from_fn::from_fn(|i| i as LenType);
let mut unequal_counter = 0;
const TRIALS: u16 = 128;
for trial in 0..TRIALS {
let mut zcbs = ZeroCopyBitSlice {
bit_bytes: &mut [0; BYTE_LEN],
bit_len: BIT_LEN,
};
let seed = trial.to_le_bytes();
let mut sequence: [LenType; BIT_LEN as usize] = array_from_fn::from_fn(|i| {
assert_eq!(zcbs.num_zeros(), BIT_LEN - i as LenType);
zcbs.choose_random_zero(seed).unwrap()
});
if sequence != expected_sorted {
unequal_counter += 1;
}
sequence.sort();
assert_eq!(sequence, expected_sorted);
}
assert!(unequal_counter > 3 * TRIALS / 4);
}