use crate::expr::Expr;
use crate::parse::{
parse_bail, parse_bytes_at, parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult,
};
use crate::xo65::Xo65Bytes;
#[derive(Debug)]
pub struct SectionTable<'data> {
bytes: Xo65Bytes<'data>,
sections: Box<[Section<'data>]>,
}
impl<'data> SectionTable<'data> {
pub fn as_bytes(&self) -> &'data [u8] {
self.bytes.as_inner()
}
pub fn count(&self) -> usize {
self.sections.len()
}
pub fn get(&self, idx: usize) -> Option<&Section<'data>> {
self.sections.get(idx)
}
pub fn iter(
&self,
) -> impl DoubleEndedIterator<Item = &Section<'data>>
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Clone {
self.sections.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 sections = Vec::<Section>::with_capacity(count);
for i in 0..count {
let section = Section::parse_at(&bytes, &mut off)
.with_context(|| format!("section {i} parse error"))?;
sections.push(section);
}
Ok(Self {
bytes,
sections: sections.into(),
})
}
}
#[derive(Debug)]
pub struct Section<'data> {
bytes: Xo65Bytes<'data>,
seg_name: u32,
flags: u32,
len: u32,
align: u32,
addr_size: u8,
fragment_count: u32,
fragments: Box<[SectionFragment<'data>]>,
}
impl<'data> Section<'data> {
pub fn as_bytes(&self) -> &'data [u8] {
self.bytes.as_inner()
}
pub fn segment_name(&self) -> u32 {
self.seg_name
}
pub fn flags(&self) -> u32 {
self.flags
}
pub fn len(&self) -> u32 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn align(&self) -> u32 {
self.align
}
pub fn addr_size(&self) -> u8 {
self.addr_size
}
pub fn fragment_count(&self) -> u32 {
self.fragment_count
}
pub fn fragments(&self) -> &[SectionFragment<'data>] {
&self.fragments
}
}
impl<'data> ParseAt<'data> for Section<'data> {
fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
let total_len = u32::parse_at(bytes, off)?;
let bytes = bytes.get(..*off + total_len as usize)?;
let name = parse_uleb128_at(&bytes, off)?;
let flags = parse_uleb128_at(&bytes, off)?;
let len = parse_uleb128_at(&bytes, off)?;
let align = parse_uleb128_at(&bytes, off)?;
let addr_size = u8::parse_at(&bytes, off)?;
let fragment_count = parse_uleb128_at(&bytes, off)?;
let mut fragments = Vec::<SectionFragment>::with_capacity(fragment_count as usize);
for i in 0..fragment_count {
let fragment = SectionFragment::parse_at(&bytes, off)
.with_context(|| format!("fragment {i} parse error"))?;
fragments.push(fragment);
}
Ok(Section {
bytes,
seg_name: name,
flags,
len,
align,
addr_size,
fragment_count,
fragments: fragments.into(),
})
}
}
#[derive(Debug)]
pub struct SectionFragment<'data> {
body: SectionFragmentBody<'data>,
lines: Box<[u32]>,
}
impl<'data> SectionFragment<'data> {
pub fn body(&self) -> &SectionFragmentBody<'data> {
&self.body
}
pub fn lines(&self) -> &[u32] {
&self.lines
}
}
impl<'data> ParseAt<'data> for SectionFragment<'data> {
fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
let body = SectionFragmentBody::parse_at(bytes, off)?;
let lines = parse_lines_at(bytes, off)?;
Ok(Self { body, lines })
}
}
#[derive(Debug)]
pub enum SectionFragmentBody<'data> {
Literal(&'data [u8]),
ExprU8(Expr),
ExprU16(Expr),
ExprU24(Expr),
ExprU32(Expr),
ExprI8(Expr),
ExprI16(Expr),
ExprI24(Expr),
ExprI32(Expr),
Fill(u32),
}
impl<'data> ParseAt<'data> for SectionFragmentBody<'data> {
fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
let ty = u8::parse_at(bytes, off)?;
let fragment = match ty & 0x38 {
0x00 => parse_frag_body_lit_at(bytes, off)?,
0x08 => parse_frag_body_expru_at(bytes, off, ty & 7)?,
0x10 => parse_frag_body_expri_at(bytes, off, ty & 7)?,
0x20 => parse_frag_body_fill_at(bytes, off)?,
_ => parse_bail!("invalid fragment type: {ty:#X}"),
};
Ok(fragment)
}
}
fn parse_frag_body_lit_at<'data>(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
) -> ParseResult<SectionFragmentBody<'data>> {
let len = parse_uleb128_at(bytes, off)? as usize;
let lit = parse_bytes_at(bytes, off, len)?;
Ok(SectionFragmentBody::Literal(lit))
}
fn parse_frag_body_expru_at<'data>(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
size: u8,
) -> ParseResult<SectionFragmentBody<'data>> {
let expr = Expr::parse_at(bytes, off)?;
let fragment = match size {
1 => SectionFragmentBody::ExprU8(expr),
2 => SectionFragmentBody::ExprU16(expr),
3 => SectionFragmentBody::ExprU24(expr),
4 => SectionFragmentBody::ExprU32(expr),
_ => parse_bail!("invalid unsigned expr size: {size}"),
};
Ok(fragment)
}
fn parse_frag_body_expri_at<'data>(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
size: u8,
) -> ParseResult<SectionFragmentBody<'data>> {
let expr = Expr::parse_at(bytes, off)?;
let fragment = match size {
1 => SectionFragmentBody::ExprI8(expr),
2 => SectionFragmentBody::ExprI16(expr),
3 => SectionFragmentBody::ExprI24(expr),
4 => SectionFragmentBody::ExprI32(expr),
_ => parse_bail!("invalid signed expr size: {size}"),
};
Ok(fragment)
}
fn parse_frag_body_fill_at<'data>(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
) -> ParseResult<SectionFragmentBody<'data>> {
let len = parse_uleb128_at(bytes, off)?;
Ok(SectionFragmentBody::Fill(len))
}
fn parse_lines_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
let count = parse_uleb128_at(bytes, off)? as usize;
let mut lines = Vec::<u32>::with_capacity(count);
for _ in 0..count {
let line = parse_uleb128_at(bytes, off)?;
lines.push(line);
}
Ok(lines.into())
}