xo65 0.1.0

Library for parsing cc65 object files (xo65 format)
Documentation
use crate::parse::{parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult};
use crate::xo65::Xo65Bytes;

#[derive(Debug)]
pub struct LineInfoTable<'data> {
    bytes: Xo65Bytes<'data>,

    infos: Box<[LineInfo]>,
}

impl<'data> LineInfoTable<'data> {
    pub fn as_bytes(&self) -> &'data [u8] {
        self.bytes.as_inner()
    }

    pub fn count(&self) -> usize {
        self.infos.len()
    }

    pub fn get(&self, idx: usize) -> Option<&LineInfo> {
        self.infos.get(idx)
    }

    pub fn iter(
        &self,
    ) -> impl DoubleEndedIterator<Item = &LineInfo> + ExactSizeIterator + std::iter::FusedIterator + Clone
    {
        self.infos.iter()
    }

    pub(crate) fn parse(bytes: Xo65Bytes<'data>) -> ParseResult<Self> {
        let mut off = 0;

        let count = parse_uleb128_at(&bytes, &mut off)? as usize;
        let mut infos = Vec::<LineInfo>::with_capacity(count);

        for i in 0..count {
            let info = LineInfo::parse_at(&bytes, &mut off)
                .with_context(|| format!("line info {i} parse error"))?;
            infos.push(info);
        }

        Ok(Self {
            bytes,
            infos: infos.into(),
        })
    }
}

#[derive(Debug)]
pub struct LineInfo {
    file: u32,
    row: u32,
    col: u32,
    count: u32,
    kind: LineInfoKind,
    spans: Box<[u32]>,
}

impl LineInfo {
    pub fn file(&self) -> u32 {
        self.file
    }

    pub fn row(&self) -> u32 {
        self.row
    }

    pub fn col(&self) -> u32 {
        self.col
    }

    pub fn count(&self) -> u32 {
        self.count
    }

    pub fn kind(&self) -> LineInfoKind {
        self.kind
    }

    pub fn spans(&self) -> &[u32] {
        &self.spans
    }
}

impl ParseAt<'_> for LineInfo {
    fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
        let row = parse_uleb128_at(bytes, off)?;
        let col = parse_uleb128_at(bytes, off)?;
        let file = parse_uleb128_at(bytes, off)?;
        let (kind, count) = {
            let value = parse_uleb128_at(bytes, off)?;
            (LineInfoKind::new(value & 3), value >> 2)
        };
        let spans = parse_spans_at(bytes, off)?;

        Ok(Self {
            file,
            row,
            col,
            count,
            kind,
            spans,
        })
    }
}

fn parse_spans_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
    let count = parse_uleb128_at(bytes, off)? as usize;
    let mut spans = Vec::<u32>::with_capacity(count);

    for _ in 0..count {
        let span = parse_uleb128_at(bytes, off)?;
        spans.push(span);
    }

    Ok(spans.into())
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LineInfoKind {
    Asm = 0,
    Extern = 1,
    Macro = 2,
    MacroParam = 3,
}

impl LineInfoKind {
    fn new(inner: u32) -> Self {
        match inner {
            0 => Self::Asm,
            1 => Self::Extern,
            2 => Self::Macro,
            3 => Self::MacroParam,
            _ => unreachable!(),
        }
    }
}