uid/
lib.rs

1// lib.rs
2
3// *************************************************************************
4// * Copyright (C) 2018-2021 Daniel Mueller (deso@posteo.net)              *
5// *                                                                       *
6// * This program is free software: you can redistribute it and/or modify  *
7// * it under the terms of the GNU General Public License as published by  *
8// * the Free Software Foundation, either version 3 of the License, or     *
9// * (at your option) any later version.                                   *
10// *                                                                       *
11// * This program is distributed in the hope that it will be useful,       *
12// * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14// * GNU General Public License for more details.                          *
15// *                                                                       *
16// * You should have received a copy of the GNU General Public License     *
17// * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
18// *************************************************************************
19
20#![no_std]
21#![warn(
22  future_incompatible,
23  missing_copy_implementations,
24  missing_debug_implementations,
25  missing_docs,
26  rust_2018_compatibility,
27  rust_2018_idioms,
28  trivial_casts,
29  trivial_numeric_casts,
30  unsafe_op_in_unsafe_fn,
31  unstable_features,
32  unused_import_braces,
33  unused_qualifications,
34  unused_results
35)]
36
37//! A crate providing in-memory IDs. Among others, the IDs are
38//! guaranteed to be unique, even when created on different threads.
39
40#[cfg(test)]
41#[macro_use]
42extern crate std;
43
44use core::fmt::Debug;
45use core::fmt::Display;
46use core::fmt::Formatter;
47use core::fmt::Result;
48use core::marker::PhantomData;
49use core::num::NonZeroU16;
50use core::num::NonZeroU32;
51use core::num::NonZeroU64;
52use core::num::NonZeroU8;
53use core::num::NonZeroUsize;
54use core::sync::atomic::AtomicU16;
55use core::sync::atomic::AtomicU32;
56use core::sync::atomic::AtomicU64;
57use core::sync::atomic::AtomicU8;
58use core::sync::atomic::AtomicUsize;
59use core::sync::atomic::Ordering;
60
61
62macro_rules! IdImpl {
63  ( $(#[$docs:meta])* struct $name: ident, $int_type:ty, $non_zero_type:ty, $atomic_type: ty ) => {
64    $(#[$docs])*
65    #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
66    #[repr(transparent)]
67    pub struct $name<T> {
68      id: $non_zero_type,
69      phantom: PhantomData<T>,
70    }
71
72    impl<T> $name<T> {
73      /// Create a new ID using the given value.
74      ///
75      /// # Panics
76      /// This constructor panics if an overflow of the underlying
77      /// counter occurred.
78      ///
79      /// # Safety
80      /// - `id` must not be zero
81      /// - `id` should be unique with respect to other IDs created for this
82      ///   `T` to preserve the invariant that IDs are unique
83      #[inline]
84      pub unsafe fn new_unchecked(id: $int_type) -> Self {
85        Self {
86          id: unsafe { <$non_zero_type>::new_unchecked(id) },
87          phantom: PhantomData,
88        }
89      }
90
91      /// Create a new unique ID.
92      ///
93      /// # Panics
94      /// This constructor panics if an overflow of the underlying
95      /// counter occurred.
96      #[inline]
97      pub fn new() -> Self {
98        static NEXT_ID: $atomic_type = <$atomic_type>::new(1);
99
100        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
101        assert_ne!(
102          id, 0,
103          "overflow detected; please use a larger integer to or reconsider your use case"
104        );
105
106        // SAFETY: The provided ID cannot be 0 (unless we overflow, in which
107        //         case we have other problems). We ensure uniqueness
108        //         because we increment IDs and this is the only constructor
109        //         for ID objects.
110        unsafe { Self::new_unchecked(id) }
111      }
112
113      /// Retrieve the underlying integer value.
114      #[inline]
115      pub fn get(self) -> $int_type {
116        self.id.get()
117      }
118    }
119
120    impl<T> Default for $name<T> {
121      /// Create a new unique ID.
122      #[inline]
123      fn default() -> Self {
124        Self::new()
125      }
126    }
127
128    impl<T> Debug for $name<T> {
129      #[inline]
130      fn fmt(&self, f: &mut Formatter<'_>) -> Result {
131        f.debug_tuple(stringify!($name)).field(&self.id).finish()
132      }
133    }
134
135    impl<T> Display for $name<T> {
136      /// Format the ID with the given formatter.
137      #[inline]
138      fn fmt(&self, f: &mut Formatter<'_>) -> Result {
139        write!(f, "{}", self.id)
140      }
141    }
142  }
143}
144
145
146IdImpl! {
147  /// A struct representing IDs usable for various purposes.
148  ///
149  /// Except for [`Debug`] and [`Display`] which are implemented
150  /// unconditionally, the type will only implement [`Clone`],
151  /// [`Copy`], [`Eq`], [`Ord`], [`PartialEq`], [`PartialOrd`], and
152  /// [`Hash`] if the provided `T` implements them.
153  ///
154  /// # Examples
155  ///
156  /// A commonly seen pattern for creating of a type `Id` that is unique
157  /// may look as follows:
158  /// ```rust
159  /// use uid::Id as IdT;
160  ///
161  /// #[derive(Copy, Clone, Eq, PartialEq)]
162  /// struct T(());
163  ///
164  /// type Id = IdT<T>;
165  ///
166  /// let id1 = Id::new();
167  /// let id2 = Id::new();
168  ///
169  /// assert_ne!(id1, id2)
170  /// ```
171  ///
172  /// In this example the type `T` is just an arbitrary type, but it
173  /// allows us to create distinct ID types. For example, when another ID
174  /// type is required for a different purpose, that can be easily
175  /// created:
176  /// ```rust
177  /// # use uid::Id as IdT;
178  /// # #[derive(Copy, Clone)]
179  /// # struct T(());
180  /// # type Id = IdT<T>;
181  /// #[derive(Copy, Clone)]
182  /// struct U(());
183  ///
184  /// type Key = IdT<U>;
185  ///
186  /// // `Key` and `Id` are fundamentally different types, with no
187  /// // allowed interaction between each other. That is, Rust's type
188  /// // system will prevent accidental usage of one in place of the
189  /// // other. The same can be said about the relationship to built-in
190  /// // numeric types such as `usize` or `u64`.
191  /// ```
192  struct Id, usize, NonZeroUsize, AtomicUsize
193}
194IdImpl! {
195  /// A struct representing IDs usable for various purposes using an eight
196  /// bit wide unsigned integer.
197  ///
198  /// Please see the [`Id`] type for more general information and usage
199  /// examples.
200  struct IdU8, u8, NonZeroU8, AtomicU8
201}
202IdImpl! {
203  /// A struct representing IDs usable for various purposes using an 16
204  /// bit wide unsigned integer.
205  ///
206  /// Please see the [`Id`] type for more general information and usage
207  /// examples.
208  struct IdU16, u16, NonZeroU16, AtomicU16
209}
210IdImpl! {
211  /// A struct representing IDs usable for various purposes using an 32
212  /// bit wide unsigned integer.
213  ///
214  /// Please see the [`Id`] type for more general information and usage
215  /// examples.
216  struct IdU32, u32, NonZeroU32, AtomicU32
217}
218IdImpl! {
219  /// A struct representing IDs usable for various purposes using an 64
220  /// bit wide unsigned integer.
221  ///
222  /// Please see the [`Id`] type for more general information and usage
223  /// examples.
224  struct IdU64, u64, NonZeroU64, AtomicU64
225}
226
227
228#[cfg(test)]
229mod tests {
230  use super::*;
231
232  use std::collections::BTreeSet;
233  use std::collections::HashSet;
234  use std::iter::FromIterator;
235  use std::mem::size_of;
236  use std::mem::size_of_val;
237  use std::thread::spawn;
238  use std::vec::Vec;
239
240
241  type TestId = Id<u32>;
242
243
244  /// Make sure that [`Id`] values are increasing.
245  #[test]
246  fn unique_id_increases() {
247    let id1 = TestId::new();
248    let id2 = TestId::new();
249
250    assert!(id2 > id1);
251    assert!(id2.get() > id1.get());
252  }
253
254  /// Test that [`Id`] objects created on different threads preserve
255  /// uniqueness invariant.
256  #[test]
257  fn thread_safety() {
258    #[allow(clippy::needless_collect)]
259    fn test<T>()
260    where
261      T: FromIterator<TestId> + IntoIterator,
262    {
263      let handles = (0..100).map(|_| spawn(TestId::new)).collect::<Vec<_>>();
264
265      let result = handles
266        .into_iter()
267        .map(|x| x.join().unwrap())
268        .collect::<T>();
269
270      assert_eq!(result.into_iter().count(), 100);
271    }
272
273    // Run the test both with a `BTreeSet` and `HashSet` to test the
274    // implementations of the traits they require.
275    test::<BTreeSet<TestId>>();
276    test::<HashSet<TestId>>();
277  }
278
279  /// Check that the [`Debug`] implementation of [`Id`] works as
280  /// expected.
281  #[test]
282  fn debug() {
283    let id = unsafe { TestId::new_unchecked(42) };
284    assert_eq!(format!("{:?}", id), "Id(42)");
285
286    type TestId2 = IdU16<()>;
287
288    let id = unsafe { TestId2::new_unchecked(1337) };
289    assert_eq!(format!("{:?}", id), "IdU16(1337)");
290  }
291
292  /// Check that the [`Display`] implementation of [`Id`] works as
293  /// expected.
294  #[test]
295  fn display() {
296    let id = unsafe { TestId::new_unchecked(43) };
297    assert_eq!(format!("{}", id), "43");
298  }
299
300  /// Make sure that our [`Id`] type has expected memory layout and
301  /// size.
302  #[test]
303  fn size() {
304    let id = Some(TestId::new());
305    assert_eq!(size_of_val(&id), size_of::<TestId>());
306    assert_eq!(size_of::<TestId>(), size_of::<usize>());
307
308    assert_eq!(size_of::<IdU8<()>>(), size_of::<u8>());
309    assert_eq!(size_of::<IdU16<()>>(), size_of::<u16>());
310    assert_eq!(size_of::<IdU32<()>>(), size_of::<u32>());
311    assert_eq!(size_of::<IdU64<()>>(), size_of::<u64>());
312  }
313
314  /// Verify that we panic when we create more ID objects than the
315  /// underlying integer type can represent.
316  #[test]
317  #[should_panic(expected = "overflow detected")]
318  fn overflow() {
319    (0..256).for_each(|_| {
320      let _ = IdU8::<()>::new();
321    });
322  }
323}