use super::*;
pub(crate) const NEOVM_MAX_ITEM_SIZE: usize = 0xFFFF;
impl ExecutionContext {
pub(crate) fn new_buffer(&mut self) -> Result<(), RuntimeError> {
let len = self.pop_usize("NEWBUFFER")?;
if len > NEOVM_MAX_ITEM_SIZE {
return Err(RuntimeError::ExecutionError {
message: format!(
"NEWBUFFER: length {len} exceeds NeoVM MaxItemSize ({NEOVM_MAX_ITEM_SIZE})"
),
});
}
if len > self.memory_limit {
return Err(RuntimeError::ExecutionError {
message: format!(
"NEWBUFFER: requested length {} exceeds memory limit {}",
len, self.memory_limit
),
});
}
self.push_stack(StackItem::byte_array(vec![0u8; len]))
}
pub(crate) fn memcpy_bytes(&mut self) -> Result<(), RuntimeError> {
let count = self.pop_usize("MEMCPY count")?;
let src_offset = self.pop_usize("MEMCPY src_offset")?;
let src = Self::stack_item_to_bytes(self.pop_stack()?);
let dst_offset = self.pop_usize("MEMCPY dst_offset")?;
let dst_item = self.pop_stack()?;
match dst_item {
StackItem::ByteArray(dst) => {
let src_end =
src_offset
.checked_add(count)
.ok_or(RuntimeError::ExecutionError {
message: "MEMCPY: source range overflow".to_string(),
})?;
let dst_end =
dst_offset
.checked_add(count)
.ok_or(RuntimeError::ExecutionError {
message: "MEMCPY: destination range overflow".to_string(),
})?;
{
let mut dst_ref = dst.borrow_mut();
if src_end > src.len() || dst_end > dst_ref.len() {
return Err(RuntimeError::ExecutionError {
message: "MEMCPY: range out of bounds".to_string(),
});
}
let src_slice = &src[src_offset..src_end];
dst_ref[dst_offset..dst_end].copy_from_slice(src_slice);
}
Ok(())
}
other => Err(RuntimeError::ExecutionError {
message: format!("MEMCPY: unsupported destination {other:?}"),
}),
}
}
pub(crate) fn concat_bytes(&mut self) -> Result<(), RuntimeError> {
let b = Self::stack_item_to_bytes(self.pop_stack()?);
let mut a = Self::stack_item_to_bytes(self.pop_stack()?);
if a.len().saturating_add(b.len()) > NEOVM_MAX_ITEM_SIZE {
return Err(RuntimeError::ExecutionError {
message: format!("CAT: result exceeds NeoVM MaxItemSize ({NEOVM_MAX_ITEM_SIZE})"),
});
}
a.extend_from_slice(&b);
self.push_stack(StackItem::byte_array(a))
}
pub(crate) fn substr_bytes(&mut self) -> Result<(), RuntimeError> {
let count = self.pop_usize("SUBSTR")?;
let index = self.pop_usize("SUBSTR")?;
let data = Self::stack_item_to_bytes(self.pop_stack()?);
let end = index
.checked_add(count)
.ok_or(RuntimeError::ExecutionError {
message: "SUBSTR: range overflow".to_string(),
})?;
if end > data.len() {
return Err(RuntimeError::ExecutionError {
message: "SUBSTR: out of bounds".to_string(),
});
}
self.push_stack(StackItem::byte_array(data[index..end].to_vec()))
}
pub(crate) fn left_bytes(&mut self) -> Result<(), RuntimeError> {
let count = self.pop_usize("LEFT")?;
let data = Self::stack_item_to_bytes(self.pop_stack()?);
if count > data.len() {
return Err(RuntimeError::ExecutionError {
message: "LEFT: out of bounds".to_string(),
});
}
self.push_stack(StackItem::byte_array(data[..count].to_vec()))
}
pub(crate) fn right_bytes(&mut self) -> Result<(), RuntimeError> {
let count = self.pop_usize("RIGHT")?;
let data = Self::stack_item_to_bytes(self.pop_stack()?);
if count > data.len() {
return Err(RuntimeError::ExecutionError {
message: "RIGHT: out of bounds".to_string(),
});
}
self.push_stack(StackItem::byte_array(data[data.len() - count..].to_vec()))
}
}