use core::mem::size_of;
use core::ptr;
use num_traits::FromPrimitive;
use crate::base::DevTree;
use crate::error::{DevTreeError, Result};
use crate::priv_util::SliceRead;
use crate::spec::{fdt_prop_header, FdtTok, MAX_NODE_NAME_LEN};
use fallible_iterator::FallibleIterator;
pub unsafe fn next_devtree_token<'a>(
buf: &'a [u8],
off: &mut usize,
) -> Result<Option<ParsedTok<'a>>> {
debug_assert!(buf.as_ptr().add(*off) as usize % size_of::<u32>() == 0);
debug_assert!(buf.len() > (*off + size_of::<u32>()));
let fdt_tok_val = buf.unsafe_read_be_u32(*off)?;
*off += size_of::<u32>();
match FromPrimitive::from_u32(fdt_tok_val) {
Some(FdtTok::BeginNode) => {
let name = buf.nread_bstring0(*off, MAX_NODE_NAME_LEN - 1)?;
*off += name.len() + 1;
*off += buf.as_ptr().add(*off).align_offset(size_of::<u32>());
Ok(Some(ParsedTok::BeginNode(ParsedBeginNode { name })))
}
Some(FdtTok::Prop) => {
let header_slice = buf
.get(*off..*off + size_of::<fdt_prop_header>())
.ok_or(DevTreeError::ParseError)?;
assert_eq_align!(fdt_prop_header, u32);
#[allow(clippy::cast_ptr_alignment)]
let header = &*(header_slice.as_ptr() as *const fdt_prop_header);
let prop_len = u32::from(header.len) as usize;
*off += size_of::<fdt_prop_header>();
let prop_buf = buf
.get(*off..*off + prop_len)
.ok_or(DevTreeError::ParseError)?;
*off += prop_buf.len();
*off += buf.as_ptr().add(*off).align_offset(size_of::<u32>());
let name_offset = u32::from(header.nameoff) as usize;
if name_offset > buf.len() {
return Err(DevTreeError::ParseError);
}
let name_offset = name_offset;
Ok(Some(ParsedTok::Prop(ParsedProp {
prop_buf,
name_offset,
})))
}
Some(FdtTok::EndNode) => Ok(Some(ParsedTok::EndNode)),
Some(FdtTok::Nop) => Ok(Some(ParsedTok::Nop)),
Some(FdtTok::End) => Ok(None),
None => {
Err(DevTreeError::ParseError)
}
}
}
#[derive(Clone, Debug)]
pub struct ParsedBeginNode<'a> {
pub name: &'a [u8],
}
impl<'a> PartialEq for ParsedBeginNode<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.name, other.name)
}
}
#[derive(Clone, Debug)]
pub struct ParsedProp<'a> {
pub prop_buf: &'a [u8],
pub name_offset: usize,
}
impl<'a> PartialEq for ParsedProp<'a> {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.prop_buf, other.prop_buf) && self.name_offset == other.name_offset
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParsedTok<'a> {
BeginNode(ParsedBeginNode<'a>),
EndNode,
Prop(ParsedProp<'a>),
Nop,
}
#[derive(Debug, PartialEq)]
pub struct DevTreeParseIter<'r, 'dt: 'r> {
pub offset: usize,
pub fdt: &'r DevTree<'dt>,
}
impl<'r, 'dt: 'r> DevTreeParseIter<'r, 'dt> {
pub fn new(fdt: &'r DevTree<'dt>) -> Self {
Self {
offset: fdt.off_dt_struct(),
fdt,
}
}
}
impl<'dt, 'a: 'dt> FallibleIterator for DevTreeParseIter<'dt, 'a> {
type Error = DevTreeError;
type Item = ParsedTok<'a>;
fn next(&mut self) -> Result<Option<Self::Item>> {
unsafe { next_devtree_token(self.fdt.buf(), &mut self.offset) }
}
}