Support multiple versions of heapless and heapless bytes

This allows better backwards compatibility and serializing to heapless directly.

Given https://github.com/trussed-dev/cbor-smol/issues/11 I went with a breaking change update
(the `Serializer` field was public and existing implementations are put behind a breaking change)
This commit is contained in:
Sosthène Guédon
2024-10-09 16:06:10 +02:00
committed by sosthene-nitrokey
parent 3e9e218f55
commit 40708abdc5
5 changed files with 182 additions and 87 deletions
+11 -3
View File
@@ -13,16 +13,24 @@ categories = ["development-tools", "embedded"]
[dependencies]
delog = "0.1.0-alpha.3"
heapless = "0.7"
heapless-bytes = "0.3"
heapless-bytes-v0-3 = { package = "heapless-bytes", version = "0.3", optional = true }
heapless-bytes-v0-4 = { package = "heapless-bytes", version = "0.4", optional = true }
heapless-v0-7 = { package = "heapless", version = "0.7", optional = true }
heapless-v0-8 = { package = "heapless", version = "0.8", optional = true }
serde = { version = "1", default-features = false }
[dev-dependencies]
heapless = { version = "0.7", features = ["serde"] }
heapless-v0-8 = { package = "heapless", version = "0.8", features = ["serde"] }
heapless-bytes-v0-4 = { package = "heapless-bytes", version = "0.4" }
serde = { version = "1", default-features = false, features = ["derive"] }
serde_bytes = "0.11.12"
[features]
heapless-bytes-v0-3 = ["dep:heapless-bytes-v0-3"]
heapless-bytes-v0-4 = ["dep:heapless-bytes-v0-4"]
heapless-v0-7 = ["dep:heapless-v0-7"]
heapless-v0-8 = ["dep:heapless-v0-8"]
log-all = []
log-none = []
log-info = []
+23 -11
View File
@@ -812,7 +812,8 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
// strings to fields (and the mapping from bytes to fields can be optimized out).
let length = self.raw_deserialize_u32(major)? as usize;
let bytes: &'de [u8] = self.try_take_n(length)?;
let string_slice = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?;
let string_slice =
core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?;
visitor.visit_borrowed_str(string_slice)
}
MAJOR_POSINT => self.deserialize_u64(visitor),
@@ -1054,17 +1055,28 @@ mod tests {
}
}
#[cfg_attr(
not(feature = "heapless-bytes-v0-4"),
ignore = "Enable heapless-bytes-v0-4 feature"
)]
#[test]
fn de_bytes() {
let mut buf = [0u8; 64];
#[cfg(feature = "heapless-bytes-v0-4")]
{
let mut buf = [0u8; 64];
let slice = b"thank you postcard!";
let bytes = crate::Bytes::<64>::from_slice(slice).unwrap();
let ser = cbor_serialize(&bytes, &mut buf).unwrap();
println!("serialized bytes = {:?}", ser);
let de: crate::Bytes<64> = from_bytes(&buf).unwrap();
println!("deserialized bytes = {:?}", &de);
assert_eq!(&de, slice);
let slice = b"thank you postcard!";
let bytes = heapless_bytes_v0_4::Bytes::<64>::try_from(slice).unwrap();
let ser = cbor_serialize(&bytes, &mut buf).unwrap();
println!("serialized bytes = {:?}", ser);
let de: heapless_bytes_v0_4::Bytes<64> = from_bytes(&buf).unwrap();
println!("deserialized bytes = {:?}", &de);
assert_eq!(&de, slice);
}
#[cfg(not(feature = "heapless-bytes-v0-4"))]
{
panic!("This test must be run with the heapless-v0-4 feature")
}
}
#[test]
@@ -1072,10 +1084,10 @@ mod tests {
let mut buf = [0u8; 64];
let string_slice = "thank you postcard, for blazing the path 🐝";
let mut string = heapless::String::<64>::new();
let mut string = heapless_v0_8::String::<64>::new();
string.push_str(string_slice).unwrap();
let _n = cbor_serialize(&string, &mut buf);
let de: heapless::String<64> = from_bytes(&buf).unwrap();
let de: heapless_v0_8::String<64> = from_bytes(&buf).unwrap();
assert_eq!(de, string_slice);
}
+1 -1
View File
@@ -3,7 +3,7 @@
use core::fmt::{Display, Formatter};
/// This is the Result type used by cbor-smol.
pub type Result<T> = core::result::Result<T, Error>;
pub type Result<T, Err = Error> = core::result::Result<T, Err>;
/// This is the error type used by cbor-smol
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+20 -16
View File
@@ -4,38 +4,41 @@
extern crate delog;
generate_macros!();
pub use heapless_bytes::Bytes;
pub(crate) mod consts;
pub mod de;
pub mod error;
pub mod ser;
pub use error::{Error, Result};
use ser::Writer;
// pub use de::from_bytes;
// pub use de::take_from_bytes;
/// Serialize an object to a `Writer`
///
/// Returns the amount of bytes written to the writer
pub fn cbor_serialize_to<'a, T: ?Sized + serde::Serialize, W: Writer>(
object: &T,
writer: W,
) -> Result<usize> {
let mut serializer = ser::Serializer::new(writer);
object.serialize(&mut serializer)?;
Ok(serializer.written())
}
// kudos to postcard, this is much nicer than returning size
pub fn cbor_serialize<'a, T: ?Sized + serde::Serialize>(
object: &T,
buffer: &'a mut [u8],
) -> Result<&'a [u8]> {
let writer = ser::SliceWriter::new(buffer);
let mut ser = ser::Serializer::new(writer);
object.serialize(&mut ser)?;
let writer = ser.into_inner();
let size = writer.bytes_written();
Ok(&buffer[..size])
let mut buf = &mut *buffer;
let written = cbor_serialize_to(object, &mut buf)?;
Ok(&buffer[..written])
}
#[cfg(feature = "heapless-bytes-v0-3")]
/// Append serialization of object to existing bytes, returning length of serialized object.
pub fn cbor_serialize_extending_bytes<T: ?Sized + serde::Serialize, const N: usize>(
object: &T,
bytes: &mut Bytes<N>,
bytes: &mut heapless_bytes_v0_3::Bytes<N>,
) -> Result<usize> {
let len_before = bytes.len();
let mut ser = ser::Serializer::new(bytes);
@@ -45,11 +48,12 @@ pub fn cbor_serialize_extending_bytes<T: ?Sized + serde::Serialize, const N: usi
Ok(ser.into_inner().len() - len_before)
}
#[cfg(feature = "heapless-bytes-v0-3")]
/// Serialize object into newly allocated Bytes.
pub fn cbor_serialize_bytes<T: ?Sized + serde::Serialize, const N: usize>(
object: &T,
) -> Result<Bytes<N>> {
let mut data = Bytes::<N>::new();
) -> Result<heapless_bytes_v0_3::Bytes<N>> {
let mut data = heapless_bytes_v0_3::Bytes::<N>::new();
cbor_serialize_extending_bytes(object, &mut data)?;
Ok(data)
}
+127 -56
View File
@@ -2,6 +2,8 @@ use super::error::{Error, Result};
use serde::ser;
use serde::Serialize;
use core::mem;
use crate::consts::*;
// pub fn to_slice<'a, 'b, T>(value: &'a T, buf: &'b mut [u8]) -> Result<&'b mut [u8]>
@@ -16,86 +18,157 @@ pub trait Writer {
type Error: Into<Error>;
/// Attempts to write an entire buffer into this write.
fn write_all(&mut self, buf: &[u8]) -> core::result::Result<(), Self::Error>;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error>;
}
#[derive(Debug)]
pub struct SliceWriter<'a> {
slice: &'a mut [u8],
index: usize,
}
impl<'a> SliceWriter<'a> {
/// Wraps a mutable slice so it can be used as a `Writer`.
pub fn new(slice: &'a mut [u8]) -> SliceWriter<'a> {
SliceWriter { slice, index: 0 }
}
/// Returns the number of bytes written to the underlying slice.
pub fn bytes_written(&self) -> usize {
self.index
}
/// Returns the underlying slice.
pub fn into_inner(self) -> &'a mut [u8] {
self.slice
}
}
impl<'a> Writer for SliceWriter<'a> {
impl<'a> Writer for &'a mut [u8] {
type Error = Error;
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
fn write_all<'b>(&'b mut self, buf: &[u8]) -> Result<()> {
let l = buf.len();
if self.slice.len() - self.index < l {
if self.len() < l {
// This buffer will not fit in our slice
return Err(Error::SerializeBufferFull(self.index));
return Err(Error::SerializeBufferFull(0));
}
self.slice[self.index..][..l].copy_from_slice(buf);
self.index += l;
let (current, rem) = mem::take(self).split_at_mut(l);
current.copy_from_slice(buf);
*self = rem;
Ok(())
}
}
impl<'a, const N: usize> Writer for &'a mut crate::Bytes<N> {
#[cfg(feature = "heapless-bytes-v0-3")]
impl<const N: usize> Writer for heapless_bytes_v0_3::Bytes<N> {
type Error = Error;
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.extend_from_slice(buf)
.map_err(|_| Error::SerializeBufferFull(buf.len()))
.or(Err(Error::SerializeBufferFull(self.len())))
}
}
pub struct Serializer<W>
// where
// W: Writer,
{
pub writer: W,
#[cfg(feature = "heapless-bytes-v0-4")]
impl<const N: usize> Writer for heapless_bytes_v0_4::Bytes<N> {
type Error = Error;
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.extend_from_slice(buf)
.or(Err(Error::SerializeBufferFull(self.len())))
}
}
#[cfg(feature = "heapless-v0-7")]
impl<const N: usize> Writer for heapless_v0_7::Vec<u8, N> {
type Error = Error;
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.extend_from_slice(buf)
.or(Err(Error::SerializeBufferFull(self.len())))
}
}
#[cfg(feature = "heapless-v0-8")]
impl<const N: usize> Writer for heapless_v0_8::Vec<u8, N> {
type Error = Error;
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
self.extend_from_slice(buf)
.or(Err(Error::SerializeBufferFull(self.len())))
}
}
impl<'a, T: Writer> Writer for &'a mut T {
type Error = T::Error;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
(**self).write_all(buf)
}
}
// #[derive(Debug)]
// pub struct SliceWriter<'a> {
// slice: &'a mut [u8],
// index: usize,
// }
// impl<'a> SliceWriter<'a> {
// /// Wraps a mutable slice so it can be used as a `Writer`.
// pub fn new(slice: &'a mut [u8]) -> SliceWriter<'a> {
// SliceWriter { slice, index: 0 }
// }
// /// Returns the number of bytes written to the underlying slice.
// pub fn bytes_written(&self) -> usize {
// self.index
// }
// /// Returns the underlying slice.
// pub fn into_inner(self) -> &'a mut [u8] {
// self.slice
// }
// }
// impl<'a> Writer for SliceWriter<'a> {
// type Error = Error;
// fn write_all(&mut self, buf: &[u8]) -> Result<()> {
// let l = buf.len();
// if self.slice.len() - self.index < l {
// // This buffer will not fit in our slice
// return Err(Error::SerializeBufferFull(self.index));
// }
// self.slice[self.index..][..l].copy_from_slice(buf);
// self.index += l;
// Ok(())
// }
// }
// impl<'a, const N: usize> Writer for &'a mut crate::Bytes<N> {
// type Error = Error;
// fn write_all(&mut self, buf: &[u8]) -> Result<()> {
// self.extend_from_slice(buf)
// .map_err(|_| Error::SerializeBufferFull(buf.len()))
// }
// }
struct WrittenWriter<W> {
writer: W,
written: usize,
}
impl<W: Writer> Writer for WrittenWriter<W> {
type Error = W::Error;
fn write_all(&mut self, buf: &[u8]) -> core::result::Result<(), Self::Error> {
self.written += buf.len();
self.writer.write_all(buf)
}
}
pub struct Serializer<W> {
inner: WrittenWriter<W>,
}
impl<W: Writer> Serializer<W> {
#[inline]
pub fn new(writer: W) -> Self {
Serializer {
writer,
// packed: false,
// enum_as_map: true,
inner: WrittenWriter { writer, written: 0 },
}
}
pub fn written(&self) -> usize {
self.inner.written
}
/// Unwrap the `Writer` from the `Serializer`.
#[inline]
pub fn into_inner(self) -> W {
self.writer
self.inner.writer
}
#[inline]
fn write_u8(&mut self, major: u8, value: u8) -> Result<()> {
if value <= 0x17 {
self.writer.write_all(&[major << MAJOR_OFFSET | value])
self.inner.write_all(&[major << MAJOR_OFFSET | value])
} else {
let buf = [major << MAJOR_OFFSET | 24, value];
self.writer.write_all(&buf)
self.inner.write_all(&buf)
}
.map_err(|e| e.into())
}
@@ -107,7 +180,7 @@ impl<W: Writer> Serializer<W> {
} else {
let mut buf = [major << MAJOR_OFFSET | 25, 0, 0];
buf[1..].copy_from_slice(&value.to_be_bytes());
self.writer.write_all(&buf).map_err(|e| e.into())
self.inner.write_all(&buf).map_err(|e| e.into())
}
}
@@ -118,7 +191,7 @@ impl<W: Writer> Serializer<W> {
} else {
let mut buf = [major << MAJOR_OFFSET | 26, 0, 0, 0, 0];
buf[1..].copy_from_slice(&value.to_be_bytes());
self.writer.write_all(&buf).map_err(|e| e.into())
self.inner.write_all(&buf).map_err(|e| e.into())
}
}
@@ -129,7 +202,7 @@ impl<W: Writer> Serializer<W> {
} else {
let mut buf = [major << MAJOR_OFFSET | 27, 0, 0, 0, 0, 0, 0, 0, 0];
buf[1..].copy_from_slice(&value.to_be_bytes());
self.writer.write_all(&buf).map_err(|e| e.into())
self.inner.write_all(&buf).map_err(|e| e.into())
}
}
@@ -145,7 +218,7 @@ impl<W: Writer> Serializer<W> {
false
}
None => {
self.writer
self.inner
.write_all(&[major << MAJOR_OFFSET | 31])
.map_err(|e| e.into())?;
true
@@ -186,7 +259,7 @@ where
#[inline]
fn serialize_bool(self, value: bool) -> Result<()> {
let value = if value { VALUE_TRUE } else { VALUE_FALSE };
self.writer.write_all(&[value]).map_err(|e| e.into())
self.inner.write_all(&[value]).map_err(|e| e.into())
}
#[inline]
@@ -259,20 +332,18 @@ where
#[inline]
fn serialize_str(self, value: &str) -> Result<()> {
self.write_u64(MAJOR_STR, value.len() as u64)?;
self.writer
.write_all(value.as_bytes())
.map_err(|e| e.into())
self.inner.write_all(value.as_bytes()).map_err(|e| e.into())
}
#[inline]
fn serialize_bytes(self, value: &[u8]) -> Result<()> {
self.write_u64(MAJOR_BYTES, value.len() as u64)?;
self.writer.write_all(value).map_err(|e| e.into())
self.inner.write_all(value).map_err(|e| e.into())
}
#[inline]
fn serialize_none(self) -> Result<()> {
self.writer.write_all(&[VALUE_NULL]).map_err(|e| e.into())
self.inner.write_all(&[VALUE_NULL]).map_err(|e| e.into())
}
#[inline]
@@ -587,7 +658,7 @@ where
#[inline]
fn end_inner(self) -> Result<()> {
if self.needs_eof {
self.ser.writer.write_all(&[0xff]).map_err(|e| e.into())
self.ser.inner.write_all(&[0xff]).map_err(|e| e.into())
} else {
Ok(())
}