diff --git a/src/fdt/node.rs b/src/fdt/node.rs index d1e553b..7c7a64c 100644 --- a/src/fdt/node.rs +++ b/src/fdt/node.rs @@ -10,10 +10,10 @@ use core::fmt::{self, Display, Formatter}; -use super::{FDT_TAGSIZE, Fdt, FdtToken}; +use super::{FDT_TAGSIZE, Fdt}; use crate::Node; use crate::fdt::property::{FdtPropIter, FdtProperty}; -use crate::standard::{AddressSpaceProperties, NodeStandard}; +use crate::standard::AddressSpaceProperties; /// A node in a flattened device tree. #[derive(Debug, Clone, Copy)] @@ -25,8 +25,27 @@ pub struct FdtNode<'a> { pub(crate) parent_address_space: AddressSpaceProperties, } -impl<'a> Node<'a> for FdtNode<'a> { - type Property = FdtProperty<'a>; +impl<'a> Node for FdtNode<'a> { + type Property<'b> + = FdtProperty<'a> + where + Self: 'b; + type Name<'b> + = &'a str + where + Self: 'b; + type Child<'b> + = FdtNode<'a> + where + Self: 'b; + type Properties<'b> + = FdtPropIter<'a> + where + Self: 'b; + type Children<'b> + = private::FdtChildIter<'a> + where + Self: 'b; /// Returns the name of this node. /// @@ -42,15 +61,19 @@ impl<'a> Node<'a> for FdtNode<'a> { .expect("Fdt should be valid") } - fn properties(&self) -> impl Iterator> + use<'a> { + fn name_without_address(&self) -> &'a str { + crate::util::name_without_address(self.name()) + } + + fn properties(&self) -> FdtPropIter<'a> { FdtPropIter::Start { fdt: self.fdt, offset: self.offset, } } - fn children(&self) -> impl Iterator> + use<'a> { - FdtChildIter::Start { node: *self } + fn children(&self) -> private::FdtChildIter<'a> { + private::FdtChildIter::Start { node: *self } } } @@ -97,75 +120,80 @@ impl Display for FdtNode<'_> { } } -/// An iterator over the children of a device tree node. -enum FdtChildIter<'a> { - Start { - node: FdtNode<'a>, - }, - Running { - fdt: Fdt<'a>, - offset: usize, - address_space: AddressSpaceProperties, - }, -} +mod private { + use crate::fdt::{FDT_TAGSIZE, Fdt, FdtNode, FdtToken}; + use crate::standard::{AddressSpaceProperties, NodeStandard}; + + #[derive(Debug, Clone)] + pub enum FdtChildIter<'a> { + Start { + node: FdtNode<'a>, + }, + Running { + fdt: Fdt<'a>, + offset: usize, + address_space: AddressSpaceProperties, + }, + } -impl<'a> Iterator for FdtChildIter<'a> { - type Item = FdtNode<'a>; - - fn next(&mut self) -> Option { - match self { - Self::Start { node } => { - let address_space = node.address_space(); - let mut offset = node.offset; - offset += FDT_TAGSIZE; // Skip FDT_BEGIN_NODE - offset = node - .fdt - .find_string_end(offset) - .expect("Fdt should be valid"); - offset = Fdt::align_tag_offset(offset); - *self = Self::Running { - fdt: node.fdt, + impl<'a> Iterator for FdtChildIter<'a> { + type Item = FdtNode<'a>; + + fn next(&mut self) -> Option { + match self { + Self::Start { node } => { + let address_space = node.address_space(); + let mut offset = node.offset; + offset += FDT_TAGSIZE; // Skip FDT_BEGIN_NODE + offset = node + .fdt + .find_string_end(offset) + .expect("Fdt should be valid"); + offset = Fdt::align_tag_offset(offset); + *self = Self::Running { + fdt: node.fdt, + offset, + address_space, + }; + self.next() + } + Self::Running { + fdt, offset, address_space, - }; - self.next() + } => Self::try_next(*fdt, offset, *address_space), } - Self::Running { - fdt, - offset, - address_space, - } => Self::try_next(*fdt, offset, *address_space), } } -} -impl<'a> FdtChildIter<'a> { - fn try_next( - fdt: Fdt<'a>, - offset: &mut usize, - parent_address_space: AddressSpaceProperties, - ) -> Option> { - loop { - let token = fdt.read_token(*offset).expect("Fdt should be valid"); - match token { - FdtToken::BeginNode => { - let node_offset = *offset; - *offset = fdt - .next_sibling_offset(*offset) - .expect("Fdt should be valid"); - return Some(FdtNode { - fdt, - offset: node_offset, - parent_address_space, - }); - } - FdtToken::Prop => { - *offset = fdt - .next_property_offset(*offset + FDT_TAGSIZE, false) - .expect("Fdt should be valid"); + impl<'a> FdtChildIter<'a> { + fn try_next( + fdt: Fdt<'a>, + offset: &mut usize, + parent_address_space: AddressSpaceProperties, + ) -> Option> { + loop { + let token = fdt.read_token(*offset).expect("Fdt should be valid"); + match token { + FdtToken::BeginNode => { + let node_offset = *offset; + *offset = fdt + .next_sibling_offset(*offset) + .expect("Fdt should be valid"); + return Some(FdtNode { + fdt, + offset: node_offset, + parent_address_space, + }); + } + FdtToken::Prop => { + *offset = fdt + .next_property_offset(*offset + FDT_TAGSIZE, false) + .expect("Fdt should be valid"); + } + FdtToken::EndNode | FdtToken::End => return None, + FdtToken::Nop => *offset += FDT_TAGSIZE, } - FdtToken::EndNode | FdtToken::End => return None, - FdtToken::Nop => *offset += FDT_TAGSIZE, } } } diff --git a/src/fdt/property.rs b/src/fdt/property.rs index 6258810..1a31768 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -16,13 +16,18 @@ use super::{FDT_TAGSIZE, Fdt, FdtToken}; use crate::Property; /// A property of a device tree node. -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct FdtProperty<'a> { name: &'a str, value: &'a [u8], } -impl<'a> Property<'a> for FdtProperty<'a> { +impl<'a> Property for FdtProperty<'a> { + type Str = &'a str; + type StrList = crate::values::FdtStringListIterator<'a>; + type PropEncodedArray = crate::values::PropEncodedArrayIterator<'a, N>; + type CellsItem = crate::Cells<'a>; + fn name(&self) -> &'a str { self.name } @@ -30,6 +35,8 @@ impl<'a> Property<'a> for FdtProperty<'a> { fn value(&self) -> &'a [u8] { self.value } + + crate::impl_property_methods!(get_value = |self| self.value); } impl FdtProperty<'_> { @@ -47,7 +54,7 @@ impl FdtProperty<'_> { .all(|&ch| ch.is_ascii_graphic() || ch == b' ' || ch == 0); let has_empty = self.value.windows(2).any(|window| window == [0, 0]); if is_printable && self.value.ends_with(&[0]) && !has_empty { - let mut strings = self.as_str_list(); + let mut strings = (*self).as_str_list(); if let Some(first) = strings.next() { write!(f, " = \"{first}\"")?; for s in strings { @@ -94,7 +101,8 @@ impl Display for FdtProperty<'_> { } /// An iterator over the properties of a device tree node. -pub(crate) enum FdtPropIter<'a> { +#[derive(Debug, Clone)] +pub enum FdtPropIter<'a> { Start { fdt: Fdt<'a>, offset: usize }, Running { fdt: Fdt<'a>, offset: usize }, } diff --git a/src/lib.rs b/src/lib.rs index 16fd613..205df23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,8 @@ //! // Find the child node and read its property. //! let child_node = fdt.find_node("/child").unwrap(); //! let prop = child_node.property("my-property").unwrap(); -//! assert_eq!(prop.as_str().unwrap(), "hello"); +//! let val: &str = prop.as_str().unwrap().as_ref(); +//! assert_eq!(val, "hello"); //! //! // Display the DTS //! println!("{}", fdt); @@ -88,20 +89,72 @@ pub mod memreserve; #[cfg(feature = "write")] pub mod model; pub mod standard; +mod util; mod validate; +mod values; -use core::ffi::CStr; use core::fmt::{self, Display, Formatter}; use core::ops::{BitOr, Shl}; -use zerocopy::{FromBytes, big_endian}; +use zerocopy::big_endian; use crate::error::{PropertyError, StandardError}; +macro_rules! impl_property_methods { + (get_value = |$self:ident| $get_value:expr) => { + fn as_cells(&$self) -> Result<$crate::Cells<'a>, $crate::error::PropertyError> { + Ok($crate::Cells( + <[zerocopy::big_endian::U32]>::ref_from_bytes($get_value) + .map_err(|_| $crate::error::PropertyError::InvalidLength)?, + )) + } + + fn as_str(&$self) -> Result<&'a str, $crate::error::PropertyError> { + let cstr = + core::ffi::CStr::from_bytes_with_nul($get_value).map_err(|_| $crate::error::PropertyError::InvalidString)?; + cstr.to_str().map_err(|_| $crate::error::PropertyError::InvalidString) + } + + fn as_str_list(&$self) -> $crate::values::FdtStringListIterator<'a> { + $crate::values::FdtStringListIterator { value: $get_value } + } + + fn as_prop_encoded_array( + &$self, + fields_cells: [usize; N], + ) -> Result<$crate::values::PropEncodedArrayIterator<'a, N>, $crate::error::PropertyError> { + $crate::values::PropEncodedArrayIterator::new($get_value, fields_cells) + } + }; +} +use impl_property_methods; + /// A device tree node. -pub trait Node<'a>: Sized { +pub trait Node: Sized { /// The type used for properties of the node. - type Property: Property<'a>; + type Property<'a>: Property + 'a + where + Self: 'a; + + /// The type used for child nodes. + type Child<'a>: Node + 'a + where + Self: 'a; + + /// The type used for the properties iterator. + type Properties<'a>: Iterator> + 'a + where + Self: 'a; + + /// The type used for the children iterator. + type Children<'a>: Iterator> + 'a + where + Self: 'a; + + /// The type used for the name of the node. + type Name<'a>: AsRef + Copy + 'a + where + Self: 'a; /// Returns the name of this node. /// @@ -118,25 +171,18 @@ pub trait Node<'a>: Sized { /// assert_eq!(child.name(), "child1"); /// ``` #[must_use] - fn name(&self) -> &'a str; + fn name(&self) -> Self::Name<'_>; /// Returns the name of this node without the unit address, if any. #[must_use] - fn name_without_address(&self) -> &'a str { - let name = self.name(); - if let Some((name, _)) = name.split_once('@') { - name - } else { - name - } - } + fn name_without_address(&self) -> Self::Name<'_>; /// Returns the property with the given name, if any. /// /// # Performance /// /// This default implementation iterates through all properties of the node. - fn property(&self, name: &str) -> Option { + fn property(&self, name: &str) -> Option> { self.properties().find(|property| property.name() == name) } @@ -156,7 +202,7 @@ pub trait Node<'a>: Sized { /// assert_eq!(props.next().unwrap().name(), "u64-prop"); /// assert_eq!(props.next().unwrap().name(), "str-prop"); /// ``` - fn properties(&self) -> impl Iterator + use<'a, Self>; + fn properties(&self) -> Self::Properties<'_>; /// Returns a child node by its name. /// @@ -168,13 +214,13 @@ pub trait Node<'a>: Sized { /// For example, searching for `memory` as a child of `/` would match either /// `/memory` or `/memory@4000`, while `memory@4000` would match only the /// latter. - fn child(&self, name: &str) -> Option { + fn child(&self, name: &str) -> Option> { let include_address = name.contains('@'); self.children().find(|child| { if include_address { - child.name() == name + child.name().as_ref() == name } else { - child.name_without_address() == name + child.name_without_address().as_ref() == name } }) } @@ -195,18 +241,30 @@ pub trait Node<'a>: Sized { /// assert_eq!(children.next().unwrap().name(), "child2@42"); /// assert!(children.next().is_none()); /// ``` - fn children(&self) -> impl Iterator + use<'a, Self>; + fn children(&self) -> Self::Children<'_>; } /// A property of a device tree node. -pub trait Property<'a>: Sized { +pub trait Property: Sized { + /// The type used for strings in the property. + type Str: AsRef; + + /// The type used for the strings iterator. + type StrList: Iterator; + + /// The type used for the prop-encoded-array iterator. + type PropEncodedArray: Iterator; + + /// The type used for the cells. + type CellsItem: Copy + ToCellInt; + /// Returns the name of this property. #[must_use] - fn name(&self) -> &'a str; + fn name(&self) -> &str; /// Returns the value of this property. #[must_use] - fn value(&self) -> &'a [u8]; + fn value(&self) -> &[u8]; /// Returns the value of this property as a `u32`. /// @@ -266,12 +324,7 @@ pub trait Property<'a>: Sized { /// /// Returns an error if the value of the property isn't a multiple of 4 /// bytes long. - fn as_cells(&self) -> Result, PropertyError> { - Ok(Cells( - <[big_endian::U32]>::ref_from_bytes(self.value()) - .map_err(|_| PropertyError::InvalidLength)?, - )) - } + fn as_cells(&self) -> Result; /// Returns the value of this property as a string. /// @@ -292,11 +345,7 @@ pub trait Property<'a>: Sized { /// let prop = node.property("str-prop").unwrap(); /// assert_eq!(prop.as_str().unwrap(), "hello world"); /// ``` - fn as_str(&self) -> Result<&'a str, PropertyError> { - let cstr = - CStr::from_bytes_with_nul(self.value()).map_err(|_| PropertyError::InvalidString)?; - cstr.to_str().map_err(|_| PropertyError::InvalidString) - } + fn as_str(&self) -> Result; /// Returns an iterator over the strings in this property. /// @@ -316,11 +365,7 @@ pub trait Property<'a>: Sized { /// assert_eq!(str_list.next(), Some("third")); /// assert_eq!(str_list.next(), None); /// ``` - fn as_str_list(&self) -> impl Iterator + use<'a, Self> { - FdtStringListIterator { - value: self.value(), - } - } + fn as_str_list(&self) -> Self::StrList; /// Returns an iterator over the elements of the property interpreted as a /// `prop-encoded-array`. @@ -336,43 +381,7 @@ pub trait Property<'a>: Sized { fn as_prop_encoded_array( &self, fields_cells: [usize; N], - ) -> Result; N]> + use<'a, N, Self>, PropertyError> { - let chunk_cells = fields_cells.iter().sum(); - let chunk_bytes = chunk_cells * size_of::(); - if !self.value().len().is_multiple_of(chunk_bytes) { - return Err(PropertyError::PropEncodedArraySizeMismatch { - size: self.value().len(), - chunk: chunk_cells, - }); - } - Ok(self.value().chunks_exact(chunk_bytes).map(move |chunk| { - let mut cells = <[big_endian::U32]>::ref_from_bytes(chunk) - .expect("chunk should be a multiple of 4 bytes because of chunks_exact"); - fields_cells.map(|field_cells| { - let field; - (field, cells) = cells.split_at(field_cells); - Cells(field) - }) - })) - } -} - -struct FdtStringListIterator<'a> { - value: &'a [u8], -} - -impl<'a> Iterator for FdtStringListIterator<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option { - if self.value.is_empty() { - return None; - } - let cstr = CStr::from_bytes_until_nul(self.value).ok()?; - let s = cstr.to_str().ok()?; - self.value = &self.value[s.len() + 1..]; - Some(s) - } + ) -> Result, PropertyError>; } /// An integer value split into several big-endian u32 parts. @@ -381,14 +390,21 @@ impl<'a> Iterator for FdtStringListIterator<'a> { #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Cells<'a>(pub(crate) &'a [big_endian::U32]); -impl Cells<'_> { +/// Trait for converting cells to integers. +pub trait ToCellInt { /// Converts the value to the given integer type. /// /// # Errors /// /// Returns [`StandardError::TooManyCells`] if the value has too many cells /// to fit in the given type. - pub fn to_int + Shl + BitOr>( + fn to_int + Shl + BitOr>( + self, + ) -> Result; +} + +impl ToCellInt for Cells<'_> { + fn to_int + Shl + BitOr>( self, ) -> Result { if size_of::() < self.0.len() * size_of::() { @@ -407,6 +423,12 @@ impl Cells<'_> { } } +impl AsRef<[big_endian::U32]> for Cells<'_> { + fn as_ref(&self) -> &[big_endian::U32] { + self.0 + } +} + impl Display for Cells<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("0x")?; diff --git a/src/model.rs b/src/model.rs index 39f8b8d..848aed4 100644 --- a/src/model.rs +++ b/src/model.rs @@ -81,7 +81,7 @@ impl DeviceTree { /// Returns an error if the root node of the `Fdt` cannot be parsed. #[must_use] pub fn from_fdt(fdt: &Fdt<'_>) -> Self { - let root = DeviceTreeNode::from(fdt.root()); + let root = DeviceTreeNode::from_node(&fdt.root()); let memory_reservations: Vec<_> = fdt.memory_reservations().collect(); DeviceTree { root, diff --git a/src/model/node.rs b/src/model/node.rs index 08391f0..06b571f 100644 --- a/src/model/node.rs +++ b/src/model/node.rs @@ -37,13 +37,106 @@ impl Default for DeviceTreeNode { } } -impl<'a> Node<'a> for &'a DeviceTreeNode { - type Property = &'a DeviceTreeProperty; +impl Node for DeviceTreeNode { + type Property<'a> + = &'a DeviceTreeProperty + where + Self: 'a; + type Name<'a> + = &'a str + where + Self: 'a; + type Child<'a> + = &'a DeviceTreeNode + where + Self: 'a; + type Properties<'a> + = private::DeviceTreePropertyRefIter<'a> + where + Self: 'a; + type Children<'a> + = private::DeviceTreeChildIter<'a> + where + Self: 'a; + + fn name(&self) -> &str { + (&self).name() + } + + fn name_without_address(&self) -> &str { + (&self).name_without_address() + } + + fn properties(&self) -> private::DeviceTreePropertyRefIter<'_> { + private::DeviceTreePropertyRefIter { + inner: self.properties.values(), + } + } + + /// Finds a child by its name and returns a reference to it. + /// + /// # Performance + /// + /// This is a constant-time operation if the `name` includes a unit-address, + /// or a linear-time operation if not. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::Node; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// + /// let mut node = DeviceTreeNode::new("my-node").unwrap(); + /// node.add_child(DeviceTreeNode::new("child").unwrap()); + /// let child = node.child("child"); + /// assert!(child.is_some()); + /// ``` + fn child(&self, name: &str) -> Option<&Self> { + if name.contains('@') { + self.children.get(name) + } else { + self.children() + .find(|child| child.name_without_address() == name) + } + } + + fn children(&self) -> private::DeviceTreeChildIter<'_> { + private::DeviceTreeChildIter { + inner: self.children.values(), + } + } +} + +impl<'a> Node for &'a DeviceTreeNode { + type Property<'b> + = &'b DeviceTreeProperty + where + Self: 'b; + type Name<'b> + = &'a str + where + Self: 'b; + type Child<'b> + = &'a DeviceTreeNode + where + Self: 'b; + type Properties<'b> + = private::DeviceTreePropertyRefIter<'b> + where + Self: 'b; + type Children<'b> + = private::DeviceTreeChildIter<'a> + where + Self: 'b; fn name(&self) -> &'a str { &self.name } + fn name_without_address(&self) -> &'a str { + crate::util::name_without_address(self.name()) + } + /// Finds a property by its name and returns a reference to it. /// /// # Performance @@ -65,9 +158,10 @@ impl<'a> Node<'a> for &'a DeviceTreeNode { self.properties.get(name) } - /// Returns an iterator over the properties of this node. - fn properties(&self) -> impl Iterator + use<'a> { - self.properties.values() + fn properties(&self) -> private::DeviceTreePropertyRefIter<'a> { + private::DeviceTreePropertyRefIter { + inner: self.properties.values(), + } } /// Finds a child by its name and returns a reference to it. @@ -88,18 +182,48 @@ impl<'a> Node<'a> for &'a DeviceTreeNode { /// let child = (&node).child("child"); /// assert!(child.is_some()); /// ``` - fn child(&self, name: &str) -> Option { - if name.contains('@') { - self.children.get(name) - } else { - self.children() - .find(|child| child.name_without_address() == name) + fn child(&self, name: &str) -> Option<&'a DeviceTreeNode> { + (*self).child(name) + } + + fn children(&self) -> private::DeviceTreeChildIter<'a> { + private::DeviceTreeChildIter { + inner: self.children.values(), } } +} + +mod private { + use alloc::string::String; + + use crate::model::{DeviceTreeNode, DeviceTreeProperty}; + + /// An iterator over the properties of a device tree node (by reference). + #[derive(Debug, Clone)] + pub struct DeviceTreePropertyRefIter<'a> { + pub inner: indexmap::map::Values<'a, String, DeviceTreeProperty>, + } - /// Returns an iterator over the children of this node. - fn children(&self) -> impl Iterator + use<'a> { - self.children.values() + impl<'a> Iterator for DeviceTreePropertyRefIter<'a> { + type Item = &'a DeviceTreeProperty; + + fn next(&mut self) -> Option { + self.inner.next() + } + } + + /// An iterator over the children of a device tree node. + #[derive(Debug, Clone)] + pub struct DeviceTreeChildIter<'a> { + pub inner: indexmap::map::Values<'a, String, DeviceTreeNode>, + } + + impl<'a> Iterator for DeviceTreeChildIter<'a> { + type Item = &'a DeviceTreeNode; + + fn next(&mut self) -> Option { + self.inner.next() + } } } @@ -117,7 +241,7 @@ impl DeviceTreeNode { /// use dtoolkit::model::DeviceTreeNode; /// /// let node = DeviceTreeNode::new("my-node").unwrap(); - /// assert_eq!((&node).name(), "my-node"); + /// assert_eq!(node.name(), "my-node"); /// ``` pub fn new(name: impl Into) -> Result { let name = name.into(); @@ -211,7 +335,7 @@ impl DeviceTreeNode { /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4]).unwrap()); /// let prop = node.remove_property("my-prop").unwrap(); /// assert_eq!((&prop).value(), &[1, 2, 3, 4]); - /// assert!((&node).property("my-prop").is_none()); + /// assert!(node.property("my-prop").is_none()); /// ``` pub fn remove_property(&mut self, name: &str) -> Option { self.properties.shift_remove(name) @@ -284,28 +408,29 @@ impl DeviceTreeNode { /// let mut node = DeviceTreeNode::new("my-node").unwrap(); /// node.add_child(DeviceTreeNode::new("child").unwrap()); /// let child = node.remove_child("child").unwrap(); - /// assert_eq!((&child).name(), "child"); - /// assert!((&node).child("child").is_none()); + /// assert_eq!(child.name(), "child"); + /// assert!(node.child("child").is_none()); /// ``` pub fn remove_child(&mut self, name: &str) -> Option { self.children.shift_remove(name) } } -impl<'a, T: Node<'a>> From for DeviceTreeNode { - fn from(node: T) -> Self { - let name = node.name().to_string(); +impl DeviceTreeNode { + /// Creates a new [`DeviceTreeNode`] from any type that implements [`Node`]. + pub fn from_node(node: &T) -> Self { + let name = node.name().as_ref().to_string(); let mut properties = IndexMap::with_hasher(default_hash_state()); - properties.extend( - node.properties() - .map(|prop| (prop.name().to_owned(), prop.into())), - ); + properties.extend(node.properties().map(|prop| { + let name = prop.name().to_owned(); + (name, DeviceTreeProperty::from_property(&prop)) + })); let mut children = IndexMap::with_hasher(default_hash_state()); - children.extend( - node.children() - .map(|child| (child.name().to_owned(), child.into())), - ); + children.extend(node.children().map(|child| { + let name = child.name().as_ref().to_owned(); + (name, DeviceTreeNode::from_node(&child)) + })); Self { name, diff --git a/src/model/property.rs b/src/model/property.rs index ea6f0ba..75ca248 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -10,8 +10,11 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::str; -use crate::Property; +use zerocopy::FromBytes; + use crate::error::ModelError; +use crate::values::{FdtStringListIterator, PropEncodedArrayIterator}; +use crate::{Cells, Property}; /// A mutable, in-memory representation of a device tree property. #[derive(Debug, Clone, PartialEq, Eq)] @@ -20,7 +23,12 @@ pub struct DeviceTreeProperty { value: Vec, } -impl<'a> Property<'a> for &'a DeviceTreeProperty { +impl<'a> Property for &'a DeviceTreeProperty { + type Str = &'a str; + type StrList = FdtStringListIterator<'a>; + type PropEncodedArray = PropEncodedArrayIterator<'a, N>; + type CellsItem = Cells<'a>; + fn name(&self) -> &'a str { &self.name } @@ -28,6 +36,8 @@ impl<'a> Property<'a> for &'a DeviceTreeProperty { fn value(&self) -> &'a [u8] { &self.value } + + crate::impl_property_methods!(get_value = |self| self.value.as_slice()); } impl DeviceTreeProperty { @@ -86,8 +96,10 @@ impl DeviceTreeProperty { } } -impl<'a, T: Property<'a>> From for DeviceTreeProperty { - fn from(prop: T) -> Self { +impl DeviceTreeProperty { + /// Creates a new [`DeviceTreeProperty`] from any type that implements + /// [`Property`]. + pub fn from_property(prop: &T) -> Self { let name = prop.name().to_string(); let value = prop.value().to_vec(); DeviceTreeProperty { name, value } diff --git a/src/standard.rs b/src/standard.rs index 6834226..3f2a2cc 100644 --- a/src/standard.rs +++ b/src/standard.rs @@ -29,10 +29,12 @@ pub(crate) const DEFAULT_ADDRESS_CELLS: u32 = 2; pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1; /// Methods to access standard properties on FDT nodes. -pub trait NodeStandard<'a>: Node<'a> { +pub trait NodeStandard: Node { /// Returns the value of the standard `compatible` property. #[must_use] - fn compatible(&self) -> Option + use<'a, Self>> { + fn compatible( + &self, + ) -> Option as Property>::Str> + '_> { self.property("compatible") .map(|property| property.as_str_list()) } @@ -41,19 +43,18 @@ pub trait NodeStandard<'a>: Node<'a> { /// given string. #[must_use] fn is_compatible(&self, compatible_filter: &str) -> bool { - if let Some(mut compatible) = self.compatible() { - compatible.any(|c| c == compatible_filter) - } else { - false + if let Some(prop) = self.property("compatible") { + return prop.as_str_list().any(|c| c.as_ref() == compatible_filter); } + false } /// Finds all child nodes with a `compatible` property containing the given /// string. fn find_compatible<'f>( - &self, + &'f self, compatible_filter: &'f str, - ) -> impl Iterator + use<'a, 'f, Self> { + ) -> impl Iterator> + 'f { self.children() .filter(move |child| child.is_compatible(compatible_filter)) } @@ -63,7 +64,7 @@ pub trait NodeStandard<'a>: Node<'a> { /// # Errors /// /// Returns an error if the value isn't a valid UTF-8 string. - fn model(&self) -> Result, PropertyError> { + fn model(&self) -> Result as Property>::Str>, PropertyError> { if let Some(model) = self.property("model") { Ok(Some(model.as_str()?)) } else { @@ -93,7 +94,7 @@ pub trait NodeStandard<'a>: Node<'a> { /// Returns an error if the value isn't a valid status. fn status(&self) -> Result { if let Some(status) = self.property("status") { - Ok(status.as_str()?.parse()?) + Ok(status.as_str()?.as_ref().parse()?) } else { Ok(Status::Okay) } @@ -155,7 +156,7 @@ pub trait NodeStandard<'a>: Node<'a> { } } -impl<'a, T: Node<'a>> NodeStandard<'a> for T {} +impl NodeStandard for T {} impl<'a> FdtNode<'a> { /// Returns the value of the standard `reg` property. @@ -164,7 +165,7 @@ impl<'a> FdtNode<'a> { /// /// Returns an error if the size of the value isn't a multiple of the /// expected number of address and size cells. - pub fn reg(&self) -> Result> + use<'a>>, PropertyError> { + pub fn reg(self) -> Result> + 'a>, PropertyError> { let address_cells = self.parent_address_space.address_cells as usize; let size_cells = self.parent_address_space.size_cells as usize; if let Some(property) = self.property("reg") { @@ -184,9 +185,7 @@ impl<'a> FdtNode<'a> { /// /// Returns an error if the size of the value isn't a multiple of the /// expected number of cells. - pub fn ranges( - &self, - ) -> Result> + use<'a>>, PropertyError> { + pub fn ranges(self) -> Result> + 'a>, PropertyError> { if let Some(property) = self.property("ranges") { Ok(Some( property @@ -208,9 +207,7 @@ impl<'a> FdtNode<'a> { /// /// Returns an error if the size of the value isn't a multiple of the /// expected number of cells. - pub fn dma_ranges( - &self, - ) -> Result> + use<'a>>, PropertyError> { + pub fn dma_ranges(self) -> Result> + 'a>, PropertyError> { if let Some(property) = self.property("dma-ranges") { Ok(Some( property diff --git a/src/standard/chosen.rs b/src/standard/chosen.rs index 1410f56..21900a8 100644 --- a/src/standard/chosen.rs +++ b/src/standard/chosen.rs @@ -42,14 +42,14 @@ impl Display for Chosen { } } -impl<'a, N: Node<'a>> Chosen { +impl Chosen { /// Returns the value of the standard `bootargs` property. /// /// # Errors /// /// Returns an [`PropertyError::InvalidString`] if the property's value is /// not a null-terminated string or contains invalid UTF-8. - pub fn bootargs(&self) -> Result, PropertyError> { + pub fn bootargs(&self) -> Result as Property>::Str>, PropertyError> { self.node .property("bootargs") .map(|value| value.as_str()) @@ -62,7 +62,7 @@ impl<'a, N: Node<'a>> Chosen { /// /// Returns an [`PropertyError::InvalidString`] if the property's value is /// not a null-terminated string or contains invalid UTF-8. - pub fn stdout_path(&self) -> Result, PropertyError> { + pub fn stdout_path(&self) -> Result as Property>::Str>, PropertyError> { self.node .property("stdout-path") .map(|value| value.as_str()) @@ -75,7 +75,7 @@ impl<'a, N: Node<'a>> Chosen { /// /// Returns an [`PropertyError::InvalidString`] if the property's value is /// not a null-terminated string or contains invalid UTF-8. - pub fn stdin_path(&self) -> Result, PropertyError> { + pub fn stdin_path(&self) -> Result as Property>::Str>, PropertyError> { self.node .property("stdin-path") .map(|value| value.as_str()) diff --git a/src/standard/cpus.rs b/src/standard/cpus.rs index c659f08..c878ed8 100644 --- a/src/standard/cpus.rs +++ b/src/standard/cpus.rs @@ -49,11 +49,11 @@ impl Display for Cpus { } } -impl<'a, N: Node<'a>> Cpus { +impl Cpus { /// Returns an iterator over the `/cpus/cpu@*` nodes. - pub fn cpus(&self) -> impl Iterator> + use<'a, N> { + pub fn cpus(&self) -> impl Iterator>> + '_ { self.node.children().filter_map(|child| { - if child.name_without_address() == "cpu" { + if child.name_without_address().as_ref() == "cpu" { Some(Cpu { node: child }) } else { None @@ -82,10 +82,10 @@ impl Display for Cpu { } } -impl<'a, N: Node<'a>> Cpu { +impl Cpu { /// Returns the value of the standard `enable-method` property if it is /// present. - pub fn enable_method(&self) -> Option> { + pub fn enable_method(&self) -> Option<<::Property<'_> as Property>::StrList> { Some(self.node.property("enable-method")?.as_str_list()) } @@ -112,7 +112,7 @@ impl<'a> Cpu> { /// Returns an error if a property's name or value cannot be read, or the /// `reg` property is missing, or the size of the value isn't a multiple of /// the expected number of address and size cells. - pub fn ids(&self) -> Result> + use<'a>, StandardError> { + pub fn ids(self) -> Result> + 'a, StandardError> { Ok(self .node .reg()? diff --git a/src/standard/memory.rs b/src/standard/memory.rs index b9e74b6..3fab39c 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -12,7 +12,7 @@ use core::ops::Deref; use crate::error::{PropertyError, StandardError}; use crate::fdt::{Fdt, FdtNode}; use crate::standard::Reg; -use crate::{Cells, Node, Property}; +use crate::{Node, Property, ToCellInt}; impl<'a> Fdt<'a> { /// Returns the `/memory` node. @@ -60,7 +60,7 @@ impl Display for Memory { } } -impl<'a, N: Node<'a>> Memory { +impl Memory { /// Returns the value of the standard `initial-mapped-area` property of the /// memory node. /// @@ -69,12 +69,12 @@ impl<'a, N: Node<'a>> Memory { /// Returns an error if the size of the value isn't a multiple of 5 cells. pub fn initial_mapped_area( &self, - ) -> Result + use<'a, N>>, PropertyError> { + ) -> Result + '_>, PropertyError> { if let Some(property) = self.node.property("initial-mapped-area") { Ok(Some( property .as_prop_encoded_array([2, 2, 1])? - .map(|chunk| InitialMappedArea::from_cells(chunk)), + .map(InitialMappedArea::from_cells), )) } else { Ok(None) @@ -110,7 +110,7 @@ impl InitialMappedArea { clippy::unwrap_used, reason = "The Cells passed are always the correct size" )] - fn from_cells([ea, pa, size]: [Cells; 3]) -> Self { + fn from_cells([ea, pa, size]: [C; 3]) -> Self { Self { effective_address: ea.to_int().unwrap(), physical_address: pa.to_int().unwrap(), @@ -139,7 +139,7 @@ impl Display for ReservedMemory { } } -impl<'a, N: Node<'a>> ReservedMemory { +impl ReservedMemory { /// Returns the value of the standard `size` property of the reserved memory /// node, if it is present. /// @@ -147,7 +147,7 @@ impl<'a, N: Node<'a>> ReservedMemory { /// /// Returns an error if the value of the property isn't a multiple of 4 /// bytes long. - pub fn size(&self) -> Result>, PropertyError> { + pub fn size(&self) -> Result as Property>::CellsItem>, PropertyError> { self.node .property("size") .map(|value| value.as_cells()) @@ -161,7 +161,9 @@ impl<'a, N: Node<'a>> ReservedMemory { /// /// Returns an error if the value of the property isn't a multiple of 4 /// bytes long. - pub fn alignment(&self) -> Result>, PropertyError> { + pub fn alignment( + &self, + ) -> Result as Property>::CellsItem>, PropertyError> { self.node .property("alignment") .map(|value| value.as_cells()) @@ -191,12 +193,10 @@ impl<'a> ReservedMemory> { /// /// Returns an error if the size of the value isn't a multiple of the /// expected number of address and size cells. - pub fn alloc_ranges( - &self, - ) -> Result> + use<'a>>, StandardError> { + pub fn alloc_ranges(self) -> Result> + 'a>, StandardError> { let address_cells = self.node.parent_address_space.address_cells as usize; let size_cells = self.node.parent_address_space.size_cells as usize; - if let Some(property) = self.property("alloc_ranges") { + if let Some(property) = self.node.property("alloc_ranges") { Ok(Some( property .as_prop_encoded_array([address_cells, size_cells])? diff --git a/src/standard/ranges.rs b/src/standard/ranges.rs index 91f29f1..a7ec079 100644 --- a/src/standard/ranges.rs +++ b/src/standard/ranges.rs @@ -9,8 +9,8 @@ use core::fmt::{self, Display, Formatter}; use core::ops::{BitOr, Shl}; -use crate::Cells; use crate::error::StandardError; +use crate::{Cells, ToCellInt}; /// One of the values of a `ranges` property. #[derive(Clone, Debug, Default, Eq, PartialEq)] diff --git a/src/standard/reg.rs b/src/standard/reg.rs index 0fa06c5..388c41f 100644 --- a/src/standard/reg.rs +++ b/src/standard/reg.rs @@ -9,8 +9,8 @@ use core::fmt::{self, Debug, Display, Formatter}; use core::ops::{BitOr, Shl}; -use crate::Cells; use crate::error::StandardError; +use crate::{Cells, ToCellInt}; /// The value of a `reg` property. #[derive(Clone, Copy, Default, Eq, PartialEq)] diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..1a51fee --- /dev/null +++ b/src/util.rs @@ -0,0 +1,8 @@ +#[must_use] +pub(crate) fn name_without_address(name: &str) -> &str { + if let Some((name, _)) = name.split_once('@') { + name + } else { + name + } +} diff --git a/src/values.rs b/src/values.rs new file mode 100644 index 0000000..13139c6 --- /dev/null +++ b/src/values.rs @@ -0,0 +1,66 @@ +use core::ffi::CStr; + +use zerocopy::{FromBytes, big_endian}; + +use crate::Cells; +use crate::error::PropertyError; + +/// An iterator over the strings in a device tree property. +#[derive(Debug, Clone)] +pub struct FdtStringListIterator<'a> { + pub(crate) value: &'a [u8], +} + +impl<'a> Iterator for FdtStringListIterator<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option { + if self.value.is_empty() { + return None; + } + let cstr = CStr::from_bytes_until_nul(self.value).ok()?; + let s = cstr.to_str().ok()?; + self.value = &self.value[s.len() + 1..]; + Some(s) + } +} + +/// An iterator over the prop-encoded-array elements of a device tree property. +#[derive(Debug, Clone)] +pub struct PropEncodedArrayIterator<'a, const N: usize> { + chunks: core::slice::ChunksExact<'a, u8>, + fields_cells: [usize; N], +} + +impl<'a, const N: usize> PropEncodedArrayIterator<'a, N> { + pub(crate) fn new(value: &'a [u8], fields_cells: [usize; N]) -> Result { + let chunk_cells: usize = fields_cells.iter().sum(); + let chunk_bytes = chunk_cells * size_of::(); + if !value.len().is_multiple_of(chunk_bytes) { + return Err(PropertyError::PropEncodedArraySizeMismatch { + size: value.len(), + chunk: chunk_cells, + }); + } + Ok(Self { + chunks: value.chunks_exact(chunk_bytes), + fields_cells, + }) + } +} + +impl<'a, const N: usize> Iterator for PropEncodedArrayIterator<'a, N> { + type Item = [Cells<'a>; N]; + + fn next(&mut self) -> Option { + let chunk = self.chunks.next()?; + let mut cells_slice = <[big_endian::U32]>::ref_from_bytes(chunk) + .expect("chunk should be a multiple of 4 bytes because of chunks_exact"); + + Some(self.fields_cells.map(|field_cells| { + let field; + (field, cells_slice) = cells_slice.split_at(field_cells); + Cells(field) + })) + } +} diff --git a/tests/fdt.rs b/tests/fdt.rs index da3eb2d..a92b53f 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -10,7 +10,7 @@ use dtoolkit::fdt::Fdt; #[cfg(feature = "write")] use dtoolkit::model::DeviceTree; use dtoolkit::standard::{InitialMappedArea, NodeStandard, Status}; -use dtoolkit::{Node, Property}; +use dtoolkit::{Node, Property, ToCellInt}; #[test] fn read_child_nodes() {