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;
pub const REFERENCE_SIZE: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolId(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DumpRowId(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SymbolReference {
pub id: usize,
pub symbol: SymbolId,
pub row: DumpRowId,
pub symbol_offset: u32,
pub dump_offset: u32,
}
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)
}