Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21806e77a7 | |||
| a56d5b396b | |||
| 1628b3bf74 | |||
| f729463ab3 | |||
| d777b9be7f | |||
| a6740769b9 |
@@ -5,13 +5,21 @@ A small, personal C library for learning and experimentation.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
All the features listed are Thread-safe.
|
Most core containers use internal locks for single-object operations.
|
||||||
|
Cross-object operations can still require caller-side synchronization.
|
||||||
|
|
||||||
- Hashmaps
|
- Hashmaps
|
||||||
- Strings
|
- Strings
|
||||||
- Circular queues
|
- Circular queues
|
||||||
- Vectors
|
- Vectors
|
||||||
- Stack
|
- Stack
|
||||||
|
- Set
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- Hashmap keys/values are copied as raw bytes (`memcpy`) using `key_size`/`value_size`.
|
||||||
|
- For C-string keys, provide a readable key buffer of at least `key_size` bytes.
|
||||||
|
- The set module is basic and intentionally minimal.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ void hm_free_bucket(bucket_s *bucket);
|
|||||||
* If the key already exists, the old value is freed (if free_value_fn is provided)
|
* If the key already exists, the old value is freed (if free_value_fn is provided)
|
||||||
* and replaced with the new value. If the key doesn't exist, a new entry is created.
|
* and replaced with the new value. If the key doesn't exist, a new entry is created.
|
||||||
* Both key and value are copied into the hashmap using memcpy.
|
* Both key and value are copied into the hashmap using memcpy.
|
||||||
|
* The caller must pass buffers that are readable for at least key_size/value_size bytes.
|
||||||
|
* For C-string keys, use a fixed-size key buffer (or set key_size to the exact copied size).
|
||||||
*
|
*
|
||||||
* @param[in] hashmap Pointer to the hash map.
|
* @param[in] hashmap Pointer to the hash map.
|
||||||
* @param[in] key Pointer to the key to insert (will be copied, must not be NULL).
|
* @param[in] key Pointer to the key to insert (will be copied, must not be NULL).
|
||||||
|
|||||||
+8
-9
@@ -14,7 +14,7 @@ add_global_arguments('-D_POSIX_C_SOURCE=200112L', language: 'c')
|
|||||||
lib_src = files(
|
lib_src = files(
|
||||||
'hashmap/myhashmap.c',
|
'hashmap/myhashmap.c',
|
||||||
'queue/myqueue.c',
|
'queue/myqueue.c',
|
||||||
'socket/mysocket.c',
|
'set/myset.c',
|
||||||
'stack/mystack.c',
|
'stack/mystack.c',
|
||||||
'string/mystring.c',
|
'string/mystring.c',
|
||||||
'vector/myvector.c',
|
'vector/myvector.c',
|
||||||
@@ -24,7 +24,7 @@ lib_src = files(
|
|||||||
test_src = files(
|
test_src = files(
|
||||||
'test/hashmap/hm1.c',
|
'test/hashmap/hm1.c',
|
||||||
'test/queue/queue1.c',
|
'test/queue/queue1.c',
|
||||||
'test/socket/socket1.c',
|
'test/set/set1.c',
|
||||||
'test/stack/stack1.c',
|
'test/stack/stack1.c',
|
||||||
'test/string/str1.c',
|
'test/string/str1.c',
|
||||||
'test/string/str2.c',
|
'test/string/str2.c',
|
||||||
@@ -33,11 +33,8 @@ test_src = files(
|
|||||||
'test/vector/vec1.c',
|
'test/vector/vec1.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Windows Socket lib
|
|
||||||
winsock_dep = cc.find_library('ws2_32', required: false)
|
|
||||||
|
|
||||||
# Include directories
|
# Include directories
|
||||||
inc_dir = include_directories('string', 'queue', 'hashmap', 'vector', 'stack', 'socket')
|
inc_dir = include_directories('string', 'queue', 'hashmap', 'vector', 'stack', 'set')
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
win_inc_dir = include_directories('c:/include/')
|
win_inc_dir = include_directories('c:/include/')
|
||||||
else
|
else
|
||||||
@@ -60,17 +57,19 @@ install_headers(
|
|||||||
'queue/myqueue.h',
|
'queue/myqueue.h',
|
||||||
'string/mystring.h',
|
'string/mystring.h',
|
||||||
'vector/myvector.h',
|
'vector/myvector.h',
|
||||||
|
'set/myset.h',
|
||||||
'stack/mystack.h',
|
'stack/mystack.h',
|
||||||
'socket/mysocket.h',
|
|
||||||
],
|
],
|
||||||
subdir: 'myclib',
|
subdir: 'myclib',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test executable
|
# Test executable
|
||||||
executable(
|
testlib_exe = executable(
|
||||||
'testlib',
|
'testlib',
|
||||||
lib_src + test_src,
|
test_src,
|
||||||
include_directories: [inc_dir, win_inc_dir],
|
include_directories: [inc_dir, win_inc_dir],
|
||||||
dependencies: winsock_dep,
|
dependencies: winsock_dep,
|
||||||
link_with: myclib_lib,
|
link_with: myclib_lib,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test('testlib', testlib_exe)
|
||||||
|
|||||||
+129
-6
@@ -1,6 +1,30 @@
|
|||||||
#include "myset.h"
|
#include "myset.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static int set_find_index_locked(set_s *set, void *elem, size_t *index_out) {
|
||||||
|
size_t size = vec_size(set->data);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
void *current = vec_get(set->data, i);
|
||||||
|
if (current == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equal = (memcmp(current, elem, set->elem_size) == 0);
|
||||||
|
free(current);
|
||||||
|
|
||||||
|
if (equal) {
|
||||||
|
*index_out = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*index_out = SIZE_MAX;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
set_s *set_new(size_t element_size) {
|
set_s *set_new(size_t element_size) {
|
||||||
if (element_size == 0) {
|
if (element_size == 0) {
|
||||||
@@ -12,17 +36,116 @@ set_s *set_new(size_t element_size) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: think about how to deal the free_value for each element
|
if (mtx_init(&set->lock, mtx_plain) != thrd_success) {
|
||||||
|
free(set);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
set->data = vec_new(8, element_size);
|
||||||
|
if (set->data == NULL) {
|
||||||
|
mtx_destroy(&set->lock);
|
||||||
|
free(set);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
set->elem_size = element_size;
|
||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
int set_add(set_s *set, void *elem);
|
int set_add(set_s *set, void *elem) {
|
||||||
|
if (set == NULL || elem == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int set_remove(set_s *set, void *elem);
|
if (mtx_lock(&set->lock) != thrd_success) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int set_clear(set_s *set);
|
size_t index;
|
||||||
|
if (set_find_index_locked(set, elem, &index) != 0) {
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int set_contains(set_s *set, void *elem);
|
if (index != SIZE_MAX) {
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void set_free(set_s *set);
|
int ret = vec_push(set->data, elem);
|
||||||
|
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_remove(set_s *set, void *elem) {
|
||||||
|
if (set == NULL || elem == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtx_lock(&set->lock) != thrd_success) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index;
|
||||||
|
if (set_find_index_locked(set, elem, &index) != 0) {
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == SIZE_MAX) {
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = vec_remove(set->data, index);
|
||||||
|
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_clear(set_s *set) {
|
||||||
|
if (set == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtx_lock(&set->lock) != thrd_success) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = vec_clear(set->data);
|
||||||
|
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_contains(set_s *set, void *elem) {
|
||||||
|
if (set == NULL || elem == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtx_lock(&set->lock) != thrd_success) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index;
|
||||||
|
if (set_find_index_locked(set, elem, &index) != 0) {
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx_unlock(&set->lock);
|
||||||
|
|
||||||
|
return (index == SIZE_MAX) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_free(set_s *set) {
|
||||||
|
if (set == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_free(set->data);
|
||||||
|
mtx_destroy(&set->lock);
|
||||||
|
free(set);
|
||||||
|
}
|
||||||
|
|||||||
+10
-4
@@ -1,27 +1,33 @@
|
|||||||
#ifndef MYCLIB_SET_H
|
#ifndef MYCLIB_SET_H
|
||||||
#define MYCLIB_SET_H
|
#define MYCLIB_SET_H
|
||||||
|
|
||||||
// TODO: WIP
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <threads.h>
|
#include <threads.h>
|
||||||
|
|
||||||
#include "../hashmap/myhashmap.h"
|
#include "../vector/myvector.h"
|
||||||
|
|
||||||
typedef struct set {
|
typedef struct set {
|
||||||
hashmap_s *map;
|
vec_s *data;
|
||||||
|
size_t elem_size;
|
||||||
mtx_t lock;
|
mtx_t lock;
|
||||||
} set_s;
|
} set_s;
|
||||||
|
|
||||||
|
/* Create a new set for fixed-size elements. */
|
||||||
set_s *set_new(size_t element_size);
|
set_s *set_new(size_t element_size);
|
||||||
|
|
||||||
|
/* Add a new element to the set (no-op if already present). */
|
||||||
int set_add(set_s *set, void *elem);
|
int set_add(set_s *set, void *elem);
|
||||||
|
|
||||||
|
/* Remove an element from the set. */
|
||||||
int set_remove(set_s *set, void *elem);
|
int set_remove(set_s *set, void *elem);
|
||||||
|
|
||||||
|
/* Remove all elements from the set. */
|
||||||
int set_clear(set_s *set);
|
int set_clear(set_s *set);
|
||||||
|
|
||||||
|
/* Returns 1 if present, 0 if absent, -1 on error. */
|
||||||
int set_contains(set_s *set, void *elem);
|
int set_contains(set_s *set, void *elem);
|
||||||
|
|
||||||
|
/* Free the set and all related resources. */
|
||||||
void set_free(set_s *set);
|
void set_free(set_s *set);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
#include "mysocket.h"
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
int sock_platform_init(void) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSADATA wsaData;
|
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
|
|
||||||
WSACleanup();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sock_close(socket_t socket) {
|
|
||||||
int ret = 0;
|
|
||||||
#ifdef _WIN32
|
|
||||||
ret = closesocket(socket);
|
|
||||||
#else
|
|
||||||
ret = close(socket);
|
|
||||||
#endif
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sock_platform_shutdown(void) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSACleanup();
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sock_readall(socket_t sockfd, void *buf, size_t bufsize) {
|
|
||||||
char *p = (char *)buf;
|
|
||||||
size_t total_read = 0;
|
|
||||||
|
|
||||||
while (total_read < bufsize) {
|
|
||||||
int n = recv(sockfd, p, bufsize - total_read, 0);
|
|
||||||
|
|
||||||
if (n > 0) {
|
|
||||||
/* Data received */
|
|
||||||
p += n;
|
|
||||||
total_read += n;
|
|
||||||
} else if (n == 0) {
|
|
||||||
/* Connection closed */
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
/* Error */
|
|
||||||
if (errno == EINTR) {
|
|
||||||
/* Try again */
|
|
||||||
continue;
|
|
||||||
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
/* Socket non-blocking, no data right now */
|
|
||||||
/* Returns what has read */
|
|
||||||
return total_read;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return total_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sock_writeall(socket_t socket, const void *buf, size_t n) {
|
|
||||||
const char *p = (const char *)buf;
|
|
||||||
size_t bytes_to_write = n;
|
|
||||||
int bytes_written;
|
|
||||||
|
|
||||||
while (bytes_to_write > 0) {
|
|
||||||
bytes_written = send(socket, p, bytes_to_write, 0);
|
|
||||||
|
|
||||||
if (bytes_written >= 0) {
|
|
||||||
p += bytes_written;
|
|
||||||
bytes_to_write -= bytes_written;
|
|
||||||
} else {
|
|
||||||
if (errno == EINTR) {
|
|
||||||
continue;
|
|
||||||
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
return n - bytes_to_write;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef MYCLIB_SOCKET_H
|
|
||||||
#define MYCLIB_SOCKET_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
typedef int socket_t;
|
|
||||||
#else
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
typedef int socket_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initialize the socket system */
|
|
||||||
int sock_platform_init(void);
|
|
||||||
|
|
||||||
/* Close a socket */
|
|
||||||
int sock_close(socket_t socket);
|
|
||||||
|
|
||||||
/* Clean the socket system */
|
|
||||||
int sock_platform_shutdown(void);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read 'bufsize' bytes from socket
|
|
||||||
* Returns the read bytes, -1 on failure, 0 connection closed without issues
|
|
||||||
*/
|
|
||||||
int sock_readall(socket_t sockfd, void *buf, size_t bufsize);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes 'n' bytes to socket
|
|
||||||
*/
|
|
||||||
int sock_writeall(socket_t socket, const void *buf, size_t n);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
+10
-1
@@ -21,6 +21,7 @@ stack_s *stack_new(size_t initial_capacity, size_t element_size) {
|
|||||||
/* Allocate the vec data */
|
/* Allocate the vec data */
|
||||||
stack->data = vec_new(initial_capacity, element_size);
|
stack->data = vec_new(initial_capacity, element_size);
|
||||||
if (stack->data == NULL) {
|
if (stack->data == NULL) {
|
||||||
|
mtx_destroy(&stack->lock);
|
||||||
free(stack);
|
free(stack);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -71,7 +72,14 @@ void *stack_top(stack_s *stack) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *elem = vec_get(stack->data, stack->data->size - 1);
|
size_t size = vec_size(stack->data);
|
||||||
|
if (size == 0) {
|
||||||
|
mtx_unlock(&stack->lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *elem = vec_get(stack->data, size - 1);
|
||||||
|
|
||||||
mtx_unlock(&stack->lock);
|
mtx_unlock(&stack->lock);
|
||||||
|
|
||||||
@@ -84,5 +92,6 @@ void stack_free(stack_s *stack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec_free(stack->data);
|
vec_free(stack->data);
|
||||||
|
mtx_destroy(&stack->lock);
|
||||||
free(stack);
|
free(stack);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -53,7 +53,7 @@ void test_hm1(void) {
|
|||||||
/* Pass your custom hash, equal and free functions */
|
/* Pass your custom hash, equal and free functions */
|
||||||
/* This hashmap will contain names as keys and a custom type as value */
|
/* This hashmap will contain names as keys and a custom type as value */
|
||||||
size_t key_size = sizeof(char) * MAX_STR_LEN;
|
size_t key_size = sizeof(char) * MAX_STR_LEN;
|
||||||
size_t value_size = sizeof(int) + sizeof(char) * MAX_STR_LEN;
|
size_t value_size = sizeof(struct my_custom_type);
|
||||||
hashmap_s *map =
|
hashmap_s *map =
|
||||||
hm_new(my_hash_func, my_equal_fun, my_free_key, my_free_value, key_size, value_size);
|
hm_new(my_hash_func, my_equal_fun, my_free_key, my_free_value, key_size, value_size);
|
||||||
assert(map != NULL);
|
assert(map != NULL);
|
||||||
@@ -65,11 +65,13 @@ void test_hm1(void) {
|
|||||||
strncpy(p1.favourite_brand, "Ferrari", sizeof(p1.favourite_brand));
|
strncpy(p1.favourite_brand, "Ferrari", sizeof(p1.favourite_brand));
|
||||||
|
|
||||||
/* Insert a new pair */
|
/* Insert a new pair */
|
||||||
assert(hm_set(map, "John", &p1));
|
char john_key[MAX_STR_LEN] = {0};
|
||||||
|
strncpy(john_key, "John", sizeof(john_key) - 1);
|
||||||
|
assert(hm_set(map, john_key, &p1));
|
||||||
|
|
||||||
/* Retrieve the data */
|
/* Retrieve the data */
|
||||||
/* Remember to free the value from the get function */
|
/* Remember to free the value from the get function */
|
||||||
bucket_s *john = hm_get(map, "John");
|
bucket_s *john = hm_get(map, john_key);
|
||||||
assert(john != NULL);
|
assert(john != NULL);
|
||||||
|
|
||||||
char *name = (char *)john->key;
|
char *name = (char *)john->key;
|
||||||
@@ -85,7 +87,7 @@ void test_hm1(void) {
|
|||||||
hm_free_bucket(john);
|
hm_free_bucket(john);
|
||||||
|
|
||||||
/* Remove a key from hash map */
|
/* Remove a key from hash map */
|
||||||
assert(hm_remove(map, "John"));
|
assert(hm_remove(map, john_key));
|
||||||
|
|
||||||
/* Deallocate */
|
/* Deallocate */
|
||||||
hm_free(map);
|
hm_free(map);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "../set/myset.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void test_set1(void) {
|
||||||
|
set_s *set = set_new(sizeof(int));
|
||||||
|
assert(set != NULL);
|
||||||
|
|
||||||
|
int a = 10;
|
||||||
|
int b = 20;
|
||||||
|
int another_a = 10;
|
||||||
|
int missing = 99;
|
||||||
|
|
||||||
|
assert(set_add(set, &a) == 0);
|
||||||
|
assert(set_add(set, &b) == 0);
|
||||||
|
/* Duplicate values are ignored and treated as success. */
|
||||||
|
assert(set_add(set, &another_a) == 0);
|
||||||
|
|
||||||
|
assert(set_contains(set, &a) == 1);
|
||||||
|
assert(set_contains(set, &another_a) == 1);
|
||||||
|
assert(set_contains(set, &missing) == 0);
|
||||||
|
|
||||||
|
assert(set_remove(set, &a) == 0);
|
||||||
|
assert(set_contains(set, &a) == 0);
|
||||||
|
assert(set_remove(set, &a) == -1);
|
||||||
|
|
||||||
|
assert(set_clear(set) == 0);
|
||||||
|
assert(set_contains(set, &b) == 0);
|
||||||
|
|
||||||
|
set_free(set);
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "../socket/mysocket.h"
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
void test_socket1(void) {
|
|
||||||
sock_platform_init();
|
|
||||||
struct addrinfo hints, *res, *p;
|
|
||||||
char ipstr[INET6_ADDRSTRLEN];
|
|
||||||
memset(&hints, 0, sizeof hints);
|
|
||||||
hints.ai_family = AF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
char hostname[1024];
|
|
||||||
printf("hostname> ");
|
|
||||||
fscanf(stdin, "%s", hostname);
|
|
||||||
|
|
||||||
if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
|
|
||||||
fprintf(stderr, "getaddrinfo() failed\n");
|
|
||||||
sock_platform_shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (p = res; p != NULL; p = p->ai_next) {
|
|
||||||
void *addr = 0;
|
|
||||||
char *ipver = 0;
|
|
||||||
struct sockaddr_in *ipv4;
|
|
||||||
if (p->ai_family == AF_INET) {
|
|
||||||
ipv4 = (struct sockaddr_in *)p->ai_addr;
|
|
||||||
addr = &(ipv4->sin_addr);
|
|
||||||
ipver = "IPv4";
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
|
||||||
printf("%s: %s\n", ipver, ipstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(res);
|
|
||||||
sock_platform_shutdown();
|
|
||||||
}
|
|
||||||
+3
-3
@@ -17,7 +17,7 @@ void test_vec1(void);
|
|||||||
|
|
||||||
void test_stack1(void);
|
void test_stack1(void);
|
||||||
|
|
||||||
void test_socket1(void);
|
void test_set1(void);
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
puts("==== [Running Hashmap tests] ====");
|
puts("==== [Running Hashmap tests] ====");
|
||||||
@@ -47,8 +47,8 @@ int main(void) {
|
|||||||
puts("OK!");
|
puts("OK!");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
puts("==== [Running Socket tests] ====");
|
puts("==== [Running Set tests] ====");
|
||||||
test_socket1();
|
test_set1();
|
||||||
puts("OK!");
|
puts("OK!");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ typedef struct my_elem {
|
|||||||
|
|
||||||
/* Functions used to iterate for each vector's element */
|
/* Functions used to iterate for each vector's element */
|
||||||
static void multiply(size_t index, void *elem) {
|
static void multiply(size_t index, void *elem) {
|
||||||
|
(void)index;
|
||||||
my_elem_s *e = (my_elem_s *)elem;
|
my_elem_s *e = (my_elem_s *)elem;
|
||||||
e->age = e->age * 2;
|
e->age = e->age * 2;
|
||||||
}
|
}
|
||||||
@@ -65,12 +66,40 @@ void test_vec1(void) {
|
|||||||
vec_push(v, &last);
|
vec_push(v, &last);
|
||||||
my_elem_s *lastv = (my_elem_s *)vec_pop(v);
|
my_elem_s *lastv = (my_elem_s *)vec_pop(v);
|
||||||
free(lastv);
|
free(lastv);
|
||||||
|
assert(vec_get(v, vec_size(v)) == NULL);
|
||||||
|
assert(vec_remove(v, vec_size(v)) == -1);
|
||||||
|
|
||||||
|
/* Remove from the middle and validate shifted content for elem_size > 1 */
|
||||||
|
assert(vec_remove(v, 1) == 0);
|
||||||
|
my_elem_s *shifted = (my_elem_s *)vec_get(v, 1);
|
||||||
|
assert(shifted != NULL);
|
||||||
|
assert(shifted->age == 19);
|
||||||
|
free(shifted);
|
||||||
|
|
||||||
|
my_elem_s replacement = {
|
||||||
|
.age = 40,
|
||||||
|
.name = "Updated",
|
||||||
|
};
|
||||||
|
assert(vec_set(v, 1, &replacement) == 0);
|
||||||
|
assert(vec_set(v, vec_size(v), &replacement) == -1);
|
||||||
|
my_elem_s *updated = (my_elem_s *)vec_get(v, 1);
|
||||||
|
assert(updated != NULL);
|
||||||
|
assert(updated->age == 40);
|
||||||
|
free(updated);
|
||||||
|
|
||||||
/* Iterate for each element */
|
/* Iterate for each element */
|
||||||
vec_foreach(v, multiply);
|
vec_foreach(v, multiply);
|
||||||
/* Print each element */
|
/* Print each element */
|
||||||
// vec_foreach(v, print);
|
// vec_foreach(v, print);
|
||||||
|
|
||||||
|
/* Shrink to fit current size and ensure data is still readable */
|
||||||
|
size_t before_shrink_size = vec_size(v);
|
||||||
|
assert(vec_shrink(v) == 0);
|
||||||
|
assert(vec_cap(v) == before_shrink_size);
|
||||||
|
my_elem_s *shrink_elem = (my_elem_s *)vec_get(v, 1);
|
||||||
|
assert(shrink_elem != NULL);
|
||||||
|
free(shrink_elem);
|
||||||
|
|
||||||
/* Sort */
|
/* Sort */
|
||||||
vec_sort(v, my_cmp);
|
vec_sort(v, my_cmp);
|
||||||
|
|
||||||
|
|||||||
+83
-28
@@ -22,6 +22,10 @@ static size_t next_power_two(size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec_s *vec_new(size_t initial_capacity, size_t element_size) {
|
vec_s *vec_new(size_t initial_capacity, size_t element_size) {
|
||||||
|
if (element_size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
vec_s *vec = (vec_s *)malloc(sizeof(vec_s));
|
vec_s *vec = (vec_s *)malloc(sizeof(vec_s));
|
||||||
if (vec == NULL) {
|
if (vec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -30,6 +34,11 @@ vec_s *vec_new(size_t initial_capacity, size_t element_size) {
|
|||||||
vec->capacity = next_power_two(initial_capacity);
|
vec->capacity = next_power_two(initial_capacity);
|
||||||
vec->elem_size = element_size;
|
vec->elem_size = element_size;
|
||||||
vec->size = 0;
|
vec->size = 0;
|
||||||
|
if (vec->capacity > SIZE_MAX / vec->elem_size) {
|
||||||
|
free(vec);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
vec->data = malloc(vec->capacity * vec->elem_size);
|
vec->data = malloc(vec->capacity * vec->elem_size);
|
||||||
if (vec->data == NULL) {
|
if (vec->data == NULL) {
|
||||||
free(vec);
|
free(vec);
|
||||||
@@ -58,14 +67,21 @@ int vec_push(vec_s *vec, void *elem) {
|
|||||||
|
|
||||||
if (vec->size + 1 > vec->capacity) {
|
if (vec->size + 1 > vec->capacity) {
|
||||||
/* Reallocate buffer */
|
/* Reallocate buffer */
|
||||||
vec->capacity = next_power_two(vec->size + 1);
|
size_t new_capacity = next_power_two(vec->size + 1);
|
||||||
void *tmp = realloc(vec->data, vec->capacity * vec->elem_size);
|
if (new_capacity > SIZE_MAX / vec->elem_size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tmp = realloc(vec->data, new_capacity * vec->elem_size);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
vec->data = tmp;
|
vec->data = tmp;
|
||||||
|
vec->capacity = new_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the new element */
|
/* Add the new element */
|
||||||
@@ -86,16 +102,18 @@ void vec_free(vec_s *vec) {
|
|||||||
free(vec->data);
|
free(vec->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtx_destroy(&vec->lock);
|
||||||
|
|
||||||
free(vec);
|
free(vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t vec_size(vec_s *vec) {
|
size_t vec_size(vec_s *vec) {
|
||||||
if (vec == NULL) {
|
if (vec == NULL) {
|
||||||
return (size_t)-1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock) != thrd_success) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return (size_t)-1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = vec->size;
|
size_t size = vec->size;
|
||||||
@@ -107,11 +125,11 @@ size_t vec_size(vec_s *vec) {
|
|||||||
|
|
||||||
size_t vec_cap(vec_s *vec) {
|
size_t vec_cap(vec_s *vec) {
|
||||||
if (vec == NULL) {
|
if (vec == NULL) {
|
||||||
return (size_t)-1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock) != thrd_success) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return (size_t)-1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t cap = vec->capacity;
|
size_t cap = vec->capacity;
|
||||||
@@ -122,7 +140,7 @@ size_t vec_cap(vec_s *vec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *vec_get(vec_s *vec, size_t index) {
|
void *vec_get(vec_s *vec, size_t index) {
|
||||||
if (vec == NULL || index > vec->size) {
|
if (vec == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +148,16 @@ void *vec_get(vec_s *vec, size_t index) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (index >= vec->size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void *elem = malloc(vec->elem_size);
|
void *elem = malloc(vec->elem_size);
|
||||||
if (elem == NULL) {
|
if (elem == NULL) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +177,14 @@ int vec_shrink(vec_s *vec) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *tmp = realloc(vec->data, vec->size);
|
size_t new_capacity = (vec->size == 0) ? 1 : vec->size;
|
||||||
|
if (new_capacity > SIZE_MAX / vec->elem_size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tmp = realloc(vec->data, new_capacity * vec->elem_size);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
@@ -159,7 +192,7 @@ int vec_shrink(vec_s *vec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec->data = tmp;
|
vec->data = tmp;
|
||||||
vec->capacity = vec->size;
|
vec->capacity = new_capacity;
|
||||||
|
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
@@ -175,7 +208,7 @@ int vec_clear(vec_s *vec) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(vec->data, 0, vec->size);
|
memset(vec->data, 0, vec->size * vec->elem_size);
|
||||||
vec->size = 0;
|
vec->size = 0;
|
||||||
|
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
@@ -188,15 +221,23 @@ void *vec_pop(vec_s *vec) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vec->size == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock) != thrd_success) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vec->size == 0) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void *e = malloc(vec->elem_size);
|
void *e = malloc(vec->elem_size);
|
||||||
|
if (e == NULL) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
vec->size--;
|
vec->size--;
|
||||||
memcpy(e, (char *)vec->data + (vec->size * vec->elem_size), vec->elem_size);
|
memcpy(e, (char *)vec->data + (vec->size * vec->elem_size), vec->elem_size);
|
||||||
|
|
||||||
@@ -210,29 +251,39 @@ int vec_insert(vec_s *vec, size_t index, void *value) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > vec->size) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock) != thrd_success) {
|
if (index > vec->size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vec->size + 1 > vec->capacity) {
|
if (vec->size + 1 > vec->capacity) {
|
||||||
/* No space, realloc */
|
/* No space, realloc */
|
||||||
void *tmp = realloc(vec->data, next_power_two(vec->size + 1));
|
size_t new_capacity = next_power_two(vec->size + 1);
|
||||||
|
if (new_capacity > SIZE_MAX / vec->elem_size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *tmp = realloc(vec->data, new_capacity * vec->elem_size);
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
vec->data = tmp;
|
vec->data = tmp;
|
||||||
|
vec->capacity = new_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shift memory and copy the new value */
|
/* Shift memory and copy the new value */
|
||||||
size_t tmp_size = vec->size - index + 1;
|
size_t elements_to_move = vec->size - index;
|
||||||
memmove((char *)vec->data + (index * vec->elem_size),
|
memmove((char *)vec->data + ((index + 1) * vec->elem_size),
|
||||||
(char *)vec->data + ((index + 1) * vec->elem_size), tmp_size * vec->elem_size);
|
(char *)vec->data + (index * vec->elem_size), elements_to_move * vec->elem_size);
|
||||||
memcpy((char *)vec->data + (index * vec->elem_size), value, vec->elem_size);
|
memcpy((char *)vec->data + (index * vec->elem_size), value, vec->elem_size);
|
||||||
vec->size++;
|
vec->size++;
|
||||||
|
|
||||||
@@ -246,18 +297,20 @@ int vec_remove(vec_s *vec, size_t index) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > vec->size) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock) != thrd_success) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = vec->size - index;
|
if (index >= vec->size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = vec->size - index - 1;
|
||||||
/* Overwrite bytes */
|
/* Overwrite bytes */
|
||||||
memmove((char *)vec->data + (index * vec->elem_size),
|
memmove((char *)vec->data + (index * vec->elem_size),
|
||||||
(char *)vec->data + ((index + 1) * vec->elem_size), size);
|
(char *)vec->data + ((index + 1) * vec->elem_size), size * vec->elem_size);
|
||||||
vec->size--;
|
vec->size--;
|
||||||
|
|
||||||
mtx_unlock(&vec->lock);
|
mtx_unlock(&vec->lock);
|
||||||
@@ -270,11 +323,13 @@ int vec_set(vec_s *vec, size_t index, void *value) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > vec->size) {
|
if (mtx_lock(&vec->lock) != thrd_success) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtx_lock(&vec->lock), thrd_success) {
|
if (index >= vec->size) {
|
||||||
|
mtx_unlock(&vec->lock);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user