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

            
8
use std::any;
9
use std::sync::Arc;
10

            
11
use slotmap_careful::{Key as _, KeyData, SlotMap};
12
use tor_rpcbase as rpc;
13

            
14
pub(crate) mod methods;
15
#[cfg(feature = "weakref")]
16
mod weakrefs;
17

            
18
/// Return the [`RawAddr`] of an arbitrary `Arc<T>`.
19
#[cfg(any(test, feature = "weakref"))]
20
2314
fn raw_addr_of<T: ?Sized>(arc: &Arc<T>) -> RawAddr {
21
2314
    // I assure you, each one of these 'as'es was needed in the version of
22
2314
    // Rust I wrote them in.
23
2314
    RawAddr(Arc::as_ptr(arc) as *const () as usize)
24
2314
}
25

            
26
/// Return the [`RawAddr`] of an arbitrary `Weak<T>`.
27
#[cfg(any(test, feature = "weakref"))]
28
64928
fn raw_addr_of_weak<T: ?Sized>(arc: &std::sync::Weak<T>) -> RawAddr {
29
64928
    RawAddr(std::sync::Weak::as_ptr(arc) as *const () as usize)
30
64928
}
31

            
32
slotmap_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)]
41
pub(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)]
75
struct 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)]
107
struct 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)]
116
pub(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.
138
impl 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
1040
    fn encode_with_rng<R: rand::RngCore>(self, rng: &mut R) -> rpc::ObjectId {
154
        use base64ct::Encoding;
155
1040
        let bytes = self.to_bytes(rng);
156
1040
        rpc::ObjectId::from(base64ct::Base64UrlUnpadded::encode_string(&bytes[..]))
157
1040
    }
158

            
159
    /// As `encode_with_rng`, but return an array of bytes.
160
1048
    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
1048
        let (weak_bit, ffi_idx) = match self {
164
540
            GenIdx::Weak(idx) => (1, idx.data().as_ffi()),
165
508
            GenIdx::Strong(idx) => (0, idx.data().as_ffi()),
166
        };
167
1048
        let x = rng.random::<u64>() << 1;
168
1048
        let mut bytes = Vec::with_capacity(Self::BYTE_LEN);
169
1048
        bytes.write_u64(x | weak_bit);
170
1048
        bytes.write_u64(ffi_idx.wrapping_add(x));
171
1048

            
172
1048
        bytes.try_into().expect("Length was wrong!")
173
1048
    }
174

            
175
    /// Attempt to decode `id` into a `GenIdx` than an ObjMap can use.
176
1040
    pub(crate) fn try_decode(id: &rpc::ObjectId) -> Result<Self, rpc::LookupError> {
177
        use base64ct::Encoding;
178

            
179
1040
        let bytes = base64ct::Base64UrlUnpadded::decode_vec(id.as_ref())
180
1040
            .map_err(|_| rpc::LookupError::NoObject(id.clone()))?;
181
1040
        Self::from_bytes(&bytes).ok_or_else(|| rpc::LookupError::NoObject(id.clone()))
182
1040
    }
183

            
184
    /// As `try_decode`, but take a slice of bytes.
185
1044
    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
186
        use tor_bytes::Reader;
187
1044
        let mut r = Reader::from_slice(bytes);
188
1044
        let x = r.take_u64().ok()?;
189
1044
        let is_weak = (x & 1) == 1;
190
1044
        let x = x & !1;
191
1044
        let ffi_idx = r.take_u64().ok()?;
192
1044
        r.should_be_exhausted().ok()?;
193

            
194
1044
        let ffi_idx = ffi_idx.wrapping_sub(x);
195
1044

            
196
1044
        if is_weak {
197
538
            Some(GenIdx::Weak(WeakIdx::from(KeyData::from_ffi(ffi_idx))))
198
        } else {
199
506
            Some(GenIdx::Strong(StrongIdx::from(KeyData::from_ffi(ffi_idx))))
200
        }
201
1044
    }
202
}
203

            
204
impl ObjMap {
205
    /// Create a new empty ObjMap.
206
14
    pub(crate) fn new() -> Self {
207
14
        Self::default()
208
14
    }
209

            
210
    /// Unconditionally insert a strong entry for `value` in self, and return its index.
211
22
    pub(crate) fn insert_strong(&mut self, value: Arc<dyn rpc::Object>) -> GenIdx {
212
22
        GenIdx::Strong(self.strong_arena.insert(value))
213
22
    }
214

            
215
    /// Return the entry from this ObjMap for `idx`.
216
2228
    pub(crate) fn lookup(&self, idx: GenIdx) -> Option<Arc<dyn rpc::Object>> {
217
2228
        match idx {
218
            #[cfg(feature = "weakref")]
219
2210
            GenIdx::Weak(idx) => self
220
2210
                .weak_arena
221
2210
                .get(idx)
222
2210
                .and_then(weakrefs::WeakArenaEntry::strong),
223
            #[cfg(not(feature = "weakref"))]
224
            GenIdx::Weak(_) => None,
225
18
            GenIdx::Strong(idx) => self.strong_arena.get(idx).cloned(),
226
        }
227
2228
    }
228

            
229
    /// Remove and return the entry at `idx`, if any.
230
6
    pub(crate) fn remove(&mut self, idx: GenIdx) -> Option<Arc<dyn rpc::Object>> {
231
6
        match idx {
232
            #[cfg(feature = "weakref")]
233
2
            GenIdx::Weak(idx) => {
234
2
                if let Some(entry) = self.weak_arena.remove(idx) {
235
2
                    let old_idx = self.reverse_map.remove(&entry.tagged_addr());
236
2
                    debug_assert_eq!(old_idx, Some(idx));
237
2
                    entry.obj.upgrade()
238
                } else {
239
                    None
240
                }
241
            }
242
            #[cfg(not(feature = "weakref"))]
243
            GenIdx::Weak(_) => None,
244
4
            GenIdx::Strong(idx) => self.strong_arena.remove(idx),
245
        }
246
6
    }
247

            
248
    /// Testing only: Assert that every invariant for this structure is met.
249
    #[cfg(test)]
250
222
    fn assert_okay(&self) {
251
        #[cfg(feature = "weakref")]
252
        {
253
20972
            for (index, entry) in self.weak_arena.iter() {
254
20972
                let ptr = entry.tagged_addr();
255
20972
                assert_eq!(self.reverse_map.get(&ptr), Some(&index));
256
20972
                assert_eq!(ptr, entry.tagged_addr());
257
            }
258

            
259
20972
            for (ptr, idx) in self.reverse_map.iter() {
260
20972
                let entry = self
261
20972
                    .weak_arena
262
20972
                    .get(*idx)
263
20972
                    .expect("Dangling pointer in reverse map");
264
20972

            
265
20972
                assert_eq!(&entry.tagged_addr(), ptr);
266
            }
267
        }
268
222
    }
269
}
270

            
271
#[cfg(test)]
272
mod 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
}