1
//! Helper for unique identifiers for channels.
2

            
3
use std::fmt::{Display, Formatter};
4
use std::sync::atomic::{AtomicUsize, Ordering};
5

            
6
/// Counter for allocating unique-ish identifiers for channels.
7
static 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)]
16
pub struct UniqId(usize);
17

            
18
impl UniqId {
19
    /// Construct a new UniqId.
20
543
    pub(crate) fn new() -> Self {
21
543
        // Relaxed ordering is fine; we don't care about how this
22
543
        // is instantiated with respect to other channels.
23
543
        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
24
543
        assert!(id != usize::MAX, "Exhausted the channel ID namespace");
25
543
        UniqId(id)
26
543
    }
27
}
28

            
29
impl Display for UniqId {
30
258
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31
258
        write!(f, "Chan {}", self.0)
32
258
    }
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)]
40
pub(crate) struct CircUniqIdContext {
41
    /// Next value to be handed out for this channel's circuits.
42
    next_circ_id: usize,
43
}
44

            
45
impl CircUniqIdContext {
46
    /// Create a new CircUniqIdContext
47
275
    pub(super) fn new() -> Self {
48
275
        CircUniqIdContext { next_circ_id: 0 }
49
275
    }
50
    /// Construct a new, unique-ish circuit UniqId
51
14
    pub(super) fn next(&mut self, unique_id: UniqId) -> crate::tunnel::circuit::UniqId {
52
14
        let circ_unique_id = self.next_circ_id;
53
14
        self.next_circ_id += 1;
54
14
        assert!(
55
14
            self.next_circ_id != 0,
56
            "Exhausted the unique circuit ID namespace on a channel"
57
        );
58
14
        crate::tunnel::circuit::UniqId::new(unique_id.0, circ_unique_id)
59
14
    }
60
}
61

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