#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct DirtyFlags(pub u8);
impl DirtyFlags {
pub const CLEAN: DirtyFlags = DirtyFlags(0b0000_0000);
pub const STATE: DirtyFlags = DirtyFlags(0b0000_1111);
pub const LAYOUT: DirtyFlags = DirtyFlags(0b0000_0111);
pub const PAINT: DirtyFlags = DirtyFlags(0b0000_0011);
pub const COMPOSITE: DirtyFlags = DirtyFlags(0b0000_0001);
pub const ALL: DirtyFlags = DirtyFlags(0b0000_1111);
#[inline]
pub fn is_dirty(self) -> bool {
self.0 != 0
}
#[inline]
pub fn needs_composite(self) -> bool {
self.0 & 0b0000_0001 != 0
}
#[inline]
pub fn needs_paint(self) -> bool {
self.0 & 0b0000_0010 != 0
}
#[inline]
pub fn needs_layout(self) -> bool {
self.0 & 0b0000_0100 != 0
}
#[inline]
pub fn needs_state(self) -> bool {
self.0 & 0b0000_1000 != 0
}
#[inline]
pub fn merge(self, other: DirtyFlags) -> DirtyFlags {
DirtyFlags(self.0 | other.0)
}
#[inline]
pub fn clear(self) -> DirtyFlags {
DirtyFlags::CLEAN
}
}
impl std::ops::BitOr for DirtyFlags {
type Output = DirtyFlags;
fn bitor(self, rhs: DirtyFlags) -> DirtyFlags {
DirtyFlags(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for DirtyFlags {
fn bitor_assign(&mut self, rhs: DirtyFlags) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAnd for DirtyFlags {
type Output = DirtyFlags;
fn bitand(self, rhs: DirtyFlags) -> DirtyFlags {
DirtyFlags(self.0 & rhs.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct InvalidationRecord {
pub id: KvasirId,
pub flags: DirtyFlags,
}
impl InvalidationRecord {
pub fn new(id: KvasirId, flags: DirtyFlags) -> Self {
Self { id, flags }
}
pub fn full(id: KvasirId) -> Self {
Self { id, flags: DirtyFlags::ALL }
}
}
#[cfg(test)]
mod kvasir_identity_tests {
use super::*;
#[test]
fn kvasir_id_new_is_non_zero() {
let id = KvasirId::new();
assert!(!id.is_null(), "KvasirId::new() returned null sentinel");
}
#[test]
fn kvasir_id_new_is_unique() {
let a = KvasirId::new();
let b = KvasirId::new();
let c = KvasirId::new();
assert_ne!(a, b);
assert_ne!(b, c);
assert_ne!(a, c);
}
#[test]
fn kvasir_id_null_sentinel() {
assert!(KvasirId::NULL.is_null());
assert!(!KvasirId::new().is_null());
}
#[test]
fn kvasir_id_serde_roundtrip() {
let id = KvasirId(42);
let json = serde_json::to_string(&id).unwrap();
let decoded: KvasirId = serde_json::from_str(&json).unwrap();
assert_eq!(id, decoded);
}
#[test]
fn dirty_flags_clean_is_not_dirty() {
assert!(!DirtyFlags::CLEAN.is_dirty());
}
#[test]
fn dirty_flags_all_implies_all_layers() {
let f = DirtyFlags::ALL;
assert!(f.needs_state());
assert!(f.needs_layout());
assert!(f.needs_paint());
assert!(f.needs_composite());
}
#[test]
fn dirty_flags_composite_only() {
let f = DirtyFlags::COMPOSITE;
assert!(!f.needs_state());
assert!(!f.needs_layout());
assert!(!f.needs_paint());
assert!(f.needs_composite());
}
#[test]
fn dirty_flags_merge() {
let a = DirtyFlags::COMPOSITE;
let b = DirtyFlags::PAINT;
let merged = a.merge(b);
assert!(merged.needs_composite());
assert!(merged.needs_paint());
assert!(!merged.needs_layout());
}
#[test]
fn dirty_flags_bitor() {
let combined = DirtyFlags::PAINT | DirtyFlags::COMPOSITE;
assert!(combined.needs_paint());
assert!(combined.needs_composite());
}
#[test]
fn dirty_flags_clear() {
let dirty = DirtyFlags::ALL;
let clean = dirty.clear();
assert!(!clean.is_dirty());
}
#[test]
fn dirty_flags_serde_roundtrip() {
let f = DirtyFlags::LAYOUT;
let json = serde_json::to_string(&f).unwrap();
let decoded: DirtyFlags = serde_json::from_str(&json).unwrap();
assert_eq!(f, decoded);
}
#[test]
fn invalidation_record_full() {
let id = KvasirId::new();
let rec = InvalidationRecord::full(id);
assert_eq!(rec.id, id);
assert!(rec.flags.needs_state());
assert!(rec.flags.needs_layout());
}
}