diff --git a/src/fdt/property.rs b/src/fdt/property.rs index 238f9ac..88a4c03 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -9,6 +9,7 @@ //! A read-only API for inspecting a device tree property. use core::fmt::{self, Display, Formatter}; +use core::mem::size_of; use zerocopy::FromBytes; @@ -33,8 +34,6 @@ pub(crate) struct ParsedProperty<'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>; @@ -64,7 +63,9 @@ 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) + .value_as::() + .map_err(|_| fmt::Error)?; if let Some(first) = strings.next() { write!(f, " = \"{first}\"")?; for s in strings { @@ -75,9 +76,9 @@ impl FdtProperty<'_> { } } - if self.value.len().is_multiple_of(4) { + if self.value.len().is_multiple_of(size_of::()) { write!(f, " = <")?; - let (chunks, remainder) = self.value.as_chunks::<4>(); + let (chunks, remainder) = self.value.as_chunks::<{ size_of::() }>(); debug_assert!(remainder.is_empty()); for (i, chunk) in chunks.iter().enumerate() { if i > 0 { diff --git a/src/fdt_mut/property.rs b/src/fdt_mut/property.rs index f52859e..739e38f 100644 --- a/src/fdt_mut/property.rs +++ b/src/fdt_mut/property.rs @@ -170,8 +170,6 @@ impl Display for FdtPropertyMut<'_, B> { } impl<'a, B: FdtBuffer> Property for &'a FdtPropertyMut<'_, B> { - type Str = &'a str; - type StrList = crate::values::FdtStringListIterator<'a>; type PropEncodedArray = crate::values::PropEncodedArrayIterator<'a, N>; type CellsItem = crate::Cells<'a>; @@ -187,16 +185,18 @@ impl<'a, B: FdtBuffer> Property for &'a FdtPropertyMut<'_, B> { .expect("Fdt should be valid") } - fn as_cells(&self) -> Result, crate::error::PropertyError> { - self.as_read_only().as_cells() - } - - fn as_str(&self) -> Result<&'a str, crate::error::PropertyError> { - self.as_read_only().as_str() - } - - fn as_str_list(&self) -> Self::StrList { - self.as_read_only().as_str_list() + fn value_as<'v, T: crate::FromPropertyValue<'v>>( + &self, + ) -> Result + where + Self: 'v, + { + let fdt = self.data.as_read_only(); + let value = fdt + .data + .get(self.value_offset..self.value_offset + self.len) + .expect("Fdt should be valid"); + T::from_property_value(value) } fn as_prop_encoded_array( diff --git a/src/lib.rs b/src/lib.rs index b0320d6..9d65b78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ //! // Find the child node and read its property. //! let child_node = fdt.find_node("/child").unwrap(); //! let prop = child_node.property("my-property").unwrap(); -//! let val: &str = prop.as_str().unwrap().as_ref(); +//! let val = prop.value_as::<&str>().unwrap(); //! assert_eq!(val, "hello"); //! //! // Display the DTS @@ -139,32 +139,21 @@ use core::ops::{BitOr, Shl}; use zerocopy::big_endian; use crate::error::{PropertyError, StandardError}; -pub use crate::values::ToPropertyValue; +pub use crate::values::{ + FdtStringListIterator, FromPropertyValue, PropEncodedArrayIterator, ToPropertyValue, +}; 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 value_as<'v, T: crate::FromPropertyValue<'v>>(&$self) -> Result where Self: 'v { + T::from_property_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) + ) -> Result, crate::error::PropertyError> { + crate::values::PropEncodedArrayIterator::new($get_value, fields_cells) } }; } @@ -287,12 +276,6 @@ pub trait Node: Sized { /// A property of a device tree node. 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; @@ -307,106 +290,14 @@ pub trait Property: Sized { #[must_use] fn value(&self) -> &[u8]; - /// Returns the value of this property as a `u32`. - /// - /// # Errors - /// - /// Returns an [`PropertyError::InvalidLength`] if the property's value is - /// not 4 bytes long. - /// - /// # Examples - /// - /// ``` - /// use dtoolkit::fdt::Fdt; - /// use dtoolkit::{Node, Property}; - /// - /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("u32-prop").unwrap(); - /// assert_eq!(prop.as_u32().unwrap(), 0x12345678); - /// ``` - fn as_u32(&self) -> Result { - self.value() - .try_into() - .map(u32::from_be_bytes) - .map_err(|_| PropertyError::InvalidLength) - } - - /// Returns the value of this property as a `u64`. + /// Returns the value of this property as a given type. /// /// # Errors /// - /// Returns an [`PropertyError::InvalidLength`] if the property's value is - /// not 8 bytes long. - /// - /// # Examples - /// - /// ``` - /// use dtoolkit::fdt::Fdt; - /// use dtoolkit::{Node, Property}; - /// - /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("u64-prop").unwrap(); - /// assert_eq!(prop.as_u64().unwrap(), 0x1122334455667788); - /// ``` - fn as_u64(&self) -> Result { - self.value() - .try_into() - .map(u64::from_be_bytes) - .map_err(|_| PropertyError::InvalidLength) - } - - /// Returns the value of this property as a slide of 32-bit cells. - /// - /// # Errors - /// - /// Returns an error if the value of the property isn't a multiple of 4 - /// bytes long. - fn as_cells(&self) -> Result; - - /// Returns the value of this property as a string. - /// - /// # Errors - /// - /// Returns an [`PropertyError::InvalidString`] if the property's value is - /// not a null-terminated string or contains invalid UTF-8. - /// - /// # Examples - /// - /// ``` - /// use dtoolkit::fdt::Fdt; - /// use dtoolkit::{Node, Property}; - /// - /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("str-prop").unwrap(); - /// assert_eq!(prop.as_str().unwrap(), "hello world"); - /// ``` - fn as_str(&self) -> Result; - - /// Returns an iterator over the strings in this property. - /// - /// # Examples - /// - /// ``` - /// use dtoolkit::fdt::Fdt; - /// use dtoolkit::{Node, Property}; - /// - /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("str-list-prop").unwrap(); - /// let mut str_list = prop.as_str_list(); - /// assert_eq!(str_list.next(), Some("first")); - /// assert_eq!(str_list.next(), Some("second")); - /// assert_eq!(str_list.next(), Some("third")); - /// assert_eq!(str_list.next(), None); - /// ``` - fn as_str_list(&self) -> Self::StrList; + /// Returns an error if the value cannot be parsed or has invalid length. + fn value_as<'a, T: FromPropertyValue<'a>>(&self) -> Result + where + Self: 'a; /// Returns an iterator over the elements of the property interpreted as a /// `prop-encoded-array`. diff --git a/src/model/overlay.rs b/src/model/overlay.rs index c68320f..8cda706 100644 --- a/src/model/overlay.rs +++ b/src/model/overlay.rs @@ -204,13 +204,13 @@ impl<'a> OverlayApplier<'a> { if let Some(sym_node) = self.base.root.child(NODE_SYMBOLS) && let Some(sym_prop) = sym_node.property(path) { - let abs_path = sym_prop.as_str()?; + let abs_path = sym_prop.value_as::<&str>()?; return Ok(abs_path.to_string()); } if let Some(aliases_node) = self.base.root.child("aliases") && let Some(alias_prop) = aliases_node.property(path) { - let abs_path = alias_prop.as_str()?; + let abs_path = alias_prop.value_as::<&str>()?; return Ok(abs_path.to_string()); } Err(OverlayError::TargetNotFound(path.to_string())) @@ -319,7 +319,7 @@ fn relocate_local_phandles( fn offset_node_phandles(node: &mut DeviceTreeNode, offset: u32) -> Result<(), OverlayError> { for prop_name in PHANDLE_PROPS { if let Some(prop) = node.property_mut(prop_name) - && let Ok(val) = (&*prop).as_u32() + && let Ok(val) = (&*prop).value_as::() { let new_val = val .checked_add(offset) @@ -423,7 +423,7 @@ fn resolve_external_fixups( .child(NODE_SYMBOLS) .and_then(|sym| sym.property(symbol_name)) .ok_or_else(|| OverlayError::UnresolvedSymbol(symbol_name.to_string()))? - .as_str() + .value_as::<&str>() .map_err(|_| OverlayError::UnresolvedSymbol(symbol_name.to_string()))? .to_string(); @@ -447,7 +447,7 @@ fn resolve_external_fixups( new_p }; - for loc_str in fixup_prop.as_str_list() { + for loc_str in fixup_prop.value_as::()? { let loc = FixupLocation::parse(loc_str)?; let overlay_node = overlay .find_node_mut(loc.node_path) diff --git a/src/model/property.rs b/src/model/property.rs index d7eb92a..45e7e0a 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -10,10 +10,8 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::str; -use zerocopy::FromBytes; - use crate::error::ModelError; -use crate::values::{FdtStringListIterator, PropEncodedArrayIterator}; +use crate::values::PropEncodedArrayIterator; use crate::{Cells, Property, ToPropertyValue}; /// A mutable, in-memory representation of a device tree property. @@ -24,8 +22,6 @@ pub struct DeviceTreeProperty { } impl<'a> Property for &'a DeviceTreeProperty { - type Str = &'a str; - type StrList = FdtStringListIterator<'a>; type PropEncodedArray = PropEncodedArrayIterator<'a, N>; type CellsItem = Cells<'a>; diff --git a/src/overlay.rs b/src/overlay.rs index 939735a..5387b37 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -129,15 +129,13 @@ impl Fragment { /// FragmentTarget::Phandle(p) => println!("Targeting phandle: {}", p), /// } /// ``` - pub fn target( - &self, - ) -> Result as Property>::Str>, OverlayError> { + pub fn target(&self) -> Result, OverlayError> { let target = self.node.property("target"); let target_path = self.node.property("target-path"); match (target, target_path) { - (Some(prop), None) => Ok(FragmentTarget::Phandle(prop.as_u32()?)), - (None, Some(prop)) => Ok(FragmentTarget::Path(prop.as_str()?)), + (Some(prop), None) => Ok(FragmentTarget::Phandle(prop.value_as::()?)), + (None, Some(prop)) => Ok(FragmentTarget::Path(prop.value_as::<&str>()?)), _ => Err(OverlayError::InvalidFragmentTarget), } } @@ -227,7 +225,7 @@ impl<'a> FixupLocation<'a> { pub fn get_phandle(node: &N) -> Option { for prop in PHANDLE_PROPS { if let Some(p) = node.property(prop) - && let Ok(val) = p.as_u32() + && let Ok(val) = p.value_as::() { return Some(val); } diff --git a/src/standard.rs b/src/standard.rs index 4a6fb81..48c67a7 100644 --- a/src/standard.rs +++ b/src/standard.rs @@ -32,11 +32,12 @@ pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1; pub trait NodeStandard: Node { /// Returns the value of the standard `compatible` property. #[must_use] - fn compatible( - &self, - ) -> Option as Property>::Str> + '_> { - self.property("compatible") - .map(|property| property.as_str_list()) + fn compatible(&self) -> Option + '_> { + self.property("compatible").and_then(|property| { + property + .value_as::() + .ok() + }) } /// Returns whether this node has a `compatible` property containing the @@ -44,7 +45,9 @@ pub trait NodeStandard: Node { #[must_use] fn is_compatible(&self, compatible_filter: &str) -> bool { if let Some(prop) = self.property("compatible") { - return prop.as_str_list().any(|c| c.as_ref() == compatible_filter); + return prop + .value_as::() + .is_ok_and(|mut it| it.any(|c| c == compatible_filter)); } false } @@ -64,9 +67,9 @@ pub trait NodeStandard: Node { /// # Errors /// /// Returns an error if the value isn't a valid UTF-8 string. - fn model(&self) -> Result as Property>::Str>, PropertyError> { + fn model(&self) -> Result, PropertyError> { if let Some(model) = self.property("model") { - Ok(Some(model.as_str()?)) + Ok(Some(model.value_as::<&str>()?)) } else { Ok(None) } @@ -79,7 +82,7 @@ pub trait NodeStandard: Node { /// Returns an error if the value isn't a valid u32. fn phandle(&self) -> Result, PropertyError> { if let Some(property) = self.property("phandle") { - Ok(Some(property.as_u32()?)) + Ok(Some(property.value_as::()?)) } else { Ok(None) } @@ -94,7 +97,7 @@ pub trait NodeStandard: Node { /// 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()?.as_ref().parse()?) + Ok(status.value_as::<&str>()?.parse()?) } else { Ok(Status::Okay) } @@ -107,7 +110,7 @@ pub trait NodeStandard: Node { /// Returns an error if the value isn't a valid u32. fn address_cells(&self) -> Result { if let Some(property) = self.property("#address-cells") { - Ok(property.as_u32()?) + Ok(property.value_as::()?) } else { Ok(DEFAULT_ADDRESS_CELLS) } @@ -120,7 +123,7 @@ pub trait NodeStandard: Node { /// Returns an error if the value isn't a valid u32. fn size_cells(&self) -> Result { if let Some(model) = self.property("#size-cells") { - Ok(model.as_u32()?) + Ok(model.value_as::()?) } else { Ok(DEFAULT_SIZE_CELLS) } @@ -143,7 +146,7 @@ pub trait NodeStandard: Node { /// Returns an error if the value isn't a valid u32. fn virtual_reg(&self) -> Result, PropertyError> { if let Some(property) = self.property("virtual-reg") { - Ok(Some(property.as_u32()?)) + Ok(Some(property.value_as::()?)) } else { Ok(None) } @@ -159,6 +162,29 @@ pub trait NodeStandard: Node { impl NodeStandard for T {} impl<'a> FdtNode<'a> { + /// Returns the value of the standard `compatible` property. + #[must_use] + pub fn compatible(&self) -> Option + 'a> { + self.property("compatible").and_then(|property| { + property + .value_as::>() + .ok() + }) + } + + /// Returns the value of the standard `model` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid UTF-8 string. + pub fn model(&self) -> Result, PropertyError> { + if let Some(model) = self.property("model") { + Ok(Some(model.value_as::<&'a str>()?)) + } else { + Ok(None) + } + } + /// Returns the value of the standard `reg` property. /// /// # Errors diff --git a/src/standard/chosen.rs b/src/standard/chosen.rs index 00a5005..f73e96e 100644 --- a/src/standard/chosen.rs +++ b/src/standard/chosen.rs @@ -49,10 +49,13 @@ impl Chosen { /// /// 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 as Property>::Str>, PropertyError> { + pub fn bootargs<'a>(&'a self) -> Result, PropertyError> + where + N: 'a, + { self.node .property("bootargs") - .map(|value| value.as_str()) + .map(|value| value.value_as::<&'a str>()) .transpose() } @@ -62,10 +65,13 @@ impl 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 as Property>::Str>, PropertyError> { + pub fn stdout_path<'a>(&'a self) -> Result, PropertyError> + where + N: 'a, + { self.node .property("stdout-path") - .map(|value| value.as_str()) + .map(|value| value.value_as::<&'a str>()) .transpose() } @@ -75,10 +81,13 @@ impl 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 as Property>::Str>, PropertyError> { + pub fn stdin_path<'a>(&'a self) -> Result, PropertyError> + where + N: 'a, + { self.node .property("stdin-path") - .map(|value| value.as_str()) + .map(|value| value.value_as::<&'a str>()) .transpose() } } diff --git a/src/standard/cpus.rs b/src/standard/cpus.rs index f9db40d..470cd65 100644 --- a/src/standard/cpus.rs +++ b/src/standard/cpus.rs @@ -11,6 +11,7 @@ use core::ops::Deref; use crate::error::{PropertyError, StandardError}; use crate::fdt::{Fdt, FdtNode}; +use crate::values::FdtStringListIterator; use crate::{Cells, Node, Property}; impl<'a> Fdt<'a> { @@ -51,7 +52,10 @@ impl Display for Cpus { impl Cpus { /// Returns an iterator over the `/cpus/cpu@*` nodes. - pub fn cpus(&self) -> impl Iterator>> + '_ { + pub fn cpus<'a>(&'a self) -> impl Iterator>> + 'a + where + N: 'a, + { self.node.children().filter_map(|child| { if child.name_without_address().as_ref() == "cpu" { Some(Cpu { node: child }) @@ -85,8 +89,15 @@ impl Display for Cpu { impl Cpu { /// Returns the value of the standard `enable-method` property if it is /// present. - pub fn enable_method(&self) -> Option<<::Property<'_> as Property>::StrList> { - Some(self.node.property("enable-method")?.as_str_list()) + #[must_use] + pub fn enable_method<'a>(&'a self) -> Option> + where + N: 'a, + { + self.node + .property("enable-method")? + .value_as::>() + .ok() } /// Returns the value of the standard `cpu-release-addr` property if it is @@ -98,7 +109,7 @@ impl Cpu { pub fn cpu_release_addr(&self) -> Result, PropertyError> { self.node .property("cpu-release-addr") - .map(|value| value.as_u64()) + .map(|value| value.value_as::()) .transpose() } } diff --git a/src/standard/memory.rs b/src/standard/memory.rs index 7ba4bd2..a4666fa 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -67,9 +67,12 @@ impl Memory { /// # Errors /// /// Returns an error if the size of the value isn't a multiple of 5 cells. - pub fn initial_mapped_area( - &self, - ) -> Result + '_>, PropertyError> { + pub fn initial_mapped_area<'a>( + &'a self, + ) -> Result + 'a>, PropertyError> + where + N: 'a, + { if let Some(property) = self.node.property("initial-mapped-area") { Ok(Some( property @@ -147,10 +150,13 @@ impl ReservedMemory { /// /// Returns an error if the value of the property isn't a multiple of 4 /// bytes long. - pub fn size(&self) -> Result as Property>::CellsItem>, PropertyError> { + pub fn size<'a>(&'a self) -> Result>, PropertyError> + where + N: 'a, + { self.node .property("size") - .map(|value| value.as_cells()) + .map(|value| value.value_as::>()) .transpose() } @@ -161,12 +167,13 @@ impl ReservedMemory { /// /// Returns an error if the value of the property isn't a multiple of 4 /// bytes long. - pub fn alignment( - &self, - ) -> Result as Property>::CellsItem>, PropertyError> { + pub fn alignment<'a>(&'a self) -> Result>, PropertyError> + where + N: 'a, + { self.node .property("alignment") - .map(|value| value.as_cells()) + .map(|value| value.value_as::>()) .transpose() } diff --git a/src/values.rs b/src/values.rs index 2c7c26a..5c97b39 100644 --- a/src/values.rs +++ b/src/values.rs @@ -26,6 +26,12 @@ pub struct FdtStringListIterator<'a> { pub(crate) value: &'a [u8], } +impl<'a> FromPropertyValue<'a> for FdtStringListIterator<'a> { + fn from_property_value(value: &'a [u8]) -> Result { + Ok(Self { value }) + } +} + impl<'a> Iterator for FdtStringListIterator<'a> { type Item = &'a str; @@ -80,6 +86,17 @@ impl<'a, const N: usize> Iterator for PropEncodedArrayIterator<'a, N> { } } +/// A trait for types that can be parsed from a device tree property value. +pub trait FromPropertyValue<'a>: Sized { + /// Parses a value of `Self` from a device tree property byte slice. + /// + /// # Errors + /// + /// Returns a [`PropertyError`] if the byte slice cannot be converted into + /// `Self`. + fn from_property_value(value: &'a [u8]) -> Result; +} + /// A trait for types that can be serialized into a device tree property value. pub trait ToPropertyValue { /// Returns the length in bytes of the serialized property value. @@ -105,6 +122,12 @@ impl ToPropertyValue for &T { } } +impl<'a> FromPropertyValue<'a> for &'a [u8] { + fn from_property_value(value: &'a [u8]) -> Result { + Ok(value) + } +} + impl ToPropertyValue for &[u8] { fn property_value_len(&self) -> usize { self.len() @@ -115,6 +138,12 @@ impl ToPropertyValue for &[u8] { } } +impl<'a, const N: usize> FromPropertyValue<'a> for [u8; N] { + fn from_property_value(value: &'a [u8]) -> Result { + value.try_into().map_err(|_| PropertyError::InvalidLength) + } +} + impl ToPropertyValue for [u8; N] { fn property_value_len(&self) -> usize { self.len() @@ -125,6 +154,19 @@ impl ToPropertyValue for [u8; N] { } } +impl<'a, const N: usize> FromPropertyValue<'a> for &'a [u8; N] { + fn from_property_value(value: &'a [u8]) -> Result { + value.try_into().map_err(|_| PropertyError::InvalidLength) + } +} + +#[cfg(feature = "alloc")] +impl<'a> FromPropertyValue<'a> for Vec { + fn from_property_value(value: &'a [u8]) -> Result { + Ok(value.to_vec()) + } +} + #[cfg(feature = "alloc")] impl ToPropertyValue for Vec { fn property_value_len(&self) -> usize { @@ -136,6 +178,15 @@ impl ToPropertyValue for Vec { } } +impl<'a> FromPropertyValue<'a> for u32 { + fn from_property_value(value: &'a [u8]) -> Result { + value + .try_into() + .map(u32::from_be_bytes) + .map_err(|_| PropertyError::InvalidLength) + } +} + impl ToPropertyValue for u32 { fn property_value_len(&self) -> usize { size_of::() @@ -146,6 +197,15 @@ impl ToPropertyValue for u32 { } } +impl<'a> FromPropertyValue<'a> for u64 { + fn from_property_value(value: &'a [u8]) -> Result { + value + .try_into() + .map(u64::from_be_bytes) + .map_err(|_| PropertyError::InvalidLength) + } +} + impl ToPropertyValue for u64 { fn property_value_len(&self) -> usize { size_of::() @@ -156,6 +216,15 @@ impl ToPropertyValue for u64 { } } +impl<'a> FromPropertyValue<'a> for &'a str { + fn from_property_value(value: &'a [u8]) -> Result { + let stripped = value + .strip_suffix(b"\0") + .ok_or(PropertyError::InvalidString)?; + core::str::from_utf8(stripped).map_err(|_| PropertyError::InvalidString) + } +} + impl ToPropertyValue for &str { fn property_value_len(&self) -> usize { self.len() + 1 @@ -168,6 +237,18 @@ impl ToPropertyValue for &str { } } +#[cfg(feature = "alloc")] +impl<'a> FromPropertyValue<'a> for String { + fn from_property_value(value: &'a [u8]) -> Result { + let stripped = value + .strip_suffix(b"\0") + .ok_or(PropertyError::InvalidString)?; + core::str::from_utf8(stripped) + .map(String::from) + .map_err(|_| PropertyError::InvalidString) + } +} + #[cfg(feature = "alloc")] impl ToPropertyValue for String { fn property_value_len(&self) -> usize { @@ -194,6 +275,24 @@ impl ToPropertyValue for &[u32] { } } +impl<'a, const N: usize> FromPropertyValue<'a> for [u32; N] { + fn from_property_value(value: &'a [u8]) -> Result { + if value.len() != N * size_of::() { + return Err(PropertyError::InvalidLength); + } + let mut out = [0u32; N]; + for (i, chunk) in value + .as_chunks::<{ size_of::() }>() + .0 + .iter() + .enumerate() + { + out[i] = u32::from_be_bytes(*chunk); + } + Ok(out) + } +} + impl ToPropertyValue for [u32; N] { fn property_value_len(&self) -> usize { self.len() * size_of::() @@ -207,6 +306,20 @@ impl ToPropertyValue for [u32; N] { } } +#[cfg(feature = "alloc")] +impl<'a> FromPropertyValue<'a> for Vec { + fn from_property_value(value: &'a [u8]) -> Result { + if !value.len().is_multiple_of(size_of::()) { + return Err(PropertyError::InvalidLength); + } + let mut out = Vec::with_capacity(value.len() / size_of::()); + for chunk in value.as_chunks::<{ size_of::() }>().0 { + out.push(u32::from_be_bytes(*chunk)); + } + Ok(out) + } +} + #[cfg(feature = "alloc")] impl ToPropertyValue for Vec { fn property_value_len(&self) -> usize { @@ -236,6 +349,13 @@ impl ToPropertyValue for &[&str] { } } +#[cfg(feature = "alloc")] +impl<'a> FromPropertyValue<'a> for Vec<&'a str> { + fn from_property_value(value: &'a [u8]) -> Result { + Ok(FdtStringListIterator { value }.collect::>()) + } +} + #[cfg(feature = "alloc")] impl ToPropertyValue for Vec<&str> { fn property_value_len(&self) -> usize { @@ -252,6 +372,14 @@ impl ToPropertyValue for Vec<&str> { } } +impl<'a> FromPropertyValue<'a> for Cells<'a> { + fn from_property_value(value: &'a [u8]) -> Result { + let cells = <[big_endian::U32] as FromBytes>::ref_from_bytes(value) + .map_err(|_| PropertyError::InvalidLength)?; + Ok(Self(cells)) + } +} + impl ToPropertyValue for Cells<'_> { fn property_value_len(&self) -> usize { self.0.len() * size_of::() @@ -280,4 +408,28 @@ mod tests { PropertyError::PropEncodedArraySizeMismatch { size: 4, chunk: 0 } ); } + + #[test] + fn u32_array_invalid_length() { + let bytes = [0, 0, 0, 1, 0, 0, 0, 2, 0]; // 9 bytes, not 8 + assert_eq!( + <[u32; 2]>::from_property_value(&bytes), + Err(PropertyError::InvalidLength) + ); + let short_bytes = [0, 0, 0, 1]; // 4 bytes, needed 8 + assert_eq!( + <[u32; 2]>::from_property_value(&short_bytes), + Err(PropertyError::InvalidLength) + ); + } + + #[cfg(feature = "alloc")] + #[test] + fn u32_vec_invalid_length() { + let bytes = [0, 0, 0, 1, 0]; // 5 bytes, not multiple of 4 + assert_eq!( + >::from_property_value(&bytes), + Err(PropertyError::InvalidLength) + ); + } } diff --git a/tests/fdt.rs b/tests/fdt.rs index 6e74579..1bffa09 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -57,19 +57,19 @@ fn read_prop_values() { let prop = props.next().unwrap(); assert_eq!(prop.name(), "u32-prop"); - assert_eq!(prop.as_u32().unwrap(), 0x1234_5678); + assert_eq!(prop.value_as::().unwrap(), 0x1234_5678); let prop = props.next().unwrap(); assert_eq!(prop.name(), "u64-prop"); - assert_eq!(prop.as_u64().unwrap(), 0x1122_3344_5566_7788); + assert_eq!(prop.value_as::().unwrap(), 0x1122_3344_5566_7788); let prop = props.next().unwrap(); assert_eq!(prop.name(), "str-prop"); - assert_eq!(prop.as_str().unwrap(), "hello world"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello world"); let prop = props.next().unwrap(); assert_eq!(prop.name(), "str-list-prop"); - let mut str_list = prop.as_str_list(); + let mut str_list = prop.value_as::().unwrap(); assert_eq!(str_list.next(), Some("first")); assert_eq!(str_list.next(), Some("second")); assert_eq!(str_list.next(), Some("third")); @@ -87,11 +87,11 @@ fn get_property_by_name() { let prop = node.property("u32-prop").unwrap(); assert_eq!(prop.name(), "u32-prop"); - assert_eq!(prop.as_u32().unwrap(), 0x1234_5678); + assert_eq!(prop.value_as::().unwrap(), 0x1234_5678); let prop = node.property("str-prop").unwrap(); assert_eq!(prop.name(), "str-prop"); - assert_eq!(prop.as_str().unwrap(), "hello world"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello world"); assert!(node.property("non-existent-prop").is_none()); } @@ -413,3 +413,49 @@ fn reserved_memory_alloc_ranges_zero_cells() { )) )); } + +#[test] +fn child_of_child_string_lifetime() { + let dtb = include_bytes!("dtb/test_props.dtb"); + let fdt = Fdt::new(dtb).unwrap(); + let val: &str = { + let root = fdt.root(); + let child1 = root.child("test-props").unwrap(); + let prop = child1.property("str-prop").unwrap(); + prop.value_as::<&str>().unwrap() + }; + assert_eq!(val, "hello world"); +} + +#[test] +fn standard_node_outlives_wrapper() { + let dtb = include_bytes!("dtb/test_props.dtb"); + let fdt = Fdt::new(dtb).unwrap(); + + let model_str: &str = { + let root = fdt.root(); + let standard_props = root.child("standard-props").unwrap(); + standard_props.model().unwrap().unwrap() + }; + assert_eq!(model_str, "Some Model"); + + let compatible: Vec<&str> = { + let root = fdt.root(); + let standard_props = root.child("standard-props").unwrap(); + standard_props.compatible().unwrap().collect() + }; + assert_eq!(compatible, vec!["abc,def", "some,other"]); + + let dtb_mem = include_bytes!("dtb/test_pretty_print.dtb"); + let fdt_mem = Fdt::new(dtb_mem).unwrap(); + let area = { + let memory = fdt_mem.memory().unwrap(); + memory + .initial_mapped_area() + .unwrap() + .unwrap() + .next() + .unwrap() + }; + assert_eq!(area.size, 0x1000); +} diff --git a/tests/fdt_mut.rs b/tests/fdt_mut.rs index 199cb73..3a1a77a 100644 --- a/tests/fdt_mut.rs +++ b/tests/fdt_mut.rs @@ -32,7 +32,7 @@ fn modify_property_in_place() { let fdt = Fdt::new(&data).unwrap(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hello there"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello there"); } #[test] @@ -56,7 +56,7 @@ fn modify_property_shrink_and_grow() { let fdt = Fdt::new(&data).unwrap(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hi"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hi"); // Now grow it back, since the space is now NOPs let mut fdt_mut = FdtMut::from_slice(&mut data).unwrap(); @@ -70,7 +70,7 @@ fn modify_property_shrink_and_grow() { let fdt = Fdt::new(&data).unwrap(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hello"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello"); // Growing beyond the original space should fail because there are no NOPs let mut fdt_mut = FdtMut::from_slice(&mut data).unwrap(); @@ -132,7 +132,7 @@ fn modify_property_vec_owned() { let fdt = fdt_mut.as_read_only(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hello there"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello there"); } #[cfg(feature = "arrayvec07")] @@ -152,7 +152,7 @@ fn modify_property_arrayvec_owned() { let fdt = fdt_mut.as_read_only(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hello there"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello there"); } #[cfg(feature = "heapless09")] @@ -171,7 +171,7 @@ fn modify_property_heapless_owned() { let fdt = fdt_mut.as_read_only(); let node = fdt.find_node("/test-props").unwrap(); let prop = node.property("str-prop").unwrap(); - assert_eq!(prop.as_str().unwrap(), "hello there"); + assert_eq!(prop.value_as::<&str>().unwrap(), "hello there"); } #[test] diff --git a/tests/model.rs b/tests/model.rs index b9974bc..76e5984 100644 --- a/tests/model.rs +++ b/tests/model.rs @@ -37,10 +37,16 @@ fn tree_creation() { assert_eq!(root.children().count(), 2); let child_a = root.children().find(|c| c.name() == "child-a").unwrap(); - assert_eq!(child_a.property("child-prop").unwrap().as_str(), Ok("a")); + assert_eq!( + child_a.property("child-prop").unwrap().value_as::<&str>(), + Ok("a") + ); let child_b = root.children().find(|c| c.name() == "child-b").unwrap(); - assert_eq!(child_b.property("child-prop").unwrap().as_str(), Ok("b")); + assert_eq!( + child_b.property("child-prop").unwrap().value_as::<&str>(), + Ok("b") + ); } #[test] @@ -68,7 +74,10 @@ fn tree_modification() { // Verify the modification let child = tree.root.children().find(|c| c.name() == "child").unwrap(); - assert_eq!(child.property("prop").unwrap().as_str(), Ok("new-value")); + assert_eq!( + child.property("prop").unwrap().value_as::<&str>(), + Ok("new-value") + ); // Remove the property let child = tree.root.child_mut("child").unwrap(); @@ -108,7 +117,10 @@ fn find_node_mut() { .children() .find(|c| c.name() == "child-a-a") .unwrap(); - assert_eq!(child_a_a.property("prop").unwrap().as_str(), Ok("value")); + assert_eq!( + child_a_a.property("prop").unwrap().value_as::<&str>(), + Ok("value") + ); // Find a non-existent node assert!(tree.find_node_mut("/child-a/child-c").is_none()); diff --git a/tests/overlay.rs b/tests/overlay.rs index b19dcf3..c97e843 100644 --- a/tests/overlay.rs +++ b/tests/overlay.rs @@ -36,15 +36,24 @@ fn apply_overlay_target_path() { ); let soc = base.find_node_mut("/soc").unwrap(); - assert_eq!(soc.property("status").unwrap().as_str(), Ok("okay")); - assert_eq!(soc.property("new-prop").unwrap().as_str(), Ok("foo")); + assert_eq!( + soc.property("status").unwrap().value_as::<&str>(), + Ok("okay") + ); + assert_eq!( + soc.property("new-prop").unwrap().value_as::<&str>(), + Ok("foo") + ); assert!(soc.child("serial@1000").is_some()); // Verify round-trip through serialization let dtb = base.to_dtb(); let fdt = Fdt::new(&dtb).unwrap(); let fdt_soc = fdt.find_node("/soc").unwrap(); - assert_eq!(fdt_soc.property("status").unwrap().as_str(), Ok("okay")); + assert_eq!( + fdt_soc.property("status").unwrap().value_as::<&str>(), + Ok("okay") + ); } #[test] @@ -56,7 +65,10 @@ fn apply_overlay_with_local_fixups() { // Base had max phandle 1, so overlay phandle 1 should be relocated to 2. let dev = base.find_node_mut("/dev@200").unwrap(); - assert_eq!(dev.property("phandle").unwrap().as_u32().unwrap(), 2); + assert_eq!( + dev.property("phandle").unwrap().value_as::().unwrap(), + 2 + ); assert_eq!(dev.property("clocks").unwrap().value(), &[0, 0, 0, 2]); } @@ -68,11 +80,14 @@ fn apply_overlay_with_external_fixups_and_symbols() { ); let uart = base.find_node_mut("/soc/uart@1000").unwrap(); - assert_eq!(uart.property("status").unwrap().as_str(), Ok("okay")); + assert_eq!( + uart.property("status").unwrap().value_as::<&str>(), + Ok("okay") + ); let base_sym = base.root.child("__symbols__").unwrap(); assert_eq!( - base_sym.property("uart0").unwrap().as_str(), + base_sym.property("uart0").unwrap().value_as::<&str>(), Ok("/soc/uart@1000") ); }