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:
parent
da24fc588a
commit
46b6f24432
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# VSCodium
|
||||
.cache/
|
||||
|
||||
# Meson
|
||||
build/
|
||||
debug/
|
92
include/ksr/arrays.h
Normal file
92
include/ksr/arrays.h
Normal 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
44
include/ksr/buffers.h
Normal 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
38
include/ksr/files.h
Normal 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
11
include/ksr/logging.h
Normal 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
114
include/ksr/promises.h
Normal 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
63
include/ksr/regex.h
Normal 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
17
include/ksr/string.h
Normal 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
42
meson.build
Normal 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
41
src/arrays.c
Normal 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
36
src/buffers.c
Normal 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
115
src/files.c
Normal 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
168
src/promises.c
Normal 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
58
src/regex.c
Normal 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(®ex->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(®ex->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
15
src/string.c
Normal 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
58
tests/ksrarrays.c
Normal 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
49
tests/ksrbuffers.c
Normal 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
45
tests/ksregex.c
Normal 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
33
tests/ksrfiles.c
Normal 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
69
tests/ksrpromises.c
Normal 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
15
tests/ksrstring.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user