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