use crate::{
declaration::{AllocationDeclaration, DeclarationSnapshot},
schema::SchemaMetadata,
slot::{
IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID,
is_ic_memory_stable_key,
},
text::validate_diagnostic_text,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct MemoryRequest {
authority: String,
stable_key: crate::StableKey,
schema: SchemaMetadata,
}
impl<'de> Deserialize<'de> for MemoryRequest {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error as _;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Request {
authority: String,
stable_key: crate::StableKey,
schema: SchemaMetadata,
}
let request = Request::deserialize(deserializer)?;
validate_external_authority(&request.authority).map_err(D::Error::custom)?;
validate_external_key(&request.stable_key).map_err(D::Error::custom)?;
Ok(Self {
authority: request.authority,
stable_key: request.stable_key,
schema: request.schema,
})
}
}
impl MemoryRequest {
pub fn new(
authority: impl Into<String>,
stable_key: &str,
schema: SchemaMetadata,
) -> Result<Self, MemoryDeclarationError> {
let authority = authority.into();
let stable_key = checked_request_key(&authority, stable_key)?;
Ok(Self {
authority,
stable_key,
schema,
})
}
#[must_use]
pub const fn stable_key(&self) -> &crate::StableKey {
&self.stable_key
}
#[must_use]
pub const fn schema(&self) -> &SchemaMetadata {
&self.schema
}
#[must_use]
pub fn authority(&self) -> &str {
&self.authority
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
pub enum MemoryDeclarationError {
#[error("at most 254 external requests are supported")]
TooManyDeclarations,
#[error("duplicate requested stable key {stable_key}")]
DuplicateRequest { stable_key: crate::StableKey },
#[error(transparent)]
Declaration(#[from] crate::DeclarationSnapshotError),
#[error("authority {reason}")]
InvalidAuthority {
reason: &'static str,
},
#[error("authority '{authority}' is reserved for ic-memory runtime internals")]
ReservedAuthority {
authority: String,
},
#[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
ReservedStableKey {
stable_key: String,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SealedDeclarationSnapshot {
inner: Arc<Vec<MemoryRequest>>,
}
impl SealedDeclarationSnapshot {
pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, MemoryDeclarationError> {
if requests.len() > 254 {
return Err(MemoryDeclarationError::TooManyDeclarations);
}
crate::sort::sort_unstable_by(&mut requests, |a, b| a.stable_key.cmp(&b.stable_key));
for pair in requests.windows(2) {
if pair[0].stable_key == pair[1].stable_key {
return Err(MemoryDeclarationError::DuplicateRequest {
stable_key: pair[1].stable_key.clone(),
});
}
}
Ok(Self {
inner: Arc::new(requests),
})
}
#[must_use]
pub fn requests(&self) -> &[MemoryRequest] {
&self.inner
}
pub(crate) fn resolve(
&self,
ledger: &crate::AllocationLedger,
occupied: &[bool; crate::constants::MAX_ALLOCATIONS],
historical: Vec<AllocationDeclaration>,
pool: &crate::MemoryAllocationPool,
) -> Result<DeclarationSnapshot, crate::MemoryResolutionError> {
if self.requests().len() + historical.len() > 254 {
return Err(MemoryDeclarationError::TooManyDeclarations.into());
}
let mut declarations = Vec::with_capacity(self.requests().len() + historical.len() + 1);
declarations.push(internal_ledger_declaration());
let mut available_ids = 0..crate::MEMORY_MANAGER_INVALID_ID;
for request in self.requests() {
let historical = ledger
.records()
.iter()
.find(|record| record.stable_key() == &request.stable_key);
pool.validate_authority(&request.stable_key, &request.authority)?;
let id = if let Some(record) = historical {
pool.validate_id(record.slot().id())?;
record.slot().id()
} else {
available_ids
.find(|id| pool.contains(*id) && !occupied[usize::from(*id)])
.ok_or_else(|| crate::MemoryResolutionError::Exhausted {
stable_key: request.stable_key.clone(),
authority: request.authority.clone(),
})?
};
let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
declarations.push(AllocationDeclaration {
stable_key: request.stable_key.clone(),
slot,
schema: request.schema.clone(),
});
}
declarations.extend(historical);
Ok(
DeclarationSnapshot::canonicalize(declarations)
.map_err(MemoryDeclarationError::from)?,
)
}
#[cfg(test)]
pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
pub fn checked_request_key(
authority: &str,
stable_key: &str,
) -> Result<crate::StableKey, MemoryDeclarationError> {
validate_external_authority(authority)?;
let stable_key =
crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
validate_external_key(&stable_key)?;
Ok(stable_key)
}
fn validate_external_key(stable_key: &crate::StableKey) -> Result<(), MemoryDeclarationError> {
if is_ic_memory_stable_key(stable_key.as_str()) {
return Err(MemoryDeclarationError::ReservedStableKey {
stable_key: stable_key.as_str().to_string(),
});
}
Ok(())
}
fn validate_external_authority(value: &str) -> Result<(), MemoryDeclarationError> {
reject_internal_authority(value)?;
validate_diagnostic_text(value).map_err(|error| MemoryDeclarationError::InvalidAuthority {
reason: error.reason(),
})
}
fn reject_internal_authority(value: &str) -> Result<(), MemoryDeclarationError> {
if value == IC_MEMORY_AUTHORITY_OWNER {
return Err(MemoryDeclarationError::ReservedAuthority {
authority: value.to_string(),
});
}
Ok(())
}
fn internal_ledger_declaration() -> AllocationDeclaration {
AllocationDeclaration::memory_manager(IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID)
.unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
}
#[cfg(test)]
mod tests;