use gear_core::pages::{numerated::tree::IntervalsTree, WasmPage};
use super::*;
use crate::storage::{MapStorage, TripleMapStorage};
use core::fmt::Debug;
pub trait Error {
fn duplicate_item() -> Self;
fn program_not_found() -> Self;
fn not_active_program() -> Self;
fn cannot_find_page_data() -> Self;
fn program_code_not_found() -> Self;
}
pub type MemoryMap = BTreeMap<GearPage, PageBuf>;
pub trait ProgramStorage {
type InternalError: Error;
type Error: From<Self::InternalError> + Debug;
type BlockNumber: Copy + Saturating;
type AccountId: Eq + PartialEq;
type ProgramMap: MapStorage<Key = ProgramId, Value = Program<Self::BlockNumber>>;
type MemoryPageMap: TripleMapStorage<
Key1 = ProgramId,
Key2 = MemoryInfix,
Key3 = GearPage,
Value = PageBuf,
>;
type AllocationsMap: MapStorage<Key = ProgramId, Value = IntervalsTree<WasmPage>>;
fn reset() {
Self::ProgramMap::clear();
Self::MemoryPageMap::clear();
Self::AllocationsMap::clear();
}
fn add_program(
program_id: ProgramId,
program: ActiveProgram<Self::BlockNumber>,
) -> Result<(), Self::Error> {
Self::ProgramMap::mutate(program_id, |maybe| {
if maybe.is_some() {
return Err(Self::InternalError::duplicate_item().into());
}
*maybe = Some(Program::Active(program));
Ok(())
})
}
fn get_program(program_id: ProgramId) -> Option<Program<Self::BlockNumber>> {
Self::ProgramMap::get(&program_id)
}
fn program_exists(program_id: ProgramId) -> bool {
Self::ProgramMap::contains_key(&program_id)
}
fn update_active_program<F, ReturnType>(
program_id: ProgramId,
update_action: F,
) -> Result<ReturnType, Self::Error>
where
F: FnOnce(&mut ActiveProgram<Self::BlockNumber>) -> ReturnType,
{
Self::update_program_if_active(program_id, |program, _bn| match program {
Program::Active(active_program) => update_action(active_program),
_ => unreachable!("invariant kept by update_program_if_active"),
})
}
fn remove_data_for_pages(
program_id: ProgramId,
memory_infix: MemoryInfix,
pages: impl Iterator<Item = GearPage>,
) {
for page in pages {
Self::remove_program_page_data(program_id, memory_infix, page);
}
}
fn allocations(program_id: ProgramId) -> Option<IntervalsTree<WasmPage>> {
Self::AllocationsMap::get(&program_id)
}
fn set_allocations(program_id: ProgramId, allocations: IntervalsTree<WasmPage>) {
Self::update_active_program(program_id, |program| {
program.allocations_tree_len = u32::try_from(allocations.intervals_amount())
.unwrap_or_else(|err| {
unreachable!("allocations tree length is too big to fit into u32: {err}")
});
})
.unwrap_or_else(|err| {
unreachable!("Failed to update program allocations: {err:?}")
});
Self::AllocationsMap::insert(program_id, allocations);
}
fn clear_allocations(program_id: ProgramId) {
Self::AllocationsMap::remove(program_id);
}
fn memory_infix(program_id: ProgramId) -> Option<MemoryInfix> {
match Self::ProgramMap::get(&program_id) {
Some(Program::Active(program)) => Some(program.memory_infix),
_ => None,
}
}
fn update_program_if_active<F, ReturnType>(
program_id: ProgramId,
update_action: F,
) -> Result<ReturnType, Self::Error>
where
F: FnOnce(&mut Program<Self::BlockNumber>, Self::BlockNumber) -> ReturnType,
{
let mut program =
Self::ProgramMap::get(&program_id).ok_or(Self::InternalError::program_not_found())?;
let bn = match program {
Program::Active(ref p) => p.expiration_block,
_ => return Err(Self::InternalError::not_active_program().into()),
};
let result = update_action(&mut program, bn);
Self::ProgramMap::insert(program_id, program);
Ok(result)
}
fn get_program_pages_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
) -> Result<MemoryMap, Self::Error> {
Ok(Self::MemoryPageMap::iter_prefix(&program_id, &memory_infix).collect())
}
fn set_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page: GearPage,
page_buf: PageBuf,
) {
Self::MemoryPageMap::insert(program_id, memory_infix, page, page_buf);
}
fn remove_program_page_data(
program_id: ProgramId,
memory_infix: MemoryInfix,
page_num: GearPage,
) {
Self::MemoryPageMap::remove(program_id, memory_infix, page_num);
}
fn clear_program_memory(program_id: ProgramId, memory_infix: MemoryInfix) {
Self::MemoryPageMap::clear_prefix(program_id, memory_infix);
}
fn pages_final_prefix() -> [u8; 32];
}