windows stuff
This commit is contained in:
parent
7e7c371b9e
commit
ae3536529b
3 changed files with 587 additions and 106 deletions
426
colla.c
426
colla.c
|
|
@ -5,6 +5,7 @@
|
|||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue