use std::{
sync::{
OnceLock,
atomic::{AtomicU64, Ordering},
},
time::{SystemTime, UNIX_EPOCH},
};
use crate::id::Symbol;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Ref {
Symbol(Symbol),
Content(ContentId),
Handle(HandleId),
Coord(Coordinate),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContentId {
pub algorithm: Symbol,
pub bytes: [u8; 32],
}
impl ContentId {
pub fn from_bytes(algorithm: Symbol, bytes: [u8; 32]) -> Self {
Self { algorithm, bytes }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HandleId(pub u128);
impl HandleId {
pub fn fresh() -> Self {
let salt = u128::from(*PROCESS_SALT.get_or_init(make_process_salt));
let counter = u128::from(NEXT_HANDLE.fetch_add(1, Ordering::Relaxed));
Self((salt << 64) | counter)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Coordinate {
pub space: Symbol,
pub ordinal: ContentId,
}
static NEXT_HANDLE: AtomicU64 = AtomicU64::new(1);
static PROCESS_SALT: OnceLock<u64> = OnceLock::new();
fn make_process_salt() -> u64 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |duration| duration.as_nanos());
let folded = (nanos ^ (nanos >> 64)) & u128::from(u64::MAX);
let time_salt = u64::try_from(folded).unwrap_or(0);
time_salt ^ (u64::from(std::process::id()) << 32)
}
#[cfg(test)]
mod tests {
use super::*;
fn content_id(byte: u8) -> ContentId {
ContentId::from_bytes(Symbol::qualified("core", "sha256"), [byte; 32])
}
#[test]
fn ref_id_equal_symbols_compare_equal() {
let left = Ref::Symbol(Symbol::qualified("core", "Bool"));
let right = Ref::Symbol(Symbol::qualified("core", "Bool"));
assert_eq!(left, right);
}
#[test]
fn ref_id_equal_content_ids_compare_equal() {
let left = content_id(1);
let right = content_id(1);
assert_eq!(left, right);
}
#[test]
fn ref_id_equal_coordinates_compare_equal() {
let left = Coordinate {
space: Symbol::qualified("rank", "natural"),
ordinal: content_id(2),
};
let right = Coordinate {
space: Symbol::qualified("rank", "natural"),
ordinal: content_id(2),
};
assert_eq!(left, right);
}
#[test]
fn ref_id_handle_never_equals_content_id() {
let handle = Ref::Handle(HandleId::fresh());
let content = Ref::Content(content_id(3));
assert_ne!(handle, content);
}
#[test]
fn ref_id_fresh_handles_are_distinct() {
assert_ne!(HandleId::fresh(), HandleId::fresh());
}
}