use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Generation(pub u64);
impl Generation {
#[must_use]
pub fn new(value: u64) -> Self {
Generation(value)
}
#[must_use]
pub fn is_stale(&self, current: Generation) -> bool {
self.0 < current.0
}
}
impl std::fmt::Display for Generation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntryState {
Absent,
Ready,
Stale,
InFlight,
Prepared,
Failed,
}
impl EntryState {
#[must_use]
pub const fn is_readable(self) -> bool {
matches!(self, Self::Ready | Self::Stale)
}
#[must_use]
pub const fn is_claimable(self) -> bool {
matches!(self, Self::Absent | Self::Failed | Self::Stale)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntentKind {
Write,
Move,
}
impl IntentKind {
#[must_use]
pub const fn as_u8(self) -> u8 {
match self {
Self::Write => 1,
Self::Move => 2,
}
}
#[must_use]
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(Self::Write),
2 => Some(Self::Move),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CommitIntent {
pub kind: IntentKind,
pub target_tier: TierIdLite,
pub generation: Generation,
pub started_nanos: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TierIdLite(pub u8);
impl TierIdLite {
#[must_use]
pub const fn new(index: u8) -> Self {
Self(index)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CommitToken {
pub address: crate::integrity::KeyAddress,
pub generation: Generation,
pub kind: IntentKind,
pub target_tier: TierIdLite,
}
impl CommitToken {
#[must_use]
pub const fn expired_by(&self, current: Generation) -> bool {
current.0 != self.generation.0
}
}
#[derive(Debug)]
pub struct CacheEntry<V> {
pub value: V,
pub generation: Generation,
pub expiration: Option<Duration>,
}
impl<V> CacheEntry<V> {
pub fn new(value: V, generation: Generation, expiration: Option<Duration>) -> Self {
CacheEntry {
value,
generation,
expiration,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prepared_is_not_readable() {
assert!(!EntryState::Prepared.is_readable());
assert!(EntryState::Ready.is_readable());
}
#[test]
fn prepared_is_not_claimable() {
assert!(!EntryState::Prepared.is_claimable());
}
#[test]
fn generation_staleness_is_strict() {
assert!(Generation::new(1).is_stale(Generation::new(2)));
assert!(!Generation::new(2).is_stale(Generation::new(2)));
assert!(!Generation::new(3).is_stale(Generation::new(2)));
}
#[test]
fn a_token_dies_when_the_generation_moves() {
let token = CommitToken {
address: crate::integrity::KeyAddress::of(b"k", crate::integrity::Placement::Default),
generation: Generation::new(5),
kind: IntentKind::Write,
target_tier: TierIdLite::new(1),
};
assert!(!token.expired_by(Generation::new(5)));
assert!(token.expired_by(Generation::new(6)));
}
}