Skip to content

Commit

Permalink
Add try_from_iter for ArrayMemoryRegion and VecMemoryRegion
Browse files Browse the repository at this point in the history
  • Loading branch information
hdoordt committed Aug 24, 2023
1 parent f2d838c commit 75c1622
Showing 1 changed file with 121 additions and 72 deletions.
193 changes: 121 additions & 72 deletions core/src/memory_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,56 @@ impl<const SIZE: usize> ArrayMemoryRegion<SIZE> {
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<Item = u8>]
pub fn try_from_iter<I: IntoIterator<Item = u8>>(
iter: I,
) -> Result<Self, MemoryRegionFormIterError> {
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")]
Expand Down Expand Up @@ -134,42 +184,7 @@ impl<'a, const SIZE: usize> FromIterator<&'a u8> for ArrayMemoryRegion<SIZE> {

impl<const SIZE: usize> FromIterator<u8> for ArrayMemoryRegion<SIZE> {
fn from_iter<T: IntoIterator<Item = u8>>(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()
}
}

Expand Down Expand Up @@ -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<Item = u8>]
pub fn try_from_iter<I: IntoIterator<Item = u8>>(
iter: I,
) -> Result<Self, MemoryRegionFormIterError> {
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")]
Expand Down Expand Up @@ -267,42 +325,7 @@ impl<'a> FromIterator<&'a u8> for VecMemoryRegion {
#[cfg(feature = "std")]
impl FromIterator<u8> for VecMemoryRegion {
fn from_iter<T: IntoIterator<Item = u8>>(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()
}
}

Expand Down Expand Up @@ -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::*;
Expand Down

0 comments on commit 75c1622

Please sign in to comment.