arti_rpcserver/
objmap.rs

1//! An object mapper for looking up `rpc::Object`s by ID.
2//!
3//! This mapper stores strong or weak references, and uses a generational index
4//! to keep track of names for them.
5//!
6//! TODO RPC: Add an object diagram here once the implementation settles down.
7
8use std::any;
9use std::sync::Arc;
10
11use slotmap_careful::{Key as _, KeyData, SlotMap};
12use tor_rpcbase as rpc;
13
14pub(crate) mod methods;
15#[cfg(feature = "weakref")]
16mod weakrefs;
17
18/// Return the [`RawAddr`] of an arbitrary `Arc<T>`.
19#[cfg(any(test, feature = "weakref"))]
20fn raw_addr_of<T: ?Sized>(arc: &Arc<T>) -> RawAddr {
21    // I assure you, each one of these 'as'es was needed in the version of
22    // Rust I wrote them in.
23    RawAddr(Arc::as_ptr(arc) as *const () as usize)
24}
25
26/// Return the [`RawAddr`] of an arbitrary `Weak<T>`.
27#[cfg(any(test, feature = "weakref"))]
28fn raw_addr_of_weak<T: ?Sized>(arc: &std::sync::Weak<T>) -> RawAddr {
29    RawAddr(std::sync::Weak::as_ptr(arc) as *const () as usize)
30}
31
32slotmap_careful::new_key_type! {
33    pub(crate) struct StrongIdx;
34    // TODO: Eventually, remove this if it stays unused long-term.
35    pub(crate) struct WeakIdx;
36
37}
38
39/// A mechanism to look up RPC `Objects` by their `ObjectId`.
40#[derive(Default)]
41pub(crate) struct ObjMap {
42    /// Generationally indexed arena of strong object references.
43    strong_arena: SlotMap<StrongIdx, Arc<dyn rpc::Object>>,
44    /// Generationally indexed arena of weak object references.
45    ///
46    /// Invariants:
47    /// * No object has more than one reference in this arena.
48    /// * Every `entry` in this arena at position `idx` has a corresponding
49    ///   entry in `reverse_map` entry such that
50    ///   `reverse_map[entry.tagged_addr()] == idx`.
51    #[cfg(feature = "weakref")]
52    weak_arena: SlotMap<WeakIdx, weakrefs::WeakArenaEntry>,
53    /// Backwards reference to look up weak arena references by the underlying
54    /// object identity.
55    ///
56    /// Invariants:
57    /// * For every weak `(addr,idx)` entry in this map, there is a
58    ///   corresponding ArenaEntry in `arena` such that
59    ///   `arena[idx].tagged_addr() == addr`
60    #[cfg(feature = "weakref")]
61    reverse_map: std::collections::HashMap<TaggedAddr, WeakIdx>,
62    /// Testing only: How many times have we tidied this map?
63    #[cfg(all(test, feature = "weakref"))]
64    n_tidies: usize,
65}
66
67/// The raw address of an object held in an Arc or Weak.
68///
69/// This will be the same for every clone of an Arc, and the same for every Weak
70/// derived from an Arc.
71///
72/// Note that this is not on its own sufficient to uniquely identify an object;
73/// we must also know the object's TypeId.  See [`TaggedAddr`] for more information.
74#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
75struct RawAddr(usize);
76
77/// An address, type identity, and ownership status, used to identify a `Arc<dyn rpc::Object>`.
78///
79/// This type is necessary because of the way that Rust implements `Arc<dyn
80/// Foo>`. It's represented as a "fat pointer", containing:
81///    * A pointer to the  object itself (and the reference counts that make the
82///      `Arc<>` work.)
83///    * A vtable pointer explaining how to invoke the methods of `Foo` on this
84///      particular object.
85///
86/// The trouble here is that `Arc::ptr_eq()` can give an incorrect result, since
87/// a single type can be instantiated with multiple instances of its vtable
88/// pointer, which [breaks pointer comparison on `dyn`
89/// pointers](https://doc.rust-lang.org/std/ptr/fn.eq.html).
90///
91/// Thus, instead of comparing objects by (object pointer, vtable pointer)
92/// tuples, we have to compare them by (object pointer, type id).
93///
94/// (We _do_ have to look at type ids, and not just the pointers, since
95/// `repr(transparent)` enables people to have two `Arc<dyn Object>`s that have
96/// the same object pointer but different types.)[^1]
97///
98/// # Limitations
99///
100/// This type only uniquely identifies an Arc/Weak object for that object's
101/// lifespan. After the last (strong or weak) reference is dropped, this
102/// `TaggedAddr` may refer to a different object.
103///
104/// [^1]: TODO: Verify whether the necessary transmutation here is actually
105///     guaranteed to work.
106#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
107struct TaggedAddr {
108    /// The address of the object.
109    addr: RawAddr,
110    /// The type of the object.
111    type_id: any::TypeId,
112}
113
114/// A generational index for [`ObjMap`].
115#[derive(Copy, Clone, Debug, Eq, PartialEq)]
116pub(crate) enum GenIdx {
117    /// An index into the arena of weak references.
118    //
119    // TODO: Eventually, remove this if we don't build weak references.
120    Weak(WeakIdx),
121    /// An index into the arena of strong references
122    Strong(StrongIdx),
123}
124
125/// Encoding functions for GenIdx.
126///
127/// The encoding is deliberately nondeterministic: we want to avoid situations
128/// where applications depend on the details of our ObjectIds, or hardcode the
129/// ObjectIds they expect, or rely on the same  weak generational index getting
130/// encoded the same way every time they see it.
131///
132/// The encoding is deliberately non-cryptographic: we do not want to imply
133/// that this gives any security. It is just a mild deterrent to misuse.
134///
135/// If you find yourself wanting to reverse-engineer this code so that you can
136/// analyze these object IDs, please contact the Arti developers instead and let
137/// us give you a better way to do whatever you want.
138impl GenIdx {
139    /// The length of a byte-encoded (but not base-64 encoded) GenIdx.
140    pub(crate) const BYTE_LEN: usize = 16;
141
142    /// Return true if this is a strong (owning) reference.
143    pub(crate) fn is_strong(&self) -> bool {
144        matches!(self, GenIdx::Strong(_))
145    }
146
147    /// Encode `self` into an rpc::ObjectId that we can give to a client.
148    pub(crate) fn encode(self) -> rpc::ObjectId {
149        self.encode_with_rng(&mut rand::rng())
150    }
151
152    /// As `encode`, but take a Rng as an argument. For testing.
153    fn encode_with_rng<R: rand::RngCore>(self, rng: &mut R) -> rpc::ObjectId {
154        use base64ct::Encoding;
155        let bytes = self.to_bytes(rng);
156        rpc::ObjectId::from(base64ct::Base64UrlUnpadded::encode_string(&bytes[..]))
157    }
158
159    /// As `encode_with_rng`, but return an array of bytes.
160    pub(crate) fn to_bytes<R: rand::RngCore>(self, rng: &mut R) -> [u8; Self::BYTE_LEN] {
161        use rand::Rng;
162        use tor_bytes::Writer;
163        let (weak_bit, ffi_idx) = match self {
164            GenIdx::Weak(idx) => (1, idx.data().as_ffi()),
165            GenIdx::Strong(idx) => (0, idx.data().as_ffi()),
166        };
167        let x = rng.random::<u64>() << 1;
168        let mut bytes = Vec::with_capacity(Self::BYTE_LEN);
169        bytes.write_u64(x | weak_bit);
170        bytes.write_u64(ffi_idx.wrapping_add(x));
171
172        bytes.try_into().expect("Length was wrong!")
173    }
174
175    /// Attempt to decode `id` into a `GenIdx` than an ObjMap can use.
176    pub(crate) fn try_decode(id: &rpc::ObjectId) -> Result<Self, rpc::LookupError> {
177        use base64ct::Encoding;
178
179        let bytes = base64ct::Base64UrlUnpadded::decode_vec(id.as_ref())
180            .map_err(|_| rpc::LookupError::NoObject(id.clone()))?;
181        Self::from_bytes(&bytes).ok_or_else(|| rpc::LookupError::NoObject(id.clone()))
182    }
183
184    /// As `try_decode`, but take a slice of bytes.
185    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
186        use tor_bytes::Reader;
187        let mut r = Reader::from_slice(bytes);
188        let x = r.take_u64().ok()?;
189        let is_weak = (x & 1) == 1;
190        let x = x & !1;
191        let ffi_idx = r.take_u64().ok()?;
192        r.should_be_exhausted().ok()?;
193
194        let ffi_idx = ffi_idx.wrapping_sub(x);
195
196        if is_weak {
197            Some(GenIdx::Weak(WeakIdx::from(KeyData::from_ffi(ffi_idx))))
198        } else {
199            Some(GenIdx::Strong(StrongIdx::from(KeyData::from_ffi(ffi_idx))))
200        }
201    }
202}
203
204impl ObjMap {
205    /// Create a new empty ObjMap.
206    pub(crate) fn new() -> Self {
207        Self::default()
208    }
209
210    /// Unconditionally insert a strong entry for `value` in self, and return its index.
211    pub(crate) fn insert_strong(&mut self, value: Arc<dyn rpc::Object>) -> GenIdx {
212        GenIdx::Strong(self.strong_arena.insert(value))
213    }
214
215    /// Return the entry from this ObjMap for `idx`.
216    pub(crate) fn lookup(&self, idx: GenIdx) -> Option<Arc<dyn rpc::Object>> {
217        match idx {
218            #[cfg(feature = "weakref")]
219            GenIdx::Weak(idx) => self
220                .weak_arena
221                .get(idx)
222                .and_then(weakrefs::WeakArenaEntry::strong),
223            #[cfg(not(feature = "weakref"))]
224            GenIdx::Weak(_) => None,
225            GenIdx::Strong(idx) => self.strong_arena.get(idx).cloned(),
226        }
227    }
228
229    /// Remove and return the entry at `idx`, if any.
230    pub(crate) fn remove(&mut self, idx: GenIdx) -> Option<Arc<dyn rpc::Object>> {
231        match idx {
232            #[cfg(feature = "weakref")]
233            GenIdx::Weak(idx) => {
234                if let Some(entry) = self.weak_arena.remove(idx) {
235                    let old_idx = self.reverse_map.remove(&entry.tagged_addr());
236                    debug_assert_eq!(old_idx, Some(idx));
237                    entry.obj.upgrade()
238                } else {
239                    None
240                }
241            }
242            #[cfg(not(feature = "weakref"))]
243            GenIdx::Weak(_) => None,
244            GenIdx::Strong(idx) => self.strong_arena.remove(idx),
245        }
246    }
247
248    /// Testing only: Assert that every invariant for this structure is met.
249    #[cfg(test)]
250    fn assert_okay(&self) {
251        #[cfg(feature = "weakref")]
252        {
253            for (index, entry) in self.weak_arena.iter() {
254                let ptr = entry.tagged_addr();
255                assert_eq!(self.reverse_map.get(&ptr), Some(&index));
256                assert_eq!(ptr, entry.tagged_addr());
257            }
258
259            for (ptr, idx) in self.reverse_map.iter() {
260                let entry = self
261                    .weak_arena
262                    .get(*idx)
263                    .expect("Dangling pointer in reverse map");
264
265                assert_eq!(&entry.tagged_addr(), ptr);
266            }
267        }
268    }
269}
270
271#[cfg(test)]
272mod test {
273    // @@ begin test lint list maintained by maint/add_warning @@
274    #![allow(clippy::bool_assert_comparison)]
275    #![allow(clippy::clone_on_copy)]
276    #![allow(clippy::dbg_macro)]
277    #![allow(clippy::mixed_attributes_style)]
278    #![allow(clippy::print_stderr)]
279    #![allow(clippy::print_stdout)]
280    #![allow(clippy::single_char_pattern)]
281    #![allow(clippy::unwrap_used)]
282    #![allow(clippy::unchecked_duration_subtraction)]
283    #![allow(clippy::useless_vec)]
284    #![allow(clippy::needless_pass_by_value)]
285    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
286
287    use super::*;
288    use derive_deftly::Deftly;
289    use tor_rpcbase::templates::*;
290
291    #[derive(Clone, Debug, Deftly)]
292    #[derive_deftly(Object)]
293    struct ExampleObject(#[allow(unused)] String);
294
295    impl ExampleObject {
296        fn wrap_arc(self: Arc<Self>) -> Arc<Wrapper> {
297            // SAFETY: Using `repr(transparent)` on Wrapper guarantees that
298            // transmuting an ExampleObject to a Wrapper will work correctly.
299            //
300            // Given this, and the fact that they have the same alignment and
301            // size, the documentation for `Arc::from_raw` says that this should
302            // be safe.
303            //
304            // Also this is only a test.
305            unsafe { Arc::from_raw(Arc::into_raw(self) as *const Wrapper) }
306        }
307    }
308
309    #[derive(Clone, Debug, Deftly)]
310    #[derive_deftly(Object)]
311    #[repr(transparent)]
312    struct Wrapper(ExampleObject);
313
314    #[cfg(feature = "weakref")]
315    #[test]
316    fn arc_to_addr() {
317        let a1 = Arc::new("Hello world");
318        let a2 = Arc::clone(&a1);
319        let a3 = Arc::new("Hello world");
320        let w1 = Arc::downgrade(&a2);
321
322        assert_eq!(raw_addr_of(&a1), raw_addr_of(&a2));
323        assert_eq!(raw_addr_of(&a1), raw_addr_of_weak(&w1));
324        assert_ne!(raw_addr_of(&a1), raw_addr_of(&a3));
325
326        let obj1: Arc<dyn rpc::Object> = Arc::new(ExampleObject("Hello world".into()));
327        let obj2 = Arc::clone(&obj1);
328        let obj3: Arc<dyn rpc::Object> = Arc::new(ExampleObject("Hello world".into()));
329        let obj4 = Arc::clone(&obj3);
330        let weak1 = Arc::downgrade(&obj1);
331        let weak2 = Arc::downgrade(&obj3);
332
333        assert_eq!(raw_addr_of(&obj1), raw_addr_of(&obj2));
334        assert_eq!(raw_addr_of(&obj1), raw_addr_of_weak(&weak1));
335        assert_eq!(raw_addr_of(&obj3), raw_addr_of(&obj4));
336        assert_eq!(raw_addr_of(&obj3), raw_addr_of_weak(&weak2));
337        assert_ne!(raw_addr_of(&obj1), raw_addr_of(&obj3));
338        assert_ne!(raw_addr_of(&obj1), raw_addr_of(&a1));
339    }
340
341    #[cfg(feature = "weakref")]
342    #[test]
343    fn obj_ptr() {
344        let object = Arc::new(ExampleObject("Ten tons of flax".into()));
345        let object2: Arc<dyn rpc::Object> = Arc::new(ExampleObject("Ten tons of flax".into()));
346
347        let wrapped: Arc<Wrapper> = object.clone().wrap_arc();
348        let object_dyn = object.clone() as Arc<dyn rpc::Object>;
349        let wrapped_dyn = wrapped.clone() as Arc<dyn rpc::Object>;
350
351        let object_dyn2 = Arc::clone(&object_dyn);
352        let wrapped_dyn2 = Arc::clone(&wrapped_dyn);
353        let wrapped_weak = Arc::downgrade(&wrapped_dyn);
354
355        assert_eq!(
356            TaggedAddr::for_object(&object_dyn),
357            TaggedAddr::for_object(&object_dyn2)
358        );
359        assert_ne!(
360            TaggedAddr::for_object(&object_dyn),
361            TaggedAddr::for_object(&object2)
362        );
363
364        assert_eq!(
365            TaggedAddr::for_object(&wrapped_dyn),
366            TaggedAddr::for_object(&wrapped_dyn2)
367        );
368
369        assert_ne!(
370            TaggedAddr::for_object(&object_dyn),
371            TaggedAddr::for_object(&wrapped_dyn)
372        );
373
374        assert_eq!(
375            TaggedAddr::for_object(&object_dyn).addr,
376            TaggedAddr::for_object(&wrapped_dyn).addr
377        );
378        #[cfg(feature = "weakref")]
379        assert_eq!(
380            TaggedAddr::for_object(&wrapped_dyn).addr,
381            raw_addr_of_weak(&wrapped_weak)
382        );
383
384        assert_eq!(
385            TaggedAddr::for_object(&object_dyn).type_id,
386            any::TypeId::of::<ExampleObject>()
387        );
388        assert_eq!(
389            TaggedAddr::for_object(&wrapped_dyn).type_id,
390            any::TypeId::of::<Wrapper>()
391        );
392
393        assert_eq!(
394            TaggedAddr::for_object(&object_dyn).addr,
395            raw_addr_of(&object)
396        );
397        assert_eq!(
398            TaggedAddr::for_object(&wrapped_dyn).addr,
399            raw_addr_of(&wrapped)
400        );
401        assert_ne!(
402            TaggedAddr::for_object(&object_dyn).addr,
403            raw_addr_of(&object2)
404        );
405    }
406
407    #[test]
408    fn map_basics() {
409        // Insert an object, make sure it gets inserted twice, and look it up.
410        let obj1 = Arc::new(ExampleObject("abcdef".to_string()));
411        let mut map = ObjMap::new();
412        map.assert_okay();
413        let id1 = map.insert_strong(obj1.clone());
414        let id2 = map.insert_strong(obj1.clone());
415        assert_ne!(id1, id2);
416        let obj_out1 = map.lookup(id1).unwrap();
417        let obj_out2 = map.lookup(id2).unwrap();
418        assert_eq!(raw_addr_of(&obj1), raw_addr_of(&obj_out1));
419        assert_eq!(raw_addr_of(&obj1), raw_addr_of(&obj_out2));
420        map.assert_okay();
421
422        map.remove(id1);
423        assert!(map.lookup(id1).is_none());
424        let obj_out2b = map.lookup(id2).unwrap();
425        assert_eq!(raw_addr_of(&obj_out2), raw_addr_of(&obj_out2b));
426
427        map.assert_okay();
428    }
429
430    #[cfg(feature = "weakref")]
431    #[test]
432    fn strong_and_weak() {
433        // Make sure that a strong object behaves like one, and so does a weak
434        // object.
435        let obj1: Arc<dyn rpc::Object> = Arc::new(ExampleObject("hello".to_string()));
436        let obj2: Arc<dyn rpc::Object> = Arc::new(ExampleObject("world".to_string()));
437        let mut map = ObjMap::new();
438        let id1 = map.insert_strong(obj1.clone());
439        let id2 = map.insert_weak(obj2.clone());
440
441        {
442            let out1 = map.lookup(id1);
443            let out2 = map.lookup(id2);
444            assert_eq!(raw_addr_of(&obj1), raw_addr_of(&out1.unwrap()));
445            assert_eq!(raw_addr_of(&obj2), raw_addr_of(&out2.unwrap()));
446        }
447        let addr1 = raw_addr_of(&obj1);
448        map.assert_okay();
449
450        // Now drop every object we've got, and see what we can still find.
451        drop(obj1);
452        drop(obj2);
453        {
454            let out1 = map.lookup(id1);
455            let out2 = map.lookup(id2);
456
457            // This one was strong, so it is still there.
458            assert!(out1.is_some());
459            assert_eq!(raw_addr_of(&out1.unwrap()), addr1);
460
461            // This one is weak so it went away.
462            assert!(out2.is_none());
463        }
464        map.assert_okay();
465    }
466
467    #[cfg(feature = "weakref")]
468    #[test]
469    fn remove() {
470        // Make sure that removing an object makes it go away.
471        let obj1: Arc<dyn rpc::Object> = Arc::new(ExampleObject("hello".to_string()));
472        let obj2: Arc<dyn rpc::Object> = Arc::new(ExampleObject("world".to_string()));
473        let mut map = ObjMap::new();
474        let id1 = map.insert_strong(obj1.clone());
475        let id2 = map.insert_weak(obj2.clone());
476        map.assert_okay();
477
478        map.remove(id1);
479        map.assert_okay();
480        assert!(map.lookup(id1).is_none());
481        assert!(map.lookup(id2).is_some());
482
483        map.remove(id2);
484        map.assert_okay();
485        assert!(map.lookup(id1).is_none());
486        assert!(map.lookup(id2).is_none());
487    }
488
489    #[cfg(feature = "weakref")]
490    #[test]
491    fn duplicates() {
492        // Make sure that inserting duplicate objects behaves right.
493        let obj1: Arc<dyn rpc::Object> = Arc::new(ExampleObject("hello".to_string()));
494        let obj2: Arc<dyn rpc::Object> = Arc::new(ExampleObject("world".to_string()));
495        let mut map = ObjMap::new();
496        let id1 = map.insert_strong(obj1.clone());
497        let id2 = map.insert_weak(obj2.clone());
498
499        {
500            assert_ne!(id2, map.insert_weak(obj1.clone()));
501            assert_eq!(id2, map.insert_weak(obj2.clone()));
502        }
503
504        {
505            assert_ne!(id1, map.insert_strong(obj1.clone()));
506            assert_ne!(id2, map.insert_strong(obj2.clone()));
507        }
508    }
509
510    #[cfg(feature = "weakref")]
511    #[test]
512    fn upgrade() {
513        // Make sure that inserting an object as weak and strong (in either
514        // order) makes two separate entries.
515        let obj1: Arc<dyn rpc::Object> = Arc::new(ExampleObject("hello".to_string()));
516        let obj2: Arc<dyn rpc::Object> = Arc::new(ExampleObject("world".to_string()));
517        let addr1 = raw_addr_of(&obj1);
518        let addr2 = raw_addr_of(&obj2);
519
520        let mut map = ObjMap::new();
521        let id1 = map.insert_strong(obj1.clone());
522        let id2 = map.insert_weak(obj2.clone());
523
524        assert_ne!(id2, map.insert_weak(obj1.clone()));
525        assert_ne!(id1, map.insert_strong(obj2.clone()));
526        map.assert_okay();
527
528        drop(obj1);
529        drop(obj2);
530        let out1 = map.lookup(id1).unwrap();
531        let out2 = map.lookup(id2).unwrap();
532        assert_eq!(raw_addr_of(&out1), addr1);
533        assert_eq!(raw_addr_of(&out2), addr2);
534    }
535
536    #[cfg(feature = "weakref")]
537    #[test]
538    fn tidy() {
539        let mut map = ObjMap::new();
540        let mut keep_these = vec![];
541        let mut s = vec![];
542        let mut w = vec![];
543        for _ in 0..100 {
544            let mut t = vec![];
545            for _ in 0..10 {
546                let o = Arc::new(ExampleObject("dump".into()));
547                w.push(map.insert_weak(o.clone()));
548                t.push(o);
549            }
550            let obj = Arc::new(ExampleObject("cafe".into()));
551            keep_these.push(obj.clone());
552            s.push(map.insert_weak(obj));
553            drop(t);
554            map.assert_okay();
555        }
556
557        assert_eq!(s.len(), 100);
558        assert_eq!(w.len(), 1000);
559        assert!(w.iter().all(|id| map.lookup(*id).is_none()));
560        assert!(s.iter().all(|id| map.lookup(*id).is_some()));
561
562        assert_ne!(map.weak_arena.len() + map.strong_arena.len(), 1100);
563        map.assert_okay();
564        map.tidy();
565        map.assert_okay();
566        assert_eq!(map.weak_arena.len() + map.strong_arena.len(), 100);
567
568        // This number is a bit arbitrary.
569        assert!(dbg!(map.n_tidies) < 30);
570    }
571
572    #[test]
573    fn wrapper_magic() {
574        // Make sure that the wrapper transmutation trick works well.
575        let obj = Arc::new(ExampleObject("dump".into()));
576        let wrap = obj.clone().wrap_arc();
577
578        let mut map = ObjMap::new();
579        map.insert_strong(obj);
580        map.insert_strong(wrap);
581        assert_eq!(map.strong_arena.len(), 2);
582    }
583
584    #[test]
585    fn objid_encoding() {
586        use rand::Rng;
587        fn test_roundtrip(a: u32, b: u32, rng: &mut tor_basic_utils::test_rng::TestingRng) {
588            let a: u64 = a.into();
589            let b: u64 = b.into();
590            let data = KeyData::from_ffi((a << 33) | (1_u64 << 32) | b);
591            let idx = if rng.random_bool(0.5) {
592                GenIdx::Strong(StrongIdx::from(data))
593            } else {
594                GenIdx::Weak(WeakIdx::from(data))
595            };
596            let s1 = idx.encode_with_rng(rng);
597            let s2 = idx.encode_with_rng(rng);
598            assert_ne!(s1, s2);
599            assert_eq!(idx, GenIdx::try_decode(&s1).unwrap());
600            assert_eq!(idx, GenIdx::try_decode(&s2).unwrap());
601        }
602        let mut rng = tor_basic_utils::test_rng::testing_rng();
603
604        test_roundtrip(0, 1, &mut rng);
605        test_roundtrip(0, 2, &mut rng);
606        test_roundtrip(1, 1, &mut rng);
607        test_roundtrip(0xffffffff, 0xffffffff, &mut rng);
608
609        for _ in 0..256 {
610            test_roundtrip(rng.random(), rng.random(), &mut rng);
611        }
612    }
613}