Skip to content

Commit

Permalink
Added support for <alloc and heapless> ::{Vec, String} for map::{Key …
Browse files Browse the repository at this point in the history
…and Value}
  • Loading branch information
yanshay committed Nov 4, 2024
1 parent 48a2b38 commit 1adf259
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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 }
heapless = { version = "0.8.0", optional = true }

[dev-dependencies]
approx = "0.5.1"
Expand All @@ -28,7 +29,9 @@ defmt-03 = ["dep:defmt"]
std = []
# Enable the implementation of the map Key trait for ArrayVec and ArrayString
arrayvec = ["dep:arrayvec"]
_test = ["dep:futures", "dep:approx", "std", "arrayvec"]
alloc = []
heapless = ["dep:heapless"]
_test = ["dep:futures", "dep:approx", "std", "arrayvec", "alloc", "heapless"]

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing_repro)'] }
183 changes: 183 additions & 0 deletions src/alloc_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
extern crate alloc;
use crate::map::{Key, SerializationError, Value};
use alloc::{string::String, vec::Vec};

// alloc::Vec

impl Key for Vec<u8> {
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 output = Vec::from(&buffer[2..][..data_len]);

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<'a> Value<'a> for Vec<u8> {
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
if buffer.len() < self.len() {
return Err(SerializationError::BufferTooSmall);
}

buffer[..self.len()].copy_from_slice(self.as_slice());
Ok(self.len())
}

fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
where
Self: Sized,
{
Ok(Vec::from(buffer))
}
}

// alloc::String

impl Key for String {
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 output = String::from(
core::str::from_utf8(&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<'a> Value<'a> for String {
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
if buffer.len() < self.len() {
return Err(SerializationError::BufferTooSmall);
}

buffer[..self.len()].copy_from_slice(self.as_bytes());
Ok(self.len())
}

fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
where
Self: Sized,
{
let output = String::from(
core::str::from_utf8(buffer).map_err(|_| SerializationError::InvalidFormat)?,
);

Ok(output)
}
}

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

use super::*;

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

let val = Vec::from_iter([0xAAu8; 12]);
Key::serialize_into(&val, &mut buffer).unwrap();
let new_val = <Vec<_> as Key>::deserialize_from(&buffer).unwrap();

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

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

let val = String::from("Hello world!");
Key::serialize_into(&val, &mut buffer).unwrap();
let new_val = <String as Key>::deserialize_from(&buffer).unwrap();

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

#[test]
fn value_serde_alloc_vec() {
let mut buffer = [0; 12];

let val = Vec::from_iter([0xAAu8; 12]);
Value::serialize_into(&val, &mut buffer).unwrap();
let new_val = <Vec<_> as Value>::deserialize_from(&buffer).unwrap();

assert_eq!(val, new_val);
}

#[test]
fn value_serde_alloc_string() {
let mut buffer = [0; 12];

let val = String::from("Hello world!");
Value::serialize_into(&val, &mut buffer).unwrap();
let new_val = <String as Value>::deserialize_from(&buffer).unwrap();

assert_eq!(val, new_val);
}
}
193 changes: 193 additions & 0 deletions src/heapless_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use core::str::FromStr;

use heapless::{String, Vec};

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

// heapless:: Vec

impl<const CAP: usize> Key for Vec<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 = Vec::new();
output
.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<'a, const CAP: usize> Value<'a> for Vec<u8, CAP> {
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
if buffer.len() < self.len() {
return Err(SerializationError::BufferTooSmall);
}

buffer[..self.len()].copy_from_slice(self.as_slice());
Ok(self.len())
}

fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
where
Self: Sized,
{
Vec::try_from(buffer).map_err(|_| SerializationError::InvalidFormat)
}
}

// heapless::String

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

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 = String::new();
output
.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)
}
}

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

buffer[..self.len()].copy_from_slice(self.as_bytes());
Ok(self.len())
}

fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
where
Self: Sized,
{
let output = String::from_str(
core::str::from_utf8(buffer).map_err(|_| SerializationError::InvalidFormat)?,
)
.map_err(|_| SerializationError::BufferTooSmall)?;

Ok(output)
}
}


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

use super::*;

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

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

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

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

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

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

#[test]
fn value_serde_heapless_vec() {
let mut buffer = [0; 12];

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

assert_eq!(val, new_val);
}

#[test]
fn value_serde_heapless_string() {
let mut buffer = [0; 12];

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

assert_eq!(val, new_val);
}
}
Loading

0 comments on commit 1adf259

Please sign in to comment.