extern crate alloc;
use crate::instance_handle::{HANDLE_NIL, InstanceHandle};
use crate::time::Time;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SampleStateKind {
NotRead,
Read,
}
impl SampleStateKind {
#[must_use]
pub const fn is_not_read(&self) -> bool {
matches!(self, Self::NotRead)
}
}
impl Default for SampleStateKind {
fn default() -> Self {
Self::NotRead
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ViewStateKind {
New,
NotNew,
}
impl Default for ViewStateKind {
fn default() -> Self {
Self::New
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum InstanceStateKind {
Alive,
NotAliveDisposed,
NotAliveNoWriters,
}
impl InstanceStateKind {
#[must_use]
pub const fn is_alive(&self) -> bool {
matches!(self, Self::Alive)
}
#[must_use]
pub const fn is_not_alive(&self) -> bool {
!self.is_alive()
}
}
impl Default for InstanceStateKind {
fn default() -> Self {
Self::Alive
}
}
pub mod sample_state_mask {
pub const NOT_READ: u32 = 1 << 0;
pub const READ: u32 = 1 << 1;
pub const ANY: u32 = NOT_READ | READ;
}
pub mod view_state_mask {
pub const NEW: u32 = 1 << 0;
pub const NOT_NEW: u32 = 1 << 1;
pub const ANY: u32 = NEW | NOT_NEW;
}
pub mod instance_state_mask {
pub const ALIVE: u32 = 1 << 0;
pub const NOT_ALIVE_DISPOSED: u32 = 1 << 1;
pub const NOT_ALIVE_NO_WRITERS: u32 = 1 << 2;
pub const NOT_ALIVE: u32 = NOT_ALIVE_DISPOSED | NOT_ALIVE_NO_WRITERS;
pub const ANY: u32 = ALIVE | NOT_ALIVE;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SampleInfo {
pub sample_state: SampleStateKind,
pub view_state: ViewStateKind,
pub instance_state: InstanceStateKind,
pub disposed_generation_count: i32,
pub no_writers_generation_count: i32,
pub sample_rank: i32,
pub generation_rank: i32,
pub absolute_generation_rank: i32,
pub source_timestamp: Time,
pub instance_handle: InstanceHandle,
pub publication_handle: InstanceHandle,
pub valid_data: bool,
}
impl Default for SampleInfo {
fn default() -> Self {
Self {
sample_state: SampleStateKind::NotRead,
view_state: ViewStateKind::New,
instance_state: InstanceStateKind::Alive,
disposed_generation_count: 0,
no_writers_generation_count: 0,
sample_rank: 0,
generation_rank: 0,
absolute_generation_rank: 0,
source_timestamp: Time::default(),
instance_handle: HANDLE_NIL,
publication_handle: HANDLE_NIL,
valid_data: true,
}
}
}
impl SampleInfo {
#[must_use]
pub fn new_alive(
instance: InstanceHandle,
publication: InstanceHandle,
timestamp: Time,
) -> Self {
Self {
sample_state: SampleStateKind::NotRead,
view_state: ViewStateKind::New,
instance_state: InstanceStateKind::Alive,
instance_handle: instance,
publication_handle: publication,
source_timestamp: timestamp,
valid_data: true,
..Self::default()
}
}
#[must_use]
pub fn matches_states(&self, sample_mask: u32, view_mask: u32, instance_mask: u32) -> bool {
let sample_bit = match self.sample_state {
SampleStateKind::NotRead => sample_state_mask::NOT_READ,
SampleStateKind::Read => sample_state_mask::READ,
};
let view_bit = match self.view_state {
ViewStateKind::New => view_state_mask::NEW,
ViewStateKind::NotNew => view_state_mask::NOT_NEW,
};
let inst_bit = match self.instance_state {
InstanceStateKind::Alive => instance_state_mask::ALIVE,
InstanceStateKind::NotAliveDisposed => instance_state_mask::NOT_ALIVE_DISPOSED,
InstanceStateKind::NotAliveNoWriters => instance_state_mask::NOT_ALIVE_NO_WRITERS,
};
(sample_mask & sample_bit) != 0
&& (view_mask & view_bit) != 0
&& (instance_mask & inst_bit) != 0
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn defaults_are_alive_new_not_read() {
let info = SampleInfo::default();
assert_eq!(info.sample_state, SampleStateKind::NotRead);
assert_eq!(info.view_state, ViewStateKind::New);
assert_eq!(info.instance_state, InstanceStateKind::Alive);
assert!(info.valid_data);
assert_eq!(info.disposed_generation_count, 0);
assert_eq!(info.no_writers_generation_count, 0);
assert_eq!(info.instance_handle, HANDLE_NIL);
assert_eq!(info.publication_handle, HANDLE_NIL);
}
#[test]
fn instance_state_predicates() {
assert!(InstanceStateKind::Alive.is_alive());
assert!(!InstanceStateKind::Alive.is_not_alive());
assert!(InstanceStateKind::NotAliveDisposed.is_not_alive());
assert!(InstanceStateKind::NotAliveNoWriters.is_not_alive());
}
#[test]
fn sample_state_predicate() {
assert!(SampleStateKind::NotRead.is_not_read());
assert!(!SampleStateKind::Read.is_not_read());
}
#[test]
fn matches_states_filter() {
let info = SampleInfo::default();
assert!(info.matches_states(
sample_state_mask::ANY,
view_state_mask::ANY,
instance_state_mask::ANY,
));
assert!(info.matches_states(
sample_state_mask::NOT_READ,
view_state_mask::NEW,
instance_state_mask::ALIVE,
));
assert!(!info.matches_states(
sample_state_mask::READ,
view_state_mask::ANY,
instance_state_mask::ANY,
));
assert!(!info.matches_states(
sample_state_mask::ANY,
view_state_mask::NOT_NEW,
instance_state_mask::ANY,
));
assert!(!info.matches_states(
sample_state_mask::ANY,
view_state_mask::ANY,
instance_state_mask::NOT_ALIVE,
));
}
#[test]
fn new_alive_constructor_sets_handles_and_timestamp() {
let h = InstanceHandle::from_raw(7);
let pub_h = InstanceHandle::from_raw(42);
let ts = Time::new(1, 2);
let info = SampleInfo::new_alive(h, pub_h, ts);
assert_eq!(info.instance_handle, h);
assert_eq!(info.publication_handle, pub_h);
assert_eq!(info.source_timestamp, ts);
assert!(info.valid_data);
assert_eq!(info.instance_state, InstanceStateKind::Alive);
}
#[test]
fn sample_info_all_spec_fields_accessible() {
let info = SampleInfo {
sample_state: SampleStateKind::Read,
view_state: ViewStateKind::NotNew,
instance_state: InstanceStateKind::NotAliveDisposed,
disposed_generation_count: 3,
no_writers_generation_count: 5,
sample_rank: 7,
generation_rank: 9,
absolute_generation_rank: 11,
source_timestamp: Time::new(1, 2),
instance_handle: InstanceHandle::from_raw(0xCAFE),
publication_handle: InstanceHandle::from_raw(0xBEEF),
valid_data: false,
};
assert_eq!(info.sample_state, SampleStateKind::Read);
assert_eq!(info.view_state, ViewStateKind::NotNew);
assert_eq!(info.instance_state, InstanceStateKind::NotAliveDisposed);
assert_eq!(info.disposed_generation_count, 3);
assert_eq!(info.no_writers_generation_count, 5);
assert_eq!(info.sample_rank, 7);
assert_eq!(info.generation_rank, 9);
assert_eq!(info.absolute_generation_rank, 11);
assert_eq!(info.source_timestamp, Time::new(1, 2));
assert_eq!(info.instance_handle, InstanceHandle::from_raw(0xCAFE));
assert_eq!(info.publication_handle, InstanceHandle::from_raw(0xBEEF));
assert!(!info.valid_data);
}
#[test]
fn sample_info_dispose_marker_has_invalid_data() {
let info = SampleInfo {
valid_data: false,
instance_state: InstanceStateKind::NotAliveDisposed,
..SampleInfo::default()
};
assert!(!info.valid_data);
assert!(info.instance_state.is_not_alive());
}
#[test]
fn sample_info_three_state_dimensions_independent() {
let info = SampleInfo {
sample_state: SampleStateKind::Read,
view_state: ViewStateKind::NotNew,
instance_state: InstanceStateKind::Alive,
..SampleInfo::default()
};
assert!(info.matches_states(
sample_state_mask::READ,
view_state_mask::NOT_NEW,
instance_state_mask::ALIVE,
));
assert!(!info.matches_states(
sample_state_mask::NOT_READ,
view_state_mask::ANY,
instance_state_mask::ANY,
));
}
#[test]
fn sample_info_generation_rank_starts_zero() {
let info = SampleInfo::default();
assert_eq!(info.disposed_generation_count, 0);
assert_eq!(info.no_writers_generation_count, 0);
assert_eq!(info.sample_rank, 0);
assert_eq!(info.generation_rank, 0);
assert_eq!(info.absolute_generation_rank, 0);
}
#[test]
fn enum_default_impls() {
assert_eq!(SampleStateKind::default(), SampleStateKind::NotRead);
assert_eq!(ViewStateKind::default(), ViewStateKind::New);
assert_eq!(InstanceStateKind::default(), InstanceStateKind::Alive);
}
}