use crate::ffi::errors::FFIError;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Value
{
None,
U8(u8),
U16(u16),
U32(u32),
U64(u64),
Usize(usize),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
Isize(isize),
F32(f32),
F64(f64),
Bool(bool),
Pointer(usize),
RawString(Vec<u8>),
CString(Vec<u8>),
String(Vec<u8>),
Function(u64),
Struct(Vec<Value>)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Type
{
None,
U8,
U16,
U32,
U64,
Usize,
I8,
I16,
I32,
I64,
Isize,
F32,
F64,
Bool,
Pointer,
Struct(Vec<Type>)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Pointer(pub usize);
pub trait Primitive: Sized
{
const TypeTag: Type;
fn fromValue(value: Value) -> Result<Self, FFIError>;
fn toValue(self) -> Value;
}
macro_rules! implFFIPrimitive
{
($rustType:ty, $variant:ident) =>
{
impl Primitive for $rustType
{
const TypeTag: Type = Type::$variant;
fn fromValue(value: Value) -> Result<Self, FFIError>
{
match value {
Value::$variant(v) => Ok(v),
_ => Err(FFIError::Other(format!("expected {}, got {:?}", stringify!($variant), value))),
}
}
fn toValue(self) -> Value { Value::$variant(self) }
}
impl From<$rustType> for Value
{
fn from(v: $rustType) -> Self { Value::$variant(v) }
}
};
}
implFFIPrimitive!(u8, U8);
implFFIPrimitive!(u16, U16);
implFFIPrimitive!(u32, U32);
implFFIPrimitive!(u64, U64);
implFFIPrimitive!(usize, Usize);
implFFIPrimitive!(i8, I8);
implFFIPrimitive!(i16, I16);
implFFIPrimitive!(i32, I32);
implFFIPrimitive!(i64, I64);
implFFIPrimitive!(isize, Isize);
implFFIPrimitive!(f32, F32);
implFFIPrimitive!(f64, F64);
implFFIPrimitive!(bool, Bool);
impl Primitive for Pointer
{
const TypeTag: Type = Type::Pointer;
fn fromValue(value: Value) -> Result<Self, FFIError>
{
match value
{
Value::Pointer(addr) => Ok(Self(addr)),
_ => Err(FFIError::Other(format!("expected Pointer, got {:?}", value))),
}
}
fn toValue(self) -> Value { Value::Pointer(self.0) }
}
impl Primitive for ()
{
const TypeTag: Type = Type::None;
fn fromValue(value: Value) -> Result<Self, FFIError>
{
match value {
Value::None => Ok(()),
_ => Err(FFIError::Other(format!("expected None, got {:?}", value))),
}
}
fn toValue(self) -> Value { Value::None }
}
impl From<Pointer> for usize
{
fn from(p: Pointer) -> Self { p.0 }
}
impl From<Pointer> for Value
{
fn from(p: Pointer) -> Self { Self::Pointer(p.0) }
}
#[cfg(test)]
mod tests
{
use crate::ffi;
use crate::call;
use crate::callv;
use crate::ffi::value::{Pointer, Value};
#[test]
fn signedIntegers() -> ()
{
ffi!{
let libc: Library = Library::load("libc.so.6")?;
let resI8: i8 = call!(libc, "abs", -5 as i8)?;
assert!(matches!(resI8, 5));
let resI16: i16 = call!(libc, "abs", -15 as i16)?;
assert!(matches!(resI16, 15));
let resI32: i32 = call!(libc, "abs", -42 as i32)?;
assert!(matches!(resI32, 42));
let resI64: i64 = call!(libc, "labs", -100000 as i64)?;
assert!(matches!(resI64, 100000));
let resIsize: isize = call!(libc, "labs", -500 as isize)?;
assert!(matches!(resIsize, 500));
Ok(())
}.expect("Signed integers test failed");
}
#[test]
fn unsignedIntegers() -> ()
{
ffi!{
let libc: Library = Library::load("libc.so.6")?;
let resU8: u8 = call!(libc, "strnlen", Value::CString(b"a".to_vec()), 10 as u8)?;
assert!(matches!(resU8, 1));
let resU16: u16 = call!(libc, "strnlen", Value::CString(b"ab".to_vec()), 10 as u16)?;
assert!(matches!(resU16, 2));
let resU32: u32 = call!(libc, "strnlen", Value::CString(b"abc".to_vec()), 10 as u32)?;
assert!(matches!(resU32, 3));
let resU64: u64 = call!(libc, "strnlen", Value::CString(b"abcd".to_vec()), 10 as u64)?;
assert!(matches!(resU64, 4));
let resUsize: usize = call!(libc, "strnlen", Value::CString(b"abcde".to_vec()), 10 as usize)?;
assert!(matches!(resUsize, 5));
Ok(())
}.expect("Unsigned integers test failed");
}
#[test]
fn float() -> ()
{
let resultF32: f32 = ffi!{
let libm: Library = Library::load("libm.so.6")?;
Ok(call!(libm, "sqrtf", 16.0 as f32)?)
}.expect("FFI F32 call failed");
assert!((resultF32 - 4.0).abs() < f32::EPSILON);
let resultF64: f64 = ffi!{
let libm: Library = Library::load("libm.so.6")?;
Ok(call!(libm, "pow", 2.0 as f64, 3.0 as f64)?)
}.expect("FFI F64 call failed");
assert!((resultF64 - 8.0).abs() < f64::EPSILON);
}
#[test]
fn bool() -> ()
{
let result: bool = ffi!{
let libc: Library = Library::load("libc.so.6")?;
Ok(call!(libc, "isalpha", true)?)
}.expect("FFI Bool call failed");
assert!(!result);
}
#[test]
fn pointer() -> ()
{
let result: Pointer = ffi!{
let libc: Library = Library::load("libc.so.6")?;
Ok(call!(libc, "getenv", Value::CString(b"noSuchVar".to_vec()))?)
}.expect("FFI pointer call failed");
assert_eq!(result, Pointer(0));
}
#[test]
fn pointerRoundtrip() -> ()
{
let len: usize = ffi!{
let libc: Library = Library::load("libc.so.6")?;
let ptr: Pointer = call!(libc, "strdup", Value::CString(b"hello".to_vec()))?;
assert_ne!(ptr, Pointer(0));
let result: usize = call!(libc, "strlen", ptr)?;
callv!(libc, "free", ptr)?;
Ok(result)
}.expect("pointer roundtrip failed");
assert!(matches!(len, 5));
}
#[test]
fn cString() -> ()
{
let result: usize = ffi!{
let libc: Library = Library::load("libc.so.6")?;
Ok(call!(libc, "strlen", Value::CString(b"hello".to_vec()))?)
}.expect("FFI CString call failed");
assert_eq!(result, 5);
}
#[test]
fn string() -> ()
{
let result: usize = ffi!{
let libc: Library = Library::load("libc.so.6")?;
Ok(call!(libc, "strnlen", Value::String(b"hello world".to_vec()))?)
}.expect("FFI String call failed");
assert_eq!(result, 11);
}
#[test]
fn rawString() -> ()
{
let result: i32 = ffi!{
let libc: Library = Library::load("libc.so.6")?;
Ok(call!(libc, "atoi", Value::RawString(b"12345\0".to_vec()))?)
}.expect("FFI RawString call failed");
assert_eq!(result, 12345);
}
}