Skip to main content

sva_engine/
cache.rs

1// Concern: declares what a store holds under a content hash and how a key is built | Non-concern: what the store keeps and evicts (store.rs) | IO: (Hash) -> a payload
2
3mod stats;
4mod store;
5
6pub use stats::{CacheStats, Lookup, Outcome};
7pub(crate) use stats::{Lens, Recording};
8pub use store::{Cache, CachePolicy, DEFAULT_CACHE_BYTES, PrunePolicy};
9pub use sva_formula::Hash;
10
11use sva_formula::SpectralSum;
12use sva_samples::{Buffer, Frames, Label};
13
14/// A spectral sum, one collapse of it, or one analysis of that collapse.
15#[derive(Clone, Debug, PartialEq)]
16pub enum Payload {
17    Samples(Box<Buffer>),
18    Frames(Box<Frames>),
19    Symbolic(Box<SpectralSum>),
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum PayloadKind {
24    Samples,
25    Frames,
26    Symbolic,
27}
28
29/// An entry not matching this is a miss, never a coercion.
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum Expected {
32    Samples {
33        rate: u32,
34        width: usize,
35        samples: usize,
36    },
37    Frames,
38    Symbolic,
39}
40
41#[derive(Clone, Debug, PartialEq)]
42pub struct Entry {
43    pub payload: Payload,
44    /// FORMAT 9.3: the label is part of the value, so a hit answers with the cold run's.
45    pub label: Option<Label>,
46}
47
48impl Expected {
49    pub fn kind(self) -> PayloadKind {
50        match self {
51            Expected::Samples { .. } => PayloadKind::Samples,
52            Expected::Frames => PayloadKind::Frames,
53            Expected::Symbolic => PayloadKind::Symbolic,
54        }
55    }
56}
57
58impl Payload {
59    pub fn kind(&self) -> PayloadKind {
60        match self {
61            Payload::Samples(_) => PayloadKind::Samples,
62            Payload::Frames(_) => PayloadKind::Frames,
63            Payload::Symbolic(_) => PayloadKind::Symbolic,
64        }
65    }
66
67    pub fn samples(&self) -> Option<&Buffer> {
68        match self {
69            Payload::Samples(buffer) => Some(buffer),
70            _ => None,
71        }
72    }
73
74    pub fn symbolic(&self) -> Option<&SpectralSum> {
75        match self {
76            Payload::Symbolic(sum) => Some(sum),
77            _ => None,
78        }
79    }
80
81    pub fn bytes(&self) -> usize {
82        match self {
83            Payload::Samples(b) => b.len() * b.width * size_of::<f64>(),
84            Payload::Frames(f) => f.width * f.frames * f.bins * 2 * size_of::<f64>(),
85            Payload::Symbolic(n) => {
86                size_of::<SpectralSum>()
87                    + n.lanes
88                        .iter()
89                        .map(|lane| {
90                            lane.atoms.len() * size_of::<sva_formula::SpectralAtom>()
91                                + (lane.series.len() + lane.modal.len()) * size_of::<SpectralSum>()
92                        })
93                        .sum::<usize>()
94            }
95        }
96    }
97
98    pub fn answers(&self, expected: Expected) -> bool {
99        match (self, expected) {
100            (
101                Payload::Samples(b),
102                Expected::Samples {
103                    rate,
104                    width,
105                    samples,
106                },
107            ) => b.rate == rate && b.width == width && b.len() == samples,
108            (Payload::Frames(_), Expected::Frames) => true,
109            (Payload::Symbolic(_), Expected::Symbolic) => true,
110            _ => false,
111        }
112    }
113}
114
115/// A hash carries the table version, so a bump retires every symbolic entry.
116pub fn symbolic_key(src: Hash) -> Hash {
117    mixed(src, &[0x73_79_6d_62_6f_6c_69_63])
118}
119
120/// The buffer's own key and the window it was read through.
121pub fn frames_key(buffer: Hash, window: usize, hop: usize) -> Hash {
122    mixed(
123        buffer,
124        &[window as u64, hop as u64, 0x66_72_61_6d_65_73_00_01],
125    )
126}
127
128/// One closed form at one rate, origin, length and width, scored or not: the label is part of
129/// the value.
130pub fn buffer_key(
131    symbolic: Hash,
132    rate: u32,
133    origin_secs: f64,
134    samples: usize,
135    width: usize,
136    score: sva_samples::AliasScore,
137) -> Hash {
138    mixed(
139        symbolic,
140        &[
141            u64::from(rate),
142            origin_secs.to_bits(),
143            samples as u64,
144            width as u64,
145            u64::from(score == sva_samples::AliasScore::Asked),
146            0x62_75_66_66_65_72_00_01,
147        ],
148    )
149}
150
151const ADDRESS_ROTATE: u32 = 17;
152
153pub(crate) fn mixed(seed: Hash, parts: &[u64]) -> Hash {
154    let mut lanes = sva_formula::Lanes::<ADDRESS_ROTATE>::from(seed);
155    for part in parts {
156        lanes.word(*part);
157    }
158    lanes.finish()
159}