#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use crate::error::{VmError, VmResult};
pub const MAX_NATIVE_FUNCTIONS: usize = 256;
pub const MAX_NATIVE_ARGS: usize = 8;
pub type NativeFunction = Box<dyn Fn(&[u64]) -> u64 + Send + Sync>;
pub struct NativeRegistry {
functions: Vec<Option<NativeFunction>>,
}
impl Default for NativeRegistry {
fn default() -> Self {
Self::new()
}
}
impl NativeRegistry {
pub fn new() -> Self {
let mut functions = Vec::with_capacity(MAX_NATIVE_FUNCTIONS);
for _ in 0..MAX_NATIVE_FUNCTIONS {
functions.push(None);
}
Self { functions }
}
pub fn register<F>(&mut self, id: u8, func: F) -> VmResult<()>
where
F: Fn(&[u64]) -> u64 + Send + Sync + 'static,
{
let idx = id as usize;
if self.functions[idx].is_some() {
return Err(VmError::NativeFunctionAlreadyRegistered);
}
self.functions[idx] = Some(Box::new(func));
Ok(())
}
pub fn register_replace<F>(&mut self, id: u8, func: F)
where
F: Fn(&[u64]) -> u64 + Send + Sync + 'static,
{
let idx = id as usize;
self.functions[idx] = Some(Box::new(func));
}
pub fn unregister(&mut self, id: u8) {
let idx = id as usize;
self.functions[idx] = None;
}
pub fn call(&self, id: u8, args: &[u64]) -> VmResult<u64> {
let idx = id as usize;
match &self.functions[idx] {
Some(func) => Ok(func(args)),
None => Err(VmError::NativeFunctionNotFound),
}
}
pub fn is_registered(&self, id: u8) -> bool {
self.functions[id as usize].is_some()
}
pub fn count(&self) -> usize {
self.functions.iter().filter(|f| f.is_some()).count()
}
pub fn clear(&mut self) {
for func in &mut self.functions {
*func = None;
}
}
}
pub mod standard_ids {
pub use crate::build_config::native_ids::*;
}
pub struct NativeRegistryBuilder {
registry: NativeRegistry,
}
impl Default for NativeRegistryBuilder {
fn default() -> Self {
Self::new()
}
}
impl NativeRegistryBuilder {
pub fn new() -> Self {
Self {
registry: NativeRegistry::new(),
}
}
pub fn with_function<F>(mut self, id: u8, func: F) -> Self
where
F: Fn(&[u64]) -> u64 + Send + Sync + 'static,
{
let _ = self.registry.register(id, func);
self
}
pub fn with_timestamp(self) -> Self {
self.with_function(standard_ids::GET_TIMESTAMP, |_| {
#[cfg(feature = "std")]
{
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(not(feature = "std"))]
{
0
}
})
}
pub fn with_hash(self) -> Self {
self.with_function(standard_ids::HASH_FNV1A, |args| {
let mut hash = crate::build_config::FNV_BASIS_64;
for &arg in args {
for byte in arg.to_le_bytes() {
hash ^= byte as u64;
hash = hash.wrapping_mul(crate::build_config::FNV_PRIME_64);
}
}
hash
})
}
pub fn build(self) -> NativeRegistry {
self.registry
}
}