From 6ac7edbb3bf947d74c0d18f21509e7b035097392 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Fri, 28 Jun 2024 13:54:38 +0900 Subject: [PATCH] Implement a linked list data structure --- CMakeLists.txt | 6 + cosmopolitan/Makefile | 6 +- src/data_structures/linked_list.c | 175 ++++++++++++++++++++++++++++++ src/data_structures/linked_list.h | 60 ++++++++++ src/data_structures/test.c | 102 +++++++++++++++++ 5 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 src/data_structures/linked_list.c create mode 100644 src/data_structures/linked_list.h create mode 100644 src/data_structures/test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4645618..c720fa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(SimpleArchiver_VERSION 1.0) set(SimpleArchiver_SOURCES src/main.c src/parser.c + src/data_structures/linked_list.c ) add_compile_options( @@ -21,3 +22,8 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) endif() add_executable(simplearchiver ${SimpleArchiver_SOURCES}) + +add_executable(test_datastructures + src/data_structures/test.c + src/data_structures/linked_list.c +) diff --git a/cosmopolitan/Makefile b/cosmopolitan/Makefile index 9c6b0e9..d81601f 100644 --- a/cosmopolitan/Makefile +++ b/cosmopolitan/Makefile @@ -5,10 +5,12 @@ OUTDIR = out SOURCES = \ ../src/main.c \ - ../src/parser.c + ../src/parser.c \ + ../src/data_structures/linked_list.c HEADERS = \ - ../src/parser.h + ../src/parser.h \ + ../src/data_structures/linked_list.h OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(patsubst %.c,%.c.o,${SOURCES}))) diff --git a/src/data_structures/linked_list.c b/src/data_structures/linked_list.c new file mode 100644 index 0000000..e2af7b4 --- /dev/null +++ b/src/data_structures/linked_list.c @@ -0,0 +1,175 @@ +/* + * Copyright 2024 Stephen Seo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * `linked_list.c` is the source for a linked list data structure. + */ + +#include "linked_list.h" + +#include + +SDArchiverLinkedList *simple_archiver_list_init(void) { + SDArchiverLinkedList *list = malloc(sizeof(SDArchiverLinkedList)); + + list->head = malloc(sizeof(SDArchiverLLNode)); + list->tail = malloc(sizeof(SDArchiverLLNode)); + + list->head->next = list->tail; + list->head->prev = NULL; + list->head->data = NULL; + list->head->data_free_fn = NULL; + + list->tail->next = NULL; + list->tail->prev = list->head; + list->tail->data = NULL; + list->tail->data_free_fn = NULL; + + list->count = 0; + + return list; +} + +void simple_archiver_list_free(SDArchiverLinkedList **list) { + if (list && *list) { + SDArchiverLLNode *node = (*list)->head; + SDArchiverLLNode *prev; + while (node) { + prev = node; + node = node->next; + free(prev); + if (node && node->data) { + if (node->data_free_fn) { + node->data_free_fn(node->data); + } else { + free(node->data); + } + } + } + + free(*list); + *list = NULL; + } +} + +int simple_archiver_list_add(SDArchiverLinkedList *list, void *data, + void (*data_free_fn)(void *)) { + if (!list) { + return 1; + } + + SDArchiverLLNode *new_node = malloc(sizeof(SDArchiverLLNode)); + new_node->data = data; + new_node->data_free_fn = data_free_fn; + + new_node->next = list->tail; + new_node->prev = list->tail->prev; + + list->tail->prev->next = new_node; + list->tail->prev = new_node; + + ++list->count; + + return 0; +} + +int simple_archiver_list_remove(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)) { + if (!list) { + return 0; + } + + int removed_count = 0; + + SDArchiverLLNode *node = list->head; + int iter_removed = 0; + while (node) { + if (iter_removed == 0) { + node = node->next; + } + iter_removed = 0; + if (node && node != list->tail) { + if (data_check_fn(node->data) != 0) { + SDArchiverLLNode *temp = node->next; + + if (node->data_free_fn) { + node->data_free_fn(node->data); + } else { + free(node->data); + } + + node->prev->next = node->next; + node->next->prev = node->prev; + free(node); + + node = temp; + iter_removed = 1; + ++removed_count; + --list->count; + } + } + } + + return removed_count; +} + +int simple_archiver_list_remove_once(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)) { + if (!list) { + return 0; + } + + SDArchiverLLNode *node = list->head; + while (node) { + node = node->next; + if (node && node != list->tail) { + if (data_check_fn(node->data) != 0) { + if (node->data_free_fn) { + node->data_free_fn(node->data); + } else { + free(node->data); + } + + node->prev->next = node->next; + node->next->prev = node->prev; + free(node); + + --list->count; + + return 1; + } + } + } + + return 0; +} + +void *simple_archiver_list_get(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)) { + if (!list) { + return NULL; + } + + SDArchiverLLNode *node = list->head; + while (node) { + node = node->next; + if (node && node != list->tail) { + if (data_check_fn(node->data) != 0) { + return node->data; + } + } + } + + return NULL; +} diff --git a/src/data_structures/linked_list.h b/src/data_structures/linked_list.h new file mode 100644 index 0000000..93609d5 --- /dev/null +++ b/src/data_structures/linked_list.h @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Stephen Seo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * `linked_list.h` is the header for a linked list data structure. + */ + +#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_ +#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_LINKED_LIST_H_ + +typedef struct SDArchiverLLNode { + struct SDArchiverLLNode *next; + struct SDArchiverLLNode *prev; + void *data; + void (*data_free_fn)(void *); +} SDArchiverLLNode; + +typedef struct SDArchiverLinkedList { + SDArchiverLLNode *head; + SDArchiverLLNode *tail; + unsigned int count; +} SDArchiverLinkedList; + +SDArchiverLinkedList *simple_archiver_list_init(void); +void simple_archiver_list_free(SDArchiverLinkedList **list); + +/// Returns 0 on success. +int simple_archiver_list_add(SDArchiverLinkedList *list, void *data, + void (*data_free_fn)(void *)); + +/// Returns number of removed items. +/// data_check_fn must return non-zero if the data passed to it is to be +/// removed. +int simple_archiver_list_remove(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)); + +/// Returns 1 on removed, 0 if not removed. +/// data_check_fn must return non-zero if the data passed to it is to be +/// removed. +int simple_archiver_list_remove_once(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)); + +/// Returns non-null on success. +/// data_check_fn must return non-zero if the data passed to it is to be +/// returned. +void *simple_archiver_list_get(SDArchiverLinkedList *list, + int (*data_check_fn)(void *)); + +#endif diff --git a/src/data_structures/test.c b/src/data_structures/test.c new file mode 100644 index 0000000..4121fb7 --- /dev/null +++ b/src/data_structures/test.c @@ -0,0 +1,102 @@ +/* + * Copyright 2024 Stephen Seo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * `data_structures/test.c` is the source for testing data structure code. + */ + +#include +#include + +#include "linked_list.h" + +static int checks_checked = 0; +static int checks_passed = 0; + +#define CHECK_TRUE(x) \ + do { \ + ++checks_checked; \ + if (!(x)) { \ + printf("CHECK_TRUE at line %u failed: %s\n", __LINE__, #x); \ + } else { \ + ++checks_passed; \ + } \ + } while (0); +#define CHECK_FALSE(x) \ + do { \ + ++checks_checked; \ + if (x) { \ + printf("CHECK_FALSE at line %u failed: %s\n", __LINE__, #x); \ + } else { \ + ++checks_passed; \ + } \ + } while (0); + +void no_free_fn(__attribute__((unused)) void *unused) { return; } + +int get_one_fn(void *data) { return strcmp(data, "one") == 0 ? 1 : 0; } + +int get_two_fn(void *data) { return strcmp(data, "two") == 0 ? 1 : 0; } + +int get_three_fn(void *data) { return strcmp(data, "three") == 0 ? 1 : 0; } + +int main(void) { + // Test LinkedList. + { + SDArchiverLinkedList *list = simple_archiver_list_init(); + + CHECK_TRUE(list->count == 0); + + const char *one = "one"; + const char *two = "two"; + const char *three = "three"; + + simple_archiver_list_add(list, (void *)one, no_free_fn); + + CHECK_TRUE(list->count == 1); + + simple_archiver_list_add(list, (void *)two, no_free_fn); + + CHECK_TRUE(list->count == 2); + + simple_archiver_list_add(list, (void *)three, no_free_fn); + + CHECK_TRUE(list->count == 3); + + void *ptr = simple_archiver_list_get(list, get_one_fn); + CHECK_TRUE(ptr == one); + + ptr = simple_archiver_list_get(list, get_two_fn); + CHECK_TRUE(ptr == two); + + ptr = simple_archiver_list_get(list, get_three_fn); + CHECK_TRUE(ptr == three); + + CHECK_TRUE(simple_archiver_list_remove(list, get_two_fn) == 1); + CHECK_TRUE(list->count == 2); + CHECK_TRUE(simple_archiver_list_get(list, get_two_fn) == NULL); + + CHECK_TRUE(simple_archiver_list_remove_once(list, get_one_fn) == 1); + CHECK_TRUE(list->count == 1); + CHECK_TRUE(simple_archiver_list_get(list, get_one_fn) == NULL); + + simple_archiver_list_free(&list); + + CHECK_TRUE(list == NULL); + } + + printf("Checks checked: %u\n", checks_checked); + printf("Checks passed: %u\n", checks_passed); + return checks_passed == checks_checked ? 0 : 1; +}