1use crate::{
2 declaration::{AllocationDeclaration, DeclarationSnapshot},
3 schema::SchemaMetadata,
4 slot::{
5 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_LABEL, IC_MEMORY_LEDGER_STABLE_KEY,
6 MEMORY_MANAGER_LEDGER_ID, is_ic_memory_stable_key,
7 },
8 text::validate_diagnostic_text,
9};
10use serde::{Deserialize, Serialize};
11use std::{borrow::Cow, sync::Arc};
12
13#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
21pub struct MemoryRequest {
22 authority: String,
23 stable_key: crate::StableKey,
24 schema: SchemaMetadata,
25}
26
27impl<'de> Deserialize<'de> for MemoryRequest {
28 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
29 use serde::de::Error as _;
30 #[derive(Deserialize)]
31 #[serde(deny_unknown_fields)]
32 struct Request {
33 authority: String,
34 stable_key: crate::StableKey,
35 schema: SchemaMetadata,
36 }
37 let request = Request::deserialize(deserializer)?;
38 validate_external_authority(&request.authority).map_err(D::Error::custom)?;
39 Self::from_checked_key(request.authority, request.stable_key, request.schema)
40 .map_err(D::Error::custom)
41 }
42}
43
44impl MemoryRequest {
45 pub fn new(
47 authority: impl Into<String>,
48 stable_key: &str,
49 schema: SchemaMetadata,
50 ) -> Result<Self, MemoryDeclarationError> {
51 let authority = authority.into();
52 validate_external_authority(&authority)?;
53 let stable_key =
54 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
55 Self::from_checked_key(authority, stable_key, schema)
56 }
57
58 fn from_checked_key(
61 authority: String,
62 stable_key: crate::StableKey,
63 schema: SchemaMetadata,
64 ) -> Result<Self, MemoryDeclarationError> {
65 if is_ic_memory_stable_key(stable_key.as_str()) {
66 return Err(MemoryDeclarationError::ReservedStableKey {
67 stable_key: stable_key.as_str().to_string(),
68 });
69 }
70 Ok(Self {
71 authority,
72 stable_key,
73 schema,
74 })
75 }
76
77 pub(crate) const fn with_schema(mut self, schema: SchemaMetadata) -> Self {
79 self.schema = schema;
80 self
81 }
82
83 #[must_use]
85 pub const fn stable_key(&self) -> &crate::StableKey {
86 &self.stable_key
87 }
88
89 #[must_use]
91 pub const fn schema(&self) -> &SchemaMetadata {
92 &self.schema
93 }
94
95 #[must_use]
97 pub fn authority(&self) -> &str {
98 &self.authority
99 }
100}
101
102#[non_exhaustive]
109#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
110pub enum MemoryDeclarationError {
111 #[error("at most 254 external requests are supported")]
112 TooManyDeclarations,
113 #[error("duplicate requested stable key {stable_key}")]
114 DuplicateRequest { stable_key: crate::StableKey },
115 #[error(transparent)]
117 Declaration(#[from] crate::DeclarationSnapshotError),
118 #[error("authority {reason}")]
120 InvalidAuthority {
121 reason: &'static str,
123 },
124 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
126 ReservedAuthority {
127 authority: String,
129 },
130 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
132 ReservedStableKey {
133 stable_key: String,
135 },
136}
137
138#[derive(Clone, Debug, Eq, PartialEq)]
150pub struct SealedDeclarationSnapshot {
151 inner: Arc<SealedDeclarationSnapshotInner>,
152}
153
154#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
166#[serde(deny_unknown_fields)]
167pub struct SealedDeclarationFingerprint {
168 algorithm_version: u8,
169 value: u64,
170}
171
172impl SealedDeclarationFingerprint {
173 #[must_use]
175 pub const fn algorithm_version(&self) -> u8 {
176 self.algorithm_version
177 }
178
179 #[must_use]
181 pub const fn value(&self) -> u64 {
182 self.value
183 }
184}
185
186#[derive(Debug, Eq, PartialEq)]
187struct SealedDeclarationSnapshotInner {
188 requests: Vec<MemoryRequest>,
189 fingerprint: SealedDeclarationFingerprint,
190}
191
192impl SealedDeclarationSnapshot {
193 pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, MemoryDeclarationError> {
196 if requests.len() > 254 {
197 return Err(MemoryDeclarationError::TooManyDeclarations);
198 }
199 crate::sort::sort_unstable_by(&mut requests, |a, b| a.stable_key.cmp(&b.stable_key));
200 for pair in requests.windows(2) {
201 if pair[0].stable_key == pair[1].stable_key {
202 return Err(MemoryDeclarationError::DuplicateRequest {
203 stable_key: pair[1].stable_key.clone(),
204 });
205 }
206 }
207 let fingerprint = sealed_declaration_fingerprint(&requests);
208 Ok(Self {
209 inner: Arc::new(SealedDeclarationSnapshotInner {
210 requests,
211 fingerprint,
212 }),
213 })
214 }
215
216 #[must_use]
218 pub fn requests(&self) -> &[MemoryRequest] {
219 &self.inner.requests
220 }
221
222 pub(crate) fn resolve(
223 &self,
224 ledger: &crate::AllocationLedger,
225 historical: Vec<MemoryRequest>,
226 pool: &crate::MemoryAllocationPool,
227 ) -> Result<DeclarationSnapshot, crate::MemoryResolutionError> {
228 if self.requests().len() + historical.len() > 254 {
229 return Err(MemoryDeclarationError::TooManyDeclarations.into());
230 }
231 let mut declarations = Vec::with_capacity(self.requests().len() + historical.len() + 1);
232 declarations.push(internal_ledger_declaration());
233 let mut occupied = [false; 255];
234 for record in ledger.records() {
235 occupied[usize::from(record.slot().id())] = true;
236 }
237 let mut available_ids = 0..crate::MEMORY_MANAGER_INVALID_ID;
240 for request in self
245 .requests()
246 .iter()
247 .map(Cow::Borrowed)
248 .chain(historical.into_iter().map(Cow::Owned))
249 {
250 let historical = ledger
251 .records()
252 .iter()
253 .find(|record| record.stable_key() == &request.stable_key);
254 pool.validate_authority(&request.stable_key, &request.authority)?;
255 let id = if let Some(record) = historical {
256 pool.validate_id(record.slot().id())?;
259 record.slot().id()
260 } else {
261 available_ids
262 .find(|id| pool.contains(*id) && !occupied[usize::from(*id)])
263 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
264 stable_key: request.stable_key.clone(),
265 authority: request.authority.clone(),
266 })?
267 };
268 let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
269 let declaration = match request {
274 Cow::Borrowed(request) => AllocationDeclaration {
275 stable_key: request.stable_key.clone(),
276 slot,
277 label: None,
278 schema: request.schema.clone(),
279 },
280 Cow::Owned(request) => AllocationDeclaration {
281 stable_key: request.stable_key,
282 slot,
283 label: None,
284 schema: request.schema,
285 },
286 };
287 declarations.push(declaration);
288 }
289 crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key().cmp(b.stable_key()));
290 Ok(DeclarationSnapshot::new(declarations).map_err(MemoryDeclarationError::from)?)
291 }
292
293 #[must_use]
295 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
296 self.inner.fingerprint
297 }
298
299 #[cfg(test)]
300 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
301 Arc::ptr_eq(&self.inner, &other.inner)
302 }
303}
304
305fn validate_external_authority(value: &str) -> Result<(), MemoryDeclarationError> {
306 reject_internal_authority(value)?;
307 validate_diagnostic_text(value).map_err(|error| MemoryDeclarationError::InvalidAuthority {
308 reason: error.reason(),
309 })
310}
311
312fn reject_internal_authority(value: &str) -> Result<(), MemoryDeclarationError> {
313 if value == IC_MEMORY_AUTHORITY_OWNER {
314 return Err(MemoryDeclarationError::ReservedAuthority {
315 authority: value.to_string(),
316 });
317 }
318 Ok(())
319}
320
321#[derive(Serialize)]
322struct SealedDeclarationFingerprintMaterial<'a> {
323 format: &'static str,
324 requests: &'a [MemoryRequest],
325}
326
327struct FingerprintWriter(u64);
330
331impl std::io::Write for FingerprintWriter {
332 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
333 self.0 = crate::hash::fnv64(self.0, bytes);
334 Ok(bytes.len())
335 }
336
337 fn flush(&mut self) -> std::io::Result<()> {
338 Ok(())
339 }
340}
341
342fn sealed_declaration_fingerprint(requests: &[MemoryRequest]) -> SealedDeclarationFingerprint {
343 let material = SealedDeclarationFingerprintMaterial {
344 format: "ic-memory.sealed-declaration-fingerprint.v1",
345 requests,
346 };
347 let mut writer = FingerprintWriter(crate::hash::FNV_OFFSET);
348 ciborium::into_writer(&material, &mut writer)
350 .unwrap_or_else(|_| panic!("sealed declaration fingerprint encodes into hash"));
351
352 SealedDeclarationFingerprint {
353 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
354 value: writer.0,
355 }
356}
357
358const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
359
360fn internal_ledger_declaration() -> AllocationDeclaration {
361 AllocationDeclaration::memory_manager(
362 IC_MEMORY_LEDGER_STABLE_KEY,
363 MEMORY_MANAGER_LEDGER_ID,
364 IC_MEMORY_LEDGER_LABEL,
365 )
366 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
367}
368
369#[cfg(test)]
370mod tests;