view dowa/d_memory.c @ 65:ecb6ee6a22c3

[Misc] I will no longer drink cool aids of capital P.
author June Park <parkjune1995@gmail.com>
date Wed, 24 Dec 2025 06:22:59 -0800
parents a30944e5719e
children 75de5903355c
line wrap: on
line source

#include "dowa.h"

// --- Arena --- //
Dowa_Arena *Dowa_Arena_Create(size_t capacity)
{
  Dowa_Arena *p_arena = malloc(sizeof(Dowa_Arena));
  if (p_arena == NULL)
  {
    perror("malloc");
    return NULL;
  }
  p_arena->buffer = malloc(capacity);
  if (p_arena->buffer == NULL)
  {
    perror("malloc");
    Dowa_Free(p_arena);
    return NULL;
  }
  p_arena->offset = 0;
  p_arena->capacity = capacity;
  return p_arena;
}

void *Dowa_Arena_Allocate(Dowa_Arena *p_arena, size_t size)
{
  if (!p_arena || !p_arena->buffer || size == 0)
    return NULL;

  if (p_arena->offset + size > p_arena->capacity)
  {
    return NULL;
  }
  void *currnet_ptr = p_arena->buffer + p_arena->offset;
  p_arena->offset += size;
  return currnet_ptr;
}

void Dowa_Arena_Destroy(Dowa_Arena *p_arena)
{
  if (!p_arena) return;

  if (p_arena->buffer)
    Dowa_Free(p_arena->buffer);
  Dowa_Free(p_arena);
}

void *Dowa_Arena_Copy(Dowa_Arena *p_arena, const void *src, size_t size)
{
  if (p_arena == NULL || src == NULL || size == 0)
    return NULL;

  void *dest = Dowa_Arena_Allocate(p_arena, size);
  if (!dest)
    return NULL;

  memcpy(dest, src, size);
  return dest;
}

void Dowa_Arena_Reset(Dowa_Arena *p_arena)
{
  if (!p_arena) return;
  p_arena->offset = 0;
}

size_t Dowa_Arena_Get_Used(Dowa_Arena *p_arena)
{
  if (!p_arena) return 0;
  return p_arena->offset;
}

size_t Dowa_Arena_Get_Remaining(Dowa_Arena *p_arena)
{
  if (!p_arena) return 0;
  return p_arena->capacity - p_arena->offset;
}

// --- HashMap --- //
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;
  }

  // Overriding doesn't really make sense? when copying over
  // as we need to free it.
  //
  //while (entry)
  //{
  //  if (strcmp(entry->key, key) == 0)
  //  {
  //    if (!p_hash_map->p_arena && entry->buffer)
  //      Dowa_Free(entry->buffer);
  //    entry->buffer = value;
  //    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 /* \0 */) :
    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 /* \0 */) :
    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");
}

#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