From f5b9912d12776c574f858b09f55d04720bebc487 Mon Sep 17 00:00:00 2001 From: Abdelrahman Date: Sun, 5 Jul 2026 00:34:04 +0100 Subject: [PATCH] Remove old dag.c --- scratchpad/dag.c | 630 +++++++++++++++++++++++++++++-------------- scratchpad/dag_man.c | 543 ------------------------------------- 2 files changed, 423 insertions(+), 750 deletions(-) delete mode 100644 scratchpad/dag_man.c diff --git a/scratchpad/dag.c b/scratchpad/dag.c index 65fee40..6a36c38 100644 --- a/scratchpad/dag.c +++ b/scratchpad/dag.c @@ -1,7 +1,12 @@ +// vim:fileencoding=utf-8:foldmethod=marker + +#include "../src/wapp/wapp.h" +#include #include #include -#include -#include "../src/wapp/wapp.h" +#include + +wp_intern WpLogger _log = { .name = wpStr8LitRo("dag_man") }; /* --------------------------------------------------------------------------- * Pool allocator (arena-backed, intrusive free list) @@ -18,13 +23,13 @@ typedef struct { u64 slot_size; } PrPool; -static void prPoolInit(PrPool *pool, WpAllocator *arena, u64 slot_size) { - pool->allocator = arena; +wp_intern void prPoolInit(PrPool *pool, WpAllocator *allocator, u64 slot_size) { + pool->allocator = allocator; pool->free_head = NULL; pool->slot_size = slot_size; } -static void *prPoolAlloc(PrPool *pool) { +wp_intern void *prPoolAlloc(PrPool *pool) { if (pool->free_head) { PrPoolFreeNode *node = pool->free_head; pool->free_head = node->next; @@ -33,7 +38,7 @@ static void *prPoolAlloc(PrPool *pool) { return wpMemAllocatorAlloc(pool->allocator, pool->slot_size); } -static void prPoolFree(PrPool *pool, void *slot) { +wp_intern void prPoolFree(PrPool *pool, void *slot) { if (!slot) { return; } PrPoolFreeNode *node = (PrPoolFreeNode *)slot; node->next = pool->free_head; @@ -41,128 +46,230 @@ static void prPoolFree(PrPool *pool, void *slot) { } /* --------------------------------------------------------------------------- - * Graph — unified nodes with values + forward/backward adjacency - * - * - Nodes live in a flat array; free slots are linked via next_free. - * - Each node has a generation counter bumped on free; handles are - * { index, generation } so stale references are detectable. - * - Edges are pool-allocated PrEdgeNode structs with both forward - * and backward linkage so deletion traces both directions. + * Shared types * -------------------------------------------------------------------------*/ -#define PR_INVALID_INDEX ((u64)-1) +#define INVALID_NODE_INDEX (u64)-1 +#define INVALID_NODE_ID ((PrNodeId){ .index = INVALID_NODE_INDEX, .generation = INVALID_NODE_INDEX }) -typedef struct PrEdgeNode PrEdgeNode; -struct PrEdgeNode { - PrEdgeNode *pool_next; /* used by pool free list when freed */ - PrEdgeNode *next_forward; /* chain in source's forward list */ - PrEdgeNode *next_backward; /* chain in target's backward list */ - u64 source_idx; - u64 target_idx; -}; +typedef enum { + PR_NODE_TYPE_NONE, + PR_NODE_TYPE_READ, + PR_NODE_TYPE_BLUR, + PR_NODE_TYPE_GRADE, + + COUNT_NODE_TYPES +} PrNodeType; typedef struct { u64 index; u64 generation; -} PrHandle; +} PrNodeId; +typedef PrNodeId *PrNodeIdArray; typedef struct { - int value; - u64 generation; /* bumped on every free */ - u64 next_free; /* free-list index (PR_INVALID_INDEX = active) */ - PrEdgeNode *forward_head; /* outgoing edges */ - PrEdgeNode *backward_head; /* incoming edges */ -} PrGraphNode; + union { + WpStr8 path; + f32 blur; + f32 gain; + } params; + PrNodeType type; + u64 generation; + u64 next_free; +} PrNode; +typedef PrNode *PrNodeArray; + +/* --------------------------------------------------------------------------- + * Graph edge type + * -------------------------------------------------------------------------*/ + +typedef struct PrGraphEdge PrGraphEdge; +struct PrGraphEdge { + PrGraphEdge *next_forward; + PrGraphEdge *next_backward; + PrNodeId source; + PrNodeId target; +}; +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 { - PrGraphNode *nodes; /* WpArray of PrGraphNode */ - u64 max_nodes; - u64 max_ever; /* highest index ever allocated */ - u64 free_head; /* PR_INVALID_INDEX = empty */ - u64 count; /* active node count */ - PrPool edge_pool; + PrPool edge_pool; + PrGraphNodeArray graph_nodes; + u64 capacity; } PrGraph; -/* --- initialisation ---------------------------------------------------- */ +/* --------------------------------------------------------------------------- + * PrNodeManager — owns compositor node data + handle lifecycle + topology + * -------------------------------------------------------------------------*/ -static void prGraphInit(PrGraph *g, WpAllocator *arena, u64 max_nodes) { - g->nodes = wpArrayAllocCapacity(PrGraphNode, arena, max_nodes, WP_ARRAY_INIT_FILLED); - g->max_nodes = max_nodes; - g->max_ever = 0; - g->free_head = PR_INVALID_INDEX; - g->count = 0; - prPoolInit(&g->edge_pool, arena, sizeof(PrEdgeNode)); +typedef struct { + PrNodeArray nodes; + PrGraph graph; + u64 capacity; + u64 max_count_ever; + u64 count; + u64 free_head; +} PrNodeManager; - /* Build the free list — last slot's next_free stays PR_INVALID_INDEX */ - for (u64 i = 0; i < max_nodes; i++) { - g->nodes[i].next_free = (i < max_nodes - 1) ? i + 1 : PR_INVALID_INDEX; - } - g->free_head = 0; -} +/* --------------------------------------------------------------------------- + * Function declarations (cross-referencing both types) + * -------------------------------------------------------------------------*/ -/* --- handle validation ------------------------------------------------ */ +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); -static b8 prHandleValid(PrGraph *g, PrHandle h) { - if (h.index >= g->max_nodes) { return false; } - PrGraphNode *node = &g->nodes[h.index]; - return node->generation == h.generation && node->next_free == PR_INVALID_INDEX; -} +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); -/* --- node management -------------------------------------------------- */ +/* --------------------------------------------------------------------------- + * PrNodeManager implementation + * -------------------------------------------------------------------------*/ -static PrHandle prNodeAdd(PrGraph *g, int value) { - if (g->free_head == PR_INVALID_INDEX) { - return (PrHandle){ PR_INVALID_INDEX, 0 }; +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; } - u64 idx = g->free_head; - PrGraphNode *n = &g->nodes[idx]; - g->free_head = n->next_free; + memset(mgr->nodes, 0, capacity * sizeof(PrNode)); - n->value = value; - n->next_free = PR_INVALID_INDEX; - n->forward_head = NULL; - n->backward_head = NULL; - g->count++; + for (u64 i = 0; i < capacity; ++i) { + mgr->nodes[i].next_free = i < capacity - 1 ? i + 1 : INVALID_NODE_INDEX; + } - if (idx >= g->max_ever) { g->max_ever = idx + 1; } - - return (PrHandle){ idx, n->generation }; + prGraphInit(&mgr->graph, allocator, capacity); } -static int *prNodeGetValue(PrGraph *g, PrHandle h) { - if (!prHandleValid(g, h)) { return NULL; } - return &g->nodes[h.index].value; +wp_intern b8 prNodeManagerIsStaleNode(const PrNodeManager *mgr, PrNodeId id) { + u64 generation = mgr->nodes[id.index].generation; + return id.generation != generation; } -/* --- internal: unlink edge helpers ------------------------------------ */ +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; +} -static PrEdgeNode *_unlinkForward(PrGraph *g, u64 from_idx, u64 target_idx) { - PrGraphNode *src = &g->nodes[from_idx]; - PrEdgeNode *prev = NULL; - PrEdgeNode *curr = src->forward_head; +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->graph_nodes = wpArrayAllocCapacity(PrGraphNode, allocator, capacity, WP_ARRAY_INIT_FILLED); + graph->capacity = capacity; + + if (!graph->graph_nodes) { + graph->capacity = 0; + return; + } + + 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->target_idx == target_idx) { - if (prev) { prev->next_forward = curr->next_forward; } - else { src->forward_head = curr->next_forward; } - return 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; } - return NULL; } -static void _unlinkBackward(PrGraph *g, u64 to_idx, u64 source_idx) { - PrGraphNode *dst = &g->nodes[to_idx]; - PrEdgeNode *prev = NULL; - PrEdgeNode *curr = dst->backward_head; - // This is intended to handle cases where the target node might not be the one - // immediately feeding the node represented by to_idx +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->source_idx == source_idx) { - if (prev) { prev->next_backward = curr->next_backward; } - else { dst->backward_head = curr->next_backward; } + if (curr == edge) { + if (prev) { + prev->next_backward = curr->next_backward; + } else { + gnode->next_backward = curr->next_backward; + } return; } prev = curr; @@ -172,156 +279,265 @@ static void _unlinkBackward(PrGraph *g, u64 to_idx, u64 source_idx) { /* --- edge management -------------------------------------------------- */ -static b8 prEdgeAdd(PrGraph *g, PrHandle from, PrHandle to) { - if (!prHandleValid(g, from) || !prHandleValid(g, to)) { return false; } +wp_intern void prGraphAddEdge(PrNodeManager *mgr, PrGraph *graph, u64 from_idx, u64 to_idx) { + PrGraphEdge *edge = (PrGraphEdge *)prPoolAlloc(&graph->edge_pool); + if (!edge) { return; } - PrEdgeNode *edge = (PrEdgeNode *)prPoolAlloc(&g->edge_pool); - if (!edge) { return false; } + PrNodeId from_id = prNodeManagerGetNode(mgr, from_idx); + PrNodeId to_id = prNodeManagerGetNode(mgr, to_idx); - edge->source_idx = from.index; - edge->target_idx = to.index; + edge->source = from_id; + edge->target = to_id; - PrGraphNode *src = &g->nodes[from.index]; - edge->next_forward = src->forward_head; - src->forward_head = edge; + /* 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; - PrGraphNode *dst = &g->nodes[to.index]; - edge->next_backward = dst->backward_head; - dst->backward_head = edge; - - return true; + /* 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); + } } -/* --- node removal (tears down incident edges) ------------------------- */ +wp_intern void prGraphRemoveEdges(PrGraph *graph, u64 idx) { + PrGraphNode *gnode = &graph->graph_nodes[idx]; -static void prNodeRemove(PrGraph *g, PrHandle h) { - if (!prHandleValid(g, h)) { return; } - - u64 idx = h.index; - PrGraphNode *n = &g->nodes[idx]; - - /* Free outgoing edges: unlink from each target's backward list */ - PrEdgeNode *edge = n->forward_head; - while (edge) { - PrEdgeNode *next = edge->next_forward; - _unlinkBackward(g, edge->target_idx, idx); - prPoolFree(&g->edge_pool, edge); - edge = next; + /* 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 */ - edge = n->backward_head; - while (edge) { - PrEdgeNode *next = edge->next_backward; - /* edge is also in the source's forward list — unlink by target_idx */ - _unlinkForward(g, edge->source_idx, idx); - prPoolFree(&g->edge_pool, edge); - edge = 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; } - /* Return node slot to free list with bumped generation */ - n->generation++; - n->next_free = g->free_head; - g->free_head = idx; - g->count--; + gnode->next_forward = gnode->next_backward = NULL; } -/* --- traversal helpers ------------------------------------------------ */ +/* --------------------------------------------------------------------------- + * Kahn's algorithm — topological sort / cycle detection + * + * Returns a WpArray of PrNodeId (sorted topologically). If the result count + * is less than mgr->count, the graph contains a cycle. + * -------------------------------------------------------------------------*/ -static void prDump(PrGraph *g) { - printf("Active nodes: %llu\n\n", (unsigned long long)g->count); +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; } - for (u64 i = 0; i < g->max_ever; i++) { - PrGraphNode *n = &g->nodes[i]; - if (n->next_free != PR_INVALID_INDEX) { continue; } + PrNodeIdArray result = wpArrayAllocCapacity(PrNodeId, allocator, mgr->count, WP_ARRAY_INIT_NONE); + if (!result) { return NULL; } - printf(" [%llu] g=%llu value=%d\n", - (unsigned long long)i, - (unsigned long long)n->generation, - n->value); + WpAllocator local_arena = wpMemArenaAllocatorInitZero(KiB(16)); - printf(" forward ──▶"); - PrEdgeNode *e = n->forward_head; - if (!e) { printf(" (none)"); } - while (e) { - PrGraphNode *tv = &g->nodes[e->target_idx]; - printf(" %llu[%d]", (unsigned long long)e->target_idx, tv->value); - e = e->next_forward; - if (e) { printf(","); } + WpU64Array in_degree = wpArrayAllocCapacity(u64, &local_arena, graph->capacity, WP_ARRAY_INIT_FILLED); + if (!in_degree) { return result; } + memset(in_degree, 0, wpArrayCapacity(in_degree) * sizeof(u64)); + + for (u64 i = 0; i < mgr->max_count_ever; i++) { + PrNodeId id = prNodeManagerGetNode(mgr, i); + if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } + + 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, mgr->count); + + 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); + } + } + + while (queue.count > 0) { + u64 *node_idx = wpQueuePop(u64, &queue); + if (!node_idx) { break; } + + PrNodeId id = prNodeManagerGetNode(mgr, *node_idx); + wpArrayAppendCapped(PrNodeId, result, &id); + + 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) { + in_degree[target_idx]--; + if (in_degree[target_idx] == 0) { + wpQueuePush(u64, &queue, &target_idx); + } + } + curr = curr->next_forward; + } + } + + return result; +} + +/* --------------------------------------------------------------------------- + * Dump + * -------------------------------------------------------------------------*/ + +wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph) { + printf("==============INPUTS==============\n"); + for (u64 i = 0; i < mgr->max_count_ever; ++i) { + PrNodeId id = prNodeManagerGetNode(mgr, i); + if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } + + PrGraphNode *gnode = &graph->graph_nodes[i]; + + printf("%" PRIu64 ":", id.index + 1); + + if (!(gnode->next_backward)) { + printf(" (none)"); + } else { + PrGraphEdge *curr = gnode->next_backward; + while (curr) { + printf(" %" PRIu64, curr->source.index + 1); + curr = curr->next_backward; + } + } + printf("\n"); + } - printf(" backward ◀──"); - e = n->backward_head; - if (!e) { printf(" (none)"); } - while (e) { - PrGraphNode *sv = &g->nodes[e->source_idx]; - printf(" %llu[%d]", (unsigned long long)e->source_idx, sv->value); - e = e->next_backward; - if (e) { printf(","); } + printf("==============OUTPUTS==============\n"); + for (u64 i = 0; i < mgr->max_count_ever; ++i) { + PrNodeId id = prNodeManagerGetNode(mgr, i); + if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } + + PrGraphNode *gnode = &graph->graph_nodes[i]; + + printf("%" PRIu64 ":", id.index + 1); + + if (!(gnode->next_forward)) { + printf(" (none)"); + } else { + PrGraphEdge *curr = gnode->next_forward; + while (curr) { + printf(" %" PRIu64, curr->target.index + 1); + curr = curr->next_forward; + } } + printf("\n"); } } /* --------------------------------------------------------------------------- - * Demo + * Main * -------------------------------------------------------------------------*/ -int main(void) { - srand((unsigned)time(NULL)); - - WpAllocator arena = wpMemArenaAllocatorInitZero(KiB(64)); +i32 main(void) { + WpAllocator arena = wpMemArenaAllocatorInitZero(MiB(16)); if (wpMemAllocatorInvalid(&arena)) { - fprintf(stderr, "arena init failed\n"); + wpLogFatal(&_log, wpStr8Lit("arena init failed")); return 1; } - PrGraph g; - prGraphInit(&g, &arena, 16); + PrNodeManager mgr = {0}; + prNodeManagerInit(&mgr, &arena, 128); - printf("=== Add 6 nodes ===\n"); - PrHandle nodes[10]; - for (u64 i = 0; i < 6; i++) { - nodes[i] = prNodeAdd(&g, rand() % 100); - printf(" node[%llu] = handle{%llu g%llu} val=%d\n", - (unsigned long long)i, - (unsigned long long)nodes[i].index, - (unsigned long long)nodes[i].generation, - *prNodeGetValue(&g, nodes[i])); + 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); + + 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(&mgr, &mgr.graph); + + prNodeManagerRemoveNode(&mgr, n3); + + printf("\n"); + prGraphDump(&mgr, &mgr.graph); + + PrNodeId n6 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); + + prNodeManagerAddEdge(&mgr, n4, n6); + + printf("\n"); + prGraphDump(&mgr, &mgr.graph); + + prNodeManagerRemoveNode(&mgr, n5); + + printf("\n"); + prGraphDump(&mgr, &mgr.graph); + + PrNodeId n7 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); + + prNodeManagerAddEdge(&mgr, n1, n7); + prNodeManagerAddEdge(&mgr, n2, n7); + + printf("\n"); + prGraphDump(&mgr, &mgr.graph); + + PrNodeId n8 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); + + prNodeManagerAddEdge(&mgr, n6, n8); + prNodeManagerAddEdge(&mgr, n7, n8); + + printf("\n"); + prGraphDump(&mgr, &mgr.graph); + + printf("\n=== Try adding cycle 8→1 (should be rejected) ===\n"); + prNodeManagerAddEdge(&mgr, n8, n1); + + printf("\n=== Try adding 8→1 again (still rejected) ===\n"); + prNodeManagerAddEdge(&mgr, n8, n1); + + printf("\n=== Try adding cycle 7→2 (should be rejected) ===\n\n"); + prNodeManagerAddEdge(&mgr, n7, n2); + + prGraphDump(&mgr, &mgr.graph); + + printf("\n=== Topological sort ===\n"); + 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)mgr.count); + for (u64 i = 0; i < n; i++) { + printf(" [%llu] idx=%llu gen=%llu\n", + (unsigned long long)i, + (unsigned long long)sorted[i].index, + (unsigned long long)sorted[i].generation); + } } - printf("\n=== Add edges: 0→1, 0→2, 1→3, 2→3, 3→4, 4→5 ===\n"); - u64 edge_list[][2] = { {0,1}, {0,2}, {1,3}, {2,3}, {3,4}, {4,5} }; - for (u64 i = 0; i < 6; i++) { - u64 f = edge_list[i][0], t = edge_list[i][1]; - prEdgeAdd(&g, nodes[f], nodes[t]); - } - prDump(&g); - - printf("=== Remove node 2 (index %llu) ===\n", - (unsigned long long)nodes[2].index); - prNodeRemove(&g, nodes[2]); - prDump(&g); - - printf("=== Stale check ===\n"); - printf(" nodes[2] handle{%llu g%llu} valid? %s\n", - (unsigned long long)nodes[2].index, - (unsigned long long)nodes[2].generation, - prHandleValid(&g, nodes[2]) ? "YES" : "NO"); - - printf("=== Add edge 5→0 (reuses freed edge slots) ===\n"); - prEdgeAdd(&g, nodes[5], nodes[0]); - prDump(&g); - - printf("=== Add a new node (reuses freed node slot) ===\n"); - nodes[6] = prNodeAdd(&g, 42); - printf(" new node = handle{%llu g%llu} val=%d\n", - (unsigned long long)nodes[6].index, - (unsigned long long)nodes[6].generation, - *prNodeGetValue(&g, nodes[6])); - prDump(&g); - wpMemArenaAllocatorDestroy(&arena); + return 0; } diff --git a/scratchpad/dag_man.c b/scratchpad/dag_man.c deleted file mode 100644 index 6a36c38..0000000 --- a/scratchpad/dag_man.c +++ /dev/null @@ -1,543 +0,0 @@ -// vim:fileencoding=utf-8:foldmethod=marker - -#include "../src/wapp/wapp.h" -#include -#include -#include -#include - -wp_intern WpLogger _log = { .name = wpStr8LitRo("dag_man") }; - -/* --------------------------------------------------------------------------- - * Pool allocator (arena-backed, intrusive free list) - * -------------------------------------------------------------------------*/ - -typedef struct PrPoolFreeNode PrPoolFreeNode; -struct PrPoolFreeNode { - PrPoolFreeNode *next; -}; - -typedef struct { - WpAllocator *allocator; - PrPoolFreeNode *free_head; - u64 slot_size; -} PrPool; - -wp_intern void prPoolInit(PrPool *pool, WpAllocator *allocator, u64 slot_size) { - pool->allocator = allocator; - pool->free_head = NULL; - pool->slot_size = slot_size; -} - -wp_intern void *prPoolAlloc(PrPool *pool) { - if (pool->free_head) { - PrPoolFreeNode *node = pool->free_head; - pool->free_head = node->next; - return node; - } - return wpMemAllocatorAlloc(pool->allocator, pool->slot_size); -} - -wp_intern void prPoolFree(PrPool *pool, void *slot) { - if (!slot) { return; } - PrPoolFreeNode *node = (PrPoolFreeNode *)slot; - node->next = pool->free_head; - pool->free_head = node; -} - -/* --------------------------------------------------------------------------- - * Shared types - * -------------------------------------------------------------------------*/ - -#define INVALID_NODE_INDEX (u64)-1 -#define INVALID_NODE_ID ((PrNodeId){ .index = INVALID_NODE_INDEX, .generation = INVALID_NODE_INDEX }) - -typedef enum { - PR_NODE_TYPE_NONE, - PR_NODE_TYPE_READ, - PR_NODE_TYPE_BLUR, - PR_NODE_TYPE_GRADE, - - COUNT_NODE_TYPES -} PrNodeType; - -typedef struct { - u64 index; - u64 generation; -} PrNodeId; -typedef PrNodeId *PrNodeIdArray; - -typedef struct { - union { - WpStr8 path; - f32 blur; - f32 gain; - } params; - PrNodeType type; - u64 generation; - u64 next_free; -} PrNode; -typedef PrNode *PrNodeArray; - -/* --------------------------------------------------------------------------- - * Graph edge type - * -------------------------------------------------------------------------*/ - -typedef struct PrGraphEdge PrGraphEdge; -struct PrGraphEdge { - PrGraphEdge *next_forward; - PrGraphEdge *next_backward; - PrNodeId source; - PrNodeId target; -}; -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 { - PrNodeArray nodes; - PrGraph graph; - u64 capacity; - u64 max_count_ever; - u64 count; - u64 free_head; -} PrNodeManager; - -/* --------------------------------------------------------------------------- - * 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 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 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->graph_nodes = wpArrayAllocCapacity(PrGraphNode, allocator, capacity, WP_ARRAY_INIT_FILLED); - graph->capacity = capacity; - - if (!graph->graph_nodes) { - graph->capacity = 0; - return; - } - - 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 mgr->count, the graph contains a cycle. - * -------------------------------------------------------------------------*/ - -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, mgr->count, WP_ARRAY_INIT_NONE); - if (!result) { return NULL; } - - WpAllocator local_arena = wpMemArenaAllocatorInitZero(KiB(16)); - - WpU64Array in_degree = wpArrayAllocCapacity(u64, &local_arena, graph->capacity, WP_ARRAY_INIT_FILLED); - if (!in_degree) { return result; } - memset(in_degree, 0, wpArrayCapacity(in_degree) * sizeof(u64)); - - for (u64 i = 0; i < mgr->max_count_ever; i++) { - PrNodeId id = prNodeManagerGetNode(mgr, i); - if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } - - 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, mgr->count); - - 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); - } - } - - while (queue.count > 0) { - u64 *node_idx = wpQueuePop(u64, &queue); - if (!node_idx) { break; } - - PrNodeId id = prNodeManagerGetNode(mgr, *node_idx); - wpArrayAppendCapped(PrNodeId, result, &id); - - 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) { - in_degree[target_idx]--; - if (in_degree[target_idx] == 0) { - wpQueuePush(u64, &queue, &target_idx); - } - } - curr = curr->next_forward; - } - } - - return result; -} - -/* --------------------------------------------------------------------------- - * Dump - * -------------------------------------------------------------------------*/ - -wp_intern void prGraphDump(const PrNodeManager *mgr, const PrGraph *graph) { - printf("==============INPUTS==============\n"); - for (u64 i = 0; i < mgr->max_count_ever; ++i) { - PrNodeId id = prNodeManagerGetNode(mgr, i); - if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } - - PrGraphNode *gnode = &graph->graph_nodes[i]; - - printf("%" PRIu64 ":", id.index + 1); - - if (!(gnode->next_backward)) { - printf(" (none)"); - } else { - PrGraphEdge *curr = gnode->next_backward; - while (curr) { - printf(" %" PRIu64, curr->source.index + 1); - curr = curr->next_backward; - } - } - - printf("\n"); - } - - printf("==============OUTPUTS==============\n"); - for (u64 i = 0; i < mgr->max_count_ever; ++i) { - PrNodeId id = prNodeManagerGetNode(mgr, i); - if (!prNodeManagerIsActiveNode(mgr, id)) { continue; } - - PrGraphNode *gnode = &graph->graph_nodes[i]; - - printf("%" PRIu64 ":", id.index + 1); - - if (!(gnode->next_forward)) { - printf(" (none)"); - } else { - PrGraphEdge *curr = gnode->next_forward; - while (curr) { - printf(" %" PRIu64, curr->target.index + 1); - curr = curr->next_forward; - } - } - - printf("\n"); - } -} - -/* --------------------------------------------------------------------------- - * Main - * -------------------------------------------------------------------------*/ - -i32 main(void) { - WpAllocator arena = wpMemArenaAllocatorInitZero(MiB(16)); - if (wpMemAllocatorInvalid(&arena)) { - wpLogFatal(&_log, wpStr8Lit("arena init failed")); - return 1; - } - - PrNodeManager mgr = {0}; - prNodeManagerInit(&mgr, &arena, 128); - - 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); - - 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(&mgr, &mgr.graph); - - prNodeManagerRemoveNode(&mgr, n3); - - printf("\n"); - prGraphDump(&mgr, &mgr.graph); - - PrNodeId n6 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); - - prNodeManagerAddEdge(&mgr, n4, n6); - - printf("\n"); - prGraphDump(&mgr, &mgr.graph); - - prNodeManagerRemoveNode(&mgr, n5); - - printf("\n"); - prGraphDump(&mgr, &mgr.graph); - - PrNodeId n7 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); - - prNodeManagerAddEdge(&mgr, n1, n7); - prNodeManagerAddEdge(&mgr, n2, n7); - - printf("\n"); - prGraphDump(&mgr, &mgr.graph); - - PrNodeId n8 = prNodeManagerAddNode(&mgr, PR_NODE_TYPE_READ); - - prNodeManagerAddEdge(&mgr, n6, n8); - prNodeManagerAddEdge(&mgr, n7, n8); - - printf("\n"); - prGraphDump(&mgr, &mgr.graph); - - printf("\n=== Try adding cycle 8→1 (should be rejected) ===\n"); - prNodeManagerAddEdge(&mgr, n8, n1); - - printf("\n=== Try adding 8→1 again (still rejected) ===\n"); - prNodeManagerAddEdge(&mgr, n8, n1); - - printf("\n=== Try adding cycle 7→2 (should be rejected) ===\n\n"); - prNodeManagerAddEdge(&mgr, n7, n2); - - prGraphDump(&mgr, &mgr.graph); - - printf("\n=== Topological sort ===\n"); - 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)mgr.count); - for (u64 i = 0; i < n; i++) { - printf(" [%llu] idx=%llu gen=%llu\n", - (unsigned long long)i, - (unsigned long long)sorted[i].index, - (unsigned long long)sorted[i].generation); - } - } - - wpMemArenaAllocatorDestroy(&arena); - - return 0; -}