Create first structures and functions.

+ Create ksrarrays.
+ Create ksrbuffers.
+ Create ksrpromises.
+ Create ksregex.
+ Create strings and files helper functions.
+ Define some logging constants.

+ Setup basic unit tests and code coverage.
This commit is contained in:
Madeorsk 2021-07-31 19:05:04 +02:00
parent da24fc588a
commit 46b6f24432
22 changed files with 1130 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# VSCodium
.cache/
# Meson
build/
debug/

1
gcovr.cfg Normal file
View File

@ -0,0 +1 @@
exclude = .*tests/.+

92
include/ksr/arrays.h Normal file
View File

@ -0,0 +1,92 @@
#pragma once
#include <jemalloc/jemalloc.h>
#include <stddef.h>
/*
* KSRARRAY DEFAULT SIZES.
*/
#define KSRARRAY_TINY_SIZE 2
#define KSRARRAY_SMALL_SIZE 8
#define KSRARRAY_BASIC_SIZE 16
#define KSRARRAY_BIG_SIZE 64
#define KSRARRAY_HUGE_SIZE 128
/**
* ksrarray structure.
*/
typedef struct {
void **data; // array data.
size_t length; // currently used size.
size_t _allocated_size; // currently allocated size.
} ksrarray;
/**
* Create a ksrarray with the given start size.
* @param start_size - the allocated size at start.
*/
ksrarray* ksrarray_new(const size_t start_size);
/**
* Create a tiny sized ksrarray.
*/
static inline ksrarray* ksrarray_new_tiny()
{ return ksrarray_new(KSRARRAY_TINY_SIZE); }
/**
* Create a small sized ksrarray.
*/
static inline ksrarray* ksrarray_new_small()
{ return ksrarray_new(KSRARRAY_SMALL_SIZE); }
/**
* Create a normal sized ksrarray.
*/
static inline ksrarray* ksrarray_new_basic()
{ return ksrarray_new(KSRARRAY_BASIC_SIZE); }
/**
* Create a big sized ksrarray.
*/
static inline ksrarray* ksrarray_new_big()
{ return ksrarray_new(KSRARRAY_BIG_SIZE); }
/**
* Create a huge sized ksrarray.
*/
static inline ksrarray* ksrarray_new_huge()
{ return ksrarray_new(KSRARRAY_HUGE_SIZE); }
/**
* Push a new element at the end of a ksrarray.
* @param array - the ksrarray in which to push.
* @param element - the element to push.
*/
void ksrarray_push(ksrarray *array, void *element);
/**
* Remove an element from a ksrarray.
* @param array - the ksrarray in which to remove.
* @param index - the index of the element to remove.
*/
bool ksrarray_remove(ksrarray *array, const size_t index);
/**
* Free a ksrarray.
* /!\ This will NOT free the elements, just the container and its internal data.
*/
static inline void ksrarray_free(ksrarray *array)
{
free(array->data);
free(array);
}
/**
* Get the iterator variable name, used in ksrarray_foreach.
*/
#define _ksrarray_get_iter_name(a, b) b ## _iter
/**
* For each element of the given ksrarray, do...
*/
#define ksrarray_foreach(ksrarray, element) \
for(size_t _ksrarray_get_iter_name(ksrarray, element) = 0; \
(element = ksrarray.data[_ksrarray_get_iter_name(ksrarray, element)]) || true, \
_ksrarray_get_iter_name(ksrarray, element) < ksrarray.length; \
_ksrarray_get_iter_name(ksrarray, element)++)

44
include/ksr/buffers.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <jemalloc/jemalloc.h>
#include <stdio.h>
/**
* ksrbuffer structure.
*/
typedef struct {
char *bytes;
size_t length;
} ksrbuffer;
/**
* Create a buffer of given size.
*/
ksrbuffer* ksrbuffer_new(const size_t buffer_size);
/**
* Create a buffer of given size.
*/
ksrbuffer* ksrbuffer_new_empty(const size_t buffer_size);
/**
* Create a buffer from given size and content.
*/
ksrbuffer* ksrbuffer_new_from_content(const size_t buffer_size, const char *buffer_content);
/**
* Create a buffer of default buffer size.
*/
static inline ksrbuffer* ksrbuffer_new_default()
{
return ksrbuffer_new(BUFSIZ);
}
/**
* Free a ksrbuffer and its content.
*/
static inline void ksrbuffer_free(ksrbuffer *buffer)
{
free(buffer->bytes);
free(buffer);
}

38
include/ksr/files.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <ksr/buffers.h>
#include <stdbool.h>
#include <sys/types.h>
/**
* Expand the path in parameter.
* If the path expands to multiple paths, return the first one.
*/
char* path_exp_simple(const char* path);
/**
* Make all directories to the file in argument.
* @param path - will create the directories for this path.
* @param mode - permissions of created directories.
*/
int mkpath(const char *path, const mode_t mode);
/**
* Read the file.
* @param file - the opened file stream.
* @return - the buffer of the read file.
*/
ksrbuffer* read_file(FILE *file);
/**
* Read the file from its path.
* @param file_path - path of the file to read.
* @return - the buffer of the read file.
*/
ksrbuffer* read_file_from_path(const char *file_path);
/**
* Write a buffer into a file.
* @param file_path - path of the file to read.
* @return - true if the writing is successful, false otherwise.
*/
bool write_file(const char *file_path, const ksrbuffer *buffer);

11
include/ksr/logging.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#define TERMSTYLE_RESET "\033[0m"
#define TERMSTYLE_BLACK "\033[30m"
#define TERMSTYLE_RED "\033[31m"
#define TERMSTYLE_GREEN "\033[32m"
#define TERMSTYLE_YELLOW "\033[33m"
#define TERMSTYLE_BLUE "\033[34m"
#define TERMSTYLE_MAGENTA "\033[35m"
#define TERMSTYLE_CYAN "\033[36m"
#define TERMSTYLE_WHITE "\033[37m"

114
include/ksr/promises.h Normal file
View File

@ -0,0 +1,114 @@
#pragma once
#include <jemalloc/jemalloc.h>
#include <pthread.h>
#include <ksr/arrays.h>
// sleep delay of promise await function, in µs.
#define KSRPROMISES_AWAIT_SLEEP_DELAY 50*1000
/**
* KSR promise.
*/
typedef struct {
pthread_t thread;
ksrarray *then_callbacks; // array of then callbacks.
ksrarray *catch_callbacks; // array of catch callbacks.
bool successful; // determine if the promise has been fulfilled or not.
void *result; // the promised result.
int error_code;
const char *error_message;
} ksrpromise;
// type of promise execution function.
typedef void (*ksrpromise_execution_f)(ksrpromise *promise, void *userdata);
// type of then callback.
typedef void (*ksrpromise_then_callback_f)(void *result, void *userdata);
// type of catch callback.
typedef void (*ksrpromise_catch_callback_f)(int code, const char *message, void *userdata);
/**
* Create a new promise.
* @param exec - promise execution function.
* @param data - custom user data.
* @return - the new promise.
*/
ksrpromise* ksrpromise_new(ksrpromise_execution_f exec, void *userdata);
/**
* Resolve the given promise.
* @param promise - the promise to resolve.
* @param result - the promised result.
*/
void ksrpromise_resolve(ksrpromise *promise, void *result);
/**
* Reject the given promise.
* @param promise - the promise to resolve.
* @param code - the error code.
* @param message - the error message.
*/
void ksrpromise_reject(ksrpromise *promise, int code, const char *message);
/**
* Await a given promise.
* @param promise - the promise to await.
* @return - the promised result.
*/
void* ksrpromise_await(ksrpromise *promise);
/**
* Set a new callback to call on promise resolution.
* @param promise - the promise for which to set a resolution callback.
* @param callback - the callback to set.
*/
void ksrpromise_then(ksrpromise *promise, ksrpromise_then_callback_f, void *userdata);
/**
* Set a new callback to call on promise rejection.
* @param promise - the promise for which to set a rejection callback.
* @param callback - a callback to set.
*/
void ksrpromise_catch(ksrpromise *promise, ksrpromise_catch_callback_f, void *userdata);
/**
* Determine if the given promise is successful or not.
* @param - the promise to check.
*/
static inline bool ksrpromise_is_successful(const ksrpromise *promise)
{
return promise->successful;
}
/**
* Determine if the given promise is failed or not.
* @param - the promise to check.
*/
static inline bool ksrpromise_is_failed(const ksrpromise *promise)
{
return !!promise->error_code;
}
/**
* Determine if the given promise is running or not.
* @param - the promise to check.
*/
static inline bool ksrpromise_is_running(const ksrpromise *promise)
{
return !ksrpromise_is_successful(promise) && !ksrpromise_is_failed(promise);
}
/**
* Free the promise.
* /!\ The promise is freed automatically on rejection or resolution.
* @param promise - the promise to free.
*/
static inline void ksrpromise_free(ksrpromise *promise)
{
// free arrays of callbacks.
ksrarray_free(promise->then_callbacks);
ksrarray_free(promise->catch_callbacks);
// free promise data.
free(promise);
}

63
include/ksr/regex.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <jemalloc/jemalloc.h>
#include <regex.h>
/**
* Regex structure.
*/
typedef struct {
regex_t expr;
unsigned nmatches;
char **matches;
} ksregex;
/**
* Create a ksregex from an expression.
* @param expression - the expression to compile.
* @param nmatches - the number of matches groups to get, 0 if you want none.
* @param flags - regex flags.
* @return - the created regex, NULL if an error happen.
*/
ksregex* ksregex_new(const char *expression, unsigned nmatches, int flags);
/**
* Create a ksregex from an expression with 0 match group.
* @param expression - the expression to compile.
* @param flags - regex flags.
*/
static inline ksregex* ksregex_new_nogroup(const char *expression, int flags)
{ return ksregex_new(expression, 0, flags); }
/**
* Try to match the regex with a tested string.
* @param regex - the regex to match.
* @param tested - the string that should match the regex.
* @return - true if the string matches, false otherwise.
*/
bool ksregex_matches(ksregex *regex, const char *tested);
/**
* Free ksregex matches.
*/
static inline void _ksregex_free_matches(ksregex *regex)
{
if (regex->matches)
{
// free all matches.
for (unsigned i = 0; i < regex->nmatches; i++) free(regex->matches[i]);
// free matches array.
free(regex->matches);
regex->matches = NULL;
}
}
/**
* Free a ksregex.
*/
static inline void ksregex_free(ksregex *regex)
{
// free matches groups.
_ksregex_free_matches(regex);
free(regex); // free regex data.
}

17
include/ksr/string.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <string.h>
#include <stdbool.h>
/**
* Check if two strings are equal.
* @param a - the first string to check.
* @param b - the second string to check.
*/
static inline bool str_equals(const char *a, const char *b)
{ return 0 == strcmp(a, b); }
/**
* Convert a string to lowercase.
*/
char* to_lowercase(const char* str);

42
meson.build Normal file
View File

@ -0,0 +1,42 @@
project('ksr', 'c', version : '0.1', default_options : ['warning_level=3'])
c = meson.get_compiler('c')
# declare the dependencies of the library.
deps = [ dependency('jemalloc') ]
# add pthread dependency
deps += c.find_library('pthread')
# includes
include_dirs = [ 'include' ]
# source files.
libsrc = [
'src/arrays.c',
'src/buffers.c',
'src/promises.c',
'src/regex.c',
'src/files.c',
'src/string.c',
]
# library target.
libksr = library('ksr', libsrc, dependencies : deps, include_directories : include_dirs, install : true)
# add the library to the dependencies of the executables.
deps += declare_dependency(link_with : libksr)
# test executables.
test_ksrarrays = executable('test_ksrarrays', [ 'tests/ksrarrays.c' ], dependencies : deps, include_directories : include_dirs, install : false)
test_ksrbuffers = executable('test_ksrbuffers', [ 'tests/ksrbuffers.c' ], dependencies : deps, include_directories : include_dirs, install : false)
test_ksrpromises = executable('test_ksrpromises', [ 'tests/ksrpromises.c' ], dependencies : deps, include_directories : include_dirs, install : false)
test_ksregex = executable('test_ksregex', [ 'tests/ksregex.c' ], dependencies : deps, include_directories : include_dirs, install : false)
test_ksrstring = executable('test_ksrstring', [ 'tests/ksrstring.c' ], dependencies : deps, include_directories : include_dirs, install : false)
test_ksrfiles = executable('test_ksrfiles', [ 'tests/ksrfiles.c' ], dependencies : deps, include_directories : include_dirs, install : false)
# tests.
test('ksrarrays', test_ksrarrays)
test('ksrbuffers', test_ksrbuffers)
test('ksrpromises', test_ksrpromises)
test('ksregex', test_ksregex)
test('ksrfiles', test_ksrfiles)
test('ksrstring', test_ksrstring)

41
src/arrays.c Normal file
View File

@ -0,0 +1,41 @@
#include <ksr/arrays.h>
ksrarray* ksrarray_new(const size_t start_size)
{
// allocate array.
ksrarray *array = malloc(sizeof(ksrarray));
// initialize array fields.
array->_allocated_size = start_size;
array->length = 0;
// allocate array from start size.
array->data = malloc(sizeof(void*) * array->_allocated_size);
return array; // return initialized array.
}
void ksrarray_push(ksrarray *array, void *element)
{
if (array->length == array->_allocated_size)
{ // cannot add a new element without allocating more memory.
array->_allocated_size *= 2; // allocate two times bigger space.
array->data = realloc(array->data, sizeof(void*) * array->_allocated_size); // reallocate array.
}
// push the element at the end of the array.
array->data[array->length++] = element;
}
bool ksrarray_remove(ksrarray *array, const size_t index)
{
if (index >= array->length)
// the index is not associated with a value, cannot delete it.
return false;
array->length--; // length is decreasing because we are removing a value.
for (size_t i = index; i < array->length; i++)
// moving each value left.
array->data[i] = array->data[i+1];
return true;
}

36
src/buffers.c Normal file
View File

@ -0,0 +1,36 @@
#include <ksr/buffers.h>
#include <string.h>
ksrbuffer* ksrbuffer_new(const size_t buffer_size)
{
// allocate buffer.
ksrbuffer *buffer = malloc(sizeof(ksrbuffer));
buffer->length = buffer_size;
// allocate buffer bytes.
buffer->bytes = malloc(buffer->length);
return buffer; // return allocated buffer.
}
ksrbuffer* ksrbuffer_new_empty(const size_t buffer_size)
{
// create buffer.
ksrbuffer *buffer = ksrbuffer_new(buffer_size);
// cleanup allocated memory.
memset(buffer->bytes, 0, buffer->length);
return buffer; // return created buffer.
}
ksrbuffer* ksrbuffer_new_from_content(const size_t buffer_size, const char *buffer_content)
{
// create buffer.
ksrbuffer *buffer = ksrbuffer_new(buffer_size);
// cleanup allocated memory.
memcpy(buffer->bytes, buffer_content, buffer->length);
return buffer; // return created buffer.
}

115
src/files.c Normal file
View File

@ -0,0 +1,115 @@
#include "ksr/buffers.h"
#include <ksr/files.h>
#include <string.h>
#include <wordexp.h>
#include <dirent.h>
#include <libgen.h>
#include <sys/stat.h>
#include <errno.h>
char* path_exp_simple(const char* path)
{
// expanding the path.
wordexp_t exp;
wordexp(path, &exp, 0);
// copying the first expanded path.
char *expanded_path = strdup(exp.we_wordv[0]);
wordfree(&exp); // freeing the path expansion.
return expanded_path; // returning the first expanded path.
}
int mkpath(const char *path, const mode_t mode)
{
// getting the current directory path to create.
char *fullpath = strdup(path);
char *dirpath = dirname(fullpath);
int res; // result of mkpath / mkdir.
DIR *dir = opendir(dirpath); // trying to open the directory, to see if it exists.
if (dir) // the parent directory exists, closing it.
closedir(dir);
else
{ // the parent directory does not exist, creating it recursively.
res = mkpath(dirpath, mode);
if (res != 0) goto err; // returning the error instantly if there is one.
}
// creating the current directory.
res = mkdir(dirpath, mode);
if (errno == EEXIST)
// the directory exists, everything have been created correctly.
res = 0;
err:
free(fullpath);
return res; // if an error happened while creating, returning it.
}
ksrbuffer* read_file(FILE *file)
{
if (file)
{ // the file stream is open.
// searching the end of the file.
fseek(file, 0, SEEK_END);
long filesize = ftell(file); // getting file size.
// getting back to the beginning of the file.
fseek(file, 0, SEEK_SET);
// allocating a buffer corresponding to the file size, plus the end of string char.
ksrbuffer *buffer = ksrbuffer_new(filesize + 1);
// reading the file into the buffer.
fread(buffer->bytes, 1, buffer->length, file);
buffer->bytes[buffer->length - 1] = 0; // 0-terminated string.
return buffer; // returning allocated buffer.
}
else // the file is not readable, just returning NULL as we cannot read it.
return NULL;
}
ksrbuffer* read_file_from_path(const char *file_path)
{
// expanding the file path.
char *fullpath = path_exp_simple(file_path);
// opening file.
FILE *file = fopen(fullpath, "r");
free(fullpath); // freeing used full path.
if (file)
{ // the file is readable.
// reading file stream.
ksrbuffer *buffer = read_file(file);
fclose(file); // closing the read file.
return buffer; // returning allocated buffer.
}
else // the file is not readable, just returning NULL as we cannot read it.
return NULL;
}
bool write_file(const char *file_path, const ksrbuffer *buffer)
{
// expanding the file path.
char *fullpath = path_exp_simple(file_path);
// creating directories if it is necessary.
mkpath(fullpath, 0755);
// opening file.
FILE *file = fopen(fullpath, "w");
free(fullpath); // free full path to file.
if (file)
{ // the file is writable.
// write buffer bytes to the file.
fwrite(buffer->bytes, 1, buffer->length, file);
fclose(file); // closing file stream.
return true; // returning true because the file have been written.
}
else
return false; // returning false because the file could not be written.
}

168
src/promises.c Normal file
View File

@ -0,0 +1,168 @@
#include "ksr/arrays.h"
#include <ksr/promises.h>
#include <signal.h>
#include <unistd.h>
// global variable indicating if the program has been interrupted or not.
bool interrupted = false;
/**
* Function launched on signal reception.
* @param signal - the received signal.
*/
void onsigint(/*int signal*/)
{
//if (signal == SIGINT)
// the program is now interrupted.
interrupted = true;
}
/**
* Promise thread structure.
*/
typedef struct {
ksrpromise *promise;
ksrpromise_execution_f exec;
void *data;
} promise_thread_data;
/**
* Promise then user callback.
*/
typedef struct {
ksrpromise_then_callback_f callback;
void *userdata;
} promise_then_user_callback;
/**
* Promise catch user callback.
*/
typedef struct {
ksrpromise_catch_callback_f callback;
void *userdata;
} promise_catch_user_callback;
/**
* The main promise thread function.
* @param data - thread data.
*/
void* promise_thread(void *data)
{
// reading thread data.
promise_thread_data *thread_data = data;
// executing the promise.
thread_data->exec(thread_data->promise, thread_data->data);
return NULL; // this thread has no result.
}
ksrpromise* ksrpromise_new(ksrpromise_execution_f exec, void *data)
{
// allocate the promise.
ksrpromise *promise = malloc(sizeof(ksrpromise));
promise->then_callbacks = ksrarray_new_tiny();
promise->catch_callbacks = ksrarray_new_tiny();
promise->successful = false;
promise->result = NULL;
promise->error_code = 0;
promise->error_message = NULL;
// allocate thread data.
promise_thread_data *thread_data = malloc(sizeof(promise_thread_data));
// assign thread data.
thread_data->promise = promise;
thread_data->exec = exec;
thread_data->data = data;
// initialize and run a new thread.
pthread_create(&promise->thread, NULL, promise_thread, thread_data);
return promise; // returning the created promise.
}
/**
* Run then callbacks for the given promise.
* @param promise - the promise for which to run then callbacks.
*/
void ksrpromise_run_then_callbacks(ksrpromise *promise)
{
promise_then_user_callback *user_callback; // initialize user callback pointer.
for (size_t i = 0; i < promise->then_callbacks->length; i++)
{ // for each then callback, execute it.
user_callback = promise->then_callbacks->data[i]; // getting callback.
(user_callback->callback)(promise->result, user_callback->userdata); // executing callback with promised result.
}
}
void ksrpromise_resolve(ksrpromise *promise, void *result)
{
// save success and result.
promise->successful = true;
promise->result = result;
// running then callbacks.
ksrpromise_run_then_callbacks(promise);
}
/**
* Run reject callbacks for the given promise.
* @param promise - the promise for which to run reject callbacks.
*/
void ksrpromise_run_catch_callbacks(ksrpromise *promise)
{
promise_catch_user_callback *user_callback; // initialize user callback pointer.
for (size_t i = 0; i < promise->catch_callbacks->length; i++)
{ // for each catch callback, execute it.
user_callback = promise->catch_callbacks->data[i]; // getting callback.
(user_callback->callback)(promise->error_code, promise->error_message, user_callback->userdata); // executing callback with the promise error.
}
}
void ksrpromise_reject(ksrpromise *promise, int code, const char *message)
{
// save error details.
promise->error_code = code;
promise->error_message = message;
// running catch callbacks.
ksrpromise_run_catch_callbacks(promise);
}
void* ksrpromise_await(ksrpromise *promise)
{
// register signal reception callback.
signal(SIGINT, onsigint);
while (!interrupted)
{ // infinite loop, waiting for promise end or interruption.
if (!ksrpromise_is_running(promise))
// promise is not running anymore, returning result if there is one.
return promise->result;
usleep(KSRPROMISES_AWAIT_SLEEP_DELAY); // sleeping a bit...
}
return NULL; // the promise have been interrupted before the result was found.
}
void ksrpromise_then(ksrpromise *promise, ksrpromise_then_callback_f callback, void *userdata)
{
// allocate user callback.
promise_then_user_callback *user_callback = malloc(sizeof(promise_then_user_callback));
user_callback->callback = callback;
user_callback->userdata = userdata;
// we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951).
ksrarray_push(promise->then_callbacks, user_callback);
}
void ksrpromise_catch(ksrpromise *promise, ksrpromise_catch_callback_f callback, void *userdata)
{
// allocate user callback.
promise_catch_user_callback *user_callback = malloc(sizeof(promise_catch_user_callback));
user_callback->callback = callback;
user_callback->userdata = userdata;
// we use a pointer of the given function pointer as a workaround for generic arrays (see https://stackoverflow.com/a/16682718/7496951).
ksrarray_push(promise->catch_callbacks, user_callback);
}

58
src/regex.c Normal file
View File

@ -0,0 +1,58 @@
#include <ksr/regex.h>
#include <regex.h>
#include <string.h>
ksregex* ksregex_new(const char *expression, unsigned nmatches, int flags)
{
ksregex *regex = malloc(sizeof(ksregex));
regex->nmatches = nmatches;
regex->matches = NULL;
if (regcomp(&regex->expr, expression, // compile expression in regex.
flags | REG_EXTENDED | (nmatches == 0 ? REG_NOSUB : 0)) != 0)
return NULL; // an error happened, returning NULL.
return regex; // return created regex.
}
void ksregex_save_string_matches(ksregex *regex, regmatch_t *raw_matches, const char *source)
{
// allocate string matches array.
regex->matches = malloc(sizeof(char *) * regex->nmatches);
// save full string match.
regex->matches[0] = strdup(source);
for(unsigned i = 1; i < regex->nmatches; i++)
{ // for each group match, converting it to an independent string.
regoff_t current_match_length = raw_matches[i].rm_eo - raw_matches[i].rm_so;
regex->matches[i] = malloc(current_match_length + 1); // allocate the current group match string.
// copying the string part from source to the current match string.
strncpy(regex->matches[i], &source[raw_matches[i].rm_so], current_match_length);
regex->matches[i][current_match_length] = 0; // set end of string.
}
}
bool ksregex_matches(ksregex *regex, const char *tested)
{
// if there was matches, free them.
_ksregex_free_matches(regex);
// allocate raw matches array.
regmatch_t *raw_matches = malloc(sizeof(regmatch_t) * regex->nmatches);
if (regexec(&regex->expr, tested, regex->nmatches, raw_matches, 0) != 0)
{ // an error happened, freeing results then returning NULL.
free(raw_matches); // free raw groups matches.
return false;
}
if (regex->nmatches > 0)
{ // there are matches, getting them.
ksregex_save_string_matches(regex, raw_matches, tested);
}
free(raw_matches); // free raw groups matches.
return true; // no error, return true.
}

15
src/string.c Normal file
View File

@ -0,0 +1,15 @@
#include <ksr/string.h>
#include <ctype.h>
char* to_lowercase(const char* src)
{
// duplicate the string.
char *lowercased = strdup(src);
char *ret = lowercased; // store the first pointer to return it at the end of the conversion.
for (;*lowercased; lowercased++)
// for each character, convert it to lowercase.
*lowercased = tolower(*lowercased);
return ret; // returning the first string pointer.
}

58
tests/ksrarrays.c Normal file
View File

@ -0,0 +1,58 @@
#include <ksr/arrays.h>
#include <assert.h>
#include <string.h>
int main(/*int argc, char *argv[]*/)
{
// try to create arrays.
ksrarray *array_tiny = ksrarray_new_tiny();
ksrarray *array_small = ksrarray_new_small();
ksrarray *array_basic = ksrarray_new_basic();
ksrarray *array_big = ksrarray_new_big();
ksrarray *array_huge = ksrarray_new_huge();
// try to push things in arrays.
ksrarray_push(array_tiny, "hello");
ksrarray_push(array_tiny, "world");
// check that things have been inserted properly.
assert(strcmp(array_tiny->data[0], "hello") == 0);
assert(strcmp(array_tiny->data[1], "world") == 0);
assert(array_tiny->_allocated_size == KSRARRAY_TINY_SIZE);
assert(array_tiny->length == 2);
// try to remove something.
ksrarray_remove(array_tiny, 0);
// try to remove something that does not exists.
ksrarray_remove(array_tiny, 3);
// check that the removal produces a good result.
assert(strcmp(array_tiny->data[0], "world") == 0);
assert(array_tiny->_allocated_size == KSRARRAY_TINY_SIZE);
assert(array_tiny->length == 1);
// insert more things.
ksrarray_push(array_tiny, "hello");
ksrarray_push(array_tiny, "you");
ksrarray_push(array_tiny, "!");
// check that the allocated size has increased.
assert(array_tiny->length == 4);
assert(array_tiny->_allocated_size == 2*KSRARRAY_TINY_SIZE);
// check that foreach is working.
char *element;
ksrarray_foreach((*array_tiny), element)
{
assert(element);
}
// free arrays.
ksrarray_free(array_tiny);
ksrarray_free(array_small);
ksrarray_free(array_basic);
ksrarray_free(array_big);
ksrarray_free(array_huge);
return 0;
}

49
tests/ksrbuffers.c Normal file
View File

@ -0,0 +1,49 @@
#include <assert.h>
#include <ksr/buffers.h>
int main(/*int argc, char *argv[]*/)
{
// create an empty buffer and check that it is properly instantiated.
ksrbuffer *empty_buffer = ksrbuffer_new_empty(30);
assert(empty_buffer->bytes[0] == 0);
assert(empty_buffer->bytes[10] == 0);
assert(empty_buffer->bytes[20] == 0);
assert(empty_buffer->bytes[29] == 0);
// create a buffer and set some bytes.
ksrbuffer *buffer = ksrbuffer_new(20);
buffer->bytes[0] = 12;
buffer->bytes[5] = 3;
buffer->bytes[10] = 44;
buffer->bytes[15] = 54;
buffer->bytes[19] = 8;
// create a buffer from existing content, taking raw content from the previous buffer.
ksrbuffer *copied_buffer = ksrbuffer_new_from_content(buffer->length, buffer->bytes);
// checking that content has been properly copied.
assert(buffer->bytes[0] == copied_buffer->bytes[0]);
assert(buffer->bytes[5] == copied_buffer->bytes[5]);
assert(buffer->bytes[10] == copied_buffer->bytes[10]);
assert(buffer->bytes[15] == copied_buffer->bytes[15]);
assert(buffer->bytes[19] == copied_buffer->bytes[19]);
assert(copied_buffer->bytes[0] == 12);
assert(copied_buffer->bytes[5] == 3);
assert(copied_buffer->bytes[10] == 44);
assert(copied_buffer->bytes[15] == 54);
assert(copied_buffer->bytes[19] == 8);
// try to create a default buffer.
ksrbuffer *default_buffer = ksrbuffer_new_default();
assert(default_buffer->length == BUFSIZ);
// free buffers.
ksrbuffer_free(empty_buffer);
ksrbuffer_free(buffer);
ksrbuffer_free(copied_buffer);
ksrbuffer_free(default_buffer);
return 0;
}

45
tests/ksregex.c Normal file
View File

@ -0,0 +1,45 @@
#include <assert.h>
#include <ksr/regex.h>
#include <ksr/string.h>
int main(/*int argc, char *argv[]*/)
{
// try a simple regex and check that it matches properly.
ksregex *reg1 = ksregex_new_nogroup("^[0-9]+$", 0);
assert(ksregex_matches(reg1, "12") == true);
assert(reg1->matches == NULL);
// try a more complex regex with matches groups.
ksregex *reg2 = ksregex_new("^([0-9])([a-z])$", 3, REG_ICASE);
assert(ksregex_matches(reg2, "1a"));
assert(!ksregex_matches(reg2, "H8"));
assert(!ksregex_matches(reg2, "4MM"));
assert(!ksregex_matches(reg2, "4_"));
assert(ksregex_matches(reg2, "3F"));
// check that groups have been extracted properly.
assert(str_equals("3F", reg2->matches[0]));
assert(str_equals("3", reg2->matches[1]));
assert(str_equals("F", reg2->matches[2]));
// check that groups are properly extracted even in more complex cases.
ksregex *reg3 = ksregex_new("^([0-9]+)([a-z]?)$", 3, 0);
assert(ksregex_matches(reg3, "123545"));
assert(str_equals("123545", reg3->matches[0]));
assert(str_equals("123545", reg3->matches[1]));
assert(str_equals("", reg3->matches[2]));
// check that matches are empty when matching fail.
assert(!ksregex_matches(reg3, "123bA"));
assert(reg3->matches == NULL);
// try to create an invalid regex.
assert(ksregex_new_nogroup("^(123$", 0) == NULL);
// try to free instantiated regex.
ksregex_free(reg1);
ksregex_free(reg2);
ksregex_free(reg3);
return 0;
}

33
tests/ksrfiles.c Normal file
View File

@ -0,0 +1,33 @@
#include <ksr/files.h>
#include <assert.h>
int main(/*int argc, char *argv[]*/)
{
// delete test directory if it exists.
system("rm -Rf ksrtesttesttmp");
// create a new buffer and write it in a file.
ksrbuffer *buffer = ksrbuffer_new_from_content(12, "Hello World!");
write_file("ksrtesttesttmp/tmp/test.tmp", buffer);
// try to read the previously written file, and checking that the buffer has been read properly.
ksrbuffer *read_buffer = read_file_from_path("test.tmp");
assert(read_buffer->length == 13);
assert(read_buffer->bytes[0] == 'H');
assert(read_buffer->bytes[3] == 'l');
assert(read_buffer->bytes[7] == 'o');
assert(read_buffer->bytes[11] == '!');
assert(read_buffer->bytes[12] == 0);
// delete test directory.
system("rm -Rf ksrtesttesttmp");
// try to write a file where it is not writable.
assert(write_file("/dev/test/test.tmp", buffer) == false);
// try to read a non-existing file.
assert(read_file_from_path("/dev/test.tmp") == NULL);
// try to read a NULL file.
assert(read_file(NULL) == NULL);
return 0;
}

69
tests/ksrpromises.c Normal file
View File

@ -0,0 +1,69 @@
#include <assert.h>
#include <ksr/promises.h>
#include <ksr/string.h>
#include <unistd.h>
#include <signal.h>
void promise_execution_ok(ksrpromise *promise, void *data)
{
// checking that user data has been properly transfered.
assert(data == NULL);
sleep(1);
// try to resolve the promise.
ksrpromise_resolve(promise, "OK");
}
void promise_execution_sigint(/*ksrpromise *promise, void *data*/)
{
// raise a signal to interrupt await.
raise(SIGINT);
}
void promise_execution_error(ksrpromise *promise, void *data)
{
// try to read the error details from user data ; the content will be checked in the catch callback.
const char* error = data;
sleep(1);
// try to reject the promise with an error.
ksrpromise_reject(promise, 124, error);
}
void on_promise_ok(void *result, void *userdata)
{
// checking that result and user data have been properly transfered.
assert(str_equals("OK", result));
assert(userdata == NULL);
}
void on_promise_failed(int error_code, const char *error_message, void *userdata)
{
// checking that error details and user data have been properly transfered.
assert(error_code == 124);
assert(str_equals("hello", error_message));
assert(str_equals("blabla", userdata));
}
int main(/*int argc, char *argv[]*/)
{
// try to create a promise.
ksrpromise *promise = ksrpromise_new(promise_execution_ok, NULL);
// checking that then is properly launched.
ksrpromise_then(promise, on_promise_ok, NULL);
// checking that await is working properly on a successful promise.
assert(str_equals("OK", ksrpromise_await(promise)));
// try to create a promise which fails.
ksrpromise *promise_fail = ksrpromise_new(promise_execution_error, "hello");
// checking that catch is properly launched.
ksrpromise_catch(promise_fail, on_promise_failed, "blabla");
// checking that await is working properly on a promise which fails.
assert(ksrpromise_await(promise_fail) == NULL);
// try to create a promise that will be interrupted.
ksrpromise *interrupted_promise = ksrpromise_new(promise_execution_sigint, NULL);
// checking that interruption is handled properly in an await.
assert(NULL == ksrpromise_await(interrupted_promise));
return 0;
}

15
tests/ksrstring.c Normal file
View File

@ -0,0 +1,15 @@
#include "ksr/string.h"
#include <assert.h>
#include <ksr/buffers.h>
int main(/*int argc, char *argv[]*/)
{
assert(str_equals("hello", to_lowercase("heLLo")));
assert(str_equals("hello", to_lowercase("HELLO")));
assert(str_equals("hello", to_lowercase("Hello")));
assert(str_equals("hello", to_lowercase("hellO")));
assert(!str_equals("heLLo", to_lowercase("heLLo")));
return 0;
}