From 75c1622628a697b0dbfd37abf19cd931e1b522dd Mon Sep 17 00:00:00 2001 From: Henk Oordt Date: Thu, 24 Aug 2023 09:36:17 +0200 Subject: [PATCH] Add try_from_iter for ArrayMemoryRegion and VecMemoryRegion --- core/src/memory_region.rs | 193 ++++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 72 deletions(-) diff --git a/core/src/memory_region.rs b/core/src/memory_region.rs index 70c927c..7d71c02 100644 --- a/core/src/memory_region.rs +++ b/core/src/memory_region.rs @@ -103,6 +103,56 @@ impl ArrayMemoryRegion { self.data.set_len(data_len); self.data.as_mut_ptr().copy_from(data_ptr, data_len); } + + /// Try to build a [ArrayMemoryRegion] from an [IntoIterator] + pub fn try_from_iter>( + iter: I, + ) -> Result { + use MemoryRegionFormIterError::*; + let mut iter = iter.into_iter(); + + match iter.next() { + Some(MEMORY_REGION_IDENTIFIER) => {} + Some(id) => return Err(InvalidIdentifier(id)), + None => return Err(NotEnoughItems), + } + + let start_address = u64::from_le_bytes([ + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + ]); + + let length = u64::from_le_bytes([ + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + ]); + + if length > SIZE as u64 { + return Err(LengthTooBig(length)); + } + + // This call panics if length > SIZE + // and `iter` does indeed contain more than SIZE items, + // but we've just covered that case + let data = ArrayVec::from_iter(iter.take(length as usize)); + + Ok(Self { + start_address, + data, + }) + } } #[cfg(feature = "std")] @@ -134,42 +184,7 @@ impl<'a, const SIZE: usize> FromIterator<&'a u8> for ArrayMemoryRegion { impl FromIterator for ArrayMemoryRegion { fn from_iter>(iter: T) -> Self { - let mut iter = iter.into_iter(); - - assert_eq!( - iter.next().unwrap(), - MEMORY_REGION_IDENTIFIER, - "The given iterator is not for a memory region" - ); - - let start_address = u64::from_le_bytes([ - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - ]); - - let length = u64::from_le_bytes([ - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - ]); - - let data = ArrayVec::from_iter(iter.take(length as usize)); - - Self { - start_address, - data, - } + Self::try_from_iter(iter).unwrap() } } @@ -234,6 +249,49 @@ impl VecMemoryRegion { self.data.as_mut_ptr().copy_from(data_ptr, data_len); } + + /// Try to build a [VecMemoryRegion] from an [IntoIterator] + pub fn try_from_iter>( + iter: I, + ) -> Result { + use MemoryRegionFormIterError::*; + let mut iter = iter.into_iter(); + + match iter.next() { + Some(MEMORY_REGION_IDENTIFIER) => {} + Some(id) => return Err(InvalidIdentifier(id)), + None => return Err(NotEnoughItems), + } + + let start_address = u64::from_le_bytes([ + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + ]); + + let length = u64::from_le_bytes([ + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + iter.next().ok_or(NotEnoughItems)?, + ]); + + let data = Vec::from_iter(iter.take(length as usize)); + + Ok(Self { + start_address, + data, + }) + } } #[cfg(feature = "std")] @@ -267,42 +325,7 @@ impl<'a> FromIterator<&'a u8> for VecMemoryRegion { #[cfg(feature = "std")] impl FromIterator for VecMemoryRegion { fn from_iter>(iter: T) -> Self { - let mut iter = iter.into_iter(); - - assert_eq!( - iter.next().unwrap(), - MEMORY_REGION_IDENTIFIER, - "The given iterator is not for a memory region" - ); - - let start_address = u64::from_le_bytes([ - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - ]); - - let length = u64::from_le_bytes([ - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - ]); - - let data = Vec::from_iter(iter.take(length as usize)); - - Self { - start_address, - data, - } + Self::try_from_iter(iter).unwrap() } } @@ -433,6 +456,32 @@ impl<'a> Iterator for MemoryRegionIterator<'a> { impl<'a> ExactSizeIterator for MemoryRegionIterator<'a> {} +#[derive(Debug)] +/// Specifies what went wrong building a [MemoryRegion] from an iterator +pub enum MemoryRegionFormIterError { + /// The given iterator is not for a memory region. + /// First item from iterator yielded invalid identifier. Expected [MEMORY_REGION_IDENTIFIER] + InvalidIdentifier(u8), + /// Iterator specified length too big for declared region + LengthTooBig(u64), + /// Iterator did not yield enough items to build memory region + NotEnoughItems, +} + +impl core::fmt::Display for MemoryRegionFormIterError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use MemoryRegionFormIterError::*; + match self { + InvalidIdentifier(id) => write!(f, "Iterator is not for a memory region. Started with {id}, expected {MEMORY_REGION_IDENTIFIER}"), + LengthTooBig(len) => write!(f, "Iterator specified length too big for declared region: {len}"), + NotEnoughItems => write!(f, "Iterator did not yield enough items to build memory region"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for MemoryRegionFormIterError {} + #[cfg(test)] mod tests { use super::*;