Add node manager which owns graph
This commit is contained in:
+298
-230
@@ -46,7 +46,7 @@ wp_intern void prPoolFree(PrPool *pool, void *slot) {
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Graph
|
||||
* Shared types
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
#define INVALID_NODE_INDEX (u64)-1
|
||||
@@ -80,80 +80,271 @@ typedef struct {
|
||||
typedef PrNode *PrNodeArray;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Graph Edge/Vertex type
|
||||
* If source or target are INVALID_NODE_ID, it should be treated as a vertex.
|
||||
* Otherwise, it should be treated as an edge. Think of it like homogeneous
|
||||
* coordinates where a vector {0, 0, 0, 0} is treated as a direction, while
|
||||
* a vector {0, 0, 0, 1} is treated as a point
|
||||
* ---------------------------------------------------------------------------*/
|
||||
typedef struct PrEdgeVertex PrEdgeVertex;
|
||||
struct PrEdgeVertex {
|
||||
PrEdgeVertex *next_forward;
|
||||
PrEdgeVertex *next_backward;
|
||||
* Graph edge type
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
typedef struct PrGraphEdge PrGraphEdge;
|
||||
struct PrGraphEdge {
|
||||
PrGraphEdge *next_forward;
|
||||
PrGraphEdge *next_backward;
|
||||
PrNodeId source;
|
||||
PrNodeId target;
|
||||
};
|
||||
typedef PrEdgeVertex *PrEdgeVertexArray;
|
||||
typedef PrGraphEdge *PrGraphEdgeArray;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Graph node — compact adjacency head (two pointers, no dead fields)
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
typedef struct PrGraphNode PrGraphNode;
|
||||
struct PrGraphNode {
|
||||
PrGraphEdge *next_forward;
|
||||
PrGraphEdge *next_backward;
|
||||
};
|
||||
typedef PrGraphNode *PrGraphNodeArray;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* PrGraph — owns only topology (edges + adjacency heads)
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
PrPool edge_pool;
|
||||
PrGraphNodeArray graph_nodes;
|
||||
u64 capacity;
|
||||
} PrGraph;
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* PrNodeManager — owns compositor node data + handle lifecycle + topology
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
PrPool vertex_pool;
|
||||
PrNodeArray nodes;
|
||||
PrEdgeVertexArray vertices;
|
||||
PrGraph graph;
|
||||
u64 capacity;
|
||||
u64 max_count_ever;
|
||||
u64 count;
|
||||
u64 free_head;
|
||||
} PrGraph;
|
||||
} PrNodeManager;
|
||||
|
||||
wp_intern b8 prGraphIsStaleId(const PrGraph *graph, PrNodeId id) {
|
||||
u64 generation = graph->nodes[id.index].generation;
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Function declarations (cross-referencing both types)
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
wp_intern void prNodeManagerInit(PrNodeManager *mgr, WpAllocator *allocator, u64 capacity);
|
||||
wp_intern b8 prNodeManagerIsStaleNode(const PrNodeManager *mgr, PrNodeId id);
|
||||
wp_intern b8 prNodeManagerIsActiveNode(const PrNodeManager *mgr, PrNodeId id);
|
||||
wp_intern PrNodeId prNodeManagerGetNode(const PrNodeManager *mgr, u64 index);
|
||||
wp_intern PrNodeId prNodeManagerAddNode(PrNodeManager *mgr, PrNodeType type);
|
||||
wp_intern void prNodeManagerRemoveNode(PrNodeManager *mgr, PrNodeId id);
|
||||
wp_intern void prNodeManagerAddEdge(PrNodeManager *mgr, PrNodeId from, PrNodeId to);
|
||||
|
||||
wp_intern void prGraphInit(PrGraph *graph, WpAllocator *allocator, u64 capacity);
|
||||
wp_intern void prGraphAddEdge(PrNodeManager *mgr, PrGraph *graph, u64 from_idx, u64 to_idx);
|
||||
wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx);
|
||||
wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const PrGraph *graph,
|
||||
const WpAllocator *allocator);
|
||||
wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph);
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* PrNodeManager implementation
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
wp_intern void prNodeManagerInit(PrNodeManager *mgr, WpAllocator *allocator, u64 capacity) {
|
||||
mgr->nodes = wpArrayAllocCapacity(PrNode, allocator, capacity, WP_ARRAY_INIT_FILLED);
|
||||
mgr->free_head = 0;
|
||||
mgr->capacity = capacity;
|
||||
mgr->max_count_ever = 0;
|
||||
mgr->count = 0;
|
||||
|
||||
if (!mgr->nodes) {
|
||||
mgr->capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(mgr->nodes, 0, capacity * sizeof(PrNode));
|
||||
|
||||
for (u64 i = 0; i < capacity; ++i) {
|
||||
mgr->nodes[i].next_free = i < capacity - 1 ? i + 1 : INVALID_NODE_INDEX;
|
||||
}
|
||||
|
||||
prGraphInit(&mgr->graph, allocator, capacity);
|
||||
}
|
||||
|
||||
wp_intern b8 prNodeManagerIsStaleNode(const PrNodeManager *mgr, PrNodeId id) {
|
||||
u64 generation = mgr->nodes[id.index].generation;
|
||||
return id.generation != generation;
|
||||
}
|
||||
|
||||
wp_intern b8 prGraphIsActiveNode(const PrGraph *graph, PrNodeId id) {
|
||||
u64 next_free = graph->nodes[id.index].next_free;
|
||||
return !prGraphIsStaleId(graph, id) && next_free == INVALID_NODE_INDEX;
|
||||
wp_intern b8 prNodeManagerIsActiveNode(const PrNodeManager *mgr, PrNodeId id) {
|
||||
u64 next_free = mgr->nodes[id.index].next_free;
|
||||
return !prNodeManagerIsStaleNode(mgr, id) && next_free == INVALID_NODE_INDEX;
|
||||
}
|
||||
|
||||
wp_intern PrNodeId prGraphGetNode(const PrGraph *graph, u64 index) {
|
||||
return (PrNodeId){ .index = index, .generation = graph->nodes[index].generation };
|
||||
wp_intern PrNodeId prNodeManagerGetNode(const PrNodeManager *mgr, u64 index) {
|
||||
return (PrNodeId){ .index = index, .generation = mgr->nodes[index].generation };
|
||||
}
|
||||
|
||||
wp_intern PrNodeId prNodeManagerAddNode(PrNodeManager *mgr, PrNodeType type) {
|
||||
u64 idx = mgr->free_head;
|
||||
if (idx == INVALID_NODE_INDEX) { return INVALID_NODE_ID; }
|
||||
|
||||
PrNode *node = &mgr->nodes[idx];
|
||||
|
||||
mgr->free_head = node->next_free;
|
||||
node->next_free = INVALID_NODE_INDEX;
|
||||
node->type = type;
|
||||
memset(&node->params, 0, sizeof(node->params));
|
||||
|
||||
mgr->count++;
|
||||
|
||||
if (idx + 1 > mgr->max_count_ever) { mgr->max_count_ever = idx + 1; }
|
||||
|
||||
return (PrNodeId){ .index = idx, .generation = node->generation };
|
||||
}
|
||||
|
||||
wp_intern void prNodeManagerRemoveNode(PrNodeManager *mgr, PrNodeId id) {
|
||||
if (!prNodeManagerIsActiveNode(mgr, id)) { return; }
|
||||
|
||||
/* Tear down all edges incident to this node */
|
||||
prGraphRemoveEdges(&mgr->graph, id.index);
|
||||
|
||||
/* Return node slot to free list with bumped generation */
|
||||
PrNode *node = &mgr->nodes[id.index];
|
||||
node->generation++;
|
||||
node->next_free = mgr->free_head;
|
||||
mgr->free_head = id.index;
|
||||
mgr->count--;
|
||||
}
|
||||
|
||||
wp_intern void prNodeManagerAddEdge(PrNodeManager *mgr, PrNodeId from, PrNodeId to) {
|
||||
if (!prNodeManagerIsActiveNode(mgr, from) || !prNodeManagerIsActiveNode(mgr, to)) { return; }
|
||||
if (from.index == to.index) { return; }
|
||||
prGraphAddEdge(mgr, &mgr->graph, from.index, to.index);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* PrGraph implementation
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
wp_intern void prGraphInit(PrGraph *graph, WpAllocator *allocator, u64 capacity) {
|
||||
graph->nodes = wpArrayAllocCapacity(PrNode, allocator, capacity, WP_ARRAY_INIT_FILLED);
|
||||
graph->vertices = wpArrayAllocCapacity(PrEdgeVertex, allocator, capacity, WP_ARRAY_INIT_FILLED);
|
||||
graph->free_head = 0;
|
||||
graph->graph_nodes = wpArrayAllocCapacity(PrGraphNode, allocator, capacity, WP_ARRAY_INIT_FILLED);
|
||||
graph->capacity = capacity;
|
||||
graph->max_count_ever = 0;
|
||||
graph->count = 0;
|
||||
|
||||
if (!graph->nodes || !graph->vertices) {
|
||||
if (!graph->graph_nodes) {
|
||||
graph->capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
prPoolInit(&graph->vertex_pool, allocator, sizeof(PrEdgeVertex));
|
||||
memset(graph->nodes, 0, capacity * sizeof(PrNode));
|
||||
memset(graph->vertices, 0, capacity * sizeof(PrEdgeVertex));
|
||||
|
||||
for (u64 i = 0; i < capacity; ++i) {
|
||||
graph->nodes[i].next_free = i < capacity - 1 ? i + 1 : INVALID_NODE_INDEX;
|
||||
graph->vertices[i].source = graph->vertices[i].target = INVALID_NODE_ID;
|
||||
prPoolInit(&graph->edge_pool, allocator, sizeof(PrGraphEdge));
|
||||
memset(graph->graph_nodes, 0, capacity * sizeof(PrGraphNode));
|
||||
}
|
||||
|
||||
/* --- internal: unlink edge helpers (raw indices, caller guarantees validity) -- */
|
||||
|
||||
wp_intern void _unlinkForward(PrGraph *graph, u64 from_idx, PrGraphEdge *edge) {
|
||||
PrGraphNode *gnode = &graph->graph_nodes[from_idx];
|
||||
PrGraphEdge *curr = gnode->next_forward;
|
||||
PrGraphEdge *prev = NULL;
|
||||
while (curr) {
|
||||
if (curr == edge) {
|
||||
if (prev) {
|
||||
prev->next_forward = curr->next_forward;
|
||||
} else {
|
||||
gnode->next_forward = curr->next_forward;
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next_forward;
|
||||
}
|
||||
}
|
||||
|
||||
wp_intern void _unlinkBackward(PrGraph *graph, u64 to_idx, PrGraphEdge *edge) {
|
||||
PrGraphNode *gnode = &graph->graph_nodes[to_idx];
|
||||
PrGraphEdge *curr = gnode->next_backward;
|
||||
PrGraphEdge *prev = NULL;
|
||||
while (curr) {
|
||||
if (curr == edge) {
|
||||
if (prev) {
|
||||
prev->next_backward = curr->next_backward;
|
||||
} else {
|
||||
gnode->next_backward = curr->next_backward;
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next_backward;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- edge management -------------------------------------------------- */
|
||||
|
||||
wp_intern void prGraphAddEdge(PrNodeManager *mgr, PrGraph *graph, u64 from_idx, u64 to_idx) {
|
||||
PrGraphEdge *edge = (PrGraphEdge *)prPoolAlloc(&graph->edge_pool);
|
||||
if (!edge) { return; }
|
||||
|
||||
PrNodeId from_id = prNodeManagerGetNode(mgr, from_idx);
|
||||
PrNodeId to_id = prNodeManagerGetNode(mgr, to_idx);
|
||||
|
||||
edge->source = from_id;
|
||||
edge->target = to_id;
|
||||
|
||||
/* Link into adjacency chains */
|
||||
PrGraphNode *src = &graph->graph_nodes[from_idx];
|
||||
PrGraphNode *dst = &graph->graph_nodes[to_idx];
|
||||
edge->next_forward = src->next_forward;
|
||||
edge->next_backward = dst->next_backward;
|
||||
src->next_forward = edge;
|
||||
dst->next_backward = edge;
|
||||
|
||||
/* Check whether the new edge created a cycle */
|
||||
WpAllocator scratch = wpMemArenaAllocatorInitZero(KiB(16));
|
||||
PrNodeIdArray sorted = prGraphTopologicalSort(mgr, graph, &scratch);
|
||||
u64 sorted_cnt = sorted ? wpArrayCount(sorted) : 0;
|
||||
if (sorted_cnt < mgr->count) {
|
||||
_unlinkForward(graph, from_idx, edge);
|
||||
_unlinkBackward(graph, to_idx, edge);
|
||||
prPoolFree(&graph->edge_pool, edge);
|
||||
}
|
||||
}
|
||||
|
||||
wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx) {
|
||||
PrGraphNode *gnode = &graph->graph_nodes[idx];
|
||||
|
||||
/* Free outgoing edges: unlink from each target's backward list */
|
||||
PrGraphEdge *curr = gnode->next_forward;
|
||||
while (curr) {
|
||||
PrGraphEdge *next = curr->next_forward;
|
||||
_unlinkBackward(graph, curr->target.index, curr);
|
||||
prPoolFree(&graph->edge_pool, curr);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
/* Free incoming edges: unlink from each source's forward list */
|
||||
curr = gnode->next_backward;
|
||||
while (curr) {
|
||||
PrGraphEdge *next = curr->next_backward;
|
||||
_unlinkForward(graph, curr->source.index, curr);
|
||||
prPoolFree(&graph->edge_pool, curr);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
gnode->next_forward = gnode->next_backward = NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Kahn's algorithm — topological sort / cycle detection
|
||||
*
|
||||
* Returns a WpArray of PrNodeId (sorted topologically). If the result count
|
||||
* is less than graph->count, the graph contains a cycle.
|
||||
* is less than mgr->count, the graph contains a cycle.
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
wp_intern PrNodeId *prGraphTopologicalSort(PrGraph *graph, const WpAllocator *allocator) {
|
||||
if (graph->count == 0) { return NULL; }
|
||||
if (!graph->nodes || graph->capacity == 0) { return NULL; }
|
||||
wp_intern PrNodeIdArray prGraphTopologicalSort(const PrNodeManager *mgr, const PrGraph *graph,
|
||||
const WpAllocator *allocator) {
|
||||
if (mgr->count == 0) { return NULL; }
|
||||
if (!mgr->nodes || graph->capacity == 0) { return NULL; }
|
||||
|
||||
PrNodeIdArray result = wpArrayAllocCapacity(PrNodeId, allocator, graph->count, WP_ARRAY_INIT_NONE);
|
||||
PrNodeIdArray result = wpArrayAllocCapacity(PrNodeId, allocator, mgr->count, WP_ARRAY_INIT_NONE);
|
||||
if (!result) { return NULL; }
|
||||
|
||||
WpAllocator local_arena = wpMemArenaAllocatorInitZero(KiB(16));
|
||||
@@ -162,23 +353,23 @@ wp_intern PrNodeId *prGraphTopologicalSort(PrGraph *graph, const WpAllocator *al
|
||||
if (!in_degree) { return result; }
|
||||
memset(in_degree, 0, wpArrayCapacity(in_degree) * sizeof(u64));
|
||||
|
||||
for (u64 i = 0; i < graph->max_count_ever; i++) {
|
||||
PrNodeId id = prGraphGetNode(graph, i);
|
||||
if (!prGraphIsActiveNode(graph, id)) { continue; }
|
||||
for (u64 i = 0; i < mgr->max_count_ever; i++) {
|
||||
PrNodeId id = prNodeManagerGetNode(mgr, i);
|
||||
if (!prNodeManagerIsActiveNode(mgr, id)) { continue; }
|
||||
|
||||
PrEdgeVertex *vertex = &graph->vertices[i];
|
||||
PrEdgeVertex *curr = vertex->next_forward;
|
||||
PrGraphNode *gnode = &graph->graph_nodes[i];
|
||||
PrGraphEdge *curr = gnode->next_forward;
|
||||
while (curr) {
|
||||
in_degree[curr->target.index]++;
|
||||
curr = curr->next_forward;
|
||||
}
|
||||
}
|
||||
|
||||
WpQueue queue = wpQueueAlloc(u64, &local_arena, graph->count);
|
||||
WpQueue queue = wpQueueAlloc(u64, &local_arena, mgr->count);
|
||||
|
||||
for (u64 i = 0; i < graph->max_count_ever; i++) {
|
||||
PrNodeId id = prGraphGetNode(graph, i);
|
||||
if (!prGraphIsActiveNode(graph, id)) { continue; }
|
||||
for (u64 i = 0; i < mgr->max_count_ever; i++) {
|
||||
PrNodeId id = prNodeManagerGetNode(mgr, i);
|
||||
if (!prNodeManagerIsActiveNode(mgr, id)) { continue; }
|
||||
if (in_degree[i] == 0) {
|
||||
wpQueuePush(u64, &queue, &i);
|
||||
}
|
||||
@@ -188,11 +379,11 @@ wp_intern PrNodeId *prGraphTopologicalSort(PrGraph *graph, const WpAllocator *al
|
||||
u64 *node_idx = wpQueuePop(u64, &queue);
|
||||
if (!node_idx) { break; }
|
||||
|
||||
PrNodeId id = prGraphGetNode(graph, *node_idx);
|
||||
PrNodeId id = prNodeManagerGetNode(mgr, *node_idx);
|
||||
wpArrayAppendCapped(PrNodeId, result, &id);
|
||||
|
||||
PrEdgeVertex *vertex = &graph->vertices[*node_idx];
|
||||
PrEdgeVertex *curr = vertex->next_forward;
|
||||
PrGraphNode *gnode = &graph->graph_nodes[*node_idx];
|
||||
PrGraphEdge *curr = gnode->next_forward;
|
||||
while (curr) {
|
||||
u64 target_idx = curr->target.index;
|
||||
if (in_degree[target_idx] > 0) {
|
||||
@@ -208,151 +399,24 @@ wp_intern PrNodeId *prGraphTopologicalSort(PrGraph *graph, const WpAllocator *al
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --- edge management -------------------------------------------------- */
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Dump
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
wp_intern void _unlinkForward(PrGraph *graph, PrNodeId from, PrEdgeVertex *edge) {
|
||||
if (!prGraphIsActiveNode(graph, from)) { return; }
|
||||
|
||||
PrEdgeVertex *src = &graph->vertices[from.index];
|
||||
PrEdgeVertex *curr = src->next_forward;
|
||||
PrEdgeVertex *prev = NULL;
|
||||
while (curr) {
|
||||
if (curr == edge) {
|
||||
if (prev) {
|
||||
prev->next_forward = curr->next_forward;
|
||||
} else {
|
||||
src->next_forward = curr->next_forward;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
prev = curr;
|
||||
curr = curr->next_forward;
|
||||
}
|
||||
}
|
||||
|
||||
wp_intern void _unlinkBackward(PrGraph *graph, PrNodeId to, PrEdgeVertex *edge) {
|
||||
if (!prGraphIsActiveNode(graph, to)) { return; }
|
||||
|
||||
PrEdgeVertex *dst = &graph->vertices[to.index];
|
||||
PrEdgeVertex *curr = dst->next_backward;
|
||||
PrEdgeVertex *prev = NULL;
|
||||
while (curr) {
|
||||
if (curr == edge) {
|
||||
if (prev) {
|
||||
prev->next_backward = curr->next_backward;
|
||||
} else {
|
||||
dst->next_backward = curr->next_backward;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
prev = curr;
|
||||
curr = curr->next_backward;
|
||||
}
|
||||
}
|
||||
|
||||
wp_intern void prGraphAddEdge(PrGraph *graph, PrNodeId from, PrNodeId to) {
|
||||
if (!prGraphIsActiveNode(graph, from) || !prGraphIsActiveNode(graph, to)) { return; }
|
||||
if (from.index == to.index) { return; }
|
||||
|
||||
PrEdgeVertex *edge = (PrEdgeVertex *)prPoolAlloc(&graph->vertex_pool);
|
||||
if (!edge) { return; }
|
||||
|
||||
edge->source = from;
|
||||
edge->target = to;
|
||||
|
||||
/* Link into adjacency chains */
|
||||
PrEdgeVertex *src = &graph->vertices[from.index];
|
||||
PrEdgeVertex *dst = &graph->vertices[to.index];
|
||||
edge->next_forward = src->next_forward;
|
||||
edge->next_backward = dst->next_backward;
|
||||
src->next_forward = edge;
|
||||
dst->next_backward = edge;
|
||||
|
||||
/* Check whether the new edge created a cycle */
|
||||
WpAllocator scratch = wpMemArenaAllocatorInitZero(KiB(16));
|
||||
PrNodeIdArray sorted = prGraphTopologicalSort(graph, &scratch);
|
||||
u64 sorted_count = sorted ? wpArrayCount(sorted) : 0;
|
||||
if (sorted_count < graph->count) {
|
||||
_unlinkForward(graph, from, edge);
|
||||
_unlinkBackward(graph, to, edge);
|
||||
prPoolFree(&graph->vertex_pool, edge);
|
||||
}
|
||||
|
||||
wpMemArenaAllocatorDestroy(&scratch);
|
||||
}
|
||||
|
||||
wp_intern PrNodeId prGraphAddNode(PrGraph *graph, PrNodeType type) {
|
||||
u64 idx = graph->free_head;
|
||||
if (idx == INVALID_NODE_INDEX) { return INVALID_NODE_ID; }
|
||||
|
||||
PrNode *node = &graph->nodes[idx];
|
||||
|
||||
graph->free_head = node->next_free;
|
||||
node->next_free = INVALID_NODE_INDEX;
|
||||
node->type = type;
|
||||
memset(&node->params, 0, sizeof(node->params));
|
||||
|
||||
graph->count++;
|
||||
|
||||
if (idx + 1 > graph->max_count_ever) { graph->max_count_ever = idx + 1; }
|
||||
|
||||
return (PrNodeId){ .index = idx, .generation = node->generation };
|
||||
}
|
||||
|
||||
wp_intern void prGraphRemoveNode(PrGraph *graph, PrNodeId id) {
|
||||
if (!prGraphIsActiveNode(graph, id)) { return; }
|
||||
|
||||
PrNode *node = &graph->nodes[id.index];
|
||||
u64 next_gen = node->generation + 1;
|
||||
memset(node, 0, sizeof(PrNode));
|
||||
|
||||
node->generation = next_gen;
|
||||
node->next_free = graph->free_head;
|
||||
graph->free_head = id.index;
|
||||
|
||||
PrEdgeVertex *vertex = &graph->vertices[id.index];
|
||||
if (!(vertex->next_forward) && !(vertex->next_backward)) {
|
||||
goto REMOVE_NODE_UNSET_POINTERS;
|
||||
}
|
||||
|
||||
PrEdgeVertex *curr = vertex->next_forward;
|
||||
while (curr) {
|
||||
PrEdgeVertex *next = curr->next_forward;
|
||||
_unlinkBackward(graph, curr->target, curr);
|
||||
prPoolFree(&graph->vertex_pool, curr);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
curr = vertex->next_backward;
|
||||
while (curr) {
|
||||
PrEdgeVertex *trailing = curr->next_backward;
|
||||
_unlinkForward(graph, curr->source, curr);
|
||||
prPoolFree(&graph->vertex_pool, curr);
|
||||
curr = trailing;
|
||||
}
|
||||
|
||||
REMOVE_NODE_UNSET_POINTERS:
|
||||
vertex->next_forward = vertex->next_backward = NULL;
|
||||
|
||||
graph->count--;
|
||||
}
|
||||
|
||||
wp_intern void prGraphDump(const PrGraph *graph) {
|
||||
wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph) {
|
||||
printf("==============INPUTS==============\n");
|
||||
for (u64 i = 0; i < graph->max_count_ever; ++i) {
|
||||
PrNodeId id = prGraphGetNode(graph, i);
|
||||
if (!prGraphIsActiveNode(graph, id)) { continue; }
|
||||
for (u64 i = 0; i < mgr->max_count_ever; ++i) {
|
||||
PrNodeId id = prNodeManagerGetNode(mgr, i);
|
||||
if (!prNodeManagerIsActiveNode(mgr, id)) { continue; }
|
||||
|
||||
PrEdgeVertex *vertex = &graph->vertices[i];
|
||||
PrGraphNode *gnode = &graph->graph_nodes[i];
|
||||
|
||||
printf("%" PRIu64 ":", id.index + 1);
|
||||
|
||||
if (!(vertex->next_backward)) {
|
||||
if (!(gnode->next_backward)) {
|
||||
printf(" (none)");
|
||||
} else {
|
||||
PrEdgeVertex *curr = vertex->next_backward;
|
||||
PrGraphEdge *curr = gnode->next_backward;
|
||||
while (curr) {
|
||||
printf(" %" PRIu64, curr->source.index + 1);
|
||||
curr = curr->next_backward;
|
||||
@@ -363,18 +427,18 @@ wp_intern void prGraphDump(const PrGraph *graph) {
|
||||
}
|
||||
|
||||
printf("==============OUTPUTS==============\n");
|
||||
for (u64 i = 0; i < graph->max_count_ever; ++i) {
|
||||
PrNodeId id = prGraphGetNode(graph, i);
|
||||
if (!prGraphIsActiveNode(graph, id)) { continue; }
|
||||
for (u64 i = 0; i < mgr->max_count_ever; ++i) {
|
||||
PrNodeId id = prNodeManagerGetNode(mgr, i);
|
||||
if (!prNodeManagerIsActiveNode(mgr, id)) { continue; }
|
||||
|
||||
PrEdgeVertex *vertex = &graph->vertices[i];
|
||||
PrGraphNode *gnode = &graph->graph_nodes[i];
|
||||
|
||||
printf("%" PRIu64 ":", id.index + 1);
|
||||
|
||||
if (!(vertex->next_forward)) {
|
||||
if (!(gnode->next_forward)) {
|
||||
printf(" (none)");
|
||||
} else {
|
||||
PrEdgeVertex *curr = vertex->next_forward;
|
||||
PrGraphEdge *curr = gnode->next_forward;
|
||||
while (curr) {
|
||||
printf(" %" PRIu64, curr->target.index + 1);
|
||||
curr = curr->next_forward;
|
||||
@@ -385,6 +449,10 @@ wp_intern void prGraphDump(const PrGraph *graph) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Main
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
i32 main(void) {
|
||||
WpAllocator arena = wpMemArenaAllocatorInitZero(MiB(16));
|
||||
if (wpMemAllocatorInvalid(&arena)) {
|
||||
@@ -392,75 +460,75 @@ i32 main(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrGraph graph = {0};
|
||||
prGraphInit(&graph, &arena, 128);
|
||||
PrNodeManager mgr = {0};
|
||||
prNodeManagerInit(&mgr, &arena, 128);
|
||||
|
||||
PrNodeId n1 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n2 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n3 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n4 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n5 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n1 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
PrNodeId n2 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
PrNodeId n3 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
PrNodeId n4 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
PrNodeId n5 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
|
||||
prGraphAddEdge(&graph, n1, n2);
|
||||
prGraphAddEdge(&graph, n1, n3);
|
||||
prGraphAddEdge(&graph, n1, n5);
|
||||
prGraphAddEdge(&graph, n2, n4);
|
||||
prGraphAddEdge(&graph, n3, n4);
|
||||
prGraphAddEdge(&graph, n3, n5);
|
||||
prNodeManagerAddEdge(&mgr, n1, n2);
|
||||
prNodeManagerAddEdge(&mgr, n1, n3);
|
||||
prNodeManagerAddEdge(&mgr, n1, n5);
|
||||
prNodeManagerAddEdge(&mgr, n2, n4);
|
||||
prNodeManagerAddEdge(&mgr, n3, n4);
|
||||
prNodeManagerAddEdge(&mgr, n3, n5);
|
||||
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
prGraphRemoveNode(&graph, n3);
|
||||
prNodeManagerRemoveNode(&mgr, n3);
|
||||
|
||||
printf("\n");
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
PrNodeId n6 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n6 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
|
||||
prGraphAddEdge(&graph, n4, n6);
|
||||
prNodeManagerAddEdge(&mgr, n4, n6);
|
||||
|
||||
printf("\n");
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
prGraphRemoveNode(&graph, n5);
|
||||
prNodeManagerRemoveNode(&mgr, n5);
|
||||
|
||||
printf("\n");
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
PrNodeId n7 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n7 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
|
||||
prGraphAddEdge(&graph, n1, n7);
|
||||
prGraphAddEdge(&graph, n2, n7);
|
||||
prNodeManagerAddEdge(&mgr, n1, n7);
|
||||
prNodeManagerAddEdge(&mgr, n2, n7);
|
||||
|
||||
printf("\n");
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
PrNodeId n8 = prGraphAddNode(&graph, PR_NODE_TYPE_READ);
|
||||
PrNodeId n8 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ);
|
||||
|
||||
prGraphAddEdge(&graph, n6, n8);
|
||||
prGraphAddEdge(&graph, n7, n8);
|
||||
prNodeManagerAddEdge(&mgr, n6, n8);
|
||||
prNodeManagerAddEdge(&mgr, n7, n8);
|
||||
|
||||
printf("\n");
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
printf("\n=== Try adding cycle 8→1 (should be rejected) ===\n");
|
||||
prGraphAddEdge(&graph, n8, n1);
|
||||
prNodeManagerAddEdge(&mgr, n8, n1);
|
||||
|
||||
printf("\n=== Try adding 8→1 again (still rejected) ===\n");
|
||||
prGraphAddEdge(&graph, n8, n1);
|
||||
prNodeManagerAddEdge(&mgr, n8, n1);
|
||||
|
||||
printf("\n=== Try adding cycle 7→2 (should be rejected) ===\n\n");
|
||||
prGraphAddEdge(&graph, n7, n2);
|
||||
prNodeManagerAddEdge(&mgr, n7, n2);
|
||||
|
||||
prGraphDump(&graph);
|
||||
prGraphDump(&mgr, &mgr.graph);
|
||||
|
||||
printf("\n=== Topological sort ===\n");
|
||||
PrNodeId *sorted = prGraphTopologicalSort(&graph, &arena);
|
||||
PrNodeId *sorted = prGraphTopologicalSort(&mgr, &mgr.graph, &arena);
|
||||
if (sorted) {
|
||||
u64 n = wpArrayCount(sorted);
|
||||
printf(" count: %llu / %llu active\n",
|
||||
(unsigned long long)n,
|
||||
(unsigned long long)graph.count);
|
||||
(unsigned long long)mgr.count);
|
||||
for (u64 i = 0; i < n; i++) {
|
||||
printf(" [%llu] idx=%llu gen=%llu\n",
|
||||
(unsigned long long)i,
|
||||
|
||||
Reference in New Issue
Block a user