pq.c171 lines · 4.2 KB · raw
1#include "pq.h"
2#include <stddef.h>
3#include <stdint.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8struct pq_entry {
9    uint64_t priority;
10    unsigned char data[];
11};
12
13struct pq {
14    size_t data_size;
15    size_t capacity;
16    size_t size;
17    unsigned char * entries; // Array of `struct pq_entry`
18};
19
20#define PQ_PARENT(I)    (I == 0 ? 0 : ((I - 1) / 2))
21#define PQ_LEFT(I)      (I * 2 + 1)
22#define PQ_RIGHT(I)     (I * 2 + 2)
23
24#define PQ_ENTRY(PQ, I)  ((struct pq_entry *) &(PQ)->entries[I * PQ_ENTRY_SIZE(PQ)])
25#define PQ_ENTRY_SIZE(PQ) (sizeof(uint64_t) + (PQ)->data_size)
26
27struct pq *
28pq_create(size_t data_size) {
29    struct pq * pq = calloc(1, sizeof *pq);
30    if (pq == NULL) {
31        return NULL;
32    }
33
34    pq->data_size = data_size;
35    pq->capacity = 1024;
36    pq->size = 0;
37    pq->entries = calloc(pq->capacity, PQ_ENTRY_SIZE(pq));
38    if (pq->entries == NULL) {
39        return free(pq), NULL;
40    }
41
42    return pq;
43}
44
45void
46pq_destroy(struct pq * pq) {
47    free(pq->entries);
48    free(pq);
49}
50
51void
52pq_clear(struct pq * pq) {
53    pq->size = 0;
54
55#ifdef PQ_DEBUG
56    memset(pq->entries, 0, PQ_ENTRY_SIZE(pq) * pq->capacity);
57#endif
58}
59
60int
61pq_push(struct pq * pq, uint64_t priority, const void * data) {
62    size_t idx = pq->size++;
63
64    if (pq->size > pq->capacity) {
65        size_t new_capacity = pq->capacity * 2;
66        void * new_entries = realloc(pq->entries, new_capacity * PQ_ENTRY_SIZE(pq));
67        if (new_entries == NULL) {
68            return -1;
69        }
70        pq->entries = new_entries;
71        memset(PQ_ENTRY(pq, pq->capacity), 0, PQ_ENTRY_SIZE(pq) * pq->capacity);
72        pq->capacity = new_capacity;
73    }
74
75    struct pq_entry * entry = PQ_ENTRY(pq, idx);
76    while (idx > 0) {
77        size_t parent_idx = PQ_PARENT(idx);
78        struct pq_entry * parent = PQ_ENTRY(pq, parent_idx);
79        if (parent->priority <= priority) {
80            break;
81        }
82
83        memcpy(entry, parent, PQ_ENTRY_SIZE(pq));
84        idx = parent_idx;
85        entry = parent;
86    }
87
88    entry->priority = priority;
89    if (data != NULL) {
90        memcpy(entry->data, data, pq->data_size);
91    }
92
93    return 0;
94}
95
96int
97pq_pop(struct pq * pq, uint64_t * priority_out, void * data_out) {
98    if (pq->size == 0)
99        return -1;
100
101    size_t idx = 0;
102    struct pq_entry * entry = PQ_ENTRY(pq, idx);
103    if (priority_out != NULL) {
104        *priority_out = entry->priority;
105    }
106    if (data_out != NULL) {
107        memcpy(data_out, entry->data, pq->data_size);
108    }
109
110    size_t child_idx = --pq->size;
111    uint64_t priority = PQ_ENTRY(pq, child_idx)->priority;
112    while (idx < pq->size) {
113        size_t left_idx = PQ_LEFT(idx); 
114        size_t right_idx = PQ_RIGHT(idx); 
115
116        size_t smallest_idx = idx;
117        size_t smallest_priority = priority;
118        if (left_idx < pq->size) {
119            struct pq_entry * left_entry = PQ_ENTRY(pq, left_idx);
120            if (left_entry->priority < smallest_priority) {
121                smallest_priority = left_entry->priority;
122                smallest_idx = left_idx;
123            }
124        }
125        if (right_idx < pq->size) {
126            struct pq_entry * right_entry = PQ_ENTRY(pq, right_idx);
127            if (right_entry->priority < smallest_priority) {
128                smallest_priority = right_entry->priority;
129                smallest_idx = right_idx;
130            }
131        }
132        if (smallest_idx == idx) {
133            break;
134        }
135        memcpy(PQ_ENTRY(pq, idx), PQ_ENTRY(pq, smallest_idx), PQ_ENTRY_SIZE(pq));
136        idx = smallest_idx;
137    }
138    if (child_idx != idx) {
139        memcpy(PQ_ENTRY(pq, idx), PQ_ENTRY(pq, child_idx), PQ_ENTRY_SIZE(pq));
140    }
141
142#ifdef PQ_DEBUG
143    for (size_t i = 0; i < pq->size; i++) {
144        entry = PQ_ENTRY(pq, i);
145        if (entry->priority < *priority_out)
146            printf("Found entry with priority %zu < %zu\n", entry->priority, *priority_out);
147    }
148#endif
149
150    return 0;
151}
152
153size_t
154pq_size(struct pq * pq) {
155    return pq->size;
156}
157
158#ifdef PQ_DEBUG
159void
160pq_print(struct pq * pq) {
161    for (size_t i = 0; i < pq->size; i++) {
162        struct pq_entry * entry = PQ_ENTRY(pq, i);
163        struct pq_entry * parent = PQ_ENTRY(pq, PQ_PARENT(i));
164        if (parent->priority > entry->priority) {
165            printf("!");
166        }
167        printf("%lu ", entry->priority);
168    }
169    printf("\n");
170}
171#endif