This commit is contained in:
Bailey Thompson
2017-10-16 23:11:11 -04:00
committed by GitHub
parent f6ea19b357
commit e953df50f2
5 changed files with 617 additions and 18 deletions

412
src/list.c Normal file
View File

@@ -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 <stdlib.h>
#include <memory.h>
#include <errno.h>
#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;
}

56
src/list.h Normal file
View File

@@ -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 <stdbool.h>
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 */

View File

@@ -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);
}
/**

View File

@@ -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);

View File

@@ -2,6 +2,7 @@
#include <errno.h>
#include <assert.h>
#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;
}