op_alloy_protocol/batch/
bits.rsuse crate::SpanBatchError;
use alloc::{vec, vec::Vec};
use alloy_rlp::Buf;
use core::cmp::Ordering;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SpanBatchBits(Vec<u8>);
impl AsRef<[u8]> for SpanBatchBits {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl SpanBatchBits {
pub const fn new(inner: Vec<u8>) -> Self {
Self(inner)
}
pub fn decode(b: &mut &[u8], bit_length: usize) -> Result<Self, SpanBatchError> {
let buffer_len = bit_length / 8 + if bit_length % 8 != 0 { 1 } else { 0 };
let bits = if b.len() < buffer_len {
let mut bits = vec![0; buffer_len];
bits[..b.len()].copy_from_slice(b);
b.advance(b.len());
bits
} else {
let v = b[..buffer_len].to_vec();
b.advance(buffer_len);
v
};
let sb_bits = Self(bits);
if sb_bits.bit_len() > bit_length {
return Err(SpanBatchError::BitfieldTooLong);
}
Ok(sb_bits)
}
pub fn encode(w: &mut Vec<u8>, bit_length: usize, bits: &Self) -> Result<(), SpanBatchError> {
if bits.bit_len() > bit_length {
return Err(SpanBatchError::BitfieldTooLong);
}
let buf_len = bit_length / 8 + if bit_length % 8 != 0 { 1 } else { 0 };
let mut buf = vec![0; buf_len];
buf[buf_len - bits.0.len()..].copy_from_slice(bits.as_ref());
w.extend_from_slice(&buf);
Ok(())
}
pub fn get_bit(&self, index: usize) -> Option<u8> {
let byte_index = index / 8;
let bit_index = index % 8;
if byte_index < self.0.len() {
let byte = self.0[self.0.len() - byte_index - 1];
Some(if byte & (1 << bit_index) != 0 { 1 } else { 0 })
} else {
None
}
}
pub fn set_bit(&mut self, index: usize, value: bool) {
let byte_index = index / 8;
let bit_index = index % 8;
if byte_index >= self.0.len() {
Self::resize_from_right(&mut self.0, byte_index + 1);
}
let len = self.0.len();
let byte = &mut self.0[len - byte_index - 1];
if value {
*byte |= 1 << bit_index;
} else {
*byte &= !(1 << bit_index);
}
}
pub fn bit_len(&self) -> usize {
for (i, &byte) in self.0.iter().enumerate() {
if byte != 0 {
let msb_index = 7 - byte.leading_zeros() as usize; let total_bit_length = msb_index + 1 + ((self.0.len() - i - 1) * 8);
return total_bit_length;
}
}
0
}
fn resize_from_right<T: Default + Clone>(vec: &mut Vec<T>, new_size: usize) {
let current_size = vec.len();
match new_size.cmp(¤t_size) {
Ordering::Less => {
let remove_count = current_size - new_size;
vec.drain(0..remove_count);
}
Ordering::Greater => {
let additional = new_size - current_size;
let mut prepend_elements = vec![T::default(); additional];
prepend_elements.append(vec);
*vec = prepend_elements;
}
Ordering::Equal => { }
}
}
}
#[cfg(test)]
mod test {
use super::*;
use proptest::{collection::vec, prelude::any, proptest};
proptest! {
#[test]
fn test_encode_decode_roundtrip_span_bitlist(vec in vec(any::<u8>(), 0..5096)) {
let bits = SpanBatchBits(vec);
assert_eq!(SpanBatchBits::decode(&mut bits.as_ref(), bits.0.len() * 8).unwrap(), bits);
let mut encoded = Vec::new();
SpanBatchBits::encode(&mut encoded, bits.0.len() * 8, &bits).unwrap();
assert_eq!(encoded, bits.0);
}
#[test]
fn test_span_bitlist_bitlen(index in 0usize..65536) {
let mut bits = SpanBatchBits::default();
bits.set_bit(index, true);
assert_eq!(bits.0.len(), (index / 8) + 1);
assert_eq!(bits.bit_len(), index + 1);
}
#[test]
fn test_span_bitlist_bitlen_shrink(first_index in 8usize..65536) {
let second_index = first_index.clamp(0, first_index - 8);
let mut bits = SpanBatchBits::default();
bits.set_bit(first_index, true);
assert_eq!(bits.0.len(), (first_index / 8) + 1);
assert_eq!(bits.bit_len(), first_index + 1);
bits.set_bit(first_index, false);
assert_eq!(bits.0.len(), (first_index / 8) + 1);
assert_eq!(bits.bit_len(), 0);
bits.set_bit(second_index, true);
assert_eq!(bits.0.len(), (first_index / 8) + 1);
assert_eq!(bits.bit_len(), second_index + 1);
}
}
#[test]
fn bitlist_big_endian_zero_extended() {
let mut bits = SpanBatchBits::default();
bits.set_bit(1, true);
bits.set_bit(6, true);
bits.set_bit(8, true);
bits.set_bit(15, true);
assert_eq!(bits.0[0], 0b1000_0001);
assert_eq!(bits.0[1], 0b0100_0010);
assert_eq!(bits.0.len(), 2);
assert_eq!(bits.bit_len(), 16);
}
#[test]
fn test_static_set_get_bits_span_bitlist() {
let mut bits = SpanBatchBits::default();
assert!(bits.0.is_empty());
bits.set_bit(0, true);
bits.set_bit(1, true);
bits.set_bit(2, true);
bits.set_bit(4, true);
bits.set_bit(7, true);
assert_eq!(bits.0.len(), 1);
assert_eq!(bits.get_bit(0), Some(1));
assert_eq!(bits.get_bit(1), Some(1));
assert_eq!(bits.get_bit(2), Some(1));
assert_eq!(bits.get_bit(3), Some(0));
assert_eq!(bits.get_bit(4), Some(1));
bits.set_bit(17, true);
assert_eq!(bits.get_bit(17), Some(1));
assert_eq!(bits.get_bit(32), None);
assert_eq!(bits.0.len(), 3);
}
}