Skip to content

GraphGetNode

Description

Get a traversal handle for a live node id.

Parameters

Name Direction Description
g in,out Graph owning the node.
node_id in Live node id to wrap as a GraphNode.

Success

Returns a GraphNode handle that resolves back to the same slot and generation. The graph is not modified. The handle is valid until the slot is committed for deletion.

Failure

Does not return - aborts via LOG_FATAL for an invalid or stale node id (caller bug).

Usage example (Cross-references)

Usage examples (Cross-references)
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
    
        bool result = !GraphNodeVisited(node) && (GraphNodeVisitCount(node) == 0);
        result = result && (GraphNodeIdIndex(d) == GraphNodeIdIndex(b));
        result = result && (GraphNodeIdGeneration(d) == (GraphNodeIdGeneration(b) + 1));
        result = result && (GraphNodeData(&graph, GraphGetNode(&graph, d)) == 40);
        result = result && !GraphNodeVisited(GraphGetNode(&graph, d));
        result = result && (GraphNodeIdGeneration(d) == (GraphNodeIdGeneration(b) + 1));
        result = result && (GraphNodeData(&graph, GraphGetNode(&graph, d)) == 40);
        result = result && !GraphNodeVisited(GraphGetNode(&graph, d));
    
        GraphDeinit(&graph);
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
    
        bool result = !GraphNodeMarkedForDeletion(node);
    
        bool result = GraphMarkEdgeForRemoval(&graph, a, b);
        result      = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && GraphEdgeMarkedForRemoval(&graph, a, b);
        result      = result && GraphNodeMarkedForDeletion(GraphGetNode(&graph, b));
        result      = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && GraphEdgeMarkedForRemoval(&graph, a, b);
        result      = result && GraphNodeMarkedForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 2);
        result      = result && !GraphContainsNode(&graph, b);
        counts[GraphNodeIdIndex(b)] = 29;
    
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
        result      = result && !GraphContainsNode(&graph, b);
    
        GraphNodeId a    = GraphAddNodeR(&graph, 10);
        GraphNode   node = GraphGetNode(&graph, a);
    
        (void)GraphMarkNodeForDeletion(node);
        GraphAddEdge(&graph, a, b);
    
        GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
    
        u64 committed = GraphCommitChanges(&graph);
    
        // Delete b so its slot is free at the time GraphClear walks it.
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
        result      = result && !GraphContainsNode(&graph, b);
    
        // One deletion leaves a single entry in free_indices going into clear.
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
    
        // First commit frees b's slot (free-list entry, generation bumped).
        bool result  = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        u64  removed = GraphCommitChanges(&graph);
        result       = result && (removed == 1);
        // Second commit: a free slot (b's) is present. The marked/unmarked passes
        // iterate every slot; the free slot must be skipped, not validated.
        result  = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        removed = GraphCommitChanges(&graph);
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot, a is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse b's slot, b is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot, a is now stale
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse b's slot, b is now stale
        // Mark a (index 0, in range after the shrink) so the commit has a node to
        // delete (pending_delete_count > 0, so it does not early-return).
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
    
        // Clear the memoized validated bit on a consistent graph so commit's entry
        // Mark both a (in range after the shrink) and c (the slot we hide at
        // index == len). pending_delete_count = 2 so commit does real work.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        // index == len). pending_delete_count = 2 so commit does real work.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
    
        // Clear the memoized validated bit on a consistent graph.
        result      = result && !GraphContainsNode(&graph, a);
        result      = result && GraphContainsNode(&graph, c);
        result      = result && GraphNodeMarkedForDeletion(GraphGetNode(&graph, c));
    
        GraphDeinit(&graph);
        result = result && GraphAddEdge(&graph, third_id, first_id);
        result = result && GraphAddEdge(&graph, third_id, third_id);
        result = result && (GraphNodeVisit(GraphGetNode(&graph, first_id)) == 1);
        result = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, second_id));
        result = result && GraphMarkEdgeForRemoval(&graph, third_id, third_id);
        result = result && GraphAddEdge(&graph, third_id, third_id);
        result = result && (GraphNodeVisit(GraphGetNode(&graph, first_id)) == 1);
        result = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, second_id));
        result = result && GraphMarkEdgeForRemoval(&graph, third_id, third_id);
    
        node_id     = GraphAddNodeL(&graph, name);
        node        = GraphGetNode(&graph, node_id);
        stored_name = GraphNodeDataPtr(&graph, node);
    
        node_id     = GraphAddNodeR(&graph, StrZ("alpha", &alloc));
        node        = GraphGetNode(&graph, node_id);
        stored_name = GraphNodeDataPtr(&graph, node);
        (void)a;
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)a;
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
    
        // Delete + commit b so its slot lands on the free list (reuse target).
        bool result = GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        result      = result && (GraphCommitChanges(&graph) == 1);
    
        GraphNodeId node_id = GraphAddNodeR(&graph, 11);
        GraphNode   node    = GraphGetNode(&graph, node_id);
    
        bool result = GraphAllocator(&graph)->alignment == 32 && GraphNodeIdIndex(node_id) == 0 &&
        (void)a;
    
        GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
    
        // A structural mutation that keeps b marked but re-arms the deep
        GraphNodeId b = GraphAddNodeR(&graph, 20);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        (void)a; // a keeps slot 0 occupied; only b is deleted to seed free_indices.
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        GraphNodeId c = GraphAddNodeR(&graph, 30);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        (void)GraphCommitChanges(&graph);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, c));
        (void)GraphCommitChanges(&graph);
    
        city_reset_visits(graph);
        return city_reachable_from(GraphGetNode(graph, *from_id), *to_id);
    }
        u64 predecessor_count = 0;
    
        GraphNodeForeachPredecessor(GraphGetNode(&graph, d), predecessor) {
            predecessor_sum   += GraphNodeData(&graph, predecessor);
            predecessor_count += 1;
        GraphAddEdge(&graph, a, b);
    
        GraphNodeForeachNeighbor(GraphGetNode(&graph, a), neighbor) {
            (void)neighbor;
            (void)GraphAddEdge(&graph, a, c);
        GraphAddEdge(&graph, b, c);
    
        GraphNodeForeachPredecessor(GraphGetNode(&graph, c), predecessor) {
            (void)predecessor;
            (void)GraphAddEdge(&graph, d, c);
        // Delete the middle node so its slot becomes a free hole between two
        // live slots, then commit so the deletion materializes.
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
        (void)GraphCommitChanges(&graph);
        // Begin runs the deep validator (clears the validated bit) and snapshots
        // the current epoch.
        GenericGraphNeighborIter iter = graph_neighbor_iter_begin(GraphGetNode(&graph, a));
    
        // Intentional bypass: there is no public mutator for an individual
        GraphAddEdge(&graph, a, b);
    
        GenericGraphPredecessorIter iter = graph_predecessor_iter_begin(GraphGetNode(&graph, b));
    
        // Intentional bypass: rewrite `b`'s sole in-neighbor (a) so its generation
        // Capture a handle, then delete + commit + reuse a's slot so the captured
        // id is stale (slot occupied, generation superseded).
        GraphNode stale = GraphGetNode(&graph, a);
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        // id is stale (slot occupied, generation superseded).
        GraphNode stale = GraphGetNode(&graph, a);
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        (void)GraphAddNodeR(&graph, 99); // reuse a's slot at a higher generation
            // Mark + commit a deletion mid-iteration. The commit bumps the
            // mutation epoch, so the iterator's next step must abort.
            (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, b));
            (void)GraphCommitChanges(&graph);
        }
        ValidateGraph(&graph);
    
        node_b                            = GraphGetNode(&graph, b);
        *GraphNodeDataPtr(&graph, node_b) = 25;
        IntGraph  graph_a = GraphInit(&alloc);
        IntGraph  graph_b = GraphInit(&alloc);
        GraphNode node    = GraphGetNode(&graph_a, GraphAddNodeR(&graph_a, 10));
    
        (void)GraphNodeData(&graph_b, node);
        // Delete + commit: slot 0 becomes free and its generation is bumped to
        // GraphNodeIdGeneration(a) + 1.
        result = result && GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        result = result && (GraphCommitChanges(&graph) == 1);
        result = result && !GraphContainsNode(&graph, a);
        graph.slots.length -= 1;
    
        (void)GraphGetNode(&graph, b);
    
        GraphDeinit(&graph);
    // without aborting. Real code aborts on the stale id.
    static bool test_get_node_rejects_stale_id_deadend(void) {
        WriteFmt("Testing GraphGetNode rejects a stale node id (should abort)\n");
    
        DefaultAllocator alloc = DefaultAllocatorInit();
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
    
        // a now refers to a freed slot with a stale generation.
        (void)GraphGetNode(&graph, a);
    
        GraphDeinit(&graph);
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        // Reuse a's slot at a higher generation; `a` is now stale.
        GraphNodeId a = GraphAddNodeR(&graph, 10);
    
        (void)GraphMarkNodeForDeletion(GraphGetNode(&graph, a));
        (void)GraphCommitChanges(&graph);
        GraphNodeId free_match = make_raw_node_id(GraphNodeIdIndex(a), GraphNodeIdGeneration(a) + 1);
    
        (void)GraphGetNode(&graph, free_match);
    
        GraphDeinit(&graph);
Last updated on