// vim:fileencoding=utf-8:foldmethod=marker #include "../src/wapp/wapp.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 (u32)-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 { u32 index; u32 generation; } PrNodeId; typedef struct { union { WpStr8 path; f32 blur; f32 gain; } params; PrNodeType type; u32 generation; u32 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; PrEdgeVertex *prev; PrNodeId source; PrNodeId target; }; typedef PrEdgeVertex *PrEdgeVertexArray; typedef struct { PrPool vertex_pool; PrNodeArray nodes; PrEdgeVertexArray vertices; u64 capacity; u64 max_count_ever; u64 count; u32 free_head; } PrGraph; wp_intern b8 prGraphIsStaleId(const PrGraph *graph, PrNodeId id) { u32 generation = graph->nodes[id.index].generation; return id.generation != generation; } wp_intern b8 prGraphIsActiveNode(const PrGraph *graph, PrNodeId id) { u32 next_free = graph->nodes[id.index].next_free; return !prGraphIsStaleId(graph, id) && next_free == INVALID_NODE_INDEX; } wp_intern PrNodeId prGraphGetNode(const PrGraph *graph, u32 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; } 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 = src->next; edge->prev = dst->prev; src->next = edge; dst->prev = edge; } wp_intern PrNodeId prGraphAddNode(PrGraph *graph, PrNodeType type) { u32 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; PrEdgeVertex *prev = NULL; while (curr) { if (curr == edge) { if (prev) { prev->next = curr->next; } else { src->next = curr->next; } return; } prev = curr; curr = curr->next; } } wp_intern void _unlinkBackward(PrGraph *graph, PrNodeId to, PrEdgeVertex *edge) { if (!prGraphIsActiveNode(graph, to)) { return; } PrEdgeVertex *dst = &graph->vertices[to.index]; PrEdgeVertex *curr = dst->prev; PrEdgeVertex *prev = NULL; while (curr) { if (curr == edge) { if (prev) { prev->prev = curr->prev; } else { dst->prev = curr->prev; } return; } prev = curr; curr = curr->prev; } } wp_intern void prGraphRemoveNode(PrGraph *graph, PrNodeId id) { if (!prGraphIsActiveNode(graph, id)) { return; } PrNode *node = &graph->nodes[id.index]; u32 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) && !(vertex->prev)) { goto REMOVE_NODE_UNSET_POINTERS; } PrEdgeVertex *curr = vertex->next; while (curr) { PrEdgeVertex *next = curr->next; _unlinkBackward(graph, curr->target, curr); prPoolFree(&graph->vertex_pool, curr); curr = next; } curr = vertex->prev; while (curr) { PrEdgeVertex *prev = curr->prev; _unlinkForward(graph, curr->source, curr); prPoolFree(&graph->vertex_pool, curr); curr = prev; } REMOVE_NODE_UNSET_POINTERS: vertex->next = vertex->prev = 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("%u:", id.index + 1); if (!(vertex->prev)) { printf(" (none)"); } else { PrEdgeVertex *curr = vertex->prev; while (curr) { printf(" %u", curr->source.index + 1); curr = curr->prev; } } 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("%u:", id.index + 1); if (!(vertex->next)) { printf(" (none)"); } else { PrEdgeVertex *curr = vertex->next; while (curr) { printf(" %u", curr->target.index + 1); curr = curr->next; } } 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; }