use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SegmentId(NonZeroU64);
const FRESH_BASE: u64 = 1 << 48;
impl SegmentId {
pub fn fresh() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(FRESH_BASE);
let raw = COUNTER.fetch_add(1, Ordering::Relaxed);
Self(NonZeroU64::new(raw).expect("SegmentId counter wrapped to zero"))
}
pub const fn from_raw(value: NonZeroU64) -> Self {
Self(value)
}
pub const fn from_u64(value: u64) -> Self {
match NonZeroU64::new(value) {
Some(v) => Self(v),
None => panic!("SegmentId::from_u64 requires a non-zero value"),
}
}
pub const fn raw(self) -> NonZeroU64 {
self.0
}
pub const fn get(self) -> u64 {
self.0.get()
}
}
impl std::fmt::Display for SegmentId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SegmentId({})", self.0.get())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_ids_are_unique_and_non_zero() {
let a = SegmentId::fresh();
let b = SegmentId::fresh();
assert_ne!(a, b);
assert!(a.get() > 0);
assert!(b.get() > 0);
}
#[test]
fn raw_round_trips() {
let id = SegmentId::from_u64(42);
assert_eq!(id.get(), 42);
assert_eq!(SegmentId::from_raw(id.raw()), id);
}
#[test]
fn const_construction_is_usable_in_a_const_item() {
const A: SegmentId = SegmentId::from_u64(7);
assert_eq!(A.get(), 7);
}
#[test]
fn fresh_ids_never_collide_with_small_app_constants() {
const APP: SegmentId = SegmentId::from_u64(1);
for _ in 0..64 {
assert_ne!(SegmentId::fresh(), APP);
}
assert!(SegmentId::fresh().get() >= FRESH_BASE);
}
}