compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/graph.rad
raw
| 1 | //! Construction authority for region-bound graphs with stable node identities. |
| 2 | //! |
| 3 | //! Typed opaque wrappers keep their payloads and links private. They check all |
| 4 | //! linked nodes before writing a payload, then mark the node complete. Payload |
| 5 | //! writes require the builder. Frozen readers require the published graph. |
| 6 | |
| 7 | @test mod tests; |
| 8 | |
| 9 | use std::lang::alloc; |
| 10 | |
| 11 | /// Largest node count represented by a graph. |
| 12 | constant MAX_NODE_COUNT: u32 = 0xffffffff; |
| 13 | |
| 14 | /// Exclusive authority to construct one graph. |
| 15 | export opaque record Builder: 'g { |
| 16 | /// Unique identity shared by this graph's nodes. |
| 17 | owner: &'g u8, |
| 18 | /// Number of reserved node identities. |
| 19 | count: u32, |
| 20 | /// Number of nodes whose payloads are not complete. |
| 21 | pending: u32, |
| 22 | } |
| 23 | |
| 24 | /// Stable identity retained by a typed node wrapper. |
| 25 | export opaque record Node: 'g + Copy { |
| 26 | /// Identity of the graph that owns this node. |
| 27 | owner: &'g u8, |
| 28 | /// Completion state controlled by the graph builder. |
| 29 | initialized: &'g cell bool, |
| 30 | } |
| 31 | |
| 32 | /// Shared authority to read a completed graph. |
| 33 | export opaque record Frozen: 'g + Copy { |
| 34 | /// Identity of the graph that owns all published nodes. |
| 35 | owner: &'g u8, |
| 36 | /// Number of published node identities. |
| 37 | count: u32, |
| 38 | } |
| 39 | |
| 40 | /// Invalid access through a graph capability. |
| 41 | export union Error: Copy { |
| 42 | /// The node belongs to a different graph. |
| 43 | ForeignNode, |
| 44 | } |
| 45 | |
| 46 | /// A graph that cannot yet be published. |
| 47 | export union FreezeError: 'g { |
| 48 | /// Construction authority for the graph that still has pending nodes. |
| 49 | Incomplete(Builder 'g), |
| 50 | } |
| 51 | |
| 52 | /// Create an empty graph whose identity lives in the allocation session. |
| 53 | export fn new 'g (storage: &Session 'g) -> Builder 'g throws (alloc::AllocError) { |
| 54 | let owner = try storage.new(0 as u8); |
| 55 | return Builder 'g { owner: &*owner, count: 0, pending: 0 }; |
| 56 | } |
| 57 | |
| 58 | /// Reserve a stable node identity before its typed payload is complete. |
| 59 | /// Allocation failure leaves the builder unchanged. |
| 60 | export fn reserve 'g (builder: &mut Builder 'g, storage: &Session 'g) -> Node 'g |
| 61 | throws (alloc::AllocError) |
| 62 | { |
| 63 | if builder.count == MAX_NODE_COUNT { |
| 64 | throw alloc::AllocError::OutOfMemory; |
| 65 | } |
| 66 | let initialized = try storage.new(false); |
| 67 | let result = Node 'g { owner: builder.owner, initialized: &cell *initialized }; |
| 68 | set builder.count += 1; |
| 69 | set builder.pending += 1; |
| 70 | return result; |
| 71 | } |
| 72 | |
| 73 | /// Check a node before reading or writing its typed payload during construction. |
| 74 | /// Typed wrappers must also check every node linked by a payload write. |
| 75 | export fn check 'g (builder: &Builder 'g, node: Node 'g) throws (Error) { |
| 76 | if builder.owner <> node.owner { |
| 77 | throw Error::ForeignNode; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /// Mark a node complete after its typed payload and links are initialized. |
| 82 | /// Completing an initialized node leaves the pending count unchanged. |
| 83 | export fn complete 'g (builder: &mut Builder 'g, node: Node 'g) throws (Error) { |
| 84 | try check(builder, node); |
| 85 | if not *node.initialized { |
| 86 | assert builder.pending > 0; |
| 87 | set *node.initialized = true; |
| 88 | set builder.pending -= 1; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /// Return the number of reserved nodes that still need initialization. |
| 93 | export fn pending 'g (builder: &Builder 'g) -> u32 { |
| 94 | return builder.pending; |
| 95 | } |
| 96 | |
| 97 | /// Consume construction authority without publishing the graph. |
| 98 | /// Node identity allocations remain owned by the allocation session. |
| 99 | export fn abandon 'g (builder: Builder 'g) { |
| 100 | let case Builder 'g { owner: _, count: _, pending: _ } = builder |
| 101 | else panic "abandon: invalid builder"; |
| 102 | } |
| 103 | |
| 104 | /// Consume construction authority and publish the completed graph. |
| 105 | /// An incomplete graph returns its builder in the error for recovery. |
| 106 | export fn freeze 'g (builder: Builder 'g) -> Frozen 'g throws (FreezeError 'g) { |
| 107 | if builder.pending <> 0 { |
| 108 | throw FreezeError 'g::Incomplete(builder); |
| 109 | } |
| 110 | let case Builder 'g { owner, count, pending: _ } = builder |
| 111 | else panic "freeze: invalid builder"; |
| 112 | return Frozen 'g { owner, count }; |
| 113 | } |
| 114 | |
| 115 | /// Check a node before reading its typed payload through a frozen graph. |
| 116 | export fn checkFrozen 'g (graph: &Frozen 'g, node: Node 'g) throws (Error) { |
| 117 | if graph.owner <> node.owner { |
| 118 | throw Error::ForeignNode; |
| 119 | } |
| 120 | assert *node.initialized; |
| 121 | } |
| 122 | |
| 123 | /// Return the number of nodes in a frozen graph. |
| 124 | export fn len 'g (graph: &Frozen 'g) -> u32 { |
| 125 | return graph.count; |
| 126 | } |