use std::io;
use crate::{private, BitCount, BitRead, BitWrite, SignedBitCount, SignedInteger, UnsignedInteger};
#[derive(Copy, Clone, Debug)]
pub enum CheckedError {
ExcessiveBits,
ExcessiveValue,
}
impl core::error::Error for CheckedError {}
impl core::fmt::Display for CheckedError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Self::ExcessiveBits => core::fmt::Display::fmt("excessive bits for type written", f),
Self::ExcessiveValue => core::fmt::Display::fmt("excessive value for bits written", f),
}
}
}
impl From<CheckedError> for io::Error {
#[inline]
fn from(error: CheckedError) -> Self {
match error {
CheckedError::ExcessiveBits => io::Error::new(
io::ErrorKind::InvalidInput,
"excessive bits for type written",
),
CheckedError::ExcessiveValue => io::Error::new(
io::ErrorKind::InvalidInput,
"excessive value for bits written",
),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Checked<C, T> {
pub(crate) count: C,
pub(crate) value: T,
}
impl<C, T> Checked<C, T> {
#[inline]
pub fn into_count_value(self) -> (C, T) {
(self.count, self.value)
}
#[inline]
pub fn into_value(self) -> T {
self.value
}
}
impl<C, T> AsRef<T> for Checked<C, T> {
fn as_ref(&self) -> &T {
&self.value
}
}
pub type CheckedUnsigned<const MAX: u32, T> = Checked<BitCount<MAX>, T>;
impl<const MAX: u32, U: UnsignedInteger> Checkable for CheckedUnsigned<MAX, U> {
#[inline]
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
writer.write_unsigned_counted(self.count, self.value)
}
#[inline]
fn written_bits(&self) -> u32 {
self.count.bits
}
}
impl<const MAX: u32, U: UnsignedInteger> CheckablePrimitive for CheckedUnsigned<MAX, U> {
type CountType = BitCount<MAX>;
#[inline]
fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self> {
reader
.read_unsigned_counted(count)
.map(|value| Self { count, value })
}
}
impl<const MAX: u32, U: UnsignedInteger> private::Checkable for CheckedUnsigned<MAX, U> {
fn write_endian<E, W>(
self,
writer: &mut W,
queue_value: &mut u8,
queue_bits: &mut u32,
) -> io::Result<()>
where
E: private::Endianness,
W: io::Write,
{
E::write_bits_checked(writer, queue_value, queue_bits, self)
}
}
impl<const MAX: u32, U: UnsignedInteger> CheckedUnsigned<MAX, U> {
#[inline]
pub fn new(count: impl TryInto<BitCount<MAX>>, value: U) -> Result<Self, CheckedError> {
let count @ BitCount { bits } =
count.try_into().map_err(|_| CheckedError::ExcessiveBits)?;
if MAX <= U::BITS_SIZE || bits <= U::BITS_SIZE {
if bits == 0 {
Ok(Self {
count,
value: U::ZERO,
})
} else if value <= U::ALL >> (U::BITS_SIZE - bits) {
Ok(Self { count, value })
} else {
Err(CheckedError::ExcessiveValue)
}
} else {
Err(CheckedError::ExcessiveBits)
}
}
pub fn new_fixed<const BITS: u32>(value: U) -> Result<Self, CheckedError> {
const {
assert!(BITS <= U::BITS_SIZE, "excessive bits for type written");
}
if BITS == 0 {
Ok(Self {
count: BitCount::new::<0>(),
value: U::ZERO,
})
} else if BITS == U::BITS_SIZE || value <= (U::ALL >> (U::BITS_SIZE - BITS)) {
Ok(Self {
count: BitCount::new::<BITS>(),
value,
})
} else {
Err(CheckedError::ExcessiveValue)
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct FixedBitCount<const BITS: u32>;
impl<const BITS: u32> From<FixedBitCount<BITS>> for BitCount<BITS> {
fn from(_count: FixedBitCount<BITS>) -> Self {
BitCount::new::<BITS>()
}
}
impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<BitCount<MAX>>
for FixedBitCount<BITS>
{
type Error = BitCount<MAX>;
fn try_from(count: BitCount<MAX>) -> Result<Self, Self::Error> {
(count.bits == BITS).then_some(FixedBitCount).ok_or(count)
}
}
pub type CheckedUnsignedFixed<const BITS: u32, T> = Checked<FixedBitCount<BITS>, T>;
impl<const BITS: u32, U: UnsignedInteger> CheckedUnsignedFixed<BITS, U> {
pub fn new_fixed(value: U) -> Result<Self, CheckedError> {
const {
assert!(BITS <= U::BITS_SIZE, "excessive bits for type written");
}
if BITS == 0 {
Ok(Self {
count: FixedBitCount,
value: U::ZERO,
})
} else if BITS == U::BITS_SIZE || value <= (U::ALL >> (U::BITS_SIZE - BITS)) {
Ok(Self {
count: FixedBitCount,
value,
})
} else {
Err(CheckedError::ExcessiveValue)
}
}
}
impl<const BITS: u32, U: UnsignedInteger> Checkable for CheckedUnsignedFixed<BITS, U> {
#[inline]
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
writer.write_unsigned::<BITS, _>(self.value)
}
#[inline]
fn written_bits(&self) -> u32 {
BITS
}
}
impl<const BITS: u32, U: UnsignedInteger> private::Checkable for CheckedUnsignedFixed<BITS, U> {
fn write_endian<E, W>(
self,
writer: &mut W,
queue_value: &mut u8,
queue_bits: &mut u32,
) -> io::Result<()>
where
E: private::Endianness,
W: io::Write,
{
E::write_bits_checked(
writer,
queue_value,
queue_bits,
Checked {
value: self.value,
count: self.count.into(),
},
)
}
}
impl<const BITS: u32, U: UnsignedInteger> CheckablePrimitive for CheckedUnsignedFixed<BITS, U> {
type CountType = FixedBitCount<BITS>;
fn read<R: BitRead + ?Sized>(reader: &mut R, count: FixedBitCount<BITS>) -> io::Result<Self> {
Ok(Self {
value: reader.read_unsigned::<BITS, _>()?,
count,
})
}
}
pub type CheckedSigned<const MAX: u32, T> = Checked<SignedBitCount<MAX>, T>;
impl<const MAX: u32, S: SignedInteger> Checkable for CheckedSigned<MAX, S> {
#[inline]
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
writer.write_signed_counted(self.count, self.value)
}
#[inline]
fn written_bits(&self) -> u32 {
self.count.bits.into()
}
}
impl<const MAX: u32, S: SignedInteger> CheckablePrimitive for CheckedSigned<MAX, S> {
type CountType = SignedBitCount<MAX>;
#[inline]
fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self> {
reader
.read_signed_counted(count)
.map(|value| Self { count, value })
}
}
impl<const MAX: u32, S: SignedInteger> private::Checkable for CheckedSigned<MAX, S> {
#[inline]
fn write_endian<E, W>(
self,
writer: &mut W,
queue_value: &mut u8,
queue_bits: &mut u32,
) -> io::Result<()>
where
E: private::Endianness,
W: io::Write,
{
E::write_signed_bits_checked(writer, queue_value, queue_bits, self)
}
}
impl<const MAX: u32, S: SignedInteger> CheckedSigned<MAX, S> {
#[inline]
pub fn new(count: impl TryInto<SignedBitCount<MAX>>, value: S) -> Result<Self, CheckedError> {
let count @ SignedBitCount {
bits: BitCount { bits },
unsigned: BitCount {
bits: unsigned_bits,
},
} = count.try_into().map_err(|_| CheckedError::ExcessiveBits)?;
if MAX <= S::BITS_SIZE || bits <= S::BITS_SIZE {
if bits == S::BITS_SIZE
|| (((S::ZERO - S::ONE) << unsigned_bits) <= value
&& value < (S::ONE << unsigned_bits))
{
Ok(Self { count, value })
} else {
Err(CheckedError::ExcessiveValue)
}
} else {
Err(CheckedError::ExcessiveBits)
}
}
pub fn new_fixed<const BITS: u32>(value: S) -> Result<Self, CheckedError> {
const {
assert!(BITS <= S::BITS_SIZE, "excessive bits for type written");
}
if BITS == S::BITS_SIZE
|| (((S::ZERO - S::ONE) << (BITS - 1)) <= value && value < (S::ONE << (BITS - 1)))
{
Ok(Self {
count: SignedBitCount::new::<BITS>(),
value,
})
} else {
Err(CheckedError::ExcessiveValue)
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct FixedSignedBitCount<const BITS: u32>;
impl<const BITS: u32> From<FixedSignedBitCount<BITS>> for SignedBitCount<BITS> {
fn from(_count: FixedSignedBitCount<BITS>) -> Self {
SignedBitCount::new::<BITS>()
}
}
impl<const BITS: u32, const MAX: u32> core::convert::TryFrom<SignedBitCount<MAX>>
for FixedSignedBitCount<BITS>
{
type Error = SignedBitCount<MAX>;
fn try_from(count: SignedBitCount<MAX>) -> Result<Self, Self::Error> {
(count.bits.bits == BITS)
.then_some(FixedSignedBitCount)
.ok_or(count)
}
}
pub type CheckedSignedFixed<const BITS: u32, T> = Checked<FixedSignedBitCount<BITS>, T>;
impl<const BITS: u32, S: SignedInteger> CheckedSignedFixed<BITS, S> {
pub fn new_fixed(value: S) -> Result<Self, CheckedError> {
const {
assert!(BITS <= S::BITS_SIZE, "excessive bits for type written");
}
if BITS == S::BITS_SIZE
|| (((S::ZERO - S::ONE) << (BITS - 1)) <= value && value < (S::ONE << (BITS - 1)))
{
Ok(Self {
count: FixedSignedBitCount,
value,
})
} else {
Err(CheckedError::ExcessiveValue)
}
}
}
impl<const BITS: u32, S: SignedInteger> Checkable for CheckedSignedFixed<BITS, S> {
#[inline]
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
writer.write_signed::<BITS, _>(self.value)
}
#[inline]
fn written_bits(&self) -> u32 {
BITS
}
}
impl<const BITS: u32, S: SignedInteger> private::Checkable for CheckedSignedFixed<BITS, S> {
#[inline]
fn write_endian<E, W>(
self,
writer: &mut W,
queue_value: &mut u8,
queue_bits: &mut u32,
) -> io::Result<()>
where
E: private::Endianness,
W: io::Write,
{
E::write_signed_bits_checked(
writer,
queue_value,
queue_bits,
CheckedSigned {
value: self.value,
count: self.count.into(),
},
)
}
}
impl<const BITS: u32, S: SignedInteger> CheckablePrimitive for CheckedSignedFixed<BITS, S> {
type CountType = FixedSignedBitCount<BITS>;
fn read<R: BitRead + ?Sized>(
reader: &mut R,
count: FixedSignedBitCount<BITS>,
) -> io::Result<Self> {
Ok(Self {
value: reader.read_signed::<BITS, _>()?,
count,
})
}
}
pub trait Checkable: private::Checkable + Sized {
fn write<W: BitWrite + ?Sized>(&self, writer: &mut W) -> io::Result<()>;
fn written_bits(&self) -> u32;
}
pub trait CheckablePrimitive: Checkable {
type CountType;
fn read<R: BitRead + ?Sized>(reader: &mut R, count: Self::CountType) -> io::Result<Self>;
}