1use crate::{
2 key::{StableKey, StableKeyError},
3 schema::SchemaMetadata,
4 slot::{MemoryManagerSlot, MemoryManagerSlotError},
5 text::{DiagnosticTextError, validate_diagnostic_text},
6};
7use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
8
9#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21#[serde(deny_unknown_fields)]
22pub struct AllocationDeclaration {
23 pub(crate) stable_key: StableKey,
25 pub(crate) slot: MemoryManagerSlot,
27 #[serde(deserialize_with = "deserialize_label")]
29 pub(crate) label: Option<String>,
30 pub(crate) schema: SchemaMetadata,
32}
33
34impl AllocationDeclaration {
35 pub fn new(
37 stable_key: impl AsRef<str>,
38 slot: MemoryManagerSlot,
39 label: Option<String>,
40 schema: SchemaMetadata,
41 ) -> Result<Self, DeclarationSnapshotError> {
42 let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
43 validate_label(label.as_deref())?;
44 Ok(Self {
45 stable_key,
46 slot,
47 label,
48 schema,
49 })
50 }
51
52 pub fn memory_manager(
54 stable_key: impl AsRef<str>,
55 id: u8,
56 label: impl Into<String>,
57 ) -> Result<Self, DeclarationSnapshotError> {
58 Self::memory_manager_with_schema(stable_key, id, label, SchemaMetadata::default())
59 }
60
61 pub fn memory_manager_unlabeled(
63 stable_key: impl AsRef<str>,
64 id: u8,
65 ) -> Result<Self, DeclarationSnapshotError> {
66 Self::memory_manager_unlabeled_with_schema(stable_key, id, SchemaMetadata::default())
67 }
68
69 pub fn memory_manager_with_schema(
71 stable_key: impl AsRef<str>,
72 id: u8,
73 label: impl Into<String>,
74 schema: SchemaMetadata,
75 ) -> Result<Self, DeclarationSnapshotError> {
76 let slot =
77 MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
78 Self::new(stable_key, slot, Some(label.into()), schema)
79 }
80
81 pub fn memory_manager_unlabeled_with_schema(
83 stable_key: impl AsRef<str>,
84 id: u8,
85 schema: SchemaMetadata,
86 ) -> Result<Self, DeclarationSnapshotError> {
87 let slot =
88 MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
89 Self::new(stable_key, slot, None, schema)
90 }
91
92 #[must_use]
94 pub const fn stable_key(&self) -> &StableKey {
95 &self.stable_key
96 }
97
98 #[must_use]
100 pub const fn slot(&self) -> &MemoryManagerSlot {
101 &self.slot
102 }
103
104 #[must_use]
106 pub fn label(&self) -> Option<&str> {
107 self.label.as_deref()
108 }
109
110 #[must_use]
112 pub const fn schema(&self) -> &SchemaMetadata {
113 &self.schema
114 }
115}
116
117#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
129pub struct DeclarationSnapshot {
130 declarations: Vec<AllocationDeclaration>,
132}
133
134impl<'de> Deserialize<'de> for DeclarationSnapshot {
135 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
136 #[derive(Deserialize)]
137 #[serde(rename = "DeclarationSnapshot", deny_unknown_fields)]
138 struct Snapshot {
139 #[serde(deserialize_with = "crate::cbor::deserialize_records")]
140 declarations: Vec<AllocationDeclaration>,
141 }
142
143 let snapshot = Snapshot::deserialize(deserializer)?;
144 Self::new(snapshot.declarations).map_err(D::Error::custom)
145 }
146}
147
148impl DeclarationSnapshot {
149 pub fn new(declarations: Vec<AllocationDeclaration>) -> Result<Self, DeclarationSnapshotError> {
151 validate_declaration_set(&declarations)?;
152 Ok(Self { declarations })
153 }
154
155 pub(crate) fn canonicalize(
158 mut declarations: Vec<AllocationDeclaration>,
159 ) -> Result<Self, DeclarationSnapshotError> {
160 if declarations.len() > crate::constants::MAX_ALLOCATIONS {
161 return Err(DeclarationSnapshotError::TooManyDeclarations);
162 }
163 crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key.cmp(&b.stable_key));
164 let mut duplicate_keys = [false; crate::constants::MAX_ALLOCATIONS];
165 for (index, pair) in declarations.windows(2).enumerate() {
166 duplicate_keys[index + 1] = pair[0].stable_key == pair[1].stable_key;
167 }
168 validate_declaration_identities(&declarations, &duplicate_keys)?;
169 Ok(Self { declarations })
170 }
171
172 #[must_use]
174 pub const fn is_empty(&self) -> bool {
175 self.declarations.is_empty()
176 }
177
178 #[must_use]
180 pub const fn len(&self) -> usize {
181 self.declarations.len()
182 }
183
184 #[must_use]
186 pub fn declarations(&self) -> &[AllocationDeclaration] {
187 &self.declarations
188 }
189
190 pub(crate) fn into_declarations(self) -> Vec<AllocationDeclaration> {
191 self.declarations
192 }
193}
194
195#[non_exhaustive]
200#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
201pub enum DeclarationSnapshotError {
202 #[error("at most 255 allocation declarations are supported")]
203 TooManyDeclarations,
204 #[error(transparent)]
206 Key(StableKeyError),
207 #[error(transparent)]
209 MemoryManagerSlot(MemoryManagerSlotError),
210 #[error("stable key '{0}' is declared more than once")]
212 DuplicateStableKey(StableKey),
213 #[error("allocation slot '{0:?}' is declared more than once")]
215 DuplicateSlot(MemoryManagerSlot),
216 #[error("allocation declaration label must not be empty when present")]
218 EmptyLabel,
219 #[error("allocation declaration label must be at most 256 bytes")]
221 LabelTooLong,
222 #[error("allocation declaration label must be ASCII")]
224 NonAsciiLabel,
225 #[error("allocation declaration label must not contain ASCII control characters")]
227 ControlCharacterLabel,
228}
229
230fn validate_label(label: Option<&str>) -> Result<(), DeclarationSnapshotError> {
231 let Some(label) = label else {
232 return Ok(());
233 };
234 validate_diagnostic_text(label).map_err(|error| match error {
235 DiagnosticTextError::Empty => DeclarationSnapshotError::EmptyLabel,
236 DiagnosticTextError::TooLong => DeclarationSnapshotError::LabelTooLong,
237 DiagnosticTextError::NonAscii => DeclarationSnapshotError::NonAsciiLabel,
238 DiagnosticTextError::ControlCharacter => DeclarationSnapshotError::ControlCharacterLabel,
239 })
240}
241
242fn deserialize_label<'de, D: Deserializer<'de>>(
245 deserializer: D,
246) -> Result<Option<String>, D::Error> {
247 let label = Option::<String>::deserialize(deserializer)?;
248 validate_label(label.as_deref()).map_err(D::Error::custom)?;
249 Ok(label)
250}
251
252fn validate_declaration_set(
253 declarations: &[AllocationDeclaration],
254) -> Result<(), DeclarationSnapshotError> {
255 if declarations.len() > crate::constants::MAX_ALLOCATIONS {
256 return Err(DeclarationSnapshotError::TooManyDeclarations);
257 }
258 let duplicate_keys = crate::key::duplicate_key_positions(
259 declarations
260 .iter()
261 .enumerate()
262 .map(|(index, declaration)| (&declaration.stable_key, index))
263 .collect(),
264 );
265 validate_declaration_identities(declarations, &duplicate_keys)
266}
267
268fn validate_declaration_identities(
269 declarations: &[AllocationDeclaration],
270 duplicate_keys: &[bool; crate::constants::MAX_ALLOCATIONS],
271) -> Result<(), DeclarationSnapshotError> {
272 let mut slots = [false; crate::constants::MAX_ALLOCATIONS];
273
274 for (index, declaration) in declarations.iter().enumerate() {
275 let occupied = &mut slots[usize::from(declaration.slot.id())];
276 if *occupied {
277 return Err(DeclarationSnapshotError::DuplicateSlot(
278 declaration.slot.clone(),
279 ));
280 }
281 *occupied = true;
282 if duplicate_keys[index] {
283 return Err(DeclarationSnapshotError::DuplicateStableKey(
284 declaration.stable_key.clone(),
285 ));
286 }
287 }
288
289 Ok(())
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use crate::slot::MemoryManagerSlot;
296
297 fn declaration(key: &str, id: u8) -> AllocationDeclaration {
298 AllocationDeclaration::new(
299 key,
300 MemoryManagerSlot::new(id).expect("usable slot"),
301 None,
302 SchemaMetadata::default(),
303 )
304 .expect("declaration")
305 }
306
307 #[test]
308 fn declaration_rejects_unbounded_label_metadata() {
309 let err = AllocationDeclaration::new(
310 "app.users.v1",
311 MemoryManagerSlot::new(100).expect("usable slot"),
312 Some("x".repeat(257)),
313 SchemaMetadata::default(),
314 )
315 .expect_err("label too long");
316
317 assert_eq!(err, DeclarationSnapshotError::LabelTooLong);
318 }
319
320 #[test]
321 fn memory_manager_declaration_constructor_builds_common_declaration() {
322 let declaration = AllocationDeclaration::memory_manager("app.orders.v1", 100, "orders")
323 .expect("declaration");
324
325 assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
326 assert_eq!(
327 declaration.slot,
328 MemoryManagerSlot::new(100).expect("usable slot")
329 );
330 assert_eq!(declaration.label.as_deref(), Some("orders"));
331 assert_eq!(declaration.schema, SchemaMetadata::default());
332 }
333
334 #[test]
335 fn memory_manager_declaration_constructor_rejects_invalid_slot() {
336 let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX, "orders")
337 .expect_err("sentinel must fail");
338
339 assert!(matches!(
340 err,
341 DeclarationSnapshotError::MemoryManagerSlot(_)
342 ));
343 }
344
345 #[test]
346 fn snapshot_decode_rejects_unusable_memory_manager_slot() {
347 let snapshot = DeclarationSnapshot::new(vec![declaration("app.orders.v1", 100)]).unwrap();
348 let mut value = serde_json::to_value(snapshot).unwrap();
349 value["declarations"][0]["slot"]["slot"]["MemoryManagerId"] = serde_json::json!(255);
350 assert!(serde_json::from_value::<DeclarationSnapshot>(value).is_err());
351 }
352
353 #[test]
354 fn rejects_duplicate_keys() {
355 let err = DeclarationSnapshot::new(vec![
356 declaration("app.users.v1", 100),
357 declaration("app.users.v1", 101),
358 ])
359 .expect_err("duplicate key");
360
361 assert_eq!(
362 err,
363 DeclarationSnapshotError::DuplicateStableKey(StableKey::parse("app.users.v1").unwrap())
364 );
365 }
366
367 #[test]
368 fn rejects_duplicate_slots() {
369 for second_key in ["app.orders.v1", "app.users.v1"] {
370 let err = DeclarationSnapshot::new(vec![
371 declaration("app.users.v1", 100),
372 declaration(second_key, 100),
373 ])
374 .expect_err("duplicate slot precedes duplicate key");
375
376 assert_eq!(
377 err,
378 DeclarationSnapshotError::DuplicateSlot(MemoryManagerSlot::new(100).unwrap())
379 );
380 }
381 }
382
383 #[test]
384 fn canonical_resolution_preserves_identity_failures_and_bounds() {
385 let choices = [
388 ("app.a.v1", 10),
389 ("app.a.v1", 11),
390 ("app.b.v1", 10),
391 ("app.b.v1", 11),
392 ];
393 for code in 0..256 {
394 let mut code = code;
395 let input: Vec<_> = (0..4)
396 .map(|_| {
397 let (key, id) = choices[code % choices.len()];
398 code /= choices.len();
399 declaration(key, id)
400 })
401 .collect();
402 let mut ordered = input.clone();
403 crate::sort::sort_unstable_by(&mut ordered, |a, b| a.stable_key.cmp(&b.stable_key));
404 assert_eq!(
405 DeclarationSnapshot::canonicalize(input),
406 DeclarationSnapshot::new(ordered)
407 );
408 }
409 for count in [0, 1, 254, 255, 256] {
410 let input: Vec<_> = (0..count)
411 .rev()
412 .map(|index| {
413 declaration(
414 &format!("app.store{index:03}.v1"),
415 u8::try_from(index % 255).unwrap(),
416 )
417 })
418 .collect();
419 let result = DeclarationSnapshot::canonicalize(input);
420 if count <= 255 {
421 let snapshot = result.unwrap();
422 assert_eq!(snapshot.len(), count);
423 assert!(
424 snapshot
425 .declarations()
426 .windows(2)
427 .all(|pair| { pair[0].stable_key < pair[1].stable_key })
428 );
429 } else {
430 assert_eq!(result, Err(DeclarationSnapshotError::TooManyDeclarations));
431 }
432 }
433 }
434}