reactor_rt/
ids.rs

1/*
2 * Copyright (c) 2021, TU Dresden.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
22 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25use std::cmp::Ordering;
26use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
27use std::hash::{Hash, Hasher};
28use std::str::FromStr;
29
30use impl_types::{GlobalIdImpl, ReactionIdImpl, ReactorIdImpl};
31use index_vec::Idx;
32
33macro_rules! simple_idx_type {
34    ($(#[$($attrs:tt)*])* $id:ident($impl_t:ty)) => {
35
36$(#[$($attrs)*])*
37#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
38#[repr(transparent)]
39pub struct $id($impl_t);
40
41impl $id {
42    // a const fn to be able to use this in const context
43    pub const fn new(u: $impl_t) -> Self {
44        Self(u)
45    }
46
47    pub const fn raw(self) -> $impl_t {
48        self.0
49    }
50
51    pub(crate) fn plus(&self, u: usize) -> Self {
52        Self::from_usize(self.0 as usize + u)
53    }
54
55    pub(crate) const fn index(self) -> usize {
56        self.0 as usize
57    }
58
59    #[allow(unused)]
60    pub(crate) fn get_and_incr(&mut self) -> Self {
61        let id = *self;
62        *self = Self(self.0 + 1);
63        id
64    }
65}
66
67impl Idx for $id {
68    fn from_usize(idx: usize) -> Self {
69        debug_assert!(idx <= <$impl_t>::MAX as usize);
70        Self(idx as $impl_t)
71    }
72
73    fn index(self) -> usize {
74        self.0 as usize
75    }
76}
77
78impl Display for $id {
79    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
80        write!(f, "{}", self.0)
81    }
82}
83    };
84}
85
86simple_idx_type! {
87    /// ID of a reaction local to its containing reactor.
88    LocalReactionId(ReactionIdImpl)
89}
90
91simple_idx_type! {
92    /// The unique identifier of a reactor instance during
93    /// execution.
94    ReactorId(ReactorIdImpl)
95}
96
97macro_rules! global_id_newtype {
98    {$(#[$m:meta])* $id:ident} => {
99        $(#[$m])*
100        #[derive(Eq, Ord, PartialOrd, PartialEq, Hash, Copy, Clone)]
101        pub struct $id(pub(crate) GlobalId);
102
103        impl $id {
104            pub fn new(container: $crate::ReactorId, local: $crate::LocalReactionId) -> Self {
105                Self($crate::GlobalId::new(container, local))
106            }
107        }
108
109        impl Debug for $id {
110            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
111                write!(f, "{:?}", self.0)
112            }
113        }
114
115        impl Display for $id {
116            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
117                write!(f, "{}", self.0)
118            }
119        }
120    };
121}
122
123global_id_newtype! {
124    /// Global identifier for a reaction.
125    GlobalReactionId
126}
127
128/// Identifies a component of a reactor using the ID of its container
129/// and a local component ID.
130#[derive(Eq, Copy, Clone)]
131pub(crate) struct GlobalId {
132    container: ReactorId,
133    local: LocalReactionId,
134}
135
136impl GlobalId {
137    pub fn new(container: ReactorId, local: LocalReactionId) -> Self {
138        Self { container, local }
139    }
140
141    #[allow(unused)]
142    pub(crate) fn from_raw(u: GlobalIdImpl) -> Self {
143        unsafe { std::mem::transmute(u) }
144    }
145
146    pub(crate) const fn container(&self) -> ReactorId {
147        self.container
148    }
149
150    pub(crate) const fn local(&self) -> LocalReactionId {
151        self.local
152    }
153}
154
155impl FromStr for GlobalId {
156    type Err = &'static str;
157
158    fn from_str(s: &str) -> Result<Self, Self::Err> {
159        if let Some((container, local)) = s.split_once('/') {
160            let container = container.parse::<ReactorIdImpl>().map_err(|_| "invalid reactor id")?;
161            let local = local.parse::<ReactionIdImpl>().map_err(|_| "invalid local id")?;
162            Ok(GlobalId::new(ReactorId::new(container), LocalReactionId::new(local)))
163        } else {
164            Err("Expected format {int}/{int}")
165        }
166    }
167}
168
169// Hashing global ids is a very hot operation in the framework,
170// therefore we give it an optimal implementation.
171// The implementation was verified to be faster than the default
172// derive by a micro benchmark in this repo.
173impl Hash for GlobalId {
174    #[inline]
175    fn hash<H: Hasher>(&self, state: &mut H) {
176        let as_impl: &GlobalIdImpl = unsafe { std::mem::transmute(self) };
177        // this is written so that it works regardless of the concrete type of GlobalIdImpl
178        Hash::hash(as_impl, state);
179    }
180}
181
182// Since Hash was implemented explicitly, we have to do it for PartialEq as well.
183impl PartialEq for GlobalId {
184    fn eq(&self, other: &Self) -> bool {
185        let self_impl: &GlobalIdImpl = unsafe { std::mem::transmute(self) };
186        let other_impl: &GlobalIdImpl = unsafe { std::mem::transmute(other) };
187        self_impl == other_impl
188    }
189}
190
191// Same reasoning as for Hash, comparison is used to keep Level
192// sets sorted, when feature `vec-id-sets` is enabled.
193impl Ord for GlobalId {
194    fn cmp(&self, other: &Self) -> Ordering {
195        let self_as_impl: &GlobalIdImpl = unsafe { std::mem::transmute(self) };
196        let other_as_impl: &GlobalIdImpl = unsafe { std::mem::transmute(other) };
197        self_as_impl.cmp(other_as_impl)
198    }
199}
200
201impl PartialOrd for GlobalId {
202    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
203        Some(self.cmp(other))
204    }
205}
206
207impl Debug for GlobalId {
208    #[inline]
209    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
210        <Self as Display>::fmt(self, f)
211    }
212}
213
214impl Display for GlobalId {
215    #[inline]
216    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
217        write!(f, "{}/{}", self.container(), self.local())
218    }
219}
220
221/// private implementation types
222pub(crate) mod impl_types {
223    cfg_if! {
224        if #[cfg(all(target_pointer_width = "64", feature = "wide-ids"))] {
225            type MyUsize = usize;
226            type HalfUsize = u32;
227        } else {
228            type MyUsize = u32;
229            type HalfUsize = u16;
230        }
231    }
232
233    pub type TriggerIdImpl = MyUsize;
234    pub type ReactionIdImpl = HalfUsize;
235    pub type ReactorIdImpl = HalfUsize;
236    pub type GlobalIdImpl = MyUsize;
237    assert_eq_size!(GlobalIdImpl, (ReactorIdImpl, ReactionIdImpl));
238    assert_impl_all!(GlobalIdImpl: petgraph::graph::IndexType);
239    assert_impl_all!(ReactorIdImpl: petgraph::graph::IndexType);
240}