Skip to content

Commit

Permalink
Merge pull request #48 from tweedegolf/dyn-len-key
Browse files Browse the repository at this point in the history
Make keys dynamic length
  • Loading branch information
diondokter authored Apr 22, 2024
2 parents ef891c3 + 074c560 commit 90f7d49
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: cargo test
- run: cargo test --features arrayvec

clippy:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"rust-analyzer.cargo.allTargets": false,
"rust-analyzer.linkedProjects": [
"Cargo.toml",
"fuzz/Cargo.toml",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
and carries a predefined error subtype.
- Added `erase_all` function as a helper to erase the flash in a region.
- *Breaking:* Changed the way that queue iteration works. Now there's an `iter` function instead of two separate `peek_many` and `pop_many` functions. The new iter returns an entry from which you can get the data that was just peeked. If you want to pop it, then call the pop function on the entry.
- Added `arrayvec` feature that when activated impls the `Key` trait for `ArrayVec` and `ArrayString`.

## 1.0.0 01-03-24

Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ embedded-storage-async = "0.4.1"
defmt = { version = "0.3", optional = true }
futures = { version = "0.3.30", features = ["executor"], optional = true }
approx = { version = "0.5.1", optional = true }
arrayvec = { version = "0.7.4", default-features = false, optional = true }

[dev-dependencies]
approx = "0.5.1"
Expand All @@ -25,4 +26,6 @@ futures-test = "0.3.30"
[features]
defmt-03 = ["dep:defmt"]
std = []
_test = ["dep:futures", "dep:approx", "std"]
# Enable the implementation of the map Key trait for ArrayVec and ArrayString
arrayvec = ["dep:arrayvec"]
_test = ["dep:futures", "dep:approx", "std", "arrayvec"]
123 changes: 123 additions & 0 deletions src/arrayvec_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use arrayvec::{ArrayString, ArrayVec};

use crate::map::{Key, SerializationError};

impl<const CAP: usize> Key for ArrayVec<u8, CAP> {
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
if buffer.len() < self.len() + 2 {
return Err(SerializationError::BufferTooSmall);
}

if self.len() > u16::MAX as usize {
return Err(SerializationError::InvalidData);
}

buffer[..2].copy_from_slice(&(self.len() as u16).to_le_bytes());
buffer[2..][..self.len()].copy_from_slice(self);

Ok(self.len() + 2)
}

fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
let total_len = Self::get_len(buffer)?;

if buffer.len() < total_len {
return Err(SerializationError::BufferTooSmall);
}

let data_len = total_len - 2;

let mut output = ArrayVec::new();
output
.try_extend_from_slice(&buffer[2..][..data_len])
.map_err(|_| SerializationError::InvalidFormat)?;

Ok((output, total_len))
}

fn get_len(buffer: &[u8]) -> Result<usize, SerializationError> {
if buffer.len() < 2 {
return Err(SerializationError::BufferTooSmall);
}

let len = u16::from_le_bytes(buffer[..2].try_into().unwrap());

Ok(len as usize + 2)
}
}

impl<const CAP: usize> Key for ArrayString<CAP> {
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
if buffer.len() < self.len() + 2 {
return Err(SerializationError::BufferTooSmall);
}

if self.len() > u16::MAX as usize {
return Err(SerializationError::InvalidData);
}

buffer[..2].copy_from_slice(&(self.len() as u16).to_le_bytes());
buffer[2..][..self.len()].copy_from_slice(self.as_bytes());

Ok(self.len() + 2)
}

fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
let total_len = Self::get_len(buffer)?;

if buffer.len() < total_len {
return Err(SerializationError::BufferTooSmall);
}

let data_len = total_len - 2;

let mut output = ArrayString::new();
output
.try_push_str(
core::str::from_utf8(&buffer[2..][..data_len])
.map_err(|_| SerializationError::InvalidFormat)?,
)
.map_err(|_| SerializationError::InvalidFormat)?;

Ok((output, total_len))
}

fn get_len(buffer: &[u8]) -> Result<usize, SerializationError> {
if buffer.len() < 2 {
return Err(SerializationError::BufferTooSmall);
}

let len = u16::from_le_bytes(buffer[..2].try_into().unwrap());

Ok(len as usize + 2)
}
}

#[cfg(test)]
mod tests {
use core::str::FromStr;

use super::*;

#[test]
fn serde_arrayvec() {
let mut buffer = [0; 128];

let val = ArrayVec::<u8, 12>::from_iter([0xAA; 12]);
val.serialize_into(&mut buffer).unwrap();
let new_val = ArrayVec::<u8, 12>::deserialize_from(&buffer).unwrap();

assert_eq!((val, 14), new_val);
}

#[test]
fn serde_arraystring() {
let mut buffer = [0; 128];

let val = ArrayString::<45>::from_str("Hello world!").unwrap();
val.serialize_into(&mut buffer).unwrap();
let new_val = ArrayString::<45>::deserialize_from(&buffer).unwrap();

assert_eq!((val, 14), new_val);
}
}
16 changes: 12 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use core::{
ops::{Deref, DerefMut, Range},
};
use embedded_storage_async::nor_flash::NorFlash;
use map::MapValueError;
use map::SerializationError;

#[cfg(feature = "arrayvec")]
mod arrayvec_impl;
pub mod cache;
mod item;
pub mod map;
Expand Down Expand Up @@ -377,8 +379,14 @@ pub enum Error<S> {
BufferTooBig,
/// A provided buffer was to small to be used (usize is size needed)
BufferTooSmall(usize),
/// A map value error
MapValueError(MapValueError),
/// A serialization error (from the key or value)
SerializationError(SerializationError),
}

impl<S> From<SerializationError> for Error<S> {
fn from(v: SerializationError) -> Self {
Self::SerializationError(v)
}
}

impl<S: PartialEq> PartialEq for Error<S> {
Expand Down Expand Up @@ -407,7 +415,7 @@ where
f,
"A provided buffer was to small to be used. Needed was {needed}"
),
Error::MapValueError(value) => write!(f, "Map value error: {value}"),
Error::SerializationError(value) => write!(f, "Map value error: {value}"),
}
}
}
Expand Down
Loading

0 comments on commit 90f7d49

Please sign in to comment.