diff --git a/source/durandal/bin.rs b/source/durandal/bin.rs index 2854015..ed2fa7a 100644 --- a/source/durandal/bin.rs +++ b/source/durandal/bin.rs @@ -131,7 +131,8 @@ macro_rules! _durandal_read_impl { /// /// # Panics /// -/// This macro will not panic unless any index expression used exceeds `size`. +/// This macro will not panic unless any index expression used exceeds or +/// equals `size`. /// /// # Examples /// diff --git a/source/durandal/bit.rs b/source/durandal/bit.rs new file mode 100644 index 0000000..327db69 --- /dev/null +++ b/source/durandal/bit.rs @@ -0,0 +1,110 @@ +//! Bit streams. + +use crate::durandal::err::*; + +/// Reads `width` bits from `b` starting at bit `cr_ptr` into an integer. +/// +/// # Errors +/// +/// The function will return an error if `width` is 0 or more than 64, or if +/// `(cr_ptr + width - 1) / 8` would overflow an index into `b`. +pub fn read_bits(b: &[u8], cr_ptr: usize, width: u8) -> ResultS<(usize, u64)> +{ + if width < 1 || width > 64 { + bail!("invalid number of bits"); + } + + let nx_ptr = cr_ptr + usize::from(width); + let ed_ptr = nx_ptr - 1; + + let first_byte = cr_ptr / 8; + let last_byte = ed_ptr / 8; + + if b.len() <= last_byte { + bail!("not enough data"); + } + + let first_bits = cr_ptr as u32 % 8; + let last_bits = nx_ptr as u32 % 8; + + let last_mask = ((1u128 << last_bits) - 1) as u64; + + let num_bytes = last_byte - first_byte; + + let bytes = get_bytes(b, num_bytes, first_byte); + + let bits = u64::from_be_bytes(bytes); + let bits = bits.wrapping_shl(first_bits); + let bits = bits >> (64 - width); + + // special case for over byte boundary + let bits = if num_bytes == 8 { + let lbyt = u64::from(b[last_byte]); + let lbyt = lbyt >> (8 - last_bits); + let lbyt = lbyt & last_mask; + + (bits & !last_mask) | lbyt + } else { + bits + }; + + Ok((nx_ptr, bits)) +} + +fn get_bytes(b: &[u8], num_bytes: usize, f: usize) -> [u8; 8] +{ + match num_bytes { + 8 | + 7 => [b[f], b[f+1], b[f+2], b[f+3], b[f+4], b[f+5], b[f+6], b[f+7]], + 6 => [b[f], b[f+1], b[f+2], b[f+3], b[f+4], b[f+5], b[f+6], 0], + 5 => [b[f], b[f+1], b[f+2], b[f+3], b[f+4], b[f+5], 0, 0], + 4 => [b[f], b[f+1], b[f+2], b[f+3], b[f+4], 0, 0, 0], + 3 => [b[f], b[f+1], b[f+2], b[f+3], 0, 0, 0, 0], + 2 => [b[f], b[f+1], b[f+2], 0, 0, 0, 0, 0], + 1 => [b[f], b[f+1], 0, 0, 0, 0, 0, 0], + 0 => [b[f], 0, 0, 0, 0, 0, 0, 0], + _ => panic!("invalid number of bytes to read ({})", num_bytes), + } +} + +#[test] +fn bit_tests() +{ + const INPUT: &[u8] = &[0b011_00101, 0b10101010, 0b00010000, 0b00000000, + 0b11111111, 0b11100001, 0b10101100, 0b00110011, + 0b10_1001_01, 0b11100_000, 0b00000111, 0b000000_01, + 0b11001010, 0b10101111, 0b00101011, 0b0_1101010, + 0b11010101, 0b10100011, 0b01010101, 0b11_000001]; + + let (p, n) = read_bits(INPUT, 0, 3).unwrap(); + assert_eq!(n, 0b011); + + let (p, n) = read_bits(INPUT, p, 63).unwrap(); + assert_eq!(n, 0x16A8_4003_FF86_B0CE); + + let (p, n) = read_bits(INPUT, p, 4).unwrap(); + assert_eq!(n, 0b1001); + + let (p, n) = read_bits(INPUT, p, 7).unwrap(); + assert_eq!(n, 0b0111100); + + let (p, n) = read_bits(INPUT, p, 17).unwrap(); + assert_eq!(n, 0b00000000111000000); + + let (p, n) = read_bits(INPUT, p, 27).unwrap(); + assert_eq!(n, 0b011100101010101111001010110); + + let (p, n) = read_bits(INPUT, p, 33).unwrap(); + assert_eq!(n, 0b110101011010101101000110101010111); + + let (p, n) = read_bits(INPUT, p, 6).unwrap(); + assert_eq!(n, 1); + + let e = read_bits(INPUT, p, 1); + assert!(if let Err(_) = e {true} else {false}); + + let e = read_bits(INPUT, p, 2); + assert!(if let Err(_) = e {true} else {false}); +} + +// EOF diff --git a/source/durandal/mod.rs b/source/durandal/mod.rs index 0848bf3..697ebff 100644 --- a/source/durandal/mod.rs +++ b/source/durandal/mod.rs @@ -7,6 +7,7 @@ pub mod cenum; #[macro_use] pub mod bin; +pub mod bit; pub mod crc; pub mod file; pub mod fixed;