Skip to content

Commit f92e87c

Browse files
committed
Add FixedSignedBitCount and CheckedSignedFixed
These add consistency with their unsigned variants: FixedBitCount and CheckedUnsignedFixed
1 parent e583b49 commit f92e87c

File tree

1 file changed

+166
-2
lines changed

1 file changed

+166
-2
lines changed

src/lib.rs

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,11 @@ impl<const MAX: u32, U: UnsignedInteger> CheckedUnsigned<MAX, U> {
21272127
/// let v3 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
21282128
/// let v4 = r.read_checked::<FourBits>(FixedBitCount).unwrap();
21292129
///
2130+
/// assert_eq!(v1.into_value(), 0b0001);
2131+
/// assert_eq!(v2.into_value(), 0b1111);
2132+
/// assert_eq!(v3.into_value(), 0b0110);
2133+
/// assert_eq!(v4.into_value(), 0b1001);
2134+
///
21302135
/// // write those same values back to disk
21312136
/// let mut w = BitWriter::endian(vec![], BigEndian);
21322137
/// w.write_checked(v1).unwrap();
@@ -2160,7 +2165,7 @@ impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<BitCount<MAX>>
21602165
pub type CheckedUnsignedFixed<const BITS: u32, T> = Checked<FixedBitCount<BITS>, T>;
21612166

21622167
impl<const BITS: u32, U: UnsignedInteger> CheckedUnsignedFixed<BITS, U> {
2163-
/// Returns our value if it fits in the given number of const bits
2168+
/// Returns our checked value if it fits in the given number of const bits
21642169
///
21652170
/// # Examples
21662171
///
@@ -2178,7 +2183,7 @@ impl<const BITS: u32, U: UnsignedInteger> CheckedUnsignedFixed<BITS, U> {
21782183
/// ```
21792184
///
21802185
/// ```compile_fail
2181-
/// use bitstream_io::{BitCount, CheckedUnsignedFixed};
2186+
/// use bitstream_io::CheckedUnsignedFixed;
21822187
///
21832188
/// // a bit count of 9 is too large for u8
21842189
///
@@ -2285,6 +2290,7 @@ impl<const MAX: u32, S: SignedInteger> CheckablePrimitive for CheckedSigned<MAX,
22852290
}
22862291

22872292
impl<const MAX: u32, S: SignedInteger> private::Checkable for CheckedSigned<MAX, S> {
2293+
#[inline]
22882294
fn write_endian<E, W>(
22892295
self,
22902296
writer: &mut W,
@@ -2389,6 +2395,164 @@ impl<const MAX: u32, S: SignedInteger> CheckedSigned<MAX, S> {
23892395
}
23902396
}
23912397

2398+
/// A fixed number of bits to be consumed or written
2399+
///
2400+
/// Analagous to [`SignedBitCount`], this is a zero-sized type
2401+
/// whose value is fixed at compile-time and cannot be changed.
2402+
///
2403+
/// # Example
2404+
///
2405+
/// ```
2406+
/// use bitstream_io::{
2407+
/// BigEndian, BitRead, BitReader, BitWrite, BitWriter,
2408+
/// CheckedSignedFixed, FixedSignedBitCount,
2409+
/// };
2410+
///
2411+
/// type FourBits = CheckedSignedFixed<4, i8>;
2412+
///
2413+
/// let input: &[u8] = &[0b0001_1111, 0b0110_1001];
2414+
/// let mut r = BitReader::endian(input, BigEndian);
2415+
///
2416+
/// // read 4, 4-bit values
2417+
/// let v1 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
2418+
/// let v2 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
2419+
/// let v3 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
2420+
/// let v4 = r.read_checked::<FourBits>(FixedSignedBitCount).unwrap();
2421+
///
2422+
/// assert_eq!(v1.into_value(), 1);
2423+
/// assert_eq!(v2.into_value(), -1);
2424+
/// assert_eq!(v3.into_value(), 6);
2425+
/// assert_eq!(v4.into_value(), -7);
2426+
///
2427+
/// // write those same values back to disk
2428+
/// let mut w = BitWriter::endian(vec![], BigEndian);
2429+
/// w.write_checked(v1).unwrap();
2430+
/// w.write_checked(v2).unwrap();
2431+
/// w.write_checked(v3).unwrap();
2432+
/// w.write_checked(v4).unwrap();
2433+
///
2434+
/// // ensure they're the same
2435+
/// assert_eq!(w.into_writer().as_slice(), input);
2436+
/// ```
2437+
#[derive(Copy, Clone, Debug)]
2438+
pub struct FixedSignedBitCount<const BITS: u32>;
2439+
2440+
impl<const BITS: u32> From<FixedSignedBitCount<BITS>> for SignedBitCount<BITS> {
2441+
fn from(_count: FixedSignedBitCount<BITS>) -> Self {
2442+
SignedBitCount::new::<BITS>()
2443+
}
2444+
}
2445+
2446+
impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<SignedBitCount<MAX>>
2447+
for FixedSignedBitCount<BITS>
2448+
{
2449+
type Error = SignedBitCount<MAX>;
2450+
2451+
fn try_from(count: SignedBitCount<MAX>) -> Result<Self, Self::Error> {
2452+
(count.bits.bits == BITS)
2453+
.then_some(FixedSignedBitCount)
2454+
.ok_or(count)
2455+
}
2456+
}
2457+
2458+
/// A signed type with a verified value for a fixed number of bits
2459+
pub type CheckedSignedFixed<const BITS: u32, T> = Checked<FixedSignedBitCount<BITS>, T>;
2460+
2461+
impl<const BITS: u32, S: SignedInteger> CheckedSignedFixed<BITS, S> {
2462+
/// Returns our checked value if it fits in the given number of const bits
2463+
///
2464+
/// # Examples
2465+
///
2466+
/// ```
2467+
/// use bitstream_io::{SignedBitCount, CheckedSignedFixed, CheckedError};
2468+
///
2469+
/// // a value of 3 fits into a 3 bit count
2470+
/// assert!(CheckedSignedFixed::<3, _>::new_fixed(3i8).is_ok());
2471+
///
2472+
/// // a value of 4 does not fit into a 3 bit count
2473+
/// assert!(matches!(
2474+
/// CheckedSignedFixed::<3, _>::new_fixed(4i8),
2475+
/// Err(CheckedError::ExcessiveValue),
2476+
/// ));
2477+
/// ```
2478+
///
2479+
/// ```compile_fail
2480+
/// use bitstream_io::CheckedSignedFixed;
2481+
///
2482+
/// // a bit count of 9 is too large for i8
2483+
///
2484+
/// // because this is checked at compile-time,
2485+
/// // it does not compile at all
2486+
/// let c = CheckedSignedFixed::<9, _>::new_fixed(1i8);
2487+
/// ```
2488+
pub fn new_fixed(value: S) -> Result<Self, CheckedError> {
2489+
const {
2490+
assert!(BITS <= S::BITS_SIZE, "excessive bits for type written");
2491+
}
2492+
2493+
if BITS == S::BITS_SIZE
2494+
|| (((S::ZERO - S::ONE) << (BITS - 1)) <= value && value < (S::ONE << (BITS - 1)))
2495+
{
2496+
Ok(Self {
2497+
count: FixedSignedBitCount,
2498+
value,
2499+
})
2500+
} else {
2501+
Err(CheckedError::ExcessiveValue)
2502+
}
2503+
}
2504+
}
2505+
impl<const BITS: u32, S: SignedInteger> Checkable for CheckedSignedFixed<BITS, S> {
2506+
#[inline]
2507+
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
2508+
// a naive default implementation
2509+
writer.write_signed::<BITS, _>(self.value)
2510+
}
2511+
2512+
#[inline]
2513+
fn written_bits(&self) -> u32 {
2514+
BITS
2515+
}
2516+
}
2517+
2518+
impl<const BITS: u32, S: SignedInteger> private::Checkable for CheckedSignedFixed<BITS, S> {
2519+
#[inline]
2520+
fn write_endian<E, W>(
2521+
self,
2522+
writer: &mut W,
2523+
queue_value: &mut u8,
2524+
queue_bits: &mut u32,
2525+
) -> io::Result<()>
2526+
where
2527+
E: private::Endianness,
2528+
W: io::Write,
2529+
{
2530+
E::write_signed_bits_checked(
2531+
writer,
2532+
queue_value,
2533+
queue_bits,
2534+
CheckedSigned {
2535+
value: self.value,
2536+
count: self.count.into(),
2537+
},
2538+
)
2539+
}
2540+
}
2541+
2542+
impl<const BITS: u32, S: SignedInteger> CheckablePrimitive for CheckedSignedFixed<BITS, S> {
2543+
type CountType = FixedSignedBitCount<BITS>;
2544+
2545+
fn read<R: BitRead + ?Sized>(
2546+
reader: &mut R,
2547+
count: FixedSignedBitCount<BITS>,
2548+
) -> std::io::Result<Self> {
2549+
Ok(Self {
2550+
value: reader.read_signed::<BITS, _>()?,
2551+
count,
2552+
})
2553+
}
2554+
}
2555+
23922556
/// A trait for writable types whose values can be validated
23932557
///
23942558
/// Ordinarily, when writing a value to a stream with a given

0 commit comments

Comments
 (0)