use super::error::DtbError;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DtbToken {
BeginNode,
EndNode,
Property,
End,
}
impl DtbToken {
pub const FDT_BEGIN_NODE: u32 = 0x0000_0001;
pub const FDT_END_NODE: u32 = 0x0000_0002;
pub const FDT_PROP: u32 = 0x0000_0003;
pub const FDT_END: u32 = 0x0000_0009;
pub fn from_u32(value: u32) -> Result<Self, DtbError> {
match value {
Self::FDT_BEGIN_NODE => Ok(DtbToken::BeginNode),
Self::FDT_END_NODE => Ok(DtbToken::EndNode),
Self::FDT_PROP => Ok(DtbToken::Property),
Self::FDT_END => Ok(DtbToken::End),
_ => Err(DtbError::InvalidToken),
}
}
#[must_use]
pub fn to_u32(self) -> u32 {
match self {
DtbToken::BeginNode => Self::FDT_BEGIN_NODE,
DtbToken::EndNode => Self::FDT_END_NODE,
DtbToken::Property => Self::FDT_PROP,
DtbToken::End => Self::FDT_END,
}
}
pub fn parse(input: &[u8]) -> Result<(&[u8], Self), DtbError> {
if input.len() < 4 {
return Err(DtbError::MalformedHeader);
}
if (input.as_ptr() as usize) % 4 != 0 {
return Err(DtbError::AlignmentError);
}
let token_bytes: [u8; 4] = input[0..4]
.try_into()
.map_err(|_| DtbError::MalformedHeader)?;
let token_value = u32::from_be_bytes(token_bytes);
let token = Self::from_u32(token_value)?;
Ok((&input[4..], token))
}
#[must_use]
pub fn calculate_padding(offset: usize) -> usize {
(4 - (offset % 4)) % 4
}
#[must_use]
pub fn skip_padding(input: &[u8], current_offset: usize) -> &[u8] {
let padding = Self::calculate_padding(current_offset);
if padding > 0 && input.len() >= padding {
&input[padding..]
} else {
input
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_constants() {
assert_eq!(DtbToken::FDT_BEGIN_NODE, 0x00000001);
assert_eq!(DtbToken::FDT_END_NODE, 0x00000002);
assert_eq!(DtbToken::FDT_PROP, 0x00000003);
assert_eq!(DtbToken::FDT_END, 0x00000009);
}
#[test]
fn test_token_from_u32() {
assert_eq!(DtbToken::from_u32(0x00000001).unwrap(), DtbToken::BeginNode);
assert_eq!(DtbToken::from_u32(0x00000002).unwrap(), DtbToken::EndNode);
assert_eq!(DtbToken::from_u32(0x00000003).unwrap(), DtbToken::Property);
assert_eq!(DtbToken::from_u32(0x00000009).unwrap(), DtbToken::End);
assert!(DtbToken::from_u32(0x12345678).is_err());
}
#[test]
fn test_token_to_u32() {
assert_eq!(DtbToken::BeginNode.to_u32(), 0x00000001);
assert_eq!(DtbToken::EndNode.to_u32(), 0x00000002);
assert_eq!(DtbToken::Property.to_u32(), 0x00000003);
assert_eq!(DtbToken::End.to_u32(), 0x00000009);
}
#[test]
fn test_token_parse_begin_node() {
let data = [0x00, 0x00, 0x00, 0x01, 0x12, 0x34, 0x56, 0x78];
let result = DtbToken::parse(&data);
assert!(result.is_ok());
let (remaining, token) = result.unwrap();
assert_eq!(token, DtbToken::BeginNode);
assert_eq!(remaining, &[0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn test_token_parse_property() {
let data = [0x00, 0x00, 0x00, 0x03, 0xAB, 0xCD, 0xEF, 0x00];
let result = DtbToken::parse(&data);
assert!(result.is_ok());
let (remaining, token) = result.unwrap();
assert_eq!(token, DtbToken::Property);
assert_eq!(remaining, &[0xAB, 0xCD, 0xEF, 0x00]);
}
#[test]
fn test_token_parse_invalid() {
let data = [0x12, 0x34, 0x56, 0x78];
let result = DtbToken::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_token_parse_insufficient_data() {
let data = [0x00, 0x00, 0x00];
let result = DtbToken::parse(&data);
assert!(result.is_err());
}
#[test]
fn test_calculate_padding() {
assert_eq!(DtbToken::calculate_padding(0), 0);
assert_eq!(DtbToken::calculate_padding(1), 3);
assert_eq!(DtbToken::calculate_padding(2), 2);
assert_eq!(DtbToken::calculate_padding(3), 1);
assert_eq!(DtbToken::calculate_padding(4), 0);
assert_eq!(DtbToken::calculate_padding(5), 3);
}
#[test]
fn test_skip_padding() {
let data = [0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
let result = DtbToken::skip_padding(&data, 0);
assert_eq!(result, &data);
let result = DtbToken::skip_padding(&data[1..], 1);
assert_eq!(result, &data[4..]);
let result = DtbToken::skip_padding(&data[2..], 2);
assert_eq!(result, &data[4..]);
}
}