diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..ef516ad --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: -ferror-limit=0 diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..a7e8fe3 --- /dev/null +++ b/.woodpecker.yml @@ -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] diff --git a/Makefile b/Makefile index 238d4d3..30b6a31 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 724ba54..9381fb1 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/include/vieter_heap.h b/include/vieter_heap.h new file mode 100644 index 0000000..c7a9706 --- /dev/null +++ b/include/vieter_heap.h @@ -0,0 +1,27 @@ +#ifndef VIETER_HEAP +#define VIETER_HEAP + +#include +#include + +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 diff --git a/src/cron/expression.c b/src/cron/vieter_cron_expression.c similarity index 100% rename from src/cron/expression.c rename to src/cron/vieter_cron_expression.c diff --git a/src/cron/parse.c b/src/cron/vieter_cron_parse.c similarity index 100% rename from src/cron/parse.c rename to src/cron/vieter_cron_parse.c diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c new file mode 100644 index 0000000..6d543df --- /dev/null +++ b/src/heap/vieter_heap.c @@ -0,0 +1,38 @@ +#include "vieter_heap.h" +#include "vieter_heap_tree.h" + +#include + +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; +} diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c new file mode 100644 index 0000000..e65fb56 --- /dev/null +++ b/src/heap/vieter_heap_tree.c @@ -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) {} diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h new file mode 100644 index 0000000..18808e1 --- /dev/null +++ b/src/heap/vieter_heap_tree.h @@ -0,0 +1,30 @@ +#ifndef VIETER_HEAP_TREE +#define VIETER_HEAP_TREE + +#include +#include + +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 diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c new file mode 100644 index 0000000..832404d --- /dev/null +++ b/test/heap/test_heap.c @@ -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} +};