1extern crate alloc;
37
38use crate::instance_handle::{HANDLE_NIL, InstanceHandle};
39use crate::time::Time;
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
43pub enum SampleStateKind {
44 NotRead,
47 Read,
50}
51
52impl SampleStateKind {
53 #[must_use]
55 pub const fn is_not_read(&self) -> bool {
56 matches!(self, Self::NotRead)
57 }
58}
59
60impl Default for SampleStateKind {
61 fn default() -> Self {
62 Self::NotRead
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
68pub enum ViewStateKind {
69 New,
72 NotNew,
74}
75
76impl Default for ViewStateKind {
77 fn default() -> Self {
78 Self::New
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
84pub enum InstanceStateKind {
85 Alive,
88 NotAliveDisposed,
90 NotAliveNoWriters,
93}
94
95impl InstanceStateKind {
96 #[must_use]
98 pub const fn is_alive(&self) -> bool {
99 matches!(self, Self::Alive)
100 }
101 #[must_use]
103 pub const fn is_not_alive(&self) -> bool {
104 !self.is_alive()
105 }
106}
107
108impl Default for InstanceStateKind {
109 fn default() -> Self {
110 Self::Alive
111 }
112}
113
114pub mod sample_state_mask {
117 pub const NOT_READ: u32 = 1 << 0;
119 pub const READ: u32 = 1 << 1;
121 pub const ANY: u32 = NOT_READ | READ;
123}
124
125pub mod view_state_mask {
127 pub const NEW: u32 = 1 << 0;
129 pub const NOT_NEW: u32 = 1 << 1;
131 pub const ANY: u32 = NEW | NOT_NEW;
133}
134
135pub mod instance_state_mask {
138 pub const ALIVE: u32 = 1 << 0;
140 pub const NOT_ALIVE_DISPOSED: u32 = 1 << 1;
142 pub const NOT_ALIVE_NO_WRITERS: u32 = 1 << 2;
144 pub const NOT_ALIVE: u32 = NOT_ALIVE_DISPOSED | NOT_ALIVE_NO_WRITERS;
146 pub const ANY: u32 = ALIVE | NOT_ALIVE;
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub struct SampleInfo {
161 pub sample_state: SampleStateKind,
163 pub view_state: ViewStateKind,
165 pub instance_state: InstanceStateKind,
167 pub disposed_generation_count: i32,
170 pub no_writers_generation_count: i32,
173 pub sample_rank: i32,
176 pub generation_rank: i32,
179 pub absolute_generation_rank: i32,
182 pub source_timestamp: Time,
184 pub instance_handle: InstanceHandle,
186 pub publication_handle: InstanceHandle,
189 pub valid_data: bool,
192}
193
194impl Default for SampleInfo {
195 fn default() -> Self {
196 Self {
197 sample_state: SampleStateKind::NotRead,
198 view_state: ViewStateKind::New,
199 instance_state: InstanceStateKind::Alive,
200 disposed_generation_count: 0,
201 no_writers_generation_count: 0,
202 sample_rank: 0,
203 generation_rank: 0,
204 absolute_generation_rank: 0,
205 source_timestamp: Time::default(),
206 instance_handle: HANDLE_NIL,
207 publication_handle: HANDLE_NIL,
208 valid_data: true,
209 }
210 }
211}
212
213impl SampleInfo {
214 #[must_use]
217 pub fn new_alive(
218 instance: InstanceHandle,
219 publication: InstanceHandle,
220 timestamp: Time,
221 ) -> Self {
222 Self {
223 sample_state: SampleStateKind::NotRead,
224 view_state: ViewStateKind::New,
225 instance_state: InstanceStateKind::Alive,
226 instance_handle: instance,
227 publication_handle: publication,
228 source_timestamp: timestamp,
229 valid_data: true,
230 ..Self::default()
231 }
232 }
233
234 #[must_use]
237 pub fn matches_states(&self, sample_mask: u32, view_mask: u32, instance_mask: u32) -> bool {
238 let sample_bit = match self.sample_state {
239 SampleStateKind::NotRead => sample_state_mask::NOT_READ,
240 SampleStateKind::Read => sample_state_mask::READ,
241 };
242 let view_bit = match self.view_state {
243 ViewStateKind::New => view_state_mask::NEW,
244 ViewStateKind::NotNew => view_state_mask::NOT_NEW,
245 };
246 let inst_bit = match self.instance_state {
247 InstanceStateKind::Alive => instance_state_mask::ALIVE,
248 InstanceStateKind::NotAliveDisposed => instance_state_mask::NOT_ALIVE_DISPOSED,
249 InstanceStateKind::NotAliveNoWriters => instance_state_mask::NOT_ALIVE_NO_WRITERS,
250 };
251 (sample_mask & sample_bit) != 0
252 && (view_mask & view_bit) != 0
253 && (instance_mask & inst_bit) != 0
254 }
255}
256
257#[cfg(test)]
258#[allow(clippy::expect_used, clippy::unwrap_used)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn defaults_are_alive_new_not_read() {
264 let info = SampleInfo::default();
265 assert_eq!(info.sample_state, SampleStateKind::NotRead);
266 assert_eq!(info.view_state, ViewStateKind::New);
267 assert_eq!(info.instance_state, InstanceStateKind::Alive);
268 assert!(info.valid_data);
269 assert_eq!(info.disposed_generation_count, 0);
270 assert_eq!(info.no_writers_generation_count, 0);
271 assert_eq!(info.instance_handle, HANDLE_NIL);
272 assert_eq!(info.publication_handle, HANDLE_NIL);
273 }
274
275 #[test]
276 fn instance_state_predicates() {
277 assert!(InstanceStateKind::Alive.is_alive());
278 assert!(!InstanceStateKind::Alive.is_not_alive());
279 assert!(InstanceStateKind::NotAliveDisposed.is_not_alive());
280 assert!(InstanceStateKind::NotAliveNoWriters.is_not_alive());
281 }
282
283 #[test]
284 fn sample_state_predicate() {
285 assert!(SampleStateKind::NotRead.is_not_read());
286 assert!(!SampleStateKind::Read.is_not_read());
287 }
288
289 #[test]
290 fn matches_states_filter() {
291 let info = SampleInfo::default();
292 assert!(info.matches_states(
293 sample_state_mask::ANY,
294 view_state_mask::ANY,
295 instance_state_mask::ANY,
296 ));
297 assert!(info.matches_states(
298 sample_state_mask::NOT_READ,
299 view_state_mask::NEW,
300 instance_state_mask::ALIVE,
301 ));
302 assert!(!info.matches_states(
303 sample_state_mask::READ,
304 view_state_mask::ANY,
305 instance_state_mask::ANY,
306 ));
307 assert!(!info.matches_states(
308 sample_state_mask::ANY,
309 view_state_mask::NOT_NEW,
310 instance_state_mask::ANY,
311 ));
312 assert!(!info.matches_states(
313 sample_state_mask::ANY,
314 view_state_mask::ANY,
315 instance_state_mask::NOT_ALIVE,
316 ));
317 }
318
319 #[test]
320 fn new_alive_constructor_sets_handles_and_timestamp() {
321 let h = InstanceHandle::from_raw(7);
322 let pub_h = InstanceHandle::from_raw(42);
323 let ts = Time::new(1, 2);
324 let info = SampleInfo::new_alive(h, pub_h, ts);
325 assert_eq!(info.instance_handle, h);
326 assert_eq!(info.publication_handle, pub_h);
327 assert_eq!(info.source_timestamp, ts);
328 assert!(info.valid_data);
329 assert_eq!(info.instance_state, InstanceStateKind::Alive);
330 }
331
332 #[test]
335 fn sample_info_all_spec_fields_accessible() {
336 let info = SampleInfo {
340 sample_state: SampleStateKind::Read,
341 view_state: ViewStateKind::NotNew,
342 instance_state: InstanceStateKind::NotAliveDisposed,
343 disposed_generation_count: 3,
344 no_writers_generation_count: 5,
345 sample_rank: 7,
346 generation_rank: 9,
347 absolute_generation_rank: 11,
348 source_timestamp: Time::new(1, 2),
349 instance_handle: InstanceHandle::from_raw(0xCAFE),
350 publication_handle: InstanceHandle::from_raw(0xBEEF),
351 valid_data: false,
352 };
353
354 assert_eq!(info.sample_state, SampleStateKind::Read);
355 assert_eq!(info.view_state, ViewStateKind::NotNew);
356 assert_eq!(info.instance_state, InstanceStateKind::NotAliveDisposed);
357 assert_eq!(info.disposed_generation_count, 3);
358 assert_eq!(info.no_writers_generation_count, 5);
359 assert_eq!(info.sample_rank, 7);
360 assert_eq!(info.generation_rank, 9);
361 assert_eq!(info.absolute_generation_rank, 11);
362 assert_eq!(info.source_timestamp, Time::new(1, 2));
363 assert_eq!(info.instance_handle, InstanceHandle::from_raw(0xCAFE));
364 assert_eq!(info.publication_handle, InstanceHandle::from_raw(0xBEEF));
365 assert!(!info.valid_data);
366 }
367
368 #[test]
369 fn sample_info_dispose_marker_has_invalid_data() {
370 let info = SampleInfo {
374 valid_data: false,
375 instance_state: InstanceStateKind::NotAliveDisposed,
376 ..SampleInfo::default()
377 };
378 assert!(!info.valid_data);
379 assert!(info.instance_state.is_not_alive());
380 }
381
382 #[test]
383 fn sample_info_three_state_dimensions_independent() {
384 let info = SampleInfo {
387 sample_state: SampleStateKind::Read,
388 view_state: ViewStateKind::NotNew,
389 instance_state: InstanceStateKind::Alive,
390 ..SampleInfo::default()
391 };
392 assert!(info.matches_states(
394 sample_state_mask::READ,
395 view_state_mask::NOT_NEW,
396 instance_state_mask::ALIVE,
397 ));
398 assert!(!info.matches_states(
400 sample_state_mask::NOT_READ,
401 view_state_mask::ANY,
402 instance_state_mask::ANY,
403 ));
404 }
405
406 #[test]
407 fn sample_info_generation_rank_starts_zero() {
408 let info = SampleInfo::default();
410 assert_eq!(info.disposed_generation_count, 0);
411 assert_eq!(info.no_writers_generation_count, 0);
412 assert_eq!(info.sample_rank, 0);
413 assert_eq!(info.generation_rank, 0);
414 assert_eq!(info.absolute_generation_rank, 0);
415 }
416
417 #[test]
418 fn enum_default_impls() {
419 assert_eq!(SampleStateKind::default(), SampleStateKind::NotRead);
420 assert_eq!(ViewStateKind::default(), ViewStateKind::New);
421 assert_eq!(InstanceStateKind::default(), InstanceStateKind::Alive);
422 }
423}