Implemented a priority heap.
Added tests for the priority heap.
Add a "default" lcg generator function.
Tweak hash_map to use the default lcg generator function.
`clang-format`.
src/parser.c
src/data_structures/linked_list.c
src/data_structures/hash_map.c
+ src/data_structures/priority_heap.c
src/algorithms/linear_congruential_gen.c
)
src/data_structures/test.c
src/data_structures/linked_list.c
src/data_structures/hash_map.c
+ src/data_structures/priority_heap.c
src/algorithms/linear_congruential_gen.c
)
../src/parser.c \
../src/algorithms/linear_congruential_gen.c \
../src/data_structures/linked_list.c \
- ../src/data_structures/hash_map.c
+ ../src/data_structures/hash_map.c \
+ ../src/data_structures/priority_heap.c
HEADERS = \
../src/parser.h \
../src/algorithms/linear_congruential_gen.h \
../src/data_structures/linked_list.h \
../src/data_structures/hash_map.h \
+ ../src/data_structures/priority_heap.h \
../src/platforms.h
OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(patsubst %.c,%.c.o,${SOURCES})))
// "m" is implicity 2^64.
return seed * a + c;
}
+
+unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed) {
+ // "m" is implicity 2^64.
+ return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
+}
unsigned long long a,
unsigned long long c);
+unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed);
+
#endif
seed += temp;
}
- return simple_archiver_algo_lcg(seed, SC_ALGO_LCG_DEFAULT_A,
- SC_ALGO_LCG_DEFAULT_C);
+ return simple_archiver_algo_lcg_defaults(seed);
}
/// Returns 0 on success.
--- /dev/null
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `priority_heap.c` is the source for a priority heap implementation.
+ */
+
+#include "priority_heap.h"
+
+#include <stdlib.h>
+
+void simple_archiver_priority_heap_internal_realloc(
+ SDArchiverPHeap **priority_heap) {
+ SDArchiverPHeap *new_priority_heap = malloc(sizeof(SDArchiverPHeap));
+
+ new_priority_heap->capacity = (*priority_heap)->capacity * 2;
+ new_priority_heap->size = 0;
+
+ new_priority_heap->nodes =
+ calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
+
+ for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
+ if ((*priority_heap)->nodes[idx].is_valid != 0) {
+ simple_archiver_priority_heap_insert(
+ &new_priority_heap, (*priority_heap)->nodes[idx].priority,
+ (*priority_heap)->nodes[idx].data,
+ (*priority_heap)->nodes[idx].data_cleanup_fn);
+ (*priority_heap)->nodes[idx].is_valid = 0;
+ }
+ }
+
+ simple_archiver_priority_heap_free(priority_heap);
+
+ *priority_heap = new_priority_heap;
+}
+
+SDArchiverPHeap *simple_archiver_priority_heap_init(void) {
+ SDArchiverPHeap *priority_heap = malloc(sizeof(SDArchiverPHeap));
+
+ priority_heap->capacity = SC_SA_DS_PRIORITY_HEAP_START_SIZE;
+ priority_heap->size = 0;
+
+ priority_heap->nodes =
+ calloc(priority_heap->capacity, sizeof(SDArchiverPHNode));
+
+ return priority_heap;
+}
+
+void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
+ if (priority_heap && *priority_heap) {
+ for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
+ if ((*priority_heap)->nodes[idx].is_valid != 0) {
+ if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
+ (*priority_heap)
+ ->nodes[idx]
+ .data_cleanup_fn((*priority_heap)->nodes[idx].data);
+ } else {
+ free((*priority_heap)->nodes[idx].data);
+ }
+ (*priority_heap)->nodes[idx].is_valid = 0;
+ }
+ }
+
+ free((*priority_heap)->nodes);
+ free(*priority_heap);
+ *priority_heap = NULL;
+ }
+}
+
+void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
+ long long priority, void *data,
+ void (*data_cleanup_fn)(void *)) {
+ if (!priority_heap || !*priority_heap) {
+ return;
+ }
+
+ if ((*priority_heap)->size + 1 >= (*priority_heap)->capacity) {
+ simple_archiver_priority_heap_internal_realloc(priority_heap);
+ }
+
+ unsigned int hole = (*priority_heap)->size + 1;
+
+ while (hole > 1 && priority < (*priority_heap)->nodes[hole / 2].priority) {
+ (*priority_heap)->nodes[hole] = (*priority_heap)->nodes[hole / 2];
+ hole /= 2;
+ }
+
+ (*priority_heap)->nodes[hole].priority = priority;
+ (*priority_heap)->nodes[hole].data = data;
+ (*priority_heap)->nodes[hole].data_cleanup_fn = data_cleanup_fn;
+ (*priority_heap)->nodes[hole].is_valid = 1;
+
+ ++(*priority_heap)->size;
+}
+
+void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap) {
+ if (priority_heap && priority_heap->size != 0) {
+ return priority_heap->nodes[1].data;
+ }
+
+ return NULL;
+}
+
+void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap) {
+ if (!priority_heap || priority_heap->size == 0) {
+ return NULL;
+ }
+
+ void *data = priority_heap->nodes[1].data;
+ priority_heap->nodes[1].is_valid = 0;
+
+ SDArchiverPHNode end = priority_heap->nodes[priority_heap->size];
+ priority_heap->nodes[priority_heap->size].is_valid = 0;
+
+ unsigned int hole = 1;
+ while (hole * 2 + 1 <= priority_heap->size) {
+ if (priority_heap->nodes[hole * 2].is_valid != 0 &&
+ priority_heap->nodes[hole * 2 + 1].is_valid != 0) {
+ if (end.priority < priority_heap->nodes[hole * 2].priority &&
+ end.priority < priority_heap->nodes[hole * 2 + 1].priority) {
+ break;
+ }
+ if (priority_heap->nodes[hole * 2].priority <
+ priority_heap->nodes[hole * 2 + 1].priority) {
+ priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
+ hole = hole * 2;
+ } else {
+ priority_heap->nodes[hole] = priority_heap->nodes[hole * 2 + 1];
+ hole = hole * 2 + 1;
+ }
+ } else if (priority_heap->nodes[hole * 2].is_valid != 0) {
+ if (end.priority < priority_heap->nodes[hole * 2].priority) {
+ break;
+ }
+ priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
+ hole = hole * 2;
+ break;
+ } else {
+ break;
+ }
+ }
+
+ priority_heap->nodes[hole] = end;
+
+ --priority_heap->size;
+
+ return data;
+}
--- /dev/null
+/*
+ * Copyright 2024 Stephen Seo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * `priority_heap.h` is the header for a priority heap implementation.
+ */
+
+#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
+#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
+
+#define SC_SA_DS_PRIORITY_HEAP_START_SIZE 32
+
+typedef struct SDArchiverPHNode {
+ long long priority;
+ void *data;
+ void (*data_cleanup_fn)(void *);
+ /// Is non-zero if valid.
+ int is_valid;
+} SDArchiverPHNode;
+
+typedef struct SDArchiverPHeap {
+ SDArchiverPHNode *nodes;
+ unsigned long long capacity;
+ unsigned long long size;
+} SDArchiverPHeap;
+
+SDArchiverPHeap *simple_archiver_priority_heap_init(void);
+void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
+
+/// If data_cleanup_fn is NULL, then "free()" is used on data when popped or
+/// freed.
+void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
+ long long priority, void *data,
+ void (*data_cleanup_fn)(void *));
+
+/// Returns NULL if empty or if priority_heap is NULL.
+void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap);
+
+/// Returns NULL if empty or if priority_heap is NULL.
+/// When data is popped, the data_cleanup_fn is ignored and the user must take
+/// ownership of the returned data pointer.
+void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap);
+
+#endif
#include <stdlib.h>
#include <string.h>
+#include "../algorithms/linear_congruential_gen.h"
#include "hash_map.h"
#include "linked_list.h"
+#include "priority_heap.h"
static int checks_checked = 0;
static int checks_passed = 0;
simple_archiver_hash_map_free(&hash_map);
}
+ // Test PriorityHeap.
+ {
+ SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
+ simple_archiver_priority_heap_free(&priority_heap);
+
+ priority_heap = simple_archiver_priority_heap_init();
+
+ // Just 3 elements.
+ for (unsigned int idx = 0; idx < 3; ++idx) {
+ unsigned int *data = malloc(sizeof(unsigned int));
+ *data = idx;
+ simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+ }
+ for (unsigned int idx = 0; idx < 3; ++idx) {
+ unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+ CHECK_TRUE(*data == idx);
+ if (*data != idx) {
+ printf("idx is %u, data is %u\n", idx, *data);
+ }
+ data = simple_archiver_priority_heap_pop(priority_heap);
+ CHECK_TRUE(*data == idx);
+ if (*data != idx) {
+ printf("idx is %u, data is %u\n", idx, *data);
+ }
+ free(data);
+ }
+
+ // 100 elements.
+ unsigned int max = 100;
+
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ unsigned int *data = malloc(sizeof(unsigned int));
+ *data = idx;
+ simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+ }
+
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+ CHECK_TRUE(*data == idx);
+ data = simple_archiver_priority_heap_pop(priority_heap);
+ CHECK_TRUE(*data == idx);
+ free(data);
+ }
+
+ // Insert in reverse order.
+ for (unsigned int idx = max; idx-- > 0;) {
+ unsigned int *data = malloc(sizeof(unsigned int));
+ *data = idx;
+ simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+ }
+
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+ CHECK_TRUE(*data == idx);
+ data = simple_archiver_priority_heap_pop(priority_heap);
+ CHECK_TRUE(*data == idx);
+ free(data);
+ }
+
+ // Insert in random order.
+ unsigned int *array = malloc(sizeof(unsigned int) * max);
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ array[idx] = idx;
+ }
+
+ // Deterministic randomization.
+ for (unsigned int idx = max - 1; idx-- > 0;) {
+ unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
+ (unsigned long long)(idx + 1);
+ if (idx != other_idx) {
+ unsigned int temp = array[max - 1];
+ array[max - 1] = array[other_idx];
+ array[other_idx] = temp;
+ }
+ }
+
+ // Insert the deterministically randomized array.
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ simple_archiver_priority_heap_insert(&priority_heap, array[idx],
+ array + idx, no_free_fn);
+ }
+
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
+ CHECK_TRUE(*data == idx);
+ if (*data != idx) {
+ printf("idx is %u, data is %u\n", idx, *data);
+ }
+ data = simple_archiver_priority_heap_pop(priority_heap);
+ CHECK_TRUE(*data == idx);
+ if (*data != idx) {
+ printf("idx is %u, data is %u\n", idx, *data);
+ }
+ }
+ free(array);
+
+ simple_archiver_priority_heap_free(&priority_heap);
+
+ // Insert, don't pop, do free, for memcheck.
+ priority_heap = simple_archiver_priority_heap_init();
+ for (unsigned int idx = 0; idx < max; ++idx) {
+ unsigned int *data = malloc(sizeof(unsigned int));
+ *data = idx;
+ simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
+ }
+ simple_archiver_priority_heap_free(&priority_heap);
+ }
+
printf("Checks checked: %u\n", checks_checked);
printf("Checks passed: %u\n", checks_passed);
return checks_passed == checks_checked ? 0 : 1;
#define SIMPLE_ARCHIVER_PLATFORM_UNKNOWN 0
#if defined __COSMOPOLITAN__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN
#elif defined _WIN32
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_WINDOWS
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_WINDOWS
#elif defined __APPLE__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_MAC
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_MAC
#elif defined __linux__
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_LINUX
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_LINUX
#else
-# define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_UNKNOWN
+#define SIMPLE_ARCHIVER_PLATFORM SIMPLE_ARCHIVER_PLATFORM_UNKNOWN
#endif