use wasefire_error::Error;
use crate::Nat;
use crate::format::Word;
pub struct Field {
pub pos: Nat,
pub len: Nat,
}
impl Field {
pub fn get(&self, word: Word) -> Nat {
(word.0 >> self.pos) & self.mask()
}
pub fn set(&self, word: &mut Word, value: Nat) -> Result<(), Error> {
if value & self.mask() != value {
return Err(crate::INVALID_STORAGE);
}
let mask = !(self.mask() << self.pos);
word.0 &= mask | (value << self.pos);
if self.get(*word) != value {
return Err(crate::INVALID_STORAGE);
}
Ok(())
}
fn mask(&self) -> u32 {
(1 << self.len) - 1
}
}
pub struct ConstField {
pub field: Field,
pub value: Nat,
}
impl ConstField {
pub fn check(&self, word: Word) -> bool {
self.field.get(word) == self.value
}
pub fn set(&self, word: &mut Word) -> Result<(), Error> {
self.field.set(word, self.value)
}
}
pub struct Bit {
pub pos: Nat,
}
impl Bit {
pub fn get(&self, word: Word) -> bool {
word.0 & (1 << self.pos) == 0
}
pub fn set(&self, word: &mut Word) {
word.0 &= !(1 << self.pos);
}
}
pub struct Checksum {
pub field: Field,
}
impl Checksum {
pub fn get(&self, word: Word) -> Result<Nat, Error> {
let checksum = self.field.get(word);
let zeros = word.0.count_zeros() - (self.field.len - checksum.count_ones());
checksum.checked_sub(zeros).ok_or(crate::INVALID_STORAGE)
}
pub fn set(&self, word: &mut Word, value: Nat) -> Result<(), Error> {
debug_assert_eq!(self.field.get(*word), self.field.mask());
self.field.set(word, word.0.count_zeros() + value)
}
}
#[cfg(any(doc, test))]
pub struct Length {
pub pos: Nat,
}
#[cfg_attr(doc, macro_export)] macro_rules! bitfield {
($($input: tt)*) => {
bitfield_impl! { []{ pos: 0 }[$($input)*] }
};
}
macro_rules! bitfield_impl {
([$($output: tt)*]{ pos: $pos: expr }
[$(#[$meta: meta])* $pub:vis $name: ident: Bit, $($input: tt)*]) => {
bitfield_impl! {
[$($output)* $(#[$meta])* $pub const $name: Bit = Bit { pos: $pos };]
{ pos: $pos + 1 }
[$($input)*]
}
};
([$($output: tt)*]{ pos: $pos: expr }
[$(#[$meta: meta])* $pub:vis $name: ident: Field <= $max: expr, $($input: tt)*]) => {
bitfield_impl! {
[$($output)* $(#[$meta])* $pub const $name: Field = Field {
pos: $pos,
len: num_bits($max),
};]
{ pos: $pos + $name.len }
[$($input)*]
}
};
([$($output: tt)*]{ pos: $pos: expr }
[$(#[$meta: meta])* $pub:vis $name: ident: Checksum <= $max: expr, $($input: tt)*]) => {
bitfield_impl! {
[$($output)* $(#[$meta])* $pub const $name: Checksum = Checksum {
field: Field { pos: $pos, len: num_bits($max) }
};]
{ pos: $pos + $name.field.len }
[$($input)*]
}
};
([$($output: tt)*]{ pos: $pos: expr }
[$(#[$meta: meta])* $pub:vis $name: ident: Length, $($input: tt)*]) => {
bitfield_impl! {
[$($output)* $(#[$meta])* $pub const $name: Length = Length { pos: $pos };]
{ pos: $pos }
[$($input)*]
}
};
([$($output: tt)*]{ pos: $pos: expr }
[$(#[$meta: meta])* $pub:vis $name: ident: ConstField = $bits: tt, $($input: tt)*]) => {
bitfield_impl! {
Reverse $(#[$meta])* $pub $name []$bits
[$($output)*]{ pos: $pos }[$($input)*]
}
};
([$($output: tt)*]{ pos: $pos: expr }[]) => { $($output)* };
(Reverse $(#[$meta: meta])* $pub:vis $name: ident [$($output_bits: tt)*]
[$bit: tt $($input_bits: tt)*] [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => {
bitfield_impl! {
Reverse $(#[$meta])* $pub $name [$bit $($output_bits)*][$($input_bits)*]
[$($output)*]{ pos: $pos }[$($input)*]
}
};
(Reverse $(#[$meta: meta])* $pub:vis $name: ident $bits: tt []
[$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => {
bitfield_impl! {
ConstField $(#[$meta])* $pub $name { len: 0, val: 0 }$bits
[$($output)*]{ pos: $pos }[$($input)*]
}
};
(ConstField $(#[$meta: meta])* $pub:vis $name: ident { len: $len: expr, val: $val: expr }[]
[$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => {
bitfield_impl! {
[$($output)* $(#[$meta])* $pub const $name: ConstField = ConstField {
field: Field { pos: $pos, len: $len },
value: $val,
};]
{ pos: $pos + $name.field.len }
[$($input)*]
}
};
(ConstField $(#[$meta: meta])* $pub:vis $name: ident { len: $len: expr, val: $val: expr }
[$bit: tt $($bits: tt)*][$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => {
bitfield_impl! {
ConstField $(#[$meta])* $pub $name { len: $len + 1, val: $val * 2 + $bit }[$($bits)*]
[$($output)*]{ pos: $pos }[$($input)*]
}
};
}
pub fn count_zeros(slice: &[u8]) -> Nat {
slice.iter().map(|&x| x.count_zeros()).sum()
}
pub const fn num_bits(x: Nat) -> Nat {
8 * core::mem::size_of::<Nat>() as Nat - x.leading_zeros()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn field_ok() {
let field = Field { pos: 3, len: 5 };
assert_eq!(field.get(Word(0x00000000)), 0);
assert_eq!(field.get(Word(0x00000007)), 0);
assert_eq!(field.get(Word(0x00000008)), 1);
assert_eq!(field.get(Word(0x000000f8)), 0x1f);
assert_eq!(field.get(Word(0x0000ff37)), 6);
let mut word = Word(0xffffffff);
field.set(&mut word, 3).unwrap();
assert_eq!(word, Word(0xffffff1f));
}
#[test]
fn const_field_ok() {
let field = ConstField { field: Field { pos: 3, len: 5 }, value: 9 };
assert!(!field.check(Word(0x00000000)));
assert!(!field.check(Word(0x0000ffff)));
assert!(field.check(Word(0x00000048)));
assert!(field.check(Word(0x0000ff4f)));
let mut word = Word(0xffffffff);
field.set(&mut word).unwrap();
assert_eq!(word, Word(0xffffff4f));
}
#[test]
fn bit_ok() {
let bit = Bit { pos: 3 };
assert!(bit.get(Word(0x00000000)));
assert!(bit.get(Word(0xfffffff7)));
assert!(!bit.get(Word(0x00000008)));
assert!(!bit.get(Word(0xffffffff)));
let mut word = Word(0xffffffff);
bit.set(&mut word);
assert_eq!(word, Word(0xfffffff7));
}
#[test]
fn checksum_ok() {
let field = Checksum { field: Field { pos: 3, len: 5 } };
assert_eq!(field.get(Word(0x00000000)), Err(crate::INVALID_STORAGE));
assert_eq!(field.get(Word(0xffffffff)), Ok(31));
assert_eq!(field.get(Word(0xffffff07)), Ok(0));
assert_eq!(field.get(Word(0xffffff0f)), Ok(1));
assert_eq!(field.get(Word(0x00ffff67)), Ok(4));
assert_eq!(field.get(Word(0x7fffff07)), Err(crate::INVALID_STORAGE));
let mut word = Word(0x0fffffff);
field.set(&mut word, 4).unwrap();
assert_eq!(word, Word(0x0fffff47));
}
#[test]
fn bitfield_ok() {
bitfield! {
FIELD: Field <= 127,
CONST_FIELD: ConstField = [0 1 0 1],
BIT: Bit,
CHECKSUM: Checksum <= 58,
LENGTH: Length,
}
assert_eq!(FIELD.pos, 0);
assert_eq!(FIELD.len, 7);
assert_eq!(CONST_FIELD.field.pos, 7);
assert_eq!(CONST_FIELD.field.len, 4);
assert_eq!(CONST_FIELD.value, 10);
assert_eq!(BIT.pos, 11);
assert_eq!(CHECKSUM.field.pos, 12);
assert_eq!(CHECKSUM.field.len, 6);
assert_eq!(LENGTH.pos, 18);
}
#[test]
fn count_zeros_ok() {
assert_eq!(count_zeros(&[0xff, 0xff]), 0);
assert_eq!(count_zeros(&[0xff, 0xfe]), 1);
assert_eq!(count_zeros(&[0x7f, 0xff]), 1);
assert_eq!(count_zeros(&[0x12, 0x48]), 12);
assert_eq!(count_zeros(&[0x00, 0x00]), 16);
}
#[test]
fn num_bits_ok() {
assert_eq!(num_bits(0), 0);
assert_eq!(num_bits(1), 1);
assert_eq!(num_bits(2), 2);
assert_eq!(num_bits(3), 2);
assert_eq!(num_bits(4), 3);
assert_eq!(num_bits(5), 3);
assert_eq!(num_bits(8), 4);
assert_eq!(num_bits(9), 4);
assert_eq!(num_bits(16), 5);
}
}