Skip to main content

sim_kernel/
ref_id.rs

1//! Reference identity: the [`Ref`] contract and its content/handle ids.
2//!
3//! Defines the stable reference types -- content ids, handle ids, and
4//! coordinates -- that name data and objects across the substrate; libraries
5//! resolve refs to values.
6
7use std::{
8    sync::{
9        OnceLock,
10        atomic::{AtomicU64, Ordering},
11    },
12    time::{SystemTime, UNIX_EPOCH},
13};
14
15use crate::id::Symbol;
16
17/// A stable reference to data or an object across the substrate.
18///
19/// The kernel defines the reference contract; libraries resolve a [`Ref`] to a
20/// concrete value. A ref names its target by symbol, by content id, by a
21/// process-local handle, or by a ranked coordinate.
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub enum Ref {
24    /// A named reference resolved through the registry.
25    Symbol(Symbol),
26    /// A content-addressed reference to an immutable datum.
27    Content(ContentId),
28    /// A process-local handle to a live object.
29    Handle(HandleId),
30    /// A ranked coordinate within a named space.
31    Coord(Coordinate),
32}
33
34/// Content-addressed identity: a hash algorithm plus its 32-byte digest.
35#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct ContentId {
37    /// The hash algorithm that produced the digest (for example `core/sha256`).
38    pub algorithm: Symbol,
39    /// The 32-byte content digest.
40    pub bytes: [u8; 32],
41}
42
43impl ContentId {
44    /// Builds a content id from an algorithm symbol and a 32-byte digest.
45    pub fn from_bytes(algorithm: Symbol, bytes: [u8; 32]) -> Self {
46        Self { algorithm, bytes }
47    }
48}
49
50/// Process-local handle to a live object, unique within this process.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52pub struct HandleId(pub u128);
53
54impl HandleId {
55    /// Allocates a fresh handle, distinct from every other in this process.
56    pub fn fresh() -> Self {
57        let salt = u128::from(*PROCESS_SALT.get_or_init(make_process_salt));
58        let counter = u128::from(NEXT_HANDLE.fetch_add(1, Ordering::Relaxed));
59        Self((salt << 64) | counter)
60    }
61}
62
63/// A ranked coordinate: an ordinal position within a named space.
64#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct Coordinate {
66    /// The named coordinate space.
67    pub space: Symbol,
68    /// The position within the space, keyed by content id.
69    pub ordinal: ContentId,
70}
71
72static NEXT_HANDLE: AtomicU64 = AtomicU64::new(1);
73static PROCESS_SALT: OnceLock<u64> = OnceLock::new();
74
75fn make_process_salt() -> u64 {
76    let nanos = SystemTime::now()
77        .duration_since(UNIX_EPOCH)
78        .map_or(0, |duration| duration.as_nanos());
79    let folded = (nanos ^ (nanos >> 64)) & u128::from(u64::MAX);
80    let time_salt = u64::try_from(folded).unwrap_or(0);
81    time_salt ^ (u64::from(std::process::id()) << 32)
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    fn content_id(byte: u8) -> ContentId {
89        ContentId::from_bytes(Symbol::qualified("core", "sha256"), [byte; 32])
90    }
91
92    #[test]
93    fn ref_id_equal_symbols_compare_equal() {
94        let left = Ref::Symbol(Symbol::qualified("core", "Bool"));
95        let right = Ref::Symbol(Symbol::qualified("core", "Bool"));
96
97        assert_eq!(left, right);
98    }
99
100    #[test]
101    fn ref_id_equal_content_ids_compare_equal() {
102        let left = content_id(1);
103        let right = content_id(1);
104
105        assert_eq!(left, right);
106    }
107
108    #[test]
109    fn ref_id_equal_coordinates_compare_equal() {
110        let left = Coordinate {
111            space: Symbol::qualified("rank", "natural"),
112            ordinal: content_id(2),
113        };
114        let right = Coordinate {
115            space: Symbol::qualified("rank", "natural"),
116            ordinal: content_id(2),
117        };
118
119        assert_eq!(left, right);
120    }
121
122    #[test]
123    fn ref_id_handle_never_equals_content_id() {
124        let handle = Ref::Handle(HandleId::fresh());
125        let content = Ref::Content(content_id(3));
126
127        assert_ne!(handle, content);
128    }
129
130    #[test]
131    fn ref_id_fresh_handles_are_distinct() {
132        assert_ne!(HandleId::fresh(), HandleId::fresh());
133    }
134}