Skip to main content

sim_lib_mutation/managed/
handles.rs

1/// The tracing ABI understood by this arena.
2#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3pub enum TraceContractVersion {
4    /// Roots, strong and weak edges, ephemerons, safepoints, clearing, and teardown.
5    V1,
6}
7/// A stable managed-object identity assigned from allocation order.
8#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
9pub struct ManagedId(u64);
10
11impl ManagedId {
12    /// Returns the zero-based allocation ordinal.
13    pub const fn allocation_ordinal(self) -> u64 {
14        self.0
15    }
16}
17
18/// A stable identity for one root registration.
19#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
20pub struct RootId(u64);
21
22/// An object handle. It does not itself keep the object rooted.
23#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
24pub struct ManagedHandle {
25    id: ManagedId,
26}
27
28impl ManagedHandle {
29    /// Returns the managed identity.
30    pub const fn id(self) -> ManagedId {
31        self.id
32    }
33
34    /// Produces a non-rooting weak handle.
35    pub const fn downgrade(self) -> WeakHandle {
36        WeakHandle { id: self.id }
37    }
38}
39
40/// A registered root handle. Dropping this value does not mutate the arena;
41/// callers explicitly release it so root changes remain transactional.
42#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
43pub struct RootedHandle {
44    root: RootId,
45    handle: ManagedHandle,
46}
47
48impl RootedHandle {
49    /// Returns the root registration identity.
50    pub const fn root_id(self) -> RootId {
51        self.root
52    }
53
54    /// Returns the underlying object handle.
55    pub const fn handle(self) -> ManagedHandle {
56        self.handle
57    }
58}
59
60/// A non-rooting object handle which may become stale.
61#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
62pub struct WeakHandle {
63    id: ManagedId,
64}
65
66impl WeakHandle {
67    /// Returns the identity without claiming the object is still live.
68    pub const fn id(self) -> ManagedId {
69        self.id
70    }
71}
72
73/// Stable identity of an edge within its owning object.
74///
75/// Identities are allocated monotonically by [`EdgeAllocator`]. They are local
76/// to one managed object, remain ordered by allocation, and are never reused.
77#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
78pub struct EdgeId(pub u32);
79
80impl EdgeId {
81    /// Returns the zero-based allocation ordinal within the owning object.
82    pub const fn allocation_ordinal(self) -> u32 {
83        self.0
84    }
85}
86
87/// The collection semantics of a managed edge.
88#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
89pub enum EdgeKind {
90    /// A retaining edge.
91    Strong,
92    /// A non-retaining edge that may be cleared.
93    Weak,
94    /// A key/value edge whose value is retained only by a reachable key.
95    Ephemeron,
96}
97
98/// Hard limits for the outgoing edges owned by one [`ManagedNode`].
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub struct EdgeLimits {
101    total: usize,
102    strong: usize,
103    weak: usize,
104    ephemeron: usize,
105}
106
107impl EdgeLimits {
108    /// Default per-node hard cap. Per-kind caps share the same ceiling while
109    /// the total cap prevents their sum from exceeding it.
110    pub const DEFAULT: Self = Self::new(65_536, 65_536, 65_536, 65_536);
111
112    /// Defines the total and per-kind edge caps. Zero is a valid cap.
113    pub const fn new(total: usize, strong: usize, weak: usize, ephemeron: usize) -> Self {
114        Self {
115            total,
116            strong,
117            weak,
118            ephemeron,
119        }
120    }
121
122    /// Returns the total outgoing-edge cap.
123    pub const fn total(self) -> usize {
124        self.total
125    }
126
127    /// Returns the cap for `kind`.
128    pub const fn for_kind(self, kind: EdgeKind) -> usize {
129        match kind {
130            EdgeKind::Strong => self.strong,
131            EdgeKind::Weak => self.weak,
132            EdgeKind::Ephemeron => self.ephemeron,
133        }
134    }
135}
136
137impl Default for EdgeLimits {
138    fn default() -> Self {
139        Self::DEFAULT
140    }
141}
142
143/// One allocation-ordered edge in a deterministic node snapshot.
144#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub enum EdgeSnapshot {
146    /// A retaining edge.
147    Strong {
148        /// Stable edge identity.
149        edge: EdgeId,
150        /// Retained target.
151        target: ManagedId,
152    },
153    /// A non-retaining edge.
154    Weak {
155        /// Stable edge identity.
156        edge: EdgeId,
157        /// Non-retained target.
158        target: ManagedId,
159    },
160    /// An ephemeron key/value pair.
161    Ephemeron {
162        /// Stable edge identity.
163        edge: EdgeId,
164        /// Conditional-retention key.
165        key: ManagedId,
166        /// Conditionally retained value.
167        value: ManagedId,
168    },
169}
170
171impl EdgeSnapshot {
172    /// Returns the stable edge identity independent of edge kind.
173    pub const fn id(self) -> EdgeId {
174        match self {
175            Self::Strong { edge, .. } | Self::Weak { edge, .. } | Self::Ephemeron { edge, .. } => {
176                edge
177            }
178        }
179    }
180}
181
182/// An edge identity paired with its immutable collection semantics.
183#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
184pub struct TypedEdgeId {
185    id: EdgeId,
186    kind: EdgeKind,
187}
188
189impl TypedEdgeId {
190    /// Returns the stable identity.
191    pub const fn id(self) -> EdgeId {
192        self.id
193    }
194
195    /// Returns the edge's collection semantics.
196    pub const fn kind(self) -> EdgeKind {
197        self.kind
198    }
199}
200
201/// Fail-closed edge allocation errors.
202#[derive(Clone, Copy, Debug, Eq, PartialEq)]
203pub enum EdgeAllocationError {
204    /// The per-object edge identity space is exhausted.
205    IdentityExhausted,
206    /// The configured total or per-kind edge cap was reached.
207    CapacityExceeded {
208        /// Kind requested by the refused insertion.
209        kind: EdgeKind,
210        /// Applicable total or per-kind cap.
211        cap: usize,
212    },
213}
214
215impl fmt::Display for EdgeAllocationError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        match self {
218            Self::IdentityExhausted => f.write_str("managed edge identity space exhausted"),
219            Self::CapacityExceeded { kind, cap } => {
220                write!(f, "managed {kind:?} edge cap {cap} reached")
221            }
222        }
223    }
224}
225
226impl Error for EdgeAllocationError {}
227
228/// Monotonic, per-object allocation of stable edge identities.
229///
230/// Removing an edge is deliberately not an allocator operation: allocated
231/// identities are evidence and remain consumed for the lifetime of their
232/// owner. Once the `u32` identity space is exhausted, every later allocation
233/// fails without changing allocator state.
234#[derive(Clone, Debug, Eq, PartialEq)]
235pub struct EdgeAllocator {
236    next: Option<u32>,
237}
238
239impl EdgeAllocator {
240    /// Creates an allocator whose first identity has ordinal zero.
241    pub const fn new() -> Self {
242        Self { next: Some(0) }
243    }
244
245    /// Allocates the next identity with immutable `kind` semantics.
246    pub fn allocate(&mut self, kind: EdgeKind) -> Result<TypedEdgeId, EdgeAllocationError> {
247        let ordinal = self.next.ok_or(EdgeAllocationError::IdentityExhausted)?;
248        self.next = ordinal.checked_add(1);
249        Ok(TypedEdgeId {
250            id: EdgeId(ordinal),
251            kind,
252        })
253    }
254
255    #[cfg(test)]
256    pub(crate) const fn starting_at(next: u32) -> Self {
257        Self { next: Some(next) }
258    }
259}
260
261impl Default for EdgeAllocator {
262    fn default() -> Self {
263        Self::new()
264    }
265}