driven/state/
dirty_bits.rs1use std::sync::atomic::{AtomicU64, Ordering};
6
7#[derive(Debug)]
9pub struct DirtyMask(AtomicU64);
10
11impl DirtyMask {
12 pub fn new() -> Self {
14 Self(AtomicU64::new(0))
15 }
16
17 pub fn all_dirty() -> Self {
19 Self(AtomicU64::new(u64::MAX))
20 }
21
22 pub fn mark_dirty(&self, bit: u8) {
24 if bit < 64 {
25 self.0.fetch_or(1 << bit, Ordering::SeqCst);
26 }
27 }
28
29 pub fn clear(&self, bit: u8) {
31 if bit < 64 {
32 self.0.fetch_and(!(1 << bit), Ordering::SeqCst);
33 }
34 }
35
36 pub fn clear_all(&self) {
38 self.0.store(0, Ordering::SeqCst);
39 }
40
41 pub fn is_dirty(&self, bit: u8) -> bool {
43 if bit >= 64 {
44 return false;
45 }
46 (self.0.load(Ordering::SeqCst) & (1 << bit)) != 0
47 }
48
49 pub fn any_dirty(&self) -> bool {
51 self.0.load(Ordering::SeqCst) != 0
52 }
53
54 pub fn raw(&self) -> u64 {
56 self.0.load(Ordering::SeqCst)
57 }
58
59 pub fn count(&self) -> u32 {
61 self.0.load(Ordering::SeqCst).count_ones()
62 }
63
64 pub fn dirty_indices(&self) -> Vec<u8> {
66 let raw = self.raw();
67 (0..64).filter(|&i| (raw & (1 << i)) != 0).collect()
68 }
69
70 pub fn swap(&self, new_value: u64) -> u64 {
72 self.0.swap(new_value, Ordering::SeqCst)
73 }
74}
75
76impl Default for DirtyMask {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl Clone for DirtyMask {
83 fn clone(&self) -> Self {
84 Self(AtomicU64::new(self.0.load(Ordering::SeqCst)))
85 }
86}
87
88#[derive(Debug)]
90pub struct DirtyBits {
91 pub persona: DirtyMask,
93 pub standards: DirtyMask,
95 pub workflow: DirtyMask,
97 pub context: DirtyMask,
99 change_counter: AtomicU64,
101 last_sync: AtomicU64,
103}
104
105impl DirtyBits {
106 pub fn new() -> Self {
108 Self {
109 persona: DirtyMask::new(),
110 standards: DirtyMask::new(),
111 workflow: DirtyMask::new(),
112 context: DirtyMask::new(),
113 change_counter: AtomicU64::new(0),
114 last_sync: AtomicU64::new(0),
115 }
116 }
117
118 pub fn dirty_persona(&self, index: u8) {
120 self.persona.mark_dirty(index);
121 self.change_counter.fetch_add(1, Ordering::SeqCst);
122 }
123
124 pub fn dirty_standard(&self, index: u8) {
126 self.standards.mark_dirty(index);
127 self.change_counter.fetch_add(1, Ordering::SeqCst);
128 }
129
130 pub fn dirty_workflow(&self, index: u8) {
132 self.workflow.mark_dirty(index);
133 self.change_counter.fetch_add(1, Ordering::SeqCst);
134 }
135
136 pub fn dirty_context(&self, index: u8) {
138 self.context.mark_dirty(index);
139 self.change_counter.fetch_add(1, Ordering::SeqCst);
140 }
141
142 pub fn has_changes(&self) -> bool {
144 self.change_counter.load(Ordering::SeqCst) > self.last_sync.load(Ordering::SeqCst)
145 }
146
147 pub fn mark_synced(&self) {
149 self.persona.clear_all();
150 self.standards.clear_all();
151 self.workflow.clear_all();
152 self.context.clear_all();
153 self.last_sync
154 .store(self.change_counter.load(Ordering::SeqCst), Ordering::SeqCst);
155 }
156
157 pub fn change_count(&self) -> u64 {
159 self.change_counter.load(Ordering::SeqCst)
160 }
161
162 pub fn pending_changes(&self) -> u64 {
164 self.change_counter.load(Ordering::SeqCst) - self.last_sync.load(Ordering::SeqCst)
165 }
166
167 pub fn summary(&self) -> DirtySummary {
169 DirtySummary {
170 persona_dirty: self.persona.any_dirty(),
171 standards_dirty: self.standards.any_dirty(),
172 workflow_dirty: self.workflow.any_dirty(),
173 context_dirty: self.context.any_dirty(),
174 total_dirty: self.persona.count()
175 + self.standards.count()
176 + self.workflow.count()
177 + self.context.count(),
178 }
179 }
180}
181
182impl Default for DirtyBits {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188#[derive(Debug, Clone)]
190pub struct DirtySummary {
191 pub persona_dirty: bool,
193 pub standards_dirty: bool,
195 pub workflow_dirty: bool,
197 pub context_dirty: bool,
199 pub total_dirty: u32,
201}
202
203impl DirtySummary {
204 pub fn any_dirty(&self) -> bool {
206 self.persona_dirty || self.standards_dirty || self.workflow_dirty || self.context_dirty
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_dirty_mask() {
216 let mask = DirtyMask::new();
217 assert!(!mask.any_dirty());
218
219 mask.mark_dirty(0);
220 assert!(mask.is_dirty(0));
221 assert!(mask.any_dirty());
222
223 mask.mark_dirty(5);
224 assert!(mask.is_dirty(5));
225 assert_eq!(mask.count(), 2);
226
227 mask.clear(0);
228 assert!(!mask.is_dirty(0));
229 assert!(mask.is_dirty(5));
230 }
231
232 #[test]
233 fn test_dirty_bits() {
234 let bits = DirtyBits::new();
235 assert!(!bits.has_changes());
236
237 bits.dirty_standard(0);
238 assert!(bits.has_changes());
239 assert_eq!(bits.pending_changes(), 1);
240
241 bits.dirty_standard(1);
242 assert_eq!(bits.pending_changes(), 2);
243
244 bits.mark_synced();
245 assert!(!bits.has_changes());
246 assert_eq!(bits.pending_changes(), 0);
247 }
248
249 #[test]
250 fn test_dirty_summary() {
251 let bits = DirtyBits::new();
252 bits.dirty_persona(0);
253 bits.dirty_workflow(5);
254
255 let summary = bits.summary();
256 assert!(summary.persona_dirty);
257 assert!(!summary.standards_dirty);
258 assert!(summary.workflow_dirty);
259 assert!(summary.any_dirty());
260 }
261}