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