Skip to content

Commit e99ecdf

Browse files
committed
feat: support zero-copy deserialization
Signed-off-by: Ahmed Charles <[email protected]>
1 parent 7217bb0 commit e99ecdf

File tree

2 files changed

+144
-24
lines changed

2 files changed

+144
-24
lines changed

ciborium-io/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ pub trait Write {
5353
fn flush(&mut self) -> Result<(), Self::Error>;
5454
}
5555

56+
#[cfg(feature = "std")]
57+
/// Abstracted EOF error.
58+
pub fn eof() -> std::io::Error {
59+
std::io::ErrorKind::UnexpectedEof.into()
60+
}
61+
5662
#[cfg(feature = "std")]
5763
impl<T: std::io::Read> Read for T {
5864
type Error = std::io::Error;
@@ -108,6 +114,12 @@ impl<W: Write + ?Sized> Write for &mut W {
108114
#[derive(Clone, Debug)]
109115
pub struct EndOfFile(());
110116

117+
#[cfg(not(feature = "std"))]
118+
/// Abstracted EOF error.
119+
pub fn eof() -> EndOfFile {
120+
EndOfFile(())
121+
}
122+
111123
#[cfg(not(feature = "std"))]
112124
impl Read for &[u8] {
113125
type Error = EndOfFile;

ciborium/src/de/mod.rs

Lines changed: 132 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub use error::Error;
88

99
use alloc::{string::String, vec::Vec};
1010

11-
use ciborium_io::Read;
11+
use ciborium_io::{eof, Read};
1212
use ciborium_ll::*;
1313
use serde::de::{self, value::BytesDeserializer, Deserializer as _};
1414

@@ -47,14 +47,103 @@ impl<E: de::Error> Expected<E> for Header {
4747
}
4848
}
4949

50+
enum Reference<'b, 'c, T: ?Sized + 'static> {
51+
Borrowed(&'b T),
52+
Copied(&'c T),
53+
}
54+
55+
trait ReadSlice<'de>: Read {
56+
fn read_slice<'a>(&'a mut self, len: usize) -> Result<Reference<'de, 'a, [u8]>, Self::Error>;
57+
}
58+
59+
/// TODO
60+
pub struct Reader<R> {
61+
r: R,
62+
buf: Vec<u8>,
63+
}
64+
65+
impl<R> Reader<R> {
66+
fn new(r: R) -> Self {
67+
Self {
68+
r,
69+
buf: Vec::with_capacity(128),
70+
}
71+
}
72+
}
73+
74+
impl<R: Read> Read for Reader<R> {
75+
type Error = R::Error;
76+
77+
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
78+
self.r.read_exact(data)
79+
}
80+
}
81+
82+
impl<'de, R: Read> ReadSlice<'de> for Reader<R> {
83+
fn read_slice<'a>(&'a mut self, len: usize) -> Result<Reference<'de, 'a, [u8]>, Self::Error> {
84+
self.buf.resize(len, 0);
85+
self.r.read_exact(&mut self.buf)?;
86+
Ok(Reference::Copied(&self.buf[..]))
87+
}
88+
}
89+
90+
/// TODO
91+
pub struct SliceReader<'de> {
92+
_slice: &'de [u8],
93+
buf: &'de [u8],
94+
}
95+
96+
impl<'de> SliceReader<'de> {
97+
fn new(r: &'de [u8]) -> Self {
98+
Self { _slice: r, buf: r }
99+
}
100+
}
101+
102+
impl<'de> Read for SliceReader<'de> {
103+
type Error = <&'de [u8] as ciborium_io::Read>::Error;
104+
105+
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
106+
self.buf.read_exact(data)
107+
}
108+
}
109+
110+
impl<'de> ReadSlice<'de> for SliceReader<'de> {
111+
fn read_slice<'a>(&'a mut self, len: usize) -> Result<Reference<'de, 'a, [u8]>, Self::Error> {
112+
if len > self.buf.len() {
113+
return Err(eof());
114+
}
115+
let (a, b) = self.buf.split_at(len);
116+
self.buf = b;
117+
Ok(Reference::Borrowed(a))
118+
}
119+
}
120+
50121
/// Deserializer
51122
pub struct Deserializer<'b, R> {
52123
decoder: Decoder<R>,
53124
scratch: &'b mut [u8],
54125
recurse: usize,
55126
}
56127

57-
fn noop(_: u8) {}
128+
impl<'a, R: Read> Deserializer<'a, Reader<R>> {
129+
fn from_reader(r: R, scratch: &'a mut [u8], recurse: usize) -> Self {
130+
Self {
131+
decoder: Reader::new(r).into(),
132+
scratch,
133+
recurse,
134+
}
135+
}
136+
}
137+
138+
impl<'a, 'de> Deserializer<'a, SliceReader<'de>> {
139+
fn from_slice(s: &'de [u8], scratch: &'a mut [u8], recurse: usize) -> Self {
140+
Self {
141+
decoder: SliceReader::new(s).into(),
142+
scratch,
143+
recurse,
144+
}
145+
}
146+
}
58147

59148
impl<'a, R: Read> Deserializer<'a, R>
60149
where
@@ -147,6 +236,8 @@ where
147236
}
148237
}
149238

239+
fn noop(_: u8) {}
240+
150241
impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &'a mut Deserializer<'b, R>
151242
where
152243
R::Error: core::fmt::Debug,
@@ -806,11 +897,7 @@ pub fn from_reader_with_buffer<T: de::DeserializeOwned, R: Read>(
806897
where
807898
R::Error: core::fmt::Debug,
808899
{
809-
let mut reader = Deserializer {
810-
decoder: reader.into(),
811-
scratch: scratch_buffer,
812-
recurse: 256,
813-
};
900+
let mut reader = Deserializer::from_reader(reader, scratch_buffer, 256);
814901

815902
T::deserialize(&mut reader)
816903
}
@@ -830,11 +917,7 @@ where
830917
{
831918
let mut scratch = [0; 4096];
832919

833-
let mut reader = Deserializer {
834-
decoder: reader.into(),
835-
scratch: &mut scratch,
836-
recurse: recurse_limit,
837-
};
920+
let mut reader = Deserializer::from_reader(reader, &mut scratch, recurse_limit);
838921

839922
T::deserialize(&mut reader)
840923
}
@@ -844,15 +927,11 @@ where
844927
pub fn deserializer_from_reader_with_buffer<R: Read>(
845928
reader: R,
846929
scratch_buffer: &mut [u8],
847-
) -> Deserializer<'_, R>
930+
) -> Deserializer<'_, Reader<R>>
848931
where
849932
R::Error: core::fmt::Debug,
850933
{
851-
Deserializer {
852-
decoder: reader.into(),
853-
scratch: scratch_buffer,
854-
recurse: 256,
855-
}
934+
Deserializer::from_reader(reader, scratch_buffer, 256)
856935
}
857936

858937
/// Returns a deserializer with a specified scratch buffer
@@ -865,13 +944,42 @@ pub fn deserializer_from_reader_with_buffer_and_recursion_limit<R: Read>(
865944
reader: R,
866945
scratch_buffer: &mut [u8],
867946
recurse_limit: usize,
868-
) -> Deserializer<'_, R>
947+
) -> Deserializer<'_, Reader<R>>
869948
where
870949
R::Error: core::fmt::Debug,
871950
{
872-
Deserializer {
873-
decoder: reader.into(),
874-
scratch: scratch_buffer,
875-
recurse: recurse_limit,
876-
}
951+
Deserializer::from_reader(reader, scratch_buffer, recurse_limit)
952+
}
953+
954+
/// Deserializes as CBOR from a type with [`impl
955+
/// ciborium_io::Read`](ciborium_io::Read) using a 4KB buffer on the stack.
956+
///
957+
/// If you want to deserialize faster at the cost of more memory, consider using
958+
/// [`from_reader_with_buffer`](from_reader_with_buffer) with a larger buffer,
959+
/// for example 64KB.
960+
#[inline]
961+
pub fn from_slice<'de, T: de::Deserialize<'de>>(
962+
reader: &'de [u8],
963+
) -> Result<T, Error<<&'de [u8] as ciborium_io::Read>::Error>>
964+
where
965+
<&'de [u8] as ciborium_io::Read>::Error: core::fmt::Debug,
966+
{
967+
let mut scratch = [0; 4096];
968+
from_slice_with_buffer(reader, &mut scratch)
969+
}
970+
971+
/// Deserializes as CBOR from a type with [`impl
972+
/// ciborium_io::Read`](ciborium_io::Read), using a caller-specific buffer as a
973+
/// temporary scratch space.
974+
#[inline]
975+
pub fn from_slice_with_buffer<'de, T: de::Deserialize<'de>>(
976+
reader: &'de [u8],
977+
scratch_buffer: &mut [u8],
978+
) -> Result<T, Error<<&'de [u8] as ciborium_io::Read>::Error>>
979+
where
980+
<&'de [u8] as ciborium_io::Read>::Error: core::fmt::Debug,
981+
{
982+
let mut reader = Deserializer::from_slice(reader, scratch_buffer, 256);
983+
984+
T::deserialize(&mut reader)
877985
}

0 commit comments

Comments
 (0)