r2fas 0.2.0

radare2 core plugin that loads FASM -s symbolic dumps for named labels, source lines, and comments
//! Symbol-use events from the optional FAS reference dump.

use crate::error::{FasError, Result};
use crate::fas::bytes;
use crate::fas::dump::DUMP_ROW_SIZE;
use crate::fas::header::Header;
use crate::fas::symbol::SYMBOL_SIZE;

/// Size of one symbol-reference record.
pub const REFERENCE_SIZE: usize = 8;

/// Stable index into the FAS symbol table.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolId(pub usize);

/// Stable index into the assembly dump.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DumpRowId(pub usize);

/// One event where an assembly row referenced a symbol.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SymbolReference {
    /// Stable record index in the reference table.
    pub id: usize,
    /// Referenced symbol-table record.
    pub symbol: SymbolId,
    /// Assembly-dump row at which the reference occurred.
    pub row: DumpRowId,
    /// Original byte offset into the symbol table.
    pub symbol_offset: u32,
    /// Original byte offset into the dump row array.
    pub dump_offset: u32,
}

/// Parse and validate the optional reference table.
pub fn parse_references(
    header: &Header,
    data: &[u8],
    symbol_count: usize,
    dump_row_count: usize,
) -> Result<Option<Vec<SymbolReference>>> {
    let Some(raw) = header.symbol_references(data)? else {
        return Ok(None);
    };
    if raw.len() % REFERENCE_SIZE != 0 {
        return Err(FasError::Geometry("references not a multiple of 8"));
    }
    raw.chunks_exact(REFERENCE_SIZE)
        .enumerate()
        .map(|(id, record)| {
            let symbol_offset = bytes::u32_at(record, 0)?;
            let dump_offset = bytes::u32_at(record, 4)?;
            let symbol = table_id(symbol_offset, SYMBOL_SIZE, symbol_count, "symbol reference")?;
            let row = table_id(dump_offset, DUMP_ROW_SIZE, dump_row_count, "dump reference")?;
            Ok(SymbolReference {
                id,
                symbol: SymbolId(symbol),
                row: DumpRowId(row),
                symbol_offset,
                dump_offset,
            })
        })
        .collect::<Result<Vec<_>>>()
        .map(Some)
}

fn table_id(offset: u32, stride: usize, count: usize, kind: &'static str) -> Result<usize> {
    let offset = usize::try_from(offset).map_err(|_| FasError::Geometry(kind))?;
    if offset % stride != 0 {
        return Err(FasError::Geometry(kind));
    }
    let id = offset / stride;
    if id >= count {
        return Err(FasError::Geometry(kind));
    }
    Ok(id)
}