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 ScopeTable<'data> {
    bytes: Xo65Bytes<'data>,

    scopes: Box<[Scope]>,
}

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

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

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

    pub fn iter(
        &self,
    ) -> impl DoubleEndedIterator<Item = &Scope> + ExactSizeIterator + std::iter::FusedIterator + Clone
    {
        self.scopes.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 scopes = Vec::<Scope>::with_capacity(count);

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

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

#[derive(Debug)]
pub struct Scope {
    name: u32,
    kind: ScopeKind,
    parent: u32,
    lexical_level: u32,
    flags: ScopeFlags,
    size: Option<u32>,
    label: Option<u32>,
    spans: Box<[u32]>,
}

impl Scope {
    pub fn name(&self) -> u32 {
        self.name
    }

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

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

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

    pub fn flags(&self) -> ScopeFlags {
        self.flags
    }

    pub fn size(&self) -> Option<u32> {
        self.size
    }

    pub fn label(&self) -> Option<u32> {
        self.label
    }

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

impl ParseAt<'_> for Scope {
    fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
        let parent = parse_uleb128_at(bytes, off)?;
        let lexical_level = parse_uleb128_at(bytes, off)?;
        let flags = parse_uleb128_at(bytes, off).map(ScopeFlags)?;
        let kind = parse_uleb128_at(bytes, off).map(ScopeKind::new)?;
        let name = parse_uleb128_at(bytes, off)?;
        let size = flags
            .has_size()
            .then(|| parse_uleb128_at(bytes, off))
            .transpose()?;
        let label = flags
            .has_label()
            .then(|| parse_uleb128_at(bytes, off))
            .transpose()?;
        let spans = parse_spans_at(bytes, off)?;

        Ok(Self {
            name,
            kind,
            parent,
            lexical_level,
            flags,
            size,
            label,
            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())
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ScopeFlags(u32);

impl ScopeFlags {
    pub fn get(self) -> u32 {
        self.0
    }

    pub fn has_size(self) -> bool {
        (self.0 & (1 << 0)) != 0
    }

    pub fn has_label(self) -> bool {
        (self.0 & (1 << 1)) != 0
    }
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ScopeKind {
    Global = 0,
    File = 1,
    ScopeProc = 2,
    StructUnion = 3,
    Enum = 4,
    Undefined = 0xFF,
    Unknown(u32),
}

impl ScopeKind {
    fn new(inner: u32) -> Self {
        match inner {
            0 => Self::Global,
            1 => Self::File,
            2 => Self::ScopeProc,
            3 => Self::StructUnion,
            4 => Self::Enum,
            0xFF => Self::Undefined,
            _ => Self::Unknown(inner),
        }
    }
}