use cfx_types::U256;
use cfx_vm_types::ReturnData;
const MAX_RETURN_WASTE_BYTES: usize = 16384;
pub trait Memory {
fn size(&self) -> usize;
fn resize(&mut self, new_size: usize);
fn expand(&mut self, new_size: usize);
fn write_byte(&mut self, offset: U256, value: U256);
fn write(&mut self, offset: U256, value: U256);
fn read(&self, offset: U256) -> U256;
fn write_slice(&mut self, offset: U256, _: &[u8]);
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8];
fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
}
pub fn is_valid_range(off: usize, size: usize) -> bool {
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn size(&self) -> usize { self.len() }
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&[]
} else {
&self[off..off + size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off + 32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut []
} else {
&mut self[off..off + s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
if !slice.is_empty() {
let off = offset.low_u64() as usize;
self[off..off + slice.len()].copy_from_slice(slice);
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
value.to_big_endian(&mut self[off..off + 32]);
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) { self.resize(new_size, 0); }
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData {
let mut offset = offset.low_u64() as usize;
let size = size.low_u64() as usize;
if !is_valid_range(offset, size) {
return ReturnData::empty();
}
if self.len() - size > MAX_RETURN_WASTE_BYTES {
if offset == 0 {
self.truncate(size);
self.shrink_to_fit();
} else {
self = self[offset..(offset + size)].to_vec();
offset = 0;
}
}
ReturnData::new(self, offset, size)
}
}
#[cfg(test)]
mod tests {
use super::Memory;
use cfx_types::U256;
#[test]
fn test_memory_read_and_write() {
let mem: &mut dyn Memory = &mut vec![];
mem.resize(0x80 + 32);
mem.write(U256::from(0x80), U256::from(0xabcdef));
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
let mem: &mut dyn Memory = &mut vec![];
mem.resize(32);
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_slice_and_write_slice() {
let mem: &mut dyn Memory = &mut vec![];
mem.resize(32);
{
let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes();
mem.write_slice(U256::from(0), slice);
assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice);
}
{
let slice = "67890".as_bytes();
mem.write_slice(U256::from(0x1), slice);
assert_eq!(
mem.read_slice(U256::from(0), U256::from(7)),
"a67890g".as_bytes()
);
}
{
let slice = [];
mem.write_slice(U256::from(0x1000), &slice);
assert_eq!(mem.size(), 32);
}
}
}