use alloc::borrow::ToOwned;
use alloc::collections::{btree_map, BTreeMap};
use alloc::string::String;
use core::marker::PhantomData;
use crate::isa::InstructionSet;
use crate::library::constants::LIBS_MAX_TOTAL;
use crate::library::{Lib, LibId, LibSite};
pub trait Program {
type Isa: InstructionSet;
type Iter<'a>: Iterator<Item = &'a Lib>
where
Self: 'a;
fn lib_count(&self) -> u16;
fn libs(&self) -> Self::Iter<'_>;
fn lib(&self, id: LibId) -> Option<&Lib>;
fn entrypoint(&self) -> LibSite;
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[cfg_attr(feature = "std", derive(Error))]
#[display(doc_comments)]
pub enum ProgError {
IsaNotSupported(String),
TooManyLibs,
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct Prog<Isa, const RUNTIME_MAX_TOTAL_LIBS: u16 = 1024>
where
Isa: InstructionSet,
{
libs: BTreeMap<LibId, Lib>,
entrypoint: LibSite,
#[cfg_attr(feature = "serde", serde(skip))]
phantom: PhantomData<Isa>,
}
impl<Isa, const RUNTIME_MAX_TOTAL_LIBS: u16> Prog<Isa, RUNTIME_MAX_TOTAL_LIBS>
where
Isa: InstructionSet,
{
const RUNTIME_MAX_TOTAL_LIBS: u16 = RUNTIME_MAX_TOTAL_LIBS;
fn empty_unchecked() -> Self {
Prog { libs: BTreeMap::new(), entrypoint: LibSite::with(0, zero!()), phantom: default!() }
}
pub fn new(lib: Lib) -> Self {
let mut runtime = Self::empty_unchecked();
let id = lib.id();
runtime.add_lib(lib).expect("adding single library to lib segment overflows");
runtime.set_entrypoint(LibSite::with(0, id));
runtime
}
pub fn with(
libs: impl IntoIterator<Item = Lib>,
entrypoint: LibSite,
) -> Result<Self, ProgError> {
let mut runtime = Self::empty_unchecked();
for lib in libs {
runtime.add_lib(lib)?;
}
runtime.set_entrypoint(entrypoint);
Ok(runtime)
}
#[inline]
pub fn add_lib(&mut self, lib: Lib) -> Result<bool, ProgError> {
if self.lib_count() >= LIBS_MAX_TOTAL.min(Self::RUNTIME_MAX_TOTAL_LIBS) {
return Err(ProgError::TooManyLibs);
}
for isa in &lib.isae {
if !Isa::is_supported(isa) {
return Err(ProgError::IsaNotSupported(isa.to_owned()));
}
}
Ok(self.libs.insert(lib.id(), lib).is_none())
}
pub fn set_entrypoint(&mut self, entrypoint: LibSite) { self.entrypoint = entrypoint; }
}
impl<Isa, const RUNTIME_MAX_TOTAL_LIBS: u16> Program for Prog<Isa, RUNTIME_MAX_TOTAL_LIBS>
where
Isa: InstructionSet,
{
type Isa = Isa;
type Iter<'a> = btree_map::Values<'a, LibId, Lib> where Self: 'a;
fn lib_count(&self) -> u16 { self.libs.len() as u16 }
fn libs(&self) -> Self::Iter<'_> { self.libs.values() }
fn lib(&self, id: LibId) -> Option<&Lib> { self.libs.get(&id) }
fn entrypoint(&self) -> LibSite { self.entrypoint }
}