# HG changeset patch # User June Park # Date 1767023407 28800 # Node ID 4532ce6d9eb8fbc9fbd5e436b9da9cfeb9b3da64 # Parent 75de5903355c8bec826fa35ae8a96bfa4b291396 [Seobeo] Added router to the server logic. Few dowa string manipulation logics. diff -r 75de5903355c -r 4532ce6d9eb8 .claude/settings.local.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.claude/settings.local.json Mon Dec 29 07:50:07 2025 -0800 @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(ls:*)", + "Bash(hg status:*)" + ] + } +} diff -r 75de5903355c -r 4532ce6d9eb8 cutelient/BUILD --- a/cutelient/BUILD Sun Dec 28 20:34:22 2025 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -load("@rules_cc//cc:cc_binary.bzl", "cc_binary") - -cc_binary( - name = "cutelient", - srcs = ["main.c"], - deps = ["//seobeo:seobeo"], -) diff -r 75de5903355c -r 4532ce6d9eb8 cutelient/main.c --- a/cutelient/main.c Sun Dec 28 20:34:22 2025 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -#include -#include "seobeo/seobeo.h" - - -int main(int argc, char **argv) -{ - if (argc < 3) - { - err(EXIT_FAILURE, "Usage: %s URL, PORT, PATH\n", argv[0]); - } - Seobeo_Web_Client_Get(argv[1], argv[2], argv[3]); - return 0; -} diff -r 75de5903355c -r 4532ce6d9eb8 dowa/d_memory.c --- a/dowa/d_memory.c Sun Dec 28 20:34:22 2025 -0800 +++ b/dowa/d_memory.c Mon Dec 29 07:50:07 2025 -0800 @@ -26,9 +26,10 @@ return Dowa_Arena_Allocate_Aligned(p_arena, size, sizeof(void*) * 2); } -void Dowa_Arena_Destroy(Dowa_Arena *p_arena) +void Dowa_Arena_Free(Dowa_Arena *p_arena) { - if (!p_arena) return; + if (!p_arena) + return; if (p_arena->buffer) Dowa_Free(p_arena->buffer); @@ -633,505 +634,3 @@ Dowa_Hash_Index* p_index = dowa__hashmap_get_index(p_map); return p_index ? p_index->item_count : 0; } - -/* -// --- OLD HashMap Implementation (Commented out for migration) --- // -Dowa_HashMap *Dowa_HashMap_Create(size_t capacity) -{ - Dowa_HashMap *p_hash_map; - p_hash_map = malloc(sizeof(Dowa_HashMap)); - if (p_hash_map == NULL) - { - return NULL; - } - p_hash_map->entries = calloc(capacity, sizeof(*p_hash_map->entries)); - if (p_hash_map->entries == NULL) - { - Dowa_Free(p_hash_map); - return NULL; - } - p_hash_map->capacity = capacity; - p_hash_map->current_capacity = 0; - p_hash_map->p_arena = NULL; - return p_hash_map; -} - -Dowa_HashMap *Dowa_HashMap_Create_With_Arena(size_t capacity, Dowa_Arena *p_arena) -{ - if (p_arena == NULL) - { - printf("Arena is NULL\n"); - return Dowa_HashMap_Create(capacity); - } - - Dowa_HashMap *p_hash_map; - p_hash_map = Dowa_Arena_Allocate(p_arena, sizeof(Dowa_HashMap)); - if (p_hash_map == NULL) - { - return NULL; - } - p_hash_map->entries = Dowa_Arena_Allocate(p_arena, sizeof(*p_hash_map->entries) * capacity); - if (p_hash_map->entries == NULL) - { - return NULL; - } - memset(p_hash_map->entries, 0, capacity * sizeof *p_hash_map->entries); - p_hash_map->capacity = capacity; - p_hash_map->current_capacity = 0; - p_hash_map->p_arena = p_arena; - return p_hash_map; -} - -void Dowa_HashMap_Destroy(Dowa_HashMap *p_hash_map) -{ - if (!p_hash_map) return; - - if (p_hash_map->p_arena) - { - // Free arena instead of the map... - // Dowa_Arena_Destroy(p_hash_map->p_arena); - return; - } - else - { - Dowa_HashEntry *entry; - Dowa_HashEntry *next; - if (p_hash_map->entries) - { - for (int idx=0; idxcapacity; idx++) - { - entry = p_hash_map->entries[idx]; - while (entry) - { - next = entry->next; - Dowa_Free(entry->key); - Dowa_Free(entry->buffer); - Dowa_Free(entry); - entry = next; - } - } - } - Dowa_Free(p_hash_map->entries); - } - Dowa_Free(p_hash_map); -} - -int32 Dowa_HashMap_Get_Position(Dowa_HashMap *p_hash_map, const char *key) -{ - if (!p_hash_map || !key) - return -1; - - int32 hash_val = HASH_KEY_NUMBER; - int32 c; - while ((c = *key++)) - { - hash_val = (hash_val << 5) + hash_val + c; - } - return hash_val % p_hash_map->capacity; -} - -void *Dowa_HashMap_Get(Dowa_HashMap *p_hash_map, const char *key) -{ - if (!p_hash_map || !key) - return NULL; - - int idx = Dowa_HashMap_Get_Position(p_hash_map, key); - Dowa_HashEntry *entry = p_hash_map->entries[idx]; - - while (entry) - { - if (strcmp(entry->key, key) == 0) - { - return entry->buffer; - } - entry = entry->next; - } - - return NULL; -} - - -int32 Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_HashMap *p_hash_map, const char *key, - void *value, size_t value_size, - Dowa_HashMap_ValueType type) -{ - if (!p_hash_map || !key || !value) - return -1; - - int idx = Dowa_HashMap_Get_Position(p_hash_map, key); - Dowa_HashEntry *entry = p_hash_map->entries[idx]; - Dowa_HashEntry *prev = NULL; - - // Old key - while (entry) - { - if (strcmp(entry->key, key) == 0) - { - // Fails if it the key exists... - return -1; - } - prev = entry; - entry = entry->next; - } - - // New Key - entry = p_hash_map->p_arena ? - Dowa_Arena_Allocate(p_hash_map->p_arena, sizeof(Dowa_HashEntry)) : - malloc(sizeof(Dowa_HashEntry)); - if (!entry) { perror("malloc or arena alloc"); return -1; } - - entry->key = p_hash_map->p_arena ? - Dowa_Arena_Copy(p_hash_map->p_arena, key, strlen(key) + 1) : - strdup(key); - if (!entry->key) - { - perror("strdup or arena copy"); - if (!p_hash_map->p_arena) Dowa_Free(entry); - return -1; - } - entry->buffer = value; - entry->capacity = value_size; - entry->type = type; - entry->next = NULL; - - if (prev) - prev->next = entry; - else - p_hash_map->entries[idx] = entry; - - p_hash_map->current_capacity++; - return 0; -} - -int32 Dowa_HashMap_Push_Value_With_Type(Dowa_HashMap *p_hash_map, const char *key, - void *value, size_t value_size, - Dowa_HashMap_ValueType type) -{ - if (!p_hash_map || !key || !value) - return -1; - - int idx = Dowa_HashMap_Get_Position(p_hash_map, key); - Dowa_HashEntry *entry = p_hash_map->entries[idx]; - Dowa_HashEntry *prev = NULL; - - // Check for existing key - while (entry) - { - if (strcmp(entry->key, key) == 0) - { - if (!p_hash_map->p_arena && entry->buffer) - Dowa_Free(entry->buffer); - - entry->buffer = p_hash_map->p_arena ? - Dowa_Arena_Allocate(p_hash_map->p_arena, value_size) : - malloc(value_size); - if (!entry->buffer) { perror("malloc or arena alloc"); return -1; } - - memcpy(entry->buffer, value, value_size); - entry->capacity = value_size; - entry->type = type; - return 0; - } - prev = entry; - entry = entry->next; - } - - // New Key - entry = p_hash_map->p_arena ? - Dowa_Arena_Allocate(p_hash_map->p_arena, sizeof(Dowa_HashEntry)) : - malloc(sizeof(Dowa_HashEntry)); - if (!entry) { perror("malloc or arena alloc"); return -1; } - - entry->key = p_hash_map->p_arena ? - Dowa_Arena_Copy(p_hash_map->p_arena, key, strlen(key) + 1) : - strdup(key); - if (!entry->key) { perror("strdup"); return -1; } - - entry->buffer = p_hash_map->p_arena ? - Dowa_Arena_Allocate(p_hash_map->p_arena, value_size) : - malloc(value_size); - if (!entry->buffer) { perror("malloc or arena alloc"); return -1; } - - memcpy(entry->buffer, value, value_size); - entry->capacity = value_size; - entry->type = type; - entry->next = NULL; - - if (prev) - prev->next = entry; - else - p_hash_map->entries[idx] = entry; - - p_hash_map->current_capacity++; - return 0; -} - -void Dowa_HashMap_Push_Value(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size) -{ - Dowa_HashMap_Push_Value_With_Type(p_hash_map, key, value, value_size, DOWA_HASH_MAP_TYPE_BUFFER); -} - -void Dowa_HashMap_Pop_Key(Dowa_HashMap *p_hash_map, const char *key) -{ - if (!p_hash_map || !key) - return; - - int idx = Dowa_HashMap_Get_Position(p_hash_map, key); - Dowa_HashEntry *entry = p_hash_map->entries[idx]; - Dowa_HashEntry *prev = NULL; - - while (entry) - { - if (strcmp(entry->key, key) == 0) - { - if (prev) - prev->next = entry->next; - else - p_hash_map->entries[idx] = entry->next; - - if (!(p_hash_map->p_arena)) - { - Dowa_Free(entry->key); - Dowa_Free(entry->buffer); - Dowa_Free(entry); - } - - if (p_hash_map->current_capacity > 0) - p_hash_map->current_capacity--; - return; - } - prev = entry; - entry = entry->next; - } -} - -boolean Dowa_HashMap_Has_Key(Dowa_HashMap *p_hash_map, const char *key) -{ - if (!p_hash_map || !key) - return FALSE; - - int idx = Dowa_HashMap_Get_Position(p_hash_map, key); - Dowa_HashEntry *entry = p_hash_map->entries[idx]; - - while (entry) - { - if (strcmp(entry->key, key) == 0) - return TRUE; - entry = entry->next; - } - - return FALSE; -} - -void Dowa_HashMap_Clear(Dowa_HashMap *p_hash_map) -{ - if (!p_hash_map) return; - - if (p_hash_map->p_arena) - { - for (int idx=0; idxcapacity; idx++) - p_hash_map->entries[idx] = NULL; - } - else - { - Dowa_HashEntry *entry; - Dowa_HashEntry *next; - if (p_hash_map->entries) - { - for (int idx=0; idxcapacity; idx++) - { - entry = p_hash_map->entries[idx]; - while (entry) - { - next = entry->next; - Dowa_Free(entry->key); - Dowa_Free(entry->buffer); - Dowa_Free(entry); - entry = next; - } - p_hash_map->entries[idx] = NULL; - } - } - } - p_hash_map->current_capacity = 0; -} - -uint32 Dowa_HashMap_Get_Count(Dowa_HashMap *p_hash_map) -{ - if (!p_hash_map) return 0; - return p_hash_map->current_capacity; -} - -void Dowa_HashMap_Print(Dowa_HashMap *map) -{ - if (!map) - { - printf("HashMap is NULL\n"); - return; - } - - printf("\n-----------\n\n"); - for (size_t i = 0; i < map->capacity; ++i) - { - Dowa_HashEntry *e = map->entries[i]; - while (e) - { - if (!e) break; - printf("%s: ", e->key); - switch (e->type) - { - case DOWA_HASH_MAP_TYPE_BUFFER: - { - unsigned char *p = e->buffer; - for (size_t j = 0; j < e->capacity; ++j) - { - printf("%02x", p[j]); - } - printf("\n"); - } - break; - case DOWA_HASH_MAP_TYPE_STRING: - { - printf("%.*s\n", (int)e->capacity, (char*)e->buffer); - } - break; - case DOWA_HASH_MAP_TYPE_HASHMAP: - { - printf("This is a hashmap with size of %zu, and pointer %p\n", e->capacity, (void *)e); - } - break; - case DOWA_HASH_MAP_TYPE_INT: - { - printf("%d\n", *(int32*)e->buffer); - } - break; - default: - { - printf("\n"); - } - } - e = e->next; - } - } - printf("-----------\n"); -} - -// -- Array --// -Dowa_Array *Dowa_Array_Init() -{ - Dowa_Array *dowa_array = malloc(sizeof(Dowa_Array)); - if (dowa_array == NULL) - { - return NULL; - } - - dowa_array->capacity = DOWA_ARRAY_DEFAULT_CAPACITY; - dowa_array->items = (Dowa_Array_Item *)malloc(sizeof(Dowa_Array_Item) * DOWA_ARRAY_DEFAULT_CAPACITY); - if (dowa_array->items == NULL) - { - return NULL; - } - - dowa_array->current_length = 0; - return dowa_array; -} - -// void Dowa_Array_Push_Value(Dowa_Array *dowa_array, void *buffer, uint32 length) -// { -// if (dowa_array->current_length == dowa_array->capacity) -// { -// dowa_array->items = realloc(dowa_array->items, sizeof(Dowa_Array_Item) * dowa_array->capacity * 2); -// dowa_array->capacity = dowa_array->capacity * 2; -// } -// Dowa_Array_Item *item = &(dowa_array->items[dowa_array->current_length]); -// item->value = malloc(length); -// if (item->value == NULL) -// { -// // didn't alloc should raise an error. -// return; -// } -// memcpy(item->value, buffer, length); -// item->length = length; -// dowa_array->current_length += 1; -// } - - -#ifdef DIRECTORY -int Dowa_HashMap_Cache_Folder(Dowa_HashMap *p_hash_map, const char *folder_path) -{ - if (!p_hash_map || !folder_path) - return -1; - - DIR *dir = opendir(folder_path); - if (!dir) { perror("opendir"); return -1; } - - struct dirent *entry; - while ((entry = readdir(dir))) - { - // skip "." and ".." - if (entry->d_name[0] == '.' && - (entry->d_name[1] == '\0' || - (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) - continue; - - char fullpath[PATH_MAX]; - snprintf(fullpath, sizeof fullpath, "%s/%s", folder_path, entry->d_name); - - struct stat st; - if (stat(fullpath, &st) < 0) { perror("stat"); continue; } - - if (S_ISREG(st.st_mode)) - { - size_t size = (size_t)st.st_size; - FILE *f = fopen(fullpath, "rb"); - if (!f) { perror("fopen"); continue; } - - // Allocate exact file size (no extra byte for null terminator) - void *buf = p_hash_map->p_arena ? - Dowa_Arena_Allocate(p_hash_map->p_arena, size) : - malloc(size); - if (!buf) { perror("malloc"); fclose(f); closedir(dir); return -1; } - - if (fread(buf, 1, size, f) != size) - { - perror("fread"); - if (!p_hash_map->p_arena) Dowa_Free(buf); - fclose(f); - continue; - } - fclose(f); - - // Store file data as-is without null terminator (for binary files like images) - Dowa_HashMap_Push_Value_With_Type(p_hash_map, entry->d_name, buf, size, DOWA_HASH_MAP_TYPE_STRING); - if (!p_hash_map->p_arena) - Dowa_Free(buf); // Dowa_HashMap_PushValue made its own copy - } - else if (S_ISDIR(st.st_mode)) - { - // TODO: Adjust the sizes of the recursive map? - Dowa_HashMap *p_child_map = Dowa_HashMap_Create_With_Arena(100, p_hash_map->p_arena); - if (!p_child_map) - { - perror("Dowa_HashMap_Create"); - return -1; - } - if (Dowa_HashMap_Cache_Folder(p_child_map, fullpath) == -1) - { - perror("Dowa_HashMap_Cache_Folder"); - return -1; - } - - // Should not copy as we malloced already. - if (Dowa_HashMap_Push_Value_With_Type_NoCopy(p_hash_map, entry->d_name, p_child_map, - sizeof(p_child_map), DOWA_HASH_MAP_TYPE_HASHMAP) == -1) - { - Dowa_HashMap_Destroy(p_child_map); - return -1; - } - } - } - closedir(dir); - return 0; -} -#endif -*/ diff -r 75de5903355c -r 4532ce6d9eb8 dowa/d_string.c --- a/dowa/d_string.c Sun Dec 28 20:34:22 2025 -0800 +++ b/dowa/d_string.c Mon Dec 29 07:50:07 2025 -0800 @@ -1,20 +1,74 @@ #include "dowa.h" -char *Dowa_Int32ToString(uint32 int32, char *buffer) +#ifndef MAX_STR_BUFFER + #define MAX_STR_BUFFER 1024 +#endif + +char *Dowa_String_Slice(char *from, size_t start, size_t end, Dowa_Arena *p_arena) { - sprintf(buffer, "%d", int32); + char *buffer = p_arena == NULL ? + malloc(sizeof(char) * end-start+1) : Dowa_Arena_Allocate(p_arena, end-start+1); + if (!buffer) + return NULL; + + for (int32 i = 0; i < (end - start); i++) + buffer[i] = from[start + i]; + + buffer[end - start] = '\0'; return buffer; } -const char *Dowa_String_Slice(const char *from, size_t start, size_t end) -{ - static char buffer[1024] = {0}; - size_t buffer_pos = 0; - for (int i = start; start < strlen(from) || start < end; i++) +char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena) +{ + if (!from || from[0] == '\n') + return NULL; + + int32 *token_pos_arr = NULL; + for (int32 i = 0; i < from_length; i++) { - buffer[buffer_pos++] = from[i]; + if (from[i] == token[0]) + { + int32 curr_token_pointer = 0; + while (curr_token_pointer < token_length && (i + curr_token_pointer) < from_length) + { + if (from[i + curr_token_pointer] != token[curr_token_pointer]) + break; + curr_token_pointer++; + } + + if (curr_token_pointer == token_length) + { + Dowa_Array_Push(token_pos_arr, i); + } + } } - return buffer; + + char **splitted_strings = NULL; + int32 start = 0; + int32 num_tokens = Dowa_Array_Length(token_pos_arr); + + for (int32 i = 0; i <= num_tokens; i++) + { + int32 end = (i < num_tokens) ? token_pos_arr[i] : from_length; + + char *val = Dowa_String_Slice(from, start, end, p_arena); + + if (p_arena) + { + Dowa_Array_Push_Arena(splitted_strings, val, p_arena); + } + else + { + Dowa_Array_Push(splitted_strings, val); + } + + if (i < num_tokens) { + start = token_pos_arr[i] + token_length; + } + } + + Dowa_Array_Free(token_pos_arr); + return splitted_strings; } int32 Dowa_String_Pos_Find(const char *from, const char *value, const size_t from_length, const size_t value_length) @@ -57,12 +111,12 @@ return NULL; } -int32 Dowa_String_Pos_Find_Char(const char *from, int c, int32 from_len) +int32 Dowa_String_Pos_Find_Char(const char *from, int c, int32 from_length) { - if (!from || from_len == 0) + if (!from || from_length == 0) return -1; - for (int32 i = 0; i < from_len; i++) + for (int32 i = 0; i < from_length; i++) { if ((int)from[i] == c) return i; @@ -70,12 +124,12 @@ return -1; } -char *Dowa_String_Find_Char(const char *from, int c, int32 from_len) +char *Dowa_String_Find_Char(const char *from, int c, int32 from_length) { - if (!from || from_len == 0) + if (!from || from_length == 0) return NULL; - for (int32 i = 0; i < from_len; i++) + for (int32 i = 0; i < from_length; i++) { if ((int)from[i] == c) return &from[i]; @@ -83,22 +137,11 @@ return NULL ; } -// char **Dowa_String_Split(char *from, char *split_token, int32 from_length, int32 split_token_length) -// { -// char *current_from = from; -// int32 moved = 0; -// while (1) -// { -// int32 next_token = Dowa_String_Pos_Find( -// current_from, split_token, from_length - moved, split_token_length -// ); -// -// if (next_token == -1) -// { -// break; -// } -// char *curr = malloc(sizeof(char) * next_token); -// while (i < next_token) -// curr[i++] = from[i]; -// } -// } +char *Dowa_String_Copy_Arena(char *from, Dowa_Arena *p_arena) +{ + char *buffer = Dowa_Arena_Allocate(p_arena, sizeof(char*) * strlen(from) + 1); + if (buffer); + memcpy(buffer, from, strlen(from) + 1); + return buffer; +} + diff -r 75de5903355c -r 4532ce6d9eb8 dowa/dowa.h --- a/dowa/dowa.h Sun Dec 28 20:34:22 2025 -0800 +++ b/dowa/dowa.h Mon Dec 29 07:50:07 2025 -0800 @@ -66,7 +66,7 @@ DLAPI Dowa_Arena *Dowa_Arena_Create(size_t capacity); DLAPI void *Dowa_Arena_Allocate(Dowa_Arena *p_arena, size_t size); DLAPI void *Dowa_Arena_Allocate_Aligned(Dowa_Arena *p_arena, size_t size, size_t alignment); -DLAPI void Dowa_Arena_Destroy(Dowa_Arena *p_arena); +DLAPI void Dowa_Arena_Free(Dowa_Arena *p_arena); DLAPI void *Dowa_Arena_Copy(Dowa_Arena *p_arena, const void *p_src, size_t size); DLAPI void Dowa_Arena_Reset(Dowa_Arena *p_arena); DLAPI size_t Dowa_Arena_Get_Used(Dowa_Arena *p_arena); @@ -211,76 +211,15 @@ DLAPI void dowa__hashmap_free(void* p_map); DLAPI size_t dowa__hashmap_count(void* p_map); -// --- OLD API (kept for reference, will be removed after migration) --- // -/* - - -OLD HashMap API - Commented out for migration -typedef enum { - DOWA_HASH_MAP_TYPE_BUFFER, - DOWA_HASH_MAP_TYPE_STRING, - DOWA_HASH_MAP_TYPE_HASHMAP, - DOWA_HASH_MAP_TYPE_INT -} Dowa_HashMap_ValueType; - -typedef struct Dowa_HashEntry { - char *key; - void *buffer; - size_t capacity; - Dowa_HashMap_ValueType type; - struct Dowa_HashEntry *next; -} Dowa_HashEntry; - -typedef struct { - Dowa_HashEntry **entries; - size_t capacity; - uint32 current_capacity; - Dowa_Arena *p_arena; -} Dowa_HashMap; - -extern Dowa_HashMap *Dowa_HashMap_Create(size_t capacity); -extern Dowa_HashMap *Dowa_HashMap_Create_With_Arena(size_t capacity, Dowa_Arena *arena); -extern void Dowa_HashMap_Destroy(Dowa_HashMap *p_hash_map); -extern int32 Dowa_HashMap_Get_Position(Dowa_HashMap *p_hash_map, const char *key); -extern void *Dowa_HashMap_Get(Dowa_HashMap *p_hash_map, const char *key); -extern void Dowa_HashMap_Push_Value(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size); -extern int32 Dowa_HashMap_Push_Value_With_Type(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size, Dowa_HashMap_ValueType type); -extern int32 Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_HashMap *p_hash_map, const char *key, void *value, size_t value_size, Dowa_HashMap_ValueType type); -extern void Dowa_HashMap_Pop_Key(Dowa_HashMap *p_hash_map, const char *key); -extern boolean Dowa_HashMap_Has_Key(Dowa_HashMap *p_hash_map, const char *key); -extern void Dowa_HashMap_Clear(Dowa_HashMap *p_hash_map); -extern uint32 Dowa_HashMap_Get_Count(Dowa_HashMap *p_hash_map); - -OLD Array API - Commented out for migration -#define DOWA_ARRAY_DEFAULT_CAPACITY 256 - -typedef struct { - void *value; - uint32 length; -} Dowa_Array_Item; - -typedef struct { - uint32 capacity; - uint32 current_length; - Dowa_Array_Item *items; -} Dowa_Array; - -#define Dowa_Array_Push_Value(dowa_array, buffer, length) ... -Dowa_Array *Dowa_Array_Init(); - -OLD Utility Functions - Will be migrated -extern void Dowa_HashMap_Print(Dowa_HashMap *map); -extern int Dowa_HashMap_Cache_Folder(Dowa_HashMap *map, const char *folder_path); -*/ - // --- String Manipulation --- // -DLAPI const char *Dowa_String_Slice(const char *p_from, size_t start, size_t end); +DLAPI char *Dowa_String_Slice(char *from, size_t start, size_t end, Dowa_Arena *p_arena); +DLAPI char **Dowa_String_Split(char *from, char *token, int32 from_length, int32 token_length, Dowa_Arena *p_arena); +DLAPI char *Dowa_String_Copy_Arena(char *from, Dowa_Arena *p_arena); DLAPI int32 Dowa_String_Pos_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length); DLAPI char *Dowa_String_Find(const char *p_from, const char *p_value, const size_t from_length, const size_t value_length); -DLAPI int32 Dowa_String_Pos_Find_Char(const char *p_from, int c, int32 from_len); -DLAPI char *Dowa_String_Find_Char(const char *p_from, int c, int32 from_len); -DLAPI char *Dowa_Int32ToString(uint32 value, char *p_buffer); +DLAPI int32 Dowa_String_Pos_Find_Char(const char *p_from, int c, int32 from_length); +DLAPI char *Dowa_String_Find_Char(const char *p_from, int c, int32 from_length); #endif diff -r 75de5903355c -r 4532ce6d9eb8 dowa/dowa_test.c --- a/dowa/dowa_test.c Sun Dec 28 20:34:22 2025 -0800 +++ b/dowa/dowa_test.c Mon Dec 29 07:50:07 2025 -0800 @@ -24,7 +24,7 @@ strcpy(p_arena_mem2, "data"); printf(" [Arena Allocate] mem2 = \"%s\"\n", p_arena_mem2); - Dowa_Arena_Destroy(p_arena); + Dowa_Arena_Free(p_arena); printf(" [Arena] destroyed\n\n"); // --- Test Array Operations --- @@ -163,7 +163,7 @@ for (size_t i = 0; i < map_length; i++) printf(" [%zu] '%s' => %d\n", i, p_arena_map[i].key, p_arena_map[i].value); - Dowa_Arena_Destroy(p_map_arena); + Dowa_Arena_Free(p_map_arena); printf(" Arena destroyed (including map)\n\n"); // --- Test Array with Arena --- @@ -181,7 +181,7 @@ printf(" %d", p_arena_numbers[i]); printf("\n"); - Dowa_Arena_Destroy(p_array_arena); + Dowa_Arena_Free(p_array_arena); printf(" Arena destroyed (including array)\n\n"); // --- Test Medium HashMap (Stress Test) --- @@ -212,6 +212,38 @@ Dowa_HashMap_Free(p_large_map); printf(" Medium map freed\n\n"); + + printf("=== String Manipulations === \n\n"); + + printf(" Split strings without arena \n\n"); + { + char *from = "june_park_hell"; + char *token = "_"; + Dowa_Arena *p_arena = NULL; + + char **arr = Dowa_String_Split(from, token, strlen(from), 1, p_arena); + int32 arr_length = Dowa_Array_Length(arr); + printf("arr_length: %i\n", arr_length); + for (int32 i = 0; i < arr_length; i++) + printf("%s\n", arr[i]); + Dowa_Array_Free(arr); + if (arr == NULL) + printf("Free properly\n"); + } + + printf("\n Split strings with arena \n\n"); + { + char *from = "june_park_hell_arena"; + char *token = "_"; + Dowa_Arena *p_arena = Dowa_Arena_Create(1024); + + char **arr = Dowa_String_Split(from, token, strlen(from), 1, p_arena); + int32 arr_length = Dowa_Array_Length(arr); + for (int32 i = 0; i < arr_length; i++) + printf("%s\n", arr[i]); + Dowa_Arena_Free(p_arena); + } + printf("=== All tests passed! ===\n"); return 0; } diff -r 75de5903355c -r 4532ce6d9eb8 mrjunejune/main.c --- a/mrjunejune/main.c Sun Dec 28 20:34:22 2025 -0800 +++ b/mrjunejune/main.c Mon Dec 29 07:50:07 2025 -0800 @@ -1,24 +1,30 @@ -/** - * - * MrJuneJune - * - * - Server side generated my blog created using entirely in C. - * - Resume - * - What have I done? - * - Blogs.... - * - */ - #include "seobeo/seobeo.h" volatile sig_atomic_t stop_server = 0; -void handle_sigint(int sig) { +void handle_sigint(int sig) +{ printf("Failed\n"); stop_server = 1; } +Seobeo_Request_Entry* GetUser(Seobeo_Request_Entry *req, Dowa_Arena *arena) +{ + void *id_kv = Dowa_HashMap_Get_Ptr(req, ":id"); + const char *user_id = id_kv ? ((Seobeo_Request_Entry*)id_kv)->value : "unknown"; + + Seobeo_Request_Entry *resp = NULL; + char *body = Dowa_Arena_Allocate(arena, 256); + snprintf(body, 256, "{\"id\":\"%s\",\"name\":\"John Doe\"}", user_id); + + Dowa_HashMap_Push_Arena(resp, "body", body, arena); + return resp; +} + + int main(void) { + Seobeo_Router_Init(); + Seobeo_Router_Register("GET", "/v1/users/:id", GetUser); Seobeo_Web_Server_Start("mrjunejune/pages", "6969", SEOBEO_MODE_EDGE, 2); } diff -r 75de5903355c -r 4532ce6d9eb8 seobeo/BUILD --- a/seobeo/BUILD Sun Dec 28 20:34:22 2025 -0800 +++ b/seobeo/BUILD Mon Dec 29 07:50:07 2025 -0800 @@ -30,6 +30,7 @@ "s_linux_network.c", "s_web.c", "s_ssl.c", + "s_router.c", "os/s_macos_edge.c", ], hdrs = [":seobeo_hdrs"], @@ -49,6 +50,7 @@ "s_linux_network.c", "s_web.c", "s_ssl.c", + "s_router.c", "os/s_linux_edge.c", ], hdrs = [":seobeo_hdrs"], @@ -79,6 +81,7 @@ "s_linux_network.c", "s_web.c", "s_ssl.c", + "s_router.c", "snapshot_creator.c", "os/s_macos_edge.c", ], @@ -99,6 +102,7 @@ "s_linux_network.c", "s_web.c", "s_ssl.c", + "s_router.c", "snapshot_creator.c", "os/s_linux_edge.c", ], diff -r 75de5903355c -r 4532ce6d9eb8 seobeo/s_router.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/seobeo/s_router.c Mon Dec 29 07:50:07 2025 -0800 @@ -0,0 +1,178 @@ +#include "seobeo/seobeo.h" +#include +#include +#include + +struct Seobeo_Route_Struct { + char *method; // "GET", "POST", "PUT", "DELETE" + char *path_pattern; // "/v1/users/:id/posts/:post_id" + Seobeo_Route_Handler handler; + + // Pre-parsed path segments for efficient matching + char **path_segments; // ["v1", "users", ":id", "posts", ":post_id"] + boolean *is_param; // [false, false, true, false, true] + size_t segment_count; +}; + +static Seobeo_Route *g_routes = NULL; + +void Seobeo_Router_Init() +{ + g_routes = NULL; +} + +void Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler) +{ + Seobeo_Route route = {0}; + + route.method = strdup(method); + route.path_pattern = strdup(path_pattern); + route.handler = handler; + + // save it as global variable + route.path_segments = Dowa_String_Split(path_pattern, "/", strlen(path_pattern), 1, NULL); + route.segment_count = Dowa_Array_Length(route.path_segments); + route.is_param = (boolean*)malloc(sizeof(boolean) * route.segment_count); + + for (size_t i = 0; i < route.segment_count; i++) + route.is_param[i] = (route.path_segments[i][0] == ':'); + + Dowa_Array_Push(g_routes, route); +} + +// Match route and extract path parameters +static boolean match_route_and_extract( + Seobeo_Route *route, + const char *request_path, + Seobeo_Request_Entry **pp_request_map, + Dowa_Arena *p_arena) +{ + Dowa_Arena *p_temp_arena = Dowa_Arena_Create(1024); + char **request_segments = Dowa_String_Split(request_path, "/", strlen(request_path), 1, p_temp_arena); + size_t request_segment_count = Dowa_Array_Length(request_segments); + // Check segment count matches + if (request_segment_count != route->segment_count) + { + Dowa_Arena_Free(p_temp_arena); + return FALSE; + } + + for (size_t i = 0; i < route->segment_count; i++) + { + // parameters + if (route->is_param[i]) + { + char *param_name = route->path_segments[i]; // e.g., ":id" + char *param_value = request_segments[i]; // e.g., "123" + + // Should Copy to arena + char *key = Dowa_String_Copy_Arena(param_name, p_arena); + char *value = Dowa_String_Copy_Arena(param_value, p_arena); + Dowa_HashMap_Push_Arena(*pp_request_map, key, value, p_arena); + } + else + { + // Does not match. + if (strcmp(route->path_segments[i], request_segments[i]) != 0) + { + Dowa_Arena_Free(p_temp_arena); + return FALSE; + } + } + } + + Dowa_Arena_Free(p_temp_arena); + return TRUE; +} + +Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, + const char *path, + Seobeo_Request_Entry **pp_request_map, + Dowa_Arena *p_arena) { + if (g_routes == NULL) + { + return NULL; + } + + size_t route_count = Dowa_Array_Length(g_routes); + for (size_t i = 0; i < route_count; i++) + { + Seobeo_Route *route = &g_routes[i]; + if (strcmp(route->method, method) != 0) + { + continue; + } + + if (match_route_and_extract(route, path, pp_request_map, p_arena)) + { + return route->handler; + } + } + + return NULL; +} + +void Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, + Seobeo_Request_Entry *p_response_map, + Dowa_Arena *p_arena) +{ + if (p_response_map == NULL) + { + char *header = Dowa_Arena_Allocate(p_arena, 1024); + Seobeo_Web_Header_Generate(header, HTTP_INTERNAL_ERROR, "text/plain", 21); + Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); + Seobeo_Handle_Queue(p_handle, (uint8_t*)"Internal Server Error", 21); + Seobeo_Handle_Flush(p_handle); + return; + } + + // Header + int status = HTTP_OK; + void *p_status_kv = Dowa_HashMap_Get_Ptr(p_response_map, "status"); + if (p_status_kv) + { + const char *status_str = ((Seobeo_Request_Entry*)p_status_kv)->value; + status = atoi(status_str); + } + + // Body + const char *body = ""; + void *p_body_kv = Dowa_HashMap_Get_Ptr(p_response_map, "body"); + if (p_body_kv) + { + body = ((Seobeo_Request_Entry*)p_body_kv)->value; + } + + // Default text plain + const char *content_type = "text/plain"; + void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type"); + if (p_content_type_kv) + { + content_type = ((Seobeo_Request_Entry*)p_content_type_kv)->value; + } + + char *header = Dowa_Arena_Allocate(p_arena, 1024); + Seobeo_Web_Header_Generate(header, status, content_type, strlen(body)); + + Seobeo_Handle_Queue(p_handle, (uint8_t*)header, strlen(header)); + Seobeo_Handle_Queue(p_handle, (uint8_t*)body, strlen(body)); + Seobeo_Handle_Flush(p_handle); +} + +void Seobeo_Router_Destroy() +{ + if (g_routes == NULL) + return; + + size_t route_count = Dowa_Array_Length(g_routes); + for (size_t i = 0; i < route_count; i++) + { + Seobeo_Route *route = &g_routes[i]; + if (route->method) free(route->method); + if (route->path_pattern) free(route->path_pattern); + if (route->path_segments) Dowa_Array_Free(route->path_segments); + if (route->is_param) free(route->is_param); + } + Dowa_Array_Free(g_routes); + g_routes = NULL; +} diff -r 75de5903355c -r 4532ce6d9eb8 seobeo/s_web.c --- a/seobeo/s_web.c Sun Dec 28 20:34:22 2025 -0800 +++ b/seobeo/s_web.c Mon Dec 29 07:50:07 2025 -0800 @@ -95,7 +95,7 @@ Dowa_Arena *p_request_arena = Dowa_Arena_Create(1*1024*1024); // 1MB for request parsing if (!p_request_arena) { perror("Dowa_Arena_Create request"); goto clean_up; } - Dowa_Arena *p_response_arena = Dowa_Arena_Create(1*1024*1024); // 1MB for response + Dowa_Arena *p_response_arena = Dowa_Arena_Create(5*1024*1024); // 1MB for response if (!p_response_arena) { perror("Dowa_Arena_Create response"); goto clean_up; } void *p_response_header = Dowa_Arena_Allocate(p_response_arena, (size_t)1024*5); // 5Kb @@ -131,7 +131,24 @@ goto clean_up; } - // --- Handle different HTTP methods --- + // Extract path + void *p_path_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); + const char *path = p_path_kv ? ((Seobeo_Request_Entry*)p_path_kv)->value : "/"; + + // --- Try to match API route first --- + Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena); + + if (handler != NULL) + { + // Call the API handler + Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena); + + // Send the response + Seobeo_Router_Send_Response(p_cli_handle, p_response_map, p_response_arena); + goto clean_up; + } + + // --- Handle different HTTP methods (static files fallback) --- if (strcmp(method, "GET") == 0) { void *p_kv = Dowa_HashMap_Get_Ptr(p_req_map, "Path"); @@ -153,9 +170,7 @@ snprintf(file_path, 512, "%.*s", (int)(L-1), path+1); } else - { strcpy(file_path, path); - } } // Check if file is in cache, load if not @@ -171,12 +186,9 @@ } else { - // Load from disk and cache file_content = Seobeo_Web_LoadFile(file_path, &body_size); if (file_content) - { Dowa_HashMap_Push(p_html_cache, file_path, file_content); - } } if (!file_content) @@ -191,6 +203,7 @@ goto clean_up; } + // Serve static file const char *mime = "application/octet-stream"; if (strstr(file_path, ".html")) mime = "text/html; charset=utf-8"; else if (strstr(file_path, ".css")) mime = "text/css"; @@ -218,42 +231,8 @@ Seobeo_Handle_Flush(p_cli_handle); printf("DONE\n\n\n"); } - else if (strcmp(method, "POST") == 0) - { - // --- TODO: Add POST logic here --- - Seobeo_Web_Header_Generate(p_response_header, - HTTP_NOT_FOUND, - "text/plain", 0); - Seobeo_Handle_Queue(p_cli_handle, - (const uint8*)p_response_header, - (uint32)strlen(p_response_header)); - Seobeo_Handle_Flush(p_cli_handle); - } - else if (strcmp(method, "PUT") == 0) - { - // --- TODO: Add PUT logic here --- - Seobeo_Web_Header_Generate(p_response_header, - HTTP_NOT_FOUND, - "text/plain", 0); - Seobeo_Handle_Queue(p_cli_handle, - (const uint8*)p_response_header, - (uint32)strlen(p_response_header)); - Seobeo_Handle_Flush(p_cli_handle); - } - else if (strcmp(method, "DELETE") == 0) - { - // --- TODO: Add DELETE logic here --- - Seobeo_Web_Header_Generate(p_response_header, - HTTP_NOT_FOUND, - "text/plain", 0); - Seobeo_Handle_Queue(p_cli_handle, - (const uint8*)p_response_header, - (uint32)strlen(p_response_header)); - Seobeo_Handle_Flush(p_cli_handle); - } else { - // Unknown or unsupported method Seobeo_Web_Header_Generate(p_response_header, HTTP_FORBIDDEN, "text/plain", 0); @@ -269,9 +248,9 @@ if (p_cli_handle) Seobeo_Handle_Destroy(p_cli_handle); if (p_request_arena) - Dowa_Arena_Destroy(p_request_arena); + Dowa_Arena_Free(p_request_arena); if (p_response_arena) - Dowa_Arena_Destroy(p_response_arena); + Dowa_Arena_Free(p_response_arena); return; } @@ -561,7 +540,7 @@ break; }else { - Dowa_Arena_Destroy(p_request_arena); + Dowa_Arena_Free(p_request_arena); Seobeo_Handle_Destroy(h); return -1; } @@ -569,7 +548,7 @@ // Debug printf("Request body %s", p_request_body); - Dowa_Arena_Destroy(p_request_arena); + Dowa_Arena_Free(p_request_arena); Seobeo_Handle_Destroy(h); return 0; } diff -r 75de5903355c -r 4532ce6d9eb8 seobeo/seobeo.h --- a/seobeo/seobeo.h Sun Dec 28 20:34:22 2025 -0800 +++ b/seobeo/seobeo.h Mon Dec 29 07:50:07 2025 -0800 @@ -63,6 +63,18 @@ /* Generic HTTP GET Rquest to given host and port with path. It will mimic chrome. */ extern int Seobeo_Web_Client_Get(const char *host, const char *port, const char *path); +// --- Router --- // +/* Initialize the router system (called automatically by Seobeo_Web_Server_Start) */ +extern void Seobeo_Router_Init(); +/* Register an API route handler. Call before starting server. */ +extern void Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler); +/* Clean up router resources */ +extern void Seobeo_Router_Destroy(); +/* Find matching route handler (internal use) */ +extern Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena); +/* Send HTTP response from response map (internal use) */ +extern void Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena); + // --- Helper functions --- // /* Destroy handle. It will handle all NULL poointers. */ extern void Seobeo_Handle_Destroy(Seobeo_Handle *p_handle); diff -r 75de5903355c -r 4532ce6d9eb8 seobeo/seobeo_internal.h --- a/seobeo/seobeo_internal.h Sun Dec 28 20:34:22 2025 -0800 +++ b/seobeo/seobeo_internal.h Mon Dec 29 07:50:07 2025 -0800 @@ -65,6 +65,16 @@ // HTTP request map type: maps header names to header values typedef Dowa_KV(char*, char*) Seobeo_Request_Entry; +// --- Router Types --- // +// Forward declaration +typedef struct Seobeo_Route_Struct Seobeo_Route; + +// Route handler function type +typedef Seobeo_Request_Entry* (*Seobeo_Route_Handler)( + Seobeo_Request_Entry *p_request_map, + Dowa_Arena *p_arena +); + // --- Parse Header into Dowa Map ---// extern int Seobeo_Web_Header_Parse(Seobeo_Handle *p_handle, Seobeo_Request_Entry **pp_map, Dowa_Arena *p_arena);