colla/colla.h
2025-10-21 10:30:40 +02:00

1291 lines
36 KiB
C

#ifndef COLLA_HEADER
#define COLLA_HEADER
#if defined(_WIN32)
#define COLLA_WIN 1
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 0
#elif defined(__EMSCRIPTEN__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 0
#define COLLA_EMC 1
#elif defined(__linux__)
#define COLLA_WIN 0
#define COLLA_OSX 0
#define COLLA_LIN 1
#define COLLA_EMC 0
#elif defined(__APPLE__)
#define COLLA_WIN 0
#define COLLA_OSX 1
#define COLLA_LIN 0
#define COLLA_EMC 0
#endif
#if COLLA_LIN
#define _FILE_OFFSET_BITS 1
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h>
#include <uchar.h>
// LIBC FUNCTIONS ///////////////////////////////
extern void *memcpy(void *dst, const void *src, size_t size);
extern void *memmove(void *dst, const void *src, size_t size);
#define static_assert(cond, ...) _Static_assert(cond, "" __VA_ARGS__)
/////////////////////////////////////////////////
// CORE MODULES /////////////////////////////////
typedef enum {
COLLA_CORE = 0,
COLLA_OS = 1 << 0,
COLLA_NET = 1 << 1,
COLLA_ALL = 0xff,
} colla_modules_e;
void colla_init(colla_modules_e modules);
void colla_cleanup(void);
/////////////////////////////////////////////////
// OS AND COMPILER MACROS ///////////////////////
#if defined(_DEBUG)
#define COLLA_DEBUG 1
#define COLLA_RELEASE 0
#else
#define COLLA_DEBUG 0
#define COLLA_RELEASE 1
#endif
#if defined(__COSMOPOLITAN__)
#define COLLA_COSMO 1
#else
#define COLLA_COSMO 0
#endif
#define COLLA_POSIX (COLLA_OSX || COLLA_LIN || COLLA_COSMO)
#if defined(__clang__)
#define COLLA_CLANG 1
#define COLLA_MSVC 0
#define COLLA_TCC 0
#define COLLA_GCC 0
#elif defined(_MSC_VER)
#define COLLA_CLANG 0
#define COLLA_MSVC 1
#define COLLA_TCC 0
#define COLLA_GCC 0
#elif defined(__TINYC__)
#define COLLA_CLANG 0
#define COLLA_MSVC 0
#define COLLA_TCC 1
#define COLLA_GCC 0
#elif defined(__GNUC__)
#define COLLA_CLANG 0
#define COLLA_MSVC 0
#define COLLA_TCC 0
#define COLLA_GCC 1
#endif
#if COLLA_CLANG
#define COLLA_CMT_LIB 0
#elif COLLA_MSVC
#define COLLA_CMT_LIB 1
#elif COLLA_TCC
#define COLLA_CMT_LIB 1
#elif COLLA_GCC
#define COLLA_CMT_LIB 0
#endif
#if COLLA_TCC || COLLA_CLANG
#define alignof __alignof__
#endif
#if COLLA_WIN
#undef NOMINMAX
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#ifdef UNICODE
#define COLLA_UNICODE 1
#else
#define COLLA_UNICODE 0
#endif
#endif
/////////////////////////////////////////////////
// USEFUL MACROS ////////////////////////////////
#define arrlen(a) (sizeof(a) / sizeof((a)[0]))
#define COLLA_UNUSED(v) (void)(v)
#define COLLA__STRINGIFY(x) #x
#define COLLA_STRINGIFY(x) COLLA__STRINGIFY(x)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define KB(n) (((u64)n) << 10)
#define MB(n) (((u64)n) << 20)
#define GB(n) (((u64)n) << 30)
#define TB(n) (((u64)n) << 40)
#if COLLA_DEBUG
#define colla__assert(file, line, cond, ...) do { if (!(cond)) fatal(file ":" line " ASSERT FAILED: (" COLLA__STRINGIFY(cond) ") " __VA_ARGS__); } while (0)
#define colla_assert(...) colla__assert(__FILE__, COLLA__STRINGIFY(__LINE__), __VA_ARGS__)
#else
#define colla_assert(...) (void)0
#endif
/////////////////////////////////////////////////
// BASIC TYPES //////////////////////////////////
#if COLLA_WIN && COLLA_UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
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 size_t usize;
typedef ptrdiff_t isize;
typedef uintptr_t uptr;
typedef intptr_t iptr;
typedef struct {
u8 *data;
usize len;
} buffer_t;
typedef struct arena_t arena_t;
/////////////////////////////////////////////////
// LINKED LISTS /////////////////////////////////
// singly linked list
#define list_push(list, item) ((item)->next=(list), (list)=(item))
#define list_pop(list) ((list) = (list) ? (list)->next : NULL)
// double linked list
#define dlist_push(list, item) do { \
if (item) (item)->next = (list); \
if (list) (list)->prev = (item); \
(list) = (item); \
} while (0)
#define dlist_pop(list, item) do { \
if (!(item)) break; \
if ((item)->prev) (item)->prev->next = (item)->next; \
if ((item)->next) (item)->next->prev = (item)->prev; \
if ((item) == (list)) (list) = (item)->next; \
} while (0)
// ordered linked list
#define olist_push(head, tail, item) do { \
if (tail) { \
(tail)->next = (item); \
(tail) = (item); \
} \
else { \
(head) = (tail) = (item); \
} \
} while (0)
#define for_each(it, list) for (typeof(list) it = list; it; it = it->next)
/////////////////////////////////////////////////
// FORMATTING ///////////////////////////////////
int fmt_print(const char *fmt, ...);
int fmt_printv(const char *fmt, va_list args);
int fmt_buffer(char *buf, usize len, const char *fmt, ...);
int fmt_bufferv(char *buf, usize len, const char *fmt, va_list args);
/////////////////////////////////////////////////
/*
dynamic chunked array which uses an arena to allocate,
the structure needs to follow this exact format, if you want
you can use the macro darr_define(struct_name, item_type) instead:
////////////////////////////////////
typedef struct arr_t arr_t;
struct arr_t {
int *items;
usize block_size;
usize count;
arr_t *next;
arr_t *head;
};
// equivalent to
darr_define(arr_t, int);
////////////////////////////////////
by default a chunk is 64 items long, you can change this default
by modifying the arr.block_size value before adding to the array,
or by defining DARRAY_DEFAULT_BLOCK_SIZE
usage example:
////////////////////////////////////
darr_define(arr_t, int);
arr_t *arr = NULL;
for (int i = 0; i < 100; ++i) {
darr_push(&arena, arr, i);
}
for_each (chunk, arr) {
for (int i = 0; i < chunk->count; ++i) {
info("%d -> %d", i, chunk->items[i]);
}
}
*/
#define DARRAY_DEFAULT_BLOCK_SIZE (64)
#define darr_define(struct_name, item_type) typedef struct struct_name struct_name; \
struct struct_name { \
item_type *items; \
usize block_size; \
usize count; \
struct_name *next; \
struct_name *head; \
}
#define darr__alloc_first(arena, arr) do { \
(arr) = (arr) ? (arr) : alloc(arena, typeof(*arr)); \
(arr)->head = (arr)->head ? (arr)->head : (arr); \
(arr)->block_size = (arr)->block_size ? (arr)->block_size : DARRAY_DEFAULT_BLOCK_SIZE; \
(arr)->items = alloc(arena, typeof(*arr->items), arr->block_size); \
colla_assert((arr)->count == 0); \
} while (0)
#define darr__alloc_block(arena, arr) do { \
typeof(arr) newarr = alloc(arena, typeof(*arr)); \
newarr->block_size = arr->block_size; \
newarr->items = alloc(arena, typeof(*arr->items), arr->block_size); \
newarr->head = arr->head; \
arr->next = newarr; \
arr = newarr; \
} while (0)
#define darr_push(arena, arr, item) do { \
if (!(arr) || (arr)->items == NULL) darr__alloc_first(arena, arr); \
if ((arr)->count >= (arr)->block_size) darr__alloc_block(arena, arr); \
(arr)->items[(arr)->count++] = (item); \
} while (0)
// STRING TYPES /////////////////////////////////
#define STR_NONE SIZE_MAX
#define STR_END SIZE_MAX
typedef struct str_t str_t;
struct str_t {
char *buf;
usize len;
};
typedef struct str16_t str16_t;
struct str16_t {
char16_t *buf;
usize len;
};
#if COLLA_UNICODE
typedef str16_t tstr_t;
#else
typedef str_t tstr_t;
#endif
typedef struct strview_t strview_t;
struct strview_t {
const char *buf;
usize len;
};
darr_define(str_list_t, str_t);
darr_define(strv_list_t, strview_t);
// ALLOCATED STRING /////////////////////////////
#define str__1(arena, x) \
_Generic((x), \
const char *: str_init, \
char *: str_init, \
strview_t: str_init_view \
)(arena, x)
#define str__2(arena, cstr, clen) str_init_len(arena, cstr, clen)
#define str__impl(_1, _2, n, ...) str__##n
// either:
// arena_t arena, [const] char *cstr, [usize len]
// arena_t arena, strview_t view
#define str(arena, ...) str__impl(__VA_ARGS__, 2, 1, 0)(arena, __VA_ARGS__)
#define STR_EMPTY (str_t){0}
str_t str_init(arena_t *arena, const char *buf);
str_t str_init_len(arena_t *arena, const char *buf, usize len);
str_t str_init_view(arena_t *arena, strview_t view);
str_t str_fmt(arena_t *arena, const char *fmt, ...);
str_t str_fmtv(arena_t *arena, const char *fmt, va_list args);
tstr_t tstr_init(TCHAR *str, usize optional_len);
str16_t str16_init(char16_t *str, usize optional_len);
str_t str_from_str16(arena_t *arena, str16_t src);
str_t str_from_tstr(arena_t *arena, tstr_t src);
str16_t str16_from_str(arena_t *arena, str_t src);
usize str16_len(char16_t *str);
bool str_equals(str_t a, str_t b);
int str_compare(str_t a, str_t b);
str_t str_dup(arena_t *arena, str_t src);
str_t str_cat(arena_t *arena, str_t a, str_t b);
bool str_is_empty(str_t ctx);
void str_lower(str_t *src);
void str_upper(str_t *src);
void str_replace(str_t *ctx, char from, char to);
// if len == SIZE_MAX, copies until end
strview_t str_sub(str_t ctx, usize from, usize to);
// STRING VIEW //////////////////////////////////
// these macros might be THE worst code ever written, but they work ig
// detects if you're trying to create a string view from either:
// - a str_t -> calls strv_init_str
// - a string literal -> calls strv_init_len with comptime size
// - a c string -> calls strv_init with runtime size
#define STRV_EMPTY (strview_t){0}
// needed for strv__init_literal _Generic implementation, it's never actually called
strview_t strv__ignore(str_t s, size_t l);
#define strv__check(x, ...) ((#x)[0] == '"')
#define strv__init_literal(x, ...) \
_Generic((x), \
char *: strv_init_len, \
const char *: strv_init_len, \
str_t: strv__ignore \
)(x, sizeof(x) - 1)
#define strv__1(x) \
_Generic((x), \
char *: strv_init, \
const char *: strv_init, \
str_t: strv_init_str \
)(x)
#define strv__2(cstr, clen) strv_init_len(cstr, clen)
#define strv__impl(_1, _2, n, ...) strv__##n
#define strv(...) strv__check(__VA_ARGS__) ? strv__init_literal(__VA_ARGS__) : strv__impl(__VA_ARGS__, 2, 1, 0)(__VA_ARGS__)
#define cstrv(cstr) { cstr, sizeof(cstr) - 1, }
strview_t strv_init(const char *cstr);
strview_t strv_init_len(const char *buf, usize size);
strview_t strv_init_str(str_t str);
bool strv_is_empty(strview_t ctx);
bool strv_equals(strview_t a, strview_t b);
int strv_compare(strview_t a, strview_t b);
char strv_front(strview_t ctx);
char strv_back(strview_t ctx);
str16_t strv_to_str16(arena_t *arena, strview_t src);
tstr_t strv_to_tstr(arena_t *arena, strview_t src);
str_t strv_to_upper(arena_t *arena, strview_t src);
str_t strv_to_lower(arena_t *arena, strview_t src);
strview_t strv_remove_prefix(strview_t ctx, usize n);
strview_t strv_remove_suffix(strview_t ctx, usize n);
strview_t strv_trim(strview_t ctx);
strview_t strv_trim_left(strview_t ctx);
strview_t strv_trim_right(strview_t ctx);
strview_t strv_sub(strview_t ctx, usize from, usize to);
bool strv_starts_with(strview_t ctx, char c);
bool strv_starts_with_view(strview_t ctx, strview_t view);
bool strv_ends_with(strview_t ctx, char c);
bool strv_ends_with_view(strview_t ctx, strview_t view);
bool strv_contains(strview_t ctx, char c);
bool strv_contains_view(strview_t ctx, strview_t view);
bool strv_contains_either(strview_t ctx, strview_t chars);
usize strv_find(strview_t ctx, char c, usize from);
usize strv_find_view(strview_t ctx, strview_t view, usize from);
usize strv_find_either(strview_t ctx, strview_t chars, usize from);
usize strv_rfind(strview_t ctx, char c, usize from_right);
usize strv_rfind_view(strview_t ctx, strview_t view, usize from_right);
// CTYPE ////////////////////////////////////////
bool char_is_space(char c);
bool char_is_alpha(char c);
bool char_is_num(char c);
char char_lower(char c);
char char_upper(char c);
// INPUT STREAM /////////////////////////////////
typedef struct instream_t instream_t;
struct instream_t {
const char *beg;
const char *cur;
usize len;
};
instream_t istr_init(strview_t str);
// get the current character and advance
char istr_get(instream_t *ctx);
// get the current character but don't advance
char istr_peek(instream_t *ctx);
// get the next character but don't advance
char istr_peek_next(instream_t *ctx);
// returns the previous character
char istr_prev(instream_t *ctx);
// returns the character before the previous
char istr_prev_prev(instream_t *ctx);
// ignore characters until the delimiter
void istr_ignore(instream_t *ctx, char delim);
// ignore characters until the delimiter and skip it
void istr_ignore_and_skip(instream_t *ctx, char delim);
// skip n characters
void istr_skip(instream_t *ctx, usize n);
// skips whitespace (' ', '\\n', '\\t', '\\r')
void istr_skip_whitespace(instream_t *ctx);
// returns to the beginning of the stream
void istr_rewind(instream_t *ctx);
// returns back <amount> characters
void istr_rewind_n(instream_t *ctx, usize amount);
// returns the number of bytes read from beginning of stream
usize istr_tell(instream_t *ctx);
// returns the number of bytes left to read in the stream
usize istr_remaining(instream_t *ctx);
// return true if the stream doesn't have any new bytes to read
bool istr_is_finished(instream_t *ctx);
bool istr_get_bool(instream_t *ctx, bool *val);
bool istr_get_u8(instream_t *ctx, u8 *val);
bool istr_get_u16(instream_t *ctx, u16 *val);
bool istr_get_u32(instream_t *ctx, u32 *val);
bool istr_get_u64(instream_t *ctx, u64 *val);
bool istr_get_i8(instream_t *ctx, i8 *val);
bool istr_get_i16(instream_t *ctx, i16 *val);
bool istr_get_i32(instream_t *ctx, i32 *val);
bool istr_get_i64(instream_t *ctx, i64 *val);
bool istr_get_num(instream_t *ctx, double *val);
bool istr_get_float(instream_t *ctx, float *val);
strview_t istr_get_view(instream_t *ctx, char delim);
strview_t istr_get_view_either(instream_t *ctx, strview_t chars);
strview_t istr_get_view_len(instream_t *ctx, usize len);
strview_t istr_get_line(instream_t *ctx);
// OUTPUT STREAM ////////////////////////////////
typedef struct outstream_t outstream_t;
struct outstream_t {
char *beg;
arena_t *arena;
};
outstream_t ostr_init(arena_t *exclusive_arena);
void ostr_clear(outstream_t *ctx);
usize ostr_tell(outstream_t *ctx);
char ostr_back(outstream_t *ctx);
str_t ostr_to_str(outstream_t *ctx);
strview_t ostr_as_view(outstream_t *ctx);
void ostr_pop(outstream_t *ctx, usize count);
void ostr_print(outstream_t *ctx, const char *fmt, ...);
void ostr_printv(outstream_t *ctx, const char *fmt, va_list args);
void ostr_putc(outstream_t *ctx, char c);
void ostr_puts(outstream_t *ctx, strview_t v);
void ostr_append_bool(outstream_t *ctx, bool val);
void ostr_append_uint(outstream_t *ctx, u64 val);
void ostr_append_int(outstream_t *ctx, i64 val);
void ostr_append_num(outstream_t *ctx, double val);
// INPUT BINARY STREAM //////////////////////////
typedef struct {
const u8 *beg;
const u8 *cur;
usize len;
} ibstream_t;
ibstream_t ibstr_init(buffer_t buffer);
bool ibstr_is_finished(ibstream_t *ib);
usize ibstr_tell(ibstream_t *ib);
usize ibstr_remaining(ibstream_t *ib);
usize ibstr_read(ibstream_t *ib, void *buffer, usize len);
void ibstr_skip(ibstream_t *ib, usize count);
bool ibstr_get_u8(ibstream_t *ib, u8 *out);
bool ibstr_get_u16(ibstream_t *ib, u16 *out);
bool ibstr_get_u32(ibstream_t *ib, u32 *out);
bool ibstr_get_u64(ibstream_t *ib, u64 *out);
bool ibstr_get_i8(ibstream_t *ib, i8 *out);
bool ibstr_get_i16(ibstream_t *ib, i16 *out);
bool ibstr_get_i32(ibstream_t *ib, i32 *out);
bool ibstr_get_i64(ibstream_t *ib, i64 *out);
// ARENA ////////////////////////////////////////
#if COLLA_WIN && !COLLA_TCC
#define alignof __alignof
#endif
typedef enum arena_type_e {
ARENA_TYPE_NONE, // only here so that a 0 initialised arena is valid
ARENA_VIRTUAL,
ARENA_MALLOC,
ARENA_MALLOC_ALWAYS,
ARENA_STATIC,
} arena_type_e;
typedef enum alloc_flags_e {
ALLOC_FLAGS_NONE = 0,
ALLOC_NOZERO = 1 << 0,
ALLOC_SOFT_FAIL = 1 << 1,
} alloc_flags_e;
typedef struct arena_t arena_t;
struct arena_t {
u8 *beg;
u8 *cur;
u8 *end;
arena_type_e type;
};
typedef struct arena_desc_t arena_desc_t;
struct arena_desc_t {
arena_type_e type;
usize size;
u8 *static_buffer;
};
typedef struct arena_alloc_desc_t arena_alloc_desc_t;
struct arena_alloc_desc_t {
arena_t *arena;
usize count;
alloc_flags_e flags;
usize align;
usize size;
};
// arena_type_e type, usize allocation, [ byte *static_buffer ]
#define arena_make(...) arena_init(&(arena_desc_t){ __VA_ARGS__ })
// arena_t *arena, T type, [ usize count, alloc_flags_e flags, usize align, usize size ]
#define alloc(arenaptr, type, ...) arena_alloc(&(arena_alloc_desc_t){ .size = sizeof(type), .count = 1, .align = alignof(type), .arena = arenaptr, __VA_ARGS__ })
// simple arena that always calls malloc internally, this is useful if you need
// malloc for some reason but want to still use the arena interface
// WARN: most arena functions outside of alloc/scratch won't work!
// you also need to each allocation afterwards! this is still
// malloc
extern arena_t malloc_arena;
arena_t arena_init(const arena_desc_t *desc);
void arena_cleanup(arena_t *arena);
arena_t arena_scratch(arena_t *arena, usize size);
void *arena_alloc(const arena_alloc_desc_t *desc);
usize arena_tell(arena_t *arena);
usize arena_remaining(arena_t *arena);
usize arena_capacity(arena_t *arena);
void arena_rewind(arena_t *arena, usize from_start);
void arena_pop(arena_t *arena, usize amount);
// OS LAYER /////////////////////////////////////
#define OS_ARENA_SIZE (MB(1))
#define OS_WAIT_INFINITE (0xFFFFFFFF)
typedef struct oshandle_t oshandle_t;
struct oshandle_t {
uptr data;
};
typedef struct os_system_info_t os_system_info_t;
struct os_system_info_t {
u32 processor_count;
u64 page_size;
str_t machine_name;
};
void os_init(void);
void os_cleanup(void);
os_system_info_t os_get_system_info(void);
void os_abort(int code);
iptr os_get_last_error(void);
// NOT thread safe
str_t os_get_error_string(iptr error);
// == HANDLE ====================================
oshandle_t os_handle_zero(void);
bool os_handle_match(oshandle_t a, oshandle_t b);
bool os_handle_valid(oshandle_t handle);
#define OS_MAX_WAITABLE_HANDLES 256
typedef enum {
OS_WAIT_FINISHED,
OS_WAIT_ABANDONED,
OS_WAIT_TIMEOUT,
OS_WAIT_FAILED
} os_wait_result_e;
typedef struct {
os_wait_result_e result;
u32 index;
} os_wait_t;
os_wait_t os_wait_on_handles(oshandle_t *handles, int count, bool wait_all, u32 milliseconds);
// == LOGGING ===================================
typedef enum os_log_level_e {
LOG_BASIC,
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERR,
LOG_FATAL,
} os_log_level_e;
typedef enum os_log_colour_e {
LOG_COL_BLACK = 0,
LOG_COL_BLUE = 1,
LOG_COL_GREEN = 2,
LOG_COL_CYAN = LOG_COL_BLUE | LOG_COL_GREEN,
LOG_COL_RED = 4,
LOG_COL_MAGENTA = LOG_COL_RED | LOG_COL_BLUE,
LOG_COL_YELLOW = LOG_COL_RED | LOG_COL_GREEN,
LOG_COL_GREY = LOG_COL_RED | LOG_COL_BLUE | LOG_COL_GREEN,
LOG_COL_LIGHT = 8,
LOG_COL_DARK_GREY = LOG_COL_BLACK | LOG_COL_LIGHT,
LOG_COL_LIGHT_BLUE = LOG_COL_BLUE | LOG_COL_LIGHT,
LOG_COL_LIGHT_GREEN = LOG_COL_GREEN | LOG_COL_LIGHT,
LOG_COL_LIGHT_CYAN = LOG_COL_CYAN | LOG_COL_LIGHT,
LOG_COL_LIGHT_RED = LOG_COL_RED | LOG_COL_LIGHT,
LOG_COL_LIGHT_MAGENTA = LOG_COL_MAGENTA | LOG_COL_LIGHT,
LOG_COL_LIGHT_YELLOW = LOG_COL_YELLOW | LOG_COL_LIGHT,
LOG_COL_WHITE = LOG_COL_GREY | LOG_COL_LIGHT,
LOG_COL_RESET,
LOG_COL__COUNT,
} os_log_colour_e;
void os_log_print(os_log_level_e level, const char *fmt, ...);
void os_log_printv(os_log_level_e level, const char *fmt, va_list args);
void os_log_set_colour(os_log_colour_e colour);
void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background);
oshandle_t os_stdout(void);
oshandle_t os_stdin(void);
#define print(...) fmt_print(__VA_ARGS__)
#define println(...) os_log_print(LOG_BASIC, __VA_ARGS__)
#define debug(...) os_log_print(LOG_DEBUG, __VA_ARGS__)
#define info(...) os_log_print(LOG_INFO, __VA_ARGS__)
#define warn(...) os_log_print(LOG_WARN, __VA_ARGS__)
#define err(...) os_log_print(LOG_ERR, __VA_ARGS__)
#define fatal(...) os_log_print(LOG_FATAL, __VA_ARGS__)
// == FILE ======================================
typedef enum filemode_e {
OS_FILE_READ = 1 << 0,
OS_FILE_WRITE = 1 << 1,
} filemode_e;
bool os_file_exists(strview_t filename);
bool os_dir_exists(strview_t folder);
bool os_file_or_dir_exists(strview_t path);
bool os_dir_create(strview_t folder);
tstr_t os_file_fullpath(arena_t *arena, strview_t filename);
void os_file_split_path(strview_t path, strview_t *dir, strview_t *name, strview_t *ext);
bool os_file_delete(strview_t path);
bool os_dir_delete(strview_t path);
oshandle_t os_file_open(strview_t path, filemode_e mode);
void os_file_close(oshandle_t handle);
bool os_file_putc(oshandle_t handle, char c);
bool os_file_puts(oshandle_t handle, strview_t str);
bool os_file_print(arena_t scratch, oshandle_t handle, const char *fmt, ...);
bool os_file_printv(arena_t scratch, oshandle_t handle, const char *fmt, va_list args);
usize os_file_read(oshandle_t handle, void *buf, usize len);
usize os_file_write(oshandle_t handle, const void *buf, usize len);
usize os_file_read_buf(oshandle_t handle, buffer_t *buf);
usize os_file_write_buf(oshandle_t handle, buffer_t buf);
bool os_file_seek(oshandle_t handle, usize offset);
bool os_file_seek_end(oshandle_t handle);
void os_file_rewind(oshandle_t handle);
usize os_file_tell(oshandle_t handle);
usize os_file_size(oshandle_t handle);
bool os_file_is_finished(oshandle_t handle);
buffer_t os_file_read_all(arena_t *arena, strview_t path);
buffer_t os_file_read_all_fp(arena_t *arena, oshandle_t handle);
str_t os_file_read_all_str(arena_t *arena, strview_t path);
str_t os_file_read_all_str_fp(arena_t *arena, oshandle_t handle);
bool os_file_write_all(strview_t name, buffer_t buffer);
bool os_file_write_all_fp(oshandle_t handle, buffer_t buffer);
bool os_file_write_all_str(strview_t name, strview_t data);
bool os_file_write_all_str_fp(oshandle_t handle, strview_t data);
u64 os_file_time(strview_t path);
u64 os_file_time_fp(oshandle_t handle);
bool os_file_has_changed(strview_t path, u64 last_change);
// == DIR WALKER ================================
typedef enum dir_type_e {
DIRTYPE_FILE,
DIRTYPE_DIR,
} dir_type_e;
typedef struct dir_entry_t dir_entry_t;
struct dir_entry_t {
str_t name;
dir_type_e type;
usize file_size;
};
#define dir_foreach(arena, it, dir) for (dir_entry_t *it = os_dir_next(arena, dir); it; it = os_dir_next(arena, dir))
typedef struct dir_t dir_t;
dir_t *os_dir_open(arena_t *arena, strview_t path);
bool os_dir_is_valid(dir_t *dir);
// optional, only call this if you want to return before os_dir_next returns NULL
void os_dir_close(dir_t *dir);
dir_entry_t *os_dir_next(arena_t *arena, dir_t *dir);
// == PROCESS ===================================
typedef struct os_env_t os_env_t;
typedef strv_list_t os_cmd_t;
#define os_make_cmd(...) &(os_cmd_t){ .items = (strview_t[]){ __VA_ARGS__ }, .count = arrlen(((strview_t[]){ __VA_ARGS__ })) }
typedef struct os_cmd_options_t os_cmd_options_t;
struct os_cmd_options_t {
os_env_t *env;
// redirected if !NULL
oshandle_t *error;
oshandle_t *out;
oshandle_t *in;
};
void os_set_env_var(arena_t scratch, strview_t key, strview_t value);
str_t os_get_env_var(arena_t *arena, strview_t key);
os_env_t *os_get_env(arena_t *arena);
bool os_run_cmd(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *options);
oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *options);
bool os_process_wait(oshandle_t proc, uint time, int *out_exit);
// == MEMORY ====================================
void *os_alloc(usize size);
void os_free(void *ptr);
void *os_reserve(usize size, usize *out_padded_size);
bool os_commit(void *ptr, usize num_of_pages);
bool os_release(void *ptr, usize size);
usize os_pad_to_page(usize byte_count);
// == THREAD ====================================
typedef int (thread_func_t)(u64 thread_id, void *userdata);
oshandle_t os_thread_launch(thread_func_t func, void *userdata);
bool os_thread_detach(oshandle_t thread);
bool os_thread_join(oshandle_t thread, int *code);
u64 os_thread_get_id(oshandle_t thread);
// == MUTEX =====================================
oshandle_t os_mutex_create(void);
void os_mutex_free(oshandle_t mutex);
void os_mutex_lock(oshandle_t mutex);
void os_mutex_unlock(oshandle_t mutex);
bool os_mutex_try_lock(oshandle_t mutex);
#if !COLLA_NO_CONDITION_VARIABLE
// == CONDITION VARIABLE ========================
oshandle_t os_cond_create(void);
void os_cond_free(oshandle_t cond);
void os_cond_signal(oshandle_t cond);
void os_cond_broadcast(oshandle_t cond);
void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds);
#endif
// PARSERS //////////////////////////////////////
// == INI ============================================
typedef struct inivalue_t inivalue_t;
struct inivalue_t {
strview_t key;
strview_t value;
inivalue_t *next;
};
typedef struct initable_t initable_t;
struct initable_t {
strview_t name;
inivalue_t *values;
inivalue_t *tail;
initable_t *next;
};
typedef struct ini_t ini_t;
struct ini_t {
strview_t text;
initable_t *tables;
initable_t *tail;
};
typedef struct iniopt_t iniopt_t;
struct iniopt_t {
bool merge_duplicate_tables; // default false
bool merge_duplicate_keys; // default false
char key_value_divider; // default =
strview_t comment_vals; // default ;#
};
typedef struct iniarray_t iniarray_t;
struct iniarray_t {
strview_t *values;
usize count;
};
#define INI_ROOT strv("__ROOT__")
ini_t ini_parse(arena_t *arena, strview_t filename, iniopt_t *opt);
ini_t ini_parse_fp(arena_t *arena, oshandle_t file, iniopt_t *opt);
ini_t ini_parse_str(arena_t *arena, strview_t str, iniopt_t *opt);
bool ini_is_valid(ini_t *ini);
initable_t *ini_get_table(ini_t *ini, strview_t name);
inivalue_t *ini_get(initable_t *table, strview_t key);
iniarray_t ini_as_arr(arena_t *arena, inivalue_t *value, char delim);
u64 ini_as_uint(inivalue_t *value);
i64 ini_as_int(inivalue_t *value);
double ini_as_num(inivalue_t *value);
bool ini_as_bool(inivalue_t *value);
typedef enum {
INI_PRETTY_COLOUR_KEY,
INI_PRETTY_COLOUR_VALUE,
INI_PRETTY_COLOUR_DIVIDER,
INI_PRETTY_COLOUR_TABLE,
INI_PRETTY_COLOUR__COUNT,
} ini_pretty_colours_e;
typedef struct ini_pretty_opts_t ini_pretty_opts_t;
struct ini_pretty_opts_t {
oshandle_t custom_target;
bool use_custom_colours;
os_log_colour_e colours[INI_PRETTY_COLOUR__COUNT];
};
void ini_pretty_print(ini_t *ini, const ini_pretty_opts_t *options);
// == JSON ===========================================
typedef enum jsontype_e {
JSON_NULL,
JSON_ARRAY,
JSON_STRING,
JSON_NUMBER,
JSON_BOOL,
JSON_OBJECT,
} jsontype_e;
typedef enum jsonflags_e {
JSON_DEFAULT = 0,
JSON_NO_TRAILING_COMMAS = 1 << 0,
JSON_NO_COMMENTS = 1 << 1,
} jsonflags_e;
typedef struct json_t json_t;
struct json_t {
json_t *next;
json_t *prev;
strview_t key;
union {
json_t *array;
strview_t string;
double number;
bool boolean;
json_t *object;
};
jsontype_e type;
};
json_t *json_parse(arena_t *arena, strview_t filename, jsonflags_e flags);
json_t *json_parse_str(arena_t *arena, strview_t str, jsonflags_e flags);
json_t *json_get(json_t *node, strview_t key);
#define json_check(val, js_type) ((val) && (val)->type == js_type)
#define json_for(it, arr) for (json_t *it = json_check(arr, JSON_ARRAY) ? arr->array : NULL; it; it = it->next)
typedef enum json_pretty_colours_e {
JSON_PRETTY_COLOUR_KEY,
JSON_PRETTY_COLOUR_STRING,
JSON_PRETTY_COLOUR_NUM,
JSON_PRETTY_COLOUR_NULL,
JSON_PRETTY_COLOUR_TRUE,
JSON_PRETTY_COLOUR_FALSE,
JSON_PRETTY_COLOUR__COUNT,
} json_pretty_colours_e;
typedef struct json_pretty_opts_t json_pretty_opts_t;
struct json_pretty_opts_t {
oshandle_t custom_target;
bool use_custom_colours;
os_log_colour_e colours[JSON_PRETTY_COLOUR__COUNT];
};
void json_pretty_print(json_t *root, const json_pretty_opts_t *options);
// == XML ============================================
typedef struct xmlattr_t xmlattr_t;
struct xmlattr_t {
strview_t key;
strview_t value;
xmlattr_t *next;
};
typedef struct xmltag_t xmltag_t;
struct xmltag_t {
strview_t key;
xmlattr_t *attributes;
strview_t content;
xmltag_t *child;
xmltag_t *tail;
xmltag_t *next;
};
typedef struct xml_t xml_t;
struct xml_t {
strview_t text;
xmltag_t *root;
xmltag_t *tail;
};
xml_t xml_parse(arena_t *arena, strview_t filename);
xml_t xml_parse_str(arena_t *arena, strview_t xmlstr);
xmltag_t *xml_get_tag(xmltag_t *parent, strview_t key, bool recursive);
strview_t xml_get_attribute(xmltag_t *tag, strview_t key);
// == HTML ===========================================
typedef struct htmltag_t htmltag_t;
struct htmltag_t {
str_t key;
xmlattr_t *attributes;
strview_t content;
htmltag_t *children;
htmltag_t *tail;
htmltag_t *next;
};
typedef struct html_t html_t;
struct html_t {
strview_t text;
htmltag_t *root;
htmltag_t *tail;
};
html_t html_parse(arena_t *arena, strview_t filename);
html_t html_parse_str(arena_t *arena, strview_t str);
htmltag_t *html_get_tag(htmltag_t *parent, strview_t key, bool recursive);
strview_t html_get_attribute(htmltag_t *tag, strview_t key);
// NETWORKING ///////////////////////////////////
void net_init(void);
void net_cleanup(void);
iptr net_get_last_error(void);
typedef enum http_method_e {
HTTP_GET,
HTTP_POST,
HTTP_HEAD,
HTTP_PUT,
HTTP_DELETE,
HTTP_METHOD__COUNT,
} http_method_e;
const char *http_get_method_string(http_method_e method);
const char *http_get_status_string(int status);
typedef struct http_version_t http_version_t;
struct http_version_t {
u8 major;
u8 minor;
};
typedef struct http_header_t http_header_t;
struct http_header_t {
strview_t key;
strview_t value;
http_header_t *next;
};
typedef struct http_req_t http_req_t;
struct http_req_t {
http_method_e method;
http_version_t version;
http_header_t *headers;
strview_t url;
strview_t body;
};
typedef struct http_res_t http_res_t;
struct http_res_t {
int status_code;
http_version_t version;
http_header_t *headers;
strview_t body;
};
http_header_t *http_parse_headers(arena_t *arena, strview_t header_string);
http_req_t http_parse_req(arena_t *arena, strview_t request);
http_res_t http_parse_res(arena_t *arena, strview_t response);
str_t http_req_to_str(arena_t *arena, http_req_t *req);
str_t http_res_to_str(arena_t *arena, http_res_t *res);
bool http_has_header(http_header_t *headers, strview_t key);
void http_set_header(http_header_t *headers, strview_t key, strview_t value);
strview_t http_get_header(http_header_t *headers, strview_t key);
str_t http_make_url_safe(arena_t *arena, strview_t string);
str_t http_decode_url_safe(arena_t *arena, strview_t string);
typedef struct {
strview_t host;
strview_t uri;
} http_url_t;
http_url_t http_split_url(strview_t url);
typedef struct {
arena_t *arena;
strview_t url;
http_version_t version; // 1.1 by default
http_method_e request_type;
http_header_t *headers;
int header_count; // optional, if set to 0 it traverses headers using h->next
strview_t body;
} http_request_desc_t;
typedef void (*http_request_callback_fn)(strview_t chunk, void *udata);
// arena_t *arena, strview_t url, [ http_header_t *headers, int header_count, strview_t body ]
#define http_get(arena, url, ...) http_request(&(http_request_desc_t){ arena, url, .request_type = HTTP_GET, .version = { 1, 1 }, __VA_ARGS__ })
http_res_t http_request(http_request_desc_t *request);
http_res_t http_request_cb(http_request_desc_t *request, http_request_callback_fn callback, void *userdata);
// SOCKETS //////////////////////////
typedef uintptr_t socket_t;
typedef struct skpoll_t skpoll_t;
#define SK_ADDR_LOOPBACK "127.0.0.1"
#define SK_ADDR_ANY "0.0.0.0"
#define SK_ADDR_BROADCAST "255.255.255.255"
struct skpoll_t {
uintptr_t socket;
short events;
short revents;
};
#define SOCKET_ERROR (-1)
typedef enum {
SOCK_TCP,
SOCK_UDP,
} sktype_e;
typedef enum {
SOCK_EVENT_NONE,
SOCK_EVENT_READ = 1 << 0,
SOCK_EVENT_WRITE = 1 << 1,
SOCK_EVENT_ACCEPT = 1 << 2,
SOCK_EVENT_CONNECT = 1 << 3,
SOCK_EVENT_CLOSE = 1 << 4,
} skevent_e;
// Opens a socket
socket_t sk_open(sktype_e type);
// Opens a socket using 'protocol', options are
// ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp
socket_t sk_open_protocol(const char *protocol);
// Checks that a opened socket is valid, returns true on success
bool sk_is_valid(socket_t sock);
// Closes a socket, returns true on success
bool sk_close(socket_t sock);
// Fill out a sk_addrin_t structure with "ip" and "port"
// skaddrin_t sk_addrin_init(const char *ip, uint16_t port);
// Associate a local address with a socket
bool sk_bind(socket_t sock, const char *ip, u16 port);
// Place a socket in a state in which it is listening for an incoming connection
bool sk_listen(socket_t sock, int backlog);
// Permits an incoming connection attempt on a socket
socket_t sk_accept(socket_t sock);
// Connects to a server (e.g. "127.0.0.1" or "google.com") with a port(e.g. 1234), returns true on success
bool sk_connect(socket_t sock, const char *server, u16 server_port);
// Sends data on a socket, returns true on success
int sk_send(socket_t sock, const void *buf, int len);
// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
int sk_recv(socket_t sock, void *buf, int len);
// Wait for an event on some sockets
int sk_poll(skpoll_t *to_poll, int num_to_poll, int timeout);
oshandle_t sk_bind_event(socket_t sock, skevent_e event);
void sk_destroy_event(oshandle_t handle);
void sk_reset_event(oshandle_t handle);
// WEBSOCKETS ///////////////////////
bool websocket_init(arena_t scratch, socket_t socket, strview_t key);
buffer_t websocket_encode(arena_t *arena, strview_t message);
str_t websocket_decode(arena_t *arena, buffer_t message);
// SHA 1 ////////////////////////////
typedef struct sha1_t sha1_t;
struct sha1_t {
u32 digest[5];
u8 block[64];
usize block_index;
usize byte_count;
};
typedef struct sha1_result_t sha1_result_t;
struct sha1_result_t {
u32 digest[5];
};
sha1_t sha1_init(void);
sha1_result_t sha1(sha1_t *ctx, const void *buf, usize len);
str_t sha1_str(arena_t *arena, sha1_t *ctx, const void *buf, usize len);
// BASE 64 //////////////////////////
buffer_t base64_encode(arena_t *arena, buffer_t buffer);
buffer_t base64_decode(arena_t *arena, buffer_t buffer);
// PRETTY PRINTING //////////////////////////////
strview_t pretty_log_to_colour(os_log_colour_e colour);
void pretty_print(arena_t scratch, const char *fmt, ...);
void pretty_printv(arena_t scratch, const char *fmt, va_list args);
str_t pretty_print_get_string(arena_t *arena, const char *fmt, ...);
str_t pretty_print_get_stringv(arena_t *arena, const char *fmt, va_list args);
#endif