Skip to content

Commit 5351e18

Browse files
authored
tls_codec: optimize serde serialized size using serde_bytes (#2022)
1 parent 76a6717 commit 5351e18

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

tls_codec/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- [#2022](https://github.com/RustCrypto/formats/pull/2022) Optimize serde serialization of byte vectors using `serde_bytes`. Deserialization of the old serialized format will still work, but newly serialized instances of `VLBytes` will have the new format.
11+
1012
## 0.4.2
1113

1214
- [#1628](https://github.com/RustCrypto/formats/pull/1628) Bump MSRV to 1.74

tls_codec/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ zeroize = { version = "1.8", default-features = false, features = [
2121
arbitrary = { version = "1.4", features = ["derive"], optional = true }
2222
tls_codec_derive = { version = "=0.4.2", path = "./derive", optional = true }
2323
serde = { version = "1.0.184", features = ["derive"], optional = true }
24+
serde_bytes = { version = "0.11.17", optional = true }
2425

2526
[dev-dependencies]
2627
criterion = { version = "0.6", default-features = false }
28+
ciborium = "0.2.2"
2729

2830
[features]
2931
default = ["std"]
3032
arbitrary = ["std", "dep:arbitrary"]
3133
derive = ["tls_codec_derive"]
32-
serde = ["std", "dep:serde"]
34+
serde = ["std", "dep:serde", "dep:serde_bytes"]
3335
mls = [] # In MLS variable length vectors are limited compared to QUIC.
3436
std = ["tls_codec_derive?/std"]
3537
conditional_deserialization = [

tls_codec/src/quic_vec.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ macro_rules! impl_vl_bytes_generic {
298298
#[cfg_attr(feature = "std", derive(Zeroize))]
299299
#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
300300
pub struct VLBytes {
301+
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_bytes::serialize"))]
302+
#[cfg_attr(
303+
feature = "serde",
304+
serde(deserialize_with = "serde_impl::de_vec_bytes_compat")
305+
)]
301306
vec: Vec<u8>,
302307
}
303308

@@ -385,6 +390,56 @@ impl Size for &VLBytes {
385390
}
386391
}
387392

393+
#[cfg(feature = "serde")]
394+
mod serde_impl {
395+
use std::{fmt, vec::Vec};
396+
397+
use serde::{Deserializer, de};
398+
399+
pub(super) fn de_vec_bytes_compat<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
400+
where
401+
D: Deserializer<'de>,
402+
{
403+
struct BytesOrSeq;
404+
405+
impl<'de> de::Visitor<'de> for BytesOrSeq {
406+
type Value = Vec<u8>;
407+
408+
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409+
f.write_str("either a byte blob or a sequence of u8")
410+
}
411+
412+
// New format (native bytes; e.g., CBOR/Bincode/Msgpack)
413+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
414+
where
415+
E: de::Error,
416+
{
417+
Ok(v.to_vec())
418+
}
419+
420+
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
421+
where
422+
E: de::Error,
423+
{
424+
Ok(v)
425+
}
426+
427+
// Old format (seq of u8)
428+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
429+
where
430+
A: de::SeqAccess<'de>,
431+
{
432+
let mut out = Vec::new();
433+
while let Some(b) = seq.next_element::<u8>()? {
434+
out.push(b);
435+
}
436+
Ok(out)
437+
}
438+
}
439+
440+
deserializer.deserialize_any(BytesOrSeq)
441+
}
442+
}
388443
pub struct VLByteSlice<'a>(pub &'a [u8]);
389444

390445
impl fmt::Debug for VLByteSlice<'_> {

tls_codec/tests/serde_impls.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![cfg(feature = "serde")]
2+
3+
use tls_codec::VLBytes;
4+
5+
// Old VLBytes without serde bytes serialization
6+
#[derive(serde::Serialize, serde::Deserialize)]
7+
struct OldVLBytes {
8+
vec: Vec<u8>,
9+
}
10+
11+
impl From<VLBytes> for OldVLBytes {
12+
fn from(v: VLBytes) -> Self {
13+
OldVLBytes { vec: v.into() }
14+
}
15+
}
16+
17+
#[test]
18+
fn serde_impls() {
19+
let value = VLBytes::from(vec![32; 128]);
20+
let old_value: OldVLBytes = value.clone().into();
21+
let mut new_serialized = Vec::new();
22+
ciborium::into_writer(&value, &mut new_serialized).unwrap();
23+
let mut old_serialized = Vec::new();
24+
ciborium::into_writer(&old_value, &mut old_serialized).unwrap();
25+
26+
// Serialization format has changed
27+
assert_ne!(new_serialized, old_serialized);
28+
assert!(new_serialized.len() < old_serialized.len());
29+
30+
// We should be able to deserialize both into the new format
31+
let deserialized: VLBytes = ciborium::from_reader(new_serialized.as_slice()).unwrap();
32+
let old_deserialized: VLBytes = ciborium::from_reader(old_serialized.as_slice()).unwrap();
33+
34+
assert_eq!(deserialized, old_deserialized);
35+
}

0 commit comments

Comments
 (0)