1use crate::{
2 key::{StableKey, StableKeyError},
3 schema::SchemaMetadata,
4 slot::{MemoryManagerSlot, MemoryManagerSlotError},
5};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[serde(deny_unknown_fields)]
21pub struct AllocationDeclaration {
22 pub(crate) stable_key: StableKey,
24 pub(crate) slot: MemoryManagerSlot,
26 pub(crate) schema: SchemaMetadata,
28}
29
30impl AllocationDeclaration {
31 pub fn new(
33 stable_key: impl AsRef<str>,
34 slot: MemoryManagerSlot,
35 schema: SchemaMetadata,
36 ) -> Result<Self, DeclarationSnapshotError> {
37 let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
38 Ok(Self {
39 stable_key,
40 slot,
41 schema,
42 })
43 }
44
45 pub fn memory_manager(
47 stable_key: impl AsRef<str>,
48 id: u8,
49 ) -> Result<Self, DeclarationSnapshotError> {
50 Self::memory_manager_with_schema(stable_key, id, SchemaMetadata::default())
51 }
52
53 pub fn memory_manager_with_schema(
55 stable_key: impl AsRef<str>,
56 id: u8,
57 schema: SchemaMetadata,
58 ) -> Result<Self, DeclarationSnapshotError> {
59 let slot =
60 MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
61 Self::new(stable_key, slot, schema)
62 }
63
64 #[must_use]
66 pub const fn stable_key(&self) -> &StableKey {
67 &self.stable_key
68 }
69
70 #[must_use]
72 pub const fn slot(&self) -> &MemoryManagerSlot {
73 &self.slot
74 }
75
76 #[must_use]
78 pub const fn schema(&self) -> &SchemaMetadata {
79 &self.schema
80 }
81}
82
83#[derive(Debug, Eq, PartialEq)]
93pub struct DeclarationSnapshot {
94 declarations: Vec<AllocationDeclaration>,
96}
97
98impl DeclarationSnapshot {
99 pub(crate) fn canonicalize(
100 mut declarations: Vec<AllocationDeclaration>,
101 ) -> Result<Self, DeclarationSnapshotError> {
102 if declarations.len() > crate::constants::MAX_ALLOCATIONS {
103 return Err(DeclarationSnapshotError::TooManyDeclarations);
104 }
105 crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key.cmp(&b.stable_key));
106 let mut slots = [false; crate::constants::MAX_ALLOCATIONS];
107 for (index, declaration) in declarations.iter().enumerate() {
108 let occupied = &mut slots[usize::from(declaration.slot.id())];
109 if *occupied {
110 return Err(DeclarationSnapshotError::DuplicateSlot(
111 declaration.slot.clone(),
112 ));
113 }
114 *occupied = true;
115 if index > 0 && declarations[index - 1].stable_key == declaration.stable_key {
116 return Err(DeclarationSnapshotError::DuplicateStableKey(
117 declaration.stable_key.clone(),
118 ));
119 }
120 }
121 Ok(Self { declarations })
122 }
123
124 #[must_use]
126 pub fn declarations(&self) -> &[AllocationDeclaration] {
127 &self.declarations
128 }
129
130 pub(crate) fn into_declarations(self) -> Vec<AllocationDeclaration> {
131 self.declarations
132 }
133}
134
135#[non_exhaustive]
140#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
141pub enum DeclarationSnapshotError {
142 #[error("at most 255 allocation declarations are supported")]
143 TooManyDeclarations,
144 #[error(transparent)]
146 Key(StableKeyError),
147 #[error(transparent)]
149 MemoryManagerSlot(MemoryManagerSlotError),
150 #[error("stable key '{0}' is declared more than once")]
152 DuplicateStableKey(StableKey),
153 #[error("allocation slot '{0:?}' is declared more than once")]
155 DuplicateSlot(MemoryManagerSlot),
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::slot::MemoryManagerSlot;
162
163 fn declaration(key: &str, id: u8) -> AllocationDeclaration {
164 AllocationDeclaration::new(
165 key,
166 MemoryManagerSlot::new(id).expect("usable slot"),
167 SchemaMetadata::default(),
168 )
169 .expect("declaration")
170 }
171
172 #[test]
173 fn memory_manager_declaration_constructor_builds_common_declaration() {
174 let declaration =
175 AllocationDeclaration::memory_manager("app.orders.v1", 100).expect("declaration");
176
177 assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
178 assert_eq!(
179 declaration.slot,
180 MemoryManagerSlot::new(100).expect("usable slot")
181 );
182 assert_eq!(declaration.schema, SchemaMetadata::default());
183 }
184
185 #[test]
186 fn memory_manager_declaration_constructor_rejects_invalid_slot() {
187 let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX)
188 .expect_err("sentinel must fail");
189
190 assert!(matches!(
191 err,
192 DeclarationSnapshotError::MemoryManagerSlot(_)
193 ));
194 }
195
196 #[test]
197 fn declaration_decode_rejects_unusable_memory_manager_slot() {
198 let mut value = serde_json::to_value(declaration("app.orders.v1", 100)).unwrap();
199 value["slot"] = serde_json::json!(255);
200 assert!(serde_json::from_value::<AllocationDeclaration>(value.clone()).is_err());
201 let bytes = crate::test_cbor::to_vec(&value).unwrap();
202 assert!(crate::test_cbor::from_slice::<AllocationDeclaration>(&bytes).is_err());
203 }
204
205 #[test]
206 fn rejects_duplicate_keys() {
207 let err = DeclarationSnapshot::canonicalize(vec![
208 declaration("app.users.v1", 100),
209 declaration("app.users.v1", 101),
210 ])
211 .expect_err("duplicate key");
212
213 assert_eq!(
214 err,
215 DeclarationSnapshotError::DuplicateStableKey(StableKey::parse("app.users.v1").unwrap())
216 );
217 }
218
219 #[test]
220 fn rejects_duplicate_slots() {
221 for second_key in ["app.orders.v1", "app.users.v1"] {
222 let err = DeclarationSnapshot::canonicalize(vec![
223 declaration("app.users.v1", 100),
224 declaration(second_key, 100),
225 ])
226 .expect_err("duplicate slot precedes duplicate key");
227
228 assert_eq!(
229 err,
230 DeclarationSnapshotError::DuplicateSlot(MemoryManagerSlot::new(100).unwrap())
231 );
232 }
233 }
234
235 #[test]
236 fn canonical_resolution_preserves_identity_failures_and_bounds() {
237 let choices = [
240 ("app.a.v1", 10),
241 ("app.a.v1", 11),
242 ("app.b.v1", 10),
243 ("app.b.v1", 11),
244 ];
245 for code in 0..256 {
246 let mut code = code;
247 let input: Vec<_> = (0..4)
248 .map(|_| {
249 let (key, id) = choices[code % choices.len()];
250 code /= choices.len();
251 declaration(key, id)
252 })
253 .collect();
254 let mut ordered = input.clone();
255 crate::sort::sort_unstable_by(&mut ordered, |a, b| a.stable_key.cmp(&b.stable_key));
256 let expected = ordered.iter().enumerate().find_map(|(index, declaration)| {
257 let prior = &ordered[..index];
258 if prior.iter().any(|row| row.slot == declaration.slot) {
259 Some(DeclarationSnapshotError::DuplicateSlot(
260 declaration.slot.clone(),
261 ))
262 } else if prior
263 .iter()
264 .any(|row| row.stable_key == declaration.stable_key)
265 {
266 Some(DeclarationSnapshotError::DuplicateStableKey(
267 declaration.stable_key.clone(),
268 ))
269 } else {
270 None
271 }
272 });
273 assert_eq!(
274 DeclarationSnapshot::canonicalize(input).unwrap_err(),
275 expected.unwrap()
276 );
277 }
278 for count in [0, 1, 254, 255, 256] {
279 let input: Vec<_> = (0..count)
280 .rev()
281 .map(|index| {
282 declaration(
283 &format!("app.store{index:03}.v1"),
284 u8::try_from(index % 255).unwrap(),
285 )
286 })
287 .collect();
288 let result = DeclarationSnapshot::canonicalize(input);
289 if count <= 255 {
290 let snapshot = result.unwrap();
291 assert_eq!(snapshot.declarations().len(), count);
292 assert!(
293 snapshot
294 .declarations()
295 .windows(2)
296 .all(|pair| { pair[0].stable_key < pair[1].stable_key })
297 );
298 } else {
299 assert_eq!(result, Err(DeclarationSnapshotError::TooManyDeclarations));
300 }
301 }
302 }
303}