1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Bounded decoded rANS model cache (ADR-0014).
//!
//! Model objects are immutable and content-addressed, so a decoded model
//! is a pure memo of its bytes. Decoding a model is comparatively
//! expensive (cumulative table build), so this cache is a real hot-path
//! win for repeated reads. Never authoritative.
//!
//! # PURPOSE
//!
//! A bounded LRU cache of decoded rANS models, keyed by the model
//! object's [`ChunkId`]. Reading an extent whose descriptor references a
//! model pays the decode once and then hits here.
//!
//! # BOUNDARY
//!
//! Knows only `(ChunkId → RansModel)`; no descriptors, no store, no
//! format. It must never participate in correctness: dropping every entry
//! changes only latency (`docs/security/resource-bounds.md` §3 gives the
//! models cache a 32 MiB budget).
//!
//! # MODEL
//!
//! A pure memo: model objects are immutable and content-addressed, so the
//! same id always decodes to the same model — memoization is sound by
//! construction. Each entry carries a monotonically increasing recency
//! clock tick; insertion beyond `capacity` evicts the least-recent entry
//! (an LRU-ish policy, ADR-0014: "eviction is LRU-ish and never affects
//! correctness"). [`ModelCache::get`] returns a *clone*, so callers can
//! never corrupt cached state.
//!
//! # PERSISTENT AUTHORITY
//!
//! None. The model bytes live in the store; the cache is rebuilt on
//! demand. This is the ADR-0014 contract: dropping every cache must leave
//! the filesystem fully correct.
//!
//! # CORRECTNESS INVARIANTS
//!
//! - memoization is sound exactly because models are immutable and
//! content-addressed: same id ⇒ same bytes ⇒ same decoded model;
//! - a miss returns `None`, which the caller treats as "decode and
//! insert" — never as an error;
//! - eviction is performance-only; a re-decode repairs the entry.
//!
//! # CONCURRENCY
//!
//! `&mut self` on every operation: the caller serializes access (the
//! cache has no internal locking and is not `Sync`).
//!
//! # RESOURCE BOUNDS
//!
//! `capacity` bounds the entry count; the store sizes it against the
//! models memory budget. Eviction scans all entries for the minimum
//! recency tick — `O(n)` per insert, acceptable because the budget caps
//! `n`.
//!
//! # PERFORMANCE
//!
//! Model decode is comparatively expensive (cumulative table build), so
//! repeated reads that share a model object win here. Sharing is common
//! after Phase-9G: one amortized cohort model object is referenced by N
//! extents (ADR-0005), so this cache is where that sharing pays off on
//! the read path.
//!
//! # FAILURE MODES
//!
//! No fallible paths. `get` → `None` on miss; evicting a still-wanted
//! model costs exactly one re-decode.
//!
//! # HISTORY / EVIDENCE
//!
//! ADR-0014 (caches are performance-only, never authoritative);
//! `docs/security/resource-bounds.md` §3 (the budget); Phase-9G model
//! amortization (shared model objects across extents).
use HashMap;
use crateChunkId;
use crateRansModel;
/// A bounded LRU cache of decoded models.
///
/// `entries: ChunkId → (RansModel, last-touch clock tick)`; `clock` is a
/// monotonically increasing counter that never resets while the cache
/// lives. Insertion evicts the entry with the smallest tick when over
/// `capacity` — an `O(n)` victim scan, fine for the small bounded budgets
/// this cache is sized against.