354 lines
8.9 KiB
C
354 lines
8.9 KiB
C
// vim:fileencoding=utf-8:foldmethod=marker
|
|
|
|
#include "../src/wapp/wapp.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* 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;
|
|
}
|