feat: initial version
This commit is contained in:
22
.clang-format
Normal file
22
.clang-format
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignEscapedNewlines: DontAlign
|
||||||
|
AlignOperands: DontAlign
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: TopLevel
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBraces: WebKit
|
||||||
|
IndentCaseLabels: false
|
||||||
|
TabWidth: 4
|
||||||
|
IndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
ColumnLimit: 0
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
SpaceBeforeParens: false
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bin/
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
# ni-linkedlist
|
# ni-linkedlist
|
||||||
|
|
||||||
Linked List of the NI-C system
|
Linked List of the NI-C system
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [ni-arena](https://codecave.studiosi.es/studiosi/ni-arena) Arena Allocator of the NI-C system
|
||||||
|
|
||||||
|
## Test Dependencies
|
||||||
|
|
||||||
|
- [ni-leakdetector](https://codecave.studiosi.es/studiosi/ni-leakdetector) leak detector of the NI-C system
|
||||||
19
makefile
Normal file
19
makefile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
RELEASE_flags=-DNDEBUG -O2 -g0
|
||||||
|
DEBUG_flags=-std=c23 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -O0
|
||||||
|
COMPILER=clang
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -fr bin
|
||||||
|
|
||||||
|
format:
|
||||||
|
clang-format -i test/nilinkedlist_test.c
|
||||||
|
clang-format -i nilinkedlist.h
|
||||||
|
|
||||||
|
test: bin/test
|
||||||
|
bin/test/nilinkedlist_test
|
||||||
|
|
||||||
|
.PHONY: clean format test
|
||||||
|
|
||||||
|
bin/test: test/nilinkedlist_test.c
|
||||||
|
mkdir -p bin/test
|
||||||
|
${COMPILER} ${DEBUG_flags} test/nilinkedlist_test.c -o bin/test/nilinkedlist_test
|
||||||
188
nilinkedlist.h
Normal file
188
nilinkedlist.h
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
#include "../ni-arena/niarena.h"
|
||||||
|
|
||||||
|
#define NILINKEDLIST_IMPLEMENTATION
|
||||||
|
|
||||||
|
typedef struct _NILinkedListNode {
|
||||||
|
void *content;
|
||||||
|
struct _NILinkedListNode *next;
|
||||||
|
} NILinkedListNode;
|
||||||
|
|
||||||
|
typedef struct _NILinkedList {
|
||||||
|
size_t count;
|
||||||
|
struct _NILinkedListNode *first;
|
||||||
|
} NILinkedList;
|
||||||
|
|
||||||
|
typedef void(NILinkedListVisitorFunctor)(const void *);
|
||||||
|
typedef void *(NILinkedListMapFunctor)(const void *, NIArena **arena);
|
||||||
|
typedef void(NILinkedListReduceFunctor)(void **accumulator, const void *current);
|
||||||
|
|
||||||
|
NILinkedList *nilinkedlist_new(NIArena **arena);
|
||||||
|
bool nilinkedlist_empty(NILinkedList **self);
|
||||||
|
void nilinkedlist_push(NILinkedList **self, NIArena **arena, void *content);
|
||||||
|
const void *nilinkedlist_pop(NILinkedList **self);
|
||||||
|
void nilinkedlist_emplace(NILinkedList **self, NIArena **arena, void *content, const size_t idx);
|
||||||
|
const void *nilinkedlist_peek(NILinkedList *self, const size_t idx);
|
||||||
|
void nilinkedlist_enqueue(NILinkedList **self, NIArena **arena, void *content);
|
||||||
|
const void *nilinkedlist_dequeue(NILinkedList **self);
|
||||||
|
void nilinkedlist_traverse(NILinkedList *list, NILinkedListVisitorFunctor f);
|
||||||
|
void nilinkedlist_map(NILinkedList *list, NIArena **arena, NILinkedListMapFunctor f, NILinkedList **out);
|
||||||
|
void nilinkedlist_reduce(NILinkedList *list, NILinkedListReduceFunctor f, void **out);
|
||||||
|
|
||||||
|
#ifdef NILINKEDLIST_IMPLEMENTATION
|
||||||
|
|
||||||
|
NILinkedList *
|
||||||
|
nilinkedlist_new(NIArena **arena)
|
||||||
|
{
|
||||||
|
NILinkedList *list =
|
||||||
|
(NILinkedList *)niarena_alloc(arena, sizeof(NILinkedList));
|
||||||
|
list->count = 0;
|
||||||
|
list->first = NULL;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
NILinkedListNode *
|
||||||
|
nilinkedlist_node_new(NIArena **arena)
|
||||||
|
{
|
||||||
|
NILinkedListNode *node =
|
||||||
|
(NILinkedListNode *)niarena_alloc(arena, sizeof(NILinkedListNode));
|
||||||
|
node->next = NULL;
|
||||||
|
node->content = NULL;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nilinkedlist_empty(NILinkedList **self)
|
||||||
|
{
|
||||||
|
return (*self)->count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_push(NILinkedList **self, NIArena **arena, void *content)
|
||||||
|
{
|
||||||
|
NILinkedList *list = *self;
|
||||||
|
NILinkedListNode *node = nilinkedlist_node_new(arena);
|
||||||
|
node->content = content;
|
||||||
|
if(list->first == NULL) {
|
||||||
|
list->first = node;
|
||||||
|
} else {
|
||||||
|
NILinkedListNode *current_node = list->first;
|
||||||
|
while(current_node->next != NULL) {
|
||||||
|
current_node = current_node->next;
|
||||||
|
}
|
||||||
|
current_node->next = node;
|
||||||
|
}
|
||||||
|
list->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
nilinkedlist_pop(NILinkedList **self)
|
||||||
|
{
|
||||||
|
NILinkedList *list = *self;
|
||||||
|
if(list->count == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NILinkedListNode *current_node = list->first;
|
||||||
|
NILinkedListNode *previous_node = NULL;
|
||||||
|
while(current_node->next != NULL) {
|
||||||
|
previous_node = current_node;
|
||||||
|
current_node = current_node->next;
|
||||||
|
}
|
||||||
|
if(previous_node == NULL) {
|
||||||
|
list->first = NULL;
|
||||||
|
} else {
|
||||||
|
previous_node->next = NULL;
|
||||||
|
}
|
||||||
|
list->count--;
|
||||||
|
return current_node->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_emplace(NILinkedList **self, NIArena **arena, void *content, const size_t idx)
|
||||||
|
{
|
||||||
|
NILinkedList *list = *self;
|
||||||
|
NILinkedListNode *node = nilinkedlist_node_new(arena);
|
||||||
|
node->content = content;
|
||||||
|
if(list->count == 0 || idx == 0) {
|
||||||
|
node->next = list->first;
|
||||||
|
list->first = node;
|
||||||
|
} else {
|
||||||
|
NILinkedListNode *aux = list->first;
|
||||||
|
const size_t real_idx = list->count < idx ? list->count : idx;
|
||||||
|
for(size_t i = 1; i < real_idx; i++) {
|
||||||
|
aux = aux->next;
|
||||||
|
}
|
||||||
|
node->next = aux->next;
|
||||||
|
aux->next = node;
|
||||||
|
}
|
||||||
|
list->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
nilinkedlist_peek(NILinkedList *self, const size_t idx)
|
||||||
|
{
|
||||||
|
if(self->count == 0 || idx >= self->count) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t real_idx = idx + 1; // 1 indexed
|
||||||
|
NILinkedListNode *node = self->first;
|
||||||
|
for(size_t i = 1; i < real_idx; i++) {
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
return node->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_enqueue(NILinkedList **self, NIArena **arena, void *content)
|
||||||
|
{
|
||||||
|
nilinkedlist_emplace(self, arena, content, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
nilinkedlist_dequeue(NILinkedList **self)
|
||||||
|
{
|
||||||
|
return nilinkedlist_pop(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_traverse(NILinkedList *list, NILinkedListVisitorFunctor f)
|
||||||
|
{
|
||||||
|
if(list->count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NILinkedListNode *node = list->first;
|
||||||
|
while(node->next != NULL) {
|
||||||
|
f(node->content);
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_map(NILinkedList *list, NIArena **arena, NILinkedListMapFunctor f, NILinkedList **out)
|
||||||
|
{
|
||||||
|
if(list->count > 0) {
|
||||||
|
void *content;
|
||||||
|
NILinkedListNode *node = list->first;
|
||||||
|
while(node->next != NULL) {
|
||||||
|
content = f(node->content, arena);
|
||||||
|
nilinkedlist_push(out, arena, content);
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
content = f(node->content, arena);
|
||||||
|
nilinkedlist_push(out, arena, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_reduce(NILinkedList *list, NILinkedListReduceFunctor f, void **out)
|
||||||
|
{
|
||||||
|
if(list->count > 0) {
|
||||||
|
NILinkedListNode *node = list->first;
|
||||||
|
while(node->next != NULL) {
|
||||||
|
f(out, node->content);
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
f(out, node->content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
205
test/nilinkedlist_test.c
Normal file
205
test/nilinkedlist_test.c
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
#define NILEAKDETECTOR_VERBOSE true
|
||||||
|
#include "../../ni-leakdetector/nileakdetector.h"
|
||||||
|
|
||||||
|
#define NIARENA_IMPLEMENTATION
|
||||||
|
#define NILINKEDLIST_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "../../ni-arena/niarena.h"
|
||||||
|
#include "../nilinkedlist.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
NIArena *arena;
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_new_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
assert(nilinkedlist_empty(&list));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_stack_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
|
||||||
|
// 0, 1, 2
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = i;
|
||||||
|
nilinkedlist_push(&list, &arena, n);
|
||||||
|
}
|
||||||
|
assert(list->count == 3);
|
||||||
|
|
||||||
|
// 2, 1, 0
|
||||||
|
for(int i = 2; i >= 0; i--) {
|
||||||
|
int *n = (int *)nilinkedlist_pop(&list);
|
||||||
|
assert(*n == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nilinkedlist_empty(&list));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_queue_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
|
||||||
|
// 0, 1, 2
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = i;
|
||||||
|
nilinkedlist_emplace(&list, &arena, n, 0); // insert at the beginning
|
||||||
|
}
|
||||||
|
assert(list->count == 3);
|
||||||
|
|
||||||
|
// 0, 1, 2
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
int *n = (int *)nilinkedlist_pop(&list);
|
||||||
|
assert(*n == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nilinkedlist_empty(&list));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_emplace_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = i;
|
||||||
|
nilinkedlist_push(&list, &arena, n);
|
||||||
|
}
|
||||||
|
// 0, 1, 2
|
||||||
|
int *i_1 = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*i_1 = 101;
|
||||||
|
nilinkedlist_emplace(&list, &arena, i_1, 1);
|
||||||
|
// 0, 101, 1, 2
|
||||||
|
int *i_2 = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*i_2 = 102;
|
||||||
|
nilinkedlist_emplace(&list, &arena, i_2, 3);
|
||||||
|
// 0, 101, 1, 102, 2
|
||||||
|
int *i_3 = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*i_3 = 103;
|
||||||
|
nilinkedlist_emplace(&list, &arena, i_3, 5);
|
||||||
|
// 0, 101, 1, 102, 2, 103
|
||||||
|
int r[6] = {0, 101, 1, 102, 2, 103};
|
||||||
|
for(int i = 5; i >= 0; i--) {
|
||||||
|
int *n = (int *)nilinkedlist_pop(&list);
|
||||||
|
assert(*n == r[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_emplace_far_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
|
||||||
|
int *i = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*i = 1000;
|
||||||
|
nilinkedlist_emplace(&list, &arena, i, 10000);
|
||||||
|
int *j = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*j = 2000;
|
||||||
|
nilinkedlist_emplace(&list, &arena, j, 20000);
|
||||||
|
|
||||||
|
int *k = (int *)nilinkedlist_pop(&list);
|
||||||
|
assert(*k == 2000);
|
||||||
|
int *l = (int *)nilinkedlist_pop(&list);
|
||||||
|
assert(*l == 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_int_printf_visitor(const void *content)
|
||||||
|
{
|
||||||
|
int *n = (int *)content;
|
||||||
|
printf("%d\n", *n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_traverse_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
|
||||||
|
for(int i = 0; i < 5; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = i;
|
||||||
|
nilinkedlist_push(&list, &arena, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
nilinkedlist_traverse(list, nilinkedlist_int_printf_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
nilinkedlist_int_duplicate(const void *p, NIArena **_arena)
|
||||||
|
{
|
||||||
|
int *n = (int *)p;
|
||||||
|
int *np = (int *)niarena_alloc(_arena, sizeof(int));
|
||||||
|
*np = *n * 2;
|
||||||
|
return (void *)np;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_map_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
for(int i = 0; i < 5; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = 100 + i;
|
||||||
|
nilinkedlist_push(&list, &arena, n);
|
||||||
|
}
|
||||||
|
NILinkedList *new_list = nilinkedlist_new(&arena);
|
||||||
|
nilinkedlist_map(list, &arena, nilinkedlist_int_duplicate, &new_list);
|
||||||
|
for(int i = 0; i < 5; i++) {
|
||||||
|
const int *j = (int *)nilinkedlist_peek(new_list, i);
|
||||||
|
assert(*j == 2 * (100 + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_int_sum(void **sum_pp, const void *current)
|
||||||
|
{
|
||||||
|
int **current_sum_pp = (int **)sum_pp;
|
||||||
|
const int *current_int = (int *)current;
|
||||||
|
*(*current_sum_pp) += *current_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nilinkedlist_reduce_test()
|
||||||
|
{
|
||||||
|
NILinkedList *list = nilinkedlist_new(&arena);
|
||||||
|
int total = 0;
|
||||||
|
for(int i = 0; i < 5; i++) {
|
||||||
|
int *n = (int *)niarena_alloc(&arena, sizeof(int));
|
||||||
|
*n = 100 + i;
|
||||||
|
total += 100 + i;
|
||||||
|
nilinkedlist_push(&list, &arena, n);
|
||||||
|
}
|
||||||
|
int *result = niarena_alloc(&arena, sizeof(int));
|
||||||
|
nilinkedlist_reduce(list, nilinkedlist_int_sum, (void **)&result);
|
||||||
|
assert(*result == total);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
arena = niarena_new();
|
||||||
|
|
||||||
|
nilinkedlist_new_test();
|
||||||
|
nilinkedlist_stack_test();
|
||||||
|
nilinkedlist_queue_test();
|
||||||
|
nilinkedlist_emplace_test();
|
||||||
|
nilinkedlist_emplace_far_test();
|
||||||
|
|
||||||
|
nilinkedlist_traverse_test();
|
||||||
|
nilinkedlist_map_test();
|
||||||
|
nilinkedlist_reduce_test();
|
||||||
|
|
||||||
|
niarena_delete(arena);
|
||||||
|
nileakdetector_print_summary();
|
||||||
|
|
||||||
|
printf("== TESTS SUCCESSFUL ==\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user