windows stuff

This commit is contained in:
alessandro bason 2025-10-11 20:35:40 +02:00
parent 7e7c371b9e
commit ae3536529b
3 changed files with 587 additions and 106 deletions

418
colla.c
View file

@ -5,6 +5,7 @@
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#if COLLA_TCC #if COLLA_TCC
#define COLLA_NO_CONDITION_VARIABLE 1 #define COLLA_NO_CONDITION_VARIABLE 1
@ -34,6 +35,13 @@
colla_modules_e colla__initialised_modules = 0; colla_modules_e colla__initialised_modules = 0;
extern void os_init(void);
extern void os_cleanup(void);
#if !COLLA_NO_NET
extern void net_init(void);
extern void net_cleanup(void);
#endif
static char *colla_fmt__stb_callback(const char *buf, void *ud, int len) { static char *colla_fmt__stb_callback(const char *buf, void *ud, int len) {
// TODO maybe use os_write? // TODO maybe use os_write?
fflush(stdout); fflush(stdout);
@ -284,6 +292,18 @@ int strv_compare(strview_t a, strview_t b) {
(int)(a.len - b.len); (int)(a.len - b.len);
} }
usize strv_get_utf8_len(strview_t v) {
usize len = 0;
for (usize i = 0; i < v.len; ++i) {
if ((v.buf[i] & 0xC0) != 0x80) {
len++;
}
}
return len;
}
char strv_front(strview_t ctx) { char strv_front(strview_t ctx) {
return ctx.len > 0 ? ctx.buf[0] : '\0'; return ctx.len > 0 ? ctx.buf[0] : '\0';
} }
@ -662,7 +682,7 @@ bool istr_get_i32(instream_t *ctx, i32 *val) {
bool istr_get_i64(instream_t *ctx, i64 *val) { bool istr_get_i64(instream_t *ctx, i64 *val) {
if (!ctx || !ctx->cur || !val) return false; if (!ctx || !ctx->cur || !val) return false;
char *end = NULL; char *end = NULL;
*val = strtoll(ctx->cur, &end, 0); i64 out = strtoll(ctx->cur, &end, 0);
if (ctx->cur == end) { if (ctx->cur == end) {
return false; return false;
@ -672,6 +692,7 @@ bool istr_get_i64(instream_t *ctx, i64 *val) {
} }
ctx->cur = end; ctx->cur = end;
*val = out;
return true; return true;
} }
@ -897,6 +918,91 @@ bool ibstr_get_i64(ibstream_t *ib, i64 *out) {
return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out); return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
} }
// == REGEX ========================================================
bool rg__match_impl(rg_match_t *ctx, instream_t *r, instream_t *t) {
bool match_any = false;
usize beg = STR_END;
usize end = STR_END;
while (!istr_is_finished(r) && !istr_is_finished(t)) {
char rc = istr_peek(r);
char tc = istr_peek(t);
if (rc == '\\') {
if (istr_peek_next(r) != '*' && istr_peek_next(r) != '\\') {
warn("expected * or \\ after escape character");
return false;
}
istr_skip(r, 1);
continue;
}
if (istr_peek_next(r) == '*' && rc == tc) {
match_any = true;
istr_skip(r, 2);
istr_skip(t, 1);
beg = istr_tell(t);
continue;
}
if (rc == '*') {
match_any = true;
istr_skip(r, 1);
istr_skip(t, 1);
beg = istr_tell(t);
continue;
}
if (rc == tc) {
if (match_any && istr_peek_next(r) == istr_peek_next(t)) {
end = istr_tell(t);
ctx->text[ctx->count++] = strv(t->beg + beg, end - beg);
beg = STR_END;
match_any = false;
}
istr_skip(r, 1);
istr_skip(t, 1);
continue;
}
if (match_any) {
istr_skip(t, 1);
continue;
}
return false;
}
if (match_any && istr_is_finished(r)) {
end = t->len;
ctx->text[ctx->count++] = strv(t->beg + beg, end - beg);
return true;
}
return ctx->count > 0 && istr_is_finished(r) && istr_is_finished(t);
}
rg_match_t rg_match(strview_t rg, strview_t text) {
rg_match_t out = {0};
if (strv_contains(rg, '*')) {
instream_t r = istr_init(rg);
instream_t t = istr_init(text);
out.matches = rg__match_impl(&out, &r, &t);
}
else {
out.matches = strv_equals(rg, text);
}
return out;
}
bool rg_match_easy(strview_t rg, strview_t text) {
return rg_match(rg, text).matches;
}
// == ARENA ========================================================
static uptr arena__align(uptr ptr, usize align) { static uptr arena__align(uptr ptr, usize align) {
return (ptr + (align - 1)) & ~(align - 1); return (ptr + (align - 1)) & ~(align - 1);
} }
@ -1036,6 +1142,7 @@ static void arena__free_virtual(arena_t *arena) {
os_release(arena->beg, arena_capacity(arena)); os_release(arena->beg, arena_capacity(arena));
} }
// == MALLOC ARENA ===================================================================================================== // == MALLOC ARENA =====================================================================================================
static arena_t arena__make_malloc(usize size) { static arena_t arena__make_malloc(usize size) {
@ -1134,47 +1241,138 @@ bool os_handle_valid(oshandle_t handle) {
// == LOGGING =================================== // == LOGGING ===================================
os_log_colour_e log__level_to_colour(os_log_level_e level) { const char* os_log_level_strings[LOG_COL__COUNT] = {
os_log_colour_e colour = LOG_COL_RESET; [LOG_DEBUG] = "[DEBUG]:",
switch (level) { [LOG_INFO] = "[INFO]:",
case LOG_DEBUG: colour = LOG_COL_BLUE; break; [LOG_WARN] = "[WARN]:",
case LOG_INFO: colour = LOG_COL_GREEN; break; [LOG_ERR] = "[ERR]:",
case LOG_WARN: colour = LOG_COL_YELLOW; break; [LOG_FATAL] = "[FATAL]:",
case LOG_ERR: colour = LOG_COL_MAGENTA; break; };
case LOG_FATAL: colour = LOG_COL_RED; break;
default: break; os_log_colour_e os_log_level_colours[LOG_COL__COUNT] = {
} [LOG_DEBUG] = LOG_COL_BLUE,
return colour; [LOG_INFO] = LOG_COL_GREEN,
[LOG_WARN] = LOG_COL_YELLOW,
[LOG_ERR] = LOG_COL_MAGENTA,
[LOG_FATAL] = LOG_COL_RED,
};
os_log_options_e log_opts = OS_LOG_SIMPLE;
log_callback_t log_cbs[COLLA_LOG_MAX_CALLBACKS] = {0};
int log_cbs_count = 0;
void os_log__stdout(log_event_t *ev) {
bool notime = log_opts & OS_LOG_NOTIME;
bool nofile = log_opts & OS_LOG_NOFILE;
if (!notime) {
os_log_set_colour(LOG_COL_DARK_GREY);
fmt_print(
"%02d:%02d:%02d ",
ev->time->tm_hour,
ev->time->tm_min,
ev->time->tm_sec
);
} }
void os_log_print(os_log_level_e level, const char *fmt, ...) { os_log_set_colour(os_log_level_colours[ev->level]);
if (ev->level != LOG_BASIC) {
fmt_print("%-8s ", os_log_level_strings[ev->level]);
}
if (!nofile) {
os_log_set_colour(LOG_COL_DARK_GREY);
fmt_print("%s:%d ", ev->file, ev->line);
}
os_log_set_colour(LOG_COL_RESET);
fmt_printv(ev->fmt, ev->args);
fmt_print("\n");
}
void os_log__fp(log_event_t *ev) {
u8 tmpbuf[KB(1)] = {0};
arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
oshandle_t fp = {0};
fp.data = (uptr)ev->udata;
bool notime = log_opts & OS_LOG_NOTIME;
bool nofile = log_opts & OS_LOG_NOFILE;
if (!notime) {
os_file_print(
scratch,
fp,
"%02d:%02d:%02d ",
ev->time->tm_hour,
ev->time->tm_min,
ev->time->tm_sec
);
}
if (ev->level != LOG_BASIC) {
os_file_print(scratch, fp, "%-8s ", os_log_level_strings[ev->level]);
}
if (!nofile) {
os_file_print(scratch, fp, "%s:%d ", ev->file, ev->line);
}
os_file_printv(scratch, fp, ev->fmt, ev->args);
os_file_putc(fp, '\n');
}
void os_log_add_callback(log_callback_t cb) {
colla_assert(log_cbs_count < arrlen(log_cbs));
log_cbs[log_cbs_count++] = cb;
}
void os_log_add_fp(oshandle_t fp, os_log_level_e level) {
os_log_add_callback((log_callback_t){
.fn = os_log__fp,
.udata = (void*)fp.data,
.level = level
});
}
void os__log_init(void) {
os_log_add_callback((log_callback_t){ .fn = os_log__stdout });
}
void os_log_print(const char *file, int line, os_log_level_e level, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
os_log_printv(level, fmt, args); os_log_printv(file, line, level, fmt, args);
va_end(args); va_end(args);
} }
void os_log_printv(os_log_level_e level, const char *fmt, va_list args) { void os_log_printv(const char *file, int line, os_log_level_e level, const char *fmt, va_list args) {
const char *level_str = ""; time_t t = time(NULL);
switch (level) { struct tm log_time = { 0 };
case LOG_DEBUG: level_str = "DEBUG"; break; #define gettime(src, dst) localtime_s(&dst, &src)
case LOG_INFO: level_str = "INFO"; break; log_event_t ev = {
case LOG_WARN: level_str = "WARN"; break; .level = level,
case LOG_ERR: level_str = "ERR"; break; .line = line,
case LOG_FATAL: level_str = "FATAL"; break; .args = args,
default: break; .fmt = fmt,
.file = file,
.time = &log_time,
};
localtime_s(&log_time, &t);
for (int i = 0; i < log_cbs_count; ++i) {
if (log_cbs[i].level > level) {
continue;
}
ev.udata = log_cbs[i].udata;
log_cbs[i].fn(&ev);
} }
os_log_set_colour(log__level_to_colour(level)); bool nocrash = log_opts & OS_LOG_NOCRASH;
if (level != LOG_BASIC) {
fmt_print("[%s]: ", level_str);
}
os_log_set_colour(LOG_COL_RESET);
fmt_printv(fmt, args); if (!nocrash && level == LOG_FATAL) {
fmt_print("\n");
if (level == LOG_FATAL) {
os_abort(1); os_abort(1);
} }
} }
@ -1346,6 +1544,103 @@ usize os_pad_to_page(usize byte_count) {
return byte_count + padding; return byte_count + padding;
} }
#if !COLLA_NO_CONDITION_VARIABLE
// == JOB QUEUE =================================
int jq__worker_function(u64 id, void *udata) {
job_queue_t *q = udata;
// TODO: is this safe to not have an atomic variable?
while (!q->should_stop) {
job_t *job = jq_pop_job(q);
if (!job) {
if (q->stop_when_finished) {
break;
}
continue;
}
job->func(job->userdata);
os_mutex_lock(q->mutex);
list_push(q->freelist, job);
os_mutex_unlock(q->mutex);
}
return 0;
}
job_queue_t *jq_init(arena_t *arena, int worker_count) {
job_queue_t *q = alloc(arena, job_queue_t);
q->mutex = os_mutex_create();
q->condvar = os_cond_create();
q->thread_count = worker_count ? worker_count : os_get_system_info().processor_count;
q->threads = alloc(arena, oshandle_t, q->thread_count);
for (int i = 0; i < q->thread_count; ++i) {
q->threads[i] = os_thread_launch(jq__worker_function, q);
}
return q;
}
void jq_stop(job_queue_t *queue) {
os_mutex_lock(queue->mutex);
job_t *remaining = queue->jobs;
list_push(queue->freelist, remaining);
queue->jobs = NULL;
queue->should_stop = true;
os_cond_broadcast(queue->condvar);
os_mutex_unlock(queue->mutex);
for (int i = 0; i < queue->thread_count; ++i) {
os_thread_join(queue->threads[i], NULL);
}
os_mutex_free(queue->mutex);
os_cond_free(queue->condvar);
}
void jq_cleanup(job_queue_t *queue) {
os_mutex_lock(queue->mutex);
queue->stop_when_finished = true;
os_cond_broadcast(queue->condvar);
os_mutex_unlock(queue->mutex);
for (int i = 0; i < queue->thread_count; ++i) {
os_thread_join(queue->threads[i], NULL);
}
os_mutex_free(queue->mutex);
os_cond_free(queue->condvar);
}
void jq_push(arena_t *arena, job_queue_t *queue, job_func_f *func, void *userdata) {
os_mutex_lock(queue->mutex);
job_t *job = queue->freelist;
if (!job) {
job = alloc(arena, job_t);
}
job->func = func;
job->userdata = userdata;
list_push(queue->jobs, job);
os_cond_broadcast(queue->condvar);
os_mutex_unlock(queue->mutex);
}
job_t *jq_pop_job(job_queue_t *queue) {
job_t *job = NULL;
os_mutex_lock(queue->mutex);
while (!queue->jobs && !queue->should_stop && !queue->stop_when_finished) {
os_cond_wait(queue->condvar, queue->mutex, OS_WAIT_INFINITE);
}
job = queue->jobs;
list_pop(queue->jobs);
os_mutex_unlock(queue->mutex);
return job;
}
#endif
// == INI ============================================ // == INI ============================================
void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options); void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options);
@ -1538,7 +1833,6 @@ iniopt_t ini__get_options(const iniopt_t *options) {
return out; return out;
} }
void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopt_t *opts) { void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopt_t *opts) {
colla_assert(table); colla_assert(table);
@ -1660,6 +1954,7 @@ void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options) {
// == JSON =========================================== // == JSON ===========================================
bool json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out); bool json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out);
bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out);
json_t *json_parse(arena_t *arena, strview_t filename, jsonflags_e flags) { json_t *json_parse(arena_t *arena, strview_t filename, jsonflags_e flags) {
str_t data = os_file_read_all_str(arena, filename); str_t data = os_file_read_all_str(arena, filename);
@ -1674,11 +1969,19 @@ json_t *json_parse_str(arena_t *arena, strview_t str, jsonflags_e flags) {
instream_t in = istr_init(str); instream_t in = istr_init(str);
if (flags & JSON_ONLY_OBJECT_START) {
if (!json__parse_obj(arena, &in, flags, &root->object)) { if (!json__parse_obj(arena, &in, flags, &root->object)) {
// reset arena // reset arena
*arena = before; *arena = before;
return NULL; return NULL;
} }
}
else {
if (!json__parse_value(arena, &in, flags, &root)) {
*arena = before;
return NULL;
}
}
return root; return root;
} }
@ -1733,8 +2036,6 @@ void json_pretty_print(json_t *root, const json_pretty_opts_t *options) {
#define json__ensure(c) json__check_char(in, c) #define json__ensure(c) json__check_char(in, c)
bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out);
bool json__check_char(instream_t *in, char c) { bool json__check_char(instream_t *in, char c) {
if (istr_get(in) == c) { if (istr_get(in) == c) {
return true; return true;
@ -2044,10 +2345,14 @@ void json__pretty_print_value(json_t *value, int indent, const json_pretty_opts_
os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_NUM]); os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_NUM]);
u8 scratchbuf[256]; u8 scratchbuf[256];
arena_t scratch = arena_make(ARENA_STATIC, sizeof(scratchbuf), scratchbuf); arena_t scratch = arena_make(ARENA_STATIC, sizeof(scratchbuf), scratchbuf);
const char *fmt = "%g";
if (round(value->number) == value->number) {
fmt = "%.0f";
}
os_file_print( os_file_print(
scratch, scratch,
options->custom_target, options->custom_target,
"%g", fmt,
value->number value->number
); );
os_log_set_colour(LOG_COL_RESET); os_log_set_colour(LOG_COL_RESET);
@ -2631,6 +2936,14 @@ str_t http_res_to_str(arena_t *arena, http_res_t *res) {
return ostr_to_str(&out); return ostr_to_str(&out);
} }
http_header_t *http_add_header(arena_t *arena, http_header_t *headers, strview_t key, strview_t value) {
http_header_t *h = alloc(arena, http_header_t);
h->key = key;
h->value = value;
list_push(headers, h);
return headers;
}
bool http_has_header(http_header_t *headers, strview_t key) { bool http_has_header(http_header_t *headers, strview_t key) {
for_each(h, headers) { for_each(h, headers) {
if (strv_equals(h->key, key)) { if (strv_equals(h->key, key)) {
@ -2752,13 +3065,6 @@ http_url_t http_split_url(strview_t url) {
} }
#if !COLLA_NO_NET #if !COLLA_NO_NET
// HTTP /////////////////////////////
http_res_t http_request(http_request_desc_t *req) {
return http_request_cb(req, NULL, NULL);
}
// WEBSOCKETS /////////////////////// // WEBSOCKETS ///////////////////////
#define WEBSOCKET_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WEBSOCKET_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
@ -3208,3 +3514,29 @@ str_t pretty_print_get_stringv(arena_t *arena, const char *fmt, va_list args) {
return ostr_to_str(&out); return ostr_to_str(&out);
} }
usize pretty_print_get_length(strview_t view) {
usize size = 0;
instream_t in = istr_init(view);
while (!istr_is_finished(&in)) {
strview_t part = istr_get_view(&in, '<');
bool has_escape = strv_ends_with(part, '\\');
if (has_escape) {
part.len -= 1;
}
size += strv_get_utf8_len(part);
istr_skip(&in, 1);
if (has_escape) {
size++;
continue;
}
istr_ignore_and_skip(&in, '>');
}
return size;
}

128
colla.h
View file

@ -1,8 +1,6 @@
#ifndef COLLA_HEADER #ifndef COLLA_HEADER
#define COLLA_HEADER #define COLLA_HEADER
#define _FILE_OFFSET_BITS 1
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
@ -16,7 +14,13 @@ extern void *memmove(void *dst, const void *src, size_t size);
#define static_assert(cond, ...) _Static_assert(cond, "" __VA_ARGS__) #define static_assert(cond, ...) _Static_assert(cond, "" __VA_ARGS__)
///////////////////////////////////////////////// typedef enum {
COLLA_DARRAY_BLOCK_SIZE = 64,
COLLA_RG_MAX_MATCHES = 16,
COLLA_OS_ARENA_SIZE = 1 << 20, // MB(1)
COLLA_OS_MAX_WAITABLE_HANDLES = 256,
COLLA_LOG_MAX_CALLBACKS = 22,
} colla_constants_e;
// CORE MODULES ///////////////////////////////// // CORE MODULES /////////////////////////////////
@ -276,8 +280,6 @@ for_each (chunk, arr) {
} }
*/ */
#define DARRAY_DEFAULT_BLOCK_SIZE (64)
#define darr_define(struct_name, item_type) typedef struct struct_name struct_name; \ #define darr_define(struct_name, item_type) typedef struct struct_name struct_name; \
struct struct_name { \ struct struct_name { \
item_type *items; \ item_type *items; \
@ -290,7 +292,7 @@ for_each (chunk, arr) {
#define darr__alloc_first(arena, arr) do { \ #define darr__alloc_first(arena, arr) do { \
(arr) = (arr) ? (arr) : alloc(arena, typeof(*arr)); \ (arr) = (arr) ? (arr) : alloc(arena, typeof(*arr)); \
(arr)->head = (arr)->head ? (arr)->head : (arr); \ (arr)->head = (arr)->head ? (arr)->head : (arr); \
(arr)->block_size = (arr)->block_size ? (arr)->block_size : DARRAY_DEFAULT_BLOCK_SIZE; \ (arr)->block_size = (arr)->block_size ? (arr)->block_size : COLLA_DARRAY_BLOCK_SIZE; \
(arr)->items = alloc(arena, typeof(*arr->items), arr->block_size); \ (arr)->items = alloc(arena, typeof(*arr->items), arr->block_size); \
colla_assert((arr)->count == 0); \ colla_assert((arr)->count == 0); \
} while (0) } while (0)
@ -431,6 +433,7 @@ strview_t strv_init_str(str_t str);
bool strv_is_empty(strview_t ctx); bool strv_is_empty(strview_t ctx);
bool strv_equals(strview_t a, strview_t b); bool strv_equals(strview_t a, strview_t b);
int strv_compare(strview_t a, strview_t b); int strv_compare(strview_t a, strview_t b);
usize strv_get_utf8_len(strview_t v);
char strv_front(strview_t ctx); char strv_front(strview_t ctx);
char strv_back(strview_t ctx); char strv_back(strview_t ctx);
@ -585,6 +588,26 @@ bool ibstr_get_i16(ibstream_t *ib, i16 *out);
bool ibstr_get_i32(ibstream_t *ib, i32 *out); bool ibstr_get_i32(ibstream_t *ib, i32 *out);
bool ibstr_get_i64(ibstream_t *ib, i64 *out); bool ibstr_get_i64(ibstream_t *ib, i64 *out);
// SIMPLE REGEX /////////////////////////////////
// only supports *, every star matches until the following character
// is found, e.g.
// ab*e
// abcde
// matches (cd)
typedef struct rg_match_t rg_match_t;
struct rg_match_t {
strview_t text[COLLA_RG_MAX_MATCHES];
int count;
bool matches;
};
rg_match_t rg_match(strview_t rg, strview_t text);
bool rg_match_easy(strview_t rg, strview_t text);
/////////////////////////////////////////////////
// ARENA //////////////////////////////////////// // ARENA ////////////////////////////////////////
#if COLLA_WIN && !COLLA_TCC #if COLLA_WIN && !COLLA_TCC
@ -656,7 +679,6 @@ void arena_pop(arena_t *arena, usize amount);
// OS LAYER ///////////////////////////////////// // OS LAYER /////////////////////////////////////
#define OS_ARENA_SIZE (MB(1))
#define OS_WAIT_INFINITE (0xFFFFFFFF) #define OS_WAIT_INFINITE (0xFFFFFFFF)
typedef struct oshandle_t oshandle_t; typedef struct oshandle_t oshandle_t;
@ -664,11 +686,20 @@ struct oshandle_t {
uptr data; uptr data;
}; };
typedef enum {
OS_ARCH_X86,
OS_ARCH_ARM,
OS_ARCH_IA64,
OS_ARCH_AMD64,
OS_ARCH_ARM64,
} os_arch_e;
typedef struct os_system_info_t os_system_info_t; typedef struct os_system_info_t os_system_info_t;
struct os_system_info_t { struct os_system_info_t {
u32 processor_count; u32 processor_count;
u64 page_size; u64 page_size;
str_t machine_name; str_t machine_name;
os_arch_e architecture;
}; };
void os_init(void); void os_init(void);
@ -686,8 +717,6 @@ oshandle_t os_handle_zero(void);
bool os_handle_match(oshandle_t a, oshandle_t b); bool os_handle_match(oshandle_t a, oshandle_t b);
bool os_handle_valid(oshandle_t handle); bool os_handle_valid(oshandle_t handle);
#define OS_MAX_WAITABLE_HANDLES 256
typedef enum { typedef enum {
OS_WAIT_FINISHED, OS_WAIT_FINISHED,
OS_WAIT_ABANDONED, OS_WAIT_ABANDONED,
@ -739,8 +768,36 @@ typedef enum os_log_colour_e {
LOG_COL__COUNT, LOG_COL__COUNT,
} os_log_colour_e; } os_log_colour_e;
void os_log_print(os_log_level_e level, const char *fmt, ...); typedef struct log_event_t log_event_t;
void os_log_printv(os_log_level_e level, const char *fmt, va_list args); struct log_event_t {
va_list args;
const char *fmt;
const char *file;
int line;
struct tm *time;
os_log_level_e level;
void *udata;
};
typedef struct log_callback_t log_callback_t;
struct log_callback_t {
void (*fn)(log_event_t *ev);
void *udata;
os_log_level_e level;
};
typedef enum {
OS_LOG_DEFAULT = 0,
OS_LOG_NOTIME = 1 << 0,
OS_LOG_NOFILE = 1 << 1,
OS_LOG_NOCRASH = 1 << 2,
OS_LOG_SIMPLE = OS_LOG_NOTIME | OS_LOG_NOFILE,
} os_log_options_e;
void os_log_add_callback(log_callback_t cb);
void os_log_add_fp(oshandle_t fp, os_log_level_e level);
void os_log_print(const char *file, int line, os_log_level_e level, const char *fmt, ...);
void os_log_printv(const char *file, int line, 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(os_log_colour_e colour);
void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background); void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background);
@ -748,12 +805,12 @@ oshandle_t os_stdout(void);
oshandle_t os_stdin(void); oshandle_t os_stdin(void);
#define print(...) fmt_print(__VA_ARGS__) #define print(...) fmt_print(__VA_ARGS__)
#define println(...) os_log_print(LOG_BASIC, __VA_ARGS__) #define println(...) os_log_print(__FILE__, __LINE__, LOG_BASIC, __VA_ARGS__)
#define debug(...) os_log_print(LOG_DEBUG, __VA_ARGS__) #define debug(...) os_log_print(__FILE__, __LINE__, LOG_DEBUG, __VA_ARGS__)
#define info(...) os_log_print(LOG_INFO, __VA_ARGS__) #define info(...) os_log_print(__FILE__, __LINE__, LOG_INFO, __VA_ARGS__)
#define warn(...) os_log_print(LOG_WARN, __VA_ARGS__) #define warn(...) os_log_print(__FILE__, __LINE__, LOG_WARN, __VA_ARGS__)
#define err(...) os_log_print(LOG_ERR, __VA_ARGS__) #define err(...) os_log_print(__FILE__, __LINE__, LOG_ERR, __VA_ARGS__)
#define fatal(...) os_log_print(LOG_FATAL, __VA_ARGS__) #define fatal(...) os_log_print(__FILE__, __LINE__, LOG_FATAL, __VA_ARGS__)
// == FILE ====================================== // == FILE ======================================
@ -893,6 +950,39 @@ void os_cond_broadcast(oshandle_t cond);
void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds); void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds);
// == JOB QUEUE =================================
typedef void (job_func_f)(void *userdata);
typedef struct job_t job_t;
struct job_t {
job_t *next;
job_func_f *func;
void *userdata;
};
typedef struct job_queue_t job_queue_t;
struct job_queue_t {
job_t *jobs;
job_t *freelist;
bool should_stop;
bool stop_when_finished;
int reader;
int writer;
oshandle_t mutex;
oshandle_t condvar;
oshandle_t *threads;
int thread_count;
};
// pass 0 to worker count to use max workers (os_get_system_info().processor_count)
job_queue_t *jq_init(arena_t *arena, int worker_count);
void jq_stop(job_queue_t *queue);
// no need to call this if you call jq_stop
void jq_cleanup(job_queue_t *queue);
void jq_push(arena_t *arena, job_queue_t *queue, job_func_f *func, void *userdata);
job_t *jq_pop_job(job_queue_t *queue);
#endif #endif
// PARSERS ////////////////////////////////////// // PARSERS //////////////////////////////////////
@ -969,7 +1059,6 @@ struct ini_pretty_opts_t {
void ini_pretty_print(ini_t *ini, const ini_pretty_opts_t *options); void ini_pretty_print(ini_t *ini, const ini_pretty_opts_t *options);
// == JSON =========================================== // == JSON ===========================================
typedef enum jsontype_e { typedef enum jsontype_e {
@ -985,6 +1074,7 @@ typedef enum jsonflags_e {
JSON_DEFAULT = 0, JSON_DEFAULT = 0,
JSON_NO_TRAILING_COMMAS = 1 << 0, JSON_NO_TRAILING_COMMAS = 1 << 0,
JSON_NO_COMMENTS = 1 << 1, JSON_NO_COMMENTS = 1 << 1,
JSON_ONLY_OBJECT_START = 1 << 2,
} jsonflags_e; } jsonflags_e;
typedef struct json_t json_t; typedef struct json_t json_t;
@ -1144,6 +1234,7 @@ 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_req_to_str(arena_t *arena, http_req_t *req);
str_t http_res_to_str(arena_t *arena, http_res_t *res); str_t http_res_to_str(arena_t *arena, http_res_t *res);
http_header_t *http_add_header(arena_t *arena, http_header_t *headers, strview_t key, strview_t value);
bool http_has_header(http_header_t *headers, strview_t key); 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); 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); strview_t http_get_header(http_header_t *headers, strview_t key);
@ -1285,5 +1376,6 @@ 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_string(arena_t *arena, const char *fmt, ...);
str_t pretty_print_get_stringv(arena_t *arena, const char *fmt, va_list args); str_t pretty_print_get_stringv(arena_t *arena, const char *fmt, va_list args);
usize pretty_print_get_length(strview_t view);
#endif #endif

View file

@ -15,6 +15,50 @@
#endif #endif
#endif #endif
#ifndef PROCESSOR_ARCHITECTURE_ARM64
#define PROCESSOR_ARCHITECTURE_ARM64 12
#endif
const char win32__fg_colours[LOG_COL__COUNT][6] = {
[LOG_COL_RESET] = "\x1b[39m",
[LOG_COL_BLACK] = "\x1b[30m",
[LOG_COL_RED] = "\x1b[31m",
[LOG_COL_GREEN] = "\x1b[32m",
[LOG_COL_YELLOW] = "\x1b[33m",
[LOG_COL_BLUE] = "\x1b[34m",
[LOG_COL_MAGENTA] = "\x1b[35m",
[LOG_COL_CYAN] = "\x1b[36m",
[LOG_COL_WHITE] = "\x1b[37m",
[LOG_COL_DARK_GREY] = "\x1b[90m",
[LOG_COL_LIGHT_RED] = "\x1b[91m",
[LOG_COL_LIGHT_GREEN] = "\x1b[92m",
[LOG_COL_LIGHT_YELLOW] = "\x1b[93m",
[LOG_COL_LIGHT_BLUE] = "\x1b[94m",
[LOG_COL_LIGHT_MAGENTA] = "\x1b[95m",
[LOG_COL_LIGHT_CYAN] = "\x1b[96m",
};
const char win32__bg_colours[LOG_COL__COUNT][7] = {
[LOG_COL_RESET] = "\x1b[49m",
[LOG_COL_BLACK] = "\x1b[40m",
[LOG_COL_RED] = "\x1b[41m",
[LOG_COL_GREEN] = "\x1b[42m",
[LOG_COL_YELLOW] = "\x1b[43m",
[LOG_COL_BLUE] = "\x1b[44m",
[LOG_COL_MAGENTA] = "\x1b[45m",
[LOG_COL_CYAN] = "\x1b[46m",
[LOG_COL_WHITE] = "\x1b[47m",
[LOG_COL_DARK_GREY] = "\x1b[100m",
[LOG_COL_LIGHT_RED] = "\x1b[101m",
[LOG_COL_LIGHT_GREEN] = "\x1b[102m",
[LOG_COL_LIGHT_YELLOW] = "\x1b[103m",
[LOG_COL_LIGHT_BLUE] = "\x1b[104m",
[LOG_COL_LIGHT_MAGENTA] = "\x1b[105m",
[LOG_COL_LIGHT_CYAN] = "\x1b[106m",
};
void os__log_init(void);
str_t str_os_from_str16(arena_t *arena, str16_t src) { str_t str_os_from_str16(arena_t *arena, str16_t src) {
str_t out = {0}; str_t out = {0};
@ -144,11 +188,20 @@ void os_init(void) {
SYSTEM_INFO sysinfo = {0}; SYSTEM_INFO sysinfo = {0};
GetSystemInfo(&sysinfo); GetSystemInfo(&sysinfo);
os_arch_e architectures[] = {
[PROCESSOR_ARCHITECTURE_INTEL] = OS_ARCH_X86,
[PROCESSOR_ARCHITECTURE_ARM] = OS_ARCH_ARM,
[PROCESSOR_ARCHITECTURE_IA64] = OS_ARCH_IA64,
[PROCESSOR_ARCHITECTURE_AMD64] = OS_ARCH_AMD64,
[PROCESSOR_ARCHITECTURE_ARM64] = OS_ARCH_ARM64,
};
os_system_info_t *info = &w32_data.info; os_system_info_t *info = &w32_data.info;
info->processor_count = (u64)sysinfo.dwNumberOfProcessors; info->processor_count = (u64)sysinfo.dwNumberOfProcessors;
info->page_size = sysinfo.dwPageSize; info->page_size = sysinfo.dwPageSize;
info->architecture = architectures[sysinfo.wProcessorArchitecture];
w32_data.arena = arena_make(ARENA_VIRTUAL, OS_ARENA_SIZE); w32_data.arena = arena_make(ARENA_VIRTUAL, COLLA_OS_ARENA_SIZE);
TCHAR namebuf[MAX_COMPUTERNAME_LENGTH + 1]; TCHAR namebuf[MAX_COMPUTERNAME_LENGTH + 1];
DWORD namebuflen = sizeof(namebuf); DWORD namebuflen = sizeof(namebuf);
@ -176,6 +229,7 @@ void os_init(void) {
else { else {
err("couldn't get console screen buffer info: %v", os_get_error_string(os_get_last_error())); err("couldn't get console screen buffer info: %v", os_get_error_string(os_get_last_error()));
} }
os__log_init();
} }
void os_cleanup(void) { void os_cleanup(void) {
@ -187,7 +241,9 @@ void os_cleanup(void) {
void os_abort(int code) { void os_abort(int code) {
#if COLLA_DEBUG #if COLLA_DEBUG
if (code != 0) abort(); if (code != 0) {
__debugbreak();
}
#endif #endif
ExitProcess(code); ExitProcess(code);
} }
@ -218,8 +274,8 @@ str_t os_get_error_string(iptr error) {
} }
os_wait_t os_wait_on_handles(oshandle_t *handles, int count, bool wait_all, u32 milliseconds) { os_wait_t os_wait_on_handles(oshandle_t *handles, int count, bool wait_all, u32 milliseconds) {
HANDLE win_handles[OS_MAX_WAITABLE_HANDLES] = {0}; HANDLE win_handles[COLLA_OS_MAX_WAITABLE_HANDLES] = {0};
colla_assert(count < MAXIMUM_WAIT_OBJECTS); colla_assert(count < COLLA_OS_MAX_WAITABLE_HANDLES);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
win_handles[i] = (HANDLE)(handles[i].data); win_handles[i] = (HANDLE)(handles[i].data);
@ -252,16 +308,12 @@ os_system_info_t os_get_system_info(void) {
} }
void os_log_set_colour(os_log_colour_e colour) { void os_log_set_colour(os_log_colour_e colour) {
WORD attribute = colour == LOG_COL_RESET ? w32_data.default_fg : (WORD)colour; WriteFile((HANDLE)w32_data.hstdout.data, win32__fg_colours[colour], arrlen(win32__fg_colours[colour]), NULL, NULL);
SetConsoleTextAttribute((HANDLE)w32_data.hstdout.data, attribute);
} }
void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background) { void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background) {
WORD fg_attr = foreground == LOG_COL_RESET ? w32_data.default_fg : (WORD)foreground; WriteFile((HANDLE)w32_data.hstdout.data, win32__fg_colours[foreground], arrlen(win32__fg_colours[foreground]), NULL, NULL);
WORD bg_attr = (background == LOG_COL_RESET ? w32_data.default_bg : (WORD)background) << 4; WriteFile((HANDLE)w32_data.hstdout.data, win32__bg_colours[background], arrlen(win32__bg_colours[background]), NULL, NULL);
WORD attribute = fg_attr | bg_attr;
SetConsoleTextAttribute((HANDLE)w32_data.hstdout.data, attribute);
} }
oshandle_t os_stdout(void) { oshandle_t os_stdout(void) {
@ -456,16 +508,20 @@ u64 os_file_time_fp(oshandle_t handle) {
// == DIR WALKER ================================ // == DIR WALKER ================================
typedef struct dir_t { typedef struct dir_t {
WIN32_FIND_DATA find_data; WIN32_FIND_DATAW find_data;
HANDLE handle; HANDLE handle;
dir_entry_t cur_entry; dir_entry_t cur_entry;
dir_entry_t next_entry; dir_entry_t next_entry;
} dir_t; } dir_t;
dir_entry_t os__dir_entry_from_find_data(arena_t *arena, WIN32_FIND_DATA *fd) { dir_entry_t os__dir_entry_from_find_data(arena_t *arena, WIN32_FIND_DATAW *fd) {
dir_entry_t out = {0}; dir_entry_t out = {0};
out.name = str_from_tstr(arena, tstr_init(fd->cFileName, 0)); out.name = str_from_str16(arena, str16_init(fd->cFileName, 0));
if (strv_equals(strv(out.name), strv("cygwin"))) {
out.type = 0;
}
if (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
out.type = DIRTYPE_DIR; out.type = DIRTYPE_DIR;
@ -482,33 +538,30 @@ dir_entry_t os__dir_entry_from_find_data(arena_t *arena, WIN32_FIND_DATA *fd) {
} }
dir_t *os_dir_open(arena_t *arena, strview_t path) { dir_t *os_dir_open(arena_t *arena, strview_t path) {
usize prev = arena_tell(arena);
dir_t* ctx = alloc(arena, dir_t);
arena_t scratch = *arena; arena_t scratch = *arena;
#if 0 str16_t winpath = strv_to_str16(&scratch, path);
u8 tmpbuf[KB(1)] = {0}; DWORD pathlen = GetFullPathNameW(winpath.buf, 0, NULL, NULL);
arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
#endif
tstr_t winpath = strv_to_tstr(&scratch, path); WCHAR *fullpath = alloc(&scratch, WCHAR, pathlen + 10);
// get a little extra leeway
TCHAR fullpath[MAX_PATH + 16] = {0}; pathlen = GetFullPathNameW(winpath.buf, pathlen + 1, fullpath, NULL);
DWORD pathlen = GetFullPathName(winpath.buf, MAX_PATH, fullpath, NULL);
// add asterisk at the end of the path if (fullpath[pathlen] != L'\\' && fullpath[pathlen] != L'/') {
if (fullpath[pathlen] != '\\' && fullpath[pathlen] != '/') { fullpath[pathlen++] = L'\\';
fullpath[pathlen++] = '\\';
} }
fullpath[pathlen++] = '*'; fullpath[pathlen++] = L'*';
fullpath[pathlen++] = '\0';
ctx->handle = FindFirstFile(fullpath, &ctx->find_data); WIN32_FIND_DATAW first = {0};
HANDLE handle = FindFirstFileW(fullpath, &first);
if (ctx->handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE) {
arena_rewind(arena, prev);
return NULL; return NULL;
} }
dir_t *ctx = alloc(arena, dir_t);
ctx->handle = handle;
ctx->find_data = first;
ctx->next_entry = os__dir_entry_from_find_data(arena, &ctx->find_data); ctx->next_entry = os__dir_entry_from_find_data(arena, &ctx->find_data);
return ctx; return ctx;
@ -530,13 +583,18 @@ dir_entry_t *os_dir_next(arena_t *arena, dir_t *dir) {
dir->cur_entry = dir->next_entry; dir->cur_entry = dir->next_entry;
while (true) {
dir->next_entry = (dir_entry_t){0}; dir->next_entry = (dir_entry_t){0};
if (!FindNextFileW(dir->handle, &dir->find_data)) {
if (FindNextFile(dir->handle, &dir->find_data)) {
dir->next_entry = os__dir_entry_from_find_data(arena, &dir->find_data);
}
else {
os_dir_close(dir); os_dir_close(dir);
break;
}
// HACK: skip system files/directories
if (dir->find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
continue;
}
dir->next_entry = os__dir_entry_from_find_data(arena, &dir->find_data);
break;
} }
return &dir->cur_entry; return &dir->cur_entry;
@ -956,7 +1014,7 @@ http_res_t http_request_cb(http_request_desc_t *req, http_request_callback_fn ca
(DWORD_PTR)NULL // userdata (DWORD_PTR)NULL // userdata
); );
if (!connection) { if (!connection) {
err("call to InternetConnect failed: %u", os_get_error_string(os_get_last_error())); err("call to InternetConnect failed: %u", os_get_last_error());
goto failed; goto failed;
} }
@ -1014,7 +1072,6 @@ http_res_t http_request_cb(http_request_desc_t *req, http_request_callback_fn ca
// buffer is not big enough, allocate one with the arena instead // buffer is not big enough, allocate one with the arena instead
if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
info("buffer is too small");
buffer = alloc(req->arena, u8, bufsize + 1); buffer = alloc(req->arena, u8, bufsize + 1);
result = HttpQueryInfo(request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer, &bufsize, NULL); result = HttpQueryInfo(request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer, &bufsize, NULL);
} }