From 744c44f018fd074fbfabe11b41506333c5377c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Tue, 10 Feb 2026 15:51:24 +0000 Subject: [PATCH 1/6] refactor: use GATs in `Node` and `Property` traits This changes the `Node` and `Property` traits so that instead of requiring external lifetimes, the lifetimes are defined by the trait implementations themselves. This is mainly useful for unifying the `Fdt` APIs (which borrow the data) and `DeviceTree` APIs (which own the data). Furthermore, this will be useful when we introduce mutable `Fdt` in-place APIs, since `&mut self` methods cannot return non-self-borrowed data. With the old version this would require implementing the mutating traits for `&T` rather than for `T` as we did with `DeviceTree` trait impls. The main downsides is increased complexity of the traits. --- src/fdt/node.rs | 56 ++++++++++-- src/fdt/property.rs | 40 ++++++++- src/lib.rs | 162 ++++++++++++++++++---------------- src/model.rs | 2 +- src/model/node.rs | 196 +++++++++++++++++++++++++++++++++-------- src/model/property.rs | 41 ++++++++- src/standard.rs | 33 ++++--- src/standard/chosen.rs | 8 +- src/standard/cpus.rs | 12 +-- src/standard/memory.rs | 70 +++++++++++---- src/values.rs | 66 ++++++++++++++ tests/fdt.rs | 6 +- 12 files changed, 516 insertions(+), 176 deletions(-) create mode 100644 src/values.rs diff --git a/src/fdt/node.rs b/src/fdt/node.rs index d1e553b..32d7393 100644 --- a/src/fdt/node.rs +++ b/src/fdt/node.rs @@ -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> + = FdtChildIter<'a> + where + Self: 'b; /// Returns the name of this node. /// @@ -42,15 +61,24 @@ 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 { + let name = self.name(); + if let Some((name, _)) = name.split_once('@') { + name + } else { + 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) -> FdtChildIter<'a> { + FdtChildIter(FdtChildIterInner::Start { node: *self }) } } @@ -98,7 +126,19 @@ impl Display for FdtNode<'_> { } /// An iterator over the children of a device tree node. -enum FdtChildIter<'a> { +#[derive(Debug, Clone)] +pub struct FdtChildIter<'a>(FdtChildIterInner<'a>); + +impl<'a> Iterator for FdtChildIter<'a> { + type Item = FdtNode<'a>; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +#[derive(Debug, Clone)] +enum FdtChildIterInner<'a> { Start { node: FdtNode<'a>, }, @@ -109,7 +149,7 @@ enum FdtChildIter<'a> { }, } -impl<'a> Iterator for FdtChildIter<'a> { +impl<'a> Iterator for FdtChildIterInner<'a> { type Item = FdtNode<'a>; fn next(&mut self) -> Option { @@ -139,7 +179,7 @@ impl<'a> Iterator for FdtChildIter<'a> { } } -impl<'a> FdtChildIter<'a> { +impl<'a> FdtChildIterInner<'a> { fn try_next( fdt: Fdt<'a>, offset: &mut usize, diff --git a/src/fdt/property.rs b/src/fdt/property.rs index b6df84a..c0d72c6 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -8,22 +8,29 @@ //! A read-only API for inspecting a device tree property. +use core::ffi::CStr; use core::fmt::{self, Display, Formatter}; use zerocopy::{FromBytes, big_endian}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; use crate::Property; +use crate::error::PropertyError; /// 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], value_offset: usize, } -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 } @@ -31,6 +38,30 @@ impl<'a> Property<'a> for FdtProperty<'a> { fn value(&self) -> &'a [u8] { self.value } + + fn as_cells(&self) -> Result, PropertyError> { + Ok(crate::Cells( + <[big_endian::U32]>::ref_from_bytes(self.value) + .map_err(|_| PropertyError::InvalidLength)?, + )) + } + + 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_list(&self) -> crate::values::FdtStringListIterator<'a> { + crate::values::FdtStringListIterator { value: self.value } + } + + fn as_prop_encoded_array( + &self, + fields_cells: [usize; N], + ) -> Result, PropertyError> { + crate::values::PropEncodedArrayIterator::new(self.value, fields_cells) + } } impl FdtProperty<'_> { @@ -48,7 +79,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 { @@ -95,7 +126,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 290098f..0952a4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,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); @@ -87,19 +88,41 @@ pub mod memreserve; #[cfg(feature = "write")] pub mod model; pub mod standard; +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}; /// 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. /// @@ -116,25 +139,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) } @@ -154,7 +170,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. /// @@ -166,13 +182,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 } }) } @@ -193,18 +209,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`. /// @@ -264,12 +292,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. /// @@ -290,11 +313,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. /// @@ -314,11 +333,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`. @@ -334,43 +349,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. @@ -379,14 +358,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::() { @@ -405,6 +391,26 @@ impl Cells<'_> { } } +impl Cells<'_> { + /// 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>( + self, + ) -> Result { + ToCellInt::to_int(self) + } +} + +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 29e7c22..c84d696 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 48b5622..4b29792 100644 --- a/src/model/node.rs +++ b/src/model/node.rs @@ -36,13 +36,110 @@ 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"); + /// node.add_child(DeviceTreeNode::new("child")); + /// 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 { + if let Some((name, _)) = self.name.split_once('@') { + name + } else { + &self.name + } + } + /// Finds a property by its name and returns a reference to it. /// /// # Performance @@ -64,9 +161,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. @@ -87,18 +185,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>, + } + + impl<'a> Iterator for DeviceTreePropertyRefIter<'a> { + type Item = &'a DeviceTreeProperty; + + fn next(&mut self) -> Option { + self.inner.next() } } - /// Returns an iterator over the children of this node. - fn children(&self) -> impl Iterator + use<'a> { - self.children.values() + /// 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() + } } } @@ -112,7 +240,7 @@ impl DeviceTreeNode { /// use dtoolkit::model::DeviceTreeNode; /// /// let node = DeviceTreeNode::new("my-node"); - /// assert_eq!((&node).name(), "my-node"); + /// assert_eq!(node.name(), "my-node"); /// ``` #[must_use] pub fn new(name: impl Into) -> Self { @@ -170,7 +298,7 @@ impl DeviceTreeNode { /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); - /// assert_eq!((&node).property("my-prop").unwrap().value(), &[1, 2, 3, 4]); + /// assert_eq!(node.property("my-prop").unwrap().value(), &[1, 2, 3, 4]); /// ``` pub fn add_property(&mut self, property: DeviceTreeProperty) { self.properties @@ -194,7 +322,7 @@ impl DeviceTreeNode { /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); /// 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) @@ -221,10 +349,7 @@ impl DeviceTreeNode { /// node.add_child(DeviceTreeNode::new("child")); /// let child = node.child_mut("child").unwrap(); /// child.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); - /// assert_eq!( - /// (&*child).property("my-prop").unwrap().value(), - /// &[1, 2, 3, 4] - /// ); + /// assert_eq!(child.property("my-prop").unwrap().value(), &[1, 2, 3, 4]); /// ``` #[must_use] pub fn child_mut(&mut self, name: &str) -> Option<&mut DeviceTreeNode> { @@ -245,7 +370,7 @@ impl DeviceTreeNode { /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_child(DeviceTreeNode::new("child")); - /// assert_eq!((&node).child("child").unwrap().name(), "child"); + /// assert_eq!(node.child("child").unwrap().name(), "child"); /// ``` pub fn add_child(&mut self, child: DeviceTreeNode) { self.children.insert(child.name.clone(), child); @@ -267,28 +392,29 @@ impl DeviceTreeNode { /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_child(DeviceTreeNode::new("child")); /// 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 fc8288f..404194b 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -8,9 +8,13 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; +use core::ffi::CStr; use core::str; +use zerocopy::{FromBytes, big_endian}; + use crate::Property; +use crate::error::PropertyError; /// A mutable, in-memory representation of a device tree property. #[derive(Debug, Clone, PartialEq, Eq)] @@ -19,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 = crate::values::FdtStringListIterator<'a>; + type PropEncodedArray = crate::values::PropEncodedArrayIterator<'a, N>; + type CellsItem = crate::Cells<'a>; + fn name(&self) -> &'a str { &self.name } @@ -27,6 +36,30 @@ impl<'a> Property<'a> for &'a DeviceTreeProperty { fn value(&self) -> &'a [u8] { &self.value } + + fn as_cells(&self) -> Result, PropertyError> { + Ok(crate::Cells( + <[big_endian::U32]>::ref_from_bytes(&self.value) + .map_err(|_| PropertyError::InvalidLength)?, + )) + } + + 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_list(&self) -> crate::values::FdtStringListIterator<'a> { + crate::values::FdtStringListIterator { value: &self.value } + } + + fn as_prop_encoded_array( + &self, + fields_cells: [usize; N], + ) -> Result, PropertyError> { + crate::values::PropEncodedArrayIterator::new(&self.value, fields_cells) + } } impl DeviceTreeProperty { @@ -67,8 +100,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..a3d86e7 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. @@ -31,12 +31,9 @@ impl<'a> Fdt<'a> { /// Returns the `/reserved-memory/*` nodes, if any. #[must_use] - pub fn reserved_memory(self) -> Option>>> { - Some( - self.find_node("/reserved-memory")? - .children() - .map(|node| ReservedMemory { node }), - ) + pub fn reserved_memory(&self) -> Option>> { + self.find_node("/reserved-memory") + .map(ReservedMemoryNode::new) } } @@ -60,7 +57,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 +66,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 +107,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(), @@ -119,6 +116,38 @@ impl InitialMappedArea { } } +/// Typed wrapper for a `/reserved-memory` node. +#[derive(Clone, Copy, Debug)] +pub struct ReservedMemoryNode { + node: N, +} + +impl Deref for ReservedMemoryNode { + type Target = N; + + fn deref(&self) -> &Self::Target { + &self.node + } +} + +impl Display for ReservedMemoryNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.node.fmt(f) + } +} + +impl ReservedMemoryNode { + #[must_use] + fn new(node: N) -> Self { + Self { node } + } + + /// Returns an iterator over the `/reserved-memory/*` nodes. + pub fn reserved_memory(&self) -> impl Iterator>> + '_ { + self.node.children().map(ReservedMemory::new) + } +} + /// Typed wrapper for a `/reserved-memory/*` node. #[derive(Clone, Copy, Debug)] pub struct ReservedMemory { @@ -139,7 +168,12 @@ impl Display for ReservedMemory { } } -impl<'a, N: Node<'a>> ReservedMemory { +impl ReservedMemory { + #[must_use] + pub(super) fn new(node: N) -> Self { + Self { node } + } + /// Returns the value of the standard `size` property of the reserved memory /// node, if it is present. /// @@ -147,7 +181,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 +195,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 +227,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/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..1f447f9 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -263,7 +263,11 @@ fn reserved_memory() { let dtb = include_bytes!("dtb/test_pretty_print.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let reserved = fdt.reserved_memory().unwrap().collect::>(); + let reserved = fdt + .reserved_memory() + .unwrap() + .reserved_memory() + .collect::>(); assert!(reserved[0].reg().unwrap().is_none()); assert_eq!( From 1022cbf757bb0e2521e37585a2761b5583b42779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Tue, 14 Apr 2026 08:55:53 +0000 Subject: [PATCH 2/6] address review comments --- src/fdt/node.rs | 150 +++++++++++++++++++---------------------- src/fdt/property.rs | 26 +------ src/lib.rs | 45 +++++++++---- src/model/node.rs | 6 +- src/model/property.rs | 37 ++-------- src/standard/memory.rs | 40 ++--------- src/standard/ranges.rs | 2 +- src/standard/reg.rs | 2 +- src/util.rs | 8 +++ tests/fdt.rs | 8 +-- 10 files changed, 125 insertions(+), 199 deletions(-) create mode 100644 src/util.rs diff --git a/src/fdt/node.rs b/src/fdt/node.rs index 32d7393..6067ea5 100644 --- a/src/fdt/node.rs +++ b/src/fdt/node.rs @@ -43,7 +43,7 @@ impl<'a> Node for FdtNode<'a> { where Self: 'b; type Children<'b> - = FdtChildIter<'a> + = private::FdtChildIter<'a> where Self: 'b; @@ -62,12 +62,7 @@ impl<'a> Node for FdtNode<'a> { } fn name_without_address(&self) -> &'a str { - let name = self.name(); - if let Some((name, _)) = name.split_once('@') { - name - } else { - name - } + crate::util::name_without_address(self.name()) } fn properties(&self) -> FdtPropIter<'a> { @@ -77,8 +72,8 @@ impl<'a> Node for FdtNode<'a> { } } - fn children(&self) -> FdtChildIter<'a> { - FdtChildIter(FdtChildIterInner::Start { node: *self }) + fn children(&self) -> private::FdtChildIter<'a> { + private::FdtChildIter::Start { node: *self } } } @@ -125,87 +120,80 @@ impl Display for FdtNode<'_> { } } -/// An iterator over the children of a device tree node. -#[derive(Debug, Clone)] -pub struct FdtChildIter<'a>(FdtChildIterInner<'a>); - -impl<'a> Iterator for FdtChildIter<'a> { - type Item = FdtNode<'a>; - - fn next(&mut self) -> Option { - self.0.next() +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, + }, } -} - -#[derive(Debug, Clone)] -enum FdtChildIterInner<'a> { - Start { - node: FdtNode<'a>, - }, - Running { - fdt: Fdt<'a>, - offset: usize, - address_space: AddressSpaceProperties, - }, -} -impl<'a> Iterator for FdtChildIterInner<'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> FdtChildIterInner<'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 c0d72c6..039a3db 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -8,14 +8,12 @@ //! A read-only API for inspecting a device tree property. -use core::ffi::CStr; use core::fmt::{self, Display, Formatter}; use zerocopy::{FromBytes, big_endian}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; use crate::Property; -use crate::error::PropertyError; /// A property of a device tree node. #[derive(Debug, Clone, Copy, PartialEq)] @@ -39,29 +37,7 @@ impl<'a> Property for FdtProperty<'a> { self.value } - fn as_cells(&self) -> Result, PropertyError> { - Ok(crate::Cells( - <[big_endian::U32]>::ref_from_bytes(self.value) - .map_err(|_| PropertyError::InvalidLength)?, - )) - } - - 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_list(&self) -> crate::values::FdtStringListIterator<'a> { - crate::values::FdtStringListIterator { value: self.value } - } - - fn as_prop_encoded_array( - &self, - fields_cells: [usize; N], - ) -> Result, PropertyError> { - crate::values::PropEncodedArrayIterator::new(self.value, fields_cells) - } + crate::impl_property_methods!(get_value = |self| self.value); } impl FdtProperty<'_> { diff --git a/src/lib.rs b/src/lib.rs index 0952a4e..5b8636c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ pub mod memreserve; #[cfg(feature = "write")] pub mod model; pub mod standard; +mod util; mod values; use core::fmt::{self, Display, Formatter}; @@ -97,6 +98,36 @@ use zerocopy::big_endian; use crate::error::{PropertyError, StandardError}; +#[doc(hidden)] +#[macro_export] +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) + } + }; +} + /// A device tree node. pub trait Node: Sized { /// The type used for properties of the node. @@ -391,20 +422,6 @@ impl ToCellInt for Cells<'_> { } } -impl Cells<'_> { - /// 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>( - self, - ) -> Result { - ToCellInt::to_int(self) - } -} - impl AsRef<[big_endian::U32]> for Cells<'_> { fn as_ref(&self) -> &[big_endian::U32] { self.0 diff --git a/src/model/node.rs b/src/model/node.rs index 4b29792..45efd11 100644 --- a/src/model/node.rs +++ b/src/model/node.rs @@ -133,11 +133,7 @@ impl<'a> Node for &'a DeviceTreeNode { } fn name_without_address(&self) -> &'a str { - if let Some((name, _)) = self.name.split_once('@') { - name - } else { - &self.name - } + crate::util::name_without_address(self.name()) } /// Finds a property by its name and returns a reference to it. diff --git a/src/model/property.rs b/src/model/property.rs index 404194b..cc13458 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -8,13 +8,12 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; -use core::ffi::CStr; use core::str; -use zerocopy::{FromBytes, big_endian}; +use zerocopy::FromBytes; -use crate::Property; -use crate::error::PropertyError; +use crate::values::{FdtStringListIterator, PropEncodedArrayIterator}; +use crate::{Cells, Property}; /// A mutable, in-memory representation of a device tree property. #[derive(Debug, Clone, PartialEq, Eq)] @@ -25,9 +24,9 @@ pub struct DeviceTreeProperty { impl<'a> Property for &'a DeviceTreeProperty { type Str = &'a str; - type StrList = crate::values::FdtStringListIterator<'a>; - type PropEncodedArray = crate::values::PropEncodedArrayIterator<'a, N>; - type CellsItem = crate::Cells<'a>; + type StrList = FdtStringListIterator<'a>; + type PropEncodedArray = PropEncodedArrayIterator<'a, N>; + type CellsItem = Cells<'a>; fn name(&self) -> &'a str { &self.name @@ -37,29 +36,7 @@ impl<'a> Property for &'a DeviceTreeProperty { &self.value } - fn as_cells(&self) -> Result, PropertyError> { - Ok(crate::Cells( - <[big_endian::U32]>::ref_from_bytes(&self.value) - .map_err(|_| PropertyError::InvalidLength)?, - )) - } - - 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_list(&self) -> crate::values::FdtStringListIterator<'a> { - crate::values::FdtStringListIterator { value: &self.value } - } - - fn as_prop_encoded_array( - &self, - fields_cells: [usize; N], - ) -> Result, PropertyError> { - crate::values::PropEncodedArrayIterator::new(&self.value, fields_cells) - } + crate::impl_property_methods!(get_value = |self| self.value.as_slice()); } impl DeviceTreeProperty { diff --git a/src/standard/memory.rs b/src/standard/memory.rs index a3d86e7..dbf048e 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -29,11 +29,11 @@ impl<'a> Fdt<'a> { Ok(Memory { node }) } - /// Returns the `/reserved-memory/*` nodes, if any. - #[must_use] - pub fn reserved_memory(&self) -> Option>> { + /// Returns an iterator over the `/reserved-memory/*` nodes. + pub fn reserved_memory(&self) -> impl Iterator>> + '_ { self.find_node("/reserved-memory") - .map(ReservedMemoryNode::new) + .into_iter() + .flat_map(|node| node.children().map(ReservedMemory::new)) } } @@ -116,38 +116,6 @@ impl InitialMappedArea { } } -/// Typed wrapper for a `/reserved-memory` node. -#[derive(Clone, Copy, Debug)] -pub struct ReservedMemoryNode { - node: N, -} - -impl Deref for ReservedMemoryNode { - type Target = N; - - fn deref(&self) -> &Self::Target { - &self.node - } -} - -impl Display for ReservedMemoryNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.node.fmt(f) - } -} - -impl ReservedMemoryNode { - #[must_use] - fn new(node: N) -> Self { - Self { node } - } - - /// Returns an iterator over the `/reserved-memory/*` nodes. - pub fn reserved_memory(&self) -> impl Iterator>> + '_ { - self.node.children().map(ReservedMemory::new) - } -} - /// Typed wrapper for a `/reserved-memory/*` node. #[derive(Clone, Copy, Debug)] pub struct ReservedMemory { 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/tests/fdt.rs b/tests/fdt.rs index 1f447f9..f6dadbf 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() { @@ -263,11 +263,7 @@ fn reserved_memory() { let dtb = include_bytes!("dtb/test_pretty_print.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let reserved = fdt - .reserved_memory() - .unwrap() - .reserved_memory() - .collect::>(); + let reserved = fdt.reserved_memory().collect::>(); assert!(reserved[0].reg().unwrap().is_none()); assert_eq!( From 3c7b454731195e1ec1e065dca100c9cf3dd0a60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 14 May 2026 13:37:36 +0000 Subject: [PATCH 3/6] fix clippy warnings --- src/fdt/node.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fdt/node.rs b/src/fdt/node.rs index 6067ea5..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)] From 006c8e724b91e8cef4b475de18478f684f53dc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 14 May 2026 13:50:42 +0000 Subject: [PATCH 4/6] reformat --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d1ac281..d8b745b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,8 +90,8 @@ pub mod memreserve; pub mod model; pub mod standard; mod util; -mod values; mod validate; +mod values; use core::fmt::{self, Display, Formatter}; use core::ops::{BitOr, Shl}; From ddef4de3c3d5b3793c1c08a71b2dd2f92c1747f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Thu, 14 May 2026 14:21:14 +0000 Subject: [PATCH 5/6] export macro properly --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d8b745b..205df23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,8 +100,6 @@ use zerocopy::big_endian; use crate::error::{PropertyError, StandardError}; -#[doc(hidden)] -#[macro_export] macro_rules! impl_property_methods { (get_value = |$self:ident| $get_value:expr) => { fn as_cells(&$self) -> Result<$crate::Cells<'a>, $crate::error::PropertyError> { @@ -129,6 +127,7 @@ macro_rules! impl_property_methods { } }; } +use impl_property_methods; /// A device tree node. pub trait Node: Sized { From 80604bdde2ae308b5968695e3f7d209579fb82da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ma=C4=87kowski?= Date: Mon, 18 May 2026 14:26:01 +0000 Subject: [PATCH 6/6] restore reserved_memory --- src/standard/memory.rs | 18 ++++++++---------- tests/fdt.rs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/standard/memory.rs b/src/standard/memory.rs index dbf048e..3fab39c 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -29,11 +29,14 @@ impl<'a> Fdt<'a> { Ok(Memory { node }) } - /// Returns an iterator over the `/reserved-memory/*` nodes. - pub fn reserved_memory(&self) -> impl Iterator>> + '_ { - self.find_node("/reserved-memory") - .into_iter() - .flat_map(|node| node.children().map(ReservedMemory::new)) + /// Returns the `/reserved-memory/*` nodes, if any. + #[must_use] + pub fn reserved_memory(self) -> Option>>> { + Some( + self.find_node("/reserved-memory")? + .children() + .map(|node| ReservedMemory { node }), + ) } } @@ -137,11 +140,6 @@ impl Display for ReservedMemory { } impl ReservedMemory { - #[must_use] - pub(super) fn new(node: N) -> Self { - Self { node } - } - /// Returns the value of the standard `size` property of the reserved memory /// node, if it is present. /// diff --git a/tests/fdt.rs b/tests/fdt.rs index f6dadbf..a92b53f 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -263,7 +263,7 @@ fn reserved_memory() { let dtb = include_bytes!("dtb/test_pretty_print.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let reserved = fdt.reserved_memory().collect::>(); + let reserved = fdt.reserved_memory().unwrap().collect::>(); assert!(reserved[0].reg().unwrap().is_none()); assert_eq!(