Skip to content

Commit

Permalink
Make keys dynamic length
Browse files Browse the repository at this point in the history
  • Loading branch information
diondokter committed Apr 22, 2024
1 parent ef891c3 commit 57ac275
Show file tree
Hide file tree
Showing 6 changed files with 233 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
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 57ac275

Please sign in to comment.