mod asbits;
pub mod model;
use uuid::Uuid;
pub use crate::model::ParameterFlags;
use crate::model::{
ArchivedBinaryExpression, ArchivedBinaryOperator, ArchivedBuffer, ArchivedBufferDirection,
ArchivedBufferPhase, ArchivedDatabase, ArchivedExpression, ArchivedFunction, ArchivedInterface,
ArchivedMetadata, ArchivedParameter, ArchivedType, ArchivedTypeKind, ArchivedUnaryExpression,
ArchivedUnaryOperator,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[expect(missing_docs, reason = "self-explanatory")]
pub enum Architecture {
X86,
X64,
}
#[derive(Debug, Clone, Copy)]
pub struct Database<'a> {
inner: &'a ArchivedDatabase,
}
impl<'a> Database<'a> {
pub fn from_bytes(data: &'a [u8]) -> Result<Self, rkyv::rancor::Error> {
let inner = rkyv::access(data)?;
Ok(Self { inner })
}
pub unsafe fn from_bytes_unchecked(data: &'a [u8]) -> Self {
let inner = unsafe { rkyv::access_unchecked(data) };
Self { inner }
}
pub fn bucket(&self, arch: Architecture) -> Metadata<'a> {
match arch {
Architecture::X86 => self.x86(),
Architecture::X64 => self.x64(),
}
}
pub fn x86(&self) -> Metadata<'a> {
Metadata {
inner: &self.inner.x86,
}
}
pub fn x64(&self) -> Metadata<'a> {
Metadata {
inner: &self.inner.x64,
}
}
}
#[derive(Debug)]
pub struct Metadata<'a> {
inner: &'a ArchivedMetadata,
}
impl<'a> Metadata<'a> {
pub fn functions(&self) -> impl ExactSizeIterator<Item = Function<'a>> {
self.inner.functions.iter().map(|inner| Function { inner })
}
pub fn interfaces(&self) -> impl ExactSizeIterator<Item = Interface<'a>> {
self.inner
.interfaces
.iter()
.map(|inner| Interface { inner })
}
pub fn function(&self, name: impl AsRef<str>) -> Option<Function<'a>> {
self.inner
.functions_by_name
.get(name.as_ref())
.map(|index| {
let index = index.to_native() as usize;
Function {
inner: &self.inner.functions[index],
}
})
}
pub fn interface(&self, name: impl AsRef<str>) -> Option<Interface<'a>> {
self.inner
.interfaces_by_name
.get(name.as_ref())
.map(|index| {
let index = index.to_native() as usize;
Interface {
inner: &self.inner.interfaces[index],
}
})
}
pub fn interface_by_uuid(&self, uuid: Uuid) -> Option<Interface<'a>> {
self.inner.interfaces_by_uuid.get(&uuid).map(|index| {
let index = index.to_native() as usize;
Interface {
inner: &self.inner.interfaces[index],
}
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct Function<'a> {
inner: &'a ArchivedFunction,
}
impl<'a> Function<'a> {
pub fn name(&self) -> &'a str {
&self.inner.name
}
pub fn parameters(&self) -> impl ExactSizeIterator<Item = Parameter<'a>> {
self.inner
.parameters
.iter()
.enumerate()
.map(|(index, inner)| Parameter {
index,
inner,
ty: Type {
repr: TypeRepr::Archived(&inner.ty),
},
})
}
pub fn input_parameters(&self) -> impl ExactSizeIterator<Item = Parameter<'a>> {
self.inner
.parameters
.iter()
.enumerate()
.map(|(index, inner)| {
let flags = ParameterFlags::from_bits_retain(inner.flags);
let is_output_only = flags.contains(ParameterFlags::HAS_OUT_ATTRIBUTE)
&& !flags.contains(ParameterFlags::HAS_IN_ATTRIBUTE);
Parameter {
index,
inner,
ty: Type {
repr: if is_output_only {
TypeRepr::VoidPointer
}
else {
TypeRepr::Archived(&inner.ty)
},
},
}
})
}
pub fn output_parameters(&self) -> impl ExactSizeIterator<Item = Parameter<'a>> {
let inner = self.inner;
inner.output_parameter_indices.iter().map(move |index| {
let index = *index as usize;
let archived = &inner.parameters[index];
Parameter {
index,
inner: archived,
ty: Type {
repr: TypeRepr::Archived(&archived.ty),
},
}
})
}
pub fn buffers(&self) -> impl ExactSizeIterator<Item = Buffer<'a>> {
self.inner.buffers.iter().map(|inner| Buffer { inner })
}
pub fn input_buffers(&self) -> impl ExactSizeIterator<Item = Buffer<'a>> {
let inner = self.inner;
inner.input_buffer_indices.iter().map(move |index| {
let index = *index as usize;
Buffer {
inner: &inner.buffers[index],
}
})
}
pub fn output_buffers(&self) -> impl ExactSizeIterator<Item = Buffer<'a>> {
let inner = self.inner;
inner.output_buffer_indices.iter().map(move |index| {
let index = *index as usize;
Buffer {
inner: &inner.buffers[index],
}
})
}
pub fn return_ty(&self) -> Type<'a> {
Type {
repr: TypeRepr::Archived(&self.inner.return_ty),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Interface<'a> {
inner: &'a ArchivedInterface,
}
impl<'a> Interface<'a> {
pub fn name(&self) -> &'a str {
&self.inner.name
}
pub fn uuid(&self) -> Uuid {
self.inner.uuid
}
pub fn base(&self) -> Option<&'a str> {
self.inner.base.as_deref()
}
pub fn methods(&self) -> impl ExactSizeIterator<Item = Function<'a>> {
self.inner.methods.iter().map(|inner| Function { inner })
}
}
#[derive(Debug, Clone, Copy)]
pub struct Parameter<'a> {
index: usize,
inner: &'a ArchivedParameter,
ty: Type<'a>,
}
impl<'a> Parameter<'a> {
pub fn index(&self) -> usize {
self.index
}
pub fn name(&self) -> Option<&'a str> {
self.inner.name.as_deref()
}
pub fn flags(&self) -> ParameterFlags {
ParameterFlags::from_bits_retain(self.inner.flags)
}
pub fn ty(&self) -> Type<'a> {
self.ty
}
}
#[derive(Debug, Clone, Copy)]
enum TypeRepr<'a> {
Archived(&'a ArchivedType),
VoidPointer,
}
#[derive(Debug, Clone, Copy)]
pub struct Type<'a> {
repr: TypeRepr<'a>,
}
impl<'a> Type<'a> {
pub fn indirections(&self) -> usize {
match self.repr {
TypeRepr::Archived(inner) => inner.indirections as usize,
TypeRepr::VoidPointer => 1,
}
}
pub fn name(&self) -> &'a str {
match self.repr {
TypeRepr::Archived(inner) => &inner.name,
TypeRepr::VoidPointer => "void",
}
}
pub fn kind(&self) -> TypeKind {
match self.repr {
TypeRepr::Archived(inner) => TypeKind::from_archived(&inner.kind),
TypeRepr::VoidPointer => TypeKind::Void,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[expect(missing_docs, reason = "self-explanatory")]
pub enum TypeKind {
Unknown,
Void,
Bool,
Char8,
Char16,
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
F32,
F64,
Custom(u8),
}
impl TypeKind {
fn from_archived(value: &ArchivedTypeKind) -> Self {
match value {
ArchivedTypeKind::Unknown => Self::Unknown,
ArchivedTypeKind::Void => Self::Void,
ArchivedTypeKind::Bool => Self::Bool,
ArchivedTypeKind::Char8 => Self::Char8,
ArchivedTypeKind::Char16 => Self::Char16,
ArchivedTypeKind::I8 => Self::I8,
ArchivedTypeKind::I16 => Self::I16,
ArchivedTypeKind::I32 => Self::I32,
ArchivedTypeKind::I64 => Self::I64,
ArchivedTypeKind::U8 => Self::U8,
ArchivedTypeKind::U16 => Self::U16,
ArchivedTypeKind::U32 => Self::U32,
ArchivedTypeKind::U64 => Self::U64,
ArchivedTypeKind::F32 => Self::F32,
ArchivedTypeKind::F64 => Self::F64,
ArchivedTypeKind::Custom(inner) => Self::Custom(*inner),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Buffer<'a> {
inner: &'a ArchivedBuffer,
}
impl<'a> Buffer<'a> {
pub fn parameter(&self) -> usize {
self.inner.parameter as usize
}
pub fn position(&self) -> usize {
self.inner.position as usize
}
pub fn length(&self) -> Expression<'a> {
Expression::from_archived(&self.inner.length)
}
pub fn direction(&self) -> BufferDirection {
BufferDirection::from_archived(&self.inner.direction)
}
pub fn phase(&self) -> BufferPhase {
BufferPhase::from_archived(&self.inner.phase)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BufferDirection {
Input,
Output,
}
impl BufferDirection {
fn from_archived(value: &ArchivedBufferDirection) -> Self {
match value {
ArchivedBufferDirection::Input => Self::Input,
ArchivedBufferDirection::Output => Self::Output,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BufferPhase {
Pre,
Post,
}
impl BufferPhase {
fn from_archived(value: &ArchivedBufferPhase) -> Self {
match value {
ArchivedBufferPhase::Pre => Self::Pre,
ArchivedBufferPhase::Post => Self::Post,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Expression<'a> {
Return,
Constant(u64),
Parameter(usize),
UnaryExpression(UnaryExpression<'a>),
BinaryExpression(BinaryExpression<'a>),
}
impl<'a> Expression<'a> {
fn from_archived(value: &'a ArchivedExpression) -> Self {
match value {
ArchivedExpression::Return => Self::Return,
ArchivedExpression::Constant(inner) => Self::Constant(inner.to_native()),
ArchivedExpression::Parameter(inner) => Self::Parameter(*inner as usize),
ArchivedExpression::UnaryExpression(inner) => {
Self::UnaryExpression(UnaryExpression { inner })
}
ArchivedExpression::BinaryExpression(inner) => {
Self::BinaryExpression(BinaryExpression { inner })
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct UnaryExpression<'a> {
inner: &'a ArchivedUnaryExpression,
}
impl<'a> UnaryExpression<'a> {
pub fn operator(&self) -> UnaryOperator {
UnaryOperator::from_archived(&self.inner.operator)
}
pub fn expression(&self) -> Expression<'a> {
Expression::from_archived(&self.inner.expression)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BinaryExpression<'a> {
inner: &'a ArchivedBinaryExpression,
}
impl<'a> BinaryExpression<'a> {
pub fn operator(&self) -> BinaryOperator {
BinaryOperator::from_archived(&self.inner.operator)
}
pub fn lhs(&self) -> Expression<'a> {
Expression::from_archived(&self.inner.lhs)
}
pub fn rhs(&self) -> Expression<'a> {
Expression::from_archived(&self.inner.rhs)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOperator {
Dereference,
}
impl UnaryOperator {
fn from_archived(value: &ArchivedUnaryOperator) -> Self {
match value {
ArchivedUnaryOperator::Dereference => Self::Dereference,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryOperator {
Add,
Subtract,
Multiply,
Divide,
}
impl BinaryOperator {
fn from_archived(value: &ArchivedBinaryOperator) -> Self {
match value {
ArchivedBinaryOperator::Add => Self::Add,
ArchivedBinaryOperator::Subtract => Self::Subtract,
ArchivedBinaryOperator::Multiply => Self::Multiply,
ArchivedBinaryOperator::Divide => Self::Divide,
}
}
}
#[cfg(test)]
mod tests {
use rkyv::rancor::Error as RkyvError;
use super::*;
use crate::model;
fn archive_with_function(function: model::Function) -> Vec<u8> {
let db = model::Database::builder()
.x86(model::Metadata::builder().functions(vec![function]).build())
.x64(model::Metadata::default())
.build();
rkyv::to_bytes::<RkyvError>(&db)
.expect("serialize")
.to_vec()
}
fn sample_function() -> model::Function {
model::Function::builder()
.name("Func")
.parameters(vec![
model::Parameter::builder()
.name("p_in")
.flags(model::ParameterFlags::HAS_IN_ATTRIBUTE)
.ty(model::Type::builder()
.indirections(1)
.name("DWORD")
.kind(model::TypeKind::U32)
.build())
.build(),
model::Parameter::builder()
.name("p_out")
.flags(model::ParameterFlags::HAS_OUT_ATTRIBUTE)
.ty(model::Type::builder()
.indirections(1)
.name("DWORD")
.kind(model::TypeKind::U32)
.build())
.build(),
model::Parameter::builder()
.name("p_inout")
.flags(
model::ParameterFlags::HAS_IN_ATTRIBUTE
| model::ParameterFlags::HAS_OUT_ATTRIBUTE,
)
.ty(model::Type::builder()
.indirections(1)
.name("DWORD")
.kind(model::TypeKind::U32)
.build())
.build(),
])
.return_ty(
model::Type::builder()
.name("BOOL")
.kind(model::TypeKind::I32)
.build(),
)
.build()
}
#[test]
fn input_parameters_substitutes_void_pointer_for_output_only() {
let bytes = archive_with_function(sample_function());
let db = Database::from_bytes(&bytes).expect("open");
let func = db.x86().function("Func").expect("Func");
let params = func.input_parameters().collect::<Vec<_>>();
assert_eq!(params.len(), 3);
assert_eq!(params[0].name(), Some("p_in"));
assert_eq!(params[0].ty().name(), "DWORD");
assert_eq!(params[0].ty().kind(), TypeKind::U32);
assert_eq!(params[0].ty().indirections(), 1);
assert_eq!(params[1].name(), Some("p_out"));
assert_eq!(params[1].ty().name(), "void");
assert_eq!(params[1].ty().kind(), TypeKind::Void);
assert_eq!(params[1].ty().indirections(), 1);
assert_eq!(params[2].name(), Some("p_inout"));
assert_eq!(params[2].ty().name(), "DWORD");
assert_eq!(params[2].ty().kind(), TypeKind::U32);
assert_eq!(params[2].ty().indirections(), 1);
}
#[test]
fn parameters_keeps_original_type_for_output_only() {
let bytes = archive_with_function(sample_function());
let db = Database::from_bytes(&bytes).expect("open");
let func = db.x86().function("Func").expect("Func");
let params = func.parameters().collect::<Vec<_>>();
assert_eq!(params.len(), 3);
assert_eq!(params[1].name(), Some("p_out"));
assert_eq!(params[1].ty().name(), "DWORD");
assert_eq!(params[1].ty().kind(), TypeKind::U32);
assert_eq!(params[1].ty().indirections(), 1);
}
}