#![expect(unused_assignments)]
use alloc::{format, string::String, vec, vec::Vec};
use core::ops::{Index, IndexMut, Range};
use miden_core::field::PrimeField64;
use midenc_hir::{Felt, Immediate, SmallVec, SourceSpan, Type};
use midenc_session::diagnostics::{Diagnostic, miette};
use crate::Value;
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum ReadFailed {
#[error("attempted to read memory beyond addressable heap at {addr}")]
#[diagnostic()]
AddressOutOfBounds {
addr: u32,
#[label]
at: SourceSpan,
},
#[error("attempted to read value of size {size} beyond addressable heap at {addr}")]
#[diagnostic()]
SizeOutOfBounds {
addr: u32,
size: u32,
#[label]
at: SourceSpan,
},
#[error("unsupported type")]
#[diagnostic()]
UnsupportedType,
#[error("invalid field element: {0}")]
#[diagnostic()]
InvalidFelt(String),
}
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum WriteFailed {
#[error("attempted to write memory beyond addressable heap at {addr}")]
#[diagnostic()]
AddressOutOfBounds {
addr: u32,
#[label]
at: SourceSpan,
},
#[error("attempted to write value of size {size} beyond addressable heap at {addr}")]
#[diagnostic()]
SizeOutOfBounds {
addr: u32,
size: u32,
#[label]
at: SourceSpan,
},
}
pub fn read_value(addr: usize, ty: &Type, memory: &[u8]) -> Result<Value, ReadFailed> {
let imm = match ty {
Type::I1 => {
let byte = read_byte(addr, memory);
Immediate::I1((byte & 0x1) == 1)
}
Type::I8 => {
let value = read_byte(addr, memory) as i8;
Immediate::I8(value)
}
Type::U8 => {
let value = read_byte(addr, memory);
Immediate::U8(value)
}
Type::I16 => {
let value = i16::from_be_bytes(read_bytes(addr, memory));
Immediate::I16(value)
}
Type::U16 => {
let value = u16::from_be_bytes(read_bytes(addr, memory));
Immediate::U16(value)
}
Type::I32 => {
let value = i32::from_be_bytes(read_bytes(addr, memory));
Immediate::I32(value)
}
Type::U32 => {
let value = u32::from_be_bytes(read_bytes(addr, memory));
Immediate::U32(value)
}
Type::I64 => {
let value = i64::from_be_bytes(read_bytes(addr, memory));
Immediate::I64(value)
}
Type::U64 => {
let value = u64::from_be_bytes(read_bytes(addr, memory));
Immediate::U64(value)
}
Type::I128 => {
let value = i128::from_be_bytes(read_bytes(addr, memory));
Immediate::I128(value)
}
Type::U128 => {
let value = u128::from_be_bytes(read_bytes(addr, memory));
Immediate::U128(value)
}
Type::F64 => {
let value = f64::from_be_bytes(read_bytes(addr, memory));
Immediate::F64(value)
}
Type::Felt => {
let bytes = read_bytes::<8>(addr, memory);
let value = u64::from_le_bytes(bytes);
if value >= Felt::ORDER_U64 {
return Err(ReadFailed::InvalidFelt(format!(
"failed to decode felt at {addr}: value {value} exceeds field modulus"
)));
}
Immediate::Felt(Felt::new(value))
}
Type::Ptr(_) => {
let value = u32::from_be_bytes(read_bytes(addr, memory));
Immediate::U32(value)
}
_ => {
return Err(ReadFailed::UnsupportedType);
}
};
Ok(Value::Immediate(imm))
}
#[inline]
pub fn read_byte(addr: usize, memory: &[u8]) -> u8 {
memory.get(addr).copied().unwrap_or_default()
}
pub fn read_bytes<const N: usize>(addr: usize, memory: &[u8]) -> [u8; N] {
match memory.get(addr..(addr + N)) {
Some(bytes) => <[u8; N]>::try_from(bytes).unwrap(),
None if memory.len() <= addr => {
[0; N]
}
None => {
let mut buf = [0; N];
for (byte, addr) in (addr..(addr + N)).enumerate() {
buf[byte] = read_byte(addr, memory);
}
buf
}
}
}
pub trait Buffer:
Index<usize, Output = u8>
+ Index<Range<usize>, Output = [u8]>
+ IndexMut<usize, Output = u8>
+ IndexMut<Range<usize>, Output = [u8]>
{
fn get_mut(&mut self, index: usize) -> Option<&mut u8>;
fn get_slice_mut(&mut self, index: Range<usize>) -> Option<&mut [u8]>;
fn resize(&mut self, len: usize, value: u8);
}
impl Buffer for Vec<u8> {
#[inline(always)]
fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
self.as_mut_slice().get_mut(index)
}
#[inline(always)]
fn get_slice_mut(&mut self, index: Range<usize>) -> Option<&mut [u8]> {
self.as_mut_slice().get_mut(index)
}
#[inline(always)]
fn resize(&mut self, len: usize, value: u8) {
self.resize(len, value)
}
}
impl<const N: usize> Buffer for SmallVec<[u8; N]> {
#[inline(always)]
fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
self.as_mut_slice().get_mut(index)
}
#[inline(always)]
fn get_slice_mut(&mut self, index: Range<usize>) -> Option<&mut [u8]> {
self.as_mut_slice().get_mut(index)
}
#[inline(always)]
fn resize(&mut self, len: usize, value: u8) {
self.resize(len, value)
}
}
pub fn write_value<B: Buffer>(addr: usize, value: Value, memory: &mut B) {
let imm = match value {
Value::Poison { value, .. } | Value::Immediate(value) => value,
};
match imm {
Immediate::I1(value) => write_byte(addr, value as u8, memory),
Immediate::I8(value) => write_byte(addr, value as u8, memory),
Immediate::U8(value) => write_byte(addr, value, memory),
Immediate::I16(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::U16(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::I32(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::U32(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::I64(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::U64(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::I128(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::U128(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::F64(value) => write_bytes(addr, &value.to_be_bytes(), memory),
Immediate::Felt(value) => {
write_bytes(addr, &value.as_canonical_u64().to_le_bytes(), memory)
}
}
}
pub fn write_byte<B: Buffer>(addr: usize, byte: u8, memory: &mut B) {
match memory.get_mut(addr) {
Some(slot) => *slot = byte,
None => {
memory.resize(addr + 8, 0);
memory[addr] = byte;
}
}
}
pub fn write_bytes<B: Buffer>(addr: usize, bytes: &[u8], memory: &mut B) {
match memory.get_slice_mut(addr..(addr + bytes.len())) {
Some(target_bytes) => {
target_bytes.copy_from_slice(bytes);
}
None => {
memory.resize(addr + bytes.len() + 8, 0);
memory[addr..(addr + bytes.len())].copy_from_slice(bytes);
}
}
}