bitflags! {
pub struct RundownFlags: u64 {
const RUNDOWN_IN_PROGRESS = 0xF000_0000_0000_0000;
}
}
impl RundownFlags {
#[inline]
pub const fn is_rundown_in_progress(self) -> bool {
self.contains(Self::RUNDOWN_IN_PROGRESS)
}
#[inline]
pub const fn is_pre_rundown(self) -> bool {
!self.contains(Self::RUNDOWN_IN_PROGRESS)
}
#[inline]
pub const fn set_rundown_in_progress(self) -> u64 {
self.bits | Self::RUNDOWN_IN_PROGRESS.bits
}
#[inline]
pub const fn get_ref(self) -> u64 {
self.bits & (!Self::RUNDOWN_IN_PROGRESS.bits)
}
#[inline]
pub const fn is_ref_zero(self) -> bool {
self.get_ref() == 0
}
#[inline]
pub const fn is_ref_active(self) -> bool {
self.get_ref() > 0
}
#[inline]
pub fn add_ref(self) -> u64 {
if let Some(new_value) = self.bits.checked_add(1) {
new_value
} else {
panic!("Incrementing the reference-count would have over-flowed!");
}
}
#[inline]
pub fn dec_ref(self) -> u64 {
if let Some(new_value) = self.bits.checked_sub(1) {
new_value
} else {
panic!("Decrementing the reference-count would have under-flowed!");
}
}
}
#[test]
fn test_rundown_flags_refcount() {
let mut flags = RundownFlags::empty();
assert_eq!(0, flags.get_ref());
assert_eq!(true, flags.is_ref_zero());
assert_eq!(false, flags.is_ref_active());
flags = to_flags(flags.add_ref());
assert_eq!(1, flags.get_ref());
assert_eq!(false, flags.is_ref_zero());
assert_eq!(true, flags.is_ref_active());
flags = to_flags(flags.dec_ref());
assert_eq!(0, flags.get_ref());
assert_eq!(true, flags.is_ref_zero());
assert_eq!(false, flags.is_ref_active());
assert_eq!(false, flags.is_rundown_in_progress());
assert_eq!(true, flags.is_pre_rundown());
}
#[test]
fn test_rundown_flags_set_in_progress() {
let mut flags = RundownFlags::empty();
assert_eq!(0, flags.get_ref());
flags = to_flags(flags.set_rundown_in_progress());
assert_eq!(0, flags.get_ref());
assert_eq!(true, flags.is_rundown_in_progress());
assert_eq!(false, flags.is_pre_rundown());
flags = to_flags(flags.add_ref());
assert_eq!(1, flags.get_ref());
assert_eq!(true, flags.is_rundown_in_progress());
assert_eq!(false, flags.is_pre_rundown());
}
#[test]
#[should_panic]
fn test_rundown_flags_overflow_panic() {
let flags = to_flags(0xFFFF_FFFF_FFFF_FFFF);
flags.add_ref();
}
#[test]
#[should_panic]
fn test_rundown_flags_underflow_panic() {
let flags = RundownFlags::empty();
flags.dec_ref();
}
#[inline]
pub const fn to_flags(bits: u64) -> RundownFlags {
unsafe { RundownFlags::from_bits_unchecked(bits) }
}
#[test]
fn test_to_flags() {
let flags = to_flags(0xF000_0000_0000_0001);
assert_eq!(1, flags.get_ref());
assert_eq!(true, flags.is_rundown_in_progress());
}