Move everything out of src folder

This commit is contained in:
Leonardo Vencovsky committed 2023-12-13 15:14:46 +01:00
1 parent 2c1eb41280
commit 47ece66520
135 files changed
+13 -7

No files matched your search

+2638
View File
File diff suppressed because it is too large. Load diff
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_foreach.h
*
* Creation Date: 25/02/2019
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* For-Each macros that work with all Collections within the library.
*
* PFX - Functions prefix
* SNAME - Struct name
* ITERNAME - Iterator variable name
* TARGET - Target data structure variable name
*/
#ifndef CMC_UTL_FOREACH_H
#define CMC_UTL_FOREACH_H
#include "../cor/core.h"
#define CMC_FOREACH(PFX, SNAME, ITERNAME, TARGET) \
for (struct CMC_DEF_ITER(SNAME) ITERNAME = CMC_(PFX, _iter_start)(TARGET); !CMC_(PFX, _iter_at_end)(&ITERNAME); \
CMC_(PFX, _iter_next)(&ITERNAME))
#define CMC_FOREACH_REV(PFX, SNAME, ITERNAME, TARGET) \
for (struct CMC_DEF_ITER(SNAME) ITERNAME = CMC_(PFX, _iter_end)(TARGET); !CMC_(PFX, _iter_at_start)(&ITERNAME); \
CMC_(PFX, _iter_prev)(&ITERNAME))
#endif /* CMC_UTL_FOREACH_H */
+516
View File
@@ -0,0 +1,516 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_futils.h
*
* Creation Date: 15/04/2020
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* Common functions that can be used by Functions Table, instead of writing
* another one every time.
*/
#ifndef CMC_UTL_FUTILS_H
#define CMC_UTL_FUTILS_H
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* cmp
*/
// Signed Integers
static inline int cmc_i64_cmp(int64_t x1, int64_t x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_i32_cmp(int32_t x1, int32_t x2)
{
return cmc_i64_cmp((int64_t)x1, (int64_t)x2);
}
static inline int cmc_i16_cmp(int16_t x1, int16_t x2)
{
return cmc_i64_cmp((int64_t)x1, (int64_t)x2);
}
static inline int cmc_i8_cmp(int8_t x1, int8_t x2)
{
return cmc_i64_cmp((int64_t)x1, (int64_t)x2);
}
// Unsigned Integers
static inline int cmc_u64_cmp(uint64_t x1, uint64_t x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_u32_cmp(uint32_t x1, uint32_t x2)
{
return cmc_u64_cmp((uint64_t)x1, (uint64_t)x2);
}
static inline int cmc_u16_cmp(uint16_t x1, uint16_t x2)
{
return cmc_u64_cmp((uint64_t)x1, (uint64_t)x2);
}
static inline int cmc_u8_cmp(uint8_t x1, uint8_t x2)
{
return cmc_u64_cmp((uint64_t)x1, (uint64_t)x2);
}
// Other Integers
static inline int cmc_size_cmp(size_t x1, size_t x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_imax_cmp(intmax_t x1, intmax_t x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_umax_cmp(uintmax_t x1, uintmax_t x2)
{
return (x1 > x2) - (x1 < x2);
}
// Floating Point
static inline int cmc_float_cmp(float x1, float x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_double_cmp(double x1, double x2)
{
return (x1 > x2) - (x1 < x2);
}
static inline int cmc_double_long_cmp(long double x1, long double x2)
{
return (x1 > x2) - (x1 < x2);
}
// String
static inline int cmc_chr_cmp(char ch1, char ch2)
{
return (ch1 > ch2) - (ch1 < ch2);
}
static inline int cmc_str_cmp(char *ch1, char *ch2)
{
// Assuming ch1 and ch2 are both null terminated character sequences
return strcmp(ch1, ch2);
}
/**
* cpy
*/
/* For basic data types a simple assignment is usually enough */
static inline char *cmc_str_cpy(char *str)
{
size_t len = strlen(str) + 1;
char *result = malloc(len);
if (!result)
return NULL;
return memcpy(result, str, len);
}
/**
* str
*/
// Signed Integers
static inline bool cmc_i8_str(FILE *file, int8_t element)
{
return fprintf(file, "%" PRIi8 "", element) > 0;
}
static inline bool cmc_i16_str(FILE *file, int16_t element)
{
return fprintf(file, "%" PRIi16 "", element) > 0;
}
static inline bool cmc_i32_str(FILE *file, int32_t element)
{
return fprintf(file, "%" PRIi32 "", element) > 0;
}
static inline bool cmc_i64_str(FILE *file, int64_t element)
{
return fprintf(file, "%" PRIi64 "", element) > 0;
}
// Unsigned Integers
static inline bool cmc_u8_str(FILE *file, uint8_t element)
{
return fprintf(file, "%" PRIu8 "", element) > 0;
}
static inline bool cmc_u16_str(FILE *file, uint16_t element)
{
return fprintf(file, "%" PRIu16 "", element) > 0;
}
static inline bool cmc_u32_str(FILE *file, uint32_t element)
{
return fprintf(file, "%" PRIu32 "", element) > 0;
}
static inline bool cmc_u64_str(FILE *file, uint64_t element)
{
return fprintf(file, "%" PRIu64 "", element) > 0;
}
// Other Integers
static inline bool cmc_size_str(FILE *file, size_t element)
{
return fprintf(file, "%" PRIuMAX "", (uintmax_t)element) > 0;
}
static inline bool cmc_imax_str(FILE *file, intmax_t element)
{
return fprintf(file, "%" PRIiMAX "", element) > 0;
}
static inline bool cmc_umax_str(FILE *file, uintmax_t element)
{
return fprintf(file, "%" PRIu64 "", element) > 0;
}
// Floating Point
static inline bool cmc_float_str(FILE *file, float element)
{
return fprintf(file, "%f", element) > 0;
}
static inline bool cmc_double_str(FILE *file, double element)
{
return fprintf(file, "%lf", element) > 0;
}
static inline int cmc_double_long_str(FILE *file, long double element)
{
return fprintf(file, "%LF", element);
}
// String
static inline bool cmc_chr_chr(FILE *file, char element)
{
return fprintf(file, "%c", element) > 0;
}
static inline bool cmc_str_str(FILE *file, char *element)
{
return fprintf(file, "\"%s\"", element) > 0;
}
/**
* free
*/
/* For most data types a simple free() is enough */
/**
* hash
*/
// These integer hashing functions are based on splitmix64
// http://web.archive.org/web/20190929030557/http://xorshift.di.unimi.it/splitmix64.c
// Integers
static inline size_t cmc_i64_hash(int64_t e)
{
size_t x = (size_t)e;
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
static inline size_t cmc_i32_hash(int32_t e)
{
return cmc_i64_hash((int64_t)e);
}
static inline size_t cmc_i16_hash(int16_t e)
{
return cmc_i64_hash((int64_t)e);
}
static inline size_t cmc_i8_hash(int8_t e)
{
return cmc_i64_hash((int64_t)e);
}
// Unsigned Integers
static inline size_t cmc_u64_hash(uint64_t e)
{
size_t x = (size_t)e;
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
static inline size_t cmc_u32_hash(uint32_t e)
{
return cmc_u64_hash((uint64_t)e);
}
static inline size_t cmc_u16_hash(uint16_t e)
{
return cmc_u64_hash((uint64_t)e);
}
static inline size_t cmc_u8_hash(uint8_t e)
{
return cmc_u64_hash((uint64_t)e);
}
// Other Integers
static inline size_t cmc_size_hash(size_t e)
{
size_t x = (size_t)e;
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
static inline size_t cmc_imax_hash(intmax_t e)
{
size_t x = (size_t)e;
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
static inline size_t cmc_umax_hash(uintmax_t e)
{
size_t x = (size_t)e;
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
// Floating Point
static inline size_t cmc_float_hash(float e)
{
// Make both representations of 0.0 hash to the same value
// 0.0 and -0.0
// Since -0.0 has a different binary representation than 0.0
// And 0.0 == -0.0 is always true
if (e == 0.0)
e = 0.0; // if e == -0.0, now it is 0.0
union
{
float a;
size_t b;
} x;
x.a = e;
return x.b & 0xFFFFFFF8;
}
static inline size_t cmc_double_hash(double e)
{
// Make both representations of 0.0 hash to the same value
// 0.0 and -0.0
// Since -0.0 has a different binary representation than 0.0
// And 0.0 == -0.0 is always true
if (e == 0.0)
e = 0.0; // if e == -0.0, now it is 0.0
union
{
double a;
size_t b;
} x;
x.a = e;
return x.b & 0xFFFFFFFFFFFFFFF0;
}
// String
static inline size_t cmc_chr_hash(char e)
{
return cmc_u8_hash((uint8_t)e);
}
// Both djb2 and java are based on a Linear Congruential Generator
// https://en.wikipedia.org/wiki/Linear_congruential_generator
// http://www.cse.yorku.ca/~oz/hash.html
static inline size_t cmc_str_hash_djb2(char *str)
{
size_t hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
// http://www.cse.yorku.ca/~oz/hash.html
static inline size_t cmc_str_hash_sdbm(char *str)
{
size_t hash = 0;
int c;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
// Based on Java's String.hashCode()
static inline size_t cmc_str_hash_java(char *str)
{
size_t hash = UINT64_C(1125899906842597); // prime
int c;
while ((c = *str++))
hash = 31 * hash + c;
return hash;
}
// https://en.wikipedia.org/wiki/MurmurHash
// https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash3.cpp#L81
static inline size_t cmc_str_hash_murmur3(uint64_t e)
{
size_t x = (size_t)e;
x ^= (x >> 33);
x *= 0xff51afd7ed558ccd;
x ^= (x >> 33);
x *= 0xc4ceb9fe1a85ec53;
x ^= (x >> 33);
return x;
}
// A variant or murmurhash3 from
// http://web.archive.org/web/20200310023308/http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
static inline size_t cmc_str_hash_murmur3_variant(uint64_t e)
{
size_t x = (size_t)e;
x ^= (x >> 31);
x *= 0x7fb5d329728ea185;
x ^= (x >> 27);
x *= 0x81dadef4bc2dd44d;
x ^= (x >> 33);
return x;
}
// Others
// These two originally come from
// http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
static inline size_t cmc_i64_hash_mix(int64_t element)
{
size_t e = (size_t)element;
e = (~e) + (e << 21);
e = e ^ (e >> 24);
e = (e + (e << 3)) + (e << 8);
e = e ^ (e >> 14);
e = (e + (e << 2)) + (e << 4);
e = e ^ (e >> 28);
e = e + (e << 31);
return e;
}
static inline size_t cmc_u64_hash_mix(uint64_t element)
{
size_t e = (size_t)element;
e = (~e) + (e << 21);
e = e ^ (e >> 24);
e = (e + (e << 3)) + (e << 8);
e = e ^ (e >> 14);
e = (e + (e << 2)) + (e << 4);
e = e ^ (e >> 28);
e = e + (e << 31);
return e;
}
/**
* pri
*/
/* Can simply use cmp for basic data types */
#endif /* CMC_UTL_FUTILS_H */
+197
View File
@@ -0,0 +1,197 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_log.h
*
* Creation Date: 21/06/2019
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* Simple logging macros
* You can toggle colors defining the macro CMC_LOG_COLOR but be sure that
* your terminal supports ANSI color escape codes.
*
* You can customize the logging utility by accessing cmc_log_config and
* directly modifying its values to better suit your needs.
*/
/**
* What each error code means inside the DEV Collections
*
* TRACE - Trace is used to describe the inner workings of the function or an
* algorithm. It can be useful when trying to find for an error in the
* algorithm or corner cases.
* DEBUG - Mainly used for debugging, tracking certain variables or anything
* useful to help visualizing what your program is doing.
* INFO - Info can be used as a heads up. It is also used to track the main
* collection's allocation and deallocation.
* WARN - Warnings are attributed to all cases where the function can recover
* from and they are usually treated by it. In most cases the user can
* treat these warnings himself.
* ERROR - Something really bad happened. Your program will not crash yet but
* depending on what comes next, it will.
* FATAL - This will probably be the last logging message before your program
* crashes or goes into madness. Fatal errors are commonly attributed
* to dereferencing NULL pointers.
*
* Log Level
*
* TRACE - 1
* DEBUG - 2
* INFO - 3
* WARN - 4
* ERROR - 5
* FATAL - 6
*
* How the Log Level is calculated for tlevel and flevel
*
* - If the log level is 0 all logs are enabled.
* - If the log level (X) is positive:
* - All levels less than X will be disabled
* - If X is greater than the level of FATAL, all logs are disabled
* - If the log level (X) is negative:
* - All levels greater than X are enabled
* - If X is less than the level of FATAL, all logs are enabled
*/
#ifndef CMC_UTL_LOG_H
#define CMC_UTL_LOG_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include "../cor/core.h"
static const char *cmc_log_names[] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" };
#ifdef CMC_LOG_COLOR
static const char *cmc_log_color[] = { "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" };
#endif
enum cmc_log_type
{
CMC_LOG_TRACE = 1,
CMC_LOG_DEBUG = 2,
CMC_LOG_INFO = 3,
CMC_LOG_WARN = 4,
CMC_LOG_ERROR = 5,
CMC_LOG_FATAL = 6
};
static struct
{
int tlevel; /* Terminal Level */
int flevel; /* File Level */
bool tenabled; /* Terminal Output Enabled */
bool fenabled; /* File Output Enabled */
bool enabled; /* Logging enabled */
FILE *file; /* File for Output */
} cmc_log_config = { 0, 0, true, true, true, NULL };
#define cmc_log_trace(fmt, ...) cmc_log(CMC_LOG_TRACE, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define cmc_log_debug(fmt, ...) cmc_log(CMC_LOG_DEBUG, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define cmc_log_info(fmt, ...) cmc_log(CMC_LOG_INFO, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define cmc_log_warn(fmt, ...) cmc_log(CMC_LOG_WARN, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define cmc_log_error(fmt, ...) cmc_log(CMC_LOG_ERROR, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define cmc_log_fatal(fmt, ...) cmc_log(CMC_LOG_FATAL, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
CMC_UNUSED static void cmc_log(enum cmc_log_type log, const char *filename, const char *funcname, unsigned line,
const char *fmt, ...)
{
if (!cmc_log_config.enabled)
return;
if (cmc_log_config.tenabled)
{
int tl = cmc_log_config.tlevel;
if (tl > 0 && (int)log < tl)
goto skip;
if (tl < 0 && ((int)log * -1) < tl)
goto skip;
int i = (int)log - 1;
time_t t = time(NULL);
struct tm *lt = localtime(&t);
char time[32];
time[strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", lt)] = '\0';
#ifdef CMC_LOG_COLOR
fprintf(stderr, "%s %s%5s\x1b[0m at\x1b[90m %s:%s:%u\x1b[0m | ", time, cmc_log_color[i], cmc_log_names[i],
filename, funcname, line);
#else
fprintf(stderr, "%s %5s at %s:%s:%u | ", time, cmc_log_names[i], filename, funcname, line);
#endif
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
fflush(stderr);
}
skip:
if (cmc_log_config.file && cmc_log_config.fenabled)
{
int fl = cmc_log_config.flevel;
if (fl > 0 && (int)log < fl)
return;
if (fl < 0 && ((int)log * -1) < fl)
return;
int i = (int)log - 1;
time_t t = time(NULL);
struct tm *lt = localtime(&t);
char time[32];
time[strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", lt)] = '\0';
fprintf(cmc_log_config.file, "%s %5s at %s:%s:%u | ", time, cmc_log_names[i], filename, funcname, line);
va_list file_args;
va_start(file_args, fmt);
vfprintf(cmc_log_config.file, fmt, file_args);
va_end(file_args);
fprintf(cmc_log_config.file, "\n");
fflush(cmc_log_config.file);
}
}
#endif /* CMC_UTL_LOG_H */
+241
View File
@@ -0,0 +1,241 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_mutex.h
*
* Creation Date: 14/05/2020
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* A very simple, header-only and minimalistic cross-platform mutex
*
* Types
* - cmc_mutex
*
* Functions
* - cmc_mtx_init
* - cmc_mtx_destroy
* - cmc_mtx_lock
* - cmc_mtx_unlock
* - cmc_mtx_trylock
*/
#ifndef CMC_UTL_MUTEX_H
#define CMC_UTL_MUTEX_H
#include <stdbool.h>
#include "../cor/flags.h"
#if defined(_WIN32)
#define CMC_MUTEX_WINDOWS
#elif defined(__unix__)
#define CMC_MUTEX_UNIX
#else
#error "Unknown platform for CMC Mutex"
#endif
/* Platform specific includes */
#if defined(CMC_MUTEX_WINDOWS)
#include <windows.h>
#elif defined(CMC_MUTEX_UNIX)
#include <errno.h>
#include <pthread.h>
#endif
/**
* struct cmc_mutex
*
* A mutex wrapper.
*/
struct cmc_mutex
{
#if defined(CMC_MUTEX_WINDOWS)
HANDLE mutex;
#elif defined(CMC_MUTEX_UNIX)
pthread_mutex_t mutex;
#endif
int flag;
};
/**
* Acquire resources for a mutex.
*
* \param mtx An uninitialized mutex wrapper.
* \return True or false if the mutex was successfully initialized.
*/
static inline bool cmc_mtx_init(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
mtx->mutex = CreateMutex(NULL, FALSE, NULL);
if (mtx->mutex == NULL)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->mutex != NULL;
#elif defined(CMC_MUTEX_UNIX)
int err = pthread_mutex_init(&(mtx->mutex), NULL);
if (err != 0)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return err == 0;
#endif
}
/**
* Release all resources from a mutex. Calling this function on a locked mutex
* causes undefined behavior.
*
* \param mtx A mutex to be destroyed.
* \return True or false if the mutex was successfully destroyed.
*/
static inline bool cmc_mtx_destroy(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
if (!CloseHandle(mtx->mutex))
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_destroy(&(mtx->mutex)) != 0)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#endif
}
/**
* Locks a mutex. If the mutex is already locked, the thread is blocked until
* the mutex can be acquired.
*
* \param mtx A mutex to be locked.
* \return True or false if the lock on the mutex was successfully acquired.
*/
static inline bool cmc_mtx_lock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
DWORD result = WaitForSingleObject(mtx->mutex, INFINITE);
if (result == WAIT_FAILED || result == WAIT_ABANDONED)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_lock(&mtx->mutex) != 0)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return true;
#endif
}
/**
* Unlocks a mutex. If the mutex is already locked, the thread is blocked until
* the mutex can be acquired.
*
* \param mtx A mutex to be unlocked.
* \return True or false if the mutex was successfully unlocked.
*/
static inline bool cmc_mtx_unlock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
if (!ReleaseMutex(mtx->mutex))
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->flag == CMC_FLAG_OK;
#elif defined(CMC_MUTEX_UNIX)
if (pthread_mutex_unlock(&mtx->mutex) != 0)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return mtx->flag == CMC_FLAG_OK;
#endif
}
/**
* Tries to lock a mutex. If the mutex is already locked, the thread is not
* blocked and the function returns false.
*
* \param mtx A mutex to try to lock.
* \return True or false if the lock on the mutex was successfully acquired.
*/
static inline bool cmc_mtx_trylock(struct cmc_mutex *mtx)
{
#if defined(CMC_MUTEX_WINDOWS)
DWORD result = WaitForSingleObject(mtx->mutex, 0);
if (result == WAIT_FAILED || result == WAIT_ABANDONED)
{
mtx->flag = CMC_FLAG_MUTEX;
return false;
}
mtx->flag = CMC_FLAG_OK;
return result != WAIT_TIMEOUT;
#elif defined(CMC_MUTEX_UNIX)
int err = pthread_mutex_trylock(&mtx->mutex);
if (err == EINVAL || err == EFAULT)
mtx->flag = CMC_FLAG_MUTEX;
else
mtx->flag = CMC_FLAG_OK;
return err == 0;
#endif
}
#endif /* CMC_UTL_MUTEX_H */
+246
View File
@@ -0,0 +1,246 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_test.h
*
* Creation Date: 26/06/2019
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* Simple Unit Test Utility
*
* Required imports:
* - assert.h (to integrate with the cmc_assert_state variable)
* - timer.h (to calculate the execution time for the unit test)
*
* CMC_CREATE_UNIT
* Create a UnitTest
* Parameters:
* - UNAME : UnitTest name (const char *)
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
* - BODY : Body containing all tests related to this UnitTest
*
* CMC_CREATE_TEST
* Create a Test inside a UnitTest.
* Parameters:
* - TNAME : Test name (const char *)
* - BODY : Block of code containing a specific test
* Inside the BODY use the macros in ./utl/assert.h. The unit test will keep
* track of cmc_assert_state and automatically pass or fail tests.
*
* CMC_TEST_COLOR
* Define this macro to allow colored output.
*/
#ifndef CMC_UTL_TEST_H
#define CMC_UTL_TEST_H
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "../cor/core.h"
#include "assert.h"
#include "timer.h"
struct cmc_test_info
{
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
uintmax_t assert_total;
uintmax_t assert_failed;
bool aborted;
bool verbose;
};
#ifdef CMC_TEST_COLOR
static inline const char *cmc_test_color(int i)
{
// 0 - Default, all off
// 1 - Failed (red)
// 2 - Passed (green)
// 3 - Box (gray)
// 4 - Name (cyan)
// 5 - Total (yellow)
static const char *cmc_test_colors[] = { "\x1b[0m", "\x1b[31m", "\x1b[32m", "\x1b[90m", "\x1b[36m", "\x1b[33m" };
return cmc_test_colors[i];
#else
static inline const char *cmc_test_color(int i)
{
(void)i;
return "";
#endif
}
/* Logging function */
CMC_UNUSED static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
{
if (aborted)
{
printf(" X UNIT TEST");
printf("%s", cmc_test_color(4));
printf(" %s ", unit_name);
printf("%sABORTED%s", cmc_test_color(5), cmc_test_color(0));
printf(" at %s\n", current_test);
}
else
{
printf("%s", passed ? cmc_test_color(2) : cmc_test_color(1));
printf(" |");
printf("%s", cmc_test_color(0));
printf(" %s", current_test);
size_t str_len = strlen(current_test);
if (str_len < 53)
{
printf("%s", cmc_test_color(3));
for (size_t i = 0; i < 53 - str_len; i++)
putchar('.');
}
printf("%s", passed ? cmc_test_color(2) : cmc_test_color(1));
printf("%s", passed ? "PASSED" : "FAILED");
printf("%s\n", cmc_test_color(0));
}
}
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY) \
uintmax_t UNAME(void) \
{ \
const char *unit_name = #UNAME; \
const char *current_test = NULL; \
\
struct cmc_test_info tinfo = { 0 }; \
struct cmc_timer timer = { 0 }; \
\
tinfo.verbose = VERBOSE; \
tinfo.assert_total = cmc_assert_total; \
tinfo.assert_failed = cmc_assert_failed; \
\
printf("%s", cmc_test_color(3)); \
printf(" +---------------------------------------------------------------+\n"); \
printf(" | "); \
printf("%s", cmc_test_color(0)); \
printf("Unit "); \
printf("%s", cmc_test_color(4)); \
printf("%-56s", unit_name); \
printf("%s", cmc_test_color(3)); \
printf(" |\n +---------------------------------------------------------------+"); \
printf("%s\n", cmc_test_color(0)); \
\
/* Tests */ \
cmc_timer_start(timer); \
\
BODY; \
\
cmc_timer_stop(timer); \
\
tinfo.assert_total = cmc_assert_total - tinfo.assert_total; \
tinfo.assert_failed = cmc_assert_failed - tinfo.assert_failed; \
\
printf("%s", cmc_test_color(3)); \
printf(" +---------------------------------------------------------------+\n | "); \
printf("%s", cmc_test_color(0)); \
printf("Summary : "); \
printf("%s", cmc_test_color(4)); \
printf("%-52s", unit_name); \
printf("%s|\n", cmc_test_color(3)); \
printf(" +---------++----------------+-----------------+-----------------+\n | "); \
printf("%s", cmc_test_color(0)); \
printf("Tests "); \
printf("%s", cmc_test_color(3)); \
printf("||"); \
printf("%s", cmc_test_color(5)); \
printf(" Total %8" PRIuMAX "", tinfo.total); \
printf("%s", cmc_test_color(3)); \
printf(" | "); \
printf("%s", cmc_test_color(2)); \
printf("Passed %8" PRIuMAX "", tinfo.passed); \
printf("%s", cmc_test_color(3)); \
printf(" | "); \
printf("%s", cmc_test_color(1)); \
printf("Failed %8" PRIuMAX "", tinfo.failed); \
printf("%s", cmc_test_color(3)); \
printf(" |\n | "); \
printf("%s", cmc_test_color(0)); \
printf("Asserts "); \
printf("%s", cmc_test_color(3)); \
printf("||"); \
printf("%s", cmc_test_color(5)); \
printf(" Total %8" PRIuMAX "", tinfo.assert_total); \
printf("%s", cmc_test_color(3)); \
printf(" | "); \
printf("%s", cmc_test_color(2)); \
printf("Passed %8" PRIuMAX "", tinfo.assert_total - tinfo.assert_failed); \
printf("%s", cmc_test_color(3)); \
printf(" | "); \
printf("%s", cmc_test_color(1)); \
printf("Failed %8" PRIuMAX "", tinfo.assert_failed); \
printf("%s", cmc_test_color(3)); \
printf(" |\n"); \
printf(" +---------++----------------+-----------------+-----------------+\n | "); \
printf("%s", cmc_test_color(0)); \
printf("Total Runtime : %32.0f milliseconds", timer.result); \
printf(" %s|\n", cmc_test_color(3)); \
printf(" +---------------------------------------------------------------+\n"); \
printf("%s", cmc_test_color(0)); \
printf("\n"); \
\
return tinfo.failed; \
}
#define CMC_CREATE_TEST(TNAME, BODY) \
do \
{ \
current_test = #TNAME; \
\
tinfo.total += 1; \
\
/* Provided by assert.h */ \
cmc_assert_state = true; \
\
BODY; \
\
if (tinfo.verbose) \
{ \
if (!cmc_assert_state) \
{ \
tinfo.failed += 1; \
cmc_test_log(unit_name, current_test, false, false); \
} \
else \
{ \
tinfo.passed += 1; \
cmc_test_log(unit_name, current_test, false, true); \
} \
} \
\
} while (0)
#endif /* CMC_UTL_TEST_H */
+201
View File
@@ -0,0 +1,201 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_thread.h
*
* Creation Date: 14/05/2020
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* A very simple, header-only utility for spawning and joining threads
*
* Types
* - cmc_thread
* - cmc_thread_proc
*
* Functions
* - cmc_thrd_create
* - cmc_thrd_join
*/
#ifndef CMC_UTL_THREAD_H
#define CMC_UTL_THREAD_H
#include <inttypes.h>
#include <stdbool.h>
#include "../cor/flags.h"
#if defined(_WIN32)
#define CMC_THREAD_WINDOWS
#elif defined(__unix__)
#define CMC_THREAD_UNIX
#else
#error "Unknown platform for CMC Threads"
#endif
#if defined(CMC_THREAD_WINDOWS)
#include <windows.h>
#elif defined(CMC_THREAD_UNIX)
#include <pthread.h>
#endif
/* The type of a process run by a thread */
typedef int (*cmc_thread_proc)(void *);
/**
* struct cmc_thread
*
* A thread wrapper.
*/
struct cmc_thread
{
#if defined(CMC_THREAD_WINDOWS)
HANDLE thread;
#elif defined(CMC_THREAD_UNIX)
pthread_t thread;
#endif
int flag;
void *args;
cmc_thread_proc process;
};
/* A thread wrapper */
#if defined(CMC_THREAD_WINDOWS)
static DWORD cmc_thread_wrapper(void *arg)
#elif defined(CMC_THREAD_UNIX)
static void *cmc_thread_wrapper(void *arg)
#endif
{
struct cmc_thread *thr = (struct cmc_thread *)arg;
int result = thr->process(thr->args);
#if defined(CMC_THREAD_WINDOWS)
return result;
#elif defined(CMC_THREAD_UNIX)
return (void *)(intptr_t)result;
#endif
}
/**
* Creates and spawns a new thread.
*
* \param thr An uninitialized thread wrapper.
* \param proc Pointer to a function that will run in a new thread.
* \param args The arguments passed to the proc function.
* \return True or false if the thread creation was successfull.
*/
static inline bool cmc_thrd_create(struct cmc_thread *thr, cmc_thread_proc proc, void *args)
{
#if defined(CMC_THREAD_WINDOWS)
thr->args = args;
thr->process = proc;
thr->thread = CreateThread(NULL, 0, cmc_thread_wrapper, thr, 0, NULL);
if (!thr->thread)
thr->flag = CMC_FLAG_THREAD;
else
thr->flag = CMC_FLAG_OK;
return thr->flag == CMC_FLAG_OK;
#elif defined(CMC_THREAD_UNIX)
thr->args = args;
thr->process = proc;
if (pthread_create(&(thr->thread), NULL, cmc_thread_wrapper, thr) != 0)
thr->flag = CMC_FLAG_THREAD;
else
thr->flag = CMC_FLAG_OK;
return thr->flag == CMC_FLAG_OK;
#endif
}
/**
* Blocks the current thread until the target thread terminates.
*
* \param thr The target thread to wait on.
* \param result The value returned by the target thread. Optional.
* \return True or false if the join was successfull.
*/
static inline bool cmc_thrd_join(struct cmc_thread *thr, int *result)
{
#if defined(CMC_THREAD_WINDOWS)
if (WaitForSingleObject(thr->thread, INFINITE) == WAIT_FAILED)
{
thr->flag = CMC_FLAG_THREAD;
return false;
}
DWORD exit_code;
if (result)
{
if (GetExitCodeThread(thr->thread, &exit_code) == 0)
{
thr->flag = CMC_FLAG_THREAD;
return false;
}
else
*result = (int)exit_code;
}
thr->flag = CMC_FLAG_OK;
if (CloseHandle(thr->thread) == 0)
{
thr->flag = CMC_FLAG_THREAD;
return false;
}
thr->flag = CMC_FLAG_OK;
return true;
#elif defined(CMC_THREAD_UNIX)
void *exit_value;
if (pthread_join(thr->thread, &exit_value) != 0)
{
thr->flag = CMC_FLAG_THREAD;
return false;
}
if (result)
{
*result = (int)(intptr_t)exit_value;
}
thr->flag = CMC_FLAG_OK;
return true;
#endif
}
#endif /* CMC_UTL_THREAD_H */
+66
View File
@@ -0,0 +1,66 @@
/**
* Copyright (c) 2019 Leonardo Vencovsky
*
* This file is part of the C Macro Collections Library.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* utl_timer.h
*
* Creation Date: 12/04/2019
*
* Authors:
* Leonardo Vencovsky (https://github.com/LeoVen)
*
*/
/**
* Simple timer macros utility for timing code execution.
*/
#ifndef CMC_UTL_TIMER_H
#define CMC_UTL_TIMER_H
#include <time.h>
struct cmc_timer
{
clock_t start;
clock_t stop;
double result; /* The result is given in milliseconds */
};
#define cmc_timer_start(timer) \
do \
{ \
struct cmc_timer *t__ = &(timer); \
t__->start = clock(); \
} while (0)
#define cmc_timer_stop(timer) \
do \
{ \
struct cmc_timer *t__ = &(timer); \
t__->stop = clock(); \
t__->result = (double)(t__->stop - t__->start) * 1000.0 / CLOCKS_PER_SEC; \
} while (0)
#endif /* CMC_UTL_TIMER_H */