From 8f6973a08834fc78b06c068089a4296e311a4be8 Mon Sep 17 00:00:00 2001 From: Timo Lassmann Date: Mon, 7 Oct 2024 10:37:57 +0800 Subject: [PATCH] Added simple hash as separate module --- src/hash/hash.c | 370 +++++++++++++++++++++++++ src/hash/hash.h | 20 ++ src/hash/hash_struct.h | 31 +++ src/template/template.c | 508 ++++++++++------------------------- src/template/template.h | 21 +- src/tld.c | 2 + src/tld.h | 2 +- tests/CMakeLists.txt | 7 +- tests/utests/unit_hash.c | 307 +++++++++++++++++++++ tests/utests/unit_template.c | 370 +++++++------------------ 10 files changed, 980 insertions(+), 658 deletions(-) create mode 100644 src/hash/hash.c create mode 100644 src/hash/hash.h create mode 100644 src/hash/hash_struct.h create mode 100644 tests/utests/unit_hash.c diff --git a/src/hash/hash.c b/src/hash/hash.c new file mode 100644 index 0000000..8e505f4 --- /dev/null +++ b/src/hash/hash.c @@ -0,0 +1,370 @@ +#include "hash.h" +#include "../core/tld-core.h" +#include "../alloc/tld-alloc.h" +#include "../string/str.h" + +#include + +#define LOAD_FACTOR_THRESHOLD 0.75 + +#include "hash_struct.h" + +tld_internal int tld_hash_entry_alloc(tld_hash_entry **entry); +tld_internal void tld_hash_entry_free(tld_hash_entry *n); + +tld_internal unsigned int fnv1a_hash(char *str, int table_size); +tld_internal int tld_hash_resize(tld_hash *h); + +static unsigned int fnv1a_hash(char *str, int table_size) +{ + unsigned int hash = 2166136261u; + int c; + while ((c = *str++)) { + hash ^= c; + hash *= 16777619; + } + return hash % table_size; +} + +int tld_hash_add(tld_hash **hash, char *key, char *val) +{ + tld_hash* h = NULL; + if(*hash){ + h = *hash; + }else{ + RUN(tld_hash_alloc(&h, 1024)); + } + + + ASSERT(h != NULL, "No Map"); + + // Check load factor + double load_factor = (double)h->n / (double)h->table_size; + + if (load_factor > LOAD_FACTOR_THRESHOLD) { + RUN(tld_hash_resize(h)); // Double the table size + } + + + ASSERT(h != NULL, "No Map"); + + unsigned int index = fnv1a_hash(key, h->table_size); + + // Check if key already exists + tld_hash_entry* current = h->table[index]; + while (current) { + if (strcmp(TLD_STR(current->key), key) == 0) { + // Key exists, replace the value + tld_strbuf_clear(current->value); + tld_append(current->value, val); + return OK; + } + current = current->next; + } + + // Key does not exist, create a new entry + tld_hash_entry* new_entry = NULL; + tld_hash_entry_alloc(&new_entry); + + tld_append(new_entry->key, key); + tld_append(new_entry->value, val); + new_entry->next = h->table[index]; + h->table[index] = new_entry; + h->n++; + + *hash = h; + return OK; +ERROR: + return FAIL; +} + +int tld_hash_get(tld_hash *h, char *key, char **val) +{ + ASSERT(h != NULL, "No Map!!"); + ASSERT(key != NULL, "No key"); + + unsigned int index = fnv1a_hash(key, h->table_size); + tld_hash_entry* entry = h->table[index]; + + while (entry) { + if (strcmp(TLD_STR(entry->key), key) == 0) { + *val = TLD_STR(entry->value); + return OK; + } + entry = entry->next; + } + + *val = NULL; // Key not found + return FAIL; +ERROR: + return FAIL; +} + +int tld_hash_rename(tld_hash *h, char *key, char* new_key) { + ASSERT(h != NULL, "No Map!!"); + ASSERT(key != NULL, "No key"); + + unsigned int index = fnv1a_hash(key, h->table_size); + tld_hash_entry* entry = h->table[index]; + + while (entry) { + if (strcmp(TLD_STR(entry->key), key) == 0) { + tld_strbuf* tmp = NULL; + tld_strbuf_alloc(&tmp, 64); + // Remove the old key from the hash table + tld_strbuf_copy(tmp, entry->value); + + tld_hash_del(h, key); + // Add the new key with the existing replacement + tld_hash_add(&h, new_key, TLD_STR(tmp)); + tld_strbuf_free(tmp); + return OK; + } + entry = entry->next; + } + + return FAIL; // Key not found +ERROR: + return FAIL; +} + +int tld_hash_del(tld_hash *h, char *key) +{ + ASSERT(h != NULL, "No Map!!"); + ASSERT(key != NULL, "No key"); + + unsigned int index = fnv1a_hash(key, h->table_size); + tld_hash_entry* entry = h->table[index]; + tld_hash_entry* prev = NULL; + + while(entry){ + if(strcmp(TLD_STR(entry->key), key) == 0){ + if(prev){ + prev->next = entry->next; + }else{ + h->table[index] = entry->next; + } + // Free the entry + tld_hash_entry_free(entry); + /* tld_strbuf_free(entry->key); */ + /* tld_strbuf_free(entry->value); */ + /* MFREE(entry); */ + h->n--; + return OK; + } + prev = entry; + entry = entry->next; + } + + return FAIL; // Key not found +ERROR: + return FAIL; +} + +int tld_hash_iter_init(tld_hash *h) +{ + ASSERT(h != NULL, "No Map"); + h->cur_index = 0; + h->cur_entry = NULL; + while(h->cur_index < h->table_size && h->table[h->cur_index] == NULL){ + h->cur_index++; + } + + // Set current_entry to the first non-null entry if found. + if(h->cur_index < h->table_size){ + h->cur_entry = h->table[h->cur_index]; + } + return OK; +ERROR: + return FAIL; +} + +int tld_hash_iterator_next(tld_hash* h, char **key,char **value) +{ + ASSERT(h != NULL, "Iterator is NULL"); + ASSERT(key != NULL, "Key pointer is NULL"); + ASSERT(value != NULL, "Value pointer is NULL"); + + // If there is no current entry, return 0 (no more entries). + if (h->cur_entry == NULL) { + return FAIL; + } + + // Set the output key and value. + *key = TLD_STR(h->cur_entry->key); + *value = TLD_STR(h->cur_entry->value); + + // Move to the next entry in the current linked list. + h->cur_entry = h->cur_entry->next; + + + // If the current entry is NULL, move to the next index in the table. + while(h->cur_entry == NULL && h->cur_index + 1 < h->table_size){ + h->cur_index++; + h->cur_entry = h->table[h->cur_index]; + } + + return OK; +ERROR: + return FAIL; +} + + +int tld_hash_alloc(tld_hash **hash, int size) +{ + tld_hash* h = NULL; + int new_size = 0; + MMALLOC(h, sizeof(tld_hash)); + + static const int prime_sizes[] = { + 53, 97, 193, 389, 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, 196613, 393241, + 786433, 1572869, 3145739, 6291469, 12582917, + 25165843, 50331653, 100663319, 201326611, + 402653189, 805306457, 1610612741 + }; + static const int num_primes = sizeof(prime_sizes) / sizeof(prime_sizes[0]); + + for (int i = 0; i < num_primes; i++) { + if (prime_sizes[i] > size) { + new_size = prime_sizes[i]; + break; + } + } + + h->table_size = new_size; // Choose an appropriate size + h->table = NULL; + // Initialize the map with a default size + + h->n = 0; + + h->cur_index = 0; + h->cur_entry = NULL; + + MMALLOC(h->table, sizeof(tld_hash_entry*) * h->table_size); + for (int i = 0; i < h->table_size; i++) { + h->table[i] = NULL; + } + *hash = h; + return OK; +ERROR: + if(h){ + tld_hash_free(h); + } + return FAIL; +} + +int tld_hash_resize(tld_hash *h) +{ + tld_hash_entry **new_table = NULL; + + int new_size = h->table_size; + static const int prime_sizes[] = { + 53, 97, 193, 389, 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, 196613, 393241, + 786433, 1572869, 3145739, 6291469, 12582917, + 25165843, 50331653, 100663319, 201326611, + 402653189, 805306457, 1610612741 + }; + static const int num_primes = sizeof(prime_sizes) / sizeof(prime_sizes[0]); + + for (int i = 0; i < num_primes; i++) { + if (prime_sizes[i] > h->table_size) { + new_size = prime_sizes[i]; + break; + } + } + if (new_size == h->table_size) { + // If no larger prime is found, double the size + new_size = h->table_size * 2 + 1; + } + + MMALLOC(new_table, sizeof(tld_hash_entry*) * new_size); + if (!new_table) return FAIL; + + // Initialize new table + for (int i = 0; i < new_size; i++) { + new_table[i] = NULL; + } + + // Rehash all existing entries + for (int i = 0; i < h->table_size; i++) { + tld_hash_entry *entry = h->table[i]; + while (entry) { + tld_hash_entry *next = entry->next; + unsigned int new_index = fnv1a_hash(TLD_STR(entry->key), new_size); + entry->next = new_table[new_index]; + new_table[new_index] = entry; + entry = next; + } + } + + // Replace old table with new table + MFREE(h->table); + h->table = new_table; + h->table_size = new_size; + h->n_rep++; // Optionally track number of resizes + return OK; +ERROR: + return FAIL; + +} + + +int tld_hash_print(tld_hash *h) +{ + if(h){ + for(int i = 0; i < h->table_size; i++){ + tld_hash_entry* entry = h->table[i]; + while(entry){ + fprintf(stdout,"%s: %s\n", TLD_STR(entry->key), TLD_STR(entry->value)); + entry = entry->next; + } + } + } + return OK; +} + +void tld_hash_free(tld_hash *h) +{ + if(h){ + for(int i = 0; i < h->table_size; i++){ + tld_hash_entry* entry = h->table[i]; + while(entry){ + tld_hash_entry* next = entry->next; + tld_hash_entry_free(entry); + entry = next; + } + } + + MFREE(h->table); + MFREE(h); + } +} + + +int tld_hash_entry_alloc(tld_hash_entry **entry) +{ + tld_hash_entry* e = NULL; + MMALLOC(e, sizeof(tld_hash_entry)); + e->key = NULL; + e->value = NULL; + RUN(tld_strbuf_alloc(&e->key, 64)); + RUN(tld_strbuf_alloc(&e->value, 64)); + e->next = NULL; + *entry = e; + return OK; +ERROR: + return FAIL; +} + +void tld_hash_entry_free(tld_hash_entry *n) +{ + if(n){ + tld_strbuf_free(n->key); + tld_strbuf_free(n->value); + MFREE(n); + } +} + +#undef LOAD_FACTOR_THRESHOLD diff --git a/src/hash/hash.h b/src/hash/hash.h new file mode 100644 index 0000000..d2a6a2c --- /dev/null +++ b/src/hash/hash.h @@ -0,0 +1,20 @@ +#ifndef HASH_H +#define HASH_H + +#include "../core/tld-core.h" + +typedef struct tld_hash tld_hash; + +tld_external int tld_hash_iter_init(tld_hash *h); +tld_external int tld_hash_iterator_next(tld_hash* h, char **key,char **value); + +tld_external int tld_hash_add(tld_hash **hash, char *key, char *val); +tld_external int tld_hash_get(tld_hash *h, char *key, char **val); +tld_external int tld_hash_rename(tld_hash *h, char *key, char* new_key); +tld_external int tld_hash_del(tld_hash *h, char *key); + +tld_external int tld_hash_alloc(tld_hash **hash, int size); +tld_external void tld_hash_free(tld_hash *h); +tld_external int tld_hash_print(tld_hash *h); + +#endif diff --git a/src/hash/hash_struct.h b/src/hash/hash_struct.h new file mode 100644 index 0000000..9e6bcb9 --- /dev/null +++ b/src/hash/hash_struct.h @@ -0,0 +1,31 @@ +#ifndef HASH_STRUCT_H +#define HASH_STRUCT_H +#include "../string/str.h" + +typedef struct tld_hash_entry { + tld_strbuf* key; + tld_strbuf* value; + struct tld_hash_entry* next; // For handling collisions using chaining +} tld_hash_entry; + +/* typedef struct memory_pool { */ +/* tld_hash_entry **entries; */ +/* int capacity; */ +/* int n; */ +/* /\* int next; *\/ */ +/* } memory_pool; */ + + +typedef struct tld_hash { + tld_hash_entry** table; // Array of pointers to entries + tld_hash_entry* cur_entry; + /* memory_pool* pool; */ + int cur_index; + int table_size; // Size of the hash table + int n; // Number of entries + int n_rep; + int status; +} tld_hash; + + +#endif diff --git a/src/template/template.c b/src/template/template.c index 25706f1..0d4cb4c 100644 --- a/src/template/template.c +++ b/src/template/template.c @@ -3,6 +3,7 @@ #include "../core/tld-core.h" #include "../alloc/tld-alloc.h" #include "../string/str.h" +#include "../hash/hash.h" #include #include @@ -15,205 +16,6 @@ #define MAX_CHILDREN 10 #define MAX_ERROR_MSG 256 -typedef struct tld_template_hash_entry { - tld_strbuf* key; - tld_strbuf* value; - struct tld_template_hash_entry* next; // For handling collisions using chaining -} tld_template_hash_entry; - -typedef struct tld_template_hash { - tld_template_hash_entry** table; // Array of pointers to entries - int table_size; // Size of the hash table - int n; // Number of entries - int n_rep; - int status; -} tld_template_hash; - -static unsigned int str_hash(char *str, int table_size); - -static unsigned int str_hash(char *str, int table_size) -{ - unsigned int hash = 5381; - int c; - while ((c = *str++)){ - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - return hash % table_size; -} - -int tld_template_add(tld_template_hash **hash, char *key, char *val) -{ - tld_template_hash* h = NULL; - if(*hash){ - h = *hash; - }else{ - RUN(tld_template_hash_alloc(&h, 1024)); - } - - ASSERT(h != NULL, "No Map"); - - unsigned int index = str_hash(key, h->table_size); - - // Check if key already exists - tld_template_hash_entry* current = h->table[index]; - while (current) { - if (strcmp(TLD_STR(current->key), key) == 0) { - // Key exists, replace the value - tld_strbuf_clear(current->value); - tld_append(current->value, val); - return OK; - } - current = current->next; - } - - // Key does not exist, create a new entry - tld_template_hash_entry* new_entry = NULL; - MMALLOC(new_entry, sizeof(tld_template_hash_entry)); - tld_strbuf_alloc(&new_entry->key, 16); - tld_strbuf_alloc(&new_entry->value, 16); - tld_append(new_entry->key, key); - tld_append(new_entry->value, val); - new_entry->next = h->table[index]; - h->table[index] = new_entry; - h->n++; - - *hash = h; - return OK; -ERROR: - return FAIL; -} - -int tld_template_get(tld_template_hash *h, char *key, char **val) -{ - ASSERT(h != NULL, "No Map!!"); - ASSERT(key != NULL, "No key"); - - unsigned int index = str_hash(key, h->table_size); - tld_template_hash_entry* entry = h->table[index]; - - while (entry) { - if (strcmp(TLD_STR(entry->key), key) == 0) { - *val = TLD_STR(entry->value); - return OK; - } - entry = entry->next; - } - - *val = NULL; // Key not found - return FAIL; -ERROR: - return FAIL; -} - -int tld_template_rename_var(tld_template_hash *h, char *key, char* new_key) { - ASSERT(h != NULL, "No Map!!"); - ASSERT(key != NULL, "No key"); - - unsigned int index = str_hash(key, h->table_size); - tld_template_hash_entry* entry = h->table[index]; - - while (entry) { - if (strcmp(TLD_STR(entry->key), key) == 0) { - tld_strbuf* tmp = NULL; - tld_strbuf_alloc(&tmp, 64); - // Remove the old key from the hash table - tld_strbuf_copy(tmp, entry->value); - - tld_template_remove(h, key); - // Add the new key with the existing replacement - tld_template_add(&h, new_key, TLD_STR(tmp)); - tld_strbuf_free(tmp); - return OK; - } - entry = entry->next; - } - - return FAIL; // Key not found -ERROR: - return FAIL; -} - -int tld_template_remove(tld_template_hash *h, char *key) -{ - ASSERT(h != NULL, "No Map!!"); - ASSERT(key != NULL, "No key"); - - unsigned int index = str_hash(key, h->table_size); - tld_template_hash_entry* entry = h->table[index]; - tld_template_hash_entry* prev = NULL; - - while(entry){ - if(strcmp(TLD_STR(entry->key), key) == 0){ - if(prev){ - prev->next = entry->next; - }else{ - h->table[index] = entry->next; - } - // Free the entry - tld_strbuf_free(entry->key); - tld_strbuf_free(entry->value); - MFREE(entry); - h->n--; - return OK; - } - prev = entry; - entry = entry->next; - } - - return FAIL; // Key not found -ERROR: - return FAIL; -} - -int tld_template_hash_alloc(tld_template_hash **hash, int size) -{ - tld_template_hash* h = NULL; - MMALLOC(h, sizeof(tld_template_hash)); - h->table_size = size; // Choose an appropriate size - // Initialize the map with a default size - MMALLOC(h->table, sizeof(tld_template_hash_entry*) * h->table_size); - h->n = 0; - *hash = h; - return OK; -ERROR: - if(h){ - tld_template_hash_free(h); - } - return FAIL; -} - -int tld_template_hash_print(tld_template_hash *h) -{ - if(h){ - for(int i = 0; i < h->table_size; i++){ - tld_template_hash_entry* entry = h->table[i]; - while(entry){ - fprintf(stdout,"%s: %s\n", TLD_STR(entry->key), TLD_STR(entry->value)); - entry = entry->next; - } - } - } - return OK; -} - -void tld_template_hash_free(tld_template_hash *h) -{ - if(h){ - for(int i = 0; i < h->table_size; i++){ - tld_template_hash_entry* entry = h->table[i]; - while(entry){ - tld_template_hash_entry* next = entry->next; - tld_strbuf_free(entry->key); - tld_strbuf_free(entry->value); - MFREE(entry); - entry = next; - } - } - MFREE(h->table); - MFREE(h); - } -} - typedef enum { NODE_ROOT, NODE_TEXT, @@ -242,6 +44,11 @@ typedef struct { char error[MAX_ERROR_MSG]; } Parser; +tld_internal int parse_template(Parser *parser, template_node **node); +tld_internal void process_tree(template_node *node, tld_hash *hash, FILE *output,tld_strbuf* out); + +tld_internal int get_vars(template_node *node, tld_hash* h); +tld_internal void print_tree(template_node *node, int depth); tld_internal int parse_text(Parser *parser, template_node **node); tld_internal int parse_tag(Parser *parser, template_node **node); @@ -255,6 +62,97 @@ tld_internal int template_node_create(template_node **node, template_node_type tld_internal int template_node_resize(template_node *node); tld_internal void template_node_free(template_node *node); +int tld_template_extract_var(tld_strbuf *txt, tld_hash **hash) +{ + tld_hash* h = NULL; + char* tmp = NULL; + template_node* root = NULL; + tld_strbuf* out = NULL; + + galloc(&tmp,txt->len+1); + + memcpy((void*)tmp, txt->str, txt->len); + tmp[txt->len] = '\0'; + Parser parser = {tmp, 0, ""}; + /* root = parse_template(&parser); */ + parse_template(&parser, &root); + if (parser.error[0] != '\0') { + ERROR_MSG("Error parsing template: %s",parser.error); + } + + + tld_hash_alloc(&h,1024); + + /* print_tree(root, 0); */ + + RUN(get_vars(root, h)); + + + /* RUN(tld_strbuf_alloc(&out, 256)); */ + /* process_tree(root, hash, stdout,out); */ + /* RUN(tld_strbuf_copy(txt, out)); */ + + template_node_free(root); + + tld_strbuf_free(out); + gfree(tmp); + + *hash = h; + return OK; +ERROR: + if(out){ + tld_strbuf_free(out); + } + if(root){ + template_node_free(root); + } + if(tmp){ + gfree(tmp); + } + return FAIL; +} + + +int tld_template_apply(tld_strbuf *txt, tld_hash *hash) +{ + char* tmp = NULL; + template_node* root = NULL; + tld_strbuf* out = NULL; + + galloc(&tmp,txt->len+1); + + memcpy((void*)tmp, txt->str, txt->len); + tmp[txt->len] = '\0'; + Parser parser = {tmp, 0, ""}; + /* root = parse_template(&parser); */ + parse_template(&parser, &root); + if (parser.error[0] != '\0') { + ERROR_MSG("Error parsing template: %s",parser.error); + } + /* print_tree(root, 0); */ + RUN(tld_strbuf_alloc(&out, 256)); + process_tree(root, hash, stdout,out); + RUN(tld_strbuf_copy(txt, out)); + + template_node_free(root); + + tld_strbuf_free(out); + gfree(tmp); + return OK; +ERROR: + if(out){ + tld_strbuf_free(out); + } + if(root){ + template_node_free(root); + } + if(tmp){ + gfree(tmp); + } + return FAIL; +} + + int parse_template(Parser *parser, template_node **node) { template_node *root = NULL; @@ -263,8 +161,6 @@ int parse_template(Parser *parser, template_node **node) int stack_top = 0; int stack_size = 10; MMALLOC(stack, sizeof(template_node*) * stack_size); - /* MMALLOC(n->children, sizeof(template_node*) * n->n_alloc); */ - /* int stack_top = 0; */ RUN(template_node_create(&root, NODE_ROOT, NULL)); current = root; @@ -410,12 +306,49 @@ int parse_tag(Parser *parser, template_node **node) } } -void print_tree(template_node *node, int depth) { - for (int i = 0; i < depth; i++) { +int get_vars(template_node *node, tld_hash* h) +{ + switch(node->type){ + case NODE_ROOT: + /* printf("ROOT\n"); */ + break; + case NODE_TEXT: + /* printf("TEXT: %s\n", TLD_STR(node->content)); */ + break; + case NODE_VARIABLE: + tld_hash_add(&h, TLD_STR(node->content),TLD_STR(node->content)); + /* printf("VARIABLE: %s\n", TLD_STR(node->content)); */ + break; + case NODE_IF: + /* printf("IF: %s\n", TLD_STR(node->content)); */ + tld_hash_add(&h, TLD_STR(node->content),TLD_STR(node->content)); + + break; + case NODE_ELSE: + /* printf("ELSE\n"); */ + break; + case NODE_ENDIF: + /* printf("ENDIF\n"); */ + break; + } + + for(int i = 0; i < node->n; i++){ + RUN(get_vars(node->children[i], h)); + /* print_tree(node->children[i], depth + 1); */ + } + return OK; +ERROR: + return FAIL; +} + + +void print_tree(template_node *node, int depth) +{ + for(int i = 0; i < depth; i++){ printf(" "); } - switch (node->type) { + switch(node->type){ case NODE_ROOT: printf("ROOT\n"); break; @@ -436,12 +369,12 @@ void print_tree(template_node *node, int depth) { break; } - for (int i = 0; i < node->n; i++) { + for(int i = 0; i < node->n; i++){ print_tree(node->children[i], depth + 1); } } -void process_tree(template_node *node, tld_template_hash *hash, FILE *output,tld_strbuf* out) +void process_tree(template_node *node, tld_hash *hash, FILE *output,tld_strbuf* out) { switch(node->type){ case NODE_ROOT: @@ -458,7 +391,7 @@ void process_tree(template_node *node, tld_template_hash *hash, FILE *output,tld case NODE_VARIABLE: { char* replacement = NULL; - tld_template_get(hash, TLD_STR(node->content), &replacement); + tld_hash_get(hash, TLD_STR(node->content), &replacement); /* char* replacement = find_in_map(hash, TLD_STR(node->content)); */ if(replacement){ tld_append(out, replacement); @@ -472,7 +405,7 @@ void process_tree(template_node *node, tld_template_hash *hash, FILE *output,tld { char* condition_value = NULL;//f ind_in_map(hash, TLD_STR(node->content)); - tld_template_get(hash, TLD_STR(node->content), &condition_value); + tld_hash_get(hash, TLD_STR(node->content), &condition_value); int condition_met = (condition_value != NULL && strcmp(condition_value, "") != 0); if(condition_met){ @@ -513,48 +446,6 @@ void process_tree(template_node *node, tld_template_hash *hash, FILE *output,tld } } -int tld_template_apply(tld_strbuf *txt, tld_template_hash *hash) -{ - char* tmp = NULL; - - template_node* root = NULL; - /* Node *root = NULL; */ - tld_strbuf* out = NULL; - - /* Parser parser = NULL; */ - galloc(&tmp,txt->len+1); - - memcpy((void*)tmp, txt->str, txt->len); - tmp[txt->len] = '\0'; - Parser parser = {tmp, 0, ""}; - /* root = parse_template(&parser); */ - parse_template(&parser, &root); - if (parser.error[0] != '\0') { - ERROR_MSG("Error parsing template: %s",parser.error); - } - /* print_tree(root, 0); */ - RUN(tld_strbuf_alloc(&out, 256)); - process_tree(root, hash, stdout,out); - RUN(tld_strbuf_copy(txt, out)); - - template_node_free(root); - /* free_node(root); */ - - tld_strbuf_free(out); - gfree(tmp); - return OK; -ERROR: - if(out){ - tld_strbuf_free(out); - } - if(root){ - template_node_free(root); - } - if(tmp){ - gfree(tmp); - } - return FAIL; -} #undef TEMPLATE_OK #undef TEMPLATE_MISMATCH_DELIM @@ -639,120 +530,3 @@ void skip_whitespace(Parser *parser) parser_next(parser); } } - - -/* int tld_template_init(tld_template_map **map, char **id, char **rep, int n) */ -/* { */ -/* tld_template_map* m = NULL; */ - -/* RUN(tld_template_allocate(&m, n)); */ -/* /\* MMALLOC(m, sizeof(tld_template_map)); *\/ */ -/* m->n = 0; */ -/* for(int i = 0; i < n;i++){ */ -/* tld_append(m->identifier[i], id[i]); */ -/* tld_append(m->replacement[i], rep[i]); */ -/* m->n++; */ -/* } */ - -/* *map = m; */ -/* return OK; */ -/* ERROR: */ -/* tld_template_free(m); */ -/* return FAIL; */ -/* } */ - -/* int tld_template_allocate(tld_template_map **map, int n) */ -/* { */ -/* tld_template_map* m = NULL; */ -/* MMALLOC(m, sizeof(tld_template_map)); */ -/* m->identifier = NULL; */ -/* m->replacement = NULL; */ -/* m->delim_start = NULL; */ -/* m->delim_end = NULL; */ -/* m->if_delim_start = NULL; */ -/* m->if_delim_end = NULL; */ -/* m->n = 0; */ -/* m->n_alloc = n; */ -/* m->status = 0; */ -/* m->n_rep = 0; */ -/* MMALLOC(m->identifier, sizeof(tld_strbuf*) * m->n_alloc); */ -/* MMALLOC(m->replacement, sizeof(tld_strbuf*) * m->n_alloc); */ - - -/* for(int i = 0; i < m->n_alloc;i++){ */ -/* m->identifier[i] = NULL; */ -/* m->replacement[i] = NULL; */ -/* tld_strbuf_alloc(&m->identifier[i], 16); */ -/* tld_strbuf_alloc(&m->replacement[i], 16); */ -/* } */ -/* /\* Default delim = {{ and }} *\/ */ -/* tld_strbuf_alloc(&m->delim_start, 3); */ -/* tld_strbuf_alloc(&m->delim_end, 3); */ - -/* tld_append(m->delim_start, "{{"); */ -/* tld_append(m->delim_end,"}}"); */ - -/* tld_strbuf_alloc(&m->if_delim_start, 8); */ -/* tld_strbuf_alloc(&m->if_delim_end, 16); */ -/* tld_append(m->if_delim_start, "{{IF "); */ -/* tld_append(m->if_delim_end,"{{ENDIF}}"); */ - -/* *map = m; */ - -/* return OK; */ -/* ERROR: */ -/* tld_template_free(m); */ -/* return FAIL; */ -/* } */ - -/* int tld_template_resize(tld_template_map *m) */ -/* { */ -/* int old_size = m->n_alloc; */ -/* m->n_alloc = m->n_alloc + m->n_alloc /2; */ - - -/* MREALLOC(m->identifier, sizeof(tld_strbuf*) * m->n_alloc); */ -/* MREALLOC(m->replacement, sizeof(tld_strbuf*) * m->n_alloc); */ - - -/* for(int i = old_size; i < m->n_alloc;i++){ */ -/* m->identifier[i] = NULL; */ -/* m->replacement[i] = NULL; */ -/* tld_strbuf_alloc(&m->identifier[i], 16); */ -/* tld_strbuf_alloc(&m->replacement[i], 16); */ -/* } */ - -/* return OK; */ -/* ERROR: */ -/* tld_template_free(m); */ -/* return FAIL; */ -/* } */ - - -/* void tld_template_free(tld_template_map *m) */ -/* { */ -/* if(m){ */ -/* if(m->n_alloc){ */ -/* for(int i = 0; i < m->n_alloc;i++){ */ -/* tld_strbuf_free(m->identifier[i]); */ -/* tld_strbuf_free(m->replacement[i]); */ - -/* } */ -/* MFREE(m->identifier); */ -/* MFREE(m->replacement); */ -/* } */ -/* if(m->delim_start){ */ -/* tld_strbuf_free(m->delim_start); */ -/* } */ -/* if(m->delim_end){ */ -/* tld_strbuf_free(m->delim_end); */ -/* } */ -/* if(m->if_delim_start){ */ -/* tld_strbuf_free(m->if_delim_start); */ -/* } */ -/* if(m->if_delim_end){ */ -/* tld_strbuf_free(m->if_delim_end); */ -/* } */ -/* MFREE(m); */ -/* } */ -/* } */ diff --git a/src/template/template.h b/src/template/template.h index c8d35e1..719c8ca 100644 --- a/src/template/template.h +++ b/src/template/template.h @@ -4,18 +4,19 @@ #include "../core/tld-core.h" #include "../string/str.h" +typedef struct tld_hash tld_hash; -typedef struct tld_template_hash tld_template_hash; +/* typedef struct tld_template_hash tld_template_hash; */ -tld_external int tld_template_add(tld_template_hash **hash, char *key, char *val); -tld_external int tld_template_get(tld_template_hash *h, char *key, char **val); -tld_external int tld_template_rename_var(tld_template_hash *h, char *key, char* new_key); -tld_external int tld_template_remove(tld_template_hash *h, char *key); +/* tld_external int tld_template_add(tld_template_hash **hash, char *key, char *val); */ +/* tld_external int tld_template_get(tld_template_hash *h, char *key, char **val); */ +/* tld_external int tld_template_rename_var(tld_template_hash *h, char *key, char* new_key); */ +/* tld_external int tld_template_remove(tld_template_hash *h, char *key); */ -tld_external int tld_template_hash_alloc(tld_template_hash **hash, int size); -tld_external void tld_template_hash_free(tld_template_hash *h); -tld_external int tld_template_hash_print(tld_template_hash *h); - -tld_external int tld_template_apply(tld_strbuf *txt, tld_template_hash *map); +/* tld_external int tld_template_hash_alloc(tld_template_hash **hash, int size); */ +/* tld_external void tld_template_hash_free(tld_template_hash *h); */ +/* tld_external int tld_template_hash_print(tld_template_hash *h); */ +tld_external int tld_template_apply(tld_strbuf *txt, tld_hash *map); +tld_external int tld_template_extract_var(tld_strbuf *txt, tld_hash **hash); #endif diff --git a/src/tld.c b/src/tld.c index e70757b..c3c7181 100644 --- a/src/tld.c +++ b/src/tld.c @@ -7,6 +7,8 @@ #include "rng/tld-rng.c" #include "kdtree/tld-kdtree.c" +#include "hash/hash.c" + #ifndef BOOT #include "seq/tld-seqbuffer.c" #include "seq/tld-seqio.c" diff --git a/src/tld.h b/src/tld.h index 2b72ae2..6c7982d 100644 --- a/src/tld.h +++ b/src/tld.h @@ -17,7 +17,7 @@ #include "rng/tld-rng.h" #include "kdtree/tld-kdtree.h" - +#include "hash/hash.h" #ifndef BOOT #include "seq/tld-seq.h" #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2ba6d44..e7124e5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,7 +28,7 @@ function(add_custom_test TEST_NAME) PRIVATE tld-dev ) - message(STATUS "Running test: ${TEST_NAME} with command: ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${TEST_NAME}") + # message(STATUS "Running test: ${TEST_NAME} with command: ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${TEST_NAME}") add_test( NAME ${TEST_NAME} COMMAND ${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS} ${CMAKE_BINARY_DIR}/tests/${TEST_NAME} @@ -45,11 +45,14 @@ if(Threads_FOUND) target_link_libraries(unit-tpool PRIVATE tld-dev Threads::Threads) endif(Threads_FOUND) +add_custom_test(unit-template utests/unit_template.c) +add_custom_test(unit-hash utests/unit_hash.c) + add_custom_test(unit-core utests/unit_core.c) add_custom_test(unit-bit utests/unit_bitfield.c) add_custom_test(unit-kdtree utests/unit_kdtree.c) add_custom_test(unit-json utests/unit_json.c) -add_custom_test(unit-template utests/unit_template.c) + add_custom_test(unit-qnorm utests/unit_qnorm.c) add_custom_test(unit-auc utests/unit_auc.c) add_custom_test(unit-shuffle utests/unit_shuffle.c) diff --git a/tests/utests/unit_hash.c b/tests/utests/unit_hash.c new file mode 100644 index 0000000..1063f32 --- /dev/null +++ b/tests/utests/unit_hash.c @@ -0,0 +1,307 @@ +// test_hash.c +#include "tld.h" +#include "hash/hash_struct.h" + +// Macro for asserting test conditions +#define ASSERT_TEST(cond, msg) do { \ + if (!(cond)) { \ + fprintf(stderr, "TEST FAILED: %s\n", msg); \ + return -1; \ + } \ + } while(0) + +// Function prototypes +int test_initialization(void); +int test_add_and_get(void); +int test_overwrite(void); +int test_deletion(void); +int test_collision_handling(void); +int test_dynamic_resizing(void); +/* int test_iteration(); */ +int test_stress(void); + +// Main function to run all tests +int main(void) { + int failed = 0; + + printf("Running Hash Table Unit Tests...\n\n"); + + if (test_initialization() == 0) { + printf("Test Initialization: PASSED\n"); + } else { + printf("Test Initialization: FAILED\n"); + failed++; + } + + if (test_add_and_get() == 0) { + printf("Test Add and Get: PASSED\n"); + } else { + printf("Test Add and Get: FAILED\n"); + failed++; + } + + if (test_overwrite() == 0) { + printf("Test Overwrite: PASSED\n"); + } else { + printf("Test Overwrite: FAILED\n"); + failed++; + } + + if (test_deletion() == 0) { + printf("Test Deletion: PASSED\n"); + } else { + printf("Test Deletion: FAILED\n"); + failed++; + } + + if (test_collision_handling() == 0) { + printf("Test Collision Handling: PASSED\n"); + } else { + printf("Test Collision Handling: FAILED\n"); + failed++; + } + + if (test_dynamic_resizing() == 0) { + printf("Test Dynamic Resizing: PASSED\n"); + } else { + printf("Test Dynamic Resizing: FAILED\n"); + failed++; + } + + /* if (test_iteration() == 0) { */ + /* printf("Test Iteration: PASSED\n"); */ + /* } else { */ + /* printf("Test Iteration: FAILED\n"); */ + /* failed++; */ + /* } */ + + if (test_stress() == 0) { + printf("Test Stress: PASSED\n"); + } else { + printf("Test Stress: FAILED\n"); + failed++; + } + + printf("\nUnit Tests Completed: %d Passed, %d Failed\n", 8 - failed, failed); + return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +// Test 1: Initialization +int test_initialization(void) +{ + tld_hash *hash = NULL; + int initial_size = 52; // Starting with a prime number + + if (tld_hash_alloc(&hash, initial_size) != OK) { + fprintf(stderr, "Error: Hash initialization failed!\n"); + return -1; + } + /* LOG_MSG("Hash table size: %d", hash->table_size); */ + ASSERT_TEST(hash != NULL, "Hash should not be NULL after initialization."); + ASSERT_TEST(hash->table_size == 53, "Hash table size mismatch."); + ASSERT_TEST(hash->n == 0, "Hash should be empty after initialization."); + + tld_hash_free(hash); + return 0; +} + +// Test 2: Add and Get Operations +int test_add_and_get(void) +{ + tld_hash *hash = NULL; + tld_hash_alloc(&hash, 53); + + // Adding key-value pairs + ASSERT_TEST(tld_hash_add(&hash, "key1", "value1") == OK, "Failed to add key1."); + ASSERT_TEST(tld_hash_add(&hash, "key2", "value2") == OK, "Failed to add key2."); + ASSERT_TEST(tld_hash_add(&hash, "key3", "value3") == OK, "Failed to add key3."); + + // Retrieving and verifying values + char *value = NULL; + ASSERT_TEST(tld_hash_get(hash, "key1", &value) == OK && strcmp(value, "value1") == 0, "Incorrect value for key1."); + ASSERT_TEST(tld_hash_get(hash, "key2", &value) == OK && strcmp(value, "value2") == 0, "Incorrect value for key2."); + ASSERT_TEST(tld_hash_get(hash, "key3", &value) == OK && strcmp(value, "value3") == 0, "Incorrect value for key3."); + + tld_hash_free(hash); + return 0; +} + +// Test 3: Overwrite Existing Key +int test_overwrite() { + tld_hash *hash = NULL; + tld_hash_alloc(&hash, 53); + + // Add a key + ASSERT_TEST(tld_hash_add(&hash, "key1", "value1") == OK, "Failed to add key1."); + + // Overwrite the key + ASSERT_TEST(tld_hash_add(&hash, "key1", "new_value1") == OK, "Failed to overwrite key1."); + + // Verify the new value + char *value = NULL; + ASSERT_TEST(tld_hash_get(hash, "key1", &value) == OK && strcmp(value, "new_value1") == 0, "Overwrite failed for key1."); + + tld_hash_free(hash); + return 0; +} + +// Test 4: Deletion of Keys +int test_deletion() { + tld_hash *hash = NULL; + tld_hash_alloc(&hash, 53); + + // Add keys + tld_hash_add(&hash, "key1", "value1"); + tld_hash_add(&hash, "key2", "value2"); + + // Delete key2 + ASSERT_TEST(tld_hash_del(hash, "key2") == OK, "Failed to delete key2."); + + // Verify deletion + char *value = NULL; + ASSERT_TEST(tld_hash_get(hash, "key2", &value) == FAIL, "key2 should have been deleted."); + + // Ensure key1 still exists + ASSERT_TEST(tld_hash_get(hash, "key1", &value) == OK && strcmp(value, "value1") == 0, "key1 should still exist."); + + tld_hash_free(hash); + return 0; +} + +// Test 5: Collision Handling +int test_collision_handling() { + tld_hash *hash = NULL; + // Use a small table size to force collisions + tld_hash_alloc(&hash, 5); + + // Add multiple keys that likely collide + for (int i = 0; i < 10; i++) { + char key[20]; + char val[20]; + snprintf(key, sizeof(key), "key%d", i); + snprintf(val, sizeof(val), "value%d", i); + ASSERT_TEST(tld_hash_add(&hash, key, val) == OK, "Failed to add colliding key."); + } + + // Verify all keys + char *value = NULL; + for (int i = 0; i < 10; i++) { + char key[20]; + snprintf(key, sizeof(key), "key%d", i); + ASSERT_TEST(tld_hash_get(hash, key, &value) == OK, "Failed to retrieve colliding key."); + char expected_val[20]; + snprintf(expected_val, sizeof(expected_val), "value%d", i); + ASSERT_TEST(strcmp(value, expected_val) == 0, "Incorrect value for colliding key."); + } + + tld_hash_free(hash); + return 0; +} + +// Test 6: Dynamic Resizing +int test_dynamic_resizing(void) +{ + tld_hash *hash = NULL; + // Start with a small table to trigger resizing + tld_hash_alloc(&hash, 52); + + // Define load factor threshold (e.g., 0.75) + // Assuming the hash implementation resizes when load factor exceeds 0.75 + + int initial_size = hash->table_size; + + // Add entries to exceed the load factor + int entries_to_add = (int)(initial_size * 0.75) + 10; // This should trigger a resize + for (int i = 0; i < entries_to_add; i++) { + char key[20]; + char val[20]; + snprintf(key, sizeof(key), "resize_key%d", i); + snprintf(val, sizeof(val), "resize_val%d", i); + ASSERT_TEST(tld_hash_add(&hash, key, val) == OK, "Failed to add key for resizing."); + } + /* LOG_MSG("Table size after resizing: %d before: %d", hash->table_size, initial_size); */ + // Check if the table has resized + ASSERT_TEST(hash->table_size > initial_size, "Hash table did not resize as expected."); + + // Verify all entries + char *value = NULL; + for (int i = 0; i < entries_to_add; i++) { + char key[20]; + snprintf(key, sizeof(key), "resize_key%d", i); + ASSERT_TEST(tld_hash_get(hash, key, &value) == OK, "Failed to retrieve key after resizing."); + char expected_val[20]; + snprintf(expected_val, sizeof(expected_val), "resize_val%d", i); + ASSERT_TEST(strcmp(value, expected_val) == 0, "Incorrect value after resizing."); + } + + tld_hash_free(hash); + return 0; +} + +// Test 7: Iteration Over Entries +/* int test_iteration() { */ +/* tld_hash *hash = NULL; */ +/* tld_hash_alloc(&hash, 53); */ + +/* // Add multiple entries */ +/* tld_hash_add(&hash, "iter_key1", "iter_val1"); */ +/* tld_hash_add(&hash, "iter_key2", "iter_val2"); */ +/* tld_hash_add(&hash, "iter_key3", "iter_val3"); */ + +/* // Initialize iterator */ +/* tld_hash_iterator iter; */ +/* ASSERT_TEST(tld_hash_iterator_init(hash, &iter) == OK, "Failed to initialize iterator."); */ + +/* // Collect entries */ +/* int count = 0; */ +/* char *key, *value; */ +/* while (tld_hash_iterator_next(&iter, &key, &value) == OK) { */ +/* if (strcmp(key, "iter_key1") == 0) { */ +/* ASSERT_TEST(strcmp(value, "iter_val1") == 0, "Incorrect value for iter_key1."); */ +/* } else if (strcmp(key, "iter_key2") == 0) { */ +/* ASSERT_TEST(strcmp(value, "iter_val2") == 0, "Incorrect value for iter_key2."); */ +/* } else if (strcmp(key, "iter_key3") == 0) { */ +/* ASSERT_TEST(strcmp(value, "iter_val3") == 0, "Incorrect value for iter_key3."); */ +/* } else { */ +/* fprintf(stderr, "Unexpected key found during iteration: %s\n", key); */ +/* return -1; */ +/* } */ +/* count++; */ +/* } */ + +/* ASSERT_TEST(count == 3, "Iterator did not traverse all entries."); */ + +/* tld_hash_free(hash); */ +/* return 0; */ +/* } */ + +// Test 8: Stress Test with Large Data +int test_stress() { + tld_hash *hash = NULL; + tld_hash_alloc(&hash, 53); + + int total_entries = 10000; + char key[50], val[50]; + + // Add a large number of entries + for (int i = 0; i < total_entries; i++) { + snprintf(key, sizeof(key), "stress_key_%d", i); + snprintf(val, sizeof(val), "stress_val_%d", i); + ASSERT_TEST(tld_hash_add(&hash, key, val) == OK, "Failed to add key during stress test."); + } + + // Verify a subset of entries + for (int i = 0; i < total_entries; i += 1000) { // Check every 1000th entry + snprintf(key, sizeof(key), "stress_key_%d", i); + snprintf(val, sizeof(val), "stress_val_%d", i); + char *retrieved_val = NULL; + ASSERT_TEST(tld_hash_get(hash, key, &retrieved_val) == OK, "Failed to retrieve key during stress test."); + ASSERT_TEST(strcmp(retrieved_val, val) == 0, "Incorrect value during stress test."); + } + + // Optionally, print the total number of entries + printf("Total entries after stress test: %d\n", hash->n); + + tld_hash_free(hash); + return 0; +} diff --git a/tests/utests/unit_template.c b/tests/utests/unit_template.c index 71783f0..bf4d604 100644 --- a/tests/utests/unit_template.c +++ b/tests/utests/unit_template.c @@ -1,184 +1,114 @@ #include "tld.h" - int test_new_template(void); -int test_hash_operations(void); - +int test_get_vars_template(void); int main(void) { - test_new_template(); - - test_hash_operations(); - /* LOG_MSG("Success"); */ + RUN(test_new_template()); + RUN(test_get_vars_template()); return EXIT_SUCCESS; ERROR: /* LOG_MSG("FAIL"); */ return EXIT_FAILURE; } -int test_hash_operations(void) { - tld_template_hash *hash = NULL; +int test_get_vars_template(void) +{ + char* test_templates[] = { + // Basic template with variable substitution + "Hello, {{name}}!", + // IF block without a variable + "{{IF age}}You are 12 years old.{{ENDIF}}", - // Step 1: Initialize the hash - printf("Initializing hash...\n"); - if (tld_template_hash_alloc(&hash, 1024) != 0) { - fprintf(stderr, "Error: Hash initialization failed!\n"); - return -1; - } + // IF block with a variable + "{{IF age}}You are {{age}} years old.{{ENDIF}}", - // Step 2: Add key-value pairs - printf("Adding key-value pairs...\n"); - tld_template_add(&hash, "key1", "value1"); - tld_template_add(&hash, "key2", "value2"); - tld_template_add(&hash, "key3", "value3"); - - // Step 3: Retrieve values and verify correctness - printf("Retrieving values...\n"); - char *value; - if (tld_template_get(hash, "key1", &value) == 0 && strcmp(value, "value1") == 0) { - printf("key1: %s\n", value); // Expected: value1 - } else { - printf("Error: key1 retrieval failed\n"); - } + // IF-ELSE block with variable substitution + "{{IF age}}You are {{age}} years old.{{ELSE}}Your age is unknown.{{ENDIF}}", - if (tld_template_get(hash, "key2", &value) == 0 && strcmp(value, "value2") == 0) { - printf("key2: %s\n", value); // Expected: value2 - } else { - printf("Error: key2 retrieval failed\n"); - } + // Nested IF statements with variable substitution + "{{IF is_student}}You are a student.{{IF has_major}}Your major is {{major}}.{{ENDIF}}{{ENDIF}}", - // Step 4: Test overwriting a key - printf("Overwriting key1...\n"); - tld_template_add(&hash, "key1", "new_value1"); - tld_template_get(hash, "key1", &value); - printf("key1 (overwritten): %s\n", value); // Expected: new_value1 - - // Step 5: Test retrieving non-existent key - printf("Retrieving non-existent key...\n"); - if (tld_template_get(hash, "nonexistent", &value) == -1) { - printf("nonexistent key not found\n"); - } else { - printf("Error: nonexistent key found: %s\n", value); - } + // Empty variables + "Hello, {{empty_var}}!", - // Step 6: Test deleting a key - printf("Deleting key2...\n"); - tld_template_remove(hash, "key2"); - if (tld_template_get(hash, "key2", &value) == -1) { - printf("key2 deleted successfully\n"); - } else { - printf("Error: key2 still exists\n"); - } + // Unused variables (variables in the map but not in the template) + "Hello, {{name}}! Today is {{day}}.", - // Step 7: Test hash collisions by adding many keys with similar prefixes - printf("Testing hash collisions...\n"); - char key[20], val[20]; - for (int i = 0; i < 1000; i++) { - snprintf(key, 20,"collide_key%d", i); - snprintf(val,20, "value%d", i); - tld_template_add(&hash, key, val); - } + // IF block with no ELSE, and variable doesn't exist + "{{IF is_employed}}You are employed.{{ENDIF}}", - // Verify a few random keys - printf("Verifying collision keys...\n"); - for (int i = 0; i < 1000; i += 100) { - snprintf(key, 20,"collide_key%d", i); - tld_template_get(hash, key, &value); - printf("%s: %s\n", key, value); // Expected: valuei - } + // Nested IF-ELSE blocks with conditionally missing variables + "{{IF is_student}}Welcome, student! {{IF major}}Your major is {{major}}.{{ELSE}}Major unknown.{{ENDIF}}{{ELSE}}Welcome, guest!{{ENDIF}}", - // Step 8: Test hash size limit and expansion - printf("Testing hash size limit and expansion...\n"); - for (int i = 1000; i < 1100; i++) { - snprintf(key,20, "extra_key%d", i); - snprintf(val,20, "value%d", i); - tld_template_add(&hash, key, val); - } + // Multiple variables in sequence + "{{name}} is {{age}} years old and studying {{major}}.", - // Verify new keys - for (int i = 1000; i < 1100; i++) { - snprintf(key, 20,"extra_key%d", i); - tld_template_get(hash, key, &value); - printf("%s: %s\n", key, value); // Expected: valuei - } + // IF block with multiple variables + "{{IF age}}Hello {{name}}, you are {{age}} years old.{{ENDIF}}", - // Step 9: Stress test with large amounts of data - printf("Stress test with large amount of data...\n"); - for (int i = 1100; i < 2100; i++) { - snprintf(key, 20,"large_key%d", i); - snprintf(val, 20,"value%d", i); - tld_template_add(&hash, key, val); - } + // ELSE without IF (should trigger an error in a well-formed parser) + "{{ELSE}}This should not appear.{{ENDIF}}", + + // Mismatched IF and ENDIF (should trigger an error) + "{{IF name}}Hello {{name}}.", + + // Properly nested IF blocks + "{{IF name}}Hello {{name}}! {{IF age}}You are {{age}} years old.{{ENDIF}}{{ENDIF}}", + + // Mixed variables and plain text + "Your name is {{name}}. You are {{age}}. {{IF is_student}}You are a student.{{ENDIF}} Enjoy your day.", + + // No variables, just plain text + "This is a simple template with no variables.", + + // Edge case: Variables inside conditionals, but without valid map entries + "{{IF missing_var}}This variable is missing: {{missing_var}}{{ELSE}}No variable found.{{ENDIF}}", + + // Multiple conditional branches + "{{IF age}}Age: {{age}}{{ELSE}}No age provided.{{ENDIF}} {{IF name}}Name: {{name}}{{ELSE}}No name provided.{{ENDIF}}", + + // Long template with multiple variables and conditionals + "Hello {{name}}! {{IF age}}You are {{age}} years old.{{ELSE}}Your age is unknown.{{ENDIF}} {{IF is_student}}You are a student studying {{major}}.{{ENDIF}}" + }; + + // Size of the array + int num_test_templates = sizeof(test_templates) / sizeof(test_templates[0]); + + tld_hash* h = NULL; + tld_strbuf* template = NULL; + tld_strbuf_alloc(&template, 64); + + /* tld_hash_init(&map, identifiers, replacements, 4); */ + /* num_test_templates = 2; */ + for (int i = 0; i < num_test_templates; i++) { + tld_strbuf_clear(template); + tld_append(template, test_templates[i]); + /* const char *template = test_templates[i]; */ + LOG_MSG("--------------------------------------"); + LOG_MSG("%s", TLD_STR(template)); + if(tld_template_extract_var(template, &h) == OK){ + RUN(tld_hash_iter_init(h)); + char* key = NULL; + char* value = NULL; + while(tld_hash_iterator_next(h, &key, &value) == OK){ + fprintf(stdout, " Key: %s\nValue: %s\n", key, value); + /* LOG_MSG("Key: %s Value: %s", key, value); */ + } + tld_hash_free(h); + + h = NULL; + } - // Verify a few random keys - for (int i = 1100; i < 2100; i += 100) { - snprintf(key, 20, "large_key%d", i); - tld_template_get(hash, key, &value); - printf("%s: %s\n", key, value); // Expected: valuei } - tld_template_hash_print(hash); - // Step 10: Clean up - printf("Freeing hash...\n"); - tld_template_hash_free(hash); + tld_strbuf_free(template); - return 0; -} + return OK; +ERROR: + return FAIL; -/* int test_hash_operations(void) */ -/* { */ -/* tld_template_hash *hash = NULL; */ - -/* // Step 1: Initialize the hash */ -/* printf("Initializing hash...\n"); */ -/* if (tld_template_hash_alloc(&hash,1024) != 0) { */ -/* ERROR_MSG("Hash initialization failed!"); */ -/* } */ - -/* // Step 2: Add key-value pairs */ -/* printf("Adding key-value pairs...\n"); */ -/* tld_template_add(&hash, "key1", "value1"); */ -/* tld_template_add(&hash, "key2", "value2"); */ -/* tld_template_add(&hash, "key3", "value3"); */ - -/* // Step 3: Retrieve values */ -/* printf("Retrieving values...\n"); */ -/* char *value; */ -/* tld_template_get(hash, "key1", &value); */ -/* printf("key1: %s\n", value); // Expected: value1 */ -/* tld_template_get(hash, "key2", &value); */ -/* printf("key2: %s\n", value); // Expected: value2 */ - -/* // Step 4: Test overwriting a key */ -/* printf("Overwriting key1...\n"); */ -/* tld_template_add(&hash, "key1", "new_value1"); */ -/* tld_template_add(&hash, "key1", value); */ -/* printf("key1 (overwritten): %s\n", value); // Expected: new_value1 */ - -/* // Step 5: Test retrieving non-existent key */ -/* printf("Retrieving non-existent key...\n"); */ -/* if (tld_template_get(hash, "nonexistent", &value) == -1) { */ -/* printf("nonexistent key not found\n"); */ -/* } else { */ -/* printf("nonexistent key found: %s\n", value); // Should not happen */ -/* } */ - -/* // Step 6: Delete a key */ -/* printf("Deleting key2...\n"); */ -/* tld_template_remove(hash, "key2"); */ -/* if (tld_template_get(hash, "key2", &value) == -1) { */ -/* printf("key2 deleted successfully\n"); */ -/* } else { */ -/* printf("Error: key2 still exists\n"); */ -/* } */ - -/* // Step 7: Clean up */ -/* printf("Freeing hash...\n"); */ -/* tld_template_hash_free(hash); */ -/* return OK; */ -/* ERROR: */ -/* return FAIL; */ -/* } */ +} int test_new_template(void) { @@ -244,15 +174,15 @@ int test_new_template(void) // Size of the array int num_test_templates = sizeof(test_templates) / sizeof(test_templates[0]); - tld_template_hash* h = NULL; + tld_hash* h = NULL; tld_strbuf* template = NULL; tld_strbuf_alloc(&template, 64); - tld_template_add(&h, "name", "Alica"); - tld_template_add(&h, "age", "101"); - tld_template_add(&h, "is_student", "yes"); - tld_template_add(&h, "major", "Computer Science"); + tld_hash_add(&h, "name", "Alica"); + tld_hash_add(&h, "age", "101"); + tld_hash_add(&h, "is_student", "yes"); + tld_hash_add(&h, "major", "Computer Science"); - /* tld_template_init(&map, identifiers, replacements, 4); */ + /* tld_hash_init(&map, identifiers, replacements, 4); */ /* num_test_templates = 2; */ for (int i = 0; i < num_test_templates; i++) { tld_strbuf_clear(template); @@ -264,126 +194,10 @@ int test_new_template(void) LOG_MSG("%s", TLD_STR(template)); } tld_strbuf_free(template); - tld_template_hash_free(h); + tld_hash_free(h); return OK; ERROR: return FAIL; } -/* int test_default(tld_template_map *map) */ -/* { */ - -/* char txt[] = "AAAA {{AA}} BBBB {{BB}} CCCC"; */ -/* tld_strbuf* template = NULL; */ - -/* tld_strbuf_alloc(&template, 64); */ -/* tld_append(template, txt); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* tld_template_apply(template, map); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* if(tld_template_chk(map, 1) != OK){ */ -/* ERROR_MSG("default replacement test failed!"); */ -/* } */ -/* tld_strbuf_free(template); */ -/* return OK; */ -/* ERROR: */ -/* if(template){ */ -/* tld_strbuf_free(template); */ -/* } */ -/* return FAIL; */ -/* } */ - -/* int test_mismatch (tld_template_map *map) */ -/* { */ - -/* char txt[] = "AAAA {{AA} BBBB {{BB}} CCCC"; */ -/* tld_strbuf* template = NULL; */ - -/* tld_strbuf_alloc(&template, 64); */ -/* tld_append(template, txt); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* tld_template_apply(template, map); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* if(tld_template_chk(map, 1) != OK){ */ -/* ERROR_MSG("mismatch test failed!"); */ -/* } */ -/* tld_strbuf_free(template); */ -/* return OK; */ -/* ERROR: */ -/* if(template){ */ -/* tld_strbuf_free(template); */ -/* } */ -/* return FAIL; */ -/* } */ - -/* int test_mismatch2 (tld_template_map *map) */ -/* { */ - -/* char txt[] = "AAAA {{AA}} BBBB {BB}} CCCC"; */ -/* tld_strbuf* template = NULL; */ - -/* tld_strbuf_alloc(&template, 64); */ -/* tld_append(template, txt); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* tld_template_apply(template, map); */ -/* LOG_MSG("%s", TLD_STR(template)); */ -/* if(tld_template_chk(map, 1) != OK){ */ -/* ERROR_MSG("mismatch test failed!"); */ -/* } */ -/* tld_strbuf_free(template); */ -/* return OK; */ -/* ERROR: */ -/* if(template){ */ -/* tld_strbuf_free(template); */ -/* } */ -/* return FAIL; */ -/* } */ - -/* int test_norep (tld_template_map *map) */ -/* { */ - -/* char txt[] = "AAAA BBBB CCCC"; */ -/* tld_strbuf* template = NULL; */ - -/* tld_strbuf_alloc(&template, 64); */ -/* tld_append(template, txt); */ -/* LOG_MSG("%s - before", TLD_STR(template)); */ -/* tld_template_apply(template, map); */ -/* LOG_MSG("%s - after", TLD_STR(template)); */ -/* if(tld_template_chk(map, 1) != OK){ */ -/* ERROR_MSG("mismatch test failed!"); */ -/* } */ -/* tld_strbuf_free(template); */ -/* return OK; */ -/* ERROR: */ -/* if(template){ */ -/* tld_strbuf_free(template); */ -/* } */ -/* return FAIL; */ -/* } */ - - -/* int test_ifrep (tld_template_map *map) */ -/* { */ - -/* char txt[] = "AAAA {{AA}} BBBB {{IF BB}} CCCC {{BB}} {{ENDIF}}"; */ - -/* tld_strbuf* template = NULL; */ - -/* tld_strbuf_alloc(&template, 64); */ -/* tld_append(template, txt); */ -/* LOG_MSG("%s - before", TLD_STR(template)); */ -/* tld_template_apply(template, map); */ -/* LOG_MSG("%s - after", TLD_STR(template)); */ -/* if(tld_template_chk(map, 1) != OK){ */ -/* ERROR_MSG("mismatch test failed!"); */ -/* } */ -/* tld_strbuf_free(template); */ -/* return OK; */ -/* ERROR: */ -/* if(template){ */ -/* tld_strbuf_free(template); */ -/* } */ -/* return FAIL; */ -/* } */