1use std::collections::HashMap;
4use std::sync::{Arc, Mutex, MutexGuard};
5
6use sva_formula::Hash;
7use sva_samples::{Buffer, Extent, Label};
8
9use super::stored::{Header, Samples};
10use super::{Entry, Expected, Payload, Stored, joined};
11
12pub const DEFAULT_CACHE_BYTES: u64 = 2 << 30;
13
14pub const DEFAULT_MARK_EVERY: usize = 16_384;
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub enum CachePolicy {
21 #[default]
22 All,
23 Forks,
25 Target,
26}
27
28impl CachePolicy {
29 pub const ALL: [CachePolicy; 3] = [CachePolicy::All, CachePolicy::Forks, CachePolicy::Target];
30
31 pub fn name(self) -> &'static str {
32 match self {
33 CachePolicy::All => "all",
34 CachePolicy::Forks => "forks",
35 CachePolicy::Target => "target",
36 }
37 }
38
39 pub fn named(name: &str) -> Option<CachePolicy> {
40 CachePolicy::ALL.into_iter().find(|p| p.name() == name)
41 }
42
43 fn keeps(self, fork: bool, target: bool) -> bool {
44 match self {
45 CachePolicy::All => true,
46 CachePolicy::Forks => fork || target,
47 CachePolicy::Target => target,
48 }
49 }
50}
51
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
53pub enum PrunePolicy {
54 #[default]
56 Oldest,
57 Forks,
59}
60
61impl PrunePolicy {
62 pub const ALL: [PrunePolicy; 2] = [PrunePolicy::Oldest, PrunePolicy::Forks];
63
64 pub fn name(self) -> &'static str {
65 match self {
66 PrunePolicy::Oldest => "oldest",
67 PrunePolicy::Forks => "forks",
68 }
69 }
70
71 pub fn named(name: &str) -> Option<PrunePolicy> {
72 PrunePolicy::ALL.into_iter().find(|p| p.name() == name)
73 }
74}
75
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
78pub struct Counters {
79 pub disk_lookups: u64,
80 pub disk_reads: u64,
81 pub disk_read_bytes: u64,
82 pub promotions: u64,
84 pub writebacks: u64,
85 pub hits: u64,
87 pub probation_evictions: u64,
88 pub protected_evictions: u64,
89}
90
91impl Counters {
92 pub fn since(self, then: Counters) -> Counters {
93 Counters {
94 disk_lookups: self.disk_lookups - then.disk_lookups,
95 disk_reads: self.disk_reads - then.disk_reads,
96 disk_read_bytes: self.disk_read_bytes - then.disk_read_bytes,
97 promotions: self.promotions - then.promotions,
98 writebacks: self.writebacks - then.writebacks,
99 hits: self.hits - then.hits,
100 probation_evictions: self.probation_evictions - then.probation_evictions,
101 protected_evictions: self.protected_evictions - then.protected_evictions,
102 }
103 }
104
105 pub fn evictions(self) -> u64 {
106 self.probation_evictions + self.protected_evictions
107 }
108}
109
110const PROTECTED: (u64, u64) = (3, 4);
113
114#[derive(Clone, Copy, Debug)]
115pub(crate) struct Stamp {
116 pub tree: u64,
117 pub fork: bool,
118 pub slot: Option<Hash>,
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123pub(crate) enum Kept {
124 Held,
125 Replaced,
126 Refused,
127}
128
129pub(crate) enum Known {
132 Hit(Arc<Stored>),
133 Miss,
134 Unknown,
135}
136
137#[derive(Clone, Debug, PartialEq)]
140pub(crate) enum Offered {
141 Values {
142 keys: Vec<(i64, Hash)>,
143 by: i64,
144 },
145 Moves {
146 of: Hash,
147 by: i64,
148 },
149 Held(Vec<Arc<Buffer>>),
151}
152
153pub(crate) struct Writeback {
154 pub(crate) key: Hash,
155 pub(crate) head: Header,
156 pub(crate) parts: Vec<Arc<Buffer>>,
157}
158
159enum Item {
160 Value {
161 payload: Payload,
162 label: Option<Label>,
163 slot: Option<Hash>,
164 },
165 Node {
166 source: Source,
167 slot: Option<Hash>,
168 dirty: bool,
170 settled: bool,
171 },
172}
173
174enum Source {
175 Disk {
176 head: Box<Header>,
177 chunks: Vec<Arc<Buffer>>,
178 },
179 Offered {
180 stored: Box<Stored>,
181 offered: Offered,
182 },
183}
184
185impl Source {
186 fn stored(&self) -> &Stored {
187 match self {
188 Source::Disk { head, .. } => head.stored(),
189 Source::Offered { stored, .. } => stored,
190 }
191 }
192}
193
194struct Held {
195 item: Item,
196 read: u64,
197 since: u64,
198 hit_round: u64,
199 protected: bool,
200 tree: u64,
201 fork: bool,
202}
203
204impl Held {
205 fn admitted(item: Item, at: u64, tree: u64, fork: bool) -> Held {
206 Held {
207 item,
208 read: at,
209 since: at,
210 hit_round: 0,
211 protected: false,
212 tree,
213 fork,
214 }
215 }
216}
217
218fn planes(b: &Buffer) -> u64 {
219 (b.len() * b.width * size_of::<f64>()) as u64
220}
221
222impl Held {
223 fn bytes(&self) -> u64 {
224 match &self.item {
225 Item::Value { payload, .. } => payload.bytes() as u64,
226 Item::Node {
227 source:
228 Source::Disk { chunks, .. }
229 | Source::Offered {
230 offered: Offered::Held(chunks),
231 ..
232 },
233 ..
234 } => chunks.iter().map(|c| planes(c)).sum(),
235 Item::Node { .. } => 0,
236 }
237 }
238
239 fn slot(&self) -> Option<Hash> {
240 match &self.item {
241 Item::Value { slot, .. } | Item::Node { slot, .. } => *slot,
242 }
243 }
244}
245
246#[derive(Default)]
247struct State {
248 entries: HashMap<Hash, Held>,
249 slots: HashMap<Hash, Hash>,
250 misses: HashMap<Hash, u64>,
251 pending: Vec<Writeback>,
252 bytes: u64,
253 max_bytes: u64,
254 policy: CachePolicy,
255 clock: u64,
256 tree: u64,
257 round: u64,
258 mark_every: usize,
259 counters: Counters,
260 disk: bool,
261 failed: (u64, Option<String>),
263}
264
265impl State {
266 fn tick(&mut self) -> u64 {
267 self.clock += 1;
268 self.clock
269 }
270
271 fn stands(&self, key: Hash) -> bool {
272 self.disk && self.doomed(key).len() > 1
273 }
274
275 fn unsettled(&self, key: Hash) -> bool {
276 matches!(
277 self.entries.get(&key),
278 Some(Held {
279 item: Item::Node { settled: false, .. },
280 ..
281 })
282 )
283 }
284
285 fn doomed(&self, key: Hash) -> Vec<Hash> {
287 let mut out = vec![key];
288 let mut k = 0;
289 while k < out.len() {
290 let gone = out[k];
291 for (at, held) in &self.entries {
292 let Item::Node {
293 source: Source::Offered { offered, .. },
294 ..
295 } = &held.item
296 else {
297 continue;
298 };
299 let reads = match offered {
300 Offered::Values { keys, .. } => keys.iter().any(|(_, key)| *key == gone),
301 Offered::Moves { of, .. } => *of == gone,
302 Offered::Held(_) => false,
303 };
304 if reads && !out.contains(at) {
305 out.push(*at);
306 }
307 }
308 k += 1;
309 }
310 out
311 }
312
313 fn remove(&mut self, key: Hash) -> bool {
316 if !self.entries.contains_key(&key) {
317 return false;
318 }
319 let doomed = self.doomed(key);
320 for at in &doomed {
321 self.flush(*at);
322 }
323 for at in doomed {
324 if at == key || !self.unsettled(at) {
325 self.discard(at);
326 }
327 }
328 true
329 }
330
331 fn discard(&mut self, key: Hash) {
332 let Some(gone) = self.entries.remove(&key) else {
333 return;
334 };
335 self.bytes -= gone.bytes();
336 let value = matches!(gone.item, Item::Value { .. });
337 if let Some(slot) = gone.slot().filter(|_| value)
338 && self.slots.get(&slot) == Some(&key)
339 {
340 self.slots.remove(&slot);
341 }
342 }
343
344 fn flush(&mut self, key: Hash) {
347 let Some(Held {
348 item:
349 Item::Node {
350 dirty: dirty @ true,
351 settled,
352 ..
353 },
354 ..
355 }) = self.entries.get_mut(&key)
356 else {
357 return;
358 };
359 *dirty = !*settled;
360 if let Some(back) = self.written(key) {
361 self.pending.push(back);
362 }
363 }
364
365 fn written(&self, key: Hash) -> Option<Writeback> {
366 let Item::Node {
367 source: Source::Offered { stored, offered },
368 ..
369 } = &self.entries.get(&key)?.item
370 else {
371 return None;
372 };
373 let head = |samples| Header::new((**stored).clone(), samples);
374 Some(match offered {
375 Offered::Moves { of, by } => {
376 let (of, by) = self.referred(*of, *by)?;
377 Writeback {
378 key,
379 head: head(Samples::Of { key: of, by }),
380 parts: Vec::new(),
381 }
382 }
383 _ => Writeback {
384 key,
385 head: head(Samples::None),
386 parts: self
387 .offered_parts(offered, Extent::EVERYWHERE)
388 .unwrap_or_default(),
389 },
390 })
391 }
392
393 fn referred(&self, of: Hash, by: i64) -> Option<(Hash, i64)> {
394 match &self.entries.get(&of)?.item {
395 Item::Node {
396 source: Source::Disk { head, .. },
397 ..
398 } => match head.samples() {
399 Samples::Entry { file, shift, .. } => Some((*file, by - shift)),
400 _ => None,
401 },
402 Item::Node {
403 source:
404 Source::Offered {
405 offered: Offered::Moves { of, by: more },
406 ..
407 },
408 ..
409 } => self.referred(*of, by + more),
410 Item::Node { .. } => Some((of, by)),
411 Item::Value { .. } => None,
412 }
413 }
414
415 fn shares(&self, keys: &[(i64, Hash)]) -> Option<Vec<(Arc<Buffer>, Extent)>> {
417 let mut out = Vec::new();
418 for (k, (start, key)) in keys.iter().enumerate() {
419 let end = keys.get(k + 1).map_or(i64::MAX, |(next, _)| *next);
420 let within = Extent::new(*start, end);
421 let parts = match &self.entries.get(key).map(|held| &held.item) {
422 Some(Item::Value {
423 payload: Payload::Segments(parts),
424 ..
425 }) => parts.clone(),
426 Some(Item::Value {
427 payload: Payload::Run(run),
428 ..
429 }) => vec![Arc::clone(&run.samples)],
430 _ => Vec::new(),
431 };
432 let met = |part: Arc<Buffer>| {
433 let met = part.extent().intersect(within);
434 (!met.is_empty()).then_some((part, met))
435 };
436 out.extend(parts.into_iter().filter_map(met));
437 }
438 let held = keys.iter().any(|(_, key)| self.entries.contains_key(key));
439 held.then_some(out)
440 }
441
442 fn offered_parts(&self, offered: &Offered, over: Extent) -> Option<Vec<Arc<Buffer>>> {
443 let (parts, by): (Vec<Arc<Buffer>>, i64) = match offered {
444 Offered::Values { keys, by } => {
445 let within = over.shifted(*by);
446 let met = self.shares(keys)?.into_iter();
447 let met = met.filter(|(_, held)| !held.intersect(within).is_empty());
448 (met.map(|(part, held)| clipped(&part, held)).collect(), *by)
449 }
450 Offered::Moves { of, by } => (self.resident(*of, over.shifted(*by))?.0, *by),
451 Offered::Held(parts) => (parts.clone(), 0),
452 };
453 let over = over.shifted(by);
454 let meets = |b: &&Arc<Buffer>| !b.extent().intersect(over).is_empty();
455 Some(parts.iter().filter(meets).map(|p| moved(p, -by)).collect())
456 }
457
458 fn resident(&self, key: Hash, over: Extent) -> Option<Resident> {
461 let Item::Node { source, .. } = &self.entries.get(&key)?.item else {
462 return None;
463 };
464 match source {
465 Source::Offered { offered, .. } => Some((self.offered_parts(offered, over)?, None)),
466 Source::Disk { head, chunks } => {
467 let meets = |b: &&Arc<Buffer>| !b.extent().intersect(over).is_empty();
468 let parts: Vec<Arc<Buffer>> = chunks.iter().filter(meets).cloned().collect();
469 let mut lacks = Vec::new();
470 for held in head.stored().extents() {
471 let mut asked = vec![held.intersect(over)];
472 for part in chunks {
473 asked = asked
474 .into_iter()
475 .flat_map(|a| minus(a, part.extent()))
476 .collect();
477 }
478 lacks.extend(asked.into_iter().filter(|a| !a.is_empty()));
479 }
480 let hull = lacks.iter().fold(Extent::NOWHERE, |h, e| h.hull(*e));
481 Some((parts, (!hull.is_empty()).then(|| ((**head).clone(), hull))))
482 }
483 }
484 }
485
486 fn coverage(&self, key: Hash, depth: usize) -> Option<Vec<Extent>> {
487 let Item::Node { source, .. } = &self.entries.get(&key)?.item else {
488 return None;
489 };
490 let offered = match source {
491 Source::Disk { head, .. } => return Some(head.stored().extents().to_vec()),
492 Source::Offered { offered, .. } => offered,
493 };
494 match offered {
495 Offered::Moves { of, by } if depth < 64 => {
496 let held = self.coverage(*of, depth + 1)?;
497 Some(held.into_iter().map(|e| e.shifted(-by)).collect())
498 }
499 Offered::Moves { .. } => None,
500 Offered::Values { keys, by } => {
501 let shares = self.shares(keys)?.into_iter();
502 Some(shares.map(|(_, held)| held.shifted(-by)).collect())
503 }
504 Offered::Held(parts) => Some(parts.iter().map(|p| p.extent()).collect()),
505 }
506 }
507
508 fn evict(&mut self, key: Hash) {
509 let Some(held) = self.entries.get_mut(&key) else {
510 return;
511 };
512 let protected = held.protected;
513 let gone = match &mut held.item {
514 Item::Node {
515 source: Source::Disk { chunks, .. },
516 ..
517 } => {
518 let freed: u64 = std::mem::take(chunks).iter().map(|c| planes(c)).sum();
519 self.bytes -= freed;
520 true
521 }
522 _ => self.remove(key),
523 };
524 match (gone, protected) {
525 (false, _) => {}
526 (true, false) => self.counters.probation_evictions += 1,
527 (true, true) => self.counters.protected_evictions += 1,
528 }
529 }
530
531 fn prune(&mut self, policy: PrunePolicy) {
532 let newest = self.tree;
533 let mut named: Vec<(u64, Hash)> = self
534 .entries
535 .iter()
536 .filter(|(_, held)| match policy {
537 PrunePolicy::Oldest => held.tree != newest,
538 PrunePolicy::Forks => !held.fork,
539 })
540 .map(|(key, held)| (held.read, *key))
541 .collect();
542 named.sort_unstable();
543 for (_, key) in named {
544 self.evict(key);
545 }
546 self.bounded();
547 }
548
549 fn hit(&mut self, key: Hash, round: Option<u64>) {
552 let tick = self.tick();
553 let mut promoted = false;
554 for at in self.under(key) {
555 let Some(held) = self.entries.get_mut(&at) else {
556 continue;
557 };
558 held.read = tick;
559 if round.is_some_and(|round| held.hit_round == round) {
560 continue;
561 }
562 held.hit_round = round.unwrap_or(0);
563 promoted |= !std::mem::replace(&mut held.protected, true);
564 self.counters.hits += 1;
565 }
566 if promoted {
567 self.shared();
568 }
569 }
570
571 fn under(&self, key: Hash) -> Vec<Hash> {
572 let mut out = vec![key];
573 let mut k = 0;
574 while k < out.len() {
575 if let Some(Item::Node {
576 source: Source::Offered { offered, .. },
577 ..
578 }) = self.entries.get(&out[k]).map(|held| &held.item)
579 {
580 let more: Vec<Hash> = match offered {
581 Offered::Values { keys, .. } => keys.iter().map(|(_, key)| *key).collect(),
582 Offered::Moves { of, .. } => vec![*of],
583 Offered::Held(_) => Vec::new(),
584 };
585 for at in more {
586 if !out.contains(&at) {
587 out.push(at);
588 }
589 }
590 }
591 k += 1;
592 }
593 out
594 }
595
596 fn shared(&mut self) {
598 let most =
599 (u128::from(self.max_bytes) * u128::from(PROTECTED.0) / u128::from(PROTECTED.1)) as u64;
600 let mut protected: Vec<(u64, Hash, u64)> = self
601 .entries
602 .iter()
603 .filter(|(_, held)| held.protected)
604 .map(|(key, held)| (held.read, *key, held.bytes()))
605 .collect();
606 let mut bytes: u64 = protected.iter().map(|(_, _, b)| b).sum();
607 protected.sort_unstable();
608 for (_, key, held) in protected {
609 if bytes <= most {
610 break;
611 }
612 let tick = self.tick();
613 if let Some(entry) = self.entries.get_mut(&key) {
614 entry.protected = false;
615 entry.since = tick;
616 bytes -= held;
617 }
618 }
619 }
620
621 fn bounded(&mut self) {
624 if self.bytes <= self.max_bytes {
625 return;
626 }
627 self.shared();
628 let max = self.max_bytes;
629 let mut order: Vec<(u8, u64, Hash)> = self
630 .entries
631 .iter()
632 .filter(|(_, held)| held.bytes() > 0)
633 .map(|(key, held)| match (held.bytes() > max, held.protected) {
634 (true, _) => (0, held.since, *key),
635 (false, false) => (1, held.since, *key),
636 (false, true) => (2, held.read, *key),
637 })
638 .collect();
639 order.sort_unstable();
640 for (_, _, key) in order {
641 if self.bytes <= self.max_bytes {
642 break;
643 }
644 self.evict(key);
645 }
646 }
647}
648
649type Resident = (Vec<Arc<Buffer>>, Option<(Header, Extent)>);
650
651fn minus(e: Extent, cut: Extent) -> Vec<Extent> {
652 let met = e.intersect(cut);
653 if met.is_empty() {
654 return vec![e];
655 }
656 vec![Extent::new(e.start, met.start), Extent::new(met.end, e.end)]
657}
658
659fn clipped(part: &Arc<Buffer>, met: Extent) -> Arc<Buffer> {
660 let held = part.extent();
661 match met == held {
662 true => Arc::clone(part),
663 false => Arc::new(part.over(met, held)),
664 }
665}
666
667fn moved(part: &Arc<Buffer>, by: i64) -> Arc<Buffer> {
668 if by == 0 {
669 return Arc::clone(part);
670 }
671 let mut out = (**part).clone();
672 out.start += by;
673 Arc::new(out)
674}
675
676#[derive(Clone)]
679pub(crate) struct Memory {
680 state: Arc<Mutex<State>>,
681}
682
683impl Default for Memory {
684 fn default() -> Memory {
685 Memory::holding(DEFAULT_CACHE_BYTES)
686 }
687}
688
689impl Memory {
690 pub(crate) fn holding(max_bytes: u64) -> Memory {
691 Memory {
692 state: Arc::new(Mutex::new(State {
693 max_bytes,
694 mark_every: DEFAULT_MARK_EVERY,
695 ..State::default()
696 })),
697 }
698 }
699
700 pub(crate) fn over_disk(max_bytes: u64) -> Memory {
702 let memory = Memory::holding(max_bytes);
703 memory.locked().disk = true;
704 memory
705 }
706
707 fn locked(&self) -> MutexGuard<'_, State> {
708 self.state.lock().unwrap_or_else(|poisoned| {
709 let mut state = poisoned.into_inner();
710 state.entries.clear();
711 state.slots.clear();
712 state.bytes = 0;
713 self.state.clear_poison();
714 state
715 })
716 }
717
718 pub(crate) fn max_bytes(&self) -> u64 {
719 self.locked().max_bytes
720 }
721
722 pub(crate) fn set_max_bytes(&self, max_bytes: u64) {
723 let mut state = self.locked();
724 state.max_bytes = max_bytes;
725 state.bounded();
726 }
727
728 pub(crate) fn policy(&self) -> CachePolicy {
729 self.locked().policy
730 }
731
732 pub(crate) fn set_policy(&self, policy: CachePolicy) {
733 self.locked().policy = policy;
734 }
735
736 pub(crate) fn prune(&self, policy: PrunePolicy) {
737 self.locked().prune(policy);
738 }
739
740 pub(crate) fn clear(&self) {
742 let mut state = self.locked();
743 let keys: Vec<Hash> = state.entries.keys().copied().collect();
744 for key in &keys {
745 state.flush(*key);
746 }
747 for key in keys {
748 state.discard(key);
749 }
750 state.misses.clear();
751 }
752
753 pub(crate) fn bytes(&self) -> u64 {
754 self.locked().bytes
755 }
756
757 pub(crate) fn entries(&self) -> usize {
758 self.locked().entries.len()
759 }
760
761 pub(crate) fn holds(&self, key: Hash) -> bool {
762 self.locked().entries.contains_key(&key)
763 }
764
765 pub(crate) fn counters(&self) -> Counters {
766 self.locked().counters
767 }
768
769 pub(crate) fn count(&self, by: impl FnOnce(&mut Counters)) {
770 by(&mut self.locked().counters);
771 }
772
773 pub(crate) fn mark_every(&self) -> usize {
774 self.locked().mark_every
775 }
776
777 pub(crate) fn set_mark_every(&self, samples: usize) {
778 self.locked().mark_every = samples.max(1);
779 }
780
781 pub(crate) fn keeps(&self, fork: bool, target: bool, offered: bool) -> bool {
785 let state = self.locked();
786 state.max_bytes > 0 && ((offered && state.disk) || state.policy.keeps(fork, target))
787 }
788
789 pub(crate) fn begin_tree(&self) -> u64 {
790 let mut state = self.locked();
791 state.tree += 1;
792 state.tree
793 }
794
795 pub(crate) fn begin(&self) -> u64 {
797 let mut state = self.locked();
798 state.round += 1;
799 let round = state.round;
800 state.misses.retain(|_, met| *met + 1 >= round);
801 round
802 }
803
804 pub(crate) fn answer(&self, key: Hash, round: u64) -> Known {
805 let mut state = self.locked();
806 let covered = state.coverage(key, 0);
807 if let Some(held) = covered.clone().filter(|held| !held.is_empty()) {
808 state.hit(key, Some(round));
809 let Some(Item::Node { source, .. }) = state.entries.get(&key).map(|held| &held.item)
810 else {
811 unreachable!("only a node covers");
812 };
813 return Known::Hit(Arc::new(source.stored().holding(held)));
814 }
815 let node = matches!(
816 state.entries.get(&key),
817 Some(Held {
818 item: Item::Node { .. },
819 ..
820 })
821 );
822 match (node, covered) {
823 (true, None) => {
824 state.remove(key);
825 }
826 (true, Some(_)) => return Known::Miss,
827 (false, _) => {}
828 }
829 match state.misses.get(&key) {
830 Some(met) if *met >= round => Known::Miss,
831 _ => Known::Unknown,
832 }
833 }
834
835 pub(crate) fn miss(&self, key: Hash, round: u64) {
836 let mut state = self.locked();
837 let met = state.misses.entry(key).or_insert(round);
838 *met = (*met).max(round);
839 }
840
841 pub(crate) fn promote(&self, head: Header) {
843 let mut state = self.locked();
844 let (key, read, tree) = (head.stored().key, state.tick(), state.tree);
845 if state.entries.contains_key(&key) {
846 return;
847 }
848 state.misses.remove(&key);
849 state.counters.promotions += 1;
850 let item = Item::Node {
851 source: Source::Disk {
852 head: Box::new(head),
853 chunks: Vec::new(),
854 },
855 slot: None,
856 dirty: false,
857 settled: true,
858 };
859 state
860 .entries
861 .insert(key, Held::admitted(item, read, tree, false));
862 }
863
864 pub(crate) fn promote_samples(&self, key: Hash, read: Vec<Buffer>) -> Vec<Arc<Buffer>> {
865 let read: Vec<Arc<Buffer>> = read.into_iter().map(Arc::new).collect();
866 let mut state = self.locked();
867 let tick = state.tick();
868 let Some(held) = state.entries.get_mut(&key) else {
869 return read;
870 };
871 let Item::Node {
872 source: Source::Disk { chunks, .. },
873 ..
874 } = &mut held.item
875 else {
876 return read;
877 };
878 let mut added = 0;
879 for part in &read {
880 let covered = chunks
881 .iter()
882 .any(|c| c.extent().intersect(part.extent()) == part.extent());
883 if !covered {
884 added += planes(part);
885 chunks.push(Arc::clone(part));
886 }
887 }
888 chunks.sort_by_key(|c| c.start);
889 held.read = tick;
890 state.bytes += added;
891 state.counters.promotions += 1;
892 state.bounded();
893 read
894 }
895
896 pub(crate) fn resident(&self, key: Hash, over: Extent) -> Resident {
898 let mut state = self.locked();
899 let tick = state.tick();
900 if let Some(held) = state.entries.get_mut(&key) {
901 held.read = tick;
902 }
903 state.resident(key, over).unwrap_or_default()
904 }
905
906 pub(crate) fn forget(&self, key: Hash) {
907 self.locked().remove(key);
908 }
909
910 pub(crate) fn offer(
915 &self,
916 stored: Stored,
917 offered: Offered,
918 (slot, settled): (Option<Hash>, bool),
919 ) {
920 let mut state = self.locked();
921 let key = stored.key;
922 let (read, tree) = (state.tick(), state.tree);
923 let earned = state.entries.get(&key);
924 let (since, protected) = earned.map_or((read, false), |held| (held.since, held.protected));
925 state.discard(key);
926 let slot = slot.map(|slot| super::mixed(slot, &[0x6e_6f_64_65]));
927 let item = Item::Node {
928 source: Source::Offered {
929 stored: Box::new(stored),
930 offered,
931 },
932 slot,
933 dirty: state.disk,
934 settled,
935 };
936 let held = Held {
937 since,
938 protected,
939 ..Held::admitted(item, read, tree, false)
940 };
941 state.bytes += held.bytes();
942 state.entries.insert(key, held);
943 state.misses.remove(&key);
944 let covered = state.coverage(key, 0).is_some_and(|held| !held.is_empty());
945 if settled && !covered && !state.disk {
946 state.discard(key);
947 return;
948 }
949 if let Some(last) = slot.and_then(|slot| state.slots.insert(slot, key))
950 && last != key
951 {
952 state.remove(last);
953 }
954 state.bounded();
955 }
956
957 pub(crate) fn flush(&self) {
959 let mut state = self.locked();
960 let keys: Vec<Hash> = state.entries.keys().copied().collect();
961 for key in keys {
962 state.flush(key);
963 }
964 state.failed.1 = None;
965 }
966
967 pub(crate) fn pending(&self) -> Vec<Writeback> {
969 let mut state = self.locked();
970 match state.failed.1 {
971 Some(_) => Vec::new(),
972 None => std::mem::take(&mut state.pending),
973 }
974 }
975
976 pub(crate) fn failed(&self, why: String, left: Vec<Writeback>) {
978 let mut state = self.locked();
979 state.failed.0 += 1;
980 state.failed.1 = Some(why);
981 let mut more = std::mem::take(&mut state.pending);
982 state.pending = left;
983 state.pending.append(&mut more);
984 }
985
986 pub(crate) fn written(&self) {
987 self.locked().counters.writebacks += 1;
988 }
989
990 pub(crate) fn failures(&self) -> (u64, Option<String>) {
991 self.locked().failed.clone()
992 }
993
994 pub(crate) fn blocked(&self) -> bool {
995 self.locked().failed.1.is_some()
996 }
997
998 pub(crate) fn committed(&self) {
1001 let mut state = self.locked();
1002 let staged: Vec<Hash> = state
1003 .entries
1004 .iter()
1005 .filter(|(_, held)| {
1006 let Item::Node {
1007 source: Source::Disk { head, .. },
1008 ..
1009 } = &held.item
1010 else {
1011 return false;
1012 };
1013 matches!(head.samples(), Samples::Staged { .. })
1014 })
1015 .map(|(key, _)| *key)
1016 .collect();
1017 for key in staged {
1018 state.discard(key);
1019 }
1020 }
1021
1022 pub(crate) fn load(&self, key: Hash, expected: Expected, stamp: Stamp) -> Option<Entry> {
1024 let mut state = self.locked();
1025 let held = state.entries.get_mut(&key)?;
1026 let Item::Value { payload, label, .. } = &held.item else {
1027 return None;
1028 };
1029 if !payload.answers(expected) {
1030 state.remove(key);
1031 return None;
1032 }
1033 let entry = Entry {
1034 payload: payload.clone(),
1035 label: label.clone(),
1036 };
1037 held.tree = stamp.tree;
1038 held.fork = stamp.fork;
1039 state.hit(key, None);
1040 Some(entry)
1041 }
1042
1043 pub(crate) fn merge(
1046 &self,
1047 key: Hash,
1048 payload: Payload,
1049 label: Option<&Label>,
1050 stamp: Stamp,
1051 ) -> Kept {
1052 let mut state = self.locked();
1053 let tick = state.tick();
1054 let joined = match (state.entries.get_mut(&key), payload) {
1055 (Some(held), payload) if held.slot() == stamp.slot => {
1056 let before = held.bytes();
1057 let Item::Value { payload: had, .. } = &mut held.item else {
1058 unreachable!("a value key holds a value");
1059 };
1060 let payload = match (had, payload) {
1061 (Payload::Segments(parts), Payload::Segments(more)) => {
1062 joined(parts, more);
1063 None
1064 }
1065 (Payload::Run(run), Payload::Run(more)) if overlaps(run, &more) => {
1066 let from = (run.end() - more.samples.start).max(0) as usize;
1067 let run = Arc::make_mut(run);
1068 let samples = Arc::make_mut(&mut run.samples);
1069 for (held, more) in samples.planes.iter_mut().zip(&more.samples.planes) {
1070 held.extend_from_slice(&more[from.min(more.len())..]);
1071 }
1072 run.marks
1073 .extend(more.marks.iter().map(|(at, m)| (*at, m.clone())));
1074 None
1075 }
1076 (_, payload) => Some(payload),
1077 };
1078 match payload {
1079 None => {
1080 held.read = tick;
1081 held.tree = stamp.tree;
1082 held.fork = stamp.fork;
1083 let after = held.bytes();
1084 Ok((before, after))
1085 }
1086 Some(payload) => Err(payload),
1087 }
1088 }
1089 (_, payload) => Err(payload),
1090 };
1091 match joined {
1092 Ok((before, after)) => {
1093 state.bytes = state.bytes - before + after;
1094 state.bounded();
1095 Kept::Held
1096 }
1097 Err(payload) => {
1098 drop(state);
1099 self.store(key, payload, label, stamp)
1100 }
1101 }
1102 }
1103
1104 pub(crate) fn store(
1107 &self,
1108 key: Hash,
1109 payload: Payload,
1110 label: Option<&Label>,
1111 stamp: Stamp,
1112 ) -> Kept {
1113 let mut state = self.locked();
1114 let bytes = payload.bytes() as u64;
1115 if bytes > state.max_bytes && !state.stands(key) {
1116 return Kept::Refused;
1117 }
1118 let read = state.tick();
1119 let replaced = match stamp.slot.and_then(|slot| state.slots.insert(slot, key)) {
1120 Some(last) if last != key => state.remove(last),
1121 _ => false,
1122 };
1123 let item = Item::Value {
1124 payload,
1125 label: label.cloned(),
1126 slot: stamp.slot,
1127 };
1128 let held = Held::admitted(item, read, stamp.tree, stamp.fork);
1129 if let Some(old) = state.entries.insert(key, held) {
1130 state.bytes -= old.bytes();
1131 }
1132 state.bytes += bytes;
1133 state.bounded();
1134 match replaced {
1135 true => Kept::Replaced,
1136 false => Kept::Held,
1137 }
1138 }
1139}
1140
1141fn overlaps(held: &super::Run, more: &super::Run) -> bool {
1144 let (a, b) = (held.samples.start, held.end());
1145 a <= more.samples.start && more.samples.start <= b
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150 use super::*;
1151
1152 #[test]
1154 fn an_entry_that_does_not_answer_what_was_asked_is_a_miss_and_goes() {
1155 let memory = Memory::default();
1156 let key = Hash(7, 11);
1157 let stamp = Stamp {
1158 tree: memory.begin_tree(),
1159 fork: false,
1160 slot: None,
1161 };
1162 let four = Payload::Segments(vec![Arc::new(Buffer::mono(8_000, vec![0.25; 4]))]);
1163 for (rate, width) in [(48_000, 1), (8_000, 2)] {
1164 memory.store(key, four.clone(), None, stamp);
1165 let asked = Expected::Segments { rate, width };
1166 assert!(memory.load(key, asked, stamp).is_none());
1167 assert!(!memory.holds(key));
1168 assert_eq!(memory.bytes(), 0);
1169 }
1170 }
1171
1172 #[test]
1173 fn a_load_shares_the_samples_it_holds() {
1174 let memory = Memory::default();
1175 let key = Hash(3, 5);
1176 let stamp = Stamp {
1177 tree: memory.begin_tree(),
1178 fork: false,
1179 slot: None,
1180 };
1181 let part = Arc::new(Buffer::mono(8_000, vec![0.5; 64]));
1182 memory.store(key, Payload::Segments(vec![Arc::clone(&part)]), None, stamp);
1183 let asked = Expected::Segments {
1184 rate: 8_000,
1185 width: 1,
1186 };
1187 for _ in 0..2 {
1188 let loaded = memory.load(key, asked, stamp).expect("a hit");
1189 let Payload::Segments(parts) = loaded.payload else {
1190 panic!("segments were stored");
1191 };
1192 assert!(Arc::ptr_eq(&parts[0], &part), "the stored part itself");
1193 }
1194 }
1195}