WIP: heap implementation #3

Closed
Jef Roosens wants to merge 6 commits from Chewing_Bever/libvieter:min-heap into main
11 changed files with 227 additions and 10 deletions

2
.clangd 100644
View File

@ -0,0 +1,2 @@
CompileFlags:
Add: -ferror-limit=0

36
.woodpecker.yml 100644
View File

@ -0,0 +1,36 @@
variables:
&image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17'
matrix:
PLATFORM:
- 'linux/amd64'
- 'linux/arm64'
branches:
exclude: [ main ]
platform: ${PLATFORM}
pipeline:
lint:
image: *image
pull: true
commands:
- make lint
when:
event: [push, pull_request]
build:
image: *image
commands:
- make
- make clean
- CFLAGS='-O3' make
when:
event: [push, pull_request]
test:
image: *image
commands:
- make test
when:
event: [push, pull_request]

View File

@ -25,7 +25,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# object file is also recompiled if only a header is changed.
# -MP: generate a dummy target for every header file (according to the docs it
# prevents some errors when removing header files)
CFLAGS ?= -MMD -MP -Wall -Werror -Wextra
CFLAGS ?= -MMD -MP -Wall -Wextra
CFLAGS += $(INC_FLAGS)
.PHONY: all
@ -56,10 +56,15 @@ build-test: $(BINS_TEST)
$(BINS_TEST): %: %.c.o $(OBJS)
$(CC) $^ -o $@
# Each test includes the test directory, which contains the acutest header file
# Allow with the include directory, each test includes $(TEST_DIR) (which
# contains the acutest.h header file), and the src directory of the module it's
# testing. This allows tests to access internal methods, which aren't publicly
# exposed.
$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@
$(CC) $(CFLAGS) -I$(TEST_DIR) \
-I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \
-c $< -o $@
# =====MAINTENANCE=====
.PHONY: lint

View File

@ -1,15 +1,12 @@
# libvieter
This library powers part of Vieter, most noteably the sections that can easily
This library powers part of Vieter, most notably the sections that can easily
be implemented in C (or just parts I want to implement in C because it's fun).
The goal of this library is to be completely self-contained, meaning any
required data structures have to be implemented as well. It can only depend on
the C standard libraries.
The goal of this library is to be as self-contained as possible; data
structures should be implemented manually if possible.
Currently it contains the following:
* Cron expression parser & next time calculator
See the [source code](/src) for the list of modules.
## Development
@ -18,6 +15,27 @@ Currently it contains the following:
Everything is handled by the provided Makefile. To compile the static library,
simply run `make`.
### Project structure
Each module has its own subdirectory inside `src`, e.g. `src/cron`. This
directory contains the actual implementation of a module, along with any
internally used header files. Each internal function should be defined in a
header file, as to make testing these possible.
Each module should also have its own header file inside the `include`
directory. This header file defines the public API that the library exposes for
this specific module.
Any code in a module may only import internal headers from that module, along
with any of the public API header files. Modules should not depend on each
other's internal implementationns.
Each module should contain a README describing its contents.
All file names, function names... (even internals) should follow snake case
convention and have a prefix unique to that module, starting with `vieter_`.
For example, the `cron` modules uses the `vieter_cron_` prefix for everything.
### Testing
This library uses [Acutest](https://github.com/mity/acutest) for its tests.
@ -25,6 +43,10 @@ Tests should be placed in the `test` subdirectory, further divided into
directories that correspond those in `src`. Test files should begin with
`test_`, and their format should follow the expected format for Acutest.
Each `test_` is compiled separately into a binary, linked with libvieter. A
test file can import any of the public API header files, along with any header
files defined in its respective module. This allows testing internal functions.
To run the tests, simply run `make test`. If you wish to only run a specific
test binary, you can find them in `build/test`.

View File

@ -0,0 +1,27 @@
#ifndef VIETER_HEAP
#define VIETER_HEAP
#include <stddef.h>
#include <stdint.h>
typedef struct vieter_heap vieter_heap;
typedef enum vieter_heap_error {
vieter_heap_ok = 0,
vieter_heap_empty = 1
} vieter_heap_error;
vieter_heap *vieter_heap_init();
void vieter_heap_free(vieter_heap *heap);
uint64_t vieter_heap_size(vieter_heap *heap);
vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key,
void *data);
vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap);
vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap);
#endif

View File

@ -0,0 +1,38 @@
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#include <stdlib.h>
struct vieter_heap {
vieter_heap_tree *tree;
};
vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); }
uint64_t vieter_heap_size(vieter_heap *heap) { return 0; }
void vieter_heap_free(vieter_heap *heap) {}
vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key,
void *data) {
vieter_heap_node *new_node = vieter_heap_node_init();
new_node->key = key;
new_node->data = data;
vieter_heap_tree *new_tree = vieter_heap_tree_init(new_node, NULL, 1);
if (heap->tree == NULL) {
heap->tree = new_tree;
} else {
heap->tree = vieter_heap_tree_merge(heap->tree, new_tree);
}
return vieter_heap_ok;
}
vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) {
return vieter_heap_ok;
}
vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) {
return vieter_heap_ok;
}

View File

@ -0,0 +1,38 @@
#include "vieter_heap_tree.h"
vieter_heap_node *vieter_heap_node_init() {
return calloc(1, sizeof(vieter_heap_node));
}
vieter_heap_node *vieter_heap_node_merge_same_order(vieter_heap_node *root_a,
vieter_heap_node *root_b) {
vieter_heap_node *new_root, *new_child;
if (root_a->key <= root_b->key) {
new_root = root_a;
new_child = root_b;
} else {
new_root = root_b;
new_child = root_a;
}
new_root->next_largest_order = new_root->largest_order;
new_root->largest_order = new_child;
return new_root;
}
vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root,
vieter_heap_tree *next,
uint64_t order) {
vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree));
tree->root = root;
tree->next = next;
tree->order = order;
return tree;
}
vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a,
vieter_heap_tree *tree_b) {}

View File

@ -0,0 +1,30 @@
#ifndef VIETER_HEAP_TREE
#define VIETER_HEAP_TREE
#include <stdint.h>
#include <stdlib.h>
typedef struct vieter_heap_node {
uint64_t key;
void *data;
struct vieter_heap_node *largest_order;
struct vieter_heap_node *next_largest_order;
} vieter_heap_node;
vieter_heap_node *vieter_heap_node_init();
void vieter_heap_node_free(vieter_heap_node *node);
vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b);
typedef struct vieter_heap_tree {
uint64_t order;
vieter_heap_node *root;
struct vieter_heap_tree *next;
} vieter_heap_tree;
vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint64_t order);
vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b);
#endif

View File

@ -0,0 +1,19 @@
#include "acutest.h"
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#define TEST_SIZE(heap, size) \
TEST_CHECK(vieter_heap_size(heap) == size); \
TEST_MSG("Size: %zu", vieter_heap_size(heap))
void test_init() {
vieter_heap *heap = vieter_heap_init();
TEST_CHECK(heap != NULL);
TEST_SIZE(heap, 0);
vieter_heap_free(heap);
}
TEST_LIST = {
{"test_init", test_init},
{NULL, NULL}
};