// vim:fileencoding=utf-8:foldmethod=marker #include "../src/wapp/wapp.h" #include <_inttypes.h> #include #include #include /* --------------------------------------------------------------------------- * 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; } /* --------------------------------------------------------------------------- * Graph * -------------------------------------------------------------------------*/ #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 struct { union { WpStr8 path; f32 blur; f32 gain; } params; PrNodeType type; u64 generation; u64 next_free; } PrNode; 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; PrNodeId source; PrNodeId target; }; typedef PrEdgeVertex *PrEdgeVertexArray; typedef struct { PrPool vertex_pool; PrNodeArray nodes; PrEdgeVertexArray vertices; u64 capacity; u64 max_count_ever; u64 count; u64 free_head; } PrGraph; wp_intern b8 prGraphIsStaleId(const PrGraph *graph, PrNodeId id) { u64 generation = graph->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 PrNodeId prGraphGetNode(const PrGraph *graph, u64 index) { return (PrNodeId){ .index = index, .generation = graph->nodes[index].generation }; } 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->capacity = capacity; graph->max_count_ever = 0; graph->count = 0; 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; } } 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); PrEdgeVertex *src = &graph->vertices[from.index]; PrEdgeVertex *dst = &graph->vertices[to.index]; edge->source = from; edge->target = to; edge->next_forward = src->next_forward; edge->next_backward = dst->next_backward; src->next_forward = edge; dst->next_backward = edge; } 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 _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 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) { printf("==============INPUTS==============\n"); for (u64 i = 0; i < graph->max_count_ever; ++i) { PrNodeId id = prGraphGetNode(graph, i); if (!prGraphIsActiveNode(graph, id)) { continue; } PrEdgeVertex *vertex = &graph->vertices[i]; printf("%" PRIu64 ":", id.index + 1); if (!(vertex->next_backward)) { printf(" (none)"); } else { PrEdgeVertex *curr = vertex->next_backward; while (curr) { printf(" %" PRIu64, curr->source.index + 1); curr = curr->next_backward; } } printf("\n"); } printf("==============OUTPUTS==============\n"); for (u64 i = 0; i < graph->max_count_ever; ++i) { PrNodeId id = prGraphGetNode(graph, i); if (!prGraphIsActiveNode(graph, id)) { continue; } PrEdgeVertex *vertex = &graph->vertices[i]; printf("%" PRIu64 ":", id.index + 1); if (!(vertex->next_forward)) { printf(" (none)"); } else { PrEdgeVertex *curr = vertex->next_forward; while (curr) { printf(" %" PRIu64, curr->target.index + 1); curr = curr->next_forward; } } printf("\n"); } } i32 main(void) { WpAllocator arena = wpMemArenaAllocatorInitZero(MiB(16)); PrGraph graph = {0}; prGraphInit(&graph, &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); prGraphAddEdge(&graph, n1, n2); prGraphAddEdge(&graph, n1, n3); prGraphAddEdge(&graph, n1, n5); prGraphAddEdge(&graph, n2, n4); prGraphAddEdge(&graph, n3, n4); prGraphAddEdge(&graph, n3, n5); prGraphDump(&graph); prGraphRemoveNode(&graph, n3); printf("\n"); prGraphDump(&graph); PrNodeId n6 = prGraphAddNode(&graph, PR_NODE_TYPE_READ); prGraphAddEdge(&graph, n4, n6); printf("\n"); prGraphDump(&graph); prGraphRemoveNode(&graph, n5); printf("\n"); prGraphDump(&graph); PrNodeId n7 = prGraphAddNode(&graph, PR_NODE_TYPE_READ); prGraphAddEdge(&graph, n1, n7); prGraphAddEdge(&graph, n2, n7); printf("\n"); prGraphDump(&graph); PrNodeId n8 = prGraphAddNode(&graph, PR_NODE_TYPE_READ); prGraphAddEdge(&graph, n6, n8); prGraphAddEdge(&graph, n7, n8); printf("\n"); prGraphDump(&graph); wpMemArenaAllocatorDestroy(&arena); return 0; }