From b956ce06b98a80a5153ef2fb7a478a1555e4f30a Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Sat, 15 Oct 2022 17:47:47 +0200 Subject: [PATCH 1/6] Add depth tracking to crate::base::iters::DevTreeIter This will be useful for implementing relative navigation over nodes. --- src/base/iters.rs | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/base/iters.rs b/src/base/iters.rs index a853918..a625ea4 100644 --- a/src/base/iters.rs +++ b/src/base/iters.rs @@ -75,6 +75,11 @@ pub struct DevTreeIter<'a, 'dt: 'a> { /// Current offset into the flattened dt_struct section of the device tree. offset: usize, + + /// The depth we are currently parsing at. 0 is the level of the + /// root node, -1 is the level of an imaginary parent of our root + /// element and going one element down increases the depth by 1. + depth: isize, pub(crate) fdt: &'a DevTree<'dt>, } @@ -126,6 +131,10 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { Self { offset: fdt.off_dt_struct(), current_prop_parent_off: None, + // Initially we haven't parsed the root node, so if 0 is + // supposed to be the root level, we are one level up from + // that. + depth: -1, fdt, } } @@ -135,6 +144,7 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { fdt: self.fdt, current_prop_parent_off: Some(offset), offset: offset.get(), + depth: self.depth, }) } @@ -146,7 +156,7 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { None } - pub fn next_item(&mut self) -> Result>> { + fn next_item_with_depth(&mut self) -> Result, isize)>> { loop { let old_offset = self.offset; // Safe because we only pass offsets which are returned by next_devtree_token. @@ -154,12 +164,16 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { match res { Some(ParsedTok::BeginNode(node)) => { + self.depth += 1; self.current_prop_parent_off = unsafe { Some(NonZeroUsize::new_unchecked(old_offset)) }; - return Ok(Some(DevTreeItem::Node(DevTreeNode { - parse_iter: self.clone(), - name: from_utf8(node.name).map_err(|e| e.into()), - }))); + return Ok(Some(( + DevTreeItem::Node(DevTreeNode { + parse_iter: self.clone(), + name: from_utf8(node.name).map_err(|e| e.into()), + }), + self.depth, + ))); } Some(ParsedTok::Prop(prop)) => { // Prop must come after a node. @@ -168,16 +182,20 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { None => return Err(DevTreeError::ParseError), }; - return Ok(Some(DevTreeItem::Prop(DevTreeProp::new( - prev_node, - prop.prop_buf, - prop.name_offset, - )))); + return Ok(Some(( + DevTreeItem::Prop(DevTreeProp::new( + prev_node, + prop.prop_buf, + prop.name_offset, + )), + self.depth, + ))); } Some(ParsedTok::EndNode) => { // The current node has ended. // No properties may follow until the next node starts. self.current_prop_parent_off = None; + self.depth -= 1; } Some(_) => continue, None => return Ok(None), @@ -185,6 +203,10 @@ impl<'a, 'dt: 'a> DevTreeIter<'a, 'dt> { } } + pub fn next_item(&mut self) -> Result>> { + self.next_item_with_depth().map(|o| o.map(|(item, _)| item)) + } + pub fn next_prop(&mut self) -> Result>> { loop { match self.next() { From ebc83bff1af3825053f12019492d19585f1a2ca9 Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Tue, 18 Oct 2022 21:05:41 +0200 Subject: [PATCH 2/6] Implement filtered iterators based on DevTreeIter --- src/base/iters.rs | 267 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) diff --git a/src/base/iters.rs b/src/base/iters.rs index a625ea4..e744e9d 100644 --- a/src/base/iters.rs +++ b/src/base/iters.rs @@ -1,4 +1,5 @@ //! Iterative parsers of a [`DevTree`]. +use core::cmp::Ordering; use core::mem::size_of; use core::num::NonZeroUsize; use core::str::from_utf8; @@ -267,3 +268,269 @@ impl<'a, 'dt: 'a> FallibleIterator for DevTreeIter<'a, 'dt> { self.next_item() } } + +#[derive(Clone, PartialEq)] +struct DevTreeMinDepthIter<'a, 'dt: 'a> { + iter: DevTreeIter<'a, 'dt>, + min_depth: isize, + ended: bool, +} + +impl<'a, 'dt: 'a> DevTreeMinDepthIter<'a, 'dt> { + pub fn new(iter: DevTreeIter<'a, 'dt>, min_depth: isize) -> Self { + Self { + iter, + min_depth, + ended: false, + } + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeMinDepthIter<'a, 'dt> { + type Error = DevTreeError; + type Item = (DevTreeItem<'a, 'dt>, isize); + + fn next(&mut self) -> Result> { + if self.ended { + return Ok(None); + } + loop { + match self.iter.next_item_with_depth() { + Ok(Some((item, depth))) => { + if depth >= self.min_depth { + break Ok(Some((item, depth))); + } else if let DevTreeItem::Node(_) = item { + self.ended = true; + break Ok(None); + } + } + r => break r, + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_, max) = self.iter.size_hint(); + (0, max) + } +} + +#[derive(Clone, PartialEq)] +struct DevTreeDepthIter<'a, 'dt: 'a> { + iter: DevTreeIter<'a, 'dt>, + depth: isize, + ended: bool, +} + +impl<'a, 'dt: 'a> DevTreeDepthIter<'a, 'dt> { + fn new(iter: DevTreeIter<'a, 'dt>, depth: isize) -> Self { + Self { + iter, + depth, + ended: false, + } + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeDepthIter<'a, 'dt> { + type Error = DevTreeError; + type Item = (DevTreeItem<'a, 'dt>, isize); + + fn next(&mut self) -> Result> { + if self.ended { + return Ok(None); + } + loop { + match self.iter.next_item_with_depth() { + Ok(Some((item, depth))) => match depth.cmp(&self.depth) { + Ordering::Equal => { + break Ok(Some((item, depth))); + } + Ordering::Less => { + if let DevTreeItem::Node(_) = item { + self.ended = true; + break Ok(None); + } + } + _ => {} + }, + r => break r, + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_, max) = self.iter.size_hint(); + (0, max) + } +} + +#[derive(Clone, PartialEq)] +struct DevTreeSkipCurrentIter { + iter: I, + target_depth: isize, + done: bool, +} + +impl DevTreeSkipCurrentIter { + fn new(iter: I, current_depth: isize) -> Self { + Self { + iter, + target_depth: current_depth, + done: false, + } + } +} + +impl<'a, 'dt: 'a, I> FallibleIterator for DevTreeSkipCurrentIter +where + I: FallibleIterator, isize)>, +{ + type Error = I::Error; + type Item = (DevTreeItem<'a, 'dt>, isize); + + fn next(&mut self) -> core::result::Result, I::Error> { + if self.done { + self.iter.next() + } else { + loop { + if let Ok(Some((DevTreeItem::Node(node), depth))) = self.iter.next() { + if depth == self.target_depth { + self.done = true; + break Ok(Some((DevTreeItem::Node(node), depth))); + } + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_, max) = self.iter.size_hint(); + (0, max) + } +} + +#[derive(PartialEq, Clone)] +struct WithoutDepth(I); + +impl FallibleIterator for WithoutDepth +where + I: FallibleIterator, +{ + type Error = I::Error; + type Item = V; + + fn next(&mut self) -> core::result::Result, Self::Error> { + self.0.next().map(|o| o.map(|(v, _)| v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +/// A variant of [`DevTreeIter`] limited to only descendants of a +/// given node. +#[derive(Clone, PartialEq)] +pub struct DevTreeDescendantsIter<'a, 'dt>(WithoutDepth>); + +impl<'a, 'dt> DevTreeDescendantsIter<'a, 'dt> { + pub(crate) fn new(iter: DevTreeIter<'a, 'dt>) -> Self { + let target_depth = iter.depth + 1; + Self(WithoutDepth(DevTreeMinDepthIter::new(iter, target_depth))) + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeDescendantsIter<'a, 'dt> { + type Error = DevTreeError; + type Item = DevTreeItem<'a, 'dt>; + + fn next(&mut self) -> Result> { + self.0.next() + } +} + +/// A variant of [`DevTreeIter`] limited to only direct children of a +/// given node. +#[derive(Clone, PartialEq)] +pub struct DevTreeChildrenIter<'a, 'dt>(WithoutDepth>); + +impl<'a, 'dt> DevTreeChildrenIter<'a, 'dt> { + pub(crate) fn new(iter: DevTreeIter<'a, 'dt>) -> Self { + let target_depth = iter.depth + 1; + Self(WithoutDepth(DevTreeDepthIter::new(iter, target_depth))) + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeChildrenIter<'a, 'dt> { + type Error = DevTreeError; + type Item = DevTreeItem<'a, 'dt>; + + fn next(&mut self) -> Result> { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +/// A variant of [`DevTreeIter`] limited to only later siblings and +/// their descendants of a given node. Note that this will only yield +/// siblings that come after the reference node, but not any before +/// that node. +#[derive(Clone, PartialEq)] +pub struct DevTreeSiblingsAndDescendantsIter<'a, 'dt>( + WithoutDepth>>, +); + +impl<'a, 'dt> DevTreeSiblingsAndDescendantsIter<'a, 'dt> { + pub(crate) fn new(iter: DevTreeIter<'a, 'dt>) -> Self { + let current_depth = iter.depth; + Self(WithoutDepth(DevTreeSkipCurrentIter::new( + DevTreeMinDepthIter::new(iter, current_depth), + current_depth, + ))) + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeSiblingsAndDescendantsIter<'a, 'dt> { + type Error = DevTreeError; + type Item = DevTreeItem<'a, 'dt>; + + fn next(&mut self) -> Result> { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +/// A variant of [`DevTreeIter`] limited to only later siblings of a +/// given node. Note that this will only yield siblings that come +/// after the reference node, but not any before that node. +#[derive(Clone, PartialEq)] +pub struct DevTreeSiblingsIter<'a, 'dt>( + WithoutDepth>>, +); + +impl<'a, 'dt> DevTreeSiblingsIter<'a, 'dt> { + pub(crate) fn new(iter: DevTreeIter<'a, 'dt>) -> Self { + let current_depth = iter.depth; + Self(WithoutDepth(DevTreeSkipCurrentIter::new( + DevTreeDepthIter::new(iter, current_depth), + current_depth, + ))) + } +} + +impl<'a, 'dt> FallibleIterator for DevTreeSiblingsIter<'a, 'dt> { + type Error = DevTreeError; + type Item = DevTreeItem<'a, 'dt>; + + fn next(&mut self) -> Result> { + self.0.next() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} From 414a4ff16386094fe37605b1bb130599a66f00ab Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Fri, 21 Oct 2022 19:19:44 +0200 Subject: [PATCH 3/6] Implement generalized node, prop, and node prop filters --- src/base/iters.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/base/iters.rs b/src/base/iters.rs index e744e9d..428c5fa 100644 --- a/src/base/iters.rs +++ b/src/base/iters.rs @@ -94,6 +94,25 @@ impl<'a, 'dt: 'a> FallibleIterator for DevTreeNodeIter<'a, 'dt> { } } +pub struct DevTreeNodeFilter(pub I); +impl<'a, 'dt: 'a, I> FallibleIterator for DevTreeNodeFilter +where + I: FallibleIterator>, +{ + type Error = I::Error; + type Item = DevTreeNode<'a, 'dt>; + fn next(&mut self) -> core::result::Result, Self::Error> { + loop { + match self.0.next() { + Ok(Some(DevTreeItem::Node(item))) => break Ok(Some(item)), + Ok(Some(_)) => {} + Ok(None) => break Ok(None), + Err(e) => break Err(e), + } + } + } +} + #[derive(Clone, PartialEq)] pub struct DevTreePropIter<'a, 'dt: 'a>(pub DevTreeIter<'a, 'dt>); impl<'a, 'dt: 'a> FallibleIterator for DevTreePropIter<'a, 'dt> { @@ -104,6 +123,25 @@ impl<'a, 'dt: 'a> FallibleIterator for DevTreePropIter<'a, 'dt> { } } +pub struct DevTreePropFilter(pub I); +impl<'a, 'dt: 'a, I> FallibleIterator for DevTreePropFilter +where + I: FallibleIterator>, +{ + type Error = I::Error; + type Item = DevTreeProp<'a, 'dt>; + fn next(&mut self) -> core::result::Result, Self::Error> { + loop { + match self.0.next() { + Ok(Some(DevTreeItem::Prop(p))) => break Ok(Some(p)), + Ok(Some(_)) => {} + Ok(None) => break Ok(None), + Err(e) => break Err(e), + } + } + } +} + #[derive(Clone, PartialEq)] pub struct DevTreeNodePropIter<'a, 'dt: 'a>(pub DevTreeIter<'a, 'dt>); impl<'a, 'dt: 'a> FallibleIterator for DevTreeNodePropIter<'a, 'dt> { @@ -114,6 +152,22 @@ impl<'a, 'dt: 'a> FallibleIterator for DevTreeNodePropIter<'a, 'dt> { } } +pub struct DevTreeNodePropFilter(pub I); +impl<'a, 'dt: 'a, I> FallibleIterator for DevTreeNodePropFilter +where + I: FallibleIterator>, +{ + type Error = I::Error; + type Item = DevTreeProp<'a, 'dt>; + fn next(&mut self) -> core::result::Result, Self::Error> { + match self.0.next() { + Ok(Some(item)) => Ok(item.prop()), + Ok(None) => Ok(None), + Err(e) => Err(e), + } + } +} + #[derive(Clone, PartialEq)] pub struct DevTreeCompatibleNodeIter<'s, 'a, 'dt: 'a> { pub iter: DevTreeIter<'a, 'dt>, From a6ff1844e1c4e21de9f5070d53c290fe0b52d044 Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Thu, 29 Sep 2022 10:20:38 +0200 Subject: [PATCH 4/6] Implement some relative navigation functions to Nodes nodes in base --- src/base/node.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/base/node.rs b/src/base/node.rs index 7c8b004..b5a2331 100644 --- a/src/base/node.rs +++ b/src/base/node.rs @@ -1,9 +1,14 @@ #[cfg(doc)] use super::*; -use crate::base::iters::{DevTreeIter, DevTreeNodePropIter}; +use crate::base::iters::{ + DevTreeChildrenIter, DevTreeDescendantsIter, DevTreeIter, DevTreeNodeFilter, + DevTreeNodePropIter, DevTreeSiblingsAndDescendantsIter, DevTreeSiblingsIter, +}; use crate::error::Result; +use fallible_iterator::FallibleIterator; + /// A handle to a Device Tree Node within the device tree. #[derive(Clone)] pub struct DevTreeNode<'a, 'dt: 'a> { @@ -42,4 +47,59 @@ impl<'a, 'dt: 'a> DevTreeNode<'a, 'dt> { pub fn find_next_compatible_node(&self, string: &str) -> Result>> { self.parse_iter.clone().next_compatible_node(string) } + + /// Return an iterator over all descendants of this node. + pub fn descendants(&self) -> DevTreeDescendantsIter<'a, 'dt> { + DevTreeDescendantsIter::new(self.parse_iter.clone()) + } + + /// Return an iterator over all descendant nodes of this node. + pub fn descendant_nodes(&self) -> DevTreeNodeFilter> { + DevTreeNodeFilter(self.descendants()) + } + + /// Return and iterator over all direct children of this node. + pub fn children(&self) -> DevTreeChildrenIter<'a, 'dt> { + DevTreeChildrenIter::new(self.parse_iter.clone()) + } + + /// Return an interator over all direct children nodes of this node. + pub fn child_nodes(&self) -> DevTreeNodeFilter> { + DevTreeNodeFilter(self.children()) + } + + /// Return an iterator over all descendants of this node, all + /// siblings after the node and their descendants. If you only + /// want siblings and their descendants, but not this node's + /// descendants, first use [`next_sibling`](Self::next_sibling) and then + /// [`siblings_and_descendants`](Self::siblings_and_descendants) on that. + pub fn siblings_and_descendants(&self) -> DevTreeSiblingsAndDescendantsIter<'a, 'dt> { + DevTreeSiblingsAndDescendantsIter::new(self.parse_iter.clone()) + } + + /// Return and iterator over all descendant nodes and following + /// siblings nodes and their descendant nodes. + pub fn sibling_and_descendant_nodes( + &self, + ) -> DevTreeNodeFilter> { + DevTreeNodeFilter(self.siblings_and_descendants()) + } + + /// Return an iterator over all later siblings of this node. Note + /// that this is dependant on the order of the siblings in the + /// DevTree as this can only return siblings that come after this + /// node, not previous siblings. + pub fn siblings(&self) -> DevTreeSiblingsIter<'a, 'dt> { + DevTreeSiblingsIter::new(self.parse_iter.clone()) + } + + /// Return and iterator over all later sibling nodes of this node. + pub fn sibling_nodes(&self) -> DevTreeNodeFilter> { + DevTreeNodeFilter(self.siblings()) + } + + /// Return the next sibling of this node. + pub fn next_sibling(&self) -> Result>> { + self.sibling_nodes().next() + } } From 6fba66c331211624c24f5b375cf45ea7d5b1268f Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Thu, 29 Sep 2022 11:41:53 +0200 Subject: [PATCH 5/6] Implement resolving paths in DevTrees --- src/base/node.rs | 19 +++++++++++++++++++ src/base/tree.rs | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/base/node.rs b/src/base/node.rs index b5a2331..6f17755 100644 --- a/src/base/node.rs +++ b/src/base/node.rs @@ -102,4 +102,23 @@ impl<'a, 'dt: 'a> DevTreeNode<'a, 'dt> { pub fn next_sibling(&self) -> Result>> { self.sibling_nodes().next() } + + /// Returns the [`DevTreeNode`] at the given path below this node. + pub fn node_at_path<'s, I>(&self, path: I) -> Result>> + where + I: Iterator, + { + let mut current = self.clone(); + for component in path { + if let Some(found) = current + .child_nodes() + .find(|node| Ok(node.name()? == component))? + { + current = found; + } else { + return Ok(None); + } + } + Ok(Some(current)) + } } diff --git a/src/base/tree.rs b/src/base/tree.rs index 74be310..cce8052 100644 --- a/src/base/tree.rs +++ b/src/base/tree.rs @@ -290,4 +290,16 @@ impl<'dt> DevTree<'dt> { pub fn root(&self) -> Result>> { self.nodes().next() } + + /// Returns the [`DevTreeNode`] at the given path + pub fn node_at_path<'s, I>(&self, path: I) -> Result>> + where + I: Iterator, + { + if let Some(root) = self.root()? { + root.node_at_path(path) + } else { + Ok(None) + } + } } From e79fcdd4a5a0ba84af2992be85a1a565770650e3 Mon Sep 17 00:00:00 2001 From: Sven Bartscher Date: Thu, 29 Sep 2022 11:16:52 +0200 Subject: [PATCH 6/6] Test relative navigation --- tests/parsing_test.rs | 203 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) diff --git a/tests/parsing_test.rs b/tests/parsing_test.rs index 99b08f7..0363449 100644 --- a/tests/parsing_test.rs +++ b/tests/parsing_test.rs @@ -1,6 +1,8 @@ extern crate fdt_rs; -use fdt_rs::base::DevTree; +use core::fmt::Debug; + +use fdt_rs::base::{DevTree, DevTreeItem}; use fdt_rs::error::{DevTreeError, Result}; use fdt_rs::index::DevTreeIndex; use fdt_rs::prelude::*; @@ -54,6 +56,98 @@ static DFS_NODES: &[&str] = &[ "clint@2000000", ]; +#[derive(Debug)] +enum ItemNameExpectation<'a> { + Node(&'a str), + Prop(&'a str), +} + +fn check_item_name_expectations<'e, 'a, 'dt: 'a, GI, EI>(got: &mut GI, expected: EI) +where + EI: Iterator>, + GI: FallibleIterator>, + GI::Error: Debug, +{ + for expectation in expected { + let got_item = got + .next() + .expect("Unexpected iterator failure") + .expect("Unexpected end of iterator"); + match got_item { + DevTreeItem::Node(node) => { + let node_name = node.name().unwrap(); + match expectation { + ItemNameExpectation::Node(expected_name) => { + assert!( + &node_name == expected_name, + "Expected node with name {} got {}", + expected_name, + node_name, + ); + } + ItemNameExpectation::Prop(expected_name) => { + panic!( + "Expected prop with name {} got node with name {}", + expected_name, node_name, + ); + } + } + } + DevTreeItem::Prop(prop) => { + let prop_name = prop.name().unwrap(); + match expectation { + ItemNameExpectation::Node(expected_name) => { + panic!( + "Expected node with name {} got prop with name {}", + expected_name, prop_name, + ); + } + ItemNameExpectation::Prop(expected_name) => { + assert!( + &prop_name == expected_name, + "Expected prop with name {} got {}", + expected_name, + prop_name, + ); + } + } + } + } + } + + match got.next() { + Err(e) => panic!("Expected iterator to end, but it failed instead: {:?}", e), + Ok(Some(item)) => { + let (t, name) = match item { + DevTreeItem::Node(node) => ("node", node.name().unwrap()), + DevTreeItem::Prop(prop) => ("prop", prop.name().unwrap()), + }; + panic!( + "Expected iterator to end, but it produced a {} with name {}", + t, name + ); + } + Ok(None) => {} + } +} + +macro_rules! item_name_expectation { + (prop => $name:expr) => { + ItemNameExpectation::Prop($name) + }; + (node => $name:expr) => { + ItemNameExpectation::Node($name) + }; +} + +macro_rules! item_name_expectations { + [$($type:ident => $name:expr),*] => { vec![$( item_name_expectation!($type => $name)),*] }; +} + +macro_rules! check_item_iter { + ($iter:expr $(, $($type:ident => $name:expr),* $(,)?)?) => { check_item_name_expectations($iter, item_name_expectations![$($($type => $name),*)?].iter()) }; +} + pub struct FdtIndex<'dt> { index: DevTreeIndex<'dt, 'dt>, _vec: Vec, @@ -278,6 +372,113 @@ fn find_all_compatible() { } } +#[test] +fn get_path() { + let dt = unsafe { DevTree::new(FDT) }.unwrap(); + let core0 = dt + .node_at_path(IntoIterator::into_iter([ + "cpus", "cpu-map", "cluster0", "core0", + ])) + .unwrap() + .expect("Expected to find node at path"); + assert!(core0.name().expect("node name should be readable") == "core0"); +} + +#[test] +fn get_descendants() { + let dt = unsafe { DevTree::new(FDT) }.unwrap(); + let cpus = dt + .node_at_path(IntoIterator::into_iter(["cpus"])) + .unwrap() + .unwrap(); + check_item_iter!( + &mut cpus.descendants(), + node => "cpu-map", + node => "cluster0", + node => "core0", + prop => "cpu", + node => "cpu@0", + prop => "phandle", + prop => "device_type", + prop => "reg", + prop => "status", + prop => "compatible", + prop => "riscv,isa", + prop => "mmu-type", + node => "interrupt-controller", + prop => "#interrupt-cells", + prop => "interrupt-controller", + prop => "compatible", + prop => "phandle", + ); +} + +#[test] +fn get_children() { + let dt = unsafe { DevTree::new(FDT) }.unwrap(); + let cpus = dt + .node_at_path(IntoIterator::into_iter(["cpus"])) + .unwrap() + .unwrap(); + check_item_iter!( + &mut cpus.children(), + node => "cpu-map", + node => "cpu@0", + prop => "phandle", + prop => "device_type", + prop => "reg", + prop => "status", + prop => "compatible", + prop => "riscv,isa", + prop => "mmu-type", + ); +} + +#[test] +fn get_siblings_and_descendants() { + let dt = unsafe { DevTree::new(FDT) }.unwrap(); + let cpu_map = dt + .node_at_path(IntoIterator::into_iter(["cpus", "cpu-map"])) + .unwrap() + .unwrap(); + check_item_iter!( + &mut cpu_map.siblings_and_descendants(), + node => "cpu@0", + prop => "phandle", + prop => "device_type", + prop => "reg", + prop => "status", + prop => "compatible", + prop => "riscv,isa", + prop => "mmu-type", + node => "interrupt-controller", + prop => "#interrupt-cells", + prop => "interrupt-controller", + prop => "compatible", + prop => "phandle", + ); +} + +#[test] +fn get_siblings() { + let dt = unsafe { DevTree::new(FDT) }.unwrap(); + let cpu_map = dt + .node_at_path(IntoIterator::into_iter(["cpus", "cpu-map"])) + .unwrap() + .unwrap(); + check_item_iter!( + &mut cpu_map.siblings(), + node => "cpu@0", + prop => "phandle", + prop => "device_type", + prop => "reg", + prop => "status", + prop => "compatible", + prop => "riscv,isa", + prop => "mmu-type", + ); +} + pub mod index_tests { use super::*;