pub struct SignedBitCount<const MAX: u32> { /* private fields */ }
Expand description
A number of bits to be read or written for signed integers, with a known maximum
This is closely related to the BitCount
type, but further constrained
to have a minimum value of 1 - because signed values require at least
1 bit for the sign.
Let’s start with a basic example:
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 signed value
assert_eq!(r.read_var::<i8>(count).unwrap(), 1);
// read the second 4-bit signed value
assert_eq!(r.read_var::<i8>(count).unwrap(), -1);
// read the third 4-bit signed value
assert_eq!(r.read_var::<i8>(count).unwrap(), 6);
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 i8
types we’re reading
while also greater than 0 because i8
requires a sign bit.
But we can convert our example to use the SignedBitCount
type:
use bitstream_io::{BitRead, BitReader, BigEndian, SignedBitCount};
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();
// convert that count to a signed bit count,
// which guarantees its value is greater than 0
let count = count.signed_count().unwrap();
// that count indicates we need to read 4 bits (0b100)
assert_eq!(count, SignedBitCount::<7>::new::<4>());
// read the first 4-bit value
assert_eq!(r.read_signed_counted::<7, i8>(count).unwrap(), 1);
// read the second 4-bit value
assert_eq!(r.read_signed_counted::<7, i8>(count).unwrap(), -1);
// read the third 4-bit value
assert_eq!(r.read_signed_counted::<7, i8>(count).unwrap(), 6);
Because the BitRead::read_signed_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 SignedBitCount
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> SignedBitCount<MAX>
impl<const MAX: u32> SignedBitCount<MAX>
Sourcepub const fn new<const BITS: u32>() -> Self
pub const fn new<const BITS: u32>() -> Self
Builds a signed bit count from a constant number
of bits, which must be greater than 0 and
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, SignedBitCount};
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_signed_counted::<8, i8>(SignedBitCount::new::<3>()).unwrap(), -1);
use bitstream_io::SignedBitCount;
// trying to build a count of 10 with a maximum of 8
// fails to compile at all
let count = SignedBitCount::<8>::new::<10>();
use bitstream_io::SignedBitCount;
// trying to build a count of 0 also fails to compile
let count = SignedBitCount::<8>::new::<0>();
Sourcepub const fn checked_add<const NEW_MAX: u32>(
self,
bits: u32,
) -> Option<SignedBitCount<NEW_MAX>>
pub const fn checked_add<const NEW_MAX: u32>( self, bits: u32, ) -> Option<SignedBitCount<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::SignedBitCount;
let count = SignedBitCount::<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(SignedBitCount::<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<SignedBitCount<NEW_MAX>>
pub const fn checked_sub<const NEW_MAX: u32>( self, bits: u32, ) -> Option<SignedBitCount<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 1
or below our new maximum.
§Example
use bitstream_io::SignedBitCount;
let count = SignedBitCount::<5>::new::<5>();
// subtracting 1 from 5 yields a new count of 4
assert_eq!(count.checked_sub::<5>(1), Some(SignedBitCount::<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());
// subtracting 5 from 5 also yields None
// because SignedBitCount always requires 1 bit for the sign
assert!(count.checked_sub::<5>(5).is_none());
Sourcepub fn try_map<const NEW_MAX: u32, F>(
self,
f: F,
) -> Option<SignedBitCount<NEW_MAX>>
pub fn try_map<const NEW_MAX: u32, F>( self, f: F, ) -> Option<SignedBitCount<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
and greater than 0.
Returns None
if not.
§Examples
use bitstream_io::SignedBitCount;
let count = SignedBitCount::<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(SignedBitCount::<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);
// multiplying 5 bits by 0 results in 0 bits,
// which isn't value for a SignedBitCount
assert_eq!(count.try_map::<10, _>(|i| Some(i * 0)), None);
Trait Implementations§
Source§impl<const MAX: u32> Clone for SignedBitCount<MAX>
impl<const MAX: u32> Clone for SignedBitCount<MAX>
Source§fn clone(&self) -> SignedBitCount<MAX>
fn clone(&self) -> SignedBitCount<MAX>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read more