diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..c64d039 --- /dev/null +++ b/src/list.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2017 Bailey Thompson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "list.h" + +struct _list { + size_t data_size; + int space; + struct node *head; + struct node *tail; +}; + +struct node { + struct node *prev; + void *data; + struct node *next; +}; + +/** + * Initializes a doubly-linked list. + * + * @param data_size The size of data to store. + * + * @return The doubly-linked list, or NULL could not allocate memory. + */ +list list_init(const size_t data_size) { + struct _list *const init = malloc(sizeof(struct _list)); + if (init == NULL) { + return NULL; + } + init->data_size = data_size; + init->space = 0; + init->head = NULL; + init->tail = NULL; + return init; +} + +/** + * Gets the amount of elements in the doubly-linked list. + * + * @param me The doubly-linked list to check. + * + * @return The amount of elements. + */ +int list_size(list me) { + return me->space; +} + +/** + * Determines if the doubly-linked list is empty. + * + * @param me The doubly-linked list to check. + * + * @return If the list is empty. + */ +bool list_is_empty(list me) { + return list_size(me) == 0; +} + +/** + * Copies the nodes of the doubly-linked list to an array. + * + * @param array The array to copy the list to. + * @param me The list to copy to the array. + */ +void list_to_array(void *const array, list me) { + struct node *traverse = me->head; + int offset = 0; + while (traverse != NULL) { + memcpy(array + offset, traverse->data, me->data_size); + offset += me->data_size; + traverse = traverse->next; + } +} + +/* + * Get the node at index starting from the head. + */ +static struct node *get_node_from_head(list me, const int index) { + struct node *traverse = me->head; + for (int i = 0; i < index; i++) { + traverse = traverse->next; + } + return traverse; +} + +/* + * Get the node at index starting from tail. + */ +static struct node *get_node_from_tail(list me, const int index) { + struct node *traverse = me->tail; + for (int i = me->space - 1; i > index; i--) { + traverse = traverse->prev; + } + return traverse; +} + +/* + * Get the node at the specified index. + */ +static struct node *get_node_at(list me, const int index) { + if (index <= me->space / 2) { + return get_node_from_head(me, index); + } else { + return get_node_from_tail(me, index); + } +} + +/** + * Adds data at the first index in the doubly-linked list. + * + * @param me The list to add data to. + * @param data The data to add to the list. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int list_add_first(list me, void *const data) { + struct node *const traverse = me->head; + struct node *const add = malloc(sizeof(struct node)); + if (add == NULL) { + return -ENOMEM; + } + add->data = malloc(me->data_size); + if (add->data == NULL) { + free(add); + return -ENOMEM; + } + add->prev = NULL; + memcpy(add->data, data, me->data_size); + add->next = traverse; + if (traverse != NULL) { + traverse->prev = add; + } + me->head = add; + if (me->tail == NULL) { + me->tail = traverse; + } + me->space++; + return 0; +} + +/** + * Adds data at a specified index in the doubly-linked list. + * + * @param me The list to add data to. + * @param index The index to add the data at. + * @param data The data to add to the list. + * + * @return 0 No error. + * -ENOMEM Out of memory. + * -EINVAL Invalid parameter. + */ +int list_add_at(list me, const int index, void *const data) { + if (index < 0 || index > me->space) { + return -EINVAL; + } + if (index == 0) { + return list_add_first(me, data); + } + if (index == me->space) { + return list_add_last(me, data); + } + // The new node will go right before this node. + struct node *const traverse = get_node_at(me, index); + struct node *const add = malloc(sizeof(struct node)); + if (add == NULL) { + return -ENOMEM; + } + add->data = malloc(me->data_size); + if (add->data == NULL) { + free(add); + return -ENOMEM; + } + add->prev = traverse->prev; + memcpy(add->data, data, me->data_size); + add->next = traverse; + traverse->prev->next = add; + traverse->prev = add; + me->space++; + return 0; +} + +/** + * Adds data at the last index in the doubly-linked list. + * + * @param me The list to add data to. + * @param data The data to add to the list. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int list_add_last(list me, void *const data) { + struct node *const traverse = me->tail; + struct node *const add = malloc(sizeof(struct node)); + if (add == NULL) { + return -ENOMEM; + } + add->data = malloc(me->data_size); + if (add->data == NULL) { + free(add); + return -ENOMEM; + } + add->prev = traverse; + memcpy(add->data, data, me->data_size); + add->next = NULL; + if (traverse != NULL) { + traverse->next = add; + } + me->tail = add; + me->space++; + return 0; +} + +static bool isIllegalParameters(list me, const int index) { + return index < 0 || index >= me->space || me->space == 0; +} + +/** + * Removes the first piece of data from the doubly-linked list. + * + * @param me The list to remove data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_remove_first(list me) { + return list_remove_at(me, 0); +} + +/** + * Removes data from the doubly-linked list at the specified index. + * + * @param me The list to remove data from. + * @param index The index to remove from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_remove_at(list me, const int index) { + if (isIllegalParameters(me, index)) { + return -EINVAL; + } + struct node *const traverse = get_node_at(me, index); + if (index == 0) { + traverse->next->prev = NULL; + me->head = traverse->next; + } else if (index == me->space - 1) { + traverse->prev->next = NULL; + me->tail = traverse->prev; + } else { + traverse->prev->next = traverse->next; + traverse->next->prev = traverse->prev; + } + free(traverse); + me->space--; + return 0; +} + +/** + * Removes the last piece of data from the doubly-linked list. + * + * @param me The list to remove data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_remove_last(list me) { + return list_remove_at(me, me->space - 1); +} + +/** + * Sets the data at the first index in the doubly-linked list. + * + * @param me The list to set data for. + * @param data The data to set in the list. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_set_first(list me, void *const data) { + return list_set_at(me, 0, data); +} + +/** + * Sets the data at the specified index in the doubly-linked list. + * + * @param me The list to set data for. + * @param index The index to set data in the list. + * @param data The data to set in the list. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_set_at(list me, const int index, void *const data) { + if (isIllegalParameters(me, index)) { + return -EINVAL; + } + struct node *const traverse = get_node_at(me, index); + memcpy(traverse->data, data, me->data_size); + return 0; +} + +/** + * Sets the data at the last index in the doubly-linked list. + * + * @param me The list to set data for. + * @param data The data to set in the list. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_set_last(list me, void *const data) { + return list_set_at(me, me->space - 1, data); +} + +/** + * Gets the data at the first index in the doubly-linked list. + * + * @param data The data to get. + * @param me The list to get data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_get_first(void *const data, list me) { + return list_get_at(data, me, 0); +} + +/** + * Gets the data at the specified index in the doubly-linked list. + * + * @param data The data to get. + * @param me The list to get data from. + * @param index The index to get data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_get_at(void *const data, list me, const int index) { + if (isIllegalParameters(me, index)) { + return -EINVAL; + } + struct node *const traverse = get_node_at(me, index); + memcpy(data, traverse->data, me->data_size); + return 0; +} + +/** + * Gets the data at the last index in the doubly-linked list. + * + * @param data The data to get. + * @param me The list to get data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int list_get_last(void *const data, list me) { + return list_get_at(data, me, me->space - 1); +} + +/** + * Clears all elements from the doubly-linked list. + * + * @param me The list to clear. + */ +void list_clear(list me) { + struct node *traverse = me->head; + while (traverse != NULL) { + struct node *const temp = traverse; + traverse = traverse->next; + free(temp); + } + me->head = NULL; + me->space = 0; + me->tail = NULL; +} + +/** + * Destroys the doubly-linked list. + * + * @param me The list to destroy. + * + * @return NULL + */ +list list_destroy(list me) { + list_clear(me); + free(me); + return NULL; +} diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..254990f --- /dev/null +++ b/src/list.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Bailey Thompson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CONTAINERS_LIST_H +#define CONTAINERS_LIST_H + +#include + +typedef struct _list *list; + +// Starting +list list_init(size_t data_size); +// Utility +int list_size(list me); +bool list_is_empty(list me); +void list_to_array(void *array, list me); +// Adding +int list_add_first(list me, void *data); +int list_add_at(list me, int index, void *data); +int list_add_last(list me, void *data); +// Removing +int list_remove_first(list me); +int list_remove_at(list me, int index); +int list_remove_last(list me); +// Setting +int list_set_first(list me, void *data); +int list_set_at(list me, int index, void *data); +int list_set_last(list me, void *data); +// Getting +int list_get_first(void *data, list me); +int list_get_at(void *data, list me, int index); +int list_get_last(void *data, list me); +// Ending +void list_clear(list me); +list list_destroy(list me); + +#endif /* CONTAINERS_LIST_H */ diff --git a/src/vector.c b/src/vector.c index fe8e77b..bbea7aa 100644 --- a/src/vector.c +++ b/src/vector.c @@ -73,7 +73,7 @@ int vector_size(vector me) { * * @param me The vector to check. * - * @return True if the vector is empty, else false. + * @return If the vector is empty. */ bool vector_is_empty(vector me) { return vector_size(me) == 0; @@ -197,6 +197,10 @@ int vector_add_last(vector me, void *const data) { return vector_add_at(me, me->offset, data); } +static bool isIllegalParameters(vector me, const int index) { + return index < 0 || index >= me->offset || me->offset == 0; +} + /** * Removes the first element from the vector. * @@ -219,7 +223,7 @@ int vector_remove_first(vector me) { * -EINVAL Invalid parameter. */ int vector_remove_at(vector me, const int index) { - if (index < 0 || index >= me->offset || me->offset == 0) { + if (isIllegalParameters(me, index)) { return -EINVAL; } memmove(me->storage + index * me->data_size, @@ -249,9 +253,12 @@ int vector_remove_last(vector me) { * Sets the data for the first element in the vector. * * @param me The vector to set data for. + * + * @return 0 No error. + * -EINVAL Invalid parameter. */ -void vector_set_first(vector me, void *const data) { - vector_set_at(me, 0, data); +int vector_set_first(vector me, void *const data) { + return vector_set_at(me, 0, data); } /** @@ -264,7 +271,7 @@ void vector_set_first(vector me, void *const data) { * -EINVAL Invalid parameter. */ int vector_set_at(vector me, const int index, void *const data) { - if (index < 0 || index >= me->offset) { + if (isIllegalParameters(me, index)) { return -EINVAL; } memcpy(me->storage + index * me->data_size, data, me->data_size); @@ -275,9 +282,12 @@ int vector_set_at(vector me, const int index, void *const data) { * Sets the data for the last element in the vector. * * @param me The vector to set data for. + * + * @return 0 No error. + * -EINVAL Invalid parameter. */ -void vector_set_last(vector me, void *const data) { - vector_set_at(me, me->offset - 1, data); +int vector_set_last(vector me, void *const data) { + return vector_set_at(me, me->offset - 1, data); } /** @@ -285,9 +295,12 @@ void vector_set_last(vector me, void *const data) { * * @param data The data to copy to. * @param me The vector to copy from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. */ -void vector_get_first(void *const data, vector me) { - vector_get_at(data, me, 0); +int vector_get_first(void *const data, vector me) { + return vector_get_at(data, me, 0); } /** @@ -301,7 +314,7 @@ void vector_get_first(void *const data, vector me) { * -EINVAL Invalid parameter. */ int vector_get_at(void *const data, vector me, const int index) { - if (index < 0 || index >= me->offset) { + if (isIllegalParameters(me, index)) { return -EINVAL; } memcpy(data, me->storage + index * me->data_size, me->data_size); @@ -313,9 +326,12 @@ int vector_get_at(void *const data, vector me, const int index) { * * @param data The data to copy to. * @param me The vector to copy from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. */ -void vector_get_last(void *const data, vector me) { - vector_get_at(data, me, me->offset - 1); +int vector_get_last(void *const data, vector me) { + return vector_get_at(data, me, me->offset - 1); } /** diff --git a/src/vector.h b/src/vector.h index d9937a8..cb9a4a7 100644 --- a/src/vector.h +++ b/src/vector.h @@ -45,13 +45,13 @@ int vector_remove_first(vector me); int vector_remove_at(vector me, int index); int vector_remove_last(vector me); // Setting -void vector_set_first(vector me, void *data); +int vector_set_first(vector me, void *data); int vector_set_at(vector me, int index, void *data); -void vector_set_last(vector me, void *data); +int vector_set_last(vector me, void *data); // Getting -void vector_get_first(void *data, vector me); +int vector_get_first(void *data, vector me); int vector_get_at(void *data, vector me, int index); -void vector_get_last(void *data, vector me); +int vector_get_last(void *data, vector me); // Ending int vector_clear(vector me); vector vector_destroy(vector me); diff --git a/tst/test.c b/tst/test.c index d56bf38..d0dc3f1 100644 --- a/tst/test.c +++ b/tst/test.c @@ -2,6 +2,7 @@ #include #include #include "../src/vector.h" +#include "../src/list.h" static void test_vector(void) { int val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; @@ -117,6 +118,120 @@ static void test_vector(void) { assert(me == NULL); } -int main() { - test_vector(); +static void test_list(void) { + int val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + list me = list_init(sizeof(int)); + assert(me != NULL); + assert(list_size(me) == 0); + assert(list_is_empty(me)); + for (int i = 0; i < 10; i++) { + list_add_first(me, &val[i]); + assert(list_size(me) == i + 1); + int get = 0; + list_get_first(&get, me); + assert(get == val[i]); + } + assert(list_size(me) == 10); + assert(!list_is_empty(me)); + int get_arr[10] = {0}; + list_to_array(get_arr, me); + for (int i = 0; i < 10; i++) { + int get = 0; + list_get_at(&get, me, i); + assert(get == val[9 - i]); + assert(get_arr[i] == val[9 - i]); + } + for (int i = 0; i < 7; i++) { + list_remove_last(me); + } + int trimmed[5] = {0}; + list_to_array(trimmed, me); + assert(list_size(me) == 3); + for (int i = 0; i < 3; i++) { + assert(10 - i == trimmed[i]); + } + int add = 3; + list_add_last(me, &add); + add = -1; + list_add_at(me, 1, &add); + add = -2; + list_add_last(me, &add); + assert(list_size(me) == 6); + int aa = 456; + int a = 456; + int b = 456; + int c = 456; + int d = 456; + int e = 456; + int f = 456; + int ff = 456; + list_get_first(&aa, me); + assert(aa == 10); + list_get_at(&a, me, 0); + assert(a == 10); + list_get_at(&b, me, 1); + assert(b == -1); + list_get_at(&c, me, 2); + assert(c == 9); + list_get_at(&d, me, 3); + assert(d == 8); + list_get_at(&e, me, 4); + assert(e == 3); + list_get_at(&f, me, 5); + assert(f == -2); + list_get_last(&ff, me); + assert(ff == -2); + list_remove_first(me); + list_remove_at(me, 2); + list_remove_last(me); + assert(list_size(me) == 3); + int get = 345; + list_get_first(&get, me); + assert(get == -1); + list_get_at(&get, me, 1); + assert(get == 9); + list_get_last(&get, me); + assert(get == 3); + int set = 12; + list_set_first(me, &set); + set = 13; + list_set_at(me, 1, &set); + set = 14; + list_set_last(me, &set); + int arr[3] = {0}; + list_to_array(arr, me); + assert(arr[0] == 12); + assert(arr[1] == 13); + assert(arr[2] == 14); + set = -5; + list_set_at(me, 0, &set); + set = -6; + list_set_at(me, 1, &set); + set = -7; + list_set_at(me, 2, &set); + list_to_array(arr, me); + assert(arr[0] == -5); + assert(arr[1] == -6); + assert(arr[2] == -7); + assert(list_set_at(me, 4, &set) == -EINVAL); + assert(list_get_at(&set, me, 4) == -EINVAL); + assert(list_remove_at(me, 4) == -EINVAL); + assert(list_add_at(me, 5, &set) == -EINVAL); + assert(list_set_at(me, -1, &set) == -EINVAL); + assert(list_get_at(&set, me, -1) == -EINVAL); + assert(list_remove_at(me, -1) == -EINVAL); + assert(list_add_at(me, -1, &set) == -EINVAL); + list_clear(me); + assert(list_size(me) == 0); + assert(list_is_empty(me)); + assert(list_remove_first(me) == -EINVAL); + assert(list_remove_last(me) == -EINVAL); + me = list_destroy(me); + assert(me == NULL); +} + +int main() { + //test_vector(); + test_list(); + return 0; }