use crate::error::{VmError, VmResult};
use crate::state::VmState;
pub const VEC_HEADER_SIZE: usize = 24;
const OFFSET_CAPACITY: usize = 0;
const OFFSET_LENGTH: usize = 8;
const OFFSET_ELEM_SIZE: usize = 16;
const OFFSET_DATA: usize = 24;
#[inline]
fn vec_get_capacity(state: &VmState, vec_addr: usize) -> VmResult<u64> {
state.heap_read_u64(vec_addr + OFFSET_CAPACITY)
}
#[inline]
fn vec_get_length(state: &VmState, vec_addr: usize) -> VmResult<u64> {
state.heap_read_u64(vec_addr + OFFSET_LENGTH)
}
#[inline]
fn vec_get_elem_size(state: &VmState, vec_addr: usize) -> VmResult<u64> {
state.heap_read_u64(vec_addr + OFFSET_ELEM_SIZE)
}
#[inline]
fn vec_set_length(state: &mut VmState, vec_addr: usize, length: u64) -> VmResult<()> {
state.heap_write_u64(vec_addr + OFFSET_LENGTH, length)
}
#[inline]
fn vec_data_offset(vec_addr: usize, index: u64, elem_size: u64) -> usize {
vec_addr + OFFSET_DATA + (index as usize * elem_size as usize)
}
fn vec_read_element(state: &VmState, vec_addr: usize, index: u64, elem_size: u64) -> VmResult<u64> {
let offset = vec_data_offset(vec_addr, index, elem_size);
match elem_size {
1 => Ok(state.heap_read_u8(offset)? as u64),
2 => Ok(state.heap_read_u16(offset)? as u64),
4 => Ok(state.heap_read_u32(offset)? as u64),
8 => state.heap_read_u64(offset),
_ => Ok(offset as u64),
}
}
fn vec_write_element(state: &mut VmState, vec_addr: usize, index: u64, elem_size: u64, value: u64) -> VmResult<()> {
let offset = vec_data_offset(vec_addr, index, elem_size);
match elem_size {
1 => state.heap_write_u8(offset, value as u8),
2 => state.heap_write_u16(offset, value as u16),
4 => state.heap_write_u32(offset, value as u32),
8 => state.heap_write_u64(offset, value),
_ => {
let src_addr = value as usize;
let elem_size_usize = elem_size as usize;
let src_bytes = state.heap_read_bytes(src_addr, elem_size_usize)?;
let buffer = src_bytes.to_vec();
state.heap_write_bytes(offset, &buffer)
}
}
}
pub fn handle_vec_new(state: &mut VmState) -> VmResult<()> {
let elem_size = state.pop()?;
let capacity = state.pop()?;
if elem_size == 0 {
return Err(VmError::HeapOutOfBounds);
}
let data_size = capacity.checked_mul(elem_size)
.ok_or(VmError::HeapOutOfMemory)?;
let total_size = VEC_HEADER_SIZE as u64 + data_size;
let vec_addr = state.heap_alloc(total_size as usize)? as usize;
state.heap_write_u64(vec_addr + OFFSET_CAPACITY, capacity)?;
state.heap_write_u64(vec_addr + OFFSET_LENGTH, 0)?;
state.heap_write_u64(vec_addr + OFFSET_ELEM_SIZE, elem_size)?;
state.push(vec_addr as u64)
}
pub fn handle_vec_len(state: &mut VmState) -> VmResult<()> {
let vec_addr = state.pop()? as usize;
let length = vec_get_length(state, vec_addr)?;
state.push(length)
}
pub fn handle_vec_cap(state: &mut VmState) -> VmResult<()> {
let vec_addr = state.pop()? as usize;
let capacity = vec_get_capacity(state, vec_addr)?;
state.push(capacity)
}
pub fn handle_vec_push(state: &mut VmState) -> VmResult<()> {
let value = state.pop()?;
let vec_addr = state.pop()? as usize;
let length = vec_get_length(state, vec_addr)?;
let capacity = vec_get_capacity(state, vec_addr)?;
let elem_size = vec_get_elem_size(state, vec_addr)?;
if length >= capacity {
return Err(VmError::HeapOutOfMemory); }
vec_write_element(state, vec_addr, length, elem_size, value)?;
vec_set_length(state, vec_addr, length + 1)
}
pub fn handle_vec_pop(state: &mut VmState) -> VmResult<()> {
let vec_addr = state.pop()? as usize;
let length = vec_get_length(state, vec_addr)?;
let elem_size = vec_get_elem_size(state, vec_addr)?;
if length == 0 {
return Err(VmError::StackUnderflow); }
let new_length = length - 1;
vec_set_length(state, vec_addr, new_length)?;
let value = vec_read_element(state, vec_addr, new_length, elem_size)?;
state.push(value)
}
pub fn handle_vec_get(state: &mut VmState) -> VmResult<()> {
let index = state.pop()?;
let vec_addr = state.pop()? as usize;
let length = vec_get_length(state, vec_addr)?;
let elem_size = vec_get_elem_size(state, vec_addr)?;
if index >= length {
return Err(VmError::HeapOutOfBounds); }
let value = vec_read_element(state, vec_addr, index, elem_size)?;
state.push(value)
}
pub fn handle_vec_set(state: &mut VmState) -> VmResult<()> {
let value = state.pop()?;
let index = state.pop()?;
let vec_addr = state.pop()? as usize;
let length = vec_get_length(state, vec_addr)?;
let elem_size = vec_get_elem_size(state, vec_addr)?;
if index >= length {
return Err(VmError::HeapOutOfBounds); }
vec_write_element(state, vec_addr, index, elem_size, value)
}
pub fn handle_vec_repeat(state: &mut VmState) -> VmResult<()> {
let elem_size = state.pop()?;
let count = state.pop()?;
let value = state.pop()?;
if elem_size == 0 {
return Err(VmError::HeapOutOfBounds);
}
let data_size = count.checked_mul(elem_size)
.ok_or(VmError::HeapOutOfMemory)?;
let total_size = VEC_HEADER_SIZE as u64 + data_size;
let vec_addr = state.heap_alloc(total_size as usize)? as usize;
state.heap_write_u64(vec_addr + OFFSET_CAPACITY, count)?;
state.heap_write_u64(vec_addr + OFFSET_LENGTH, count)?; state.heap_write_u64(vec_addr + OFFSET_ELEM_SIZE, elem_size)?;
for i in 0..count {
vec_write_element(state, vec_addr, i, elem_size, value)?;
}
state.push(vec_addr as u64)
}
pub fn handle_vec_clear(state: &mut VmState) -> VmResult<()> {
let vec_addr = state.pop()? as usize;
vec_set_length(state, vec_addr, 0)
}
pub fn handle_vec_reserve(state: &mut VmState) -> VmResult<()> {
let _additional = state.pop()?;
let _vec_addr = state.pop()? as usize;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_header_constants() {
assert_eq!(VEC_HEADER_SIZE, 24);
assert_eq!(OFFSET_CAPACITY, 0);
assert_eq!(OFFSET_LENGTH, 8);
assert_eq!(OFFSET_ELEM_SIZE, 16);
assert_eq!(OFFSET_DATA, 24);
}
}