You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The G-node arena reuses slots via a free list. A handle obtained
before an eviction could silently bind to a new, unrelated node
after the slot is reallocated (ABA problem). This is not
hypothetical — the sentinel stores handles across observation
cycles where evict-then-split sequences can recycle slots.
Introduce a two-tier handle design:
- GNodeId (pub, 8 bytes): slot index + generation counter.
Used on all public API surfaces. Consuming methods
(decay, gnode_info, is_ancestor_of) validate the generation
and panic on mismatch.
- GSlotPointer (pub(crate), 4 bytes): generation-free handle
for internal tree walks where no interleaving eviction can
occur. Renamed from the former GNodeId.
- VSlotPointer (pub(crate), 4 bytes): renamed from VNodeId
for consistency with the new naming convention.
Arena changes:
- alloc() returns (index, generation) tuple
- dealloc() increments the per-slot generation counter
- New generation() accessor for handle validation
The generation Vec is cold data — touched only on alloc/dealloc,
never on hot read/write paths. Internal hot paths continue to use
4-byte GSlotPointer; the 8-byte GNodeId exists only at the API
boundary.
| A |`Cell<'a, C, V>` borrows `&'a GvGraph` + `GNodeId` — deref to fields lazily | Zero-copy, minimal struct (pointer + handle). But every field access goes through a pointer chase. |
88
+
| A |`Cell<'a, C, V>` borrows `&'a GvGraph` + `GSlotPointer` — deref to fields lazily | Zero-copy, minimal struct (pointer + handle). But every field access goes through a pointer chase. |
89
89
| B |`Cell<'a, C, V>` contains `start: C, end: C, intensity: V, depth: u32` — snapshot at creation | Copied on construction. Self-contained. No borrow escape hazards. Can outlive the graph if `'a` is removed (but then it's just a `Span`). |
90
90
| C |`Cell<'a, C, V> = &'a Span<C, V>` — `Cell` is a type alias for a reference | Simplest. No new struct. But can't add Cell-specific methods without a newtype. |
91
91
| D |`Cell<'a, C, V>` borrows individual fields: `start: &'a C, end: &'a C, intensity: &'a V`| Fine-grained borrows. Unusual in Rust — typically borrow-of-struct patterns use a single reference. |
@@ -98,7 +98,7 @@ What should borrowed view types look like?
98
98
-`Cell` should support `cell.to_span() -> Span<C, V>` for ownership
99
99
transfer regardless of option chosen.
100
100
-`Node` (an internal G-Tree node) could follow the same pattern as
101
-
Cell, adding `children: (GNodeId, GNodeId)` for traversal. Or it
101
+
Cell, adding `children: (GSlotPointer, GSlotPointer)` for traversal. Or it
102
102
can be deferred — most API surfaces only expose terminal cells.
103
103
- If `Cell` is just a `Span` with a lifetime, Option C is simplest
0 commit comments