use crate::format::superblock::{decode_symbol_table_entry, SymbolTableEntry};
use crate::format::{FormatError, FormatResult};
pub const SNOD_SIGNATURE: [u8; 4] = *b"SNOD";
#[derive(Debug, Clone)]
pub struct SymbolTableNode {
pub entries: Vec<SymbolTableEntry>,
}
impl SymbolTableNode {
pub fn decode(buf: &[u8], sizeof_addr: usize, sizeof_size: usize) -> FormatResult<Self> {
if buf.len() < 8 {
return Err(FormatError::BufferTooShort {
needed: 8,
available: buf.len(),
});
}
if buf[0..4] != SNOD_SIGNATURE {
return Err(FormatError::InvalidSignature);
}
let version = buf[4];
if version != 1 {
return Err(FormatError::InvalidVersion(version));
}
let num_symbols = u16::from_le_bytes([buf[6], buf[7]]) as usize;
let entry_size = sizeof_size + sizeof_addr + 4 + 4 + 16;
let needed = 8 + num_symbols * entry_size;
if buf.len() < needed {
return Err(FormatError::BufferTooShort {
needed,
available: buf.len(),
});
}
let mut pos = 8;
let mut entries = Vec::with_capacity(num_symbols);
for _ in 0..num_symbols {
let entry = decode_symbol_table_entry(buf, &mut pos, sizeof_addr, sizeof_size)?;
entries.push(entry);
}
Ok(SymbolTableNode { entries })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::UNDEF_ADDR;
fn build_snod(
entries: &[(u64, u64, u32, u64, u64)],
sizeof_addr: usize,
sizeof_size: usize,
) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&SNOD_SIGNATURE);
buf.push(1); buf.push(0); buf.extend_from_slice(&(entries.len() as u16).to_le_bytes());
for &(name_offset, obj_header_addr, cache_type, btree_addr, heap_addr) in entries {
buf.extend_from_slice(&name_offset.to_le_bytes()[..sizeof_size]);
buf.extend_from_slice(&obj_header_addr.to_le_bytes()[..sizeof_addr]);
buf.extend_from_slice(&cache_type.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
if cache_type == 1 {
buf.extend_from_slice(&btree_addr.to_le_bytes()[..sizeof_addr]);
buf.extend_from_slice(&heap_addr.to_le_bytes()[..sizeof_addr]);
let used = 2 * sizeof_addr;
if used < 16 {
buf.extend_from_slice(&vec![0u8; 16 - used]);
}
} else {
buf.extend_from_slice(&[0u8; 16]);
}
}
buf
}
#[test]
fn decode_basic() {
let snod = build_snod(
&[
(8, 0x100, 0, UNDEF_ADDR, UNDEF_ADDR), (16, 0x200, 1, 0x300, 0x400), ],
8,
8,
);
let node = SymbolTableNode::decode(&snod, 8, 8).unwrap();
assert_eq!(node.entries.len(), 2);
assert_eq!(node.entries[0].name_offset, 8);
assert_eq!(node.entries[0].obj_header_addr, 0x100);
assert_eq!(node.entries[0].cache_type, 0);
assert_eq!(node.entries[1].cache_type, 1);
assert_eq!(node.entries[1].btree_addr, 0x300);
assert_eq!(node.entries[1].heap_addr, 0x400);
}
#[test]
fn decode_empty() {
let snod = build_snod(&[], 8, 8);
let node = SymbolTableNode::decode(&snod, 8, 8).unwrap();
assert!(node.entries.is_empty());
}
#[test]
fn decode_bad_sig() {
let mut snod = build_snod(&[], 8, 8);
snod[0] = b'X';
assert!(matches!(
SymbolTableNode::decode(&snod, 8, 8).unwrap_err(),
FormatError::InvalidSignature
));
}
#[test]
fn decode_bad_version() {
let mut snod = build_snod(&[], 8, 8);
snod[4] = 2;
assert!(matches!(
SymbolTableNode::decode(&snod, 8, 8).unwrap_err(),
FormatError::InvalidVersion(2)
));
}
#[test]
fn decode_too_short() {
assert!(matches!(
SymbolTableNode::decode(&[0u8; 4], 8, 8).unwrap_err(),
FormatError::BufferTooShort { .. }
));
}
#[test]
fn decode_4byte() {
let snod = build_snod(&[(4, 0x80, 0, UNDEF_ADDR, UNDEF_ADDR)], 4, 4);
let node = SymbolTableNode::decode(&snod, 4, 4).unwrap();
assert_eq!(node.entries.len(), 1);
assert_eq!(node.entries[0].name_offset, 4);
assert_eq!(node.entries[0].obj_header_addr, 0x80);
}
}