Skip to content

Commit e719537

Browse files
roman-kashitsynenarxbot
authored andcommitted
feat: transparent conversion between ByteBuf and Vec<u8>
This change makes it possible to decode a serde_bytes::ByteBuf from CBOR data encoded as a Vec<u8> (and in the other direction, too). The ByteBuf -> Vec<u8> direction is not optimal because it allocates an additional buffer, but this should not be a problem because decoding one byte at a time ruins performance anyway. Context: our project migrates from serde_cbor to ciborium. ciborium is faster and generates compact code, but we have old data sets generated using inefficient Vec<u8> encoding. We want to be able to decode them with ciborium, which currently does not provide compatibility between Vec<u8> and ByteBuf as serde_cbor did. Signed-off-by: Roman Kashitsyn <[email protected]>
1 parent b6d9ae2 commit e719537

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

ciborium/src/de/mod.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,11 @@ where
371371
visitor.visit_bytes(&self.scratch[..len])
372372
}
373373

374+
Header::Array(len) => self.recurse(|me| {
375+
let access = Access(me, len);
376+
visitor.visit_seq(access)
377+
}),
378+
374379
header => Err(header.expected("bytes")),
375380
};
376381
}
@@ -397,7 +402,12 @@ where
397402
visitor.visit_byte_buf(buffer)
398403
}
399404

400-
header => Err(header.expected("expected byte buffer")),
405+
Header::Array(len) => self.recurse(|me| {
406+
let access = Access(me, len);
407+
visitor.visit_seq(access)
408+
}),
409+
410+
header => Err(header.expected("byte buffer")),
401411
};
402412
}
403413
}
@@ -412,6 +422,19 @@ where
412422
visitor.visit_seq(access)
413423
}),
414424

425+
Header::Bytes(len) => {
426+
let mut buffer = Vec::new();
427+
428+
let mut segments = self.decoder.bytes(len);
429+
while let Some(mut segment) = segments.pull()? {
430+
while let Some(chunk) = segment.pull(self.scratch)? {
431+
buffer.extend_from_slice(chunk);
432+
}
433+
}
434+
435+
visitor.visit_seq(BytesAccess::<R>(0, buffer, core::marker::PhantomData))
436+
}
437+
415438
header => Err(header.expected("array")),
416439
};
417440
}
@@ -683,6 +706,36 @@ where
683706
}
684707
}
685708

709+
struct BytesAccess<R: Read>(usize, Vec<u8>, core::marker::PhantomData<R>);
710+
711+
impl<'de, R: Read> de::SeqAccess<'de> for BytesAccess<R>
712+
where
713+
R::Error: core::fmt::Debug,
714+
{
715+
type Error = Error<R::Error>;
716+
717+
#[inline]
718+
fn next_element_seed<U: de::DeserializeSeed<'de>>(
719+
&mut self,
720+
seed: U,
721+
) -> Result<Option<U::Value>, Self::Error> {
722+
use de::IntoDeserializer;
723+
724+
if self.0 < self.1.len() {
725+
let byte = self.1[self.0];
726+
self.0 += 1;
727+
seed.deserialize(byte.into_deserializer()).map(Some)
728+
} else {
729+
Ok(None)
730+
}
731+
}
732+
733+
#[inline]
734+
fn size_hint(&self) -> Option<usize> {
735+
Some(self.1.len() - self.0)
736+
}
737+
}
738+
686739
struct TagAccess<'a, 'b, R: Read>(&'a mut Deserializer<'b, R>, usize);
687740

688741
impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &mut TagAccess<'a, 'b, R>

ciborium/tests/codec.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,22 @@ enum Enum {
397397
Tuple(u8, u16),
398398
Struct { first: u8, second: u16 },
399399
}
400+
401+
#[rstest(
402+
input,
403+
case(vec![]),
404+
case(vec![0u8, 1, 2, 3]),
405+
)]
406+
fn byte_vec_serde_bytes_compatibility(input: Vec<u8>) {
407+
use serde_bytes::ByteBuf;
408+
409+
let mut buf = Vec::new();
410+
into_writer(&input, &mut buf).unwrap();
411+
let bytes: ByteBuf = from_reader(&buf[..]).unwrap();
412+
assert_eq!(input, bytes.to_vec());
413+
414+
let mut buf = Vec::new();
415+
into_writer(&ByteBuf::from(input.clone()), &mut buf).unwrap();
416+
let bytes: Vec<u8> = from_reader(&buf[..]).unwrap();
417+
assert_eq!(input, bytes);
418+
}

0 commit comments

Comments
 (0)