/*
utils.c - Random C utilities to make it more bearable to work with.
Made for my usecase.

Tested with C23 only.

Some of these come from other people licensed in the public domain, some I wrote myself.

Documentation is included at the end of the file.

Licensed in the public domain. Do whatever you want with it.

Styleguide:
- 2 space indentation,
- pointers aligned to the type (int* ptr, not int *ptr),
- use snake_case for functions, function-like macros, variables and types,
- use ALL_CAPS for macro expansions and macro constants,
- use shorthands defined below always.
*/

#ifndef _UTILS_C
#define _UTILS_C

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

typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

typedef int8_t   i8;
typedef int16_t  i16;
typedef int32_t  i32;
typedef int64_t  i64;

typedef float    f32;
typedef double   f64;

typedef size_t   usz;
typedef ptrdiff_t isz;

#define u8_max  UINT8_MAX
#define u16_max UINT16_MAX
#define u32_max UINT32_MAX
#define u64_max UINT64_MAX

#define i8_min  INT8_MIN
#define i8_max  INT8_MAX
#define i16_min INT16_MIN
#define i16_max INT16_MAX
#define i32_min INT32_MIN
#define i32_max INT32_MAX
#define i64_min INT64_MIN
#define i64_max INT64_MAX

#define nil NULL

#ifdef USE_RANDOM_UTIL

#define _int_by_1_5(val) \
  ((val) + (val) / 2)

#include <sys/random.h>
#include <errno.h>
#include <assert.h>

static inline u64 random_u64(void) {
  u64 value = 0;
  usz offset = 0;

  while (offset < sizeof(value)) {
    ssize_t result = getrandom(
      ((u8*)&value) + offset,
      sizeof(value) - offset,
      0
    );

    if (result <= 0) {
      if (errno == EINTR) {
        continue;
      }

      assert(0 && "getrandom failed");
    }

    offset += (usz)result;
  }

  return value;
}

static inline i64 random_i64(void) {
  return (i64)random_u64();
}

static inline u32 random_u32(void) {
  return (u32)random_u64();
}

static inline i32 random_i32(void) {
  return (i32)random_u32();
}

static inline u16 random_u16(void) {
  return (u16)random_u64();
}

static inline i16 random_i16(void) {
  return (i16)random_u16();
}

static inline u8 random_u8(void) {
  return (u8)random_u64();
}

static inline i8 random_i8(void) {
  return (i8)random_u8();
}

static inline u64 random_u64_range(u64 min, u64 max) {
  if (min > max) {
    u64 tmp = min;
    min = max;
    max = tmp;
  }

  if (min == 0 && max == u64_max) {
    return random_u64();
  }

  u64 range = max - min + 1;

  u64 limit = u64_max - (u64_max % range);

  u64 value;

  do {
    value = random_u64();
  } while (value >= limit);

  return min + (value % range);
}

static inline i64 random_i64_range(i64 min, i64 max) {
  if (min > max) {
    i64 tmp = min;
    min = max;
    max = tmp;
  }

  u64 range = (u64)max - (u64)min + 1;

  u64 limit = u64_max - (u64_max % range);

  u64 value;

  do {
    value = random_u64();
  } while (value >= limit);

  return min + (i64)(value % range);
}

static inline u32 random_u32_range(u32 min, u32 max) {
  return (u32)random_u64_range(min, max);
}

static inline i32 random_i32_range(i32 min, i32 max) {
  return (i32)random_i64_range(min, max);
}

static inline u16 random_u16_range(u16 min, u16 max) {
  return (u16)random_u64_range(min, max);
}

static inline i16 random_i16_range(i16 min, i16 max) {
  return (i16)random_i64_range(min, max);
}

static inline u8 random_u8_range(u8 min, u8 max) {
  return (u8)random_u64_range(min, max);
}

static inline i8 random_i8_range(i8 min, i8 max) {
  return (i8)random_i64_range(min, max);
}

static inline f64 random_f64(void) {
  return (f64)random_u64() / ((f64)u64_max + 1.0);
}

static inline f32 random_f32(void) {
  return (f32)random_u32() / ((f32)u32_max + 1.0f);
}

static inline f64 random_f64_range(f64 min, f64 max) {
  return min + (max - min) * random_f64();
}

static inline f32 random_f32_range(f32 min, f32 max) {
  return min + (max - min) * random_f32();
}

#endif // USE_RANDOM_UTIL



#ifdef USE_ALLOC_UTIL

typedef struct {
  void** allocations;
  usz count;
  usz capacity;
} alloc_tracker;

static void alloc_tracker_init(alloc_tracker* tracker) {
  const usz initial_capacity = 16;
  tracker->allocations = malloc(sizeof(void*) * initial_capacity);
  tracker->count = 0;
  tracker->capacity = initial_capacity;
}

static bool _alloc_tracker_resize(alloc_tracker* tracker) {
  const usz new_capacity = (usz)(tracker->capacity * 1.5);
  void** new_allocations = realloc(tracker->allocations, sizeof(void*) * new_capacity);

  if (new_allocations) {
    tracker->allocations = new_allocations;
    tracker->capacity = new_capacity;
    return true;
  }

  return false;
}

static void* talloc(alloc_tracker* tracker, usz size) {
  if (tracker->count >= tracker->capacity) {
    if (!_alloc_tracker_resize(tracker)) {
      return nil;
    }
  }

  void* ptr = malloc(size);

  if (ptr) {
    tracker->allocations[tracker->count++] = ptr;
  }

  return ptr;
}

static void tfree(alloc_tracker* tracker, void* ptr) {
  for (usz i = 0; i < tracker->count; i++) {
    if (tracker->allocations[i] == ptr) {
      free(ptr);
      tracker->allocations[i] = tracker->allocations[tracker->count - 1];
      tracker->count--;
      return;
    }
  }
}

static bool alloc_tracker_add_ptr(alloc_tracker* tracker, void* ptr) {
  if (ptr == nil || tracker == nil) {
    return false;
  }

  for (usz i = 0; i < tracker->count; i++) {
    if (tracker->allocations[i] == ptr) {
      return false;
    }
  }

  if (
    tracker->count >= tracker->capacity &&
    !_alloc_tracker_resize(tracker)
  ) {
    return false;
  }

  if (tracker->count < tracker->capacity) {
    tracker->allocations[tracker->count] = ptr;
    tracker->count += 1;
  }

  return true;
}

static void* trealloc(alloc_tracker* tracker, void* ptr, usz new_size) {
  usz found_index = 0;
  bool found = false;

  for (usz i = 0; i < tracker->count; i++) {
    if (tracker->allocations[i] == ptr) {
      found_index = i;
      found = true;
      break;
    }
  }

  if (!found) {
    return nil;
  }

  void* new_ptr = realloc(ptr, new_size);

  if (!new_ptr) {
    return nil;
  }

  tracker->allocations[found_index] = new_ptr;

  return new_ptr;
}

static void alloc_tracker_free_all(alloc_tracker* tracker) {
  for (usz i = 0; i < tracker->count; i++) {
    free(tracker->allocations[i]);
  }

  free(tracker->allocations);

  tracker->allocations = nil;
  tracker->count = 0;
  tracker->capacity = 0;
}


#ifndef ARENA_ALIGNMENT
#define ARENA_ALIGNMENT 8
#endif

typedef struct arena_chunk {
  u8* memory;
  usz capacity;
  usz offset;
  struct arena_chunk* next;
} _arena_chunk;

typedef struct {
  _arena_chunk* head;
  _arena_chunk* tail;
  _arena_chunk* current;
  usz chunk_size;
} _arena_class;

typedef struct {
  _arena_class primary;
  _arena_class oversized;
} arena;

_Static_assert(
  (ARENA_ALIGNMENT & (ARENA_ALIGNMENT - 1)) == 0,
  "ARENA_ALIGNMENT must be a power of two"
);

static usz _arena_align(usz x) {
  usz mask = ARENA_ALIGNMENT - 1;
  return (x + mask) & ~mask;
}

static _arena_chunk* _arena_chunk_create(usz capacity) {
  _arena_chunk* c = (_arena_chunk*)malloc(sizeof(_arena_chunk));

  if (c == nil) {
    return nil;
  }

  c->memory = (u8*)malloc(capacity);

  if (c->memory == nil) {
    free(c);
    return nil;
  }

  c->capacity = capacity;
  c->offset = 0;
  c->next = nil;

  return c;
}

static void _arena_class_init(_arena_class* cls, usz chunk_size) {
  cls->head = nil;
  cls->tail = nil;
  cls->current = nil;
  cls->chunk_size = chunk_size;
}

static void _arena_class_destroy(_arena_class* cls) {
  _arena_chunk* c = cls->head;

  while (c != nil) {
    _arena_chunk* next = c->next;

    free(c->memory);
    free(c);

    c = next;
  }

  cls->head = nil;
  cls->tail = nil;
  cls->current = nil;
}

static void _arena_class_reset(_arena_class* cls) {
  for (_arena_chunk* c = cls->head; c != nil; c = c->next) {
    c->offset = 0;
  }

  cls->current = cls->head;
}

static void* _arena_class_alloc(_arena_class* cls, usz size) {
  size = _arena_align(size);

  if (cls->current == nil) {
    cls->current = cls->head;
  }

  while (
    cls->current != nil &&
    cls->current->offset + size > cls->current->capacity
  ) {
    cls->current = cls->current->next;
  }

  if (cls->current == nil) {
    usz alloc_size =
      (size > cls->chunk_size)
      ? size
      : cls->chunk_size;

    _arena_chunk* chunk = _arena_chunk_create(alloc_size);

    if (chunk == nil) {
      return nil;
    }

    if (cls->head == nil) {
      cls->head = chunk;
      cls->tail = chunk;
    } else {
      cls->tail->next = chunk;
      cls->tail = chunk;
    }

    cls->current = chunk;
  }

  void* ptr =
    cls->current->memory +
    cls->current->offset;

  cls->current->offset += size;

  return ptr;
}

static bool arena_init_custom(arena* a, usz chunk_size) {
  if (a == nil) {
    return false;
  }

  _arena_class_init(&a->primary, chunk_size);
  _arena_class_init(&a->oversized, chunk_size);

  return true;
}

static bool arena_init(arena* a) {
  return arena_init_custom(a, 64 * 1024); // 64 KB
}

static void arena_reset(arena* a) {
  _arena_class_reset(&a->primary);
  _arena_class_destroy(&a->oversized);
  _arena_class_init(&a->oversized, a->primary.chunk_size);
}

static void arena_destroy(arena* a) {
  _arena_class_destroy(&a->primary);
  _arena_class_destroy(&a->oversized);
}

static void* aalloc(arena* a, usz size) {
  size = _arena_align(size);

  if (size <= a->primary.chunk_size) {
    return _arena_class_alloc(&a->primary, size);
  }

  return _arena_class_alloc(&a->oversized, size);
}

#endif // USE_ALLOC_UTIL



#ifdef USE_OPTION_UTIL

typedef struct {
  enum {
    OPTION_NONE,
    OPTION_SOME
  } tag;

  void* some;
} option;

static inline option option_none(void) {
  option opt;
  opt.tag = OPTION_NONE;
  return opt;
}

static inline option option_some(void* value) {
  option opt;
  opt.tag = OPTION_SOME;
  opt.some = value;
  return opt;
}

static inline bool opt_is_none(option opt) {
  return opt.tag == OPTION_NONE;
}

static inline bool opt_is_some(option opt) {
  return opt.tag == OPTION_SOME;
}

static inline option option_from_ptr(void* ptr) {
  if (ptr == nil) {
    return option_none();
  }

  return option_some(ptr);
}

#endif // USE_OPTION_UTIL



#ifdef USE_DEFER_UTIL

#define _CONCAT_INTERNAL(x, y) x##y
#define _CONCAT(x, y) CONCAT_INTERNAL(x, y)

#define _DEFER_INTERNAL(id, code)                     \
  void _CONCAT(_defer_func_, id)(void* _unused) {     \
    (void)_unused;                                    \
    code                                              \
  }                                                   \
                                                      \
  __attribute__((cleanup(_CONCAT(_defer_func_, id)))) \
  int _CONCAT(_defer_var_, id) = 0

#define defer(code) _DEFER_INTERNAL(__COUNTER__, code)

#endif // USE_DEFER_UTIL



#ifdef USE_STR_VIEW_UTIL

/*
Taken from tsoding's nob.h
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>

typedef struct {
  size_t count;
  const char *data;
} str_view;

// Forward declarations so that the functions can call each other
static str_view str_view_chop_while(str_view *sv, int (*p)(int x));
static str_view str_view_chop_by_delim(str_view *sv, char delim);
static str_view str_view_chop_left(str_view *sv, size_t n);
static str_view str_view_chop_right(str_view *sv, size_t n);
static bool str_view_chop_prefix(str_view *sv, str_view prefix);
static bool str_view_chop_suffix(str_view *sv, str_view suffix);
static str_view str_view_trim(str_view sv);
static str_view str_view_trim_left(str_view sv);
static str_view str_view_trim_right(str_view sv);
static bool str_view_eq(str_view a, str_view b);
static bool str_view_ends_with_cstr(str_view sv, const char *cstr);
static bool str_view_ends_with(str_view sv, str_view suffix);
static bool str_view_starts_with(str_view sv, str_view prefix);
static str_view str_view_from_cstr(const char *cstr);
static str_view str_view_from_parts(const char *data, size_t count);

#define svpfmt "%.*s"
#define svpfarg(sv) (int)(sv).count, (sv).data

static str_view str_view_chop_while(str_view *sv, int (*p)(int x)) {
  size_t i = 0;
  while (i < sv->count && p(sv->data[i])) {
    i += 1;
  }

  str_view result = str_view_from_parts(sv->data, i);
  sv->count -= i;
  sv->data  += i;

  return result;
}

static str_view str_view_chop_by_delim(str_view *sv, char delim) {
  size_t i = 0;
  while (i < sv->count && sv->data[i] != delim) {
    i += 1;
  }

  str_view result = str_view_from_parts(sv->data, i);

  if (i < sv->count) {
    sv->count -= i + 1;
    sv->data  += i + 1;
  } else {
    sv->count -= i;
    sv->data  += i;
  }

  return result;
}

static bool str_view_chop_prefix(str_view *sv, str_view prefix) {
  if (str_view_starts_with(*sv, prefix)) {
    str_view_chop_left(sv, prefix.count);
    return true;
  }
  return false;
}

static bool str_view_chop_suffix(str_view *sv, str_view suffix) {
  if (str_view_ends_with(*sv, suffix)) {
    str_view_chop_right(sv, suffix.count);
    return true;
  }
  return false;
}

static str_view str_view_chop_left(str_view *sv, size_t n) {
  if (n > sv->count) {
    n = sv->count;
  }

  str_view result = str_view_from_parts(sv->data, n);

  sv->data  += n;
  sv->count -= n;

  return result;
}

static str_view str_view_chop_right(str_view *sv, size_t n) {
  if (n > sv->count) {
    n = sv->count;
  }

  str_view result = str_view_from_parts(sv->data + sv->count - n, n);

  sv->count -= n;

  return result;
}

static str_view str_view_from_parts(const char *data, size_t count) {
  str_view sv;
  sv.count = count;
  sv.data = data;
  return sv;
}

static str_view str_view_trim_left(str_view sv) {
  size_t i = 0;
  while (i < sv.count && isspace(sv.data[i])) {
    i += 1;
  }

  return str_view_from_parts(sv.data + i, sv.count - i);
}

static str_view str_view_trim_right(str_view sv) {
  size_t i = 0;
  while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) {
    i += 1;
  }

  return str_view_from_parts(sv.data, sv.count - i);
}

static str_view str_view_trim(str_view sv) {
  return str_view_trim_right(str_view_trim_left(sv));
}

static str_view str_view_from_cstr(const char *cstr) {
  return str_view_from_parts(cstr, strlen(cstr));
}

static bool str_view_eq(str_view a, str_view b) {
  if (a.count != b.count) {
    return false;
  } else {
    return memcmp(a.data, b.data, a.count) == 0;
  }
}

static bool str_view_ends_with_cstr(str_view sv, const char *cstr) {
  return str_view_ends_with(sv, str_view_from_cstr(cstr));
}

static bool str_view_ends_with(str_view sv, str_view suffix) {
  if (sv.count >= suffix.count) {
    str_view sv_tail = {
      .count = suffix.count,
      .data = sv.data + sv.count - suffix.count,
    };
    return str_view_eq(sv_tail, suffix);
  }
  return false;
}

static bool str_view_starts_with(str_view sv, str_view expected_prefix) {
  if (expected_prefix.count <= sv.count) {
    str_view actual_prefix = str_view_from_parts(sv.data, expected_prefix.count);
    return str_view_eq(expected_prefix, actual_prefix);
  }

  return false;
}

#endif // USE_STR_VIEW_UTIL



#ifdef USE_DYN_ARR_UTIL

/*
Inspired by tsoding.
*/

#include <string.h>
#include <stdalign.h>

typedef struct {
  usz count;
  usz capacity;
} _dyn_arr_header;

#define DYN_ARR_INIT_CAPACITY 8

#define _DYN_ARR_HEADER_SIZE                              \
  ((sizeof(_dyn_arr_header) + alignof(max_align_t) - 1) & \
   ~(alignof(max_align_t) - 1))

#define _arr_hdr(arr) \
  ((_dyn_arr_header*)((char*)(arr) - _DYN_ARR_HEADER_SIZE))

#define arr_len(arr) \
  ((arr) ? _arr_hdr(arr)->count : 0)

#define arr_free(arr)      \
  do {                     \
    if (arr) {             \
      free(_arr_hdr(arr)); \
      (arr) = nil;         \
    }                      \
  } while (0)

static bool _arr_push_impl(
  void** arr,
  usz elem_size,
  const void* value
) {
  if (*arr == nil) {
    usz cap = DYN_ARR_INIT_CAPACITY;

    if (
      cap == 0 ||
      cap > SIZE_MAX / elem_size
    ) {
      return 0;
    }

    usz bytes =
      _DYN_ARR_HEADER_SIZE +
      elem_size * cap;

    _dyn_arr_header* h = malloc(bytes);

    if (h == nil) {
      return false;
    }

    h->count = 0;
    h->capacity = cap;

    *arr =
      (char*)h +
      _DYN_ARR_HEADER_SIZE;
  }

  _dyn_arr_header* h = _arr_hdr(*arr);

  if (h->count >= h->capacity) {
    usz newcap = _int_by_1_5(h->capacity);

    if (
      newcap <= h->capacity ||
      newcap > SIZE_MAX / elem_size
    ) {
      return 0;
    }

    usz bytes =
      _DYN_ARR_HEADER_SIZE +
      elem_size * newcap;

    _dyn_arr_header* newh =
      realloc(h, bytes);

    if (!newh) {
      return false;
    }

    newh->capacity = newcap;

    *arr =
      (char*)newh +
      _DYN_ARR_HEADER_SIZE;

    h = newh;
  }

  memcpy(
    (char*)(*arr) +
    elem_size * h->count,
    value,
    elem_size
  );

  h->count++;

  return true;
}

#define arr_push(arr, value)   \
  _arr_push_impl(              \
    (void**)&(arr),            \
    sizeof(*(arr)),            \
    &(typeof(*(arr))){(value)} \
  )

static bool _c_arr_to_dyn_impl(
  void** arr,
  const void* c_arr,
  usz size,
  usz count
) {
  if (
    count > SIZE_MAX / size
  ) {
    return false;
  }

  usz capacity = count < DYN_ARR_INIT_CAPACITY ? DYN_ARR_INIT_CAPACITY : count;

  usz bytes =
    _DYN_ARR_HEADER_SIZE +
    size * capacity;

  _dyn_arr_header* h = malloc(bytes);

  if (h == nil) {
    return false;
  }

  h->count = count;
  h->capacity = capacity;
  memcpy(
    (char*)h + _DYN_ARR_HEADER_SIZE,
    c_arr,
    size * count
  );

  *arr =
    (char*)h +
    _DYN_ARR_HEADER_SIZE;

  return true;
}

#define c_arr_to_dyn(arr, c_arr, count) \
  _c_arr_to_dyn_impl(                   \
    (void**)&(arr),                     \
    (const void*)(c_arr),               \
    sizeof(*(arr)),                     \
    (usz)(count)                        \
  )

#endif // USE_DYN_ARR_UTIL


#ifdef USE_STR_BUILDER_UTIL

#include <string.h>

typedef struct {
  char* data;
  usz count;
  usz capacity;
} str_builder;

#define sbpfmt "%.*s"
#define sbpfarg(sb) (int)(sb).count, (sb).data

static bool str_builder_reserve(
  str_builder* sb,
  usz additional
) {
  usz required =
    sb->count +
    additional +
    1;

  if (required <= sb->capacity) {
    return true;
  }

  usz new_capacity =
    sb->capacity > 0
    ? sb->capacity
    : 64;

  while (new_capacity < required) {
    usz next = (usz)(_int_by_1_5(new_capacity));

    if (next <= new_capacity) {
      return false;
    }

    new_capacity = next;
  }

  char* new_data =
    realloc(sb->data, new_capacity);

  if (new_data == nil) {
    return false;
  }

  sb->data = new_data;
  sb->capacity = new_capacity;

  return true;
}

static bool str_builder_append_n(
  str_builder* sb,
  const char* data,
  usz size
) {
  if (!str_builder_reserve(sb, size)) {
    return false;
  }

  memcpy(
    sb->data + sb->count,
    data,
    size
  );

  sb->count += size;
  sb->data[sb->count] = '\0';

  return true;
}

static bool str_builder_append_cstr(
  str_builder* sb,
  const char* cstr
) {
  return str_builder_append_n(
    sb,
    cstr,
    strlen(cstr)
  );
}

#ifdef USE_STR_VIEW_UTIL

static bool str_builder_append_sv(
  str_builder* sb,
  str_view sv
) {
  return str_builder_append_n(
    sb,
    sv.data,
    sv.count
  );
}

static str_view str_builder_view(
  const str_builder* sb
) {
  return str_view_from_parts(
    sb->data ? sb->data : "",
    sb->count
  );
}

#endif // USE_STR_VIEW_UTIL

static void str_builder_clear(
  str_builder* sb
) {
  sb->count = 0;

  if (sb->data != nil) {
    sb->data[0] = '\0';
  }
}

static void str_builder_free(
  str_builder* sb
) {
  free(sb->data);

  sb->data = nil;
  sb->count = 0;
  sb->capacity = 0;
}

#endif // USE_STR_BUILDER_UTIL



#ifdef USE_FILE_UTIL

#include <stdio.h>

#ifdef USE_STR_BUILDER_UTIL

static bool read_entire_file(
  const char* path,
  str_builder* sb
) {
  FILE* f = fopen(path, "rb");

  if (f == nil) {
    return false;
  }

  if (fseek(f, 0, SEEK_END) != 0) {
    fclose(f);
    return false;
  }

  long size = ftell(f);

  if (size < 0) {
    fclose(f);
    return false;
  }

  rewind(f);

  str_builder_clear(sb);

  if (
    !str_builder_reserve(
      sb,
      (usz)size
    )
  ) {
    fclose(f);
    return false;
  }

  usz read =
    fread(
      sb->data,
      1,
      (usz)size,
      f
    );

  fclose(f);

  if (read != (usz)size) {
    return false;
  }

  sb->count = read;
  sb->data[sb->count] = '\0';

  return true;
}

#endif // USE_STR_BUILDER_UTIL

static bool write_entire_file_cstr(
  const char* path,
  const char* data
) {
  FILE* f = fopen(path, "wb");

  if (f == nil) {
    return false;
  }

  usz size = strlen(data);

  usz written =
    fwrite(
      data,
      1,
      size,
      f
    );

  fclose(f);

  return written == size;
}

#ifdef USE_STR_VIEW_UTIL

static bool write_entire_file_sv(
  const char* path,
  str_view sv
) {
  FILE* f = fopen(path, "wb");

  if (f == nil) {
    return false;
  }

  usz written =
    fwrite(
      sv.data,
      1,
      sv.count,
      f
    );

  fclose(f);

  return written == sv.count;
}

#endif // USE_STR_VIEW_UTIL

#if defined(USE_STR_BUILDER_UTIL) && defined(USE_STR_VIEW_UTIL)

static bool write_entire_file_sb(
  const char* path,
  const str_builder* sb
) {
  return write_entire_file_sv(
    path,
    str_builder_view(sb)
  );
}

#endif // USE_STR_BUILDER_UTIL && USE_STR_VIEW_UTIL

#ifdef USE_STR_VIEW_UTIL
#define _WRITE_FILE_SV_TYPES \
  , str_view: write_entire_file_sv
#else
#define _WRITE_FILE_SV_TYPES
#endif

#if defined(USE_STR_BUILDER_UTIL) && defined(USE_STR_VIEW_UTIL)
#define _WRITE_FILE_SB_TYPES \
  , str_builder: write_entire_file_sb \
  , str_builder*: write_entire_file_sb
#else
#define _WRITE_FILE_SB_TYPES
#endif

#define write_entire_file(path, data)   \
  _Generic((data),                      \
    char*: write_entire_file_cstr,      \
    const char*: write_entire_file_cstr \
    _WRITE_FILE_SV_TYPES                 \
    _WRITE_FILE_SB_TYPES                 \
  )(path, data)

#endif // USE_FILE_UTIL

#endif // _UTILS_C

/*
DOCUMENTATION

===============
CORE SHORTHANDS
===============

Description:

  Convenient shorthands for common types and constants.

Typedefs/macro constants:

  u8, u16, u32, u64 - Unsigned integers of 8, 16, 32 and 64 bits.
  i8, i16, i32, i64 - Signed integers of 8, 16, 32 and 64 bits.

  f32, f64 - 32-bit and 64-bit floating point numbers.

  usz - Unsigned size for platform.
  isz - Signed size for platform (pointer difference).

  u8_max, u16_max, u32_max, u64_max - Maximum values for unsigned integers.
  i8_min, i8_max, i16_min, i16_max, i32_min, i32_max, i64_min, i64_max - Minimum and maximum values for signed integers.

  nil - Shorthand for NULL pointer.

===============
USE_RANDOM_UTIL
===============

Description:

  Cryptographically secure random number generation utilities built on top of getrandom(2).

Functions/macros:

  u64 random_u64(void) - Generate a random u64.
  i64 random_i64(void) - Generate a random i64.
  u32 random_u32(void) - Generate a random u32.
  i32 random_i32(void) - Generate a random i32.
  u16 random_u16(void) - Generate a random u16.
  i16 random_i16(void) - Generate a random i16.
  u8 random_u8(void) - Generate a random u8.
  i8 random_i8(void) - Generate a random i8.

  u64 random_u64_range(u64 min, u64 max) - Generate a random u64 in the range [min, max].
  i64 random_i64_range(i64 min, i64 max) - Generate a random i64 in the range [min, max].
  u32 random_u32_range(u32 min, u32 max) - Generate a random u32 in the range [min, max].
  i32 random_i32_range(i32 min, i32 max) - Generate a random i32 in the range [min, max].
  u16 random_u16_range(u16 min, u16 max) - Generate a random u16 in the range [min, max].
  i16 random_i16_range(i16 min, i16 max) - Generate a random i16 in the range [min, max].
  u8 random_u8_range(u8 min, u8 max) - Generate a random u8 in the range [min, max].
  i8 random_i8_range(i8 min, i8 max) - Generate a random i8 in the range [min, max].

  f64 random_f64(void) - Generate a random f64 in the range [0.0, 1.0).
  f32 random_f32(void) - Generate a random f32 in the range [0.0f, 1.0f).
  f64 random_f64_range(f64 min, f64 max) - Generate a random f64 in the range [min, max).
  f32 random_f32_range(f32 min, f32 max) - Generate a random f32 in the range [min, max).

==============
USE_ALLOC_UTIL
==============

Description:

  Allocation utilities including an allocation tracker and a simple arena allocator.

Structs:

  alloc_tracker {
    void** allocations; // Array of tracked pointers
    usz count;          // Number of tracked allocations
    usz capacity;       // Capacity of the allocations array
  }

  arena {
    // Private fields...
  }

Macro constants:

  ARENA_ALIGNMENT - Alignment used for arena allocations (default 8).

Functions/macros:

  void alloc_tracker_init(alloc_tracker* tracker) - Initialize an allocation tracker.

  void* talloc(alloc_tracker* tracker, usz size)
    Allocate memory using malloc and track the allocation.

  void tfree(alloc_tracker* tracker, void* ptr)
    Free a tracked allocation and remove it from the tracker.

  bool alloc_tracker_add_ptr(alloc_tracker* tracker, void* ptr)
    Add an externally allocated pointer to the tracker.

  void* trealloc(alloc_tracker* tracker, void* ptr, usz new_size)
    Reallocate a tracked allocation.

  void alloc_tracker_free_all(alloc_tracker* tracker)
    Free all tracked allocations and reset the tracker.

  bool arena_init(arena* a)
    Initialize an arena with the default chunk size (64 KB).

  bool arena_init_custom(arena* a, usz chunk_size)
    Initialize an arena with a custom chunk size.

  void arena_reset(arena* a)
    Reset an arena for reuse.
    Keeps primary chunks allocated for future allocations.
    Frees oversized allocations.

  void arena_destroy(arena* a)
    Destroy an arena and free all associated memory.

  void* aalloc(arena* a, usz size)
    Allocate memory from the arena.

===============
USE_OPTION_UTIL
===============

Description:

  Rust-style optional value container for nullable pointers.

Structs:

  option {
    ... tag  - OPTION_NONE or OPTION_SOME
    void* some - Stored pointer value when tag == OPTION_SOME
  }

Functions/macros:

  option option_none(void)
    Create an empty option.

  option option_some(void* value)
    Create an option containing a pointer value.

  bool opt_is_none(option opt)
    Check whether an option is empty.

  bool opt_is_some(option opt)
    Check whether an option contains a value.

  option option_from_ptr(void* ptr)
    Convert a nullable pointer into an option.
    Returns OPTION_NONE when ptr == nil.

==============
USE_DEFER_UTIL
==============

Description:

  GCC-only defer utility inspired by modern languages.
  Executes code automatically when the current scope exits.

Dependencies:

  GCC compiler extensions.

Functions/macros:

  defer(code)
    Execute code automatically at scope exit.

=================
USE_STR_VIEW_UTIL
=================

Description:

  Lightweight non-owning string slice utilities.

Structs:

  str_view {
    size_t count;     // Length of the string view
    const char* data; // Pointer to string data
  }

Helper macros:

  svpfmt
    printf format string for printing str_view.

  svpfarg(str_view sv)
    printf argument helper for str_view.

Functions/macros:

  str_view str_view_chop_while(str_view* sv, int (*p)(int x))
    Remove and return characters from the beginning while predicate returns true.

  str_view str_view_chop_by_delim(str_view* sv, char delim)
    Remove and return characters until delimiter is reached.

  str_view str_view_chop_left(str_view* sv, size_t n)
    Remove and return n characters from the left side.

  str_view str_view_chop_right(str_view* sv, size_t n)
    Remove and return n characters from the right side.

  bool str_view_chop_prefix(str_view* sv, str_view prefix)
    Remove a prefix if present.

  bool str_view_chop_suffix(str_view* sv, str_view suffix)
    Remove a suffix if present.

  str_view str_view_trim(str_view sv)
    Trim whitespace from both sides.

  str_view str_view_trim_left(str_view sv)
    Trim whitespace from the left side.

  str_view str_view_trim_right(str_view sv)
    Trim whitespace from the right side.

  bool str_view_eq(str_view a, str_view b)
    Compare two string views for equality.

  bool str_view_ends_with_cstr(str_view sv, const char* cstr)
    Check whether a string view ends with a C string.

  bool str_view_ends_with(str_view sv, str_view suffix)
    Check whether a string view ends with another string view.

  bool str_view_starts_with(str_view sv, str_view prefix)
    Check whether a string view starts with another string view.

  str_view str_view_from_cstr(const char* cstr)
    Create a string view from a null-terminated C string.

  str_view str_view_from_parts(const char* data, size_t count)
    Create a string view from raw pointer and length.

================
USE_DYN_ARR_UTIL
================

Description:

  Dynamic array utilities implemented using a hidden header stored before the array data.

Macro constants:

  DYN_ARR_INIT_CAPACITY - Initial dynamic array capacity.

Functions/macros:

  usz arr_len(any* arr)
    Get the number of elements in the dynamic array.

  void arr_free(any* arr)
    Free the dynamic array and set pointer to nil.

  bool arr_push(any* arr, any value)
    Append a value to the dynamic array.
    Automatically grows the allocation if needed.

  bool c_arr_to_dyn(any* arr, any[] c_arr, usz count)
    Convert a C array to a dynamic array.

Example:
  int[] my_array = {};
  arr_push(my_array, 42);
  arr_push(my_array, 99);

  for (usz i = 0; i < arr_len(my_array); i++) {
    printf("%d\n", my_array[i]);
  }

====================
USE_STR_BUILDER_UTIL
====================

Description:

  Dynamically growing string builder for efficient string construction.

Structs:

  str_builder {
    char* data;   // String buffer
    usz count;    // Current string length
    usz capacity; // Allocated buffer size
  }

Macros:

  sbpfmt
    printf format string for printing str_builder contents.

  sbpfarg(str_builder sb)
    printf argument helper for str_builder.

Functions/macros:

  bool str_builder_reserve(str_builder* sb, usz additional)
    Ensure the builder has enough capacity for additional bytes.

  bool str_builder_append_n(str_builder* sb, const char* data, usz size)
    Append raw bytes to the builder.

  bool str_builder_append_cstr(str_builder* sb, const char* cstr)
    Append a null-terminated C string.

  bool str_builder_append_sv(str_builder* sb, str_view sv)
    Append a string view.

    Dependency:
      USE_STR_VIEW_UTIL

  str_view str_builder_view(const str_builder* sb)
    Create a str_view referencing the builder contents.

    Dependency:
      USE_STR_VIEW_UTIL

  void str_builder_clear(str_builder* sb)
    Clear the builder contents while keeping allocated memory.

  void str_builder_free(str_builder* sb)
    Free the builder memory and reset the builder.

=============
USE_FILE_UTIL
=============

Description:

  Convenience utilities for reading and writing entire files.

Functions/macros:

  bool read_entire_file(const char* path, str_builder* sb)
    Read an entire file into a string builder.

    Dependency:
      USE_STR_BUILDER_UTIL

  bool write_entire_file_cstr(const char* path, const char* data)
    Write a null-terminated C string to a file.

  bool write_entire_file_sv(const char* path, str_view sv)
    Write a string view to a file.

    Dependency:
      USE_STR_VIEW_UTIL

  bool write_entire_file_sb(const char* path, const str_builder* sb)
    Write a string builder contents to a file.

    Dependencies:
      USE_STR_BUILDER_UTIL
      USE_STR_VIEW_UTIL

  bool write_entire_file(
    const char* path,
    const char* | const str_builder* | str_view data
  )
    Generic macro wrapper selecting the correct file writing implementation
    based on the type of data.
*/