changeset 72:4532ce6d9eb8

[Seobeo] Added router to the server logic. Few dowa string manipulation logics.
author June Park <parkjune1995@gmail.com>
date Mon, 29 Dec 2025 07:50:07 -0800
parents 75de5903355c
children ede391ac83c8
files .claude/settings.local.json cutelient/BUILD cutelient/main.c dowa/d_memory.c dowa/d_string.c dowa/dowa.h dowa/dowa_test.c mrjunejune/main.c seobeo/BUILD seobeo/s_router.c seobeo/s_web.c seobeo/seobeo.h seobeo/seobeo_internal.h
diffstat 13 files changed, 375 insertions(+), 685 deletions(-) [+]
line wrap: on
line diff
--- /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:*)"
+    ]
+  }
+}
--- 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"],
-)
--- 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	<err.h>
-#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;
-}
--- 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; idx<p_hash_map->capacity; 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; idx<p_hash_map->capacity; idx++)
-      p_hash_map->entries[idx] = NULL;
-  }
-  else
-  {
-    Dowa_HashEntry *entry;
-    Dowa_HashEntry *next;
-    if (p_hash_map->entries)
-    {
-      for (int idx=0; idx<p_hash_map->capacity; 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("<unknown type>\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
-*/
--- 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;
+}
+
--- 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
--- 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;
 }
--- 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);
 }
--- 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",
   ],
--- /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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- 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;
 }
--- 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);
--- 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);