Skip to content

Commit 42ea45d

Browse files
jsmnbomqdot
authored andcommitted
Improve serialization
- Make bitflags_seq support size hint (makes it work with e.g. postcard) - Use more compact binary format for SmallVecEnumMap when not human-readable (e.g. postcard)
1 parent 9bbff35 commit 42ea45d

2 files changed

Lines changed: 58 additions & 13 deletions

File tree

crates/buttplug_core/src/util/serializers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub mod bitflags_seq {
2121
S: Serializer,
2222
T: BitFlag + Serialize,
2323
{
24-
let mut seq = serializer.serialize_seq(None)?;
24+
let mut seq = serializer.serialize_seq(Some(flags.len()))?;
2525
for flag in flags.iter() {
2626
seq.serialize_element(&flag)?;
2727
}

crates/buttplug_core/src/util/small_vec_enum_map.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
//!
88
//! With `N = 1`, no heap allocation is needed when a single variant is present — the common case.
99
//!
10+
//! # Format selection
11+
//! When the serializer/deserializer is **human-readable** (e.g. JSON), the map-with-string-keys
12+
//! format is used. For **binary** formats (e.g. postcard), a plain sequence is used instead:
13+
//! each element is serialized using serde's standard externally-tagged enum representation
14+
//! (`u32` variant index + value), which avoids all string allocations and is more compact.
15+
//!
1016
//! # Constraints
1117
//! `V` must be an enum where every active variant is a newtype variant (single unnamed field).
1218
//! Unit, tuple, and struct variants are not supported and will return a serde error at runtime.
@@ -17,8 +23,8 @@ use serde::{
1723
Deserializer,
1824
Serialize,
1925
Serializer,
20-
de::{MapAccess, Visitor},
21-
ser::SerializeMap,
26+
de::{MapAccess, SeqAccess, Visitor},
27+
ser::{SerializeMap, SerializeSeq},
2228
};
2329
use smallvec::SmallVec;
2430
use std::ops::{Deref, DerefMut};
@@ -116,14 +122,25 @@ where
116122
where
117123
S: Serializer,
118124
{
119-
let mut map = serializer.serialize_map(Some(self.0.len()))?;
120-
for v in &self.0 {
121-
// Route each element through MapEntrySerializer, which intercepts the
122-
// serialize_newtype_variant call that serde generates for enum variants
123-
// and writes it directly as a map key-value pair.
124-
v.serialize(MapEntrySerializer { map: &mut map })?;
125+
if serializer.is_human_readable() {
126+
// Human-readable (e.g. JSON): map with string variant-name keys.
127+
let mut map = serializer.serialize_map(Some(self.0.len()))?;
128+
for v in &self.0 {
129+
// Route each element through MapEntrySerializer, which intercepts the
130+
// serialize_newtype_variant call that serde generates for enum variants
131+
// and writes it directly as a map key-value pair.
132+
v.serialize(MapEntrySerializer { map: &mut map })?;
133+
}
134+
map.end()
135+
} else {
136+
// Binary (e.g. postcard): plain sequence of externally-tagged enum values.
137+
// Each element is encoded as (u32 variant_index, payload) — no string keys.
138+
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
139+
for v in &self.0 {
140+
seq.serialize_element(v)?;
141+
}
142+
seq.end()
125143
}
126-
map.end()
127144
}
128145
}
129146

@@ -275,9 +292,9 @@ where
275292
where
276293
D: Deserializer<'de>,
277294
{
278-
struct SmallVecEnumMapVisitor<V, const N: usize>(std::marker::PhantomData<[V; N]>);
295+
struct SmallVecEnumMapMapVisitor<V, const N: usize>(std::marker::PhantomData<[V; N]>);
279296

280-
impl<'de, V, const N: usize> Visitor<'de> for SmallVecEnumMapVisitor<V, N>
297+
impl<'de, V, const N: usize> Visitor<'de> for SmallVecEnumMapMapVisitor<V, N>
281298
where
282299
V: Deserialize<'de>,
283300
{
@@ -303,7 +320,35 @@ where
303320
}
304321
}
305322

306-
deserializer.deserialize_map(SmallVecEnumMapVisitor(std::marker::PhantomData))
323+
struct SmallVecEnumMapSeqVisitor<V, const N: usize>(std::marker::PhantomData<[V; N]>);
324+
325+
impl<'de, V, const N: usize> Visitor<'de> for SmallVecEnumMapSeqVisitor<V, N>
326+
where
327+
V: Deserialize<'de>,
328+
{
329+
type Value = SmallVecEnumMap<V, N>;
330+
331+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
332+
formatter.write_str("a sequence that can be converted into a SmallVecEnumMap")
333+
}
334+
335+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
336+
where
337+
A: SeqAccess<'de>,
338+
{
339+
let mut smallvec = SmallVec::new();
340+
while let Some(v) = seq.next_element::<V>()? {
341+
smallvec.push(v);
342+
}
343+
Ok(SmallVecEnumMap(smallvec))
344+
}
345+
}
346+
347+
if deserializer.is_human_readable() {
348+
deserializer.deserialize_map(SmallVecEnumMapMapVisitor(std::marker::PhantomData))
349+
} else {
350+
deserializer.deserialize_seq(SmallVecEnumMapSeqVisitor(std::marker::PhantomData))
351+
}
307352
}
308353
}
309354

0 commit comments

Comments
 (0)