From f6ea19b357b26753c792522094a302b741c6c69f Mon Sep 17 00:00:00 2001 From: Bailey Thompson Date: Sat, 14 Oct 2017 22:45:53 -0400 Subject: [PATCH] Add vector --- src/vector.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/vector.h | 59 +++++++++ tst/test.c | 122 ++++++++++++++++++ 3 files changed, 528 insertions(+) create mode 100644 src/vector.c create mode 100644 src/vector.h create mode 100644 tst/test.c diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..fe8e77b --- /dev/null +++ b/src/vector.c @@ -0,0 +1,347 @@ +/* + * 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 "vector.h" + +const int START_SPACE = 8; + +struct _vector { + size_t data_size; + int offset; + int space; + void *storage; +}; + +/** + * Initialize a vector. + * + * @param data_size The size of each element in the vector. + * + * @return The newly-initialized vector, or NULL if memory allocation error. + */ +vector vector_init(const size_t data_size) { + struct _vector *const init = malloc(sizeof(struct _vector)); + if (init == NULL) { + return NULL; + } + init->data_size = data_size; + init->offset = 0; + init->space = START_SPACE; + init->storage = malloc(init->space * init->data_size); + if (init->storage == NULL) { + free(init); + return NULL; + } + return init; +} + +/** + * Get the size being used by the vector. + * + * @param me The vector to check. + * + * @return The size being used by the vector. + */ +int vector_size(vector me) { + return me->offset; +} + +/** + * Whether or not the vector is empty. + * + * @param me The vector to check. + * + * @return True if the vector is empty, else false. + */ +bool vector_is_empty(vector me) { + return vector_size(me) == 0; +} + +/** + * Checks if the vector is big enough for the capacity specified. + * + * @param me The vector to check. + * @param capacity The capacity to check the vector for. + * + * @return True if big enough, else false. + */ +bool vector_ensure_capacity(vector me, const int capacity) { + return capacity <= me->space; +} + +/** + * Sets the size to use for the vector buffer. The size is in units and not in + * bytes. Each unit is of size specified from init. If the size to set is + * smaller than the current size, the data is lost. + * + * @param me The vector to set size. + * @param size The size to set the vector to. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int vector_set_space(vector me, const int size) { + me->space = size; + if (me->space < me->offset) { + me->offset = me->space; + } + void *const temp = realloc(me->storage, me->space * me->data_size); + if (temp == NULL) { + return -ENOMEM; + } + me->storage = temp; + return 0; +} + +/** + * Sets the size of the vector buffer to the current size being used. + * + * @param me The vector to trim. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int vector_trim_to_size(vector me) { + return vector_set_space(me, me->offset); +} + +/** + * Copies the storage element of vector to an array. + * + * @param me The vector to copy from. + * @param array The array to copy to. + */ +void vector_to_array(void *array, vector me) { + memcpy(array, me->storage, me->offset * me->data_size); +} + +/** + * Adds an element to the start of the vector. + * + * @param me The vector to add to. + * @param data The data to add to the vector. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int vector_add_first(vector me, void *const data) { + return vector_add_at(me, 0, data); +} + +/** + * Adds an element to the location specified. + * + * @param me The vector to add to. + * @param index The location in the vector to add the data to. + * @param data The data to add to the vector. + * + * @return 0 No error. + * -ENOMEM Out of memory. + * -EINVAL Invalid parameter. + */ +int vector_add_at(vector me, const int index, void *const data) { + if (index < 0 || index > me->offset) { + return -EINVAL; + } + const int full_space = me->space; + if (me->offset + 1 >= me->space) { + me->space *= 1.5; + void *const temp = realloc(me->storage, me->space * me->data_size); + if (temp == NULL) { + return -ENOMEM; + } + me->storage = temp; + } + if (index != me->offset) { + memmove(me->storage + (index + 1) * me->data_size, + me->storage + index * me->data_size, + (full_space - index) * me->data_size); + } + memcpy(me->storage + index * me->data_size, data, me->data_size); + me->offset++; + return 0; +} + +/** + * Adds an element to the end of the vector. + * + * @param me The vector to add to. + * @param data The data to add to the vector. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int vector_add_last(vector me, void *const data) { + return vector_add_at(me, me->offset, data); +} + +/** + * Removes the first element from the vector. + * + * @param me The vector to remove from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int vector_remove_first(vector me) { + return vector_remove_at(me, 0); +} + +/** + * Removes element based on its index. + * + * @param me The vector to remove from. + * @param index The location in the vector to remove the data from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int vector_remove_at(vector me, const int index) { + if (index < 0 || index >= me->offset || me->offset == 0) { + return -EINVAL; + } + memmove(me->storage + index * me->data_size, + me->storage + (index + 1) * me->data_size, + (me->space - index) * me->data_size); + me->offset--; + return 0; +} + +/** + * Removes the last element from the vector. + * + * @param me The vector to remove from. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int vector_remove_last(vector me) { + if (me->offset == 0) { + return -EINVAL; + } + me->offset--; + return 0; +} + +/** + * Sets the data for the first element in the vector. + * + * @param me The vector to set data for. + */ +void vector_set_first(vector me, void *const data) { + vector_set_at(me, 0, data); +} + +/** + * + * @param me The vector to set data for. + * @param index The location to set data at in the vector. + * @param data The data to set at the location in the vector. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int vector_set_at(vector me, const int index, void *const data) { + if (index < 0 || index >= me->offset) { + return -EINVAL; + } + memcpy(me->storage + index * me->data_size, data, me->data_size); + return 0; +} + +/** + * Sets the data for the last element in the vector. + * + * @param me The vector to set data for. + */ +void vector_set_last(vector me, void *const data) { + vector_set_at(me, me->offset - 1, data); +} + +/** + * Copies the first element of the vector to data. + * + * @param data The data to copy to. + * @param me The vector to copy from. + */ +void vector_get_first(void *const data, vector me) { + vector_get_at(data, me, 0); +} + +/** + * Copies the element at index of the vector to data. + * + * @param data The data to copy to. + * @param me The vector to copy from. + * @param index The index to copy from in the vector. + * + * @return 0 No error. + * -EINVAL Invalid parameter. + */ +int vector_get_at(void *const data, vector me, const int index) { + if (index < 0 || index >= me->offset) { + return -EINVAL; + } + memcpy(data, me->storage + index * me->data_size, me->data_size); + return 0; +} + +/** + * Copies the last element of the vector to data. + * + * @param data The data to copy to. + * @param me The vector to copy from. + */ +void vector_get_last(void *const data, vector me) { + vector_get_at(data, me, me->offset - 1); +} + +/** + * Clears the elements from the vector. + * + * @param me The vector to clear. + * + * @return 0 No error. + * -ENOMEM Out of memory. + */ +int vector_clear(vector me) { + const int ret = vector_set_space(me, START_SPACE); + me->offset = 0; + return ret; +} + +/** + * Frees the vector memory. + * + * @param me The vector to free from memory. + * + * @return NULL + */ +vector vector_destroy(vector me) { + free(me->storage); + me->storage = NULL; + free(me); + return NULL; +} diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..d9937a8 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,59 @@ +/* + * 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_VECTOR_H +#define CONTAINERS_VECTOR_H + +#include + +typedef struct _vector *vector; + +// Starting +vector vector_init(size_t data_size); +// Utility +int vector_size(vector me); +bool vector_is_empty(vector me); +bool vector_ensure_capacity(vector me, int capacity); +int vector_set_space(vector me, int size); +int vector_trim_to_size(vector me); +void vector_to_array(void *array, vector me); +// Adding +int vector_add_first(vector me, void *data); +int vector_add_at(vector me, int index, void *data); +int vector_add_last(vector me, void *data); +// Removing +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_at(vector me, int index, void *data); +void vector_set_last(vector me, void *data); +// Getting +void 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); +// Ending +int vector_clear(vector me); +vector vector_destroy(vector me); + +#endif /* CONTAINERS_VECTOR_H */ diff --git a/tst/test.c b/tst/test.c new file mode 100644 index 0000000..d56bf38 --- /dev/null +++ b/tst/test.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include "../src/vector.h" + +static void test_vector(void) { + int val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + vector me = vector_init(sizeof(int)); + assert(me != NULL); + assert(vector_size(me) == 0); + assert(vector_is_empty(me)); + assert(vector_ensure_capacity(me, 8)); + assert(!vector_ensure_capacity(me, 9)); + for (int i = 0; i < 10; i++) { + vector_add_first(me, &val[i]); + int get = 0; + vector_get_first(&get, me); + assert(get == val[i]); + } + assert(vector_size(me) == 10); + assert(!vector_is_empty(me)); + assert(vector_ensure_capacity(me, 12)); + assert(!vector_ensure_capacity(me, 13)); + int get_arr[10] = {0}; + vector_to_array(get_arr, me); + for (int i = 0; i < 10; i++) { + int get = 0; + vector_get_at(&get, me, i); + assert(get == val[9 - i]); + assert(get_arr[i] == val[9 - i]); + } + int trimmed[5] = {0}; + vector_trim_to_size(me); + vector_set_space(me, 3); + vector_to_array(trimmed, me); + assert(vector_size(me) == 3); + for (int i = 0; i < 3; i++) { + assert(10 - i == trimmed[i]); + } + int add = 3; + vector_add_last(me, &add); + add = -1; + vector_add_at(me, 1, &add); + add = -2; + vector_add_last(me, &add); + assert(vector_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; + vector_get_first(&aa, me); + assert(aa == 10); + vector_get_at(&a, me, 0); + assert(a == 10); + vector_get_at(&b, me, 1); + assert(b == -1); + vector_get_at(&c, me, 2); + assert(c == 9); + vector_get_at(&d, me, 3); + assert(d == 8); + vector_get_at(&e, me, 4); + assert(e == 3); + vector_get_at(&f, me, 5); + assert(f == -2); + vector_get_last(&ff, me); + assert(ff == -2); + vector_remove_first(me); + vector_remove_at(me, 2); + vector_remove_last(me); + assert(vector_size(me) == 3); + int get = 345; + vector_get_first(&get, me); + assert(get == -1); + vector_get_at(&get, me, 1); + assert(get == 9); + vector_get_last(&get, me); + assert(get == 3); + int set = 12; + vector_set_first(me, &set); + set = 13; + vector_set_at(me, 1, &set); + set = 14; + vector_set_last(me, &set); + int arr[3] = {0}; + vector_to_array(arr, me); + assert(arr[0] == 12); + assert(arr[1] == 13); + assert(arr[2] == 14); + set = -5; + vector_set_at(me, 0, &set); + set = -6; + vector_set_at(me, 1, &set); + set = -7; + vector_set_at(me, 2, &set); + vector_to_array(arr, me); + assert(arr[0] == -5); + assert(arr[1] == -6); + assert(arr[2] == -7); + assert(vector_set_at(me, 4, &set) == -EINVAL); + assert(vector_get_at(&set, me, 4) == -EINVAL); + assert(vector_remove_at(me, 4) == -EINVAL); + assert(vector_add_at(me, 5, &set) == -EINVAL); + assert(vector_set_at(me, -1, &set) == -EINVAL); + assert(vector_get_at(&set, me, -1) == -EINVAL); + assert(vector_remove_at(me, -1) == -EINVAL); + assert(vector_add_at(me, -1, &set) == -EINVAL); + vector_clear(me); + assert(vector_size(me) == 0); + assert(vector_is_empty(me)); + assert(vector_remove_first(me) == -EINVAL); + assert(vector_remove_last(me) == -EINVAL); + me = vector_destroy(me); + assert(me == NULL); +} + +int main() { + test_vector(); +}