Skip to content

Commit 452a724

Browse files
committed
Add optional heapless v0.9
1 parent 81dd00f commit 452a724

File tree

5 files changed

+199
-2
lines changed

5 files changed

+199
-2
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ jobs:
2020
steps:
2121
- uses: actions/checkout@v3
2222
- uses: dtolnay/rust-toolchain@stable
23-
- run: cargo test --features arrayvec
23+
- run: cargo test --features arrayvec,heapless,heapless-09,alloc
2424

2525
clippy:
2626
runs-on: ubuntu-latest
2727
steps:
2828
- uses: actions/checkout@v3
2929
- uses: dtolnay/rust-toolchain@stable
30-
- run: cargo clippy -- -D warnings
30+
- run: cargo clippy --features arrayvec,heapless,heapless-09,alloc -- -D warnings
3131

3232
fuzz:
3333
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## Unreleased
66

77
- Fixed the `Value` implementation for `Option<T>` so it now serializes and deserializes properly
8+
- Add optional heapless v0.9 support
89

910
## 5.0.0 31-07-25
1011

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ futures = { version = "0.3.30", features = ["executor"], optional = true }
1919
approx = { version = "0.5.1", optional = true }
2020
arrayvec = { version = "0.7.4", default-features = false, optional = true }
2121
heapless = { version = "0.8.0", optional = true }
22+
heapless-09 = { package = "heapless", version = "0.9.0", optional = true }
2223

2324
[dev-dependencies]
2425
approx = "0.5.1"
@@ -32,6 +33,7 @@ std = []
3233
arrayvec = ["dep:arrayvec"]
3334
alloc = []
3435
heapless = ["dep:heapless"]
36+
heapless-09 = ["dep:heapless-09"]
3537
_test = ["dep:futures", "dep:approx", "std", "arrayvec", "alloc", "heapless"]
3638

3739
[lints.rust]

src/heapless_09_impl.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
use core::str::FromStr;
2+
3+
use heapless_09::{String, Vec};
4+
5+
use crate::map::{Key, SerializationError, Value};
6+
7+
// heapless:: Vec
8+
9+
impl<const CAP: usize> Key for Vec<u8, CAP> {
10+
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
11+
if buffer.len() < self.len() + 2 {
12+
return Err(SerializationError::BufferTooSmall);
13+
}
14+
15+
if self.len() > u16::MAX as usize {
16+
return Err(SerializationError::InvalidData);
17+
}
18+
19+
buffer[..2].copy_from_slice(&(self.len() as u16).to_le_bytes());
20+
buffer[2..][..self.len()].copy_from_slice(self);
21+
22+
Ok(self.len() + 2)
23+
}
24+
25+
fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
26+
let total_len = Self::get_len(buffer)?;
27+
28+
if buffer.len() < total_len {
29+
return Err(SerializationError::BufferTooSmall);
30+
}
31+
32+
let data_len = total_len - 2;
33+
34+
let mut output = Vec::new();
35+
output
36+
.extend_from_slice(&buffer[2..][..data_len])
37+
.map_err(|_| SerializationError::InvalidFormat)?;
38+
39+
Ok((output, total_len))
40+
}
41+
42+
fn get_len(buffer: &[u8]) -> Result<usize, SerializationError> {
43+
if buffer.len() < 2 {
44+
return Err(SerializationError::BufferTooSmall);
45+
}
46+
47+
let len = u16::from_le_bytes(buffer[..2].try_into().unwrap());
48+
49+
Ok(len as usize + 2)
50+
}
51+
}
52+
53+
impl<'a, const CAP: usize> Value<'a> for Vec<u8, CAP> {
54+
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
55+
if buffer.len() < self.len() {
56+
return Err(SerializationError::BufferTooSmall);
57+
}
58+
59+
buffer[..self.len()].copy_from_slice(self.as_slice());
60+
Ok(self.len())
61+
}
62+
63+
fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
64+
where
65+
Self: Sized,
66+
{
67+
Vec::try_from(buffer).map_err(|_| SerializationError::InvalidFormat)
68+
}
69+
}
70+
71+
// heapless::String
72+
73+
impl<const CAP: usize> Key for String<CAP> {
74+
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
75+
if buffer.len() < self.len() + 2 {
76+
return Err(SerializationError::InvalidFormat);
77+
}
78+
79+
if self.len() > u16::MAX as usize {
80+
return Err(SerializationError::InvalidData);
81+
}
82+
83+
buffer[..2].copy_from_slice(&(self.len() as u16).to_le_bytes());
84+
buffer[2..][..self.len()].copy_from_slice(self.as_bytes());
85+
86+
Ok(self.len() + 2)
87+
}
88+
89+
fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
90+
let total_len = Self::get_len(buffer)?;
91+
92+
if buffer.len() < total_len {
93+
return Err(SerializationError::BufferTooSmall);
94+
}
95+
96+
let data_len = total_len - 2;
97+
98+
let mut output = String::new();
99+
output
100+
.push_str(
101+
core::str::from_utf8(&buffer[2..][..data_len])
102+
.map_err(|_| SerializationError::InvalidFormat)?,
103+
)
104+
.map_err(|_| SerializationError::InvalidFormat)?;
105+
106+
Ok((output, total_len))
107+
}
108+
109+
fn get_len(buffer: &[u8]) -> Result<usize, SerializationError> {
110+
if buffer.len() < 2 {
111+
return Err(SerializationError::BufferTooSmall);
112+
}
113+
114+
let len = u16::from_le_bytes(buffer[..2].try_into().unwrap());
115+
116+
Ok(len as usize + 2)
117+
}
118+
}
119+
120+
impl<'a, const CAP: usize> Value<'a> for String<CAP> {
121+
fn serialize_into(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
122+
if buffer.len() < self.len() {
123+
return Err(SerializationError::BufferTooSmall);
124+
}
125+
126+
buffer[..self.len()].copy_from_slice(self.as_bytes());
127+
Ok(self.len())
128+
}
129+
130+
fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
131+
where
132+
Self: Sized,
133+
{
134+
let output = String::from_str(
135+
core::str::from_utf8(buffer).map_err(|_| SerializationError::InvalidFormat)?,
136+
)
137+
.map_err(|_| SerializationError::BufferTooSmall)?;
138+
139+
Ok(output)
140+
}
141+
}
142+
143+
#[cfg(test)]
144+
mod tests {
145+
use core::str::FromStr;
146+
147+
use super::*;
148+
149+
#[test]
150+
fn key_serde_heapless_vec() {
151+
let mut buffer = [0; 128];
152+
153+
let val = Vec::<u8, 12>::from_iter([0xAA; 12]);
154+
Key::serialize_into(&val, &mut buffer).unwrap();
155+
let new_val = <Vec<u8, 12> as Key>::deserialize_from(&buffer).unwrap();
156+
157+
assert_eq!((val, 14), new_val);
158+
}
159+
160+
#[test]
161+
fn key_serde_heapless_string() {
162+
let mut buffer = [0; 128];
163+
164+
let val = String::<45>::from_str("Hello world!").unwrap();
165+
Key::serialize_into(&val, &mut buffer).unwrap();
166+
let new_val = <String<45> as Key>::deserialize_from(&buffer).unwrap();
167+
168+
assert_eq!((val, 14), new_val);
169+
}
170+
171+
#[test]
172+
fn value_serde_heapless_vec() {
173+
let mut buffer = [0; 12];
174+
175+
let val = Vec::<u8, 12>::from_iter([0xAA; 12]);
176+
Value::serialize_into(&val, &mut buffer).unwrap();
177+
let new_val = <Vec<u8, 12> as Value>::deserialize_from(&buffer).unwrap();
178+
179+
assert_eq!(val, new_val);
180+
}
181+
182+
#[test]
183+
fn value_serde_heapless_string() {
184+
let mut buffer = [0; 12];
185+
186+
let val = String::<45>::from_str("Hello world!").unwrap();
187+
Value::serialize_into(&val, &mut buffer).unwrap();
188+
let new_val = <String<45> as Value>::deserialize_from(&buffer).unwrap();
189+
190+
assert_eq!(val, new_val);
191+
}
192+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ mod arrayvec_impl;
2222
pub mod cache;
2323
#[cfg(feature = "heapless")]
2424
mod heapless_impl;
25+
#[cfg(feature = "heapless-09")]
26+
mod heapless_09_impl;
2527
mod item;
2628
pub mod map;
2729
pub mod queue;

0 commit comments

Comments
 (0)