#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct MsgId(u32);
impl MsgId {
#[doc(hidden)]
pub const INDEX_BITS: u32 = 24;
const INDEX_MASK: u32 = (1 << Self::INDEX_BITS) - 1;
#[doc(hidden)]
pub const fn new(chunk: u8, index: u32) -> Option<MsgId> {
if index > Self::INDEX_MASK {
return None;
}
Some(MsgId(((chunk as u32) << Self::INDEX_BITS) | index))
}
#[doc(hidden)]
pub const fn from_raw(raw: u32) -> MsgId {
MsgId(raw)
}
#[doc(hidden)]
pub const fn raw(self) -> u32 {
self.0
}
#[doc(hidden)]
pub const fn chunk(self) -> u8 {
#[allow(clippy::cast_possible_truncation)]
let chunk = (self.0 >> Self::INDEX_BITS) as u8;
chunk
}
#[doc(hidden)]
pub const fn index(self) -> u32 {
self.0 & Self::INDEX_MASK
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum Dir {
Ltr = 0,
Rtl = 1,
Auto = 2,
}
#[cfg(test)]
mod tests {
use super::{Dir, MsgId};
#[test]
fn bit_layout() {
let id = MsgId::new(0x12, 0x34_5678).expect("index fits");
assert_eq!(id.raw(), 0x1234_5678);
assert_eq!(id.chunk(), 0x12);
assert_eq!(id.index(), 0x34_5678);
assert_eq!(MsgId::from_raw(0x1234_5678), id);
assert_eq!(MsgId::INDEX_BITS, 24);
}
#[test]
fn index_bounds() {
assert!(MsgId::new(0, (1 << 24) - 1).is_some());
assert!(MsgId::new(0, 1 << 24).is_none());
assert!(MsgId::new(255, u32::MAX).is_none());
let max = MsgId::new(255, (1 << 24) - 1).expect("fits");
assert_eq!(max.raw(), u32::MAX);
assert_eq!((max.chunk(), max.index()), (255, 0xFF_FFFF));
let zero = MsgId::new(0, 0).expect("fits");
assert_eq!(zero.raw(), 0);
}
#[test]
fn order_is_chunk_then_index() {
let a = MsgId::new(0, 5).expect("fits");
let b = MsgId::new(0, 6).expect("fits");
let c = MsgId::new(1, 0).expect("fits");
assert!(a < b && b < c);
}
#[test]
fn dir_discriminants() {
assert_eq!(Dir::Ltr as u8, 0);
assert_eq!(Dir::Rtl as u8, 1);
assert_eq!(Dir::Auto as u8, 2);
}
}