use std::collections::VecDeque;
use std::time::Instant;
use super::RendererDeliveryId;
use crate::presentation_mutation_generation::PresentationMutationGeneration;
pub(super) const MAX_PENDING_PRESENTATION_OBSERVATIONS: usize = 64;
const PRESENTATION_KIND_BITS: usize = u64::BITS as usize;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PresentationObservationKey(u8);
impl PresentationObservationKey {
pub fn new(value: u8) -> Option<Self> {
(usize::from(value) < PRESENTATION_KIND_BITS).then_some(Self(value))
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PresentationInvalidation(u64);
impl PresentationInvalidation {
pub const fn none() -> Self {
Self(0)
}
pub const fn with(mut self, key: PresentationObservationKey) -> Self {
self.0 |= 1_u64 << key.0;
self
}
const fn contains(self, key: PresentationObservationKey) -> bool {
self.0 & (1_u64 << key.0) != 0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct OpaquePresentationFact {
label: &'static str,
key: PresentationObservationKey,
invalidates: PresentationInvalidation,
}
impl OpaquePresentationFact {
pub const fn new(
label: &'static str,
key: PresentationObservationKey,
invalidates: PresentationInvalidation,
) -> Self {
Self {
label,
key,
invalidates,
}
}
}
pub(super) struct PresentationObservation {
pub(super) delivery_id: RendererDeliveryId,
pub(super) fact: &'static str,
key: PresentationObservationKey,
pub(super) generation: PresentationMutationGeneration,
pub(super) observed_at: Instant,
}
pub(super) struct CapturedPresentationObservations {
pub(super) facts: Vec<PresentationObservation>,
pub(super) omitted: u64,
pub(super) generation: PresentationMutationGeneration,
}
struct OmittedPresentationObservations {
key: PresentationObservationKey,
count: u64,
}
impl OmittedPresentationObservations {
const fn new(key: PresentationObservationKey) -> Self {
Self { key, count: 1 }
}
fn has_key(&self, key: PresentationObservationKey) -> bool {
self.key == key
}
const fn is_invalidated_by(&self, invalidates: PresentationInvalidation) -> bool {
invalidates.contains(self.key)
}
fn increment(&mut self) {
self.count = self.count.saturating_add(1);
}
}
pub(super) struct PresentationObservationState {
generation: PresentationMutationGeneration,
pending: VecDeque<PresentationObservation>,
omitted_by_key: Vec<OmittedPresentationObservations>,
omitted_total: u64,
#[cfg(test)]
pub(super) successful_test_passes:
Vec<Vec<(RendererDeliveryId, PresentationMutationGeneration)>>,
}
impl PresentationObservationState {
pub(super) fn new() -> Self {
Self {
generation: PresentationMutationGeneration::default(),
pending: VecDeque::new(),
omitted_by_key: Vec::new(),
omitted_total: 0,
#[cfg(test)]
successful_test_passes: Vec::new(),
}
}
pub(super) fn is_empty(&self) -> bool {
self.pending.is_empty() && self.omitted_total == 0
}
pub(super) fn register(
&mut self,
delivery_id: RendererDeliveryId,
fact: OpaquePresentationFact,
observed_at: Instant,
) {
self.pending
.retain(|pending| !fact.invalidates.contains(pending.key));
self.omitted_by_key.retain(|omitted| {
if omitted.is_invalidated_by(fact.invalidates) {
self.omitted_total = self.omitted_total.saturating_sub(omitted.count);
false
} else {
true
}
});
self.generation.advance();
if self.pending.len() == MAX_PENDING_PRESENTATION_OBSERVATIONS {
if let Some(omitted) = self
.omitted_by_key
.iter_mut()
.find(|omitted| omitted.has_key(fact.key))
{
omitted.increment();
} else {
self.omitted_by_key
.push(OmittedPresentationObservations::new(fact.key));
}
self.omitted_total = self.omitted_total.saturating_add(1);
return;
}
self.pending.push_back(PresentationObservation {
delivery_id,
fact: fact.label,
key: fact.key,
generation: self.generation,
observed_at,
});
}
pub(super) fn capture(&mut self) -> CapturedPresentationObservations {
self.omitted_by_key.clear();
CapturedPresentationObservations {
facts: self.pending.drain(..).collect(),
omitted: std::mem::take(&mut self.omitted_total),
generation: self.generation,
}
}
#[cfg(test)]
pub(super) fn record_success_for_test(
&mut self,
observations: &CapturedPresentationObservations,
) {
self.successful_test_passes.push(
observations
.facts
.iter()
.map(|fact| (fact.delivery_id, fact.generation))
.collect(),
);
}
}
#[cfg(test)]
#[path = "presentation_observation_state_tests.rs"]
mod presentation_observation_state_tests;