use core::fmt;
use crate::format::low_mask;
pub trait Node: Copy + Send + Sync + 'static {
const BITS: Option<u8>;
fn pack(self, bits: u8) -> Result<u64, NodeError>;
fn unpack(raw: u64, bits: u8) -> Self;
}
impl Node for u32 {
const BITS: Option<u8> = None;
fn pack(self, bits: u8) -> Result<u64, NodeError> {
pack_field(0, "node", u64::from(self), bits)
}
fn unpack(raw: u64, _bits: u8) -> Self {
raw as u32
}
}
#[doc(hidden)]
pub fn pack_field(
packed: u64,
field: &'static str,
value: u64,
bits: u8,
) -> Result<u64, NodeError> {
let max = low_mask(bits);
if value > max {
return Err(NodeError::new(field, value, max));
}
Ok((packed << bits) | value)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct NodeError {
pub field: &'static str,
pub value: u64,
pub max: u64,
}
impl NodeError {
pub const fn new(field: &'static str, value: u64, max: u64) -> Self {
Self { field, value, max }
}
}
impl fmt::Display for NodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"node field `{}` is {}, but its maximum is {}",
self.field, self.value, self.max
)
}
}
impl std::error::Error for NodeError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plain_node_packs_within_its_width() {
assert_eq!(17u32.pack(10), Ok(17));
assert_eq!(1023u32.pack(10), Ok(1023));
assert_eq!(0u32.pack(0), Ok(0));
assert_eq!(u32::MAX.pack(32), Ok(u64::from(u32::MAX)));
}
#[test]
fn plain_node_rejects_values_that_do_not_fit() {
assert_eq!(1024u32.pack(10), Err(NodeError::new("node", 1024, 1023)));
assert_eq!(1u32.pack(0), Err(NodeError::new("node", 1, 0)));
}
#[test]
fn plain_node_round_trips() {
assert_eq!(u32::unpack(17u32.pack(10).unwrap(), 10), 17);
}
#[test]
fn pack_field_appends_most_significant_first() {
let packed = pack_field(0, "worker", 0b10001, 5).unwrap();
let packed = pack_field(packed, "process", 0b00001, 5).unwrap();
assert_eq!(packed, 0b10001_00001);
}
}