linked list
This commit is contained in:
parent
6e2fae213a
commit
1f0a022cc7
55
inc/cex/containers/linked_list.h
Normal file
55
inc/cex/containers/linked_list.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef CEX_CONTAINERS_linked_list_t_H
|
||||
#define CEX_CONTAINERS_linked_list_t_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct linked_list_node_t {
|
||||
struct linked_list_node_t* prev;
|
||||
struct linked_list_node_t* next;
|
||||
|
||||
void* data;
|
||||
} linked_list_node_t;
|
||||
|
||||
typedef struct {
|
||||
linked_list_node_t* first;
|
||||
size_t length;
|
||||
size_t data_size;
|
||||
} linked_list_t;
|
||||
|
||||
typedef int64_t index_t;
|
||||
|
||||
typedef void (*list_iterator_t)(index_t index, void* data, void* arg);
|
||||
typedef bool (*list_filter_t)(index_t index, const void* data, void* arg);
|
||||
|
||||
linked_list_t LL_create(size_t data_size);
|
||||
linked_list_t LL_from_array(size_t data_size, void* array, index_t length);
|
||||
linked_list_t LL_copy(linked_list_t* list);
|
||||
|
||||
void* LL_to_array(linked_list_t* list);
|
||||
|
||||
void LL_destroy(linked_list_t* list);
|
||||
|
||||
void* LL_insert(linked_list_t* list, index_t index);
|
||||
void* LL_insert_value(linked_list_t* list, index_t index, const void* data);
|
||||
|
||||
void* LL_add(linked_list_t* list);
|
||||
void* LL_add_value(linked_list_t* list, const void* data);
|
||||
|
||||
void LL_remove(linked_list_t* list, index_t index);
|
||||
|
||||
void* LL_get(linked_list_t* list, index_t index);
|
||||
void LL_set(linked_list_t* list, index_t index, const void* data);
|
||||
|
||||
void LL_iterate(linked_list_t* list, list_iterator_t func, void* arg);
|
||||
void LL_iterate_range(linked_list_t* list, list_iterator_t func, void* arg, index_t from, index_t to);
|
||||
|
||||
void LL_ITER_COPY(index_t _, void* data, void* list);
|
||||
void LL_ITER_SUM(index_t _, void* data, void* result);
|
||||
void LL_ITER_PRINT(index_t i, void* data, void* _);
|
||||
|
||||
linked_list_t LL_filter(linked_list_t* list, list_filter_t func, void* arg);
|
||||
linked_list_t LL_filter_range(linked_list_t* list, list_filter_t func, void* arg, index_t from, index_t to);
|
||||
|
||||
#endif
|
195
src/cex/containers/linked_list.c
Normal file
195
src/cex/containers/linked_list.c
Normal file
@ -0,0 +1,195 @@
|
||||
#include <cex/containers/linked_list.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
linked_list_node_t* step(linked_list_node_t* node, index_t amount, size_t length) {
|
||||
amount %= length;
|
||||
|
||||
if (amount > 0) {
|
||||
while (amount != 0) {
|
||||
amount--;
|
||||
node = node->next;
|
||||
}
|
||||
} else {
|
||||
while (amount != 0) {
|
||||
amount++;
|
||||
node = node->prev;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
linked_list_t LL_create(size_t data_size) {
|
||||
linked_list_t list = {.data_size = data_size, .first = NULL, .length = 0};
|
||||
return list;
|
||||
}
|
||||
|
||||
linked_list_t LL_from_array(size_t data_size, void* array, index_t length) {
|
||||
linked_list_t list = LL_create(data_size);
|
||||
|
||||
for (index_t i = 0; i < length; i++) {
|
||||
LL_add_value(&list, array + (data_size * i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void* LL_to_array(linked_list_t* list) {
|
||||
void* array = malloc(list->data_size * list->length);
|
||||
|
||||
linked_list_node_t* node = list->first;
|
||||
|
||||
for (index_t i = 0; i < list->length; i++) {
|
||||
memcpy(array + (i * list->data_size), node->data, list->data_size);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
linked_list_t LL_copy(linked_list_t* list) {
|
||||
linked_list_t result = LL_create(list->data_size);
|
||||
LL_iterate(list, LL_ITER_COPY, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void LL_destroy(linked_list_t* list) {
|
||||
linked_list_node_t* node = list->first->prev;
|
||||
list->first->prev = NULL;
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->data != NULL)
|
||||
free(node->data);
|
||||
|
||||
linked_list_node_t* follwing = node->prev;
|
||||
free(node);
|
||||
node = follwing;
|
||||
}
|
||||
|
||||
list->first = NULL;
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
void* LL_add(linked_list_t* list) {
|
||||
return LL_insert(list, list->length);
|
||||
}
|
||||
|
||||
void* LL_add_value(linked_list_t* list, const void* data) {
|
||||
return LL_insert_value(list, list->length, data);
|
||||
}
|
||||
|
||||
void* LL_insert(linked_list_t* list, index_t index) {
|
||||
linked_list_node_t* node = (linked_list_node_t*)malloc(sizeof(linked_list_node_t));
|
||||
|
||||
if (list->data_size != 0)
|
||||
node->data = malloc(list->data_size);
|
||||
else
|
||||
node->data = NULL;
|
||||
|
||||
linked_list_node_t* prev;
|
||||
linked_list_node_t* next;
|
||||
|
||||
if (list->length == 0) {
|
||||
prev = node;
|
||||
next = node;
|
||||
list->first = node;
|
||||
} else if (index == 0) {
|
||||
prev = list->first->prev;
|
||||
next = list->first;
|
||||
list->first = node;
|
||||
} else {
|
||||
index = index > 0 ? index - 1 : index;
|
||||
prev = step(list->first, index, list->length);
|
||||
next = prev->next;
|
||||
}
|
||||
|
||||
// prev -> node -> next
|
||||
prev->next = node;
|
||||
node->prev = prev;
|
||||
next->prev = node;
|
||||
node->next = next;
|
||||
|
||||
list->length++;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
void* LL_insert_value(linked_list_t* list, index_t index, const void* data) {
|
||||
void* node_data = LL_insert(list, index);
|
||||
memcpy(node_data, data, list->data_size);
|
||||
return node_data;
|
||||
}
|
||||
|
||||
void LL_remove(linked_list_t* list, index_t index) {
|
||||
if (list->length == 1) {
|
||||
if (list->first->data != NULL)
|
||||
free(list->first->data);
|
||||
free(list->first);
|
||||
list->length--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
linked_list_node_t* node = step(list->first, index, list->length);
|
||||
node->prev = node->next;
|
||||
node->next = node->prev;
|
||||
|
||||
if (node->data != NULL)
|
||||
free(node->data);
|
||||
free(node);
|
||||
}
|
||||
|
||||
void* LL_get(linked_list_t* list, index_t index) {
|
||||
return step(list->first, index, list->length)->data;
|
||||
}
|
||||
|
||||
void LL_set(linked_list_t* list, index_t index, const void* data) {
|
||||
memcpy(step(list->first, index, list->length)->data, data, list->data_size);
|
||||
}
|
||||
|
||||
void LL_iterate_range(linked_list_t* list, list_iterator_t func, void* arg, index_t from, index_t to) {
|
||||
linked_list_node_t* node = step(list->first, from, list->length);
|
||||
|
||||
for (index_t i = from; i < to; i++) {
|
||||
func(i, node->data, arg);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
void LL_iterate(linked_list_t* list, list_iterator_t func, void* arg) {
|
||||
LL_iterate_range(list, func, arg, 0, list->length);
|
||||
}
|
||||
|
||||
linked_list_t LL_filter_range(linked_list_t* list, list_filter_t func, void* arg, index_t from, index_t to) {
|
||||
linked_list_node_t* node = step(list->first, from, list->length);
|
||||
|
||||
linked_list_t result = LL_create(list->data_size);
|
||||
|
||||
for (index_t i = from; i < to; i++) {
|
||||
if (func(i, node->data, arg)) {
|
||||
LL_add_value(&result, node->data);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
linked_list_t LL_filter(linked_list_t* list, list_filter_t func, void* arg) {
|
||||
return LL_filter_range(list, func, arg, 0, list->length);
|
||||
}
|
||||
|
||||
void LL_ITER_COPY(index_t _, void* data, void* list) {
|
||||
LL_add_value((linked_list_t*)list, data);
|
||||
}
|
||||
|
||||
void LL_ITER_SUM(index_t _, void* data, void* result) {
|
||||
*(int64_t*)result += *(int64_t*)data;
|
||||
}
|
||||
|
||||
void LL_ITER_PRINT(index_t i, void* data, void* _) {
|
||||
printf("#%ld: %ld\n", i, *(int64_t*)data);
|
||||
}
|
4
test/inc/cex/containers/test_linked_list.h
Normal file
4
test/inc/cex/containers/test_linked_list.h
Normal file
@ -0,0 +1,4 @@
|
||||
#include <cex/test/assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
test_result_t test_linked_list();
|
45
test/src/cex/containers/test_linked_list.c
Normal file
45
test/src/cex/containers/test_linked_list.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include <cex/containers/linked_list.h>
|
||||
#include <cex/test/assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
TEST(linked_list)
|
||||
|
||||
LABEL("create")
|
||||
linked_list_t list = LL_create(sizeof(uint64_t));
|
||||
ASSERT_EQ("data size set", list.data_size, sizeof(uint64_t))
|
||||
ASSERT_EQ("length = 0", list.length, 0)
|
||||
ASSERT_EQ("first = NULL", list.first, NULL)
|
||||
|
||||
LABEL("add")
|
||||
int64_t num = 69;
|
||||
LL_add_value(&list, &num);
|
||||
ASSERT_EQ("length = 1", list.length, 1)
|
||||
ASSERT_NEQ("first = NULL", list.first, NULL)
|
||||
ASSERT_EQ("next = first", list.first->next, list.first)
|
||||
ASSERT_EQ("prev = first", list.first->prev, list.first)
|
||||
ASSERT_EQ("*data = num", *(int64_t*)list.first->data, num)
|
||||
|
||||
LABEL("destroy")
|
||||
LL_destroy(&list);
|
||||
ASSERT_EQ("length = 0", list.length, 0)
|
||||
ASSERT_EQ("first = NULL", list.first, NULL)
|
||||
|
||||
LABEL("from array")
|
||||
uint64_t array[10] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
||||
list = LL_from_array(sizeof(uint64_t), array, 10);
|
||||
|
||||
LABEL("iterate")
|
||||
LL_iterate(&list, LL_ITER_PRINT, NULL);
|
||||
|
||||
LABEL("to array")
|
||||
uint64_t* result_array = LL_to_array(&list);
|
||||
for (index_t i = 0; i < 10; i++) {
|
||||
ASSERT_EQ("=", array[i], result_array[i]);
|
||||
}
|
||||
free(result_array);
|
||||
|
||||
LL_destroy(&list);
|
||||
|
||||
END_TEST
|
6
test/src/main.c
Normal file
6
test/src/main.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <cex/containers/test_linked_list.h>
|
||||
#include <cex/test/assert.h>
|
||||
|
||||
PROJECT("cex")
|
||||
RUN_TEST(linked_list)
|
||||
END_PROJECT
|
Loading…
Reference in New Issue
Block a user