diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 614b264..0de9a3d 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -45,6 +45,8 @@ set(BreezeHeaders breeze/map/map.h breeze/map/tileset.h + breeze/utils/heap.c + breeze/utils/heap.h breeze/utils/string.h breeze/utils/tokenizer.h diff --git a/engine/breeze.h b/engine/breeze.h index 8a134d7..a8d1260 100644 --- a/engine/breeze.h +++ b/engine/breeze.h @@ -6,6 +6,7 @@ #include "breeze/math/vec2i.h" +#include "breeze/utils/heap.h" #include "breeze/utils/string.h" #include "breeze/utils/tokenizer.h" diff --git a/engine/breeze/utils/heap.c b/engine/breeze/utils/heap.c new file mode 100644 index 0000000..4e77230 --- /dev/null +++ b/engine/breeze/utils/heap.c @@ -0,0 +1,121 @@ +#include "heap.h" + +#include "../core/memory.h" + +typedef struct BzHeap { + i32 capacity; + i32 size; + i32 stride; + i32 weightOffset; +} BzHeap; + +#define HEAP_HEAD(heap) (((BzHeap *)heap) - 1) + +#define HEAP_LEFT(i) ((i32)(i * 2 + 1)) +#define HEAP_RIGHT(i) ((i32)(i * 2 + 2)) +#define HEAP_PARENT(i) ((i32) ((i - 1) / 2)) + +static void heapSiftUp(BzHeap *head, void *heap); +static void heapSiftDown(BzHeap *head, void *heap); + + + +void *_bzHeapNew(i32 startCapacity, i32 stride, i32 weightOffset) { + i32 numBytes = sizeof(BzHeap) + (startCapacity + 1) * stride; + BzHeap *heap = bzAlloc(numBytes); + heap[0] = (BzHeap) { + .capacity=startCapacity, + .size=0, + .stride=stride, + .weightOffset=weightOffset + }; + + return &heap[1]; +} + +void _bzHeapFree(void *heap) { + bzFree(HEAP_HEAD(heap)); +} + +i32 _bzHeapSize(void *heap) { + return HEAP_HEAD(heap)->size; +} +bool _bzHeapIsEmpty(void *heap) { + return HEAP_HEAD(heap)->size == 0; + +} +i32 _bzHeapPop(void *heap) { + BzHeap *head = HEAP_HEAD(heap); + BZ_ASSERT(head->size > 0); + + // Move first to index capacity (for output) + bzMemMove(((u8 *)heap) + head->capacity * head->stride, heap, head->stride); + head->size--; + if (head->size == 0) return 0; + + // Move last to index 0 + bzMemMove(heap, ((u8 *) heap) + head->size * head->stride, head->stride); + + heapSiftDown(head, heap); + + return head->capacity; +} +void _bzHeapPush(void *heap) { + BzHeap *head = HEAP_HEAD(heap); + BZ_ASSERT(head->size + 1 < head->capacity); + + void *item = ((u8 *)heap) + head->capacity * head->stride; + bzMemMove(((u8 *)heap) + head->size * head->stride, item, head->stride); + + head->size++; + heapSiftUp(head, heap); +} +i32 _bzHeapPushTmpIdx(void *heap) { + return HEAP_HEAD(heap)->capacity; +} + +static void heapSwap(BzHeap *head, void *heap, i32 aIdx, i32 bIdx) { + u8 *aItem = ((u8 *)heap) + aIdx * head->stride; + u8 *bItem = ((u8 *)heap) + bIdx * head->stride; + + u8 tmp[head->stride]; + bzMemMove(tmp, aItem, head->stride); + + bzMemMove(aItem, bItem, head->stride); + bzMemMove(bItem, tmp, head->stride); +} +static int heapCmp(BzHeap *head, void *heap, i32 lhs, i32 rhs) { + int *aWeight = (i32 *) (((u8 *)heap) + lhs * head->stride + head->weightOffset); + int *bWeight = (i32 *) (((u8 *)heap) + rhs * head->stride + head->weightOffset); + return (*aWeight) - (*bWeight); +} +static void heapSiftUp(BzHeap *head, void *heap) { + i32 idx = head->size - 1; + + while (idx >= 0) { + i32 parent = HEAP_PARENT(idx); + if (heapCmp(head, heap, idx, parent) >= 0) + break; + heapSwap(head, heap, idx, parent); + idx = parent; + } + +} +static void heapSiftDown(BzHeap *head, void *heap) { + i32 idx = 0; + + while (idx < head->size) { + i32 l = HEAP_LEFT(idx); + i32 r = HEAP_RIGHT(idx); + i32 smallest = idx; + if (l < head->size && heapCmp(head, heap, l, idx) < 0) + smallest = l; + if (r < head->size && heapCmp(head, heap, r, smallest) < 0) + smallest = r; + + if (smallest == idx) break; + + heapSwap(head, heap, idx, smallest); + idx = smallest; + } +} diff --git a/engine/breeze/utils/heap.h b/engine/breeze/utils/heap.h new file mode 100644 index 0000000..c8f752e --- /dev/null +++ b/engine/breeze/utils/heap.h @@ -0,0 +1,28 @@ +#ifndef BREEZE_HEAP_H +#define BREEZE_HEAP_H + +#include "../defines.h" + +void *_bzHeapNew(i32 startCapacity, i32 stride, i32 weightOffset); +void _bzHeapFree(void *heap); + +i32 _bzHeapSize(void *heap); +bool _bzHeapIsEmpty(void *heap); +i32 _bzHeapPop(void *heap); +void _bzHeapPush(void *heap); +i32 _bzHeapPushTmpIdx(void *heap); + +#define bzHeapNew(T, n) (T *) ((T *)_bzHeapNew((n), sizeof(T), offsetof(T, weight))) +#define bzHeapFree(heap) _bzHeapFree((void *) (heap)) + +#define bzHeapSize(heap) _bzHeapSize((void *) (heap)) +#define bzHeapIsEmpty(heap) _bzHeapIsEmpty((void *) (heap)) +#define bzHeapPop(heap) ((heap)[_bzHeapPop((void *) (heap))]) +#define bzHeapPush(heap, item) do { \ + void *h = (void *) (heap); \ + (heap)[_bzHeapPushTmpIdx(h)] = (item); \ + _bzHeapPush(h); \ +} while(0) + + +#endif //BREEZE_HEAP_H diff --git a/engine/tests/CMakeLists.txt b/engine/tests/CMakeLists.txt index 0d50248..0bb8758 100644 --- a/engine/tests/CMakeLists.txt +++ b/engine/tests/CMakeLists.txt @@ -7,3 +7,6 @@ target_link_libraries(window_test LINK_PRIVATE Breeze) add_executable(cute_tiled_test cute_tiled_test.c) target_link_libraries(cute_tiled_test LINK_PRIVATE Breeze) + +add_executable(heap_test heap_test.c) +target_link_libraries(heap_test LINK_PRIVATE Breeze) \ No newline at end of file diff --git a/engine/tests/heap_test.c b/engine/tests/heap_test.c new file mode 100644 index 0000000..f7776c0 --- /dev/null +++ b/engine/tests/heap_test.c @@ -0,0 +1,36 @@ +#include + +#include + +int main() { + bzLoggerInit(); + typedef struct Node { + i32 weight; + } Node; + + Node *heap = bzHeapNew(Node, 20); + bzHeapPush(heap, (Node) {3}); + bzHeapPush(heap, (Node) {2}); + bzHeapPush(heap, (Node) {1}); + bzHeapPush(heap, (Node) {15}); + bzHeapPush(heap, (Node) {2}); + bzHeapPush(heap, (Node) {8}); + bzHeapPush(heap, (Node) {10}); + + bzHeapPop(heap); + + for (int i = 0; i < bzHeapSize(heap); i++) { + printf("%d ", heap[i].weight); + } + printf("\n\n"); + + while (!bzHeapIsEmpty(heap)) { + Node node = bzHeapPop(heap); + printf("%d\n", node.weight); + } + + bzHeapFree(heap); + + + return 0; +} \ No newline at end of file