1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
// SPDX-FileCopyrightText: The im-pathtree authors
// SPDX-License-Identifier: MPL-2.0
use std::{
num::NonZeroUsize,
sync::atomic::{AtomicUsize, Ordering},
};
const ZERO_NODE_ID_VALUE: usize = 0;
static LAST_NODE_ID_VALUE: AtomicUsize = AtomicUsize::new(ZERO_NODE_ID_VALUE);
/// Fast, ephemeral node identifier.
///
/// Unique across all in-memory nodes within a single process. Identifiers
/// are re-generated and assigned when the process is restarted and must
/// not be stored permanently!
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, derive_more::Display)]
pub struct NodeId(NonZeroUsize);
impl NodeId {
/// Generate a new, unique identifier.
///
/// Only the first [`usize::MAX`] identifiers are guaranteed to be unique.
/// All following identifiers will be duplicates of previous ones.
///
/// ```
/// # use im_pathtree::NodeId;
/// let foo_id = NodeId::new();
/// let bar_id = NodeId::new();
/// assert_ne!(foo_id, bar_id);
/// ```
#[allow(clippy::new_without_default)] // Prevent unintended generation of new identifiers
pub fn new() -> Self {
loop {
// No memory ordering guarantees when invoking this function.
// We only need to ensure that the next value is unique.
let last_value = LAST_NODE_ID_VALUE.fetch_add(1, Ordering::Relaxed);
// fetch_add() performs a wrapping add, so we need to do the same
let next_value = last_value.wrapping_add(1);
if let Some(next_value) = NonZeroUsize::new(next_value) {
return Self(next_value);
}
}
}
}