Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/lf_token.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ lf_token_t* _lf_new_token(token_type_t* type, void* value, size_t length) {
if (result == NULL) {
// Nothing found on the recycle bin.
result = (lf_token_t*)calloc(1, sizeof(lf_token_t));
LF_ASSERT_NON_NULL(result);
LF_PRINT_DEBUG("_lf_new_token: Allocated memory for token: %p", (void*)result);
}
result->type = type;
Expand Down
5 changes: 5 additions & 0 deletions logging/api/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
* It provides functions for different log levels (error, warning, info, log, debug)
* and allows for custom message handling through function registration.
*/
#ifndef LOGGING_H
#define LOGGING_H

#include <stdarg.h>

// To silence warnings about a function being a candidate for format checking
Expand Down Expand Up @@ -198,3 +201,5 @@ typedef void(print_message_function_t)(const char*, va_list);
* @param log_level The level of messages to redirect.
*/
void lf_register_print_function(print_message_function_t* function, int log_level);

#endif // LOGGING_H
45 changes: 1 addition & 44 deletions util/deque.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* To use this, include the following in your target properties:
* <pre>
* target C {
* cmake-include: "/lib/c/reactor-c/util/deque.cmake"
* cmake-include: "/lib/c/reactor-c/util/deque.cmake",
* files: ["/lib/c/reactor-c/util/deque.c", "/lib/c/reactor-c/util/deque.h"]
* };
* </pre>
Expand Down Expand Up @@ -44,10 +44,6 @@ typedef struct deque_node_t {
void* value;
} deque_node_t;

/**
* Initialize the specified deque to an empty deque.
* @param d The deque.
*/
void deque_initialize(deque_t* d) {
if (d != NULL) {
d->front = NULL;
Expand All @@ -56,22 +52,13 @@ void deque_initialize(deque_t* d) {
}
}

/**
* Return true if the queue is empty.
* @param d The deque.
*/
bool deque_is_empty(deque_t* d) {
if (d != NULL) {
return (d->front == NULL);
}
return true;
}

/**
* Return the size of the queue.
* @param d The deque.
* @return The size of the queue.
*/
size_t deque_size(deque_t* d) { return d->size; }

/**
Expand All @@ -87,11 +74,6 @@ deque_node_t* _deque_create_node(void* value) {
return new_node;
}

/**
* Push a value to the front of the queue.
* @param d The queue.
* @param value The value to push.
*/
void deque_push_front(deque_t* d, void* value) {
deque_node_t* n = _deque_create_node(value);
if (d->front == NULL) {
Expand All @@ -105,11 +87,6 @@ void deque_push_front(deque_t* d, void* value) {
}
}

/**
* Push a value to the back of the queue.
* @param d The queue.
* @param value The value to push.
*/
void deque_push_back(deque_t* d, void* value) {
deque_node_t* n = _deque_create_node(value);
if (d->back == NULL) {
Expand All @@ -123,11 +100,6 @@ void deque_push_back(deque_t* d, void* value) {
}
}

/**
* Pop a value from the front of the queue, removing it from the queue.
* @param d The queue.
* @return The value on the front of the queue or NULL if the queue is empty.
*/
void* deque_pop_front(deque_t* d) {
if (d == NULL || d->front == NULL) {
return NULL;
Expand All @@ -147,11 +119,6 @@ void* deque_pop_front(deque_t* d) {
return value;
}

/**
* Pop a value from the back of the queue, removing it from the queue.
* @param d The queue.
* @return The value on the back of the queue or NULL if the queue is empty.
*/
void* deque_pop_back(deque_t* d) {
if (d == NULL || d->back == NULL) {
return NULL;
Expand All @@ -170,23 +137,13 @@ void* deque_pop_back(deque_t* d) {
return value;
}

/**
* Peek at the value on the front of the queue, leaving it on the queue.
* @param d The queue.
* @return The value on the front of the queue or NULL if the queue is empty.
*/
void* deque_peek_back(deque_t* d) {
if (d == NULL || d->back == NULL) {
return NULL;
}
return d->back->value;
}

/**
* Peek at the value on the back of the queue, leaving it on the queue.
* @param d The queue.
* @return The value on the back of the queue or NULL if the queue is empty.
*/
void* deque_peek_front(deque_t* d) {
if (d == NULL || d->front == NULL) {
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion util/deque.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*
* ```
* target C {
* cmake-include: "/lib/c/reactor-c/util/deque.cmake"
* cmake-include: "/lib/c/reactor-c/util/deque.cmake",
* files: ["/lib/c/reactor-c/util/deque.c", "/lib/c/reactor-c/util/deque.h"]
* };
* ```
Expand Down
171 changes: 171 additions & 0 deletions util/initialize_from_file.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#include "logging/api/logging.h"
#include "reactor.h"
#include "initialize_from_file.h"

typedef enum { LF_TYPE_DOUBLE, LF_TYPE_INT, LF_TYPE_STRING } lf_field_type;

/** Remove leading and trailing whitespace from the string. */
static void sc_csv_trim(char* s) {
char* p = s;
while (*p && isspace((unsigned char)*p)) {
p++;
}
if (p != s) {
memmove(s, p, strlen(p) + 1);
}
size_t n = strlen(s);
while (n > 0 && isspace((unsigned char)s[n - 1])) {
s[--n] = '\0';
}
}

/** Replace the specified delimiter with a null character and return the number of fields. */
static int sc_csv_split(char* line, char delimiter, char** fields, int max_fields) {
int n = 0;
char* cur = line;
while (n < max_fields) {
fields[n++] = cur;
char* sep = strchr(cur, delimiter);
if (!sep) {
break;
}
*sep = '\0';
cur = sep + 1;
}
return n;
}

/**
* Shared implementation for lf_initialize_double, lf_initialize_int, and _lf_initialize_string.
* The `type` parameter selects the expected va_arg pointer type (double*, int*, or char**).
* The `allocations` parameter is used only for LF_TYPE_STRING to record allocated memory.
*/
static int lf_initialize_fields(const char* filename, char delimiter, size_t row_number, lf_field_type type,
struct allocation_record_t** allocations, va_list ap) {
size_t pointer_count = 0;
FILE* f = fopen(filename, "r");
if (!f) {
lf_print_error("Could not open file \"%s\".", filename);
return -1;
}

char line[SC_CSV_LINE_MAX];
size_t row_count = 0;
while (fgets(line, sizeof(line), f)) {
// Detect truncation: fgets didn't capture a newline and we're not at EOF.
size_t len = strlen(line);
int truncated = (len > 0 && line[len - 1] != '\n' && !feof(f));

if (row_count == row_number) {
if (truncated) {
lf_print_error("Row %zu exceeds maximum line length of %d in file \"%s\".", row_number, SC_CSV_LINE_MAX - 1,
filename);
fclose(f);
return -1;
}
char* row = &line[0];
if (len >= 3 && (unsigned char)row[0] == 0xEF && (unsigned char)row[1] == 0xBB && (unsigned char)row[2] == 0xBF) {
row += 3;
}
row[strcspn(row, "\r\n")] = '\0';

char* fields[SC_CSV_MAX_COLS];
int field_count = sc_csv_split(row, delimiter, fields, SC_CSV_MAX_COLS);
for (size_t i = 0; i < (size_t)field_count; i++) {
sc_csv_trim(fields[i]);
if (type == LF_TYPE_DOUBLE) {
double* out = va_arg(ap, double*);
if (out == NULL)
break;
pointer_count++;
char* end = NULL;
double parsed = strtod(fields[i], &end);
if (end != fields[i] && *end == '\0') {
*out = parsed;
} else {
lf_print_error("Failed to parse numeric value \"%s\" at row %zu, column %zu in \"%s\".", fields[i],
row_number, i, filename);
fclose(f);
return -1;
}
} else if (type == LF_TYPE_INT) {
int* out = va_arg(ap, int*);
if (out == NULL)
break;
pointer_count++;
char* end = NULL;
long parsed = strtol(fields[i], &end, 10);
if (end != fields[i] && *end == '\0') {
*out = (int)parsed;
} else {
lf_print_error("Failed to parse integer value \"%s\" at row %zu, column %zu in \"%s\".", fields[i],
row_number, i, filename);
fclose(f);
return -1;
}
} else {
char** out = va_arg(ap, char**);
if (out == NULL)
break;
pointer_count++;
char* field = fields[i];
size_t len = strlen(field);
if (len >= 2 &&
((field[0] == '"' && field[len - 1] == '"') || (field[0] == '\'' && field[len - 1] == '\''))) {
field++;
len -= 2;
}
char* str = (char*)lf_allocate(len + 1, sizeof(char), allocations);
memcpy(str, field, len);
str[len] = '\0';
*out = str;
}
}
fclose(f);
return (int)pointer_count;
}
if (truncated) {
// Consume the rest of this over-long line so it counts as one row.
while (fgets(line, sizeof(line), f)) {
len = strlen(line);
if ((len > 0 && line[len - 1] == '\n') || feof(f))
break;
}
}
row_count++;
}
lf_print_error("Requested row %zu not found in file \"%s\".", row_number, filename);
fclose(f);
return -1;
}

int lf_initialize_double(const char* filename, char delimiter, size_t row_number, ...) {
va_list ap;
va_start(ap, row_number);
int result = lf_initialize_fields(filename, delimiter, row_number, LF_TYPE_DOUBLE, NULL, ap);
va_end(ap);
return result;
}

int lf_initialize_int(const char* filename, char delimiter, size_t row_number, ...) {
va_list ap;
va_start(ap, row_number);
int result = lf_initialize_fields(filename, delimiter, row_number, LF_TYPE_INT, NULL, ap);
va_end(ap);
return result;
}

int _lf_initialize_string(const char* filename, char delimiter, size_t row_number,
struct allocation_record_t** allocations, ...) {
va_list ap;
va_start(ap, allocations);
int result = lf_initialize_fields(filename, delimiter, row_number, LF_TYPE_STRING, allocations, ap);
va_end(ap);
return result;
}
1 change: 1 addition & 0 deletions util/initialize_from_file.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target_sources(${LF_MAIN_TARGET} PRIVATE initialize_from_file.c)
Loading
Loading