use crate::lib_bitfield;
use crate::result::{Error, Result};
lib_bitfield! {
pub Address(MSB0 [u8; 3]): u16 {
raw_page_number: 16, 9;
raw_offset_address: 8, 0;
}
}
impl Address {
pub const LEN: usize = 3;
pub const DEFAULT: [u8; Self::LEN] = [0; Self::LEN];
const MASK: u32 = 0x1ffff;
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn page_number(&self) -> u8 {
self.raw_page_number() as u8
}
pub fn set_page_number(&mut self, val: u8) {
self.set_raw_page_number(val as u16);
}
pub const fn offset_address(&self) -> u16 {
self.raw_offset_address()
}
pub fn set_offset_address(&mut self, val: u16) {
self.set_raw_offset_address(val);
}
pub const fn bits(&self) -> u32 {
let [r0, r1, r2] = self.0;
u32::from_be_bytes([0, r0, r1, r2])
}
pub const fn from_bits(val: u32) -> Self {
match Self::try_from_bits(val) {
Ok(a) => a,
Err(_) => panic!("cmd: argument: addr: invalid bit pattern"),
}
}
pub const fn try_from_bits(val: u32) -> Result<Self> {
let [_, r0, r1, r2] = (val & Self::MASK).to_be_bytes();
Ok(Self([r0, r1, r2]))
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
_ => Ok(Self([val[0] & 0x1, val[1], val[2]])),
}
}
}
impl Default for Address {
fn default() -> Self {
Self::new()
}
}
impl From<Address> for [u8; Address::LEN] {
fn from(val: Address) -> Self {
val.bytes()
}
}
impl TryFrom<&[u8]> for Address {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<&[u8; N]> for Address {
type Error = Error;
fn try_from(val: &[u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
impl<const N: usize> TryFrom<[u8; N]> for Address {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fields() {
(1..=17).map(|r| (1u32 << r) - 1).for_each(|raw_arg| {
let [_, r0, r1, r2] = raw_arg.to_be_bytes();
let raw = [r0, r1, r2];
let mut exp_addr = Address(raw);
let exp_page_num = ((raw_arg & 0x1_fe00) >> 9) as u8;
let exp_offset_addr = (raw_arg & 0x1ff) as u16;
assert_eq!(Address::try_from_bytes(&raw), Ok(exp_addr));
assert_eq!(Address::try_from_bits(raw_arg), Ok(exp_addr));
assert_eq!(Address::try_from(raw), Ok(exp_addr));
assert_eq!(Address::try_from(&raw), Ok(exp_addr));
assert_eq!(exp_addr.bytes(), raw);
assert_eq!(exp_addr.page_number(), exp_page_num);
exp_addr.set_page_number(0);
assert_eq!(exp_addr.page_number(), 0);
exp_addr.set_page_number(exp_page_num);
assert_eq!(exp_addr.page_number(), exp_page_num);
assert_eq!(exp_addr.offset_address(), exp_offset_addr);
exp_addr.set_offset_address(0);
assert_eq!(exp_addr.offset_address(), 0);
exp_addr.set_offset_address(exp_offset_addr);
assert_eq!(exp_addr.offset_address(), exp_offset_addr);
});
}
}