tor_proto/channel/
unique_id.rs

1//! Helper for unique identifiers for channels.
2
3use std::fmt::{Display, Formatter};
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6/// Counter for allocating unique-ish identifiers for channels.
7static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
8
9/// Unique identifier for a channel.
10///
11/// These identifiers are unique per process.  On 32-bit architectures
12/// it's possible to exhaust them if you do nothing but create channels
13/// for a very long time; if you do, we detect that and exit with an
14/// assertion failure.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct UniqId(usize);
17
18impl UniqId {
19    /// Construct a new UniqId.
20    pub(crate) fn new() -> Self {
21        // Relaxed ordering is fine; we don't care about how this
22        // is instantiated with respect to other channels.
23        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
24        assert!(id != usize::MAX, "Exhausted the channel ID namespace");
25        UniqId(id)
26    }
27}
28
29impl Display for UniqId {
30    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31        write!(f, "Chan {}", self.0)
32    }
33}
34
35/// Counter for allocating circuit unique ids.
36///
37/// We don't use circuit IDs here, because they can be huge, and they're
38/// random, and can get reused.
39#[derive(Debug)]
40pub(crate) struct CircUniqIdContext {
41    /// Next value to be handed out for this channel's circuits.
42    next_circ_id: usize,
43}
44
45impl CircUniqIdContext {
46    /// Create a new CircUniqIdContext
47    pub(super) fn new() -> Self {
48        CircUniqIdContext { next_circ_id: 0 }
49    }
50    /// Construct a new, unique-ish circuit UniqId
51    pub(super) fn next(&mut self, unique_id: UniqId) -> crate::tunnel::circuit::UniqId {
52        let circ_unique_id = self.next_circ_id;
53        self.next_circ_id += 1;
54        assert!(
55            self.next_circ_id != 0,
56            "Exhausted the unique circuit ID namespace on a channel"
57        );
58        crate::tunnel::circuit::UniqId::new(unique_id.0, circ_unique_id)
59    }
60}
61
62#[cfg(test)]
63mod test {
64    // @@ begin test lint list maintained by maint/add_warning @@
65    #![allow(clippy::bool_assert_comparison)]
66    #![allow(clippy::clone_on_copy)]
67    #![allow(clippy::dbg_macro)]
68    #![allow(clippy::mixed_attributes_style)]
69    #![allow(clippy::print_stderr)]
70    #![allow(clippy::print_stdout)]
71    #![allow(clippy::single_char_pattern)]
72    #![allow(clippy::unwrap_used)]
73    #![allow(clippy::unchecked_duration_subtraction)]
74    #![allow(clippy::useless_vec)]
75    #![allow(clippy::needless_pass_by_value)]
76    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
77    use super::*;
78    #[test]
79    fn chan_unique_id() {
80        let ids: Vec<UniqId> = (0..10).map(|_| UniqId::new()).collect();
81
82        // Make sure we got distinct numbers
83        let mut all_nums: Vec<_> = ids.iter().map(|x| x.0).collect();
84        all_nums.sort_unstable();
85        all_nums.dedup();
86        assert_eq!(all_nums.len(), ids.len());
87
88        assert_eq!(format!("{}", ids[3]), format!("Chan {}", ids[3].0));
89    }
90
91    #[test]
92    fn chan_circid() {
93        let chan_id99 = UniqId(99);
94        let mut ctx = CircUniqIdContext::new();
95
96        let _id0 = ctx.next(chan_id99);
97        let _id1 = ctx.next(chan_id99);
98        let id2 = ctx.next(chan_id99);
99        assert_eq!(format!("{}", id2), "Circ 99.2");
100    }
101}