use std::marker::PhantomData;
use crate::ffi::errors::FFIError;
use crate::ffi::library::sendRawRequest;
use crate::ffi::value::Value;
use crate::zygote::FFIRequest;
pub struct AllocatedMemory<'g>
{
address: usize,
length: usize,
_scope: PhantomData<&'g ()>
}
impl<'g> AllocatedMemory<'g>
{
pub(super) const fn new(address: usize, length: usize) -> Self
{
Self {
address,
length,
_scope: PhantomData,
}
}
pub const fn address(&self) -> usize { self.address }
pub const fn length(&self) -> usize { self.length }
pub const fn asPointer(&self) -> Value {
Value::Pointer(self.address)
}
pub fn read(&self) -> Result<Value, FFIError> {
sendRawRequest(FFIRequest::ReadMemory { pointer: self.address, length: self.length })
}
pub fn write(&self, value: Value) -> Result<(), FFIError> {
sendRawRequest(FFIRequest::WriteMemory { pointer: self.address, value })?;
Ok(())
}
}
impl<'g> Drop for AllocatedMemory<'g>
{
fn drop(&mut self)
{
if self.address != 0 {
let _ = sendRawRequest(FFIRequest::Free { pointer: self.address });
}
}
}
#[cfg(test)]
mod tests
{
use crate::ffi;
use crate::ffi::allocatedMemory::AllocatedMemory;
use crate::ffi::errors::FFIError;
use crate::ffi::value::Value;
use crate::ffi::value::Type;
#[test]
fn read() -> ()
{
let bytes: Vec<u8> = ffi!(|scope| {
let mem: AllocatedMemory = scope.alloc(8)?;
let libc: Library = Library::load("libc.so.6")?;
libc.call("memset", vec![mem.asPointer(), Value::I32(0xAB), Value::Usize(8)], Type::Pointer)?;
let Value::RawString(bytes) = mem.read()? else {
return Err(FFIError::Other("expected bytes".into()))
};
Ok(bytes)
}).expect("alloc/readMemory/free roundtrip failed");
assert_eq!(bytes, vec![0xABu8; 8]);
}
#[test]
fn write() -> ()
{
let len: Value = ffi!(|scope| {
let mem: AllocatedMemory = scope.alloc(32)?;
mem.write(Value::CString(b"hello".to_vec()))?;
let libc: Library = Library::load("libc.so.6")?;
let result: Value = libc.call("strlen", vec![mem.asPointer()], Type::Usize)?;
Ok(result)
}).expect("AllocatedMemory::write failed");
assert!(matches!(len, Value::Usize(5)));
}
#[test]
fn drop() -> ()
{
let (addr1, addr2): (usize, usize) = ffi!(|scope| {
let addr1: usize = {
let mem: AllocatedMemory = scope.alloc(16)?;
let a: usize = mem.address();
a
};
let mem2: AllocatedMemory = scope.alloc(16)?;
let addr2: usize = mem2.address();
Ok((addr1, addr2))
}).expect("AllocatedMemory::drop failed");
assert_eq!(addr1, addr2);
}
}