Initial commit

This commit is contained in:
Phireh 2025-01-06 19:39:32 +01:00
commit f30f7a6449
10 changed files with 1126 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
libihct.so
tests

8
Makefile Normal file
View file

@ -0,0 +1,8 @@
IHCT_CFLAGS=-Wall -Wextra -Wpedantic -Wfatal-errors -std=gnu99 -fPIC -shared
TEST_CFLAGS=-Wall -Wextra -Wpedantic -Wfatal-errors -DIHCT_SHORT -L./ -Wl,-rpath ./
tests: tests.c list.h libihct.so
gcc $(TEST_CFLAGS) tests.c -lihct -o tests
libihct.so: ihct/vector.c ihct/ihct.c
gcc $(IHCT_CFLAGS) $^ -o libihct.so

440
ihct/ihct.c Normal file
View file

@ -0,0 +1,440 @@
#include "ihct.h"
#include "vector.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <setjmp.h>
#include <signal.h> // handle tests that yield fatal signals.
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
// The point of which to restore to on a fatal signal.
jmp_buf restore_environment;
// the sigaction used for catching fatal signals.
struct sigaction recover_action;
// The final summary string printed.
char *summary_str;
// A list of all units.
static ihct_vector *testunits;
// An array of all first failed (or last if all successful) assert results in every test.
static ihct_test_result **ihct_results;
// Object representing a testing unit, containing the units name and its procedure
// (implemented test function).
typedef struct {
char *name;
ihct_test_proc procedure;
} ihct_unit;
// The number of seconds passed until a test is considered timedout.
// Default 3. Can be set with -t [time in sec]
int test_timeout = 3;
pthread_cond_t routine_done = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// These are ISO/IEC 6429 escape sequences for
// communicating text attributes to terminal emulators.
// Note that some compilers do not understand '\x1b', and therefore \033[0m is
// used instead.
#define IHCT_RESET "\033[0m"
#define IHCT_BOLD "\033[1m"
#define IHCT_FG_GRAY "\033[30;1m"
#define IHCT_FG_RED "\033[31;1m"
#define IHCT_FG_GREEN "\033[32;1m"
#define IHCT_FG_YELLOW "\033[33;1m"
#define IHCT_FG_BLUE "\033[34;1m"
#define IHCT_FG_MAGENTA "\033[35;1m"
#define IHCT_FG_CYAN "\033[36;1m"
#define IHCT_FG_WHITE "\033[37;1m"
#define IHCT_BG_BLACK "\033[40;1m"
#define IHCT_BG_RED "\033[41;1m"
#define IHCT_BG_GREEN "\033[42;1m"
#define IHCT_BG_YELLOW "\033[43;1m"
#define IHCT_BG_BLUE "\033[44;1m"
#define IHCT_BG_MAGENTA "\033[45;1m"
#define IHCT_BG_CYAN "\033[46;1m"
#define IHCT_BG_GRAY "\033[47;1m"
// Procedure called when a signal is thrown within a test. When a fatal signal is
// recieved, we jump back to before the test is ran, giving the signal code.
static void ihct_recovery_proc(int sig) {
// Restore.
longjmp(restore_environment, sig);
}
// Binds the sigaction to signals, and make it call ihct_recovery_proc.
static void ihct_setup_recover_action(void) {
recover_action.sa_handler = &ihct_recovery_proc;
sigemptyset(&recover_action.sa_mask);
recover_action.sa_flags = 0;
// binding sigactions. Dont pick up on user interrupts (let them be managed
// normally).
sigaction(SIGSEGV, &recover_action, NULL);
sigaction(SIGTERM, &recover_action, NULL);
sigaction(SIGFPE, &recover_action, NULL);
sigaction(SIGILL, &recover_action, NULL);
sigaction(SIGABRT, &recover_action, NULL);
sigaction(SIGBUS, &recover_action, NULL);
}
void ihct_print_result(ihct_test_result *result) {
switch (result->status) {
case PASS: fputs(IHCT_BG_GREEN IHCT_BOLD "." IHCT_RESET, stdout); break;
case FAIL_FORCE:
case FAIL: fputs(IHCT_BG_RED IHCT_BOLD ":" IHCT_RESET, stdout); break;
case ERR: fputs(IHCT_BG_RED IHCT_BOLD "!" IHCT_RESET, stdout); break;
case TIMEOUT: fputs(IHCT_BG_YELLOW IHCT_BOLD "?" IHCT_RESET, stdout); break;
}
}
// Reallocates and appends string s to summary_str
void ihct_add_to_summary(char *s) {
char *p = realloc(summary_str, strlen(summary_str) + strlen(s) + 1);
if(!p) {
printf("Couldn't allocate a str big enough to hold summary. Aborting.\n");
return;
}
summary_str = p;
strcat(summary_str, s);
}
void ihct_add_error_to_summary(ihct_test_result *res, ihct_unit *unit) {
char *msg;
char *msg_format;
size_t msg_size;
switch (res->status) {
case PASS: break;
case FAIL:
msg_format = IHCT_BOLD "%s:%d: "
IHCT_RESET "assertion in '"
IHCT_BOLD "%s"
IHCT_RESET "' "
IHCT_FG_RED "failed"
IHCT_RESET ":\n\t'"
IHCT_FG_YELLOW "%s"
IHCT_RESET "'\n";
msg_size = snprintf(NULL, 0, msg_format, res->file, res->line,
unit->name, res->code) + 1;
msg = calloc(msg_size, sizeof(*msg));
sprintf(msg, msg_format, res->file, res->line, unit->name, res->code);
break;
case FAIL_FORCE:
msg_format = IHCT_BOLD "%s:%d: "
IHCT_RESET "'"
IHCT_BOLD "%s"
IHCT_RESET "' "
IHCT_FG_RED "forcefully failed"
IHCT_RESET ".\n";
msg_size = snprintf(NULL, 0, msg_format, res->file, res->line,
unit->name) + 1;
msg = calloc(msg_size, sizeof(*msg));
sprintf(msg, msg_format, res->file, res->line, unit->name);
break;
case ERR:
msg_format = "unit '"
IHCT_BOLD "%s"
IHCT_RESET "' had to restore because of fatal signal ("
IHCT_FG_RED "%s"
IHCT_RESET ")\n";
msg_size = snprintf(NULL, 0, msg_format, unit->name, res->code) + 1;
msg = calloc(msg_size, sizeof(*msg));
sprintf(msg, msg_format, unit->name, res->code);
break;
case TIMEOUT:
msg_format = "unit '"
IHCT_BOLD "%s"
IHCT_RESET "' "
IHCT_FG_YELLOW "timed out "
IHCT_RESET "(took "
IHCT_FG_YELLOW "5 "
IHCT_RESET "seconds).\n";
msg_size = snprintf(NULL, 0, msg_format, unit->name) + 1;
msg = calloc(msg_size, sizeof(*msg));
sprintf(msg, msg_format, unit->name);
}
ihct_add_to_summary(msg);
free(msg);
}
bool ihct_assert_impl(bool eval, ihct_test_result *result, char *code, char *file,
unsigned long line) {
result->status = eval ? PASS : FAIL;
if(!eval) {
result->file = file;
result->line = line;
result->code = code;
return false;
}
return true;
}
void ihct_pass_impl(ihct_test_result *result, char *file, unsigned long line) {
result->status = PASS;
result->file = file;
result->line = line;
}
void ihct_fail_impl(ihct_test_result *result, char *file, unsigned long line) {
result->status = FAIL_FORCE;
result->file = file;
result->line = line;
}
static ihct_unit *ihct_init_unit(char *name, ihct_test_proc procedure) {
ihct_unit *unit = (ihct_unit *)malloc(sizeof(ihct_unit));
char *strmem = malloc(strlen(name) + 1);
strcpy(strmem, name);
unit->name = strmem;
unit->procedure = procedure;
return unit;
}
void ihct_construct_test_impl(char *name, ihct_test_proc procedure) {
ihct_unit *unit = ihct_init_unit(name, procedure);
ihct_vector_add(testunits, unit);
}
static void ihct_unit_free(ihct_unit *unit) {
free(unit->name);
free(unit);
}
void ihct_init(void) {
// atm, only initializes the unit list. Is this neccessary?
testunits = ihct_vector_init();
}
struct routine_run_unit_data {
ihct_test_proc proc;
ihct_test_result *result;
};
// Routine run in a separate thread to execute the unit.
void *routine_run_unit(void *arg) {
struct routine_run_unit_data *data = (struct routine_run_unit_data *)arg;
int old;
// Allow the thread to be canceled.
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
// Create a jump point, to be able to restore when encountering fatal signal.
// When returning here because of a fatal signal, we abort unit with status
// ERR. Has to be done inside of thread, refer to man setjmp:
// "If, in a multithreaded program, a longjmp() call employs an env
// buffer that was initialized by a call to setjmp() in a different
// thread, the behavior is undefined.".
ihct_setup_recover_action();
int restore_status = setjmp(restore_environment);
if(restore_status != 0) {
char *p = malloc(strlen(strsignal(restore_status)) + 1);
strcpy(p, strsignal(restore_status));
data->result->code = p;
data->result->status = ERR;
// We still want to emit finished signal
pthread_mutex_lock(&lock);
pthread_cond_signal(&routine_done);
pthread_mutex_unlock(&lock);
return NULL;
}
// Run test, and save it's result.
(*data->proc)(data->result);
// Emit signal that thread is finished.
pthread_mutex_lock(&lock);
pthread_cond_signal(&routine_done);
pthread_mutex_unlock(&lock);
return NULL;
}
static ihct_test_result *ihct_run_specific(ihct_unit *unit) {
// Allocate memory for the tests result, and set it to passed by default.
ihct_test_result *result = malloc(sizeof(ihct_test_result));
result->status = PASS;
// lock current thread.
pthread_mutex_lock(&lock);
// Create a separate thread to run the test in. We set a limited time
// the process may be run, and abort if it times out.
struct timespec timeout;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += test_timeout;
// Create a temporary data struct to carry data into thread.
struct routine_run_unit_data data = {unit->procedure, result};
// Create new thread to run the unit routine.
pthread_t tid;
pthread_cond_init(&routine_done, NULL);
pthread_create(&tid, NULL, routine_run_unit, &data);
int err = pthread_cond_timedwait(&routine_done, &lock, &timeout);
pthread_mutex_unlock(&lock);
// If timed out, force quit thread and return TIMEOUT.
// Note that this is not safe memory. There is a high chance that
// the thread may not be freed. Looking into solving this.
if(err == ETIMEDOUT) {
pthread_cancel(tid);
result->status = TIMEOUT;
return result;
}
//pthread_join(procedure, NULL);
pthread_join(tid, NULL);
// Run test, and save it's result into i.
//(*unit->procedure)(result);
return result;
}
int ihct_run(int argc, char **argv) {
unsigned unit_count = testunits->size;
// Allocate results
ihct_results = calloc(unit_count, sizeof(ihct_test_result *));
unsigned failed_count = 0;
// initialize the summary string
summary_str = malloc(sizeof(char));
*summary_str = '\0';
// handle args
int c;
while((c = getopt(argc, argv, "t:")) != -1) {
switch(c) {
case 't':
test_timeout = atoi(optarg);
break;
case '?':
printf("unknown option '%c'.\n", optopt);
}
}
// start clock
struct timespec tbegin, tend;
clock_gettime(CLOCK_MONOTONIC, &tbegin);
// Iterate over every test
for(unsigned i = 0; i < unit_count; i++) {
ihct_unit *unit = ihct_vector_get(testunits, i);
ihct_results[i] = ihct_run_specific(unit);
// ensure 80 width
if(i % 80 == 0 && i != 0) putc('\n', stdout);
ihct_print_result(ihct_results[i]);
fflush(stdout);
if(ihct_results[i]->status) {
failed_count++;
ihct_add_error_to_summary(ihct_results[i], unit);
}
// Frees both the unit and the result.
ihct_unit_free(unit);
// Also frees dynamic allocated string if status is err (the signal name).
if(ihct_results[i]->status == ERR) free(ihct_results[i]->code);
free(ihct_results[i]);
}
free(ihct_results);
ihct_vector_free(testunits);
clock_gettime(CLOCK_MONOTONIC, &tend);
double elapsed = (tend.tv_sec - tbegin.tv_sec);
elapsed += (tend.tv_nsec - tbegin.tv_nsec) / 1000000000.0;
// print all messages
if(strlen(summary_str) > 4) printf("\n\n%s\n", summary_str);
else puts("\n\n");
// Free summary str
free(summary_str);
printf("tests took %.2f seconds\n", elapsed);
if(failed_count) {
char *status_format = IHCT_FG_GREEN "%d successful "
IHCT_RESET "and "
IHCT_FG_RED "%d failed "
IHCT_RESET "of "
IHCT_FG_YELLOW "%d run"
IHCT_RESET "\n";
printf(status_format, unit_count-failed_count, failed_count,
unit_count);
printf(IHCT_FG_RED "FAILURE\n" IHCT_RESET);
return 1;
}
char *status_format = IHCT_FG_GREEN "%d successful "
IHCT_RESET "of "
IHCT_FG_YELLOW "%d run"
IHCT_RESET "\n";
printf(status_format, unit_count, unit_count);
printf(IHCT_FG_GREEN "SUCCESS\n" IHCT_RESET);
return 0;
}
// Lets the program run tests on itself. This is done with the compiler flag
// IHCT_TEST_SELF. Still requires an external main entrypoint.
#ifdef IHCT_TEST_SELF
// Create two internal test procedures, for testing the test creation.
static void itest_true(ihct_test_result *result) {
IHCT_ASSERT(true);
}
static void itest_false(ihct_test_result *result) {
IHCT_ASSERT(false);
}
IHCT_TEST(self_unit_create) {
ihct_unit *u = ihct_init_unit("internal_true", &itest_true);
IHCT_ASSERT_STR(u->name, "internal_true");
IHCT_ASSERT(&itest_true == u->procedure);
}
IHCT_TEST(self_vector_create) {
ihct_vector *v = ihct_init_vector();
IHCT_ASSERT(v != NULL);
IHCT_ASSERT(v->data == NULL);
IHCT_ASSERT(v->size == 0);
ihct_free_vector(v);
}
IHCT_TEST(self_vector_all) {
ihct_vector *v = ihct_init_vector();
for(int i = 0; i < 1000; ++i) {
int *t = malloc(sizeof(int));
*t = i * 2;
ihct_vector_add(v, t);
}
for(int i = 0; i < 1000; ++i) {
int *t = ihct_vector_get(v, i);
IHCT_ASSERT(i * 2 == *t);
free(t);
}
ihct_free_vector(v);
}
#endif

151
ihct/ihct.h Normal file
View file

@ -0,0 +1,151 @@
#ifndef IHCT_H
#define IHCT_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// Structure for a testunits return value. Contains state, the code (assert) which
// failed the test, and a reference to where the code is.
typedef struct {
enum {PASS, FAIL, FAIL_FORCE, ERR, TIMEOUT} status;
char *code;
char *file;
unsigned long line;
} ihct_test_result;
// Short for a function returning a test_result pointer, with no arguments.
typedef void (*ihct_test_proc)(ihct_test_result *);
// Called within a test.
bool ihct_assert_impl(bool eval, ihct_test_result *result, char *code, char *file,
unsigned long line);
void ihct_pass_impl(ihct_test_result *result, char *file, unsigned long line);
void ihct_fail_impl(ihct_test_result *result, char *file, unsigned long line);
// Called on test unit construction.
void ihct_construct_test_impl(char *s, ihct_test_proc proc);
// Runs all tests.
int ihct_run(int argc, char **argv);
// Initializes the unitlist (Has to be done before all testing units are created).
// Using priority to ensure that the unit list is constructed before it gets populated.
void ihct_init(void) __attribute__((constructor(101)));
// Assertions
/// @defgroup assertions Assertions
/// @brief Wraps all assertions.
///
/// Decides the success of a test. Right now, tests can only result in
/// PASS or FAIL. Assertions are only given for expressions.
/// @brief Asserts a statement inside a test unit. If the expression is false,
/// the unit will fail the test.
/// @ingroup assertions
/// @param expr the expression to evaluate.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_ASSERT(expr) \
if(!ihct_assert_impl(expr, result, #expr, __FILE__, __LINE__)) return
/// @brief Asserts a statement inside a test unit. If the expression is true,
/// the unit will fail the test.
/// @ingroup assertions
/// @param expr the expression to evaluate.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_NASSERT(expr) \
if(!ihct_assert_impl(!expr, result, "!(" #expr ")", __FILE__, __LINE__)) return
/// @brief Asserts two strings inside a test unit to be equal. If there is any difference
/// in the strings, the unit will fail the test.
/// @ingroup assertions
/// @param s1 first string to compare
/// @param s2 second string to compare
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_ASSERT_STR(s1, s2) \
if(!ihct_assert_impl(!strcmp(s1, s2), result, #s1 " == " #s2, __FILE__, \
__LINE__)) return
/// @brief Asserts two strings inside a test unit not to be equal. If there is any
/// difference in the strings, the unit will fail the test.
/// @ingroup assertions
/// @param s1 first string to compare
/// @param s2 second string to compare
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_NASSERT_STR(s1, s2) \
if(!ihct_assert_impl(strcmp(s1, s2), result, #s1 " != " #s2, __FILE__, \
__LINE__)) return
/// @brief Set the test as passed and return.
/// @ingroup assertions
///
/// Used for more complex tests where the PASS/FAIL status is more complex
/// than an assert. Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_PASS() \
do { ihct_pass_impl(result, __FILE__, __LINE__); return; } while(0)
/// @brief Set the test as failed and return.
/// @ingroup assertions
///
/// Used for more complex tests where the PASS/FAIL status is more complex
/// than an assert. Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_FAIL() \
do { ihct_fail_impl(result, __FILE__, __LINE__); return; } while(0)
// Function macros
/// @defgroup funcs Testing functions
/// @brief More general macros for function.
/// @brief Runs all tests. Is to be called once in the main entrypoint.
/// @ingroup funcs
/// @code
/// int main(int argc, char **argv) {
/// return IHCT_RUN(argc, argv);
/// }
/// @endcode
/// @param argc argument count, directly passed from main.
/// @param argv argument array, directly passed from main.
#define IHCT_RUN(argc, argv) \
ihct_run(argc, argv)
// Create a new test unit, and adds it using 'ihct_add_test'.
/// @brief Create a new test unit, which can take any number of asserts.
/// @ingroup funcs
/// @code
/// IHCT_TEST(basic_test) {
/// IHCT_ASSERT(1 == 1);
/// }
/// @endcode
/// @param name the name of the test.
///
/// Can be shortened to remove 'IHCT_' prefix by defining IHCT_SHORT.
#define IHCT_TEST(name) \
static void test_##name(ihct_test_result *result); \
static void __attribute__((constructor(102))) __construct_test_##name(void) { \
ihct_construct_test_impl(#name, &test_##name); \
} \
static void test_##name(ihct_test_result *result)
/// @brief Defines a fixture with data to be preloaded before a test.
/// A ficture is included by a IHCT_REQUIRE inside a test.
#define IHCT_FIXTURE(name) _Static_assert(0, "Fixtures not implemented.")
/// @brief Make the test require the given fixtures.
/// @param ... one or more fixture names.
#define IHCT_REQUIRE(...) _Static_assert(0, "Fixture requirements not implemented.")
#ifdef IHCT_SHORT
#define TEST(name) IHCT_TEST(name)
#define ASSERT(expr) IHCT_ASSERT(expr)
#define NASSERT(expr) IHCT_NASSERT(expr)
#define ASSERT_STR(s1, s2) IHCT_ASSERT_STR(s1, s2)
#define NASSERT_STR(s1, s2) IHCT_NASSERT_STR(s1, s2)
#define PASS() IHCT_PASS()
#define FAIL() IHCT_FAIL()
#endif
#endif

41
ihct/vector.c Normal file
View file

@ -0,0 +1,41 @@
#include "vector.h"
#include <stdio.h>
ihct_vector *ihct_vector_init() {
ihct_vector *v = malloc(sizeof(*v));
if(v == NULL) {
printf("Couldn't allocate memory for vector.\n");
exit(EXIT_FAILURE);
}
v->size = 0;
v->data = NULL;
return v;
}
void ihct_vector_add(ihct_vector *v, void *obj) {
if(v->size == 0) {
// Allocate a single
v->data = malloc(sizeof(obj));
if(v->data == NULL) {
printf("Couldn't allocate memory for object.\n");
exit(EXIT_FAILURE);
}
v->data[0] = obj;
} else {
void *p = realloc(v->data, (v->size + 1) * sizeof(obj));
if(p == NULL) {
printf("Couldn't allocate memory for object.\n");
exit(EXIT_FAILURE);
}
v->data = p;
v->data[v->size] = obj;
}
v->size++;
}
void *ihct_vector_get(ihct_vector *v, int index) {
return v->data[index];
}
void ihct_vector_free(ihct_vector *v) {
free(v->data);
free(v);
}

24
ihct/vector.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef IHCT_VECTOR_H
#define IHCT_VECTOR_H
#include <stdlib.h>
// Datatype representing a vector. to be used internally in IHCT_RUN
typedef struct {
void **data;
size_t size;
} ihct_vector;
// Allocates a new vector with capacity cap.
ihct_vector *ihct_vector_init();
// Add a pointer to a allocated object at the end of the vector.
void ihct_vector_add(ihct_vector *v, void *obj);
// Gets the object at location index in vector v.
void *ihct_vector_get(ihct_vector *v, int index);
// Deallocates the vector.
void ihct_vector_free(ihct_vector *v);
#endif

77
list.c Normal file
View file

@ -0,0 +1,77 @@
#include <stdio.h>
// Single linked list with a single type
#if 0
#define MAKE_LIST(type) \
struct type##_node_t; \
typedef struct { \
type data; \
struct type##_node_t *next; \
} type##_node_t; \
typedef struct type##_node_t* type##_node_t_ptr
MAKE_LIST(int);
MAKE_LIST(char);
#endif
// Single linked list with fat struct
#if 1
typedef struct node_ node_t;
typedef struct node_* node_ptr;
typedef enum { INT, UINT, CHAR, FLOAT, DOUBLE, CHAR_PTR } node_value_t;
typedef struct node_ {
int type;
union {
int vi;
unsigned int vii;
char cv;
float fv;
double dv;
void *ptr;
};
node_ptr* children;
//node_ptr prev;
} node_t;
#endif
// Time (single-linked) Time (double-linked)
// Insertar al principio O(1) O(1)
// Insertar al final O(n) / O(1) O(n) / O(1)
// Insertar arbitrario O(n) O(n/2) / O(1)
// Borrar arbitrario O(n) O(n/2) / O(1)
// Buscar O(n) O(n/2) / O(1)
int main()
{
node_t node1 = { INT, { .vi = -1 }, 0 };
node_t node2 = { UINT, { .vii = 1 }, 0 };
node_t node3 = { CHAR, { .cv = 'v' }, 0 };
node1.next = &node2;
node2.next = &node3;
node_ptr n = &node1;
do {
switch(n->type) {
case INT:
printf("%d\n", n->vi);
break;
case UINT:
printf("%d\n", n->vii);
break;
case CHAR:
printf("%c\n", n->cv);
break;
default:
break;
}
} while ((n = n->next));
return 0;
}
/* int num = (int)this; */
/* &(this->mem) */
/* int *member = num + offsetof(node_t, next); */
/* *member = 1; */

261
list.h Normal file
View file

@ -0,0 +1,261 @@
#ifndef LIST_H
#define LIST_H
#include <stdio.h>
// Single linked list with a single type
#if 0
#define MAKE_LIST(type) \
struct type##_node_t; \
typedef struct { \
type data; \
struct type##_node_t *next; \
} type##_node_t; \
typedef struct type##_node_t* type##_node_t_ptr
MAKE_LIST(int);
MAKE_LIST(char);
#endif
// Single linked list with fat struct
#if 1
typedef struct node_ node_t;
typedef struct node_* node_ptr;
typedef enum { INT, UINT, CHAR, FLOAT, DOUBLE, CHAR_PTR, VOID_PTR } node_value_t;
typedef struct node_ {
int type;
union {
int vi;
unsigned int vui;
char vc;
float vf;
double vd;
char* vcp;
void *vptr;
};
node_ptr next;
} node_t;
void list_append(node_t *start, node_t *tail)
{
node_t *n = start;
while (n->next)
n = n->next;
n->next = tail;
}
int list_size(node_t *start)
{
node_t *n = start;
int size = 1;
while (n->next)
{
n = n->next;
++size;
}
return size;
}
node_t *list_nth(node_t *head, int idx)
{
int i = 0;
node_t *n = head;
while (i < idx && n->next)
{
++i;
n = n->next;
}
if (i == idx)
return n;
else
return 0;
}
// TODO: Free memory option
int list_delete_nth(node_t *head, int idx)
{
int i = 0;
node_t *n = head;
node_t *p;
while (i < idx && n->next)
{
++i;
p = n;
n = n->next;
}
if (i == idx)
{
if (n->next)
{
p->next = n->next;
}
else
p->next = 0;
return 0;
}
else
{
return -1;
}
}
node_t *list_tail(node_t *head)
{
node_t *n = head;
while (n->next)
n = n->next;
return n;
}
int list_is_circular(node_t* start)
{
node_t* n = start;
while ((n = n->next) && n != start);
return start->next && n == start;
}
#endif
//Doubly linked list
#if 1
typedef struct dnode_ dnode_t;
typedef struct dnode_* dnode_ptr;
//typedef enum { INT, UINT, CHAR, FLOAT, DOUBLE, CHAR_PTR, VOID_PTR } dnode_value_t;
typedef struct dnode_ {
int type;
union {
int vi;
unsigned int vui;
char vc;
float vf;
double vd;
char* vcp;
void *vptr;
};
dnode_ptr next;
dnode_ptr prev;
} dnode_t;
void dlist_append(dnode_t *start, dnode_t *tail)
{
dnode_t *n = start;
while (n->next)
n = n->next;
n->next = tail;
tail->prev = n;
}
int dlist_size(dnode_t *node)
{
dnode_t *forward = node;
dnode_t *backward = node;
int size = 1;
while ((backward = backward->prev)) ++size;
while ((forward = forward->next)) ++size;
return size;
}
dnode_t *dlist_nth(int idx, dnode_t *start, dnode_t *end, unsigned int size)
{
int i = 0;
dnode_t *current = 0;
int reverse = 0;
if(end && size) {
int half = size / 2;
if(idx > half) {
reverse = 1;
i = size - 1;
}
}
current = (reverse) ? end : start;
while(i != idx && current) {
current = (reverse) ? current->prev : current->next;
i += reverse ? -1 : +1;
}
return current;
}
dnode_t *dlist_nth_circular(int steps, dnode_t* start)
{
int i = 0;
dnode_t* current = start;
int absteps = steps < 0 ? -steps : steps;
//for(int i = 0; i != absteps && current; i++)
while(i++ != absteps && current)
current = (steps < 0) ? current->prev : current->next;
return current;
}
int dlist_is_circular(dnode_t* start)
{
dnode_t* n = start;
while ((n = n->next) && n != start);
return start->next && n == start;
}
/*node_t *list_nth(node_t *head, int idx)
*{
* int i = 0;
* node_t *n = head;
* while (i < idx && n->next)
* {
* ++i;
* n = n->next;
* }
* if (i == idx)
* return n;
* else
* return 0;
*}
*/
// TODO: Free memory option
/*int list_delete_nth(node_t *head, int idx)
*{
* int i = 0;
* node_t *n = head;
* node_t *p;
* while (i < idx && n->next)
* {
* ++i;
* p = n;
* n = n->next;
* }
* if (i == idx)
* {
* if (n->next)
* {
* p->next = n->next;
* }
* else
* p->next = 0;
*
* return 0;
* }
* else
* {
* return -1;
* }
*}
*
*node_t *list_tail(node_t *head)
*{
* node_t *n = head;
* while (n->next)
* n = n->next;
*
* return n;
*}
*/
#endif
#endif //LIST_H

90
tests.c Normal file
View file

@ -0,0 +1,90 @@
#include "ihct/ihct.h"
#include "list.h"
TEST(node_create)
{
node_t node = { INT, { .vi = -1 }, 0 };
ASSERT(node.type == INT && node.vi == -1);
}
TEST(list_create)
{
node_t head = { INT, { .vi = 1 }, 0 };
node_t n = { INT, { .vi = 2 }, 0 };
list_append(&head, &n);
ASSERT(head.next == &n);
}
TEST(list_query)
{
node_t n0 = { INT, { .vi = 1 }, 0 };
node_t n1 = { INT, { .vi = 2 }, 0 };
node_t n2 = { INT, { .vi = 3 }, 0 };
list_append(&n0, &n1);
list_append(&n0, &n2);
ASSERT(list_nth(&n0, 1) == &n1);
}
TEST(list_delete)
{
node_t n0 = { INT, { .vi = 1 }, 0 };
node_t n1 = { INT, { .vi = 2 }, 0 };
node_t n2 = { INT, { .vi = 3 }, 0 };
list_append(&n0, &n1);
list_append(&n0, &n2);
list_delete_nth(&n0, 1);
ASSERT(list_nth(&n0, 1) == &n2);
}
TEST(dlist_size)
{
dnode_t n0 = { INT, { .vi = 1 }, 0, 0 };
dnode_t n1 = { INT, { .vi = 2 }, 0, 0 };
dnode_t n2 = { INT, { .vi = 3 }, 0, 0 };
dlist_append(&n0, &n1);
dlist_append(&n1, &n2);
//list_delete_nth(&n0, 1);
ASSERT(dlist_size(&n0) == 3);
ASSERT(dlist_size(&n0) == 3);
}
TEST(dlist_nth)
{
dnode_t n0 = { INT, { .vi = 1 }, 0, 0 };
dnode_t n1 = { INT, { .vi = 1 }, 0, 0 };
dnode_t n2 = { INT, { .vi = 1 }, 0, 0 };
dnode_t n3 = { INT, { .vi = 1 }, 0, 0 };
dlist_append(&n0, &n1);
dlist_append(&n1, &n2);
dlist_append(&n2, &n3);
ASSERT(dlist_nth(1, &n0, 0, 0) == &n1);
ASSERT(dlist_nth(1, &n0, &n3, 4) == &n1);
ASSERT(dlist_nth(2, &n0, 0, 0) == &n2);
ASSERT(dlist_nth(2, &n0, &n3, 4) == &n2);
ASSERT(dlist_nth(0, &n0, 0, 0) == &n0);
ASSERT(dlist_nth(3, &n0, &n3, 4) == &n3);
ASSERT(!dlist_nth(4, &n0, &n3, 4));
}
TEST(dlist_nth_circular_and_is_circular)
{
dnode_t n0 = { INT, { .vi = 0 }, 0, 0 };
dnode_t n1 = { INT, { .vi = 1 }, 0, 0 };
dnode_t n2 = { INT, { .vi = 2 }, 0, 0 };
dnode_t n3 = { INT, { .vi = 3 }, 0, 0 };
dnode_t n4 = { INT, { .vi = 4 }, 0, 0 };
dlist_append(&n0, &n1);
dlist_append(&n0, &n2);
dlist_append(&n0, &n3);
dlist_append(&n0, &n4);
dlist_append(&n4, &n0);
ASSERT(dlist_nth_circular(5, &n4) == &n4);
ASSERT(dlist_is_circular(&n0) == 1);
}
int main(int argc, char **argv)
{
return IHCT_RUN(argc, argv);
}

32
tree.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef TREE_H
#define TREE_H
typedef struct tnode_ tnode_t;
typedef struct tnode_* tnode_ptr;
typedef enum { INT, UINT, CHAR, FLOAT, DOUBLE, CHAR_PTR, VOID_PTR } tnode_value_t;
typedef struct tnode_ {
int type;
union {
int vi;
unsigned int vui;
char vc;
float vf;
double vd;
char* vcp;
void *vptr;
};
tnode_ptr parent;
tnode_ptr left_leaf;
tnode_ptr right_leaf;
} tnode_t;
tnode_t* tree_root(tnode_t* leaf)
{
tnode_t* current = leaf;
while(current = current->parent && current->parent);
return current;
}
#endif //TREE_H