#![allow(clippy::wrong_self_convention)]
use super::instruction;
use crate::{instructions::control, primitives::Spec, Host, Interpreter};
use std::boxed::Box;
pub type Instruction<H> = fn(&mut Interpreter, &mut H);
pub type InstructionTable<H> = [Instruction<H>; 256];
pub type DynInstruction<'a, H> = dyn Fn(&mut Interpreter, &mut H) + 'a;
pub type BoxedInstruction<'a, H> = Box<DynInstruction<'a, H>>;
pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256];
pub enum InstructionTables<'a, H: ?Sized> {
Plain(InstructionTable<H>),
Boxed(BoxedInstructionTable<'a, H>),
}
impl<H: Host + ?Sized> InstructionTables<'_, H> {
#[inline]
pub const fn new_plain<SPEC: Spec>() -> Self {
Self::Plain(make_instruction_table::<H, SPEC>())
}
}
impl<'a, H: Host + ?Sized + 'a> InstructionTables<'a, H> {
#[inline]
pub fn insert(&mut self, opcode: u8, instruction: Instruction<H>) {
match self {
Self::Plain(table) => table[opcode as usize] = instruction,
Self::Boxed(table) => table[opcode as usize] = Box::new(instruction),
}
}
#[inline]
pub fn to_boxed(&mut self) -> &mut BoxedInstructionTable<'a, H> {
self.to_boxed_with(|i| Box::new(i))
}
#[inline]
pub fn to_boxed_with<F>(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H>
where
F: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
{
match self {
Self::Plain(_) => self.to_boxed_with_slow(f),
Self::Boxed(boxed) => boxed,
}
}
#[cold]
fn to_boxed_with_slow<F>(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H>
where
F: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
{
let Self::Plain(table) = self else {
unreachable!()
};
*self = Self::Boxed(make_boxed_instruction_table(table, f));
let Self::Boxed(boxed) = self else {
unreachable!()
};
boxed
}
#[inline]
pub fn get_boxed(&mut self, opcode: u8) -> &mut BoxedInstruction<'a, H> {
&mut self.to_boxed()[opcode as usize]
}
#[inline]
pub fn insert_boxed(&mut self, opcode: u8, instruction: BoxedInstruction<'a, H>) {
*self.get_boxed(opcode) = instruction;
}
#[inline]
pub fn replace_boxed(
&mut self,
opcode: u8,
instruction: BoxedInstruction<'a, H>,
) -> BoxedInstruction<'a, H> {
core::mem::replace(self.get_boxed(opcode), instruction)
}
#[inline]
pub fn update_boxed<F>(&mut self, opcode: u8, f: F)
where
F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a,
{
update_boxed_instruction(self.get_boxed(opcode), f)
}
#[inline]
pub fn update_all<F>(&mut self, f: F)
where
F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + Copy + 'a,
{
match self {
Self::Plain(_) => {
self.to_boxed_with(|prev| Box::new(move |i, h| f(&prev, i, h)));
}
Self::Boxed(boxed) => boxed
.iter_mut()
.for_each(|instruction| update_boxed_instruction(instruction, f)),
}
}
}
#[inline]
pub const fn make_instruction_table<H: Host + ?Sized, SPEC: Spec>() -> InstructionTable<H> {
const {
let mut tables: InstructionTable<H> = [control::unknown; 256];
let mut i = 0;
while i < 256 {
tables[i] = instruction::<H, SPEC>(i as u8);
i += 1;
}
tables
}
}
#[inline]
pub fn make_boxed_instruction_table<'a, H, FN>(
table: &InstructionTable<H>,
mut f: FN,
) -> BoxedInstructionTable<'a, H>
where
H: Host + ?Sized,
FN: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
{
core::array::from_fn(|i| f(table[i]))
}
#[inline]
pub fn update_boxed_instruction<'a, H, F>(instruction: &mut BoxedInstruction<'a, H>, f: F)
where
H: Host + ?Sized + 'a,
F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a,
{
let prev = core::mem::replace(instruction, Box::new(|_, _| {}));
*instruction = Box::new(move |i, h| f(&prev, i, h));
}