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)]
161pub struct SampleInfo {
162 pub sample_state: SampleStateKind,
164 pub view_state: ViewStateKind,
166 pub instance_state: InstanceStateKind,
168 pub disposed_generation_count: i32,
171 pub no_writers_generation_count: i32,
174 pub sample_rank: i32,
177 pub generation_rank: i32,
180 pub absolute_generation_rank: i32,
183 pub source_timestamp: Time,
185 pub instance_handle: InstanceHandle,
187 pub publication_handle: InstanceHandle,
190 pub valid_data: bool,
193}
194
195impl Default for SampleInfo {
196 fn default() -> Self {
197 Self {
198 sample_state: SampleStateKind::NotRead,
199 view_state: ViewStateKind::New,
200 instance_state: InstanceStateKind::Alive,
201 disposed_generation_count: 0,
202 no_writers_generation_count: 0,
203 sample_rank: 0,
204 generation_rank: 0,
205 absolute_generation_rank: 0,
206 source_timestamp: Time::default(),
207 instance_handle: HANDLE_NIL,
208 publication_handle: HANDLE_NIL,
209 valid_data: true,
210 }
211 }
212}
213
214impl SampleInfo {
215 #[must_use]
218 pub fn new_alive(
219 instance: InstanceHandle,
220 publication: InstanceHandle,
221 timestamp: Time,
222 ) -> Self {
223 Self {
224 sample_state: SampleStateKind::NotRead,
225 view_state: ViewStateKind::New,
226 instance_state: InstanceStateKind::Alive,
227 instance_handle: instance,
228 publication_handle: publication,
229 source_timestamp: timestamp,
230 valid_data: true,
231 ..Self::default()
232 }
233 }
234
235 #[must_use]
238 pub fn matches_states(&self, sample_mask: u32, view_mask: u32, instance_mask: u32) -> bool {
239 let sample_bit = match self.sample_state {
240 SampleStateKind::NotRead => sample_state_mask::NOT_READ,
241 SampleStateKind::Read => sample_state_mask::READ,
242 };
243 let view_bit = match self.view_state {
244 ViewStateKind::New => view_state_mask::NEW,
245 ViewStateKind::NotNew => view_state_mask::NOT_NEW,
246 };
247 let inst_bit = match self.instance_state {
248 InstanceStateKind::Alive => instance_state_mask::ALIVE,
249 InstanceStateKind::NotAliveDisposed => instance_state_mask::NOT_ALIVE_DISPOSED,
250 InstanceStateKind::NotAliveNoWriters => instance_state_mask::NOT_ALIVE_NO_WRITERS,
251 };
252 (sample_mask & sample_bit) != 0
253 && (view_mask & view_bit) != 0
254 && (instance_mask & inst_bit) != 0
255 }
256}
257
258#[cfg(test)]
259#[allow(clippy::expect_used, clippy::unwrap_used)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn defaults_are_alive_new_not_read() {
265 let info = SampleInfo::default();
266 assert_eq!(info.sample_state, SampleStateKind::NotRead);
267 assert_eq!(info.view_state, ViewStateKind::New);
268 assert_eq!(info.instance_state, InstanceStateKind::Alive);
269 assert!(info.valid_data);
270 assert_eq!(info.disposed_generation_count, 0);
271 assert_eq!(info.no_writers_generation_count, 0);
272 assert_eq!(info.instance_handle, HANDLE_NIL);
273 assert_eq!(info.publication_handle, HANDLE_NIL);
274 }
275
276 #[test]
277 fn instance_state_predicates() {
278 assert!(InstanceStateKind::Alive.is_alive());
279 assert!(!InstanceStateKind::Alive.is_not_alive());
280 assert!(InstanceStateKind::NotAliveDisposed.is_not_alive());
281 assert!(InstanceStateKind::NotAliveNoWriters.is_not_alive());
282 }
283
284 #[test]
285 fn sample_state_predicate() {
286 assert!(SampleStateKind::NotRead.is_not_read());
287 assert!(!SampleStateKind::Read.is_not_read());
288 }
289
290 #[test]
291 fn matches_states_filter() {
292 let info = SampleInfo::default();
293 assert!(info.matches_states(
294 sample_state_mask::ANY,
295 view_state_mask::ANY,
296 instance_state_mask::ANY,
297 ));
298 assert!(info.matches_states(
299 sample_state_mask::NOT_READ,
300 view_state_mask::NEW,
301 instance_state_mask::ALIVE,
302 ));
303 assert!(!info.matches_states(
304 sample_state_mask::READ,
305 view_state_mask::ANY,
306 instance_state_mask::ANY,
307 ));
308 assert!(!info.matches_states(
309 sample_state_mask::ANY,
310 view_state_mask::NOT_NEW,
311 instance_state_mask::ANY,
312 ));
313 assert!(!info.matches_states(
314 sample_state_mask::ANY,
315 view_state_mask::ANY,
316 instance_state_mask::NOT_ALIVE,
317 ));
318 }
319
320 #[test]
321 fn new_alive_constructor_sets_handles_and_timestamp() {
322 let h = InstanceHandle::from_raw(7);
323 let pub_h = InstanceHandle::from_raw(42);
324 let ts = Time::new(1, 2);
325 let info = SampleInfo::new_alive(h, pub_h, ts);
326 assert_eq!(info.instance_handle, h);
327 assert_eq!(info.publication_handle, pub_h);
328 assert_eq!(info.source_timestamp, ts);
329 assert!(info.valid_data);
330 assert_eq!(info.instance_state, InstanceStateKind::Alive);
331 }
332
333 #[test]
336 fn sample_info_all_spec_fields_accessible() {
337 let info = SampleInfo {
341 sample_state: SampleStateKind::Read,
342 view_state: ViewStateKind::NotNew,
343 instance_state: InstanceStateKind::NotAliveDisposed,
344 disposed_generation_count: 3,
345 no_writers_generation_count: 5,
346 sample_rank: 7,
347 generation_rank: 9,
348 absolute_generation_rank: 11,
349 source_timestamp: Time::new(1, 2),
350 instance_handle: InstanceHandle::from_raw(0xCAFE),
351 publication_handle: InstanceHandle::from_raw(0xBEEF),
352 valid_data: false,
353 };
354
355 assert_eq!(info.sample_state, SampleStateKind::Read);
356 assert_eq!(info.view_state, ViewStateKind::NotNew);
357 assert_eq!(info.instance_state, InstanceStateKind::NotAliveDisposed);
358 assert_eq!(info.disposed_generation_count, 3);
359 assert_eq!(info.no_writers_generation_count, 5);
360 assert_eq!(info.sample_rank, 7);
361 assert_eq!(info.generation_rank, 9);
362 assert_eq!(info.absolute_generation_rank, 11);
363 assert_eq!(info.source_timestamp, Time::new(1, 2));
364 assert_eq!(info.instance_handle, InstanceHandle::from_raw(0xCAFE));
365 assert_eq!(info.publication_handle, InstanceHandle::from_raw(0xBEEF));
366 assert!(!info.valid_data);
367 }
368
369 #[test]
370 fn sample_info_dispose_marker_has_invalid_data() {
371 let info = SampleInfo {
375 valid_data: false,
376 instance_state: InstanceStateKind::NotAliveDisposed,
377 ..SampleInfo::default()
378 };
379 assert!(!info.valid_data);
380 assert!(info.instance_state.is_not_alive());
381 }
382
383 #[test]
384 fn sample_info_three_state_dimensions_independent() {
385 let info = SampleInfo {
388 sample_state: SampleStateKind::Read,
389 view_state: ViewStateKind::NotNew,
390 instance_state: InstanceStateKind::Alive,
391 ..SampleInfo::default()
392 };
393 assert!(info.matches_states(
395 sample_state_mask::READ,
396 view_state_mask::NOT_NEW,
397 instance_state_mask::ALIVE,
398 ));
399 assert!(!info.matches_states(
401 sample_state_mask::NOT_READ,
402 view_state_mask::ANY,
403 instance_state_mask::ANY,
404 ));
405 }
406
407 #[test]
408 fn sample_info_generation_rank_starts_zero() {
409 let info = SampleInfo::default();
411 assert_eq!(info.disposed_generation_count, 0);
412 assert_eq!(info.no_writers_generation_count, 0);
413 assert_eq!(info.sample_rank, 0);
414 assert_eq!(info.generation_rank, 0);
415 assert_eq!(info.absolute_generation_rank, 0);
416 }
417
418 #[test]
419 fn enum_default_impls() {
420 assert_eq!(SampleStateKind::default(), SampleStateKind::NotRead);
421 assert_eq!(ViewStateKind::default(), ViewStateKind::New);
422 assert_eq!(InstanceStateKind::default(), InstanceStateKind::Alive);
423 }
424}