Skip to content

Commit

Permalink
Add fixed length signed readers to Endianness trait
Browse files Browse the repository at this point in the history
As with the writer, this shifts a bit more math to compile-time.
  • Loading branch information
tuffy committed Jun 14, 2024
1 parent bdeedfd commit 10a4a61
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
52 changes: 51 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,18 @@ pub trait SignedNumeric: Numeric {
/// returns this value as a negative number.
fn as_negative(self, bits: u32) -> Self;

/// Given a two-complement positive value and certain number of bits,
/// returns this value as a negative number.
fn as_negative_fixed<const BITS: u32>(self) -> Self;

/// Given a negative value and a certain number of bits,
/// returns this value as a twos-complement positive number.
fn as_unsigned(self, bits: u32) -> Self;

/// Given a negative value and a certain number of bits,
/// returns this value as a twos-complement positive number.
fn as_unsigned_fixed<const BITS: u32>(self) -> Self;

/// Converts to a generic signed value for stream recording purposes.
fn signed_value(self) -> write::SignedValue;
}
Expand All @@ -266,10 +274,18 @@ macro_rules! define_signed_numeric {
self + (-1 << (bits - 1))
}
#[inline(always)]
fn as_negative_fixed<const BITS: u32>(self) -> Self {
self + (-1 << (BITS - 1))
}
#[inline(always)]
fn as_unsigned(self, bits: u32) -> Self {
self - (-1 << (bits - 1))
}
#[inline(always)]
fn as_unsigned_fixed<const BITS: u32>(self) -> Self {
self - (-1 << (BITS - 1))
}
#[inline(always)]
fn signed_value(self) -> write::SignedValue {
self.into()
}
Expand Down Expand Up @@ -353,6 +369,12 @@ pub trait Endianness: Sized {
R: BitRead,
S: SignedNumeric;

/// Reads signed value from reader in this endianness
fn read_signed_fixed<R, const B: u32, S>(r: &mut R) -> io::Result<S>
where
R: BitRead,
S: SignedNumeric;

/// Writes signed value to writer in this endianness
fn write_signed<W, S>(w: &mut W, bits: u32, value: S) -> io::Result<()>
where
Expand Down Expand Up @@ -509,6 +531,20 @@ impl Endianness for BigEndian {
})
}

fn read_signed_fixed<R, const B: u32, S>(r: &mut R) -> io::Result<S>
where
R: BitRead,
S: SignedNumeric,
{
let is_negative = r.read_bit()?;
let unsigned = r.read::<S>(B - 1)?;
Ok(if is_negative {
unsigned.as_negative_fixed::<B>()
} else {
unsigned
})
}

fn write_signed<W, S>(w: &mut W, bits: u32, value: S) -> io::Result<()>
where
W: BitWrite,
Expand Down Expand Up @@ -691,6 +727,20 @@ impl Endianness for LittleEndian {
})
}

fn read_signed_fixed<R, const B: u32, S>(r: &mut R) -> io::Result<S>
where
R: BitRead,
S: SignedNumeric,
{
let unsigned = r.read::<S>(B - 1)?;
let is_negative = r.read_bit()?;
Ok(if is_negative {
unsigned.as_negative_fixed::<B>()
} else {
unsigned
})
}

fn write_signed<W, S>(w: &mut W, bits: u32, value: S) -> io::Result<()>
where
W: BitWrite,
Expand All @@ -714,7 +764,7 @@ impl Endianness for LittleEndian {
if B == S::BITS_SIZE {
w.write_bytes(value.to_le_bytes().as_ref())
} else if value.is_negative() {
w.write(B - 1, value.as_unsigned(B))
w.write(B - 1, value.as_unsigned_fixed::<B>())
.and_then(|()| w.write_bit(true))
} else {
w.write(B - 1, value).and_then(|()| w.write_bit(false))
Expand Down
2 changes: 1 addition & 1 deletion src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ impl<R: io::Read, E: Endianness> BitRead for BitReader<R, E> {
const {
assert!(BITS <= S::BITS_SIZE, "excessive bits for type read");
}
E::read_signed::<_, S>(self, BITS)
E::read_signed_fixed::<_, BITS, S>(self)
}

#[inline]
Expand Down

0 comments on commit 10a4a61

Please sign in to comment.