Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2d84f9f
add Stack_Copy() and Stack_IsEmpty()
defname Oct 22, 2025
dcdab60
fix: return copy in Stack_Copy()
defname Oct 22, 2025
7f1725f
implement syntax highlighting
defname Oct 22, 2025
39d1a5f
add Table_CreateCustom() and Table_CreatePtr()
defname Oct 22, 2025
65ab4a4
Merge branch 'feat/table-support-custom-keys' into feat/sh-highlighting
defname Oct 22, 2025
a83d37c
fix minor typing bugs
defname Oct 22, 2025
48a1223
add Stack_Create(), Stack_Destroy()
defname Oct 22, 2025
db6c5ae
fix but in find_first_child to actually return the child with the fir…
defname Oct 22, 2025
29b9403
automatically add patterns to end only_start blocks and to start/end …
defname Oct 22, 2025
72376ce
fix memory leak produced in last commit
defname Oct 22, 2025
1370b1c
fix tests
defname Oct 22, 2025
abdfb6d
fix bug in SyntaxHighllighting_HighlightString(): check if child or b…
defname Oct 22, 2025
1657516
add basic tests
defname Oct 22, 2025
3ab831f
use root block it open_blocks parameter is NULL in SyntaxHighlighting…
defname Oct 22, 2025
ab4071a
add random tests
defname Oct 22, 2025
cc90894
handle src->capacity == 0 in Stack_Copy()
defname Oct 23, 2025
6f330ce
add NULL pointer guard before using the key_free_func() function poin…
defname Oct 23, 2025
ba76573
fix error message for end_regex compilation error in SyntaxBlockDef_F…
defname Oct 23, 2025
cbf841b
correct inlcudes
defname Oct 23, 2025
4dece63
fix typo
defname Oct 23, 2025
a94c78c
add missing includes
defname Oct 23, 2025
8352c27
change examples in documentation
defname Oct 23, 2025
70e3a4a
change "key == NULL" to "!key" for non pointer keys
defname Oct 23, 2025
316bf22
do not shadow parameter match in find_first_block()
defname Oct 23, 2025
68b8fed
change first parameter name in SyntaxHighlighting_HighlightString() f…
defname Oct 23, 2025
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
51 changes: 51 additions & 0 deletions src/common/stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stack.h"
#include <string.h>
#include "logging.h"


static void increase_capacity(Stack *stack) {
if (!stack) {
return;
Expand Down Expand Up @@ -49,6 +51,51 @@ void Stack_Deinit(Stack *stack) {
stack->capacity = 0;
}

Stack *Stack_Create() {
Stack *stack = malloc(sizeof(Stack));
if (!stack) {
logFatal("Failed to allocate memory for stack.");
}
Stack_Init(stack);
return stack;
}

void Stack_Destroy(Stack *stack) {
if (!stack) {
return;
}
Stack_Deinit(stack);
free(stack);
}

Stack *Stack_Copy(const Stack *src) {
if (!src) {
return NULL;
}
Stack *copy = malloc(sizeof(Stack));
if (!copy) {
logFatal("Failed to allocate memory for stack copy.");
}

size_t cap = src->capacity ? src->capacity : STACK_INITIAL_CAPACITY; // handle capacity == 0

// allocate memory
copy->items = malloc(cap * sizeof(void *));
if (!copy->items) {
logFatal("Failed to allocate memory for stack copy items.");
}
// only copy items
memcpy(copy->items, src->items, src->size * sizeof(void *));
// set remainder to 0
if (cap > src->size) {
memset(copy->items + src->size, 0, (cap - src->size) * sizeof(void *));
}
copy->size = src->size;
copy->capacity = src->capacity;

return copy;
}

void Stack_Push(Stack *stack, void *item) {
if (!stack) {
return;
Expand Down Expand Up @@ -86,6 +133,10 @@ bool Stack_Has(const Stack *stack, const void *item) {
return false;
}

bool Stack_IsEmpty(const Stack *stack) {
return stack->size == 0;
}

void Stack_Clear(Stack *stack) {
if (!stack) {
return;
Expand Down
7 changes: 7 additions & 0 deletions src/common/stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ typedef struct _Stack {
void Stack_Init(Stack *stack);
void Stack_Deinit(Stack *stack);

Stack *Stack_Create();
void Stack_Destroy(Stack *stack);

Stack *Stack_Copy(const Stack *stack);

void Stack_Push(Stack *stack, void *item);
void *Stack_Pop(Stack *stack);
void *Stack_Peek(const Stack *stack);

bool Stack_Has(const Stack *stack, const void *item);

bool Stack_IsEmpty(const Stack *stack);

void Stack_Clear(Stack *stack);


Expand Down
75 changes: 58 additions & 17 deletions src/common/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ void TableSlot_Init(TableSlot *slot) {
slot->state = TABLE_SLOT_EMPTY;
}

void TableSlot_Deinit(TableSlot *slot) {
void TableSlot_Deinit(TableSlot *slot, void (*free_key_func)(void *key)) {
if (!slot) {
return;
}
if (slot->key) {
free(slot->key);
if (slot->key && free_key_func) {
free_key_func(slot->key);
}
if (slot->destructor) {
slot->destructor(slot->value);
Expand All @@ -51,15 +51,28 @@ static uint32_t hash_string(const char *str) {
return hash;
}

static TableSlot *find_slot(const Table *table, const char *key) {
static uint32_t hash_ptr(const void *p) {
uintptr_t x = (uintptr_t)p;
return (uint32_t)(x ^ (x >> 32));
}

static int cmp_ptr(const void *a, const void *b) {
return a != b;
}

static void *cpy_ptr(const void *p) {
return (void*)p;
}

static TableSlot *find_slot(const Table *table, const void *key) {
if (!table || !key) {
logFatal("Invalid table or key in find_slot()");
}
// the +1 is a hotfix for an edge case bug. It need to be ensured that this does not succeed table->capacity!
if (table->used >= table->capacity * TABLE_MAX_LOAD_FACTOR + 1) {
logFatal("Table too loaded."); // this can only happen there is an error in the code
}
uint32_t hash = hash_string(key);
uint32_t hash = table->hash_func(key);
size_t index = hash % table->capacity;
TableSlot *tombstone = NULL;
for (;;) {
Expand All @@ -73,7 +86,7 @@ static TableSlot *find_slot(const Table *table, const char *key) {
else if (slot->state == TABLE_SLOT_TOMBSTONE && tombstone == NULL) {
tombstone = slot;
}
else if (slot->state == TABLE_SLOT_USED && strcmp(slot->key, key) == 0) {
else if (slot->state == TABLE_SLOT_USED && table->key_cmp_func(slot->key, key) == 0) {
return slot;
}

Expand Down Expand Up @@ -130,8 +143,8 @@ static void increase_capacity(Table *table) {
// free old table keys
for (size_t i=0; i<table->capacity; i++) {
TableSlot *slot = &table->slots[i];
if (slot->state == TABLE_SLOT_USED) {
free(slot->key);
if (slot->state == TABLE_SLOT_USED && table->key_free_func) {
table->key_free_func(slot->key);
}
}
// free old table slots
Expand Down Expand Up @@ -164,7 +177,7 @@ void Table_Deinit(Table *table) {
if (table->slots) {
for (size_t i=0; i<table->capacity; i++) {
TableSlot *entry = &table->slots[i];
TableSlot_Deinit(entry);
TableSlot_Deinit(entry, table->key_free_func);
}
free(table->slots);
}
Expand All @@ -174,8 +187,36 @@ void Table_Deinit(Table *table) {
}

Table *Table_Create() {
return Table_CreateCustom(
(uint32_t(*)(const void*))hash_string,
(int(*)(const void*, const void*))strcmp,
(void*(*)(const void*))strdup,
(void(*)(void*))free);
}

Table *Table_CreatePtr() {
return Table_CreateCustom(
hash_ptr,
cmp_ptr,
cpy_ptr,
NULL);
}

Table *Table_CreateCustom(
uint32_t (*hash_func)(const void *p),
int (*key_cmp_func)(const void*, const void *),
void *(*key_copy_func)(const void *p),
void (*key_free_func)(void *p)
) {
Table *table = malloc(sizeof(Table));
if (!table) {
logFatal("Failed to allocate memory for table.");
}
Table_Init(table);
table->hash_func = hash_func;
table->key_cmp_func = key_cmp_func;
table->key_copy_func = key_copy_func;
table->key_free_func = key_free_func;
return table;
}

Expand All @@ -188,7 +229,7 @@ void Table_Destroy(Table *table) {
}


void Table_Set(Table *table, const char*key, void *value, void (*destructor)(void *value)) {
void Table_Set(Table *table, const void *key, void *value, void (*destructor)(void *value)) {
if (!table || !key) {
return;
}
Expand All @@ -202,8 +243,8 @@ void Table_Set(Table *table, const char*key, void *value, void (*destructor)(voi

if (slot->state == TABLE_SLOT_EMPTY) {
slot->state = TABLE_SLOT_USED;
slot->key = strdup(key);
if (slot->key == NULL) {
slot->key = table->key_copy_func(key);
if (!slot->key) {
logFatal("No memory for string copy in Table_Set().");
}
table->used++;
Expand All @@ -216,7 +257,7 @@ void Table_Set(Table *table, const char*key, void *value, void (*destructor)(voi
slot->value = value; // potential pointers in value are copied and ownership is taken
}

void *Table_Get(const Table *table, const char *key) {
void *Table_Get(const Table *table, const void *key) {
if (!table) {
logFatal("Invalid table in Table_Get().");
}
Expand All @@ -235,7 +276,7 @@ void *Table_Get(const Table *table, const char *key) {
return slot->value;
}

void Table_Delete(Table *table, const char *key) {
void Table_Delete(Table *table, const void *key) {
if (!table) {
logFatal("Invalid table in Table_Delete().");
}
Expand All @@ -250,14 +291,14 @@ void Table_Delete(Table *table, const char *key) {
if (slot->state != TABLE_SLOT_USED) {
return;
}
TableSlot_Deinit(slot);
TableSlot_Deinit(slot, table->key_free_func);
slot->state = TABLE_SLOT_TOMBSTONE;

// used is not decremented intentionally!
// tombstones are counted to trigger rehash earlier and decrease probing
}

bool Table_Has(const Table *table, const char *key) {
bool Table_Has(const Table *table, const void *key) {
if (!table) {
logFatal("Invalid table in Table_Has().");
}
Expand All @@ -272,7 +313,7 @@ bool Table_Has(const Table *table, const char *key) {
return (slot->state == TABLE_SLOT_USED);
}

bool Table_HasOwnership(const Table *table, const char *key) {
bool Table_HasOwnership(const Table *table, const void *key) {
if (!table) {
logFatal("Invalid table in Table_HasOwnership().");
}
Expand Down
44 changes: 37 additions & 7 deletions src/common/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
* Simple implementation of a hashtable using the open addressing
* with linear probing. For hashing the djb2 hash algorithm is used.
*
* Custom keys are supported via the Table_CreateCustom() function.
*
* The load factor is calculated from all slots that are not empty
* (tombstones included). It's assumed that there not too many deletions
* occure. Otherwise the worst that can happen is that the used memory
Expand All @@ -28,6 +30,7 @@
#ifndef TABLE_H
#define TABLE_H

#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

Expand All @@ -42,25 +45,52 @@ typedef enum {
} TableSlotState;

typedef struct _TableSlot {
char *key;
void *key;
void *value;
void (*destructor)(void *value);
TableSlotState state;
} TableSlot;

void TableSlot_Init(TableSlot *slot);
void TableSlot_Deinit(TableSlot *slot);
void TableSlot_Deinit(TableSlot *slot, void (*free_key_func)(void *key));

typedef struct _Table {
TableSlot *slots;
size_t capacity;
size_t used;

uint32_t (*hash_func)(const void *p);
int (*key_cmp_func)(const void*, const void *);
void *(*key_copy_func)(const void *p);
void (*key_free_func)(void *p);
} Table;

void Table_Init(Table *table);
void Table_Deinit(Table *table);

/**
* @brief Create a `Table` with char* keys.
*/
Table *Table_Create();

/**
* @brief Create a `Table` with pointer addresses as keys.
*/
Table *Table_CreatePtr();

/**
* @brief Create a `Table`with custom keys.
*/
Table *Table_CreateCustom(
uint32_t (*hash_func)(const void *p),
int (*key_cmp_func)(const void*, const void *),
void *(*key_copy_func)(const void *p),
void (*key_free_func)(void *p)
);

/**
* @brief Destroy `table`
*/
void Table_Destroy(Table *table);

/**
Expand All @@ -70,32 +100,32 @@ void Table_Destroy(Table *table);
* for destroying it (in the case of overwriting or deleting it). If destructor is NULL the
* ownership stays by the owner and he need to take care of freeing value.
*/
void Table_Set(Table *table, const char *key, void *value, void (*destructor)(void *value));
void Table_Set(Table *table, const void *key, void *value, void (*destructor)(void *value));

/**
* @brief Return the value for key or NULL if key is not found.
*
* If NULL is a ligit value for key use Table_Has() to check if the key exists in the table.
*/
void *Table_Get(const Table *table, const char *key);
void *Table_Get(const Table *table, const void *key);

/**
* @brief Delete the entry for key in table.
*
* Delete the entry of key if it exists. If table has the ownership for value (a destructor is present)
* value will be destructed.
*/
void Table_Delete(Table *table, const char *key);
void Table_Delete(Table *table, const void *key);

/**
* @brief Return true if the key exists in table.
*/
bool Table_Has(const Table *table, const char *key);
bool Table_Has(const Table *table, const void *key);

/**
* @brief Return true if table has the ownership for the entry of key
*/
bool Table_HasOwnership(const Table *table, const char *key);
bool Table_HasOwnership(const Table *table, const void *key);

/**
* @brief Return the current number of non-free table slots.
Expand Down
Loading