use std::convert::Infallible;
use std::marker::PhantomData;
use crate::fields::ControlKind;
pub trait Packed: Sized {
const MAX_BITS: u32;
const DECODE_BITS: u32 = Self::MAX_BITS;
const CONTROL: ControlKind = ControlKind::Number;
type Error;
fn from_bits(bits: u64) -> Result<Self, Self::Error>;
fn to_bits(&self) -> u64;
}
pub const fn bits_for(max: u64) -> u32 {
64 - max.leading_zeros()
}
impl Packed for bool {
const MAX_BITS: u32 = 1;
const DECODE_BITS: u32 = 1;
const CONTROL: ControlKind = ControlKind::Toggle;
type Error = Infallible;
fn from_bits(bits: u64) -> Result<Self, Infallible> {
Ok(bits != 0)
}
fn to_bits(&self) -> u64 {
*self as u64
}
}
macro_rules! impl_packed_uint {
($($t:ty),* $(,)?) => { $(
impl Packed for $t {
const MAX_BITS: u32 = <$t>::BITS;
const DECODE_BITS: u32 = <$t>::BITS;
type Error = Infallible;
fn from_bits(bits: u64) -> Result<Self, Infallible> {
Ok(bits as $t)
}
fn to_bits(&self) -> u64 {
*self as u64
}
}
)* };
}
impl_packed_uint!(u8, u16, u32, u64);
const fn extract(raw: &[u8], lo: u32, hi: u32) -> u64 {
let mut bits = 0;
let mut i = lo;
while i <= hi {
bits = (bits << 1) | ((raw[(i / 8) as usize] >> (7 - i % 8)) & 1) as u64;
i += 1;
}
bits
}
fn splice(raw: &mut [u8], lo: u32, hi: u32, bits: u64) {
for (n, i) in (lo..=hi).enumerate() {
let mask = 1u8 << (7 - i % 8);
let set = (bits >> (hi - lo - n as u32)) & 1 != 0;
let byte = &mut raw[(i / 8) as usize];
*byte = if set { *byte | mask } else { *byte & !mask };
}
}
pub struct Field<T, const LO: u32, const HI: u32>(PhantomData<fn() -> T>);
struct SpanFits<const N: usize, const HI: u32>;
impl<const N: usize, const HI: u32> SpanFits<N, HI> {
const OK: () = assert!(((HI / 8) as usize) < N, "bit field extends past the panel");
}
impl<T: Packed, const LO: u32, const HI: u32> Field<T, LO, HI> {
pub const WIDTH: u32 = {
assert!(HI >= LO, "a bit range must not end before it starts");
assert!(HI - LO < 64, "a bit field cannot be wider than u64");
HI - LO + 1
};
const FITS: () = assert!(
T::MAX_BITS <= Self::WIDTH,
"this type can hold values wider than the field; give this field a type that \
carries its range",
);
const COHERENT: () = assert!(
T::MAX_BITS <= T::DECODE_BITS,
"this type claims more value bits than it can decode",
);
const READS: () = assert!(
Self::WIDTH <= T::DECODE_BITS,
"this field is wider than its type; decoding it would discard high bits",
);
pub fn get<const N: usize>(raw: &[u8; N]) -> Result<T, T::Error> {
let () = Self::COHERENT;
let () = Self::READS;
let () = SpanFits::<N, HI>::OK;
T::from_bits(extract(raw, LO, HI))
}
pub fn set<const N: usize>(raw: &mut [u8; N], value: T) {
let () = Self::COHERENT;
let () = Self::READS;
let () = Self::FITS;
let () = SpanFits::<N, HI>::OK;
splice(raw, LO, HI, value.to_bits());
}
}
impl<T: Packed<Error = Infallible>, const LO: u32, const HI: u32> Field<T, LO, HI> {
pub fn read<const N: usize>(raw: &[u8; N]) -> T {
match Self::get(raw) {
Ok(value) => value,
Err(never) => match never {},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Eq)]
struct Small<const BITS: u32>(u8);
impl<const BITS: u32> Packed for Small<BITS> {
const MAX_BITS: u32 = BITS;
const DECODE_BITS: u32 = u8::BITS;
type Error = Infallible;
fn from_bits(bits: u64) -> Result<Self, Infallible> {
Ok(Small(bits as u8))
}
fn to_bits(&self) -> u64 {
self.0 as u64
}
}
type Nibble = Field<Small<4>, 4, 7>;
type Byte = Field<u8, 8, 15>;
type Flag = Field<bool, 11, 11>;
#[test]
fn a_field_reads_only_its_own_bits() {
assert_eq!(Nibble::read(&[0xab, 0xcd]), Small(0xb));
assert_eq!(Byte::read(&[0xab, 0xcd]), 0xcd);
assert!(Flag::read(&[0x00, 0x10]));
assert!(!Flag::read(&[0xff, 0xef]));
}
#[test]
fn a_write_disturbs_no_other_bit() {
let mut raw = [0xab, 0xcd];
Nibble::set(&mut raw, Small(0x3));
assert_eq!(raw, [0xa3, 0xcd]);
let mut raw = [0b1010_1010];
Field::<bool, 3, 3>::set(&mut raw, true);
assert_eq!(raw, [0b1011_1010]);
Field::<bool, 3, 3>::set(&mut raw, false);
assert_eq!(raw, [0b1010_1010]);
}
#[test]
fn a_field_may_span_bytes() {
type Spanning = Field<Small<7>, 5, 11>;
assert_eq!(Spanning::WIDTH, 7);
assert_eq!(
Spanning::read(&[0b0000_0101, 0b1101_0000]),
Small(0b101_1101)
);
assert_eq!(Spanning::read(&[0, 0]), Small(0));
let mut raw = [0b1111_1000, 0b0000_1111];
Spanning::set(&mut raw, Small(0b101_1101));
assert_eq!(raw, [0b1111_1101, 0b1101_1111]);
assert_eq!(Spanning::read(&raw), Small(0b101_1101));
}
#[test]
fn widths_come_from_the_range_alone() {
assert_eq!(Flag::WIDTH, 1);
assert_eq!(Nibble::WIDTH, 4);
assert_eq!(Field::<u64, 0, 63>::WIDTH, 64);
}
#[test]
fn bits_for_counts_what_a_value_needs() {
assert_eq!(bits_for(0), 0);
assert_eq!(bits_for(1), 1);
assert_eq!(bits_for(12), 4);
assert_eq!(bits_for(13), 4);
assert_eq!(bits_for(127), 7);
assert_eq!(bits_for(128), 8);
}
}