Skip to content

Graph

Description

Typesafe directed graph definition.

Node payloads are owned by the graph. Each live node occupies one internal slot and is referred to by a stable generation/index GraphNodeId. Edges are directed and tracked incrementally in both outgoing and incoming adjacency lists of those ids.

This container is designed for analysis-style workloads such as reachability, control-flow, dependency, and graph-rewrite passes where cheap node lookup, explicit traversal, and deferred destructive mutation matter more than object-oriented node wrappers.
Like the other generic containers in this project, each Graph(T) expansion creates a distinct anonymous type. Prefer a typedef when you need to reuse the same graph type across APIs.

Fields

Name Description
slots Internal slot storage for live and reusable nodes.
free_indices Reusable slot indices populated by deletion/clear.
pending_edge_removals Directed edges marked for removal on next commit.
copy_init Optional deep-copy callback for node payloads.
copy_deinit Optional deinit callback for node payloads.
live_count Number of currently live nodes.
edge_count Number of directed edges currently stored.
pending_delete_count Number of nodes marked for deletion but not yet committed.
mutation_epoch Structural mutation counter used by traversal helpers.
alignment Alignment used for graph-owned node payload allocations.
type_anchor Type anchor for generic node payload macros.

Usage example (from documentation)

  typedef Graph(int) IntGraph;
  IntGraph graph = GraphInit();

Usage example (Cross-references)

Usage examples (Cross-references)
    /// Generic directed graph implementation
    
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Log.h>
    #include <Misra/Sys.h>
    
        if ((graph->alignment > 1) && !graph_alignment_is_pow2(graph->alignment)) {
            LOG_FATAL("Graph alignment must be 1 or a power of two");
        }
    }
    static void graph_validate_slot_limit(const GenericGraph *graph) {
        if (graph->slots.length > (u64)UINT32_MAX) {
            LOG_FATAL("Graph exceeded maximum supported slot count");
        }
    }
        in_idx       = graph_find_neighbor_index(in_neighbors, from);
        if (in_idx == SIZE_MAX) {
            LOG_FATAL("Graph reverse adjacency is inconsistent during edge removal");
        }
            } else {
                if (!graph_remove_edge_now(graph, from, neighbor_id)) {
                    LOG_FATAL("Graph failed to remove marked outgoing edge");
                }
                removed += 1;
            GraphNodeId to = VecAt(neighbors, neighbors->length - 1);
            if (!graph_remove_edge_now(graph, from, to)) {
                LOG_FATAL("Graph failed to remove outgoing edge during node deletion");
            }
            removed += 1;
    
        if (!graph) {
            LOG_FATAL("Expected a valid Graph pointer");
        }
    
        if (graph->__magic != MISRA_GRAPH_MAGIC) {
            LOG_FATAL("Graph is uninitialized or corrupted");
        }
    
        if (graph->live_count > graph->slots.length) {
            LOG_FATAL("Graph live node count exceeds slot count");
        }
    
        if (graph->pending_delete_count > graph->live_count) {
            LOG_FATAL("Graph pending delete count exceeds live node count");
        }
    
        if ((graph->live_count + graph->free_indices.length) != graph->slots.length) {
            LOG_FATAL("Graph slot accounting is inconsistent");
        }
                    target_slot = graph_require_live_slot_const(graph, neighbor_id);
                    if (!graph_neighbors_contains(&target_slot->in_neighbors, self_id)) {
                        LOG_FATAL("Graph reverse adjacency is missing predecessor entry");
                    }
                }
                    source_slot = graph_require_live_slot_const(graph, predecessor_id);
                    if (!graph_neighbors_contains(&source_slot->out_neighbors, self_id)) {
                        LOG_FATAL("Graph reverse adjacency is missing outgoing edge");
                    }
                }
            u32 index = VecAt(&graph->free_indices, free_index_i);
            if ((u64)index >= graph->slots.length) {
                LOG_FATAL("Graph free slot index out of bounds");
            }
    
            if (graph_slot_is_occupied(VecPtrAt((GraphSlots *)&graph->slots, index))) {
                LOG_FATAL("Graph free index points to an occupied slot");
            }
        }
    
            if (!graph_neighbors_contains(neighbors, pending->to)) {
                LOG_FATAL("Graph pending edge removal refers to a missing edge");
            }
        }
    
        if (graph->live_count != live_count) {
            LOG_FATAL("Graph live node count is inconsistent");
        }
    
        if ((graph->edge_count != out_edge_count) || (graph->edge_count != in_edge_count)) {
            LOG_FATAL("Graph edge count is inconsistent");
        }
    
        if (graph->pending_delete_count != marked_count) {
            LOG_FATAL("Graph pending delete count is inconsistent");
        }
    }
            if (graph_slot_is_occupied(slot) && graph_slot_is_marked(slot)) {
                if (slot->out_neighbors.length || slot->in_neighbors.length) {
                    LOG_FATAL("Graph marked node retained incident edges before release");
                }
                graph_release_slot(graph, slot, item_size);
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Container/Map.h>
    #include <Misra/Std/Container/Str.h>
    }
    
    typedef Graph(Str)                 CityGraph;
    typedef Map(const char *, GraphNodeId) CityIndex;
        WriteFmt("Testing nested foreach with external count tracking map\n");
    
        typedef Graph(int) IntGraph;
        typedef Map(GraphNodeId, u64) CountMap;
        WriteFmt("Testing GraphNodeForeachPredecessor\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphForeachNode rejects structural mutation (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphNodeForeachNeighbor rejects structural mutation (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphNodeForeachPredecessor rejects structural mutation (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        };
    
        WriteFmt("[INFO] Starting Graph.Foreach tests\n\n");
        return run_test_suite(
            tests,
            deadend_tests,
            (int)(sizeof(deadend_tests) / sizeof(deadend_tests[0])),
            "Graph.Foreach"
        );
    }
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Log.h>
    
    static bool test_graph_node_visit_scratch_state(void) {
        WriteFmt("Testing Graph node scratch visit state\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph node scratch visit state\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphMarkNodeForDeletion and GraphCommitChanges\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
    
    static bool test_graph_query_and_unmark_node_deletion(void) {
        WriteFmt("Testing Graph node mark query and unmark\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph node mark query and unmark\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphMarkEdgeForRemoval and deferred edge commit\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
    
    static bool test_graph_query_and_unmark_edge_removal(void) {
        WriteFmt("Testing Graph edge mark query and unmark\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph edge mark query and unmark\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing partial unmark of multiple pending edge removals\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing deferred removal of self-loop edge\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing overlap between pending edge removal and node deletion\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing external slot-indexed state across delete and reuse\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing stale GraphNode handle after commit (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        };
    
        WriteFmt("[INFO] Starting Graph.Ops tests\n\n");
        return run_test_suite(
            tests,
            deadend_tests,
            (int)(sizeof(deadend_tests) / sizeof(deadend_tests[0])),
            "Graph.Ops"
        );
    }
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Container/Str.h>
    #include <Misra/Std/Log.h>
        WriteFmt("Testing GraphReserve and GraphClear\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
    
    static bool test_graph_node_deep_copy(void) {
        WriteFmt("Testing Graph node deep-copy\n");
    
        typedef Graph(Str) StrGraph;
        WriteFmt("Testing Graph node deep-copy\n");
    
        typedef Graph(Str) StrGraph;
        StrGraph   graph = GraphInitWithDeepCopy(StrInitCopy, StrDeinit);
        Str        name  = StrInitFromZstr("alpha");
        };
    
        WriteFmt("[INFO] Starting Graph.Init tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Init");
    }
    
        WriteFmt("[INFO] Starting Graph.Init tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Init");
    }
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Log.h>
    
    static bool test_graph_type_defaults(void) {
        WriteFmt("Testing Graph defaults\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph defaults\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
    
    static bool test_graph_aligned_init_and_id_layout(void) {
        WriteFmt("Testing Graph aligned init and node id layout\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph aligned init and node id layout\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInitAligned(32);
        };
    
        WriteFmt("[INFO] Starting Graph.Type tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Type");
    }
    
        WriteFmt("[INFO] Starting Graph.Type tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Type");
    }
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Log.h>
    
    static bool test_graph_access_helpers(void) {
        WriteFmt("Testing Graph access helpers\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph access helpers\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphHasEdge\n");
    
        typedef Graph(const char *) ZstrGraph;
        ZstrGraph graph = GraphInit();
        WriteFmt("Testing GraphNodeData rejects foreign graph node handles (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph_a = GraphInit();
        IntGraph graph_b = GraphInit();
        WriteFmt("Testing GraphPredecessorAt out-of-bounds access (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        WriteFmt("Testing GraphNeighborAt out-of-bounds access (should abort)\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        };
    
        WriteFmt("[INFO] Starting Graph.Access tests\n\n");
        return run_test_suite(
            tests,
            deadend_tests,
            (int)(sizeof(deadend_tests) / sizeof(deadend_tests[0])),
            "Graph.Access"
        );
    }
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Log.h>
        WriteFmt("Testing GraphAddNode semantics\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph  = GraphInit();
        int      owned  = 42;
        WriteFmt("Testing GraphAddEdge deduplication\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
    
    static bool test_graph_self_loop_and_predecessor_order(void) {
        WriteFmt("Testing Graph self-loop handling and predecessor order\n");
    
        typedef Graph(int) IntGraph;
        WriteFmt("Testing Graph self-loop handling and predecessor order\n");
    
        typedef Graph(int) IntGraph;
        IntGraph graph = GraphInit();
        };
    
        WriteFmt("[INFO] Starting Graph.Insert tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Insert");
    }
    
        WriteFmt("[INFO] Starting Graph.Insert tests\n\n");
        return run_test_suite(tests, (int)(sizeof(tests) / sizeof(tests[0])), NULL, 0, "Graph.Insert");
    }
    #include <Misra/Std/Container/Str.h>
    #include <Misra/Std/Container/Vec.h>
    #include <Misra/Std/Container/Graph.h>
    #include <Misra/Std/Container/List.h>
    #include <Misra/Std/Container/Map.h>
    
    // clang-format off
    #include "Graph/Type.h"
    #include "Graph/Init.h"
    #include "Graph/Insert.h"
    // clang-format off
    #include "Graph/Type.h"
    #include "Graph/Init.h"
    #include "Graph/Insert.h"
    #include "Graph/Access.h"
    #include "Graph/Type.h"
    #include "Graph/Init.h"
    #include "Graph/Insert.h"
    #include "Graph/Access.h"
    #include "Graph/Foreach.h"
    #include "Graph/Init.h"
    #include "Graph/Insert.h"
    #include "Graph/Access.h"
    #include "Graph/Foreach.h"
    #include "Graph/Ops.h"
    #include "Graph/Insert.h"
    #include "Graph/Access.h"
    #include "Graph/Foreach.h"
    #include "Graph/Ops.h"
    #include "Graph/Memory.h"
    #include "Graph/Access.h"
    #include "Graph/Foreach.h"
    #include "Graph/Ops.h"
    #include "Graph/Memory.h"
    #include "Graph/Private.h"
    #include "Graph/Foreach.h"
    #include "Graph/Ops.h"
    #include "Graph/Memory.h"
    #include "Graph/Private.h"
    // clang-format on
    #include "Graph/Ops.h"
    #include "Graph/Memory.h"
    #include "Graph/Private.h"
    // clang-format on
Last updated on