Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make keys dynamic length #48

Merged
merged 2 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading