feat: add vector
This commit is contained in:
@@ -7,7 +7,8 @@ It is not production-ready. Use at your own risk.
|
|||||||
|
|
||||||
- Hashmaps - Thread-safe (MT Safe)
|
- Hashmaps - Thread-safe (MT Safe)
|
||||||
- Dynamic strings - 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
|
## Examples
|
||||||
You can find examples inside `test/` folder.
|
You can find examples inside `test/` folder.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ src = files(
|
|||||||
'hashmap/myhashmap.c',
|
'hashmap/myhashmap.c',
|
||||||
'queue/myqueue.c',
|
'queue/myqueue.c',
|
||||||
'string/mystring.c',
|
'string/mystring.c',
|
||||||
|
'vector/myvector.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
testlib = files(
|
testlib = files(
|
||||||
@@ -17,10 +18,11 @@ testlib = files(
|
|||||||
'test/queue/q1.c',
|
'test/queue/q1.c',
|
||||||
'test/string/str1.c',
|
'test/string/str1.c',
|
||||||
'test/string/str2.c',
|
'test/string/str2.c',
|
||||||
|
'test/vector/v1.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
sources = src + testlib
|
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)
|
executable('testlib', sources, include_directories: inc_dir)
|
||||||
|
|||||||
@@ -142,12 +142,24 @@ int mcl_string_extend(mcl_string_s *destination, mcl_string_s *source) {
|
|||||||
return -1;
|
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;
|
size_t need = destination->size + source->size;
|
||||||
if (need > destination->capacity) {
|
if (need > destination->capacity) {
|
||||||
/* Reallocate destination data buffer */
|
/* Reallocate destination data buffer */
|
||||||
destination->capacity = next_power_two(need);
|
destination->capacity = next_power_two(need);
|
||||||
char *tmp = realloc(destination->data, destination->capacity);
|
char *tmp = realloc(destination->data, destination->capacity);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
|
mtx_unlock(&destination->lock);
|
||||||
|
mtx_unlock(&source->lock);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
destination->data = tmp;
|
destination->data = tmp;
|
||||||
@@ -158,6 +170,9 @@ int mcl_string_extend(mcl_string_s *destination, mcl_string_s *source) {
|
|||||||
destination->size = need;
|
destination->size = need;
|
||||||
destination->data[destination->size] = '\0';
|
destination->data[destination->size] = '\0';
|
||||||
|
|
||||||
|
mtx_unlock(&destination->lock);
|
||||||
|
mtx_unlock(&source->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ void test_hm1();
|
|||||||
void test_q1();
|
void test_q1();
|
||||||
void test_str1();
|
void test_str1();
|
||||||
void test_str2();
|
void test_str2();
|
||||||
|
void test_v1();
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
puts("==== [Running Hashmap tests] ====");
|
puts("==== [Running Hashmap tests] ====");
|
||||||
@@ -27,5 +28,10 @@ int main(void) {
|
|||||||
puts("OK!");
|
puts("OK!");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
|
puts("==== [Running Vector tests] ====");
|
||||||
|
test_v1();
|
||||||
|
puts("OK!");
|
||||||
|
puts("");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
34
test/vector/v1.c
Normal file
34
test/vector/v1.c
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
146
vector/myvector.c
Normal file
146
vector/myvector.c
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#include "myvector.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
25
vector/myvector.h
Normal file
25
vector/myvector.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef MYCLIB_VECTOR_H
|
||||||
|
#define MYCLIB_VECTOR_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user