Skip to main content

charon_lib/utils/
dedup.rs

1//! Deduplication of repeated values in the serialized output.
2//!
3//! Note that the deduplication scheme is order-dependent: it relies on the fact that
4//! serialization and deserialization traverse the value in the same order.
5
6use indexmap::IndexMap as SeqHashMap;
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize};
9use serde_state::{DeserializeState, SerializeState};
10use std::any::type_name;
11use std::cell::RefCell;
12use std::hash::Hash;
13
14use crate::utils::type_map::{Mappable, Mapper, TypeMap};
15
16/// Identifies a deduplicated value amongst the values of its type within a single serialized
17/// output. Ids are allocated in the order in which we serialize the values.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct DedupId(u32);
20
21/// A value that we deduplicate in the serialized output. We identify values by equality, hence
22/// the bounds.
23pub trait Dedup: Mappable + Clone + Eq + Hash {}
24impl<T> Dedup for T where T: Mappable + Clone + Eq + Hash {}
25
26/// The state threaded through (de)serialization to deduplicate values. Use `()` to serialize
27/// values normally and [`DedupSerializer`] to deduplicate them.
28pub trait DedupSerializerState: Sized {
29    /// Record that we're serializing this value. Returns `None` if we're not deduplicating
30    /// values, `Some(Ok(id))` the first time we meet a given value (it must then be serialized
31    /// in full), and `Some(Err(id))` afterwards (only the id must be serialized).
32    fn record_serialized<T: Dedup>(&self, value: &T) -> Option<Result<DedupId, DedupId>>;
33    /// Record that we deserialized the value with this id.
34    fn record_deserialized<T: Dedup>(&self, id: DedupId, value: T);
35    /// Find the previously-deserialized value with that id.
36    fn get_deserialized<T: Dedup>(&self, id: DedupId) -> Option<T>;
37}
38
39/// Don't deduplicate anything.
40impl DedupSerializerState for () {
41    fn record_serialized<T: Dedup>(&self, _value: &T) -> Option<Result<DedupId, DedupId>> {
42        None
43    }
44    fn record_deserialized<T: Dedup>(&self, _id: DedupId, _value: T) {}
45    fn get_deserialized<T: Dedup>(&self, _id: DedupId) -> Option<T> {
46        None
47    }
48}
49
50struct SerializeTableMapper;
51impl Mapper for SerializeTableMapper {
52    type Value<T: Mappable> = FxHashMap<T, DedupId>;
53}
54struct DeserializeTableMapper;
55impl Mapper for DeserializeTableMapper {
56    type Value<T: Mappable> = SeqHashMap<DedupId, T>;
57}
58
59/// Deduplicate the values of each type, in one table per type.
60#[derive(Default)]
61pub struct DedupSerializer {
62    // Table used for serialization: the values we've already emitted, with the id we gave them.
63    ser: RefCell<TypeMap<SerializeTableMapper>>,
64    // Table used for deserialization: the values we've read so far, by id.
65    de: RefCell<TypeMap<DeserializeTableMapper>>,
66}
67
68impl DedupSerializerState for DedupSerializer {
69    fn record_serialized<T: Dedup>(&self, value: &T) -> Option<Result<DedupId, DedupId>> {
70        let mut ser = self.ser.borrow_mut();
71        let table = ser.or_default::<T>();
72        Some(match table.get(value) {
73            Some(&id) => Err(id),
74            None => {
75                let id = DedupId(table.len().try_into().unwrap());
76                table.insert(value.clone(), id);
77                Ok(id)
78            }
79        })
80    }
81    fn record_deserialized<T: Dedup>(&self, id: DedupId, value: T) {
82        self.de.borrow_mut().or_default::<T>().insert(id, value);
83    }
84    fn get_deserialized<T: Dedup>(&self, id: DedupId) -> Option<T> {
85        self.de
86            .borrow()
87            .get::<T>()
88            .and_then(|table| table.get(&id))
89            .cloned()
90    }
91}
92
93/// How we represent a deduplicated value in the serialized output. `T` is the serialized form of
94/// the value.
95#[derive(Serialize, Deserialize, SerializeState, DeserializeState)]
96#[serde_state(state_implements = DedupSerializerState)]
97pub enum SerDedup<T> {
98    /// A value represented normally, accompanied by its id. This is emitted the first time we
99    /// serialize a given value: subsequent times will use `SerDedup::Deduplicated` instead.
100    Value(#[serde_state(stateless)] DedupId, T),
101    /// A value represented by its id. The actual value must have been emitted as a
102    /// `SerDedup::Value` with that same id earlier.
103    #[serde_state(stateless)]
104    Deduplicated(DedupId),
105    /// A plain value without an id, emitted when we're not deduplicating.
106    Untagged(T),
107}
108
109/// Serialize `value`, deduplicating it if the state says so. `repr` is the serialized form of
110/// `value`, only used the first time we meet it.
111pub fn serialize_dedup<T, R, State, S>(
112    value: &T,
113    repr: R,
114    state: &State,
115    serializer: S,
116) -> Result<S::Ok, S::Error>
117where
118    T: Dedup,
119    R: SerializeState<State>,
120    State: DedupSerializerState,
121    S: serde::Serializer,
122{
123    let repr = match state.record_serialized(value) {
124        Some(Ok(id)) => SerDedup::Value(id, repr),
125        Some(Err(id)) => SerDedup::Deduplicated(id),
126        None => SerDedup::Untagged(repr),
127    };
128    repr.serialize_state(state, serializer)
129}
130
131/// Deserialize a value that may have been deduplicated. `build` reconstructs the value from its
132/// serialized form.
133pub fn deserialize_dedup<'de, T, R, State, D>(
134    state: &State,
135    deserializer: D,
136    build: impl FnOnce(R) -> T,
137) -> Result<T, D::Error>
138where
139    T: Dedup,
140    R: DeserializeState<'de, State>,
141    State: DedupSerializerState,
142    D: serde::Deserializer<'de>,
143{
144    use serde::de::Error;
145    Ok(
146        match SerDedup::<R>::deserialize_state(state, deserializer)? {
147            SerDedup::Value(id, repr) => {
148                let value = build(repr);
149                state.record_deserialized(id, value.clone());
150                value
151            }
152            SerDedup::Deduplicated(id) => state.get_deserialized(id).ok_or_else(|| {
153                let msg = format!(
154                    "can't deserialize deduplicated value of type {}; \
155                were you careful with managing the deduplication state?",
156                    type_name::<T>()
157                );
158                D::Error::custom(msg)
159            })?,
160            SerDedup::Untagged(repr) => build(repr),
161        },
162    )
163}
164
165/// The error we report when a deduplicated value is deserialized with serde's stateless
166/// `Deserialize` impl, which can't resolve the ids.
167pub fn stateless_deserialize_error<T>() -> String {
168    format!(
169        "trying to deserialize a deduplicated value using serde's `{ty}::deserialize` method. \
170        This won't work, use serde_state's \
171        `{ty}::deserialize_state(&DedupSerializer::default(), _)` instead",
172        ty = type_name::<T>(),
173    )
174}