use crate::structures::util::{read_i32, read_u32, read_uintptr};
pub const TFLAG_UNCOMMON: u8 = 0x01;
pub const TFLAG_EXTRA_STAR: u8 = 0x02;
pub const TFLAG_NAMED: u8 = 0x04;
pub const TFLAG_REGULAR_MEMORY: u8 = 0x08;
pub const TFLAG_GC_MASK_ON_DEMAND: u8 = 0x10;
pub const TFLAG_DIRECT_IFACE: u8 = 0x20;
#[derive(Debug, Clone, Copy, Default)]
pub struct AbiType {
pub size_: u64,
pub ptr_bytes: u64,
pub hash: u32,
pub tflag: u8,
pub align_: u8,
pub field_align_: u8,
pub kind_: u8,
pub equal: u64,
pub gcdata: u64,
pub str_off: i32,
pub ptr_to_this: i32,
}
impl AbiType {
pub fn size(ps: u8) -> usize {
(ps as usize).saturating_mul(4).saturating_add(16)
}
pub fn parse(data: &[u8], ps: u8) -> Option<Self> {
let p = ps as usize;
let total = Self::size(ps);
if data.len() < total {
return None;
}
let mut off: usize = 0;
let size_ = read_uintptr(data, off, ps)?;
off = off.checked_add(p)?;
let ptr_bytes = read_uintptr(data, off, ps)?;
off = off.checked_add(p)?;
let hash = read_u32(data, off)?;
off = off.checked_add(4)?;
let tflag = *data.get(off)?;
let align_ = *data.get(off.checked_add(1)?)?;
let field_align_ = *data.get(off.checked_add(2)?)?;
let kind_ = *data.get(off.checked_add(3)?)?;
off = off.checked_add(4)?;
let equal = read_uintptr(data, off, ps)?;
off = off.checked_add(p)?;
let gcdata = read_uintptr(data, off, ps)?;
off = off.checked_add(p)?;
let str_off = read_i32(data, off)?;
off = off.checked_add(4)?;
let ptr_to_this = read_i32(data, off)?;
Some(Self {
size_,
ptr_bytes,
hash,
tflag,
align_,
field_align_,
kind_,
equal,
gcdata,
str_off,
ptr_to_this,
})
}
pub fn kind(&self) -> u8 {
self.kind_ & 0x1f
}
pub fn has_uncommon(&self) -> bool {
self.tflag & TFLAG_UNCOMMON != 0
}
pub fn has_extra_star(&self) -> bool {
self.tflag & TFLAG_EXTRA_STAR != 0
}
pub fn is_named(&self) -> bool {
self.tflag & TFLAG_NAMED != 0
}
pub fn is_regular_memory(&self) -> bool {
self.tflag & TFLAG_REGULAR_MEMORY != 0
}
pub fn gc_mask_on_demand(&self) -> bool {
self.tflag & TFLAG_GC_MASK_ON_DEMAND != 0
}
pub fn is_direct_iface(&self) -> bool {
self.tflag & TFLAG_DIRECT_IFACE != 0 || self.kind_ & 0x20 != 0
}
pub fn gc_mask_va(&self) -> Option<u64> {
if self.gcdata == 0 || self.gc_mask_on_demand() {
None
} else {
Some(self.gcdata)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_abitype_64() -> Vec<u8> {
let mut buf = vec![0u8; 48];
buf[0..8].copy_from_slice(&0x100u64.to_le_bytes());
buf[8..16].copy_from_slice(&0x40u64.to_le_bytes());
buf[16..20].copy_from_slice(&0xDEADBEEFu32.to_le_bytes());
buf[20] = 0x05;
buf[21] = 8;
buf[22] = 8;
buf[23] = 0x39;
buf[40..44].copy_from_slice(&42i32.to_le_bytes());
buf[44..48].copy_from_slice(&(-1i32).to_le_bytes());
buf
}
#[test]
fn parse_64bit_buffer() {
let buf = make_abitype_64();
let t = AbiType::parse(&buf, 8).unwrap();
assert_eq!(t.size_, 0x100);
assert_eq!(t.ptr_bytes, 0x40);
assert_eq!(t.hash, 0xDEADBEEF);
assert_eq!(t.tflag, 0x05);
assert_eq!(t.align_, 8);
assert_eq!(t.field_align_, 8);
assert_eq!(t.kind_, 0x39);
assert_eq!(t.str_off, 42);
assert_eq!(t.ptr_to_this, -1);
}
#[test]
fn parse_32bit_buffer() {
let mut buf = vec![0u8; 32];
buf[0..4].copy_from_slice(&0x20u32.to_le_bytes());
buf[4..8].copy_from_slice(&0x08u32.to_le_bytes());
buf[8..12].copy_from_slice(&0xCAFEBABEu32.to_le_bytes());
buf[12] = 0x00;
buf[13] = 4;
buf[14] = 4;
buf[15] = 17;
buf[24..28].copy_from_slice(&100i32.to_le_bytes());
buf[28..32].copy_from_slice(&200i32.to_le_bytes());
let t = AbiType::parse(&buf, 4).unwrap();
assert_eq!(t.size_, 0x20);
assert_eq!(t.ptr_bytes, 0x08);
assert_eq!(t.hash, 0xCAFEBABE);
assert_eq!(t.kind_, 17);
assert_eq!(t.str_off, 100);
assert_eq!(t.ptr_to_this, 200);
}
#[test]
fn kind_masks_low_5_bits() {
let buf = make_abitype_64();
let t = AbiType::parse(&buf, 8).unwrap();
assert_eq!(t.kind(), 25);
}
#[test]
fn tflag_methods() {
let buf = make_abitype_64();
let t = AbiType::parse(&buf, 8).unwrap();
assert!(t.has_uncommon());
assert!(!t.has_extra_star());
assert!(t.is_named());
}
#[test]
fn too_short_buffer_returns_none() {
let buf = vec![0u8; 40]; assert!(AbiType::parse(&buf, 8).is_none());
}
}