From 250ffe3c3fc26132afa38ca4a08c9abf43077c3e Mon Sep 17 00:00:00 2001 From: alessandro bason Date: Sat, 11 Oct 2025 20:35:40 +0200 Subject: [PATCH] windows stuff --- colla.c | 426 ++++++++++++++++++++++++++++++++++++++++++++------ colla.h | 128 ++++++++++++--- colla_win32.c | 147 +++++++++++------ 3 files changed, 591 insertions(+), 110 deletions(-) diff --git a/colla.c b/colla.c index 6835d35..6f2ea19 100644 --- a/colla.c +++ b/colla.c @@ -5,6 +5,7 @@ #include #include #include +#include #if COLLA_TCC #define COLLA_NO_CONDITION_VARIABLE 1 @@ -34,6 +35,13 @@ 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) { // TODO maybe use os_write? fflush(stdout); @@ -284,6 +292,18 @@ int strv_compare(strview_t a, strview_t b) { (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) { 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) { if (!ctx || !ctx->cur || !val) return false; char *end = NULL; - *val = strtoll(ctx->cur, &end, 0); + i64 out = strtoll(ctx->cur, &end, 0); if (ctx->cur == end) { return false; @@ -672,6 +692,7 @@ bool istr_get_i64(instream_t *ctx, i64 *val) { } ctx->cur = end; + *val = out; return true; } @@ -897,6 +918,91 @@ bool ibstr_get_i64(ibstream_t *ib, i64 *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) { 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)); } + // == MALLOC ARENA ===================================================================================================== static arena_t arena__make_malloc(usize size) { @@ -1134,47 +1241,138 @@ bool os_handle_valid(oshandle_t handle) { // == LOGGING =================================== -os_log_colour_e log__level_to_colour(os_log_level_e level) { - os_log_colour_e colour = LOG_COL_RESET; - switch (level) { - case LOG_DEBUG: colour = LOG_COL_BLUE; break; - case LOG_INFO: colour = LOG_COL_GREEN; break; - case LOG_WARN: colour = LOG_COL_YELLOW; break; - case LOG_ERR: colour = LOG_COL_MAGENTA; break; - case LOG_FATAL: colour = LOG_COL_RED; break; - default: break; +const char* os_log_level_strings[LOG_COL__COUNT] = { + [LOG_DEBUG] = "[DEBUG]:", + [LOG_INFO] = "[INFO]:", + [LOG_WARN] = "[WARN]:", + [LOG_ERR] = "[ERR]:", + [LOG_FATAL] = "[FATAL]:", +}; + +os_log_colour_e os_log_level_colours[LOG_COL__COUNT] = { + [LOG_DEBUG] = LOG_COL_BLUE, + [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 + ); } - return colour; + + 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_print(os_log_level_e level, const char *fmt, ...) { +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_start(args, fmt); - os_log_printv(level, fmt, args); + os_log_printv(file, line, level, fmt, args); va_end(args); } -void os_log_printv(os_log_level_e level, const char *fmt, va_list args) { - const char *level_str = ""; - switch (level) { - case LOG_DEBUG: level_str = "DEBUG"; break; - case LOG_INFO: level_str = "INFO"; break; - case LOG_WARN: level_str = "WARN"; break; - case LOG_ERR: level_str = "ERR"; break; - case LOG_FATAL: level_str = "FATAL"; break; - default: break; +void os_log_printv(const char *file, int line, os_log_level_e level, const char *fmt, va_list args) { + time_t t = time(NULL); + struct tm log_time = { 0 }; +#define gettime(src, dst) localtime_s(&dst, &src) + log_event_t ev = { + .level = level, + .line = line, + .args = args, + .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)); - if (level != LOG_BASIC) { - fmt_print("[%s]: ", level_str); - } - os_log_set_colour(LOG_COL_RESET); + bool nocrash = log_opts & OS_LOG_NOCRASH; - fmt_printv(fmt, args); - fmt_print("\n"); - - if (level == LOG_FATAL) { + if (!nocrash && level == LOG_FATAL) { os_abort(1); } } @@ -1346,6 +1544,103 @@ usize os_pad_to_page(usize byte_count) { 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 ============================================ 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; } - void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopt_t *opts) { colla_assert(table); @@ -1660,6 +1954,7 @@ void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options) { // == JSON =========================================== 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) { str_t data = os_file_read_all_str(arena, filename); @@ -1674,12 +1969,20 @@ json_t *json_parse_str(arena_t *arena, strview_t str, jsonflags_e flags) { instream_t in = istr_init(str); - if (!json__parse_obj(arena, &in, flags, &root->object)) { - // reset arena - *arena = before; - return NULL; + if (flags & JSON_ONLY_OBJECT_START) { + if (!json__parse_obj(arena, &in, flags, &root->object)) { + // reset arena + *arena = before; + return NULL; + } } - + else { + if (!json__parse_value(arena, &in, flags, &root)) { + *arena = before; + return NULL; + } + } + 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) -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) { if (istr_get(in) == c) { 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]); u8 scratchbuf[256]; 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( scratch, options->custom_target, - "%g", + fmt, value->number ); 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); } +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) { for_each(h, headers) { if (strv_equals(h->key, key)) { @@ -2752,13 +3065,6 @@ http_url_t http_split_url(strview_t url) { } #if !COLLA_NO_NET - -// HTTP ///////////////////////////// - -http_res_t http_request(http_request_desc_t *req) { - return http_request_cb(req, NULL, NULL); -} - // WEBSOCKETS /////////////////////// #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); } + +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; +} diff --git a/colla.h b/colla.h index 53a4d98..716150c 100644 --- a/colla.h +++ b/colla.h @@ -1,8 +1,6 @@ #ifndef COLLA_HEADER #define COLLA_HEADER -#define _FILE_OFFSET_BITS 1 - #include #include #include @@ -16,7 +14,13 @@ extern void *memmove(void *dst, const void *src, size_t size); #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 ///////////////////////////////// @@ -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; \ struct struct_name { \ item_type *items; \ @@ -290,7 +292,7 @@ for_each (chunk, arr) { #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)->block_size = (arr)->block_size ? (arr)->block_size : COLLA_DARRAY_BLOCK_SIZE; \ (arr)->items = alloc(arena, typeof(*arr->items), arr->block_size); \ colla_assert((arr)->count == 0); \ } while (0) @@ -431,6 +433,7 @@ 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); +usize strv_get_utf8_len(strview_t v); char strv_front(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_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 //////////////////////////////////////// #if COLLA_WIN && !COLLA_TCC @@ -656,7 +679,6 @@ 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; @@ -664,11 +686,20 @@ struct oshandle_t { 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; struct os_system_info_t { u32 processor_count; u64 page_size; str_t machine_name; + os_arch_e architecture; }; 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_valid(oshandle_t handle); -#define OS_MAX_WAITABLE_HANDLES 256 - typedef enum { OS_WAIT_FINISHED, OS_WAIT_ABANDONED, @@ -739,8 +768,36 @@ typedef enum os_log_colour_e { 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); +typedef struct log_event_t log_event_t; +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_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); #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__) +#define println(...) os_log_print(__FILE__, __LINE__, LOG_BASIC, __VA_ARGS__) +#define debug(...) os_log_print(__FILE__, __LINE__, LOG_DEBUG, __VA_ARGS__) +#define info(...) os_log_print(__FILE__, __LINE__, LOG_INFO, __VA_ARGS__) +#define warn(...) os_log_print(__FILE__, __LINE__, LOG_WARN, __VA_ARGS__) +#define err(...) os_log_print(__FILE__, __LINE__, LOG_ERR, __VA_ARGS__) +#define fatal(...) os_log_print(__FILE__, __LINE__, LOG_FATAL, __VA_ARGS__) // == FILE ====================================== @@ -893,6 +950,39 @@ void os_cond_broadcast(oshandle_t cond); 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 // PARSERS ////////////////////////////////////// @@ -969,7 +1059,6 @@ struct ini_pretty_opts_t { void ini_pretty_print(ini_t *ini, const ini_pretty_opts_t *options); - // == JSON =========================================== typedef enum jsontype_e { @@ -985,6 +1074,7 @@ typedef enum jsonflags_e { JSON_DEFAULT = 0, JSON_NO_TRAILING_COMMAS = 1 << 0, JSON_NO_COMMENTS = 1 << 1, + JSON_ONLY_OBJECT_START = 1 << 2, } jsonflags_e; 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_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); 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); @@ -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_stringv(arena_t *arena, const char *fmt, va_list args); +usize pretty_print_get_length(strview_t view); #endif diff --git a/colla_win32.c b/colla_win32.c index e24523f..7243b42 100644 --- a/colla_win32.c +++ b/colla_win32.c @@ -15,6 +15,50 @@ #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 out = {0}; @@ -144,11 +188,20 @@ void os_init(void) { SYSTEM_INFO sysinfo = {0}; 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; info->processor_count = (u64)sysinfo.dwNumberOfProcessors; 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]; DWORD namebuflen = sizeof(namebuf); @@ -176,6 +229,7 @@ void os_init(void) { else { err("couldn't get console screen buffer info: %v", os_get_error_string(os_get_last_error())); } + os__log_init(); } void os_cleanup(void) { @@ -187,7 +241,9 @@ void os_cleanup(void) { void os_abort(int code) { #if COLLA_DEBUG - if (code != 0) abort(); + if (code != 0) { + __debugbreak(); + } #endif 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) { - HANDLE win_handles[OS_MAX_WAITABLE_HANDLES] = {0}; - colla_assert(count < MAXIMUM_WAIT_OBJECTS); + HANDLE win_handles[COLLA_OS_MAX_WAITABLE_HANDLES] = {0}; + colla_assert(count < COLLA_OS_MAX_WAITABLE_HANDLES); for (int i = 0; i < count; ++i) { 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) { - WORD attribute = colour == LOG_COL_RESET ? w32_data.default_fg : (WORD)colour; - SetConsoleTextAttribute((HANDLE)w32_data.hstdout.data, attribute); + WriteFile((HANDLE)w32_data.hstdout.data, win32__fg_colours[colour], arrlen(win32__fg_colours[colour]), NULL, NULL); } 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; - WORD bg_attr = (background == LOG_COL_RESET ? w32_data.default_bg : (WORD)background) << 4; - - WORD attribute = fg_attr | bg_attr; - SetConsoleTextAttribute((HANDLE)w32_data.hstdout.data, attribute); + WriteFile((HANDLE)w32_data.hstdout.data, win32__fg_colours[foreground], arrlen(win32__fg_colours[foreground]), NULL, NULL); + WriteFile((HANDLE)w32_data.hstdout.data, win32__bg_colours[background], arrlen(win32__bg_colours[background]), NULL, NULL); } oshandle_t os_stdout(void) { @@ -280,14 +332,14 @@ arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf) DWORD os__win_mode_to_access(filemode_e mode) { DWORD out = 0; - if (mode & OS_FILE_READ) out |= GENERIC_READ; - if (mode & OS_FILE_WRITE) out |= GENERIC_WRITE; + if (mode & FILEMODE_READ) out |= GENERIC_READ; + if (mode & FILEMODE_WRITE) out |= GENERIC_WRITE; return out; } DWORD os__win_mode_to_creation(filemode_e mode) { - if (mode == OS_FILE_READ) return OPEN_EXISTING; - if (mode == OS_FILE_WRITE) return CREATE_ALWAYS; + if (mode == FILEMODE_READ) return OPEN_EXISTING; + if (mode == FILEMODE_WRITE) return CREATE_ALWAYS; return OPEN_ALWAYS; } @@ -456,16 +508,20 @@ u64 os_file_time_fp(oshandle_t handle) { // == DIR WALKER ================================ typedef struct dir_t { - WIN32_FIND_DATA find_data; + WIN32_FIND_DATAW find_data; HANDLE handle; dir_entry_t cur_entry; dir_entry_t next_entry; } 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}; - 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) { 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) { - usize prev = arena_tell(arena); - dir_t* ctx = alloc(arena, dir_t); - arena_t scratch = *arena; -#if 0 - u8 tmpbuf[KB(1)] = {0}; - arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf); -#endif + str16_t winpath = strv_to_str16(&scratch, path); + DWORD pathlen = GetFullPathNameW(winpath.buf, 0, NULL, NULL); + + WCHAR *fullpath = alloc(&scratch, WCHAR, pathlen + 10); - tstr_t winpath = strv_to_tstr(&scratch, path); - // get a little extra leeway - TCHAR fullpath[MAX_PATH + 16] = {0}; - DWORD pathlen = GetFullPathName(winpath.buf, MAX_PATH, fullpath, NULL); - // add asterisk at the end of the path - if (fullpath[pathlen] != '\\' && fullpath[pathlen] != '/') { - fullpath[pathlen++] = '\\'; + pathlen = GetFullPathNameW(winpath.buf, pathlen + 1, fullpath, NULL); + + if (fullpath[pathlen] != L'\\' && fullpath[pathlen] != L'/') { + fullpath[pathlen++] = L'\\'; } - fullpath[pathlen++] = '*'; - fullpath[pathlen++] = '\0'; + fullpath[pathlen++] = L'*'; - ctx->handle = FindFirstFile(fullpath, &ctx->find_data); + WIN32_FIND_DATAW first = {0}; + HANDLE handle = FindFirstFileW(fullpath, &first); - if (ctx->handle == INVALID_HANDLE_VALUE) { - arena_rewind(arena, prev); + if (handle == INVALID_HANDLE_VALUE) { 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); 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->next_entry = (dir_entry_t){0}; - - if (FindNextFile(dir->handle, &dir->find_data)) { + while (true) { + dir->next_entry = (dir_entry_t){0}; + if (!FindNextFileW(dir->handle, &dir->find_data)) { + 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); - } - else { - os_dir_close(dir); + break; } 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 ); 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; } @@ -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 if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - info("buffer is too small"); buffer = alloc(req->arena, u8, bufsize + 1); result = HttpQueryInfo(request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer, &bufsize, NULL); }