Skip to content

Commit 33235ff

Browse files
committed
feat: support zero-copy deserialization
Signed-off-by: Ahmed Charles <[email protected]>
1 parent 88cf117 commit 33235ff

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(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::{
1414
de::{self, value::BytesDeserializer, Deserializer as _},
@@ -48,14 +48,103 @@ impl<E: de::Error> Expected<E> for Header {
4848
}
4949
}
5050

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

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

60149
impl<'a, R: Read> Deserializer<'a, R>
61150
where
@@ -148,6 +237,8 @@ where
148237
}
149238
}
150239

240+
fn noop(_: u8) {}
241+
151242
impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &'a mut Deserializer<'b, R>
152243
where
153244
R::Error: core::fmt::Debug,
@@ -874,11 +965,7 @@ pub fn from_reader_with_buffer<T: de::DeserializeOwned, R: Read>(
874965
where
875966
R::Error: core::fmt::Debug,
876967
{
877-
let mut reader = Deserializer {
878-
decoder: reader.into(),
879-
scratch: scratch_buffer,
880-
recurse: 256,
881-
};
968+
let mut reader = Deserializer::from_reader(reader, scratch_buffer, 256);
882969

883970
T::deserialize(&mut reader)
884971
}
@@ -898,11 +985,7 @@ where
898985
{
899986
let mut scratch = [0; 4096];
900987

901-
let mut reader = Deserializer {
902-
decoder: reader.into(),
903-
scratch: &mut scratch,
904-
recurse: recurse_limit,
905-
};
988+
let mut reader = Deserializer::from_reader(reader, &mut scratch, recurse_limit);
906989

907990
T::deserialize(&mut reader)
908991
}
@@ -912,15 +995,11 @@ where
912995
pub fn deserializer_from_reader_with_buffer<R: Read>(
913996
reader: R,
914997
scratch_buffer: &mut [u8],
915-
) -> Deserializer<'_, R>
998+
) -> Deserializer<'_, Reader<R>>
916999
where
9171000
R::Error: core::fmt::Debug,
9181001
{
919-
Deserializer {
920-
decoder: reader.into(),
921-
scratch: scratch_buffer,
922-
recurse: 256,
923-
}
1002+
Deserializer::from_reader(reader, scratch_buffer, 256)
9241003
}
9251004

9261005
/// Returns a deserializer with a specified scratch buffer
@@ -933,13 +1012,42 @@ pub fn deserializer_from_reader_with_buffer_and_recursion_limit<R: Read>(
9331012
reader: R,
9341013
scratch_buffer: &mut [u8],
9351014
recurse_limit: usize,
936-
) -> Deserializer<'_, R>
1015+
) -> Deserializer<'_, Reader<R>>
9371016
where
9381017
R::Error: core::fmt::Debug,
9391018
{
940-
Deserializer {
941-
decoder: reader.into(),
942-
scratch: scratch_buffer,
943-
recurse: recurse_limit,
944-
}
1019+
Deserializer::from_reader(reader, scratch_buffer, recurse_limit)
1020+
}
1021+
1022+
/// Deserializes as CBOR from a type with [`impl
1023+
/// ciborium_io::Read`](ciborium_io::Read) using a 4KB buffer on the stack.
1024+
///
1025+
/// If you want to deserialize faster at the cost of more memory, consider using
1026+
/// [`from_reader_with_buffer`](from_reader_with_buffer) with a larger buffer,
1027+
/// for example 64KB.
1028+
#[inline]
1029+
pub fn from_slice<'de, T: de::Deserialize<'de>>(
1030+
reader: &'de [u8],
1031+
) -> Result<T, Error<<&'de [u8] as ciborium_io::Read>::Error>>
1032+
where
1033+
<&'de [u8] as ciborium_io::Read>::Error: core::fmt::Debug,
1034+
{
1035+
let mut scratch = [0; 4096];
1036+
from_slice_with_buffer(reader, &mut scratch)
1037+
}
1038+
1039+
/// Deserializes as CBOR from a type with [`impl
1040+
/// ciborium_io::Read`](ciborium_io::Read), using a caller-specific buffer as a
1041+
/// temporary scratch space.
1042+
#[inline]
1043+
pub fn from_slice_with_buffer<'de, T: de::Deserialize<'de>>(
1044+
reader: &'de [u8],
1045+
scratch_buffer: &mut [u8],
1046+
) -> Result<T, Error<<&'de [u8] as ciborium_io::Read>::Error>>
1047+
where
1048+
<&'de [u8] as ciborium_io::Read>::Error: core::fmt::Debug,
1049+
{
1050+
let mut reader = Deserializer::from_slice(reader, scratch_buffer, 256);
1051+
1052+
T::deserialize(&mut reader)
9451053
}

0 commit comments

Comments
 (0)