feat: hectic C: debug string constructor init
This commit is contained in:
@@ -40,7 +40,7 @@ ColorMode color_mode = COLOR_MODE_AUTO;
|
|||||||
|
|
||||||
// Global logging variables
|
// Global logging variables
|
||||||
LogLevel current_log_level = LOG_LEVEL_INFO;
|
LogLevel current_log_level = LOG_LEVEL_INFO;
|
||||||
LogRule *log_rules = NULL; // Linked list of log rules
|
LogRule *log_rules = NULL;
|
||||||
|
|
||||||
const char* color_mode_to_string(ColorMode mode) {
|
const char* color_mode_to_string(ColorMode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@@ -64,6 +64,8 @@ void set_output_color_mode(ColorMode mode) {
|
|||||||
|
|
||||||
#define POSITION_INFO_DECLARATION const char *file, const char *func, int line
|
#define POSITION_INFO_DECLARATION const char *file, const char *func, int line
|
||||||
#define POSITION_INFO file, func, line
|
#define POSITION_INFO file, func, line
|
||||||
|
#define COLORING_DECLARATION POSITION_INFO_DECLARATION Arena *arena
|
||||||
|
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
// -- Logger --
|
// -- Logger --
|
||||||
@@ -127,7 +129,6 @@ void logger_level(LogLevel level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_logger(void) {
|
void init_logger(void) {
|
||||||
// Read log level or rules from environment
|
|
||||||
const char* env_level = getenv("LOG_LEVEL");
|
const char* env_level = getenv("LOG_LEVEL");
|
||||||
|
|
||||||
if (env_level) {
|
if (env_level) {
|
||||||
@@ -194,9 +195,13 @@ char* raise_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------
|
// -----------
|
||||||
// -- arena --
|
// -- debug --
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
|
// ------------
|
||||||
|
// -- arena --
|
||||||
|
// ------------
|
||||||
|
|
||||||
Arena arena_init__(POSITION_INFO_DECLARATION, size_t size) {
|
Arena arena_init__(POSITION_INFO_DECLARATION, size_t size) {
|
||||||
// Function entry logging
|
// Function entry logging
|
||||||
raise_message(LOG_LEVEL_DEBUG, POSITION_INFO,
|
raise_message(LOG_LEVEL_DEBUG, POSITION_INFO,
|
||||||
@@ -354,6 +359,9 @@ void arena_free__(POSITION_INFO_DECLARATION, Arena *arena) {
|
|||||||
arena->capacity = 0;
|
arena->capacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Duplicates a string and returns a pointer to the new string.
|
||||||
|
*/
|
||||||
char* arena_strdup__(POSITION_INFO_DECLARATION, Arena *arena, const char *s) {
|
char* arena_strdup__(POSITION_INFO_DECLARATION, Arena *arena, const char *s) {
|
||||||
// Function entry logging
|
// Function entry logging
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
@@ -384,6 +392,26 @@ char* arena_strdup__(POSITION_INFO_DECLARATION, Arena *arena, const char *s) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Duplicates a string and returns a pointer to the new string.
|
||||||
|
* The string is formatted using the provided format string and arguments.
|
||||||
|
*/
|
||||||
|
char* arena_strdup_fmt__(POSITION_INFO_DECLARATION, Arena *arena, const char *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int len = vsnprintf(NULL, 0, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (len < 0) return NULL;
|
||||||
|
|
||||||
|
char *temp = arena_alloc__(POSITION_INFO, DISPOSABLE_ARENA, len + 1);
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(temp, len + 1, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return arena_strdup__(POSITION_INFO, arena, temp);
|
||||||
|
}
|
||||||
|
|
||||||
char* arena_strncpy__(POSITION_INFO_DECLARATION, Arena *arena, const char *start, size_t len) {
|
char* arena_strncpy__(POSITION_INFO_DECLARATION, Arena *arena, const char *start, size_t len) {
|
||||||
// Function entry logging
|
// Function entry logging
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
@@ -417,6 +445,9 @@ char* arena_strncpy__(POSITION_INFO_DECLARATION, Arena *arena, const char *start
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replaces a substring in a string with a new string.
|
||||||
|
*/
|
||||||
char* arena_repstr__(POSITION_INFO_DECLARATION, Arena *arena,
|
char* arena_repstr__(POSITION_INFO_DECLARATION, Arena *arena,
|
||||||
const char *src, size_t start, size_t len, const char *rep) {
|
const char *src, size_t start, size_t len, const char *rep) {
|
||||||
// Function entry logging
|
// Function entry logging
|
||||||
@@ -1117,70 +1148,6 @@ Json *json_get_object_item__(POSITION_INFO_DECLARATION, const Json * const objec
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------
|
|
||||||
// -- slice --
|
|
||||||
// -----------
|
|
||||||
|
|
||||||
// Create a slice from an array with boundary check.
|
|
||||||
Slice slice_create__(POSITION_INFO_DECLARATION, size_t isize, void *array, size_t array_len, size_t start, size_t len) {
|
|
||||||
// Function entry logging
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
|
||||||
"SLICE: Creating slice (source: %p, array_length: %zu, start: %zu, length: %zu, item_size: %zu)",
|
|
||||||
array, array_len, start, len, isize);
|
|
||||||
|
|
||||||
// Boundary check
|
|
||||||
if (start + len > array_len) {
|
|
||||||
raise_message(LOG_LEVEL_WARN, POSITION_INFO,
|
|
||||||
"SLICE: Slice boundaries exceed array length (start: %zu, length: %zu, array_length: %zu)",
|
|
||||||
start, len, array_len);
|
|
||||||
return (Slice){NULL, 0, isize};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create valid slice
|
|
||||||
Slice result = (Slice){ (char *)array + start * isize, len, isize };
|
|
||||||
|
|
||||||
// Success logging
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
|
||||||
"SLICE: Slice created successfully (data: %p, length: %zu, item_size: %zu)",
|
|
||||||
result.data, result.len, result.isize);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a subslice from an existing slice.
|
|
||||||
Slice slice_subslice__(POSITION_INFO_DECLARATION, Slice s, size_t start, size_t len) {
|
|
||||||
// Function entry logging
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
|
||||||
"SLICE: Creating subslice (source: %p, source_length: %zu, start: %zu, length: %zu)",
|
|
||||||
s.data, s.len, start, len);
|
|
||||||
|
|
||||||
// Boundary check
|
|
||||||
if (start + len > s.len) {
|
|
||||||
raise_message(LOG_LEVEL_WARN, POSITION_INFO,
|
|
||||||
"SLICE: Subslice boundaries exceed source slice length (start: %zu, length: %zu, source_length: %zu)",
|
|
||||||
start, len, s.len);
|
|
||||||
return (Slice){NULL, 0, s.isize};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create valid subslice
|
|
||||||
Slice result = (Slice){(char*)s.data + start * s.isize, len, s.isize};
|
|
||||||
|
|
||||||
// Success logging
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
|
||||||
"SLICE: Subslice created successfully (data: %p, length: %zu, item_size: %zu)",
|
|
||||||
result.data, result.len, result.isize);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int* arena_slice_copy__(POSITION_INFO_DECLARATION, Arena *arena, Slice s) {
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "arena_slice_copy(<optimized>, <optimized>)");
|
|
||||||
int *copy = (void*) arena_alloc__(POSITION_INFO, arena, s.len * sizeof(int));
|
|
||||||
if (copy)
|
|
||||||
memcpy(copy, s.data, s.len * s.isize);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* json_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Json json) {
|
char* json_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Json json) {
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "json_to_debug_str(<optimized>, <optimized>)");
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "json_to_debug_str(<optimized>, <optimized>)");
|
||||||
|
|
||||||
@@ -1253,6 +1220,139 @@ char* json_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Json json) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
// -- slice --
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
// Create a slice from an array with boundary check.
|
||||||
|
Slice slice_create__(POSITION_INFO_DECLARATION, size_t isize, void *array, size_t array_len, size_t start, size_t len) {
|
||||||
|
// Function entry logging
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
|
"SLICE: Creating slice (source: %p, array_length: %zu, start: %zu, length: %zu, item_size: %zu)",
|
||||||
|
array, array_len, start, len, isize);
|
||||||
|
|
||||||
|
// Boundary check
|
||||||
|
if (start + len > array_len) {
|
||||||
|
raise_message(LOG_LEVEL_WARN, POSITION_INFO,
|
||||||
|
"SLICE: Slice boundaries exceed array length (start: %zu, length: %zu, array_length: %zu)",
|
||||||
|
start, len, array_len);
|
||||||
|
return (Slice){NULL, 0, isize};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create valid slice
|
||||||
|
Slice result = (Slice){ (char *)array + start * isize, len, isize };
|
||||||
|
|
||||||
|
// Success logging
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
|
"SLICE: Slice created successfully (data: %p, length: %zu, item_size: %zu)",
|
||||||
|
result.data, result.len, result.isize);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a subslice from an existing slice.
|
||||||
|
Slice slice_subslice__(POSITION_INFO_DECLARATION, Slice s, size_t start, size_t len) {
|
||||||
|
// Function entry logging
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
|
"SLICE: Creating subslice (source: %p, source_length: %zu, start: %zu, length: %zu)",
|
||||||
|
s.data, s.len, start, len);
|
||||||
|
|
||||||
|
// Boundary check
|
||||||
|
if (start + len > s.len) {
|
||||||
|
raise_message(LOG_LEVEL_WARN, POSITION_INFO,
|
||||||
|
"SLICE: Subslice boundaries exceed source slice length (start: %zu, length: %zu, source_length: %zu)",
|
||||||
|
start, len, s.len);
|
||||||
|
return (Slice){NULL, 0, s.isize};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create valid subslice
|
||||||
|
Slice result = (Slice){(char*)s.data + start * s.isize, len, s.isize};
|
||||||
|
|
||||||
|
// Success logging
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO,
|
||||||
|
"SLICE: Subslice created successfully (data: %p, length: %zu, item_size: %zu)",
|
||||||
|
result.data, result.len, result.isize);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int* arena_slice_copy__(POSITION_INFO_DECLARATION, Arena *arena, Slice s) {
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "arena_slice_copy(<optimized>, <optimized>)");
|
||||||
|
int *copy = (void*) arena_alloc__(POSITION_INFO, arena, s.len * sizeof(int));
|
||||||
|
if (copy)
|
||||||
|
memcpy(copy, s.data, s.len * s.isize);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* slice_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Slice slice) {
|
||||||
|
// Create complete information about the Slice structure
|
||||||
|
char buffer_meta[128];
|
||||||
|
snprintf(buffer_meta, sizeof(buffer_meta), "Slice{addr=%p, data=%p, len=%zu, isize=%zu, content=",
|
||||||
|
(void*)&slice, slice.data, slice.len, slice.isize);
|
||||||
|
|
||||||
|
size_t meta_len = strlen(buffer_meta);
|
||||||
|
|
||||||
|
// For NULL data, output a simple message
|
||||||
|
if (!slice.data) {
|
||||||
|
char* result = arena_alloc(arena, meta_len + 6);
|
||||||
|
strcpy(result, buffer_meta);
|
||||||
|
strcat(result, "NULL}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffer with space for quotes, metadata and null terminator
|
||||||
|
size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace
|
||||||
|
char* buffer = arena_alloc(arena, buffer_size);
|
||||||
|
|
||||||
|
// Copy metadata
|
||||||
|
strcpy(buffer, buffer_meta);
|
||||||
|
char* pos = buffer + meta_len;
|
||||||
|
|
||||||
|
*pos++ = '"';
|
||||||
|
|
||||||
|
// Copy slice data with escaping
|
||||||
|
for (size_t i = 0; i < slice.len; i++) {
|
||||||
|
char c = ((char*)slice.data)[i];
|
||||||
|
if (c == '\0') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = '0';
|
||||||
|
} else if (c == '\n') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = 'n';
|
||||||
|
} else if (c == '\r') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = 'r';
|
||||||
|
} else if (c == '\t') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = 't';
|
||||||
|
} else if (c == '"') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = '"';
|
||||||
|
} else if (c == '\\') {
|
||||||
|
*pos++ = '\\';
|
||||||
|
*pos++ = '\\';
|
||||||
|
} else if (c < 32 || c > 126) {
|
||||||
|
// Non-printable characters as hex
|
||||||
|
pos += sprintf(pos, "\\x%02x", (unsigned char)c);
|
||||||
|
} else {
|
||||||
|
*pos++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pos++ = '"';
|
||||||
|
*pos++ = '}'; // Closing brace for the structure
|
||||||
|
*pos = '\0';
|
||||||
|
|
||||||
|
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "slice_to_debug_str: %s", buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
// -- logger rules --
|
||||||
|
// ------------------
|
||||||
|
|
||||||
// Clean up existing log rules
|
// Clean up existing log rules
|
||||||
void free_log_rules() {
|
void free_log_rules() {
|
||||||
LogRule *rule = log_rules;
|
LogRule *rule = log_rules;
|
||||||
@@ -1536,71 +1636,6 @@ char* logger_rules_to_string(Arena *arena) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* slice_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, Slice slice) {
|
|
||||||
// Create complete information about the Slice structure
|
|
||||||
char buffer_meta[128];
|
|
||||||
snprintf(buffer_meta, sizeof(buffer_meta), "Slice{addr=%p, data=%p, len=%zu, isize=%zu, content=",
|
|
||||||
(void*)&slice, slice.data, slice.len, slice.isize);
|
|
||||||
|
|
||||||
size_t meta_len = strlen(buffer_meta);
|
|
||||||
|
|
||||||
// For NULL data, output a simple message
|
|
||||||
if (!slice.data) {
|
|
||||||
char* result = arena_alloc(arena, meta_len + 6);
|
|
||||||
strcpy(result, buffer_meta);
|
|
||||||
strcat(result, "NULL}");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate buffer with space for quotes, metadata and null terminator
|
|
||||||
size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace
|
|
||||||
char* buffer = arena_alloc(arena, buffer_size);
|
|
||||||
|
|
||||||
// Copy metadata
|
|
||||||
strcpy(buffer, buffer_meta);
|
|
||||||
char* pos = buffer + meta_len;
|
|
||||||
|
|
||||||
*pos++ = '"';
|
|
||||||
|
|
||||||
// Copy slice data with escaping
|
|
||||||
for (size_t i = 0; i < slice.len; i++) {
|
|
||||||
char c = ((char*)slice.data)[i];
|
|
||||||
if (c == '\0') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = '0';
|
|
||||||
} else if (c == '\n') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = 'n';
|
|
||||||
} else if (c == '\r') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = 'r';
|
|
||||||
} else if (c == '\t') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = 't';
|
|
||||||
} else if (c == '"') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = '"';
|
|
||||||
} else if (c == '\\') {
|
|
||||||
*pos++ = '\\';
|
|
||||||
*pos++ = '\\';
|
|
||||||
} else if (c < 32 || c > 126) {
|
|
||||||
// Non-printable characters as hex
|
|
||||||
pos += sprintf(pos, "\\x%02x", (unsigned char)c);
|
|
||||||
} else {
|
|
||||||
*pos++ = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*pos++ = '"';
|
|
||||||
*pos++ = '}'; // Closing brace for the structure
|
|
||||||
*pos = '\0';
|
|
||||||
|
|
||||||
raise_message(LOG_LEVEL_TRACE, POSITION_INFO, "slice_to_debug_str: %s", buffer);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// -- Templater --
|
// -- Templater --
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ void arena_reset__(const char *file, const char *func, int line, Arena *arena);
|
|||||||
void arena_free__(const char *file, const char *func, int line, Arena *arena);
|
void arena_free__(const char *file, const char *func, int line, Arena *arena);
|
||||||
|
|
||||||
char* arena_strdup__(const char *file, const char *func, int line, Arena *arena, const char *s);
|
char* arena_strdup__(const char *file, const char *func, int line, Arena *arena, const char *s);
|
||||||
|
char* arena_strdup_fmt__(const char *file, const char *func, int line, Arena *arena, const char *fmt, ...);
|
||||||
|
|
||||||
char* arena_repstr__(const char *file, const char *func, int line, Arena *arena,
|
char* arena_repstr__(const char *file, const char *func, int line, Arena *arena,
|
||||||
const char *src, size_t start, size_t len, const char *rep);
|
const char *src, size_t start, size_t len, const char *rep);
|
||||||
@@ -314,6 +315,9 @@ char* arena_strncpy__(const char *file, const char *func, int line, Arena *arena
|
|||||||
#define arena_strdup(arena, s) \
|
#define arena_strdup(arena, s) \
|
||||||
arena_strdup__(__FILE__, __func__, __LINE__, arena, s)
|
arena_strdup__(__FILE__, __func__, __LINE__, arena, s)
|
||||||
|
|
||||||
|
#define arena_strdup_fmt(arena, ...) \
|
||||||
|
arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define arena_repstr(arena, src, start, len, rep) \
|
#define arena_repstr(arena, src, start, len, rep) \
|
||||||
arena_repstr__(__FILE__, __func__, __LINE__, arena, src, start, len, rep)
|
arena_repstr__(__FILE__, __func__, __LINE__, arena, src, start, len, rep)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Usage: make.sh [build|check] [--norun] [--debug] [--color]
|
# Usage: make.sh [build|check[test1 test2 ...]] [--norun] [--debug] [--color]
|
||||||
# Options:
|
# Options:
|
||||||
# build Build the library and app (default if no mode is provided).
|
# build Build the library and app (default if no mode is provided).
|
||||||
# watch Build the library and app and watch for changes.
|
# watch Build the library and app and watch for changes.
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
# --debug Build with -O0 (debug mode).
|
# --debug Build with -O0 (debug mode).
|
||||||
# --color Pass -fdiagnostics-color=always to compiler.
|
# --color Pass -fdiagnostics-color=always to compiler.
|
||||||
# help, --help Show this help message.
|
# help, --help Show this help message.
|
||||||
|
# test1 test2 (check only) Run specific tests by name (without .c extension)
|
||||||
|
|
||||||
check_dependencies() {
|
check_dependencies() {
|
||||||
for dep in cc ar; do
|
for dep in cc ar; do
|
||||||
@@ -29,13 +30,14 @@ check_dependencies
|
|||||||
|
|
||||||
print_help() {
|
print_help() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 [build|check] [--norun] [--debug] [--color]
|
Usage: $0 [build|check[test1 test2 ...]] [--norun] [--debug] [--color]
|
||||||
build Build the library and app (default).
|
build Build the library and app (default).
|
||||||
watch Build the library and app and watch for changes.
|
watch Build the library and app and watch for changes.
|
||||||
check Build tests; runs them unless --norun is specified.
|
check Build tests; runs them unless --norun is specified.
|
||||||
--norun (check only) Build tests but do not run them.
|
--norun (check only) Build tests but do not run them.
|
||||||
--debug Build with debug flags (-O0).
|
--debug Build with debug flags (-O0).
|
||||||
--color Force colored compiler diagnostics.
|
--color Force colored compiler diagnostics.
|
||||||
|
test1 test2 (check only) Run specific tests by name (without .c extension)
|
||||||
help, --help Display this help message.
|
help, --help Display this help message.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -51,15 +53,12 @@ esac
|
|||||||
# Default flags
|
# Default flags
|
||||||
RUN_TESTS=1
|
RUN_TESTS=1
|
||||||
OPTFLAGS="-O2"
|
OPTFLAGS="-O2"
|
||||||
CFLAGS="-Wall -Wextra -Werror -pedantic -fsanitize=address "
|
CFLAGS="-Wall -Wextra -Werror -pedantic -fsanitize=address -fanalyzer"
|
||||||
LDFLAGS="-lhectic"
|
LDFLAGS="-lhectic"
|
||||||
STD_FLAGS="-std=c99"
|
STD_FLAGS="-std=c99"
|
||||||
COLOR_FLAG=""
|
COLOR_FLAG=""
|
||||||
DEBUG=0
|
DEBUG=0
|
||||||
|
|
||||||
MODE="${1:-build}"
|
|
||||||
shift
|
|
||||||
|
|
||||||
# Process options
|
# Process options
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -76,17 +75,17 @@ while [ $# -gt 0 ]; do
|
|||||||
;;
|
;;
|
||||||
--color)
|
--color)
|
||||||
COLOR_FLAG="-fdiagnostics-color=always"
|
COLOR_FLAG="-fdiagnostics-color=always"
|
||||||
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
break
|
||||||
print_help
|
|
||||||
exit 1
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
MODE="${1:-build}"
|
||||||
|
shift
|
||||||
|
|
||||||
if [ -n "$COLOR_FLAG" ]; then
|
if [ -n "$COLOR_FLAG" ]; then
|
||||||
CFLAGS="$CFLAGS $COLOR_FLAG"
|
CFLAGS="$CFLAGS $COLOR_FLAG"
|
||||||
fi
|
fi
|
||||||
@@ -104,19 +103,46 @@ case "$MODE" in
|
|||||||
;;
|
;;
|
||||||
check)
|
check)
|
||||||
mkdir -p target/test
|
mkdir -p target/test
|
||||||
export LOG_LEVEL=TRACE
|
|
||||||
|
# Get list of tests to run
|
||||||
|
TESTS_TO_RUN=()
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--norun|--debug|--color)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
TESTS_TO_RUN+=("$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# If no specific tests provided, run all
|
||||||
|
if [ ${#TESTS_TO_RUN[@]} -eq 0 ]; then
|
||||||
|
TESTS_TO_RUN=("all")
|
||||||
|
fi
|
||||||
|
|
||||||
for test_file in test/*.c; do
|
for test_file in test/*.c; do
|
||||||
exe="target/test/$(basename "${test_file%.c}")"
|
test_name=$(basename "${test_file%.c}")
|
||||||
|
|
||||||
|
# Skip if specific tests are requested and this isn't one of them
|
||||||
|
if [ "${TESTS_TO_RUN[0]}" != "all" ] && ! [[ " ${TESTS_TO_RUN[*]} " =~ " ${test_name} " ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
exe="target/test/$test_name"
|
||||||
|
echo "Building test: $test_name"
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
cc $CFLAGS $OPTFLAGS -I. "$test_file" -Ltarget -lhectic $LDFLAGS -o "$exe"
|
cc $CFLAGS $OPTFLAGS -I. "$test_file" -Ltarget -lhectic $LDFLAGS -o "$exe"
|
||||||
if [ "$?" -ne 0 ]; then
|
if [ "$?" -ne 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$RUN_TESTS" -eq 1 ]; then
|
if [ "$RUN_TESTS" -eq 1 ]; then
|
||||||
if [ "$DEBUG" -eq 1 ]; then
|
if [ "$DEBUG" -eq 1 ]; then
|
||||||
gdb -tui "$exe"
|
env LOG_LEVEL="$LOG_LEVEL" gdb -tui "$exe"
|
||||||
fi
|
fi
|
||||||
"$exe"
|
env LOG_LEVEL="$LOG_LEVEL" "$exe"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
logger_level(LOG_LEVEL_DEBUG);
|
printf("Running %s\n", __FILE__);
|
||||||
|
|
||||||
TEST_RAISE_GENERIC(raise_debug, LOG_LEVEL_DEBUG, "DEBUG");
|
TEST_RAISE_GENERIC(raise_debug, LOG_LEVEL_DEBUG, "DEBUG");
|
||||||
TEST_RAISE_GENERIC(raise_log, LOG_LEVEL_LOG, "LOG");
|
TEST_RAISE_GENERIC(raise_log, LOG_LEVEL_LOG, "LOG");
|
||||||
|
|||||||
98
package/c/hectic/test/01-debug.c
Executable file
98
package/c/hectic/test/01-debug.c
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "hectic.h"
|
||||||
|
|
||||||
|
typedef struct TestStruct TestStruct;
|
||||||
|
|
||||||
|
struct TestStruct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
char *c;
|
||||||
|
TestStruct *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct TestStruct2 TestStruct2;
|
||||||
|
|
||||||
|
struct TestStruct2 {
|
||||||
|
int a;
|
||||||
|
char *c;
|
||||||
|
float f;
|
||||||
|
TestStruct2 *left;
|
||||||
|
TestStruct *other;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct PtrSet {
|
||||||
|
void **data;
|
||||||
|
size_t size;
|
||||||
|
size_t capacity;
|
||||||
|
} PtrSet;
|
||||||
|
|
||||||
|
static bool debug_ptrset_contains(PtrSet *set, void *ptr) {
|
||||||
|
for (size_t i = 0; i < set->size; i++) {
|
||||||
|
if (set->data[i] == ptr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_ptrset_add(PtrSet *set, void *ptr) {
|
||||||
|
if (set->size == set->capacity) {
|
||||||
|
set->capacity = set->capacity ? set->capacity * 2 : 4;
|
||||||
|
set->data = realloc(set->data, set->capacity * sizeof(void*));
|
||||||
|
}
|
||||||
|
set->data[set->size++] = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define STRING_TO_DEBUG_STR(arena, name, string) \
|
||||||
|
arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s = %p \"%s\"", name, string, string)
|
||||||
|
|
||||||
|
#define NUMBER_TO_DEBUG_STR(arena, name, number) \
|
||||||
|
arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s = %d", name, number)
|
||||||
|
|
||||||
|
#define STRUCT_TO_DEBUG_STR(arena, type, name, ptr, ...) __extension__ ({ \
|
||||||
|
char *result; \
|
||||||
|
if ((ptr) == NULL) { \
|
||||||
|
result = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \
|
||||||
|
} else { \
|
||||||
|
char* fields = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s, %s, %s", __VA_ARGS__); \
|
||||||
|
result = arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = {%s} %p", #type, name, fields, ptr); \
|
||||||
|
} \
|
||||||
|
result; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define test_struct_to_debug_str(arena, name, self) test_struct_to_debug_str__(arena, name, self)
|
||||||
|
|
||||||
|
char *test_struct_to_debug_str__(Arena *arena, char *name, TestStruct *self) {
|
||||||
|
if (name == NULL) {
|
||||||
|
name = "$1";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *result = STRUCT_TO_DEBUG_STR(arena, TestStruct, name, self,
|
||||||
|
NUMBER_TO_DEBUG_STR(arena, "a", self->a),
|
||||||
|
NUMBER_TO_DEBUG_STR(arena, "b", self->b),
|
||||||
|
test_struct_to_debug_str__(arena, "next", self->next)
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//char *test_struct_to_debug_str__(Arena *arena, char *name, char *type, TestStruct *self) {
|
||||||
|
// char *result = arena_strdup_fmt(arena, "%s %s{", name, type);
|
||||||
|
//
|
||||||
|
// result = arena_strdup_fmt(arena, "%s%s", result, NUMBER_TO_DEBUG_STR(arena, "a", self->a));
|
||||||
|
// result = arena_strdup_fmt(arena, "%s%s", result, NUMBER_TO_DEBUG_STR(arena, "b", self->b));
|
||||||
|
// result = arena_strdup_fmt(arena, "%s%s", result, test_struct_to_debug_str__(arena, "next", TestStruct, self->next));
|
||||||
|
//
|
||||||
|
// result = arena_strdup_fmt(arena, "%s} (%p)", result, self);
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("%sRunning %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
init_logger();
|
||||||
|
|
||||||
|
TestStruct test_struct = {.a = 1, .b = 2, .next = NULL};
|
||||||
|
raise_notice("%s", test_struct_to_debug_str(DISPOSABLE_ARENA, "test_struct", &test_struct));
|
||||||
|
|
||||||
|
printf("%sAll tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -91,7 +91,7 @@ void test_arena_overwrite_detection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
logger_level(LOG_LEVEL_DEBUG);
|
init_logger();
|
||||||
|
|
||||||
test_arena_init();
|
test_arena_init();
|
||||||
test_arena_alloc();
|
test_arena_alloc();
|
||||||
@@ -124,7 +124,7 @@ static void test_arena_reset_reuse(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
logger_level(LOG_LEVEL_DEBUG);
|
init_logger();
|
||||||
|
|
||||||
test_parse_json_object();
|
test_parse_json_object();
|
||||||
test_parse_json_number();
|
test_parse_json_number();
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "hectic.h"
|
|
||||||
|
|
||||||
#define ARENA_SIZE 1024 * 1024
|
|
||||||
|
|
||||||
static char *remove_all_spaces(char *s) {
|
|
||||||
char *new_s = NULL;
|
|
||||||
while (*s) {
|
|
||||||
if (*s != ' ' && *s != '\t' && *s != '\n') {
|
|
||||||
new_s = s;
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
return new_s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_template_node_to_debug_str(Arena *arena) {
|
|
||||||
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
|
||||||
root->type = TEMPLATE_NODE_TEXT;
|
|
||||||
root->value.text.content = arena_strncpy(arena, "Hello", 5);
|
|
||||||
|
|
||||||
root->next = arena_alloc(arena, sizeof(TemplateNode));
|
|
||||||
root->next->type = TEMPLATE_NODE_INTERPOLATE;
|
|
||||||
root->next->value.interpolate.key = arena_strncpy(arena, "name", 4);
|
|
||||||
|
|
||||||
root->next->next = arena_alloc(arena, sizeof(TemplateNode));
|
|
||||||
root->next->next->type = TEMPLATE_NODE_TEXT;
|
|
||||||
root->next->next->value.text.content = arena_strncpy(arena, "!", 1);
|
|
||||||
|
|
||||||
char *debug_str = template_node_to_debug_str(arena, root);
|
|
||||||
|
|
||||||
raise_notice("debug_str: %s", debug_str);
|
|
||||||
assert(strcmp(
|
|
||||||
remove_all_spaces(debug_str),
|
|
||||||
remove_all_spaces("" \
|
|
||||||
"[" \
|
|
||||||
" {" \
|
|
||||||
" \"type\":\"TEXT\"," \
|
|
||||||
" \"content\":{" \
|
|
||||||
" \"content\":\"Hello\"" \
|
|
||||||
" }" \
|
|
||||||
" }," \
|
|
||||||
" {" \
|
|
||||||
" \"type\":\"INTERPOLATE\"," \
|
|
||||||
" \"content\":{" \
|
|
||||||
" \"key\":\"name\"" \
|
|
||||||
" }" \
|
|
||||||
" }," \
|
|
||||||
" {" \
|
|
||||||
" \"type\":\"TEXT\"," \
|
|
||||||
" \"content\":{" \
|
|
||||||
" \"content\":\"!\"" \
|
|
||||||
" }" \
|
|
||||||
" }" \
|
|
||||||
"]")) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_template_parse(Arena *arena, TemplateConfig *config) {
|
|
||||||
const char *template = "Hello {% name %}!";
|
|
||||||
TemplateResult *result = template_parse(arena, &template, config);
|
|
||||||
|
|
||||||
Arena *debug_arena = DISPOSABLE_ARENA;
|
|
||||||
const char *debug_str = template_node_to_debug_str(debug_arena, &result->Result.node);
|
|
||||||
raise_notice("debug_str: %s", debug_str);
|
|
||||||
raise_notice("result: %s", json_to_pretty_str(debug_arena, json_parse(debug_arena, &debug_str)));
|
|
||||||
assert(result->type == TEMPLATE_RESULT_NODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
init_logger();
|
|
||||||
|
|
||||||
Arena arena = arena_init(ARENA_SIZE);
|
|
||||||
|
|
||||||
TemplateConfig config = template_default_config();
|
|
||||||
|
|
||||||
printf("%sRunning template parser tests...%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
|
||||||
|
|
||||||
test_template_node_to_debug_str(&arena);
|
|
||||||
printf("%sTest 0: template_node_to_debug_str passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
|
||||||
arena_reset(&arena);
|
|
||||||
|
|
||||||
test_template_parse(&arena, &config);
|
|
||||||
printf("%sTest 1: template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
|
||||||
arena_reset(&arena);
|
|
||||||
|
|
||||||
arena_free(&arena);
|
|
||||||
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -83,7 +83,7 @@ void test_slice_string() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
logger_level(LOG_LEVEL_DEBUG);
|
init_logger();
|
||||||
|
|
||||||
test_slice_create();
|
test_slice_create();
|
||||||
test_slice_subslice();
|
test_slice_subslice();
|
||||||
92
package/c/hectic/test/06-templater.c
Executable file
92
package/c/hectic/test/06-templater.c
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "hectic.h"
|
||||||
|
|
||||||
|
#define ARENA_SIZE 1024 * 1024
|
||||||
|
|
||||||
|
//static char *remove_all_spaces(char *s) {
|
||||||
|
// char *new_s = NULL;
|
||||||
|
// while (*s) {
|
||||||
|
// if (*s != ' ' && *s != '\t' && *s != '\n') {
|
||||||
|
// new_s = s;
|
||||||
|
// }
|
||||||
|
// s++;
|
||||||
|
// }
|
||||||
|
// return new_s;
|
||||||
|
//}
|
||||||
|
|
||||||
|
static void test_template_node_to_debug_str(Arena *arena) {
|
||||||
|
TemplateNode *root = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
|
root->type = TEMPLATE_NODE_TEXT;
|
||||||
|
root->value.text.content = arena_strncpy(arena, "Hello", 5);
|
||||||
|
|
||||||
|
root->next = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
|
root->next->type = TEMPLATE_NODE_INTERPOLATE;
|
||||||
|
root->next->value.interpolate.key = arena_strncpy(arena, "name", 4);
|
||||||
|
|
||||||
|
root->next->next = arena_alloc(arena, sizeof(TemplateNode));
|
||||||
|
root->next->next->type = TEMPLATE_NODE_TEXT;
|
||||||
|
root->next->next->value.text.content = arena_strncpy(arena, "!", 1);
|
||||||
|
|
||||||
|
char *debug_str = template_node_to_debug_str(arena, root);
|
||||||
|
|
||||||
|
raise_notice("debug_str: %s", debug_str);
|
||||||
|
//assert(strcmp(
|
||||||
|
// remove_all_spaces(debug_str),
|
||||||
|
// remove_all_spaces(""
|
||||||
|
// "["
|
||||||
|
// " {"
|
||||||
|
// " \"type\":\"TEXT\","
|
||||||
|
// " \"content\":{"
|
||||||
|
// " \"content\":\"Hello\""
|
||||||
|
// " }"
|
||||||
|
// " },"
|
||||||
|
// " {"
|
||||||
|
// " \"type\":\"INTERPOLATE\","
|
||||||
|
// " \"content\":{"
|
||||||
|
// " \"key\":\"name\""
|
||||||
|
// " }"
|
||||||
|
// " },"
|
||||||
|
// " {"
|
||||||
|
// " \"type\":\"TEXT\","
|
||||||
|
// " \"content\":{"
|
||||||
|
// " \"content\":\"!\""
|
||||||
|
// " }"
|
||||||
|
// " }"
|
||||||
|
// "]")) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//static void test_template_parse(Arena *arena, TemplateConfig *config) {
|
||||||
|
// const char *template = "Hello {% name %}!";
|
||||||
|
// TemplateResult *result = template_parse(arena, &template, config);
|
||||||
|
//
|
||||||
|
// Arena *debug_arena = DISPOSABLE_ARENA;
|
||||||
|
// const char *debug_str = template_node_to_debug_str(debug_arena, &result->Result.node);
|
||||||
|
// raise_notice("debug_str: %s", debug_str);
|
||||||
|
// raise_notice("result: %s", json_to_pretty_str(debug_arena, json_parse(debug_arena, &debug_str)));
|
||||||
|
// assert(result->type == TEMPLATE_RESULT_NODE);
|
||||||
|
//}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
init_logger();
|
||||||
|
|
||||||
|
Arena arena = arena_init(ARENA_SIZE);
|
||||||
|
|
||||||
|
//TemplateConfig config = template_default_config();
|
||||||
|
|
||||||
|
printf("%sRunning template parser tests...%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
|
||||||
|
test_template_node_to_debug_str(&arena);
|
||||||
|
printf("%sTest 0: template_node_to_debug_str passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
arena_reset(&arena);
|
||||||
|
|
||||||
|
//test_template_parse(&arena, &config);
|
||||||
|
//printf("%sTest 1: template_parse passed%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
//arena_reset(&arena);
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
|
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user