diff --git a/README.md b/README.md index 804dea9..bcda4d8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ It is not production-ready. Use at your own risk. - Hashmaps - Thread-safe (MT Safe) - Dynamic strings - Thread-safe (MT Safe) -- Circular Queue - Thread-safe (MT Safe) +- Circular Queues - Thread-safe (MT Safe) +- Vectors - Thread-safe (MT Safe) ## Examples You can find examples inside `test/` folder. diff --git a/meson.build b/meson.build index 2d90872..21e65be 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,7 @@ src = files( 'hashmap/myhashmap.c', 'queue/myqueue.c', 'string/mystring.c', + 'vector/myvector.c', ) testlib = files( @@ -17,10 +18,11 @@ testlib = files( 'test/queue/q1.c', 'test/string/str1.c', 'test/string/str2.c', + 'test/vector/v1.c', ) sources = src + testlib -inc_dir = include_directories('string', 'queue', 'hashmap') +inc_dir = include_directories('string', 'queue', 'hashmap', 'vector') executable('testlib', sources, include_directories: inc_dir) diff --git a/string/mystring.c b/string/mystring.c index da2096f..4a89832 100644 --- a/string/mystring.c +++ b/string/mystring.c @@ -142,12 +142,24 @@ int mcl_string_extend(mcl_string_s *destination, mcl_string_s *source) { return -1; } + if (mtx_lock(&destination->lock) != thrd_success) { + return -1; + } + + if (mtx_lock(&source->lock) != thrd_success) { + mtx_unlock(&destination->lock); + return -1; + } + size_t need = destination->size + source->size; if (need > destination->capacity) { /* Reallocate destination data buffer */ destination->capacity = next_power_two(need); char *tmp = realloc(destination->data, destination->capacity); if (tmp == NULL) { + mtx_unlock(&destination->lock); + mtx_unlock(&source->lock); + return -1; } destination->data = tmp; @@ -158,6 +170,9 @@ int mcl_string_extend(mcl_string_s *destination, mcl_string_s *source) { destination->size = need; destination->data[destination->size] = '\0'; + mtx_unlock(&destination->lock); + mtx_unlock(&source->lock); + return 0; } diff --git a/test/test.c b/test/test.c index 5c1bf8d..f2396f7 100644 --- a/test/test.c +++ b/test/test.c @@ -9,6 +9,7 @@ void test_hm1(); void test_q1(); void test_str1(); void test_str2(); +void test_v1(); int main(void) { puts("==== [Running Hashmap tests] ===="); @@ -27,5 +28,10 @@ int main(void) { puts("OK!"); puts(""); + puts("==== [Running Vector tests] ===="); + test_v1(); + puts("OK!"); + puts(""); + return 0; } diff --git a/test/vector/v1.c b/test/vector/v1.c new file mode 100644 index 0000000..109c40e --- /dev/null +++ b/test/vector/v1.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "../../vector/myvector.h" + +typedef struct my_elem { + char name[32]; + int age; +} my_elem_s; + +void test_v1() { + /* Allocate a new vector */ + size_t elem_size = sizeof(my_elem_s); + mcl_vector_s *v = mcl_vector_new(10, elem_size); + assert(mcl_vector_size(v) == 0); + assert(mcl_vector_capacity(v) == 16); + + /* Push an element */ + my_elem_s e1 = { + .age = 21, + .name = "John", + }; + mcl_vector_push(v, &e1); + assert(mcl_vector_size(v) == 1); + + /* Retrieve an element (Remember to FREE) */ + my_elem_s *e1_v = (my_elem_s *)mcl_vector_get(v, 0); + printf("name: %s, age: %d\n", e1_v->name, e1_v->age); + free(e1_v); + + /* Deallocate the vector */ + mcl_vector_free(v); +} \ No newline at end of file diff --git a/vector/myvector.c b/vector/myvector.c new file mode 100644 index 0000000..77657d8 --- /dev/null +++ b/vector/myvector.c @@ -0,0 +1,146 @@ +#include "myvector.h" + +#include +#include +#include + +/* Returns the next power of two of a number */ +static size_t next_power_two(size_t len) { + if (len == 0) return 1; + + size_t p = 1; + while (p < len) { + if (p > SIZE_MAX / 2) { + p = len; + break; + } + p <<= 1; + } + + return p; +} + +mcl_vector_s *mcl_vector_new(size_t initial_capacity, size_t element_size) { + if (initial_capacity < 0) { + return NULL; + } + + mcl_vector_s *vec = (mcl_vector_s *)malloc(sizeof(mcl_vector_s)); + if (vec == NULL) { + return NULL; + } + + vec->capacity = next_power_two(initial_capacity); + vec->elem_size = element_size; + vec->size = 0; + vec->data = malloc(vec->capacity * vec->elem_size); + if (vec->data == NULL) { + free(vec); + + return NULL; + } + + if (mtx_init(&vec->lock, mtx_plain) != thrd_success) { + free(vec->data); + free(vec); + + return NULL; + } + + return vec; +} + +int mcl_vector_push(mcl_vector_s *vec, void *elem) { + if (vec == NULL || elem == NULL) { + return -1; + } + + if (mtx_lock(&vec->lock) != thrd_success) { + return -1; + } + + if (vec->size + 1 > vec->capacity) { + /* Reallocate buffer */ + vec->capacity = next_power_two(vec->size + 1); + void *tmp = malloc(vec->capacity * vec->elem_size); + if (tmp == NULL) { + mtx_unlock(&vec->lock); + + return -1; + } + vec->data = tmp; + } + + /* Add the new element */ + memcpy(vec->data + (vec->size * vec->elem_size), elem, vec->elem_size); + vec->size++; + + mtx_unlock(&vec->lock); + + return 0; +} + +void mcl_vector_free(mcl_vector_s *vec) { + if (vec == NULL) { + return; + } + + if (vec->data != NULL) { + free(vec->data); + } + + free(vec); +} + +size_t mcl_vector_size(mcl_vector_s *vec) { + if (vec == NULL) { + return -1; + } + + if (mtx_lock(&vec->lock) != thrd_success) { + return -1; + } + + size_t size = vec->size; + + mtx_unlock(&vec->lock); + + return size; +} + +size_t mcl_vector_capacity(mcl_vector_s *vec) { + if (vec == NULL) { + return -1; + } + + if (mtx_lock(&vec->lock) != thrd_success) { + return -1; + } + + size_t cap = vec->capacity; + + mtx_unlock(&vec->lock); + + return cap; +} + +void *mcl_vector_get(mcl_vector_s *vec, size_t index) { + if (vec == NULL || index < 0 || index > vec->size) { + return NULL; + } + + if (mtx_lock(&vec->lock) != thrd_success) { + return NULL; + } + + void *elem = malloc(vec->elem_size); + if (elem == NULL) { + return NULL; + } + + memcpy(elem, vec->data + (index * vec->elem_size), vec->elem_size); + + mtx_unlock(&vec->lock); + + return elem; +} diff --git a/vector/myvector.h b/vector/myvector.h new file mode 100644 index 0000000..4412d24 --- /dev/null +++ b/vector/myvector.h @@ -0,0 +1,25 @@ +#ifndef MYCLIB_VECTOR_H +#define MYCLIB_VECTOR_H + +#include +#include +#include + +typedef struct { + void *data; + size_t elem_size; + size_t size; + size_t capacity; + mtx_t lock; +} mcl_vector_s; + +mcl_vector_s *mcl_vector_new(size_t initial_capacity, size_t element_size); +int mcl_vector_push(mcl_vector_s *vec, void *elem); +size_t mcl_vector_size(mcl_vector_s *vec); +size_t mcl_vector_capacity(mcl_vector_s *vec); + +void *mcl_vector_get(mcl_vector_s *vec, size_t index); + +void mcl_vector_free(mcl_vector_s *vec); + +#endif