use super::{ItemId, ItemIdLe};
use crate::parser::{Parser, ParserError, ParserMut};
use crate::types::{Leaf, TypeIndex, TypeIndexLe};
use crate::types::{PointerFlags, introduces_virtual};
use anyhow::Context;
use tracing::error;
use zerocopy::{LE, U32};
pub trait RecordVisitor {
fn is_empty(&self) -> bool;
fn peek_rest(&self) -> &[u8];
fn u16(&mut self) -> Result<u16, ParserError>;
fn u32(&mut self) -> Result<u32, ParserError>;
fn skip(&mut self, n: usize) -> Result<(), ParserError>;
fn item(&mut self) -> Result<(), ParserError>;
fn ty(&mut self) -> Result<(), ParserError>;
fn name_index(&mut self) -> Result<(), ParserError>;
fn number(&mut self) -> Result<(), ParserError>;
fn strz(&mut self) -> Result<(), ParserError>;
}
#[allow(missing_docs)]
pub trait IndexVisitorMut {
#[allow(unused_variables)]
fn type_index(&mut self, offset: usize, value: &mut TypeIndexLe) -> Result<(), ParserError> {
Ok(())
}
#[allow(unused_variables)]
fn item_id(&mut self, offset: usize, value: &mut ItemIdLe) -> Result<(), ParserError> {
Ok(())
}
#[allow(unused_variables)]
fn name_index(&mut self, offset: usize, value: &mut U32<LE>) -> Result<(), ParserError> {
Ok(())
}
}
#[allow(missing_docs)]
pub trait IndexVisitor {
#[allow(unused_variables)]
fn type_index(&mut self, offset: usize, value: TypeIndex) -> Result<(), ParserError> {
Ok(())
}
#[allow(unused_variables)]
fn item_id(&mut self, offset: usize, value: ItemId) -> Result<(), ParserError> {
Ok(())
}
#[allow(unused_variables)]
fn name_index(&mut self, offset: usize, value: u32) -> Result<(), ParserError> {
Ok(())
}
}
struct RefVisitor<'a, IV: IndexVisitor> {
parser: Parser<'a>,
original_len: usize,
index_visitor: IV,
}
impl<'a, IV: IndexVisitor> RecordVisitor for RefVisitor<'a, IV> {
fn is_empty(&self) -> bool {
self.parser.is_empty()
}
fn peek_rest(&self) -> &[u8] {
self.parser.peek_rest()
}
fn u16(&mut self) -> Result<u16, ParserError> {
self.parser.u16()
}
fn u32(&mut self) -> Result<u32, ParserError> {
self.parser.u32()
}
fn skip(&mut self, n: usize) -> Result<(), ParserError> {
self.parser.skip(n)
}
fn ty(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ti = self.parser.type_index()?;
self.index_visitor.type_index(offset, ti)?;
Ok(())
}
fn item(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ii = self.parser.u32()?;
self.index_visitor.item_id(offset, ii)?;
Ok(())
}
fn number(&mut self) -> Result<(), ParserError> {
self.parser.number()?;
Ok(())
}
fn strz(&mut self) -> Result<(), ParserError> {
self.parser.skip_strz()
}
fn name_index(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ni = self.parser.u32()?;
self.index_visitor.name_index(offset, ni)?;
Ok(())
}
}
struct MutVisitor<'a, IV: IndexVisitorMut> {
parser: ParserMut<'a>,
original_len: usize,
index_visitor: IV,
}
impl<'a, IV: IndexVisitorMut> RecordVisitor for MutVisitor<'a, IV> {
fn is_empty(&self) -> bool {
self.parser.is_empty()
}
fn peek_rest(&self) -> &[u8] {
self.parser.peek_rest()
}
fn u16(&mut self) -> Result<u16, ParserError> {
self.parser.u16()
}
fn u32(&mut self) -> Result<u32, ParserError> {
self.parser.u32()
}
fn skip(&mut self, n: usize) -> Result<(), ParserError> {
self.parser.skip(n)
}
fn ty(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ti: &mut TypeIndexLe = self.parser.get_mut()?;
self.index_visitor.type_index(offset, ti)?;
Ok(())
}
fn item(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ii: &mut ItemIdLe = self.parser.get_mut()?;
self.index_visitor.item_id(offset, ii)?;
Ok(())
}
fn number(&mut self) -> Result<(), ParserError> {
self.parser.skip_number()
}
fn strz(&mut self) -> Result<(), ParserError> {
self.parser.skip_strz()
}
fn name_index(&mut self) -> Result<(), ParserError> {
let offset = self.original_len - self.parser.len();
let ni: &mut U32<LE> = self.parser.get_mut()?;
self.index_visitor.name_index(offset, ni)?;
Ok(())
}
}
#[inline(never)]
pub fn visit_type_indexes_in_record_slice<IV: IndexVisitor>(
type_kind: Leaf,
record_data: &[u8],
index_visitor: IV,
) -> Result<(), anyhow::Error> {
let record_data_len = record_data.len();
let mut v = RefVisitor {
original_len: record_data.len(),
parser: Parser::new(record_data),
index_visitor,
};
visit_type_indexes_in_record(type_kind, &mut v).with_context(|| {
let offset = record_data_len - v.parser.len();
format!("at byte offset 0x{offset:x} {offset} within type record")
})
}
#[inline(never)]
pub fn visit_type_indexes_in_record_slice_mut<IV>(
type_kind: Leaf,
record_data: &mut [u8],
index_visitor: IV,
) -> Result<(), anyhow::Error>
where
IV: IndexVisitorMut,
{
let record_data_len = record_data.len();
let mut v = MutVisitor {
original_len: record_data.len(),
parser: ParserMut::new(record_data),
index_visitor,
};
visit_type_indexes_in_record(type_kind, &mut v).with_context(|| {
let offset = record_data_len - v.parser.len();
format!("at byte offset 0x{offset:x} {offset} within type record")
})
}
pub fn visit_type_indexes_in_record<V: RecordVisitor>(
type_kind: Leaf,
p: &mut V,
) -> Result<(), ParserError> {
match type_kind {
Leaf::LF_LABEL => {}
Leaf::LF_MODIFIER => {
p.ty()?;
}
Leaf::LF_POINTER => {
p.ty()?; let attr = PointerFlags(p.u32()?);
match attr.mode() {
2 => {
p.ty()?;
}
3 => {
p.ty()?;
}
_ => {}
}
}
Leaf::LF_ALIAS => {
p.ty()?;
}
Leaf::LF_ARRAY => {
p.ty()?; p.ty()?; }
Leaf::LF_CLASS | Leaf::LF_STRUCTURE => {
p.u16()?; p.skip(2)?; p.ty()?; p.ty()?; p.ty()?; }
Leaf::LF_ENUM => {
p.u16()?; p.skip(2)?; p.ty()?; p.ty()?; }
Leaf::LF_UNION => {
p.u16()?; p.skip(2)?; p.ty()?; }
Leaf::LF_PROCEDURE => {
p.ty()?; p.skip(4)?; p.ty()?; }
Leaf::LF_MFUNCTION => {
p.ty()?; p.ty()?; p.ty()?; p.skip(4)?; p.ty()?; }
Leaf::LF_ARGLIST => {
let num_args = p.u32()?;
for _ in 0..num_args {
p.ty()?;
}
}
Leaf::LF_FIELDLIST => {
let mut prev_item_kind = None;
loop {
let rest = p.peek_rest();
if rest.is_empty() {
break;
}
let mut padding_len = 0;
while padding_len < rest.len() && rest[padding_len] >= 0xf0 {
padding_len += 1;
}
if padding_len > 0 {
p.skip(padding_len)?;
}
if p.is_empty() {
break;
}
let item_kind = Leaf(p.u16()?);
let after = prev_item_kind.replace(item_kind);
match item_kind {
Leaf::LF_BCLASS => {
let _attr = p.u16()?;
p.ty()?; p.number()?; }
Leaf::LF_VBCLASS => {
let _attr = p.u16()?;
p.ty()?; p.ty()?; p.number()?; p.number()?; }
Leaf::LF_IVBCLASS => {
let _attr = p.u16()?;
p.ty()?; p.ty()?; p.number()?; p.number()?; }
Leaf::LF_ENUMERATE => {
let _attr = p.u16()?;
p.number()?; p.strz()?; }
Leaf::LF_FRIENDFCN => {
p.skip(2)?; p.ty()?; p.strz()?; }
Leaf::LF_INDEX => {
p.skip(2)?; p.ty()?; }
Leaf::LF_MEMBER => {
let _attr = p.u16()?;
p.ty()?; p.number()?; p.strz()?; }
Leaf::LF_STMEMBER => {
let _attr = p.u16()?;
p.ty()?; p.strz()?; }
Leaf::LF_METHOD => {
let _count = p.u16()?;
p.ty()?; p.strz()?; }
Leaf::LF_NESTEDTYPE => {
p.skip(2)?; p.ty()?; p.strz()?; }
Leaf::LF_VFUNCTAB => {
p.skip(2)?; p.ty()?; }
Leaf::LF_FRIENDCLS => {
p.skip(2)?; p.ty()?; }
Leaf::LF_ONEMETHOD => {
let attr = p.u16()?; p.ty()?; if introduces_virtual(attr) {
p.u32()?; }
p.strz()?; }
Leaf::LF_VFUNCOFF => {
p.u16()?; p.ty()?; p.u32()?; }
Leaf::LF_NESTEDTYPEEX => {
p.u16()?; p.ty()?; p.strz()?; }
unknown_item_kind => {
error!(
?unknown_item_kind,
?after,
"unrecognized item within LF_FIELDLIST"
);
break;
}
}
}
}
Leaf::LF_DERIVED => {
let count = p.u32()?;
for _ in 0..count {
p.ty()?;
}
}
Leaf::LF_BITFIELD => {
p.ty()?;
}
Leaf::LF_METHODLIST => {
while !p.is_empty() {
let attr = p.u16()?;
p.skip(2)?; p.ty()?;
if introduces_virtual(attr) {
p.skip(4)?; }
}
}
Leaf::LF_DIMCONU => {
p.ty()?; }
Leaf::LF_DIMCONLU => {
p.ty()?; }
Leaf::LF_DIMVARU => {
let rank = p.u32()?;
p.ty()?; for _ in 0..rank {
p.ty()?; }
}
Leaf::LF_VTSHAPE | Leaf::LF_PRECOMP | Leaf::LF_ENDPRECOMP | Leaf::LF_SKIP => {}
Leaf::LF_VFTPATH => {
let count = p.u32()?;
for _ in 0..count {
p.ty()?;
}
}
Leaf::LF_VFTABLE => {
p.ty()?; p.ty()?; }
Leaf::LF_CLASS2 | Leaf::LF_STRUCTURE2 | Leaf::LF_UNION2 | Leaf::LF_INTERFACE2 => {
p.skip(4)?; p.ty()?; p.ty()?; p.ty()?; }
Leaf::LF_FUNC_ID => {
p.item()?; p.ty()?; }
Leaf::LF_MFUNC_ID => {
p.ty()?; p.ty()?; }
Leaf::LF_BUILDINFO => {
let n = p.u16()?;
for _ in 0..n {
p.item()?;
}
}
Leaf::LF_SUBSTR_LIST => {
let count = p.u32()?;
for _ in 0..count {
p.item()?;
}
}
Leaf::LF_STRING_ID => {
p.item()?; }
Leaf::LF_UDT_SRC_LINE => {
p.ty()?;
p.name_index()?; }
Leaf::LF_UDT_MOD_SRC_LINE => {
p.ty()?;
p.name_index()?; }
_ => {
error!("unrecognized type kind: {:?}", type_kind);
return Err(ParserError::new());
}
}
Ok(())
}