message_io/network/
resource_id.rs

1use std::sync::{
2    atomic::{Ordering, AtomicUsize},
3};
4
5/// Information about the type of resource
6#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
7pub enum ResourceType {
8    Local,
9    Remote,
10}
11
12/// Unique identifier of a network resource in your system.
13/// The identifier wrap 3 values,
14/// - The type, that can be a value of [ResourceType].
15/// - The adapter id, that represents the adapter that creates this id
16/// - The base value: that is an unique identifier of the resource inside of its adapter.
17#[derive(Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ResourceId {
19    id: usize,
20}
21
22impl ResourceId {
23    const ADAPTER_ID_POS: usize = 0;
24    const RESOURCE_TYPE_POS: usize = 7;
25    const BASE_VALUE_POS: usize = 8;
26
27    const ADAPTER_ID_MASK: u8 = 0b01111111; // 5 bits
28    const BASE_VALUE_MASK: usize = 0xFFFFFFFFFFFFFF00_u64 as usize; // 7 bytes
29
30    pub const MAX_BASE_VALUE: usize = (Self::BASE_VALUE_MASK >> Self::BASE_VALUE_POS);
31    pub const MAX_ADAPTER_ID: u8 = (Self::ADAPTER_ID_MASK >> Self::ADAPTER_ID_POS);
32    pub const MAX_ADAPTERS: usize = Self::MAX_ADAPTER_ID as usize + 1;
33
34    fn new(adapter_id: u8, resource_type: ResourceType, base_value: usize) -> Self {
35        debug_assert!(
36            adapter_id <= Self::MAX_ADAPTER_ID,
37            "The adapter_id must be less than {}",
38            Self::MAX_ADAPTER_ID + 1,
39        );
40
41        debug_assert!(
42            base_value <= Self::MAX_BASE_VALUE,
43            "The base_value must be less than {}",
44            Self::MAX_BASE_VALUE + 1,
45        );
46
47        let resource_type = match resource_type {
48            ResourceType::Local => 1 << Self::RESOURCE_TYPE_POS,
49            ResourceType::Remote => 0,
50        };
51
52        Self {
53            id: ((adapter_id as usize) << Self::ADAPTER_ID_POS)
54                | resource_type
55                | (base_value << Self::BASE_VALUE_POS),
56        }
57    }
58
59    /// Returns the internal representation of this id
60    pub fn raw(&self) -> usize {
61        self.id
62    }
63
64    /// Returns the [ResourceType] of this resource
65    pub fn resource_type(&self) -> ResourceType {
66        if self.id & (1 << Self::RESOURCE_TYPE_POS) != 0 {
67            ResourceType::Local
68        }
69        else {
70            ResourceType::Remote
71        }
72    }
73
74    /// Tells if the id preresents a local resource.
75    pub fn is_local(&self) -> bool {
76        self.resource_type() == ResourceType::Local
77    }
78
79    /// Tells if the id preresents a remote resource.
80    pub fn is_remote(&self) -> bool {
81        self.resource_type() == ResourceType::Remote
82    }
83
84    /// Returns the associated adapter id.
85    /// Note that this returned value is the same as the value of [`crate::network::Transport::id()`]
86    /// if that transport uses the same adapter.
87    pub fn adapter_id(&self) -> u8 {
88        ((self.id & Self::ADAPTER_ID_MASK as usize) >> Self::ADAPTER_ID_POS) as u8
89    }
90
91    /// Returns the unique resource identifier inside the associated adapter.
92    pub fn base_value(&self) -> usize {
93        (self.id & Self::BASE_VALUE_MASK) >> Self::BASE_VALUE_POS
94    }
95}
96
97impl From<usize> for ResourceId {
98    fn from(raw: usize) -> Self {
99        Self { id: raw }
100    }
101}
102
103impl std::fmt::Display for ResourceId {
104    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
105        let resource_type = match self.resource_type() {
106            ResourceType::Local => "L",
107            ResourceType::Remote => "R",
108        };
109        write!(f, "[{}.{}.{}]", self.adapter_id(), resource_type, self.base_value())
110    }
111}
112
113impl std::fmt::Debug for ResourceId {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        write!(f, "{self}")
116    }
117}
118
119/// Used by the adapters in order to create unique ids for their resources.
120pub struct ResourceIdGenerator {
121    last: AtomicUsize,
122    adapter_id: u8,
123    resource_type: ResourceType,
124}
125
126impl ResourceIdGenerator {
127    pub fn new(adapter_id: u8, resource_type: ResourceType) -> Self {
128        Self { last: AtomicUsize::new(0), adapter_id, resource_type }
129    }
130
131    /// Generates a new id.
132    /// This id will contain information about the [`ResourceType`] and the associated adapter.
133    pub fn generate(&self) -> ResourceId {
134        let last = self.last.fetch_add(1, Ordering::SeqCst);
135        ResourceId::new(self.adapter_id, self.resource_type, last)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn base_value() {
145        let low_base_value = 0;
146
147        let resource_id = ResourceId::new(1, ResourceType::Local, low_base_value);
148        assert_eq!(low_base_value, resource_id.base_value());
149
150        let high_base_value = ResourceId::MAX_BASE_VALUE;
151
152        let resource_id = ResourceId::new(1, ResourceType::Local, high_base_value);
153        assert_eq!(high_base_value, resource_id.base_value());
154    }
155
156    #[test]
157    fn resource_type() {
158        let resource_id = ResourceId::new(0, ResourceType::Local, 0);
159        assert_eq!(ResourceType::Local, resource_id.resource_type());
160        assert_eq!(0, resource_id.adapter_id());
161
162        let resource_id = ResourceId::new(0, ResourceType::Remote, 0);
163        assert_eq!(ResourceType::Remote, resource_id.resource_type());
164        assert_eq!(0, resource_id.adapter_id());
165    }
166
167    #[test]
168    fn adapter_id() {
169        let adapter_id = ResourceId::MAX_ADAPTER_ID;
170
171        let resource_id = ResourceId::new(adapter_id, ResourceType::Local, 0);
172        assert_eq!(adapter_id, resource_id.adapter_id());
173        assert_eq!(ResourceType::Local, resource_id.resource_type());
174
175        let resource_id = ResourceId::new(adapter_id, ResourceType::Remote, 0);
176        assert_eq!(adapter_id, resource_id.adapter_id());
177        assert_eq!(ResourceType::Remote, resource_id.resource_type());
178    }
179}