pub struct BitCount<const MAX: u32> { /* private fields */ }
Expand description
A number of bits to be consumed or written, with a known maximum
Although BitRead::read
and BitWrite::write
should be
preferred when the number of bits is fixed and known at compile-time -
because they can validate the bit count is less than or equal
to the type’s size in bits at compile-time -
there are many instances where bit count is dynamic and
determined by the file format itself.
But when using BitRead::read_var
or BitWrite::write_var
we must pessimistically assume any number of bits as an argument
and validate that the number of bits is not larger than the
type being read or written on every call.
use bitstream_io::{BitRead, BitReader, BigEndian};
let data: &[u8] = &[0b100_0001_1, 0b111_0110_0];
let mut r = BitReader::endian(data, BigEndian);
// our bit count is a 3 bit value
let count = r.read::<3, u32>().unwrap();
// that count indicates we need to read 4 bits (0b100)
assert_eq!(count, 4);
// read the first 4-bit value
assert_eq!(r.read_var::<u8>(count).unwrap(), 0b0001);
// read the second 4-bit value
assert_eq!(r.read_var::<u8>(count).unwrap(), 0b1111);
// read the third 4-bit value
assert_eq!(r.read_var::<u8>(count).unwrap(), 0b0110);
In the preceding example, even though we know count
is a
3 bit value whose maximum value will never be greater than 7,
the subsequent read_var
calls have no way to know that.
They must assume count
could be 9, or u32::MAX
or any other u32
value
and validate the count is not larger than the u8
types we’re reading.
But we can convert our example to use the BitCount
type:
use bitstream_io::{BitRead, BitReader, BigEndian, BitCount};
let data: &[u8] = &[0b100_0001_1, 0b111_0110_0];
let mut r = BitReader::endian(data, BigEndian);
// our bit count is a 3 bit value with a maximum value of 7
let count = r.read_count::<0b111>().unwrap();
// that count indicates we need to read 4 bits (0b100)
assert_eq!(count, BitCount::<7>::new::<4>());
// read the first 4-bit value
assert_eq!(r.read_counted::<7, u8>(count).unwrap(), 0b0001);
// read the second 4-bit value
assert_eq!(r.read_counted::<7, u8>(count).unwrap(), 0b1111);
// read the third 4-bit value
assert_eq!(r.read_counted::<7, u8>(count).unwrap(), 0b0110);
Because the BitRead::read_counted
methods know at compile-time
that the bit count will be larger than 7, that check can be eliminated
simply by taking advantage of information we already know.
Leveraging the BitCount
type also allows us to reason about
bit counts in a more formal way, and use checked permutation methods
to modify them while ensuring they remain constrained by
the file format’s requirements.
Implementations§
Source§impl<const MAX: u32> BitCount<MAX>
impl<const MAX: u32> BitCount<MAX>
Sourcepub const fn new<const BITS: u32>() -> Self
pub const fn new<const BITS: u32>() -> Self
Builds a bit count from a constant number
of bits, which must not be greater than MAX
.
Intended to be used for defining constants.
Use TryFrom
to conditionally build
counts from values at runtime.
§Examples
use bitstream_io::{BitReader, BitRead, BigEndian, BitCount};
let data: &[u8] = &[0b111_00000];
let mut r = BitReader::endian(data, BigEndian);
// reading 3 bits from a stream out of a maximum of 8
// doesn't require checking that the bit count is larger
// than a u8 at runtime because specifying the maximum of 8
// guarantees our bit count will not be larger than 8
assert_eq!(r.read_counted::<8, u8>(BitCount::new::<3>()).unwrap(), 0b111);
use bitstream_io::BitCount;
// trying to build a count of 10 with a maximum of 8
// fails to compile at all
let count = BitCount::<8>::new::<10>();
Sourcepub const fn checked_add<const NEW_MAX: u32>(
self,
bits: u32,
) -> Option<BitCount<NEW_MAX>>
pub const fn checked_add<const NEW_MAX: u32>( self, bits: u32, ) -> Option<BitCount<NEW_MAX>>
Add a number of bits to our count, returning a new count with a new maximum.
Returns None
if the new count goes above our new maximum.
§Examples
use bitstream_io::BitCount;
let count = BitCount::<2>::new::<1>();
// adding 2 to 1 and increasing the max to 3 yields a new count of 3
assert_eq!(count.checked_add::<3>(2), Some(BitCount::<3>::new::<3>()));
// adding 2 to 1 without increasing the max yields None
assert_eq!(count.checked_add::<2>(2), None);
Sourcepub const fn checked_sub<const NEW_MAX: u32>(
self,
bits: u32,
) -> Option<BitCount<NEW_MAX>>
pub const fn checked_sub<const NEW_MAX: u32>( self, bits: u32, ) -> Option<BitCount<NEW_MAX>>
Subtracts a number of bits from our count, returning a new count with a new maximum.
Returns None
if the new count goes below 0
or below our new maximum.
§Example
use bitstream_io::BitCount;
let count = BitCount::<5>::new::<5>();
// subtracting 1 from 5 yields a new count of 4
assert_eq!(count.checked_sub::<5>(1), Some(BitCount::<5>::new::<4>()));
// subtracting 6 from 5 yields None
assert!(count.checked_sub::<5>(6).is_none());
// subtracting 1 with a new maximum of 3 also yields None
// because 4 is larger than the maximum of 3
assert!(count.checked_sub::<3>(1).is_none());
Sourcepub fn try_map<const NEW_MAX: u32, F>(self, f: F) -> Option<BitCount<NEW_MAX>>
pub fn try_map<const NEW_MAX: u32, F>(self, f: F) -> Option<BitCount<NEW_MAX>>
Attempt to convert our count to a count with a new bit count and new maximum.
Returns Some(count)
if the updated number of bits
is less than or equal to the new maximum.
Returns None
if not.
§Examples
use bitstream_io::BitCount;
let count = BitCount::<5>::new::<5>();
// muliplying 5 bits by 2 with a new max of 10 is ok
assert_eq!(
count.try_map::<10, _>(|i| i.checked_mul(2)),
Some(BitCount::<10>::new::<10>()),
);
// multiplying 5 bits by 3 with a new max of 10 overflows
assert_eq!(count.try_map::<10, _>(|i| i.checked_mul(3)), None);
Sourcepub const fn max(&self) -> u32
pub const fn max(&self) -> u32
Returns our maximum bit count
§Example
use bitstream_io::BitCount;
let count = BitCount::<10>::new::<5>();
assert_eq!(count.max(), 10);
Sourcepub const fn signed_count(&self) -> Option<SignedBitCount<MAX>>
pub const fn signed_count(&self) -> Option<SignedBitCount<MAX>>
Returns signed count if our bit count is greater than 0
§Example
use bitstream_io::{BitCount, SignedBitCount};
let count = BitCount::<10>::new::<5>();
assert_eq!(count.signed_count(), Some(SignedBitCount::<10>::new::<5>()));
let count = BitCount::<10>::new::<0>();
assert_eq!(count.signed_count(), None);
Trait Implementations§
Source§impl<const MAX: u32> TryFrom<u32> for BitCount<MAX>
impl<const MAX: u32> TryFrom<u32> for BitCount<MAX>
Source§fn try_from(bits: u32) -> Result<Self, Self::Error>
fn try_from(bits: u32) -> Result<Self, Self::Error>
Attempts to convert a u32
bit count to a BitCount
Attempting a bit maximum bit count larger than the largest supported type is a compile-time error
§Examples
use bitstream_io::BitCount;
use std::convert::TryInto;
assert_eq!(8u32.try_into(), Ok(BitCount::<8>::new::<8>()));
assert_eq!(9u32.try_into(), Err::<BitCount<8>, _>(9));