Struct SignedBitCount

Source
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>

Source

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>();
Source

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);
Source

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());
Source

pub fn try_map<const NEW_MAX: u32, F>( self, f: F, ) -> Option<SignedBitCount<NEW_MAX>>
where F: FnOnce(u32) -> Option<u32>,

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);
Source

pub const fn max(&self) -> u32

Returns our maximum bit count

§Example
use bitstream_io::SignedBitCount;

let count = SignedBitCount::<10>::new::<5>();
assert_eq!(count.max(), 10);
Source

pub const fn count(&self) -> BitCount<MAX>

Returns regular unsigned bit count

§Example
use bitstream_io::{BitCount, SignedBitCount};

let signed_count = SignedBitCount::<10>::new::<5>();
assert_eq!(signed_count.count(), BitCount::<10>::new::<5>());

Trait Implementations§

Source§

impl<const MAX: u32> Clone for SignedBitCount<MAX>

Source§

fn clone(&self) -> SignedBitCount<MAX>

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<const MAX: u32> Debug for SignedBitCount<MAX>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<const MAX: u32> From<SignedBitCount<MAX>> for u32

Source§

fn from(_: SignedBitCount<MAX>) -> u32

Converts to this type from the input type.
Source§

impl<const MAX: u32> Hash for SignedBitCount<MAX>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<const MAX: u32> PartialEq for SignedBitCount<MAX>

Source§

fn eq(&self, other: &SignedBitCount<MAX>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<const MAX: u32> TryFrom<BitCount<MAX>> for SignedBitCount<MAX>

Source§

type Error = ()

The type returned in the event of a conversion error.
Source§

fn try_from(count: BitCount<MAX>) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl<const MAX: u32> TryFrom<u32> for SignedBitCount<MAX>

Source§

type Error = u32

The type returned in the event of a conversion error.
Source§

fn try_from(count: u32) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl<const MAX: u32> Copy for SignedBitCount<MAX>

Source§

impl<const MAX: u32> Eq for SignedBitCount<MAX>

Source§

impl<const MAX: u32> StructuralPartialEq for SignedBitCount<MAX>

Auto Trait Implementations§

§

impl<const MAX: u32> Freeze for SignedBitCount<MAX>

§

impl<const MAX: u32> RefUnwindSafe for SignedBitCount<MAX>

§

impl<const MAX: u32> Send for SignedBitCount<MAX>

§

impl<const MAX: u32> Sync for SignedBitCount<MAX>

§

impl<const MAX: u32> Unpin for SignedBitCount<MAX>

§

impl<const MAX: u32> UnwindSafe for SignedBitCount<MAX>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.