Skip to main content

xmtp_mls_common/
tls_map.rs

1#![deny(missing_docs)]
2
3//! A deterministic, sorted key-value map with TLS codec serialization.
4//!
5//! [`TlsMap`] maintains entries in sorted key order so that serialization is
6//! byte-identical across implementations. [`TlsMapDelta`] provides an atomic
7//! mutation batch (insert / update / delete) that can be serialized independently
8//! and applied to a map with automatic rollback on failure.
9//!
10//! # Complexity
11//!
12//! Backed by a sorted `Vec`, so lookup is **O(log n)** via binary search, while
13//! insert, update, and remove are **O(n)** due to element shifting. This is a
14//! deliberate trade-off for deterministic serialization and small map sizes
15//! typical in MLS group state. Do not use this as a general-purpose map for
16//! large datasets — use [`std::collections::BTreeMap`] or [`std::collections::HashMap`]
17//! instead.
18//!
19//! In testing, a Vec and a BTreeMap were used to compare performance.
20//! The Vec was faster even for large maps at up to 50k entries for all
21//! operations except for insert and remove which were only half as fast.
22//! The Vec implementation is more memory efficient and significantly
23//! faster at deserialization.
24
25use std::io::{Read, Write};
26
27use tls_codec::{Deserialize, Serialize, Size};
28
29/// Error type for [`TlsMap`] operations.
30#[derive(Debug, thiserror::Error)]
31pub enum TlsMapError {
32    /// Attempted to insert a key that already exists.
33    #[error("key already exists")]
34    KeyExists,
35    /// Attempted to update or remove a key that does not exist.
36    #[error("key not found")]
37    KeyNotFound,
38    /// A TLS codec serialization or deserialization error.
39    #[error("tls codec error: {0}")]
40    Codec(#[from] tls_codec::Error),
41}
42
43/// A single key-value entry in a [`TlsMap`].
44///
45/// Wire format: `K(tls) || V(tls)` — key followed by value with no delimiter.
46#[derive(Clone, PartialEq, Eq)]
47pub struct TlsMapEntry<K, V> {
48    /// The entry's key.
49    pub key: K,
50    /// The entry's value.
51    pub value: V,
52}
53
54impl<K: std::fmt::Debug, V: std::fmt::Debug> std::fmt::Debug for TlsMapEntry<K, V> {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{{ key: {:?}, value: {:?} }}", self.key, self.value)
57    }
58}
59
60impl<K: Size, V: Size> Size for TlsMapEntry<K, V> {
61    #[inline]
62    fn tls_serialized_len(&self) -> usize {
63        self.key.tls_serialized_len() + self.value.tls_serialized_len()
64    }
65}
66
67impl<K: Serialize, V: Serialize> Serialize for TlsMapEntry<K, V> {
68    #[inline]
69    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
70        let key_size = self.key.tls_serialize(writer)?;
71        let value_size = self.value.tls_serialize(writer)?;
72        Ok(key_size + value_size)
73    }
74}
75
76impl<K: Deserialize + Size, V: Deserialize + Size> Deserialize for TlsMapEntry<K, V> {
77    #[inline]
78    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
79    where
80        Self: Sized,
81    {
82        let key = K::tls_deserialize(bytes)?;
83        let value = V::tls_deserialize(bytes)?;
84        Ok(Self { key, value })
85    }
86}
87
88/// A sorted key-value map with deterministic TLS codec serialization.
89///
90/// Entries are maintained in sorted order by key, ensuring byte-identical
91/// serialization across all implementations. Keys must be unique.
92///
93/// Wire format: `vlen(total_byte_length) || entry[0] || entry[1] || ...`
94/// where each entry is `K(tls) || V(tls)` and `vlen` is the QUIC
95/// variable-length encoding (RFC 9000 §16).
96///
97/// # Examples
98///
99/// ```
100/// use xmtp_mls_common::tls_map::TlsMap;
101/// use tls_codec::{Serialize, Deserialize};
102///
103/// let mut map = TlsMap::<u16, u16>::new();
104/// map.insert(3, 30).unwrap();
105/// map.insert(1, 10).unwrap();
106/// map.insert(2, 20).unwrap();
107///
108/// // Serialization is deterministic regardless of insertion order
109/// let bytes = map.tls_serialize_detached().unwrap();
110/// let deserialized = TlsMap::<u16, u16>::tls_deserialize_exact(&bytes).unwrap();
111/// assert_eq!(map, deserialized);
112/// ```
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct TlsMap<K, V> {
115    entries: Vec<TlsMapEntry<K, V>>,
116}
117
118impl<K, V> Default for TlsMap<K, V> {
119    #[inline]
120    fn default() -> Self {
121        Self {
122            entries: Vec::new(),
123        }
124    }
125}
126
127impl<K, V> TlsMap<K, V>
128where
129    K: Ord + Eq,
130{
131    /// Create an empty map.
132    ///
133    /// ```
134    /// use xmtp_mls_common::tls_map::TlsMap;
135    ///
136    /// let map = TlsMap::<u8, u16>::new();
137    /// assert!(map.is_empty());
138    /// ```
139    #[inline]
140    pub fn new() -> Self {
141        Self::default()
142    }
143
144    /// Create a map from an iterator of key-value pairs.
145    ///
146    /// Entries are sorted by key. If duplicate keys exist, the last value wins.
147    ///
148    /// ```
149    /// use xmtp_mls_common::tls_map::TlsMap;
150    ///
151    /// let map = TlsMap::from_pairs([(2, "b"), (1, "a"), (2, "c")]);
152    /// assert_eq!(map.get(&1), Some(&"a"));
153    /// assert_eq!(map.get(&2), Some(&"c")); // last value wins
154    /// assert_eq!(map.len(), 2);
155    /// ```
156    pub fn from_pairs(iter: impl IntoIterator<Item = (K, V)>) -> Self {
157        let entries = iter
158            .into_iter()
159            .collect::<std::collections::BTreeMap<K, V>>()
160            .into_iter()
161            .map(|(key, value)| TlsMapEntry { key, value })
162            .collect();
163        Self { entries }
164    }
165
166    /// Insert a key-value pair. Returns an error if the key already exists.
167    ///
168    /// ```
169    /// use xmtp_mls_common::tls_map::TlsMap;
170    ///
171    /// let mut map = TlsMap::<u8, u8>::new();
172    /// assert!(map.insert(1, 10).is_ok());
173    /// assert!(map.insert(1, 20).is_err()); // duplicate key
174    /// ```
175    #[inline]
176    pub fn insert(&mut self, key: K, value: V) -> Result<(), TlsMapError> {
177        match self.entries.binary_search_by(|e| e.key.cmp(&key)) {
178            Ok(_) => Err(TlsMapError::KeyExists),
179            Err(idx) => {
180                self.entries.insert(idx, TlsMapEntry { key, value });
181                Ok(())
182            }
183        }
184    }
185
186    /// Update an existing key's value. Returns an error if the key doesn't exist.
187    ///
188    /// ```
189    /// use xmtp_mls_common::tls_map::TlsMap;
190    ///
191    /// let mut map = TlsMap::<u8, u8>::new();
192    /// map.insert(1, 10).unwrap();
193    /// map.update(1, 20).unwrap();
194    /// assert_eq!(map.get(&1), Some(&20));
195    /// assert!(map.update(99, 0).is_err()); // key not found
196    /// ```
197    #[inline]
198    pub fn update(&mut self, key: K, value: V) -> Result<(), TlsMapError> {
199        match self.entries.binary_search_by(|e| e.key.cmp(&key)) {
200            Ok(idx) => {
201                self.entries[idx].value = value;
202                Ok(())
203            }
204            Err(_) => Err(TlsMapError::KeyNotFound),
205        }
206    }
207
208    /// Insert or update a key-value pair. Always succeeds.
209    ///
210    /// ```
211    /// use xmtp_mls_common::tls_map::TlsMap;
212    ///
213    /// let mut map = TlsMap::<u8, u8>::new();
214    /// map.set(1, 10);
215    /// map.set(1, 20); // overwrites
216    /// assert_eq!(map.get(&1), Some(&20));
217    /// assert_eq!(map.len(), 1);
218    /// ```
219    #[inline]
220    pub fn set(&mut self, key: K, value: V) {
221        match self.entries.binary_search_by(|e| e.key.cmp(&key)) {
222            Ok(idx) => self.entries[idx].value = value,
223            Err(idx) => self.entries.insert(idx, TlsMapEntry { key, value }),
224        }
225    }
226
227    /// Remove a key. Returns the removed value, or an error if the key doesn't exist.
228    ///
229    /// ```
230    /// use xmtp_mls_common::tls_map::TlsMap;
231    ///
232    /// let mut map = TlsMap::<u8, u8>::new();
233    /// map.insert(1, 10).unwrap();
234    /// assert_eq!(map.remove(&1).unwrap(), 10);
235    /// assert!(map.remove(&1).is_err()); // already removed
236    /// ```
237    #[inline]
238    pub fn remove(&mut self, key: &K) -> Result<V, TlsMapError> {
239        match self.entries.binary_search_by(|e| e.key.cmp(key)) {
240            Ok(idx) => Ok(self.entries.remove(idx).value),
241            Err(_) => Err(TlsMapError::KeyNotFound),
242        }
243    }
244
245    /// Get a value by key. Returns `None` if the key is not present.
246    ///
247    /// ```
248    /// use xmtp_mls_common::tls_map::TlsMap;
249    ///
250    /// let mut map = TlsMap::<u8, u8>::new();
251    /// map.insert(1, 10).unwrap();
252    /// assert_eq!(map.get(&1), Some(&10));
253    /// assert_eq!(map.get(&2), None);
254    /// ```
255    #[inline]
256    pub fn get(&self, key: &K) -> Option<&V> {
257        self.entries
258            .binary_search_by(|e| e.key.cmp(key))
259            .ok()
260            .map(|idx| &self.entries[idx].value)
261    }
262
263    /// Get a mutable reference to a value by key. Returns `None` if the key is not present.
264    ///
265    /// ```
266    /// use xmtp_mls_common::tls_map::TlsMap;
267    ///
268    /// let mut map = TlsMap::<u8, u8>::new();
269    /// map.insert(1, 10).unwrap();
270    /// *map.get_mut(&1).unwrap() = 20;
271    /// assert_eq!(map.get(&1), Some(&20));
272    /// ```
273    #[inline]
274    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
275        self.entries
276            .binary_search_by(|e| e.key.cmp(key))
277            .ok()
278            .map(|idx| &mut self.entries[idx].value)
279    }
280
281    /// Check if a key exists in the map.
282    #[inline]
283    pub fn contains_key(&self, key: &K) -> bool {
284        self.entries.binary_search_by(|e| e.key.cmp(key)).is_ok()
285    }
286
287    /// Returns the number of entries in the map.
288    #[inline]
289    pub fn len(&self) -> usize {
290        self.entries.len()
291    }
292
293    /// Returns true if the map contains no entries.
294    #[inline]
295    pub fn is_empty(&self) -> bool {
296        self.entries.is_empty()
297    }
298
299    /// Iterate over key-value pairs in key-sorted order.
300    ///
301    /// ```
302    /// use xmtp_mls_common::tls_map::TlsMap;
303    ///
304    /// let map = TlsMap::from_pairs([(2, "b"), (1, "a")]);
305    /// let pairs: Vec<_> = map.iter().collect();
306    /// assert_eq!(pairs, vec![(&1, &"a"), (&2, &"b")]);
307    /// ```
308    #[inline]
309    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
310        self.entries.iter().map(|e| (&e.key, &e.value))
311    }
312
313    /// Iterate over keys in sorted order.
314    #[inline]
315    pub fn keys(&self) -> impl Iterator<Item = &K> {
316        self.entries.iter().map(|e| &e.key)
317    }
318
319    /// Iterate over values in key-sorted order.
320    #[inline]
321    pub fn values(&self) -> impl Iterator<Item = &V> {
322        self.entries.iter().map(|e| &e.value)
323    }
324}
325
326// -- TLS codec for TlsMap --
327// Delegates to Vec<TlsMapEntry<K, V>> for the QUIC variable-length wire format,
328// then validates sorted + unique keys on deserialization.
329
330impl<K: Size, V: Size> Size for TlsMap<K, V> {
331    #[inline]
332    fn tls_serialized_len(&self) -> usize {
333        self.entries.tls_serialized_len()
334    }
335}
336
337impl<K: Serialize + Size + std::fmt::Debug, V: Serialize + Size + std::fmt::Debug> Serialize
338    for TlsMap<K, V>
339{
340    #[inline]
341    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
342        self.entries.tls_serialize(writer)
343    }
344}
345
346impl<K, V> Deserialize for TlsMap<K, V>
347where
348    K: Deserialize + Size + Ord + Eq,
349    V: Deserialize + Size,
350{
351    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
352    where
353        Self: Sized,
354    {
355        let entries = Vec::<TlsMapEntry<K, V>>::tls_deserialize(bytes)?;
356
357        // Verify sorted and unique
358        for [l, r] in entries.array_windows::<2>() {
359            if l.key >= r.key {
360                return Err(tls_codec::Error::DecodingError(
361                    "TlsMap entries not sorted or contain duplicates".into(),
362                ));
363            }
364        }
365
366        Ok(Self { entries })
367    }
368}
369
370// -- Delta operations --
371
372/// Undo action for rolling back a mutation during [`TlsMap::apply_delta`].
373enum UndoAction<K, V> {
374    /// Undo an insert by removing the key.
375    Remove(K),
376    /// Undo an update by restoring the previous value.
377    Restore(K, V),
378    /// Undo a delete by re-inserting the key-value pair.
379    Insert(K, V),
380}
381
382/// A single mutation to apply to a [`TlsMap`].
383///
384/// Wire format: `u8(tag) || K(tls) [|| V(tls)]` where tag 0 = Insert, 1 = Update, 2 = Delete.
385#[derive(Debug, Clone, PartialEq, Eq)]
386pub enum TlsMapMutation<K, V> {
387    /// Insert a new key-value pair. Fails if the key already exists.
388    Insert {
389        /// The key to insert.
390        key: K,
391        /// The value to associate with the key.
392        value: V,
393    },
394    /// Update an existing key's value. Fails if the key doesn't exist.
395    Update {
396        /// The key to update.
397        key: K,
398        /// The new value.
399        value: V,
400    },
401    /// Delete a key. Fails if the key doesn't exist.
402    Delete {
403        /// The key to delete.
404        key: K,
405    },
406}
407
408/// A list of mutations to apply atomically to a [`TlsMap`].
409///
410/// Built with a fluent API and applied via [`TlsMap::apply_delta`].
411///
412/// ```
413/// use xmtp_mls_common::tls_map::TlsMapDelta;
414///
415/// let delta = TlsMapDelta::<u8, u8>::new()
416///     .insert(1, 10)
417///     .update(2, 20)
418///     .delete(3);
419/// assert_eq!(delta.mutations.len(), 3);
420/// ```
421#[derive(Debug, Clone, PartialEq, Eq)]
422pub struct TlsMapDelta<K, V> {
423    /// The ordered list of mutations to apply.
424    pub mutations: Vec<TlsMapMutation<K, V>>,
425}
426
427impl<K, V> TlsMapDelta<K, V> {
428    /// Create an empty delta with no mutations.
429    #[inline]
430    pub fn new() -> Self {
431        Self {
432            mutations: Vec::new(),
433        }
434    }
435
436    /// Append an insert mutation. Consumes and returns `self` for chaining.
437    #[inline]
438    pub fn insert(mut self, key: K, value: V) -> Self {
439        self.mutations.push(TlsMapMutation::Insert { key, value });
440        self
441    }
442
443    /// Append an update mutation. Consumes and returns `self` for chaining.
444    #[inline]
445    pub fn update(mut self, key: K, value: V) -> Self {
446        self.mutations.push(TlsMapMutation::Update { key, value });
447        self
448    }
449
450    /// Append a delete mutation. Consumes and returns `self` for chaining.
451    #[inline]
452    pub fn delete(mut self, key: K) -> Self {
453        self.mutations.push(TlsMapMutation::Delete { key });
454        self
455    }
456}
457
458impl<K, V> Default for TlsMapDelta<K, V> {
459    #[inline]
460    fn default() -> Self {
461        Self::new()
462    }
463}
464
465impl<K, V> TlsMap<K, V>
466where
467    K: Ord + Eq + Clone,
468{
469    /// Apply a delta atomically. If any mutation fails, the map is unchanged.
470    ///
471    /// ```
472    /// use xmtp_mls_common::tls_map::{TlsMap, TlsMapDelta};
473    ///
474    /// let mut map = TlsMap::<u8, u8>::new();
475    /// map.insert(1, 10).unwrap();
476    ///
477    /// let delta = TlsMapDelta::new()
478    ///     .insert(2, 20)
479    ///     .update(1, 15)
480    ///     .delete(1);
481    ///
482    /// map.apply_delta(delta).unwrap();
483    /// assert_eq!(map.get(&2), Some(&20));
484    /// assert!(!map.contains_key(&1));
485    /// ```
486    pub fn apply_delta(&mut self, delta: TlsMapDelta<K, V>) -> Result<(), TlsMapError> {
487        let mut undo_stack: Vec<UndoAction<K, V>> = Vec::with_capacity(delta.mutations.len());
488
489        for mutation in delta.mutations {
490            match mutation {
491                TlsMapMutation::Insert { key, value } => {
492                    if let Err(e) = self.insert(key.clone(), value) {
493                        self.rollback(undo_stack);
494                        return Err(e);
495                    }
496                    undo_stack.push(UndoAction::Remove(key));
497                }
498                TlsMapMutation::Update { key, mut value } => match self.get_mut(&key) {
499                    Some(old) => {
500                        std::mem::swap(old, &mut value);
501                        undo_stack.push(UndoAction::Restore(key, value));
502                    }
503                    None => {
504                        self.rollback(undo_stack);
505                        return Err(TlsMapError::KeyNotFound);
506                    }
507                },
508                TlsMapMutation::Delete { key } => match self.remove(&key) {
509                    Ok(old) => {
510                        undo_stack.push(UndoAction::Insert(key, old));
511                    }
512                    Err(e) => {
513                        self.rollback(undo_stack);
514                        return Err(e);
515                    }
516                },
517            }
518        }
519
520        Ok(())
521    }
522
523    /// Replay undo actions in reverse to restore the map to its prior state.
524    fn rollback(&mut self, undo_stack: Vec<UndoAction<K, V>>) {
525        for action in undo_stack.into_iter().rev() {
526            match action {
527                UndoAction::Remove(key) => {
528                    // ignore the error as it only happens if the key is not present
529                    let _ = self.remove(&key);
530                }
531                UndoAction::Restore(key, value) | UndoAction::Insert(key, value) => {
532                    self.set(key, value);
533                }
534            }
535        }
536    }
537}
538
539// -- TLS codec for delta types --
540
541// Mutation tag: 0 = Insert, 1 = Update, 2 = Delete
542impl<K: Size, V: Size> Size for TlsMapMutation<K, V> {
543    fn tls_serialized_len(&self) -> usize {
544        1 + match self {
545            Self::Insert { key, value } | Self::Update { key, value } => {
546                key.tls_serialized_len() + value.tls_serialized_len()
547            }
548            Self::Delete { key } => key.tls_serialized_len(),
549        }
550    }
551}
552
553impl<K: Serialize + Size, V: Serialize + Size> Serialize for TlsMapMutation<K, V> {
554    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
555        match self {
556            Self::Insert { key, value } => {
557                let mut written = 0u8.tls_serialize(writer)?;
558                written += key.tls_serialize(writer)?;
559                written += value.tls_serialize(writer)?;
560                Ok(written)
561            }
562            Self::Update { key, value } => {
563                let mut written = 1u8.tls_serialize(writer)?;
564                written += key.tls_serialize(writer)?;
565                written += value.tls_serialize(writer)?;
566                Ok(written)
567            }
568            Self::Delete { key } => {
569                let mut written = 2u8.tls_serialize(writer)?;
570                written += key.tls_serialize(writer)?;
571                Ok(written)
572            }
573        }
574    }
575}
576
577impl<K, V> Deserialize for TlsMapMutation<K, V>
578where
579    K: Deserialize + Size,
580    V: Deserialize + Size,
581{
582    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
583    where
584        Self: Sized,
585    {
586        let tag = u8::tls_deserialize(bytes)?;
587        match tag {
588            0 => {
589                let key = K::tls_deserialize(bytes)?;
590                let value = V::tls_deserialize(bytes)?;
591                Ok(Self::Insert { key, value })
592            }
593            1 => {
594                let key = K::tls_deserialize(bytes)?;
595                let value = V::tls_deserialize(bytes)?;
596                Ok(Self::Update { key, value })
597            }
598            2 => {
599                let key = K::tls_deserialize(bytes)?;
600                Ok(Self::Delete { key })
601            }
602            _ => Err(tls_codec::Error::DecodingError(format!(
603                "unknown TlsMapMutation tag: {tag}"
604            ))),
605        }
606    }
607}
608
609impl<K: Size, V: Size> Size for TlsMapDelta<K, V> {
610    #[inline]
611    fn tls_serialized_len(&self) -> usize {
612        self.mutations.tls_serialized_len()
613    }
614}
615
616impl<K: Serialize + Size + std::fmt::Debug, V: Serialize + Size + std::fmt::Debug> Serialize
617    for TlsMapDelta<K, V>
618{
619    #[inline]
620    fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
621        self.mutations.tls_serialize(writer)
622    }
623}
624
625impl<K, V> Deserialize for TlsMapDelta<K, V>
626where
627    K: Deserialize + Size,
628    V: Deserialize + Size,
629{
630    fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
631    where
632        Self: Sized,
633    {
634        let mutations = Vec::<TlsMapMutation<K, V>>::tls_deserialize(bytes)?;
635        Ok(Self { mutations })
636    }
637}
638
639// -- IntoIterator --
640
641impl<K, V> IntoIterator for TlsMap<K, V> {
642    type Item = (K, V);
643    type IntoIter =
644        std::iter::Map<std::vec::IntoIter<TlsMapEntry<K, V>>, fn(TlsMapEntry<K, V>) -> (K, V)>;
645
646    #[inline]
647    fn into_iter(self) -> Self::IntoIter {
648        self.entries.into_iter().map(|e| (e.key, e.value))
649    }
650}
651
652impl<K: Ord + Eq, V> FromIterator<(K, V)> for TlsMap<K, V> {
653    #[inline]
654    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
655        Self::from_pairs(iter)
656    }
657}
658
659#[cfg(test)]
660mod tests {
661    use super::*;
662    use proptest::prelude::*;
663
664    /// Build a TlsMap from pairs using last-wins semantics for duplicate keys,
665    /// matching real-world `HashMap::collect` behavior. Uses HashMap internally
666    /// so insertion into TlsMap happens in arbitrary (non-sorted) order.
667    fn build_map<K: Ord + Eq, V>(pairs: impl Iterator<Item = (K, V)>) -> TlsMap<K, V> {
668        pairs.collect()
669    }
670
671    /// Generates the core property-based test suite for a given K, V type pair.
672    /// Every type combination that implements the TLS traits must pass all of these.
673    /// `$n` is the max number of entries to generate (must stay <= key space, e.g. 256 for u8).
674    macro_rules! tls_map_tests {
675        ($mod_name:ident, $K:ty, $V:ty, $n:expr, $mutate_v:expr) => {
676            mod $mod_name {
677                use super::*;
678
679                proptest! {
680                    #[test]
681                    fn round_trip(
682                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
683                    ) {
684                        let map = build_map(pairs.into_iter());
685                        let bytes = map.tls_serialize_detached().expect("round trip serialization should succeed");
686                        let deserialized =
687                            TlsMap::<$K, $V>::tls_deserialize_exact(&bytes).expect("round trip deserialization should succeed");
688                        prop_assert_eq!(map, deserialized);
689                    }
690
691                    #[test]
692                    fn deterministic(
693                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
694                    ) {
695                        let map = build_map(pairs.into_iter());
696                        let a = map.tls_serialize_detached().unwrap();
697                        let b = map.tls_serialize_detached().unwrap();
698                        prop_assert_eq!(a, b);
699                    }
700
701                    #[test]
702                    fn insertion_order_irrelevant(
703                        pairs in proptest::collection::hash_map(
704                            any::<$K>(), any::<$V>(), 2..$n
705                        )
706                    ) {
707                        let map_a = build_map(pairs.into_iter());
708                        let map_b = build_map(map_a.clone().into_iter().rev());
709                        prop_assert_eq!(
710                            map_a.tls_serialize_detached().unwrap(),
711                            map_b.tls_serialize_detached().unwrap()
712                        );
713                    }
714
715                    #[test]
716                    fn keys_always_sorted(
717                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
718                    ) {
719                        let map = build_map(pairs.into_iter());
720                        let keys: Vec<&$K> = map.keys().collect();
721                        for w in keys.array_windows::<2>() {
722                            prop_assert!(w[0] < w[1]);
723                        }
724                    }
725
726                    #[test]
727                    fn get_returns_inserted(
728                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 1..$n)
729                    ) {
730                        let map = build_map(pairs.clone().into_iter());
731                        let expected: std::collections::HashMap<$K, $V> = pairs
732                            .into_iter()
733                            .collect();
734                        prop_assert_eq!(map.len(), expected.len());
735                        for (k, v) in &expected {
736                            prop_assert_eq!(map.get(k), Some(v));
737                        }
738                    }
739
740                    #[test]
741                    fn insert_duplicate_fails(pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 1..$n)) {
742                        let mut map = build_map(pairs.clone().into_iter());
743                        for (k, v) in pairs {
744                            let v2 = ($mutate_v)(v);
745                            prop_assert!(matches!(
746                                map.insert(k, v2),
747                                Err(TlsMapError::KeyExists)
748                            ));
749                        }
750                    }
751
752                    #[test]
753                    fn set_upsert(pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 1..$n)) {
754                        let mut map = TlsMap::new();
755                        let mut key_set = std::collections::BTreeSet::new();
756                        for (k, v) in pairs {
757                            key_set.insert(k.clone());
758                            map.set(k.clone(), v.clone());
759                            prop_assert_eq!(map.get(&k), Some(&v));
760                            let v2 = ($mutate_v)(v);
761                            map.set(k.clone(), v2.clone());
762                            prop_assert_eq!(map.get(&k), Some(&v2));
763                            prop_assert_eq!(map.len(), key_set.len());
764                        }
765                    }
766
767                    #[test]
768                    fn remove_returns_value(pairs in proptest::collection::hash_map(any::<$K>(), any::<$V>(), 2..$n)) {
769                        let mut map = build_map(pairs.clone().into_iter());
770                        for (k, v) in pairs {
771                            prop_assert_eq!(map.remove(&k).unwrap(), v);
772                        }
773                        prop_assert!(map.is_empty());
774                    }
775
776                    #[test]
777                    fn serialized_size_matches_trait(
778                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
779                    ) {
780                        let map = build_map(pairs.into_iter());
781                        let bytes = map.tls_serialize_detached().unwrap();
782                        prop_assert_eq!(bytes.len(), map.tls_serialized_len());
783                    }
784
785                    #[test]
786                    fn from_pairs_and_collect_and_set_equivalent(
787                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
788                    ) {
789                        let map_a = TlsMap::from_pairs(pairs.clone());
790                        let map_b = pairs.clone().into_iter().collect::<TlsMap<$K, $V>>();
791                        let mut map_c = TlsMap::new();
792                        for (k, v) in pairs {
793                            map_c.set(k, v);
794                        }
795                        prop_assert_eq!(
796                            map_a.tls_serialize_detached().unwrap(),
797                            map_b.tls_serialize_detached().unwrap()
798                        );
799                        prop_assert_eq!(
800                            map_a.tls_serialize_detached().unwrap(),
801                            map_c.tls_serialize_detached().unwrap()
802                        );
803                    }
804
805                    #[test]
806                    fn delta_rollback_on_failure(
807                        pairs in proptest::collection::vec(
808                            (any::<$K>(), any::<$V>()), 1..std::cmp::min($n, 200)
809                        ),
810                        bad_key: $K,
811                    ) {
812                        let mut map = build_map(pairs.into_iter());
813                        let _ = map.remove(&bad_key);
814                        let snapshot = map.clone();
815
816                        let delta = TlsMapDelta::new().update(bad_key, <$V>::default());
817                        prop_assert!(map.apply_delta(delta).is_err());
818                        prop_assert_eq!(map, snapshot);
819                    }
820
821                    #[test]
822                    fn into_iter_sorted(
823                        pairs in proptest::collection::vec((any::<$K>(), any::<$V>()), 0..$n)
824                    ) {
825                        let map = build_map(pairs.into_iter());
826                        let collected: Vec<($K, $V)> = map.into_iter().collect();
827                        for w in collected.array_windows::<2>() {
828                            prop_assert!(w[0].0 < w[1].0);
829                        }
830                    }
831
832                    /// Verify single-entry round-trip and wire size.
833                    #[test]
834                    fn single_entry_round_trip(key in any::<$K>(), value in any::<$V>()) {
835                        let mut map = TlsMap::<$K, $V>::new();
836                        map.insert(key, value).unwrap();
837                        let bytes = map.tls_serialize_detached().unwrap();
838                        prop_assert_eq!(bytes.len(), map.tls_serialized_len());
839                        let deserialized = TlsMap::<$K, $V>::tls_deserialize_exact(&bytes).unwrap();
840                        prop_assert_eq!(map, deserialized);
841                    }
842
843                    /// Reject bytes where two entries are serialized in descending key order.
844                    #[test]
845                    fn rejects_unsorted(
846                        a in any::<$K>(),
847                        b in any::<$K>(),
848                        va in any::<$V>(),
849                        vb in any::<$V>(),
850                    ) {
851                        if a >= b { return Ok(()); }
852                        // a < b — serialize b first (wrong order)
853                        let mut content = Vec::new();
854                        b.tls_serialize(&mut content).unwrap();
855                        va.tls_serialize(&mut content).unwrap();
856                        a.tls_serialize(&mut content).unwrap();
857                        vb.tls_serialize(&mut content).unwrap();
858
859                        let mut bytes = Vec::new();
860                        tls_codec::vlen::write_length(&mut bytes, content.len()).unwrap();
861                        bytes.extend_from_slice(&content);
862
863                        prop_assert!(TlsMap::<$K, $V>::tls_deserialize_exact(&bytes).is_err());
864                    }
865
866                    /// Reject bytes where the same key appears twice.
867                    #[test]
868                    fn rejects_duplicates(
869                        key in any::<$K>(),
870                        va in any::<$V>(),
871                        vb in any::<$V>(),
872                    ) {
873                        let mut content = Vec::new();
874                        key.tls_serialize(&mut content).unwrap();
875                        va.tls_serialize(&mut content).unwrap();
876                        key.tls_serialize(&mut content).unwrap();
877                        vb.tls_serialize(&mut content).unwrap();
878
879                        let mut bytes = Vec::new();
880                        tls_codec::vlen::write_length(&mut bytes, content.len()).unwrap();
881                        bytes.extend_from_slice(&content);
882
883                        prop_assert!(TlsMap::<$K, $V>::tls_deserialize_exact(&bytes).is_err());
884                    }
885
886                    /// Mutation (Insert/Update/Delete) round-trips through serialization.
887                    #[test]
888                    fn mutation_round_trip(key in any::<$K>(), value in any::<$V>(), tag in 0u8..3) {
889                        let mutation = match tag {
890                            0 => TlsMapMutation::Insert { key, value },
891                            1 => TlsMapMutation::Update { key, value },
892                            _ => TlsMapMutation::Delete { key },
893                        };
894                        let bytes = mutation.tls_serialize_detached().unwrap();
895                        let rt = TlsMapMutation::<$K, $V>::tls_deserialize_exact(&bytes).unwrap();
896                        prop_assert_eq!(mutation, rt);
897                    }
898
899                    /// Insert new keys, update some existing keys, update+delete others.
900                    #[test]
901                    fn delta_apply_sequence(
902                        existing in proptest::collection::hash_map(
903                            any::<$K>(), any::<$V>(), 2..std::cmp::min($n, 50)
904                        ),
905                        new_entries in proptest::collection::hash_map(
906                            any::<$K>(), any::<$V>(), 1..std::cmp::min($n, 50)
907                        ),
908                        updated_value in any::<$V>(),
909                    ) {
910                        let new_entries: std::collections::HashMap<_, _> = new_entries
911                            .into_iter()
912                            .filter(|(k, _)| !existing.contains_key(k))
913                            .collect();
914                        if new_entries.is_empty() { return Ok(()); }
915
916                        // Split existing keys: first half update-only, second half update+delete
917                        let existing_keys: Vec<_> = existing.keys().cloned().collect();
918                        let mid = existing_keys.len() / 2;
919                        let (update_only, update_and_delete) = existing_keys.split_at(mid);
920                        if update_only.is_empty() || update_and_delete.is_empty() { return Ok(()); }
921
922                        let mut map: TlsMap<$K, $V> = existing.iter()
923                            .map(|(k, v)| (k.clone(), v.clone()))
924                            .collect();
925
926                        let mut delta = TlsMapDelta::new();
927                        for (k, v) in &new_entries {
928                            delta = delta.insert(k.clone(), v.clone());
929                        }
930                        for k in &existing_keys {
931                            delta = delta.update(k.clone(), updated_value.clone());
932                        }
933                        for k in update_and_delete {
934                            delta = delta.delete(k.clone());
935                        }
936
937                        map.apply_delta(delta).unwrap();
938                        prop_assert_eq!(map.len(), new_entries.len() + update_only.len());
939                        for (k, v) in &new_entries {
940                            prop_assert_eq!(map.get(k), Some(v));
941                        }
942                        for k in update_only {
943                            prop_assert_eq!(map.get(k), Some(&updated_value));
944                        }
945                        for k in update_and_delete {
946                            prop_assert!(!map.contains_key(k));
947                        }
948                    }
949
950                    /// Nested map with known outer key type, parameterized inner.
951                    #[test]
952                    fn nested_map_round_trip(
953                        entries in proptest::collection::hash_map(
954                            any::<u64>(),
955                            proptest::collection::hash_map(
956                                any::<$K>(), any::<$V>(), 0..std::cmp::min($n, 20)
957                            ),
958                            1..10
959                        ),
960                    ) {
961                        let outer: TlsMap<u64, TlsMap<$K, $V>> = entries
962                            .into_iter()
963                            .map(|(k, inner)| (k, inner.into_iter().collect()))
964                            .collect();
965                        let bytes = outer.tls_serialize_detached().unwrap();
966                        let deserialized = TlsMap::<u64, TlsMap<$K, $V>>::tls_deserialize_exact(&bytes).unwrap();
967                        prop_assert_eq!(outer, deserialized);
968                    }
969
970                    /// Nested map with parameterized outer key, known inner key type.
971                    #[test]
972                    fn nested_map_reverse_round_trip(
973                        entries in proptest::collection::hash_map(
974                            any::<$K>(),
975                            proptest::collection::hash_map(
976                                any::<u64>(), any::<$V>(), 0..20
977                            ),
978                            1..std::cmp::min($n, 10)
979                        ),
980                    ) {
981                        let outer: TlsMap<$K, TlsMap<u64, $V>> = entries
982                            .into_iter()
983                            .map(|(k, inner)| (k, inner.into_iter().collect()))
984                            .collect();
985                        let bytes = outer.tls_serialize_detached().unwrap();
986                        let deserialized = TlsMap::<$K, TlsMap<u64, $V>>::tls_deserialize_exact(&bytes).unwrap();
987                        prop_assert_eq!(outer, deserialized);
988                    }
989
990                    /// Delta (list of mutations) round-trips through serialization.
991                    #[test]
992                    fn delta_round_trip(
993                        mutations in proptest::collection::vec(
994                            (any::<$K>(), any::<$V>(), 0u8..3), 0..20
995                        )
996                    ) {
997                        let mut delta = TlsMapDelta::new();
998                        for (key, value, tag) in mutations {
999                            delta = match tag {
1000                                0 => delta.insert(key, value),
1001                                1 => delta.update(key, value),
1002                                _ => delta.delete(key),
1003                            };
1004                        }
1005                        let bytes = delta.tls_serialize_detached().unwrap();
1006                        let rt = TlsMapDelta::<$K, $V>::tls_deserialize_exact(&bytes).unwrap();
1007                        prop_assert_eq!(delta, rt);
1008                    }
1009                }
1010
1011                /// Verify that empty maps serialize to a single byte with value 0.
1012                #[test]
1013                fn empty_map_serializes_to_zero_length_prefix() {
1014                    let map = TlsMap::<$K, $V>::new();
1015                    assert_eq!(map.tls_serialize_detached().unwrap(), vec![0]);
1016                }
1017
1018                #[test]
1019                fn debug_format() {
1020                    let key = <$K>::default();
1021                    let value = <$V>::default();
1022                    let entry = TlsMapEntry::<$K, $V> {
1023                        key: key.clone(),
1024                        value: value.clone(),
1025                    };
1026                    let expected = format!("{{ key: {:?}, value: {:?} }}", key, value);
1027                    assert_eq!(format!("{:?}", entry), expected);
1028                }
1029
1030                #[test]
1031                fn get_mut_modifies_value() {
1032                    let mut map = TlsMap::<$K, $V>::new();
1033                    let key = <$K>::default();
1034                    let value = <$V>::default();
1035                    map.insert(key.clone(), value).unwrap();
1036                    let v = map.get_mut(&key).unwrap();
1037                    *v = ($mutate_v)(v.clone());
1038                    assert_ne!(map.get(&key), Some(&<$V>::default()));
1039                }
1040
1041                #[test]
1042                fn iter_yields_all_pairs() {
1043                    let mut map = TlsMap::<$K, $V>::new();
1044                    map.set(<$K>::default(), <$V>::default());
1045                    let pairs: Vec<_> = map.iter().collect();
1046                    assert_eq!(pairs.len(), 1);
1047                    assert_eq!(pairs[0], (&<$K>::default(), &<$V>::default()));
1048                }
1049
1050                #[test]
1051                fn values_yields_all_values() {
1052                    let mut map = TlsMap::<$K, $V>::new();
1053                    map.set(<$K>::default(), <$V>::default());
1054                    let vals: Vec<_> = map.values().collect();
1055                    assert_eq!(vals, vec![&<$V>::default()]);
1056                }
1057
1058                #[test]
1059                fn default_creates_empty() {
1060                    let map = TlsMap::<$K, $V>::default();
1061                    assert!(map.is_empty());
1062                    let delta = TlsMapDelta::<$K, $V>::default();
1063                    assert!(delta.mutations.is_empty());
1064                }
1065
1066                #[test]
1067                fn rejects_invalid_mutation_tag() {
1068                    let mut bytes = Vec::new();
1069                    let tag_byte = 3u8; // invalid tag
1070                    let key = <$K>::default();
1071                    let value = <$V>::default();
1072                    let content_len = 1 + key.tls_serialized_len() + value.tls_serialized_len();
1073                    tls_codec::vlen::write_length(&mut bytes, content_len).unwrap();
1074                    tag_byte.tls_serialize(&mut bytes).unwrap();
1075                    key.tls_serialize(&mut bytes).unwrap();
1076                    value.tls_serialize(&mut bytes).unwrap();
1077                    assert!(TlsMapDelta::<$K, $V>::tls_deserialize_exact(&bytes).is_err());
1078                }
1079
1080                /// Deserializing a map with descending keys must fail.
1081                #[test]
1082                fn rejects_unsorted_deterministic() {
1083                    // Serialize a valid 2-entry map, then re-encode entries in reverse order
1084                    // Build two entries: key_hi > key_lo, serialize hi first (wrong order)
1085                    let key_lo = <$K>::default();
1086                    let key_hi = {
1087                        // Serialize default key, bump last byte to get a larger key
1088                        let mut kb = key_lo.tls_serialize_detached().unwrap();
1089                        *kb.last_mut().unwrap() = kb.last().unwrap().wrapping_add(1);
1090                        kb
1091                    };
1092                    let val = <$V>::default().tls_serialize_detached().unwrap();
1093                    // content = [key_hi, val, key_lo, val] — descending order
1094                    let mut content = Vec::new();
1095                    content.extend_from_slice(&key_hi);
1096                    content.extend_from_slice(&val);
1097                    content.extend_from_slice(&key_lo.tls_serialize_detached().unwrap());
1098                    content.extend_from_slice(&val);
1099                    let mut bytes = Vec::new();
1100                    tls_codec::vlen::write_length(&mut bytes, content.len()).unwrap();
1101                    bytes.extend_from_slice(&content);
1102                    let result = TlsMap::<$K, $V>::tls_deserialize_exact(&bytes);
1103                    assert!(result.is_err(), "should reject unsorted entries");
1104                }
1105
1106                /// Deserializing a map with duplicate keys must fail.
1107                #[test]
1108                fn rejects_duplicates_deterministic() {
1109                    let key = <$K>::default();
1110                    let v1 = <$V>::default();
1111                    let v2 = ($mutate_v)(<$V>::default());
1112                    let mut content = Vec::new();
1113                    key.tls_serialize(&mut content).unwrap();
1114                    v1.tls_serialize(&mut content).unwrap();
1115                    key.tls_serialize(&mut content).unwrap();
1116                    v2.tls_serialize(&mut content).unwrap();
1117                    let mut bytes = Vec::new();
1118                    tls_codec::vlen::write_length(&mut bytes, content.len()).unwrap();
1119                    bytes.extend_from_slice(&content);
1120                    let result = TlsMap::<$K, $V>::tls_deserialize_exact(&bytes);
1121                    assert!(result.is_err(), "should reject duplicate keys");
1122                }
1123
1124                /// Appending extra bytes to a valid map must fail with tls_deserialize_exact.
1125                #[test]
1126                fn rejects_trailing_bytes() {
1127                    let mut map = TlsMap::<$K, $V>::new();
1128                    map.set(<$K>::default(), <$V>::default());
1129                    let mut bytes = map.tls_serialize_detached().unwrap();
1130                    bytes.push(0xFF);
1131                    assert!(TlsMap::<$K, $V>::tls_deserialize_exact(&bytes).is_err());
1132                }
1133            }
1134        };
1135    }
1136
1137    // u8 keys: 1000 entries across 256 key space guarantees heavy collisions
1138    tls_map_tests!(u8_u8, u8, u8, 1000, |v: u8| v.wrapping_add(1));
1139    tls_map_tests!(u8_u16, u8, u16, 1000, |v: u16| v.wrapping_add(1));
1140    tls_map_tests!(u8_u32, u8, u32, 1000, |v: u32| v.wrapping_add(1));
1141    tls_map_tests!(u8_u64, u8, u64, 1000, |v: u64| v.wrapping_add(1));
1142    tls_map_tests!(u16_u8, u16, u8, 1000, |v: u8| v.wrapping_add(1));
1143    tls_map_tests!(u16_u16, u16, u16, 1000, |v: u16| v.wrapping_add(1));
1144    tls_map_tests!(u16_u32, u16, u32, 1000, |v: u32| v.wrapping_add(1));
1145    tls_map_tests!(u16_u64, u16, u64, 1000, |v: u64| v.wrapping_add(1));
1146    tls_map_tests!(u32_u8, u32, u8, 1000, |v: u8| v.wrapping_add(1));
1147    tls_map_tests!(u32_u16, u32, u16, 1000, |v: u16| v.wrapping_add(1));
1148    tls_map_tests!(u32_u32, u32, u32, 1000, |v: u32| v.wrapping_add(1));
1149    tls_map_tests!(u32_u64, u32, u64, 1000, |v: u64| v.wrapping_add(1));
1150    tls_map_tests!(u64_u8, u64, u8, 1000, |v: u8| v.wrapping_add(1));
1151    tls_map_tests!(u64_u16, u64, u16, 1000, |v: u16| v.wrapping_add(1));
1152    tls_map_tests!(u64_u32, u64, u32, 1000, |v: u32| v.wrapping_add(1));
1153    tls_map_tests!(u64_u64, u64, u64, 1000, |v: u64| v.wrapping_add(1));
1154    tls_map_tests!(u16_32_bytes, u16, [u8; 32], 1000, |v: [u8; 32]| {
1155        let mut v = v;
1156        v[0] = v[0].wrapping_add(1);
1157        v
1158    });
1159    tls_map_tests!(u64_vec, u64, Vec<u8>, 1000, |v: Vec<u8>| {
1160        let mut v = v;
1161        v.push(42);
1162        v
1163    });
1164    tls_map_tests!(_32_bytes_vec, [u8; 32], Vec<u8>, 1000, |v: Vec<u8>| {
1165        let mut v = v;
1166        v.push(1);
1167        v
1168    });
1169    tls_map_tests!(vec_vec, Vec<u8>, Vec<u8>, 1000, |v: Vec<u8>| {
1170        let mut v = v;
1171        v.push(2);
1172        v
1173    });
1174}