#[derive(Clone, Copy)]
#[repr(C)]
union LinkOrWeight {
link: u32,
weight: f32,
}
impl Default for LinkOrWeight {
fn default() -> Self {
LinkOrWeight {
weight: f32::MIN_POSITIVE,
}
}
}
impl std::fmt::Debug for LinkOrWeight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe {
write!(
f,
"LinkOrWeight(link={}, weight={})",
self.link, self.weight
)
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Cache {
parent: u32,
child: u32,
union: LinkOrWeight,
}
impl Cache {
pub fn new() -> Self {
Cache {
parent: 0,
child: 0,
union: LinkOrWeight::default(),
}
}
#[inline]
pub fn set_parent(&mut self, parent: usize) {
assert!(parent <= u32::MAX as usize, "Parent exceeds u32::MAX");
self.parent = parent as u32;
}
#[inline]
pub fn set_child(&mut self, child: usize) {
assert!(child <= u32::MAX as usize, "Child exceeds u32::MAX");
self.child = child as u32;
}
#[inline]
pub fn set_base(&mut self, base: u8) {
unsafe {
let current_link = self.union.link;
self.union.link = (current_link & !0xFF) | (base as u32);
}
}
#[inline]
pub fn set_extra(&mut self, extra: usize) {
assert!(extra <= (u32::MAX >> 8) as usize, "Extra too large");
unsafe {
let current_link = self.union.link;
self.union.link = (current_link & 0xFF) | ((extra as u32) << 8);
}
}
#[inline]
pub fn set_weight(&mut self, weight: f32) {
self.union.weight = weight;
}
#[inline]
pub fn parent(&self) -> usize {
self.parent as usize
}
#[inline]
pub fn child(&self) -> usize {
self.child as usize
}
#[inline]
pub fn base(&self) -> u8 {
unsafe { (self.union.link & 0xFF) as u8 }
}
#[inline]
pub fn extra(&self) -> usize {
unsafe { (self.union.link >> 8) as usize }
}
#[inline]
pub fn label(&self) -> u8 {
self.base()
}
#[inline]
pub fn link(&self) -> usize {
unsafe { self.union.link as usize }
}
#[inline]
pub fn weight(&self) -> f32 {
unsafe { self.union.weight }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_new() {
let cache = Cache::new();
assert_eq!(cache.parent(), 0);
assert_eq!(cache.child(), 0);
}
#[test]
fn test_cache_parent_child() {
let mut cache = Cache::new();
cache.set_parent(100);
cache.set_child(200);
assert_eq!(cache.parent(), 100);
assert_eq!(cache.child(), 200);
}
#[test]
fn test_cache_base_extra() {
let mut cache = Cache::new();
cache.set_base(0x42);
cache.set_extra(0x123456);
assert_eq!(cache.base(), 0x42);
assert_eq!(cache.extra(), 0x123456);
assert_eq!(cache.label(), 0x42);
assert_eq!(cache.link(), 0x12345642);
}
#[test]
fn test_cache_base_preserves_extra() {
let mut cache = Cache::new();
cache.set_extra(0x123456);
cache.set_base(0x42);
assert_eq!(cache.base(), 0x42);
assert_eq!(cache.extra(), 0x123456);
}
#[test]
fn test_cache_extra_preserves_base() {
let mut cache = Cache::new();
cache.set_base(0x42);
cache.set_extra(0x123456);
assert_eq!(cache.base(), 0x42);
assert_eq!(cache.extra(), 0x123456);
}
#[test]
fn test_cache_weight() {
let mut cache = Cache::new();
cache.set_weight(3.15);
assert_eq!(cache.weight(), 3.15);
}
#[test]
fn test_cache_weight_replaces_link() {
let mut cache = Cache::new();
cache.set_base(0x42);
cache.set_extra(0x123456);
cache.set_weight(2.5);
assert_eq!(cache.weight(), 2.5);
}
#[test]
fn test_cache_link_replaces_weight() {
let mut cache = Cache::new();
cache.set_weight(2.5);
cache.set_base(0x42);
cache.set_extra(0x123456);
assert_eq!(cache.base(), 0x42);
assert_eq!(cache.extra(), 0x123456);
}
#[test]
fn test_cache_max_values() {
let mut cache = Cache::new();
cache.set_parent(u32::MAX as usize);
cache.set_child(u32::MAX as usize);
assert_eq!(cache.parent(), u32::MAX as usize);
assert_eq!(cache.child(), u32::MAX as usize);
}
#[test]
fn test_cache_base_all_bits() {
let mut cache = Cache::new();
cache.set_extra(0); cache.set_base(0xFF);
assert_eq!(cache.base(), 0xFF);
assert_eq!(cache.extra(), 0);
}
#[test]
fn test_cache_extra_max() {
let mut cache = Cache::new();
cache.set_extra((u32::MAX >> 8) as usize);
assert_eq!(cache.extra(), (u32::MAX >> 8) as usize);
}
#[test]
fn test_cache_default() {
let cache = Cache::default();
assert_eq!(cache.parent(), 0);
assert_eq!(cache.child(), 0);
}
#[test]
fn test_cache_clone() {
let mut cache1 = Cache::new();
cache1.set_parent(10);
cache1.set_child(20);
cache1.set_base(0x42);
let cache2 = cache1;
assert_eq!(cache2.parent(), 10);
assert_eq!(cache2.child(), 20);
assert_eq!(cache2.base(), 0x42);
}
}