use crate::{
GenericAllocationPolicy, MemoryAllocationPool, MemoryAuthority, MemoryManagerConfig,
MemoryRequest, MemoryRuntime, RuntimeMemory, SealedDeclarationSnapshot,
ic_stable_structures::{Cell, Memory},
};
#[must_use]
pub fn allocation_record(
declaration: &crate::AllocationDeclaration,
state: crate::AllocationState,
) -> crate::AllocationRecord {
crate::AllocationRecord::from_declaration(declaration, state)
}
pub struct FixtureCell<M: Memory> {
cell: Cell<Vec<u8>, RuntimeMemory<M>>,
maximum: usize,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FixtureCellError {
#[error(transparent)]
Declaration(#[from] crate::MemoryDeclarationError),
#[error(transparent)]
AllocationPool(#[from] crate::MemoryAllocationPoolError),
#[error(transparent)]
Construction(#[from] crate::RuntimeConstructionError),
#[error(transparent)]
Bootstrap(Box<crate::RuntimeBootstrapError<std::convert::Infallible>>),
#[error(transparent)]
Open(#[from] crate::RuntimeOpenError),
#[error(transparent)]
Payload(#[from] crate::StableCellPayloadError),
#[error("fixture allocation is already occupied")]
Occupied,
#[error("fixture value length {size} exceeds byte limit {maximum}")]
ValueTooLarge { size: usize, maximum: usize },
}
impl<M: Memory> FixtureCell<M> {
pub fn new(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Self {
Self::try_new(memory, owner, namespace, stable_key, config).expect("fixture cell setup")
}
pub fn try_new(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Result<Self, FixtureCellError> {
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
if memory.size() > 0 {
crate::stable_cell::stable_cell_payload_length(&memory, usize::MAX)?;
}
Ok(Self::from_admitted_memory(memory, usize::MAX))
}
pub fn create_bounded(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
maximum: usize,
) -> Result<Self, FixtureCellError> {
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
if memory.size() > 0 {
return Err(FixtureCellError::Occupied);
}
Ok(Self::from_admitted_memory(memory, maximum))
}
pub fn open_bounded(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
maximum: usize,
) -> Result<Self, FixtureCellError> {
if memory.size() == 0 {
return Err(crate::StableCellPayloadError::NotStableCell.into());
}
let memory = Self::open_memory(memory, owner, namespace, stable_key, config)?;
crate::stable_cell::stable_cell_payload_length(&memory, maximum)?;
Ok(Self::from_admitted_memory(memory, maximum))
}
fn open_memory(
memory: M,
owner: &str,
namespace: &str,
stable_key: &str,
config: MemoryManagerConfig,
) -> Result<RuntimeMemory<M>, FixtureCellError> {
let request = MemoryRequest::new(owner, stable_key)?;
let pool =
MemoryAllocationPool::new(vec![MemoryAuthority::new(owner, namespace)?], vec![])?;
let declarations = SealedDeclarationSnapshot::new(vec![request])?;
let mut runtime = MemoryRuntime::new_with_config(memory, config)?;
runtime
.bootstrap(&declarations, &pool, &GenericAllocationPolicy)
.map_err(|error| FixtureCellError::Bootstrap(Box::new(error)))?;
Ok(runtime.open_memory(stable_key)?)
}
fn from_admitted_memory(memory: RuntimeMemory<M>, maximum: usize) -> Self {
Self {
cell: Cell::init(memory, Vec::new()),
maximum,
}
}
#[must_use]
pub fn into_memory(self) -> RuntimeMemory<M> {
self.cell.into_memory()
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
self.cell.get()
}
pub fn try_set(&mut self, bytes: Vec<u8>) -> Result<(), FixtureCellError> {
if bytes.len() > self.maximum {
return Err(FixtureCellError::ValueTooLarge {
size: bytes.len(),
maximum: self.maximum,
});
}
self.cell.set(bytes);
Ok(())
}
pub fn set(&mut self, bytes: Vec<u8>) {
self.try_set(bytes).expect("fixture cell write");
}
}
#[cfg(test)]
mod tests;