diff --git a/colla_lin.c b/colla_lin.c index e69de29..2ac3227 100644 --- a/colla_lin.c +++ b/colla_lin.c @@ -0,0 +1,870 @@ +#include "colla.h" + +#pragma clang diagnostic ignored "-Winitializer-overrides" +#pragma clang diagnostic ignored "-Wswitch" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if !COLLA_NO_NET + #include + #include + #include + + #define htonll(x) htobe64(x) + #define ntohll(x) be64toh(x) +#endif + +strview_t os__fg_colours[LOG_COL__COUNT] = { + [LOG_COL_BLACK] = cstrv("\x1b[30m"), + [LOG_COL_BLUE] = cstrv("\x1b[34m"), + [LOG_COL_GREEN] = cstrv("\x1b[32m"), + [LOG_COL_CYAN] = cstrv("\x1b[36m"), + [LOG_COL_RED] = cstrv("\x1b[31m"), + [LOG_COL_MAGENTA] = cstrv("\x1b[35m"), + [LOG_COL_YELLOW] = cstrv("\x1b[33m"), + [LOG_COL_GREY] = cstrv("\x1b[37m"), + [LOG_COL_DARK_GREY] = cstrv("\x1b[90m"), + [LOG_COL_LIGHT_BLUE] = cstrv("\x1b[94m"), + [LOG_COL_LIGHT_GREEN] = cstrv("\x1b[92m"), + [LOG_COL_LIGHT_CYAN] = cstrv("\x1b[96m"), + [LOG_COL_LIGHT_RED] = cstrv("\x1b[91m"), + [LOG_COL_LIGHT_MAGENTA] = cstrv("\x1b[95m"), + [LOG_COL_LIGHT_YELLOW] = cstrv("\x1b[93m"), + [LOG_COL_WHITE] = cstrv("\x1b[97m"), + [LOG_COL_RESET] = cstrv("\x1b[39m"), +}; + +strview_t os__bg_colours[LOG_COL__COUNT] = { + [LOG_COL_BLACK] = cstrv("\x1b[40m"), + [LOG_COL_BLUE] = cstrv("\x1b[44m"), + [LOG_COL_GREEN] = cstrv("\x1b[42m"), + [LOG_COL_CYAN] = cstrv("\x1b[46m"), + [LOG_COL_RED] = cstrv("\x1b[41m"), + [LOG_COL_MAGENTA] = cstrv("\x1b[45m"), + [LOG_COL_YELLOW] = cstrv("\x1b[43m"), + [LOG_COL_GREY] = cstrv("\x1b[47m"), + [LOG_COL_DARK_GREY] = cstrv("\x1b[100m"), + [LOG_COL_LIGHT_BLUE] = cstrv("\x1b[104m"), + [LOG_COL_LIGHT_GREEN] = cstrv("\x1b[102m"), + [LOG_COL_LIGHT_CYAN] = cstrv("\x1b[106m"), + [LOG_COL_LIGHT_RED] = cstrv("\x1b[101m"), + [LOG_COL_LIGHT_MAGENTA] = cstrv("\x1b[105m"), + [LOG_COL_LIGHT_YELLOW] = cstrv("\x1b[103m"), + [LOG_COL_WHITE] = cstrv("\x1b[107m"), + [LOG_COL_RESET] = cstrv("\x1b[49m"), +}; + +typedef enum os_entity_kind_e { + OS_KIND_NULL, + OS_KIND_THREAD, + OS_KIND_MUTEX, + OS_KIND_CONDITION_VARIABLE, +} os_entity_kind_e; + +typedef struct os_entity_t os_entity_t; +struct os_entity_t { + os_entity_t *next; + os_entity_kind_e kind; + union { + struct { + pthread_t handle; + thread_func_t *func; + void *userdata; + } thread; + pthread_mutex_t mtx; + pthread_cond_t cv; + }; +}; + +struct { + arena_t arena; + os_system_info_t info; + + os_entity_t *entity_free; +} lin_data = {0}; + +os_entity_t *os__lin_alloc_entity(os_entity_kind_e kind) { + os_entity_t *entity = lin_data.entity_free; + if (entity) { + list_pop(lin_data.entity_free); + memset(entity, 0, sizeof(os_entity_t)); + } + else { + entity = alloc(&lin_data.arena, os_entity_t); + } + entity->kind = kind; + return entity; +} + +void os__lin_free_entity(os_entity_t *entity) { + entity->kind = OS_KIND_NULL; + list_push(lin_data.entity_free, entity); +} + +os_entity_t *os__handle_to_entity(oshandle_t handle, os_entity_kind_e expected_kind) { + if (!os_handle_valid(handle)) return NULL; + os_entity_t *entity = (os_entity_t *)handle.data; + if (entity->kind != expected_kind) return NULL; + return entity; +} + +void os_init(void) { + lin_data.info.page_size = sysconf(_SC_PAGESIZE); + lin_data.info.processor_count = sysconf(_SC_NPROCESSORS_ONLN); + + lin_data.arena = arena_make(ARENA_VIRTUAL, MB(5)); + + struct utsname name = {0}; + uname(&name); + lin_data.info.machine_name = str(&lin_data.arena, name.nodename); +} + +void os_cleanup(void) { + arena_cleanup(&lin_data.arena); +} + +void os_abort(int code) { +#if COLLA_DEBUG + if (code == 1) { + abort(); + } +#endif + exit(code); +} + +iptr os_get_last_error(void) { + return (iptr)errno; +} + +str_t os_get_error_string(iptr error) { + char *error_string = strerror(error); + return (str_t){ error_string, strlen(error_string) }; +} + +os_wait_t os_wait_on_handles(oshandle_t *handles, int count, bool wait_all, u32 milliseconds) { + // TODO + return (os_wait_t){0}; +} + +os_system_info_t os_get_system_info(void) { + return lin_data.info; +} + +void os_log_set_colour(os_log_colour_e colour) { + strview_t view = os__fg_colours[colour]; + os_file_write(os_stdout(), view.buf, view.len); +} + +void os_log_set_colour_bg(os_log_colour_e foreground, os_log_colour_e background) { + strview_t fg = os__fg_colours[foreground]; + strview_t bg = os__bg_colours[background]; + os_file_write(os_stdout(), fg.buf, fg.len); + os_file_write(os_stdout(), bg.buf, bg.len); +} + +oshandle_t os_stdout(void) { + return (oshandle_t){ (uptr)(stdout) }; +} + +oshandle_t os_stdin(void) { + return (oshandle_t){ (uptr)(stdin)}; +} + +// == FILE ====================================== + +#define OS_SMALL_SCRATCH() \ +u8 tmpbuf[KB(1)]; \ +arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf) + +const char *os__mode_to_str(filemode_e mode) { + switch ((u32)mode) { + case OS_FILE_READ: return "r"; + case OS_FILE_WRITE: return "w"; + case OS_FILE_READ | OS_FILE_WRITE: return "r+"; + } + + return "r"; +} + +bool os_file_exists(strview_t path) { + struct stat st = {0}; + if (stat(path.buf, &st) == 0) { + return st.st_mode & S_IFREG; + } + return false; +} + +bool os_dir_exists(strview_t folder) { + struct stat st = {0}; + if (stat(folder.buf, &st) == 0) { + return st.st_mode & S_IFDIR; + } + return false; +} + +bool os_file_or_dir_exists(strview_t path) { + struct stat st = {0}; + if (stat(path.buf, &st) == 0) { + return st.st_mode & (S_IFDIR | S_IFREG); + } + return false; +} + +bool os_dir_create(strview_t folder) { + return mkdir(folder.buf, 0777) == 0; +} + +tstr_t os_file_fullpath(arena_t *arena, strview_t filename) { + OS_SMALL_SCRATCH(); + + str_t fname = str(&scratch, filename); + + char fullpath[PATH_MAX] = {0}; + if (!realpath(fname.buf, fullpath)) { + return (tstr_t){0}; + } + return strv_to_tstr(arena, strv(fullpath)); +} + +bool os_file_delete(strview_t path) { + OS_SMALL_SCRATCH(); + str_t fname = str(&scratch, path); + return unlink(fname.buf) == 0; +} + +bool os_dir_delete(strview_t path) { + OS_SMALL_SCRATCH(); + str_t folder = str(&scratch, path); + return rmdir(folder.buf) == 0; +} + +oshandle_t os_file_open(strview_t path, filemode_e mode) { + FILE *fp = fopen(path.buf, os__mode_to_str(mode)); + return (oshandle_t){ (uptr)fp }; +} + +void os_file_close(oshandle_t handle) { + if (!os_handle_valid(handle)) return; + int fd = fileno((FILE*)handle.data); + int res = fsync(fd); + warn("res: %d", res); + fclose((FILE*)handle.data); + close(fd); +} + +usize os_file_read(oshandle_t handle, void *buf, usize len) { + if (!os_handle_valid(handle)) return 0; + return fread(buf, 1, len, (FILE*)handle.data); +} + +usize os_file_write(oshandle_t handle, const void *buf, usize len) { + if (!os_handle_valid(handle)) return 0; + return fwrite(buf, 1, len, (FILE*)handle.data); +} + +bool os_file_seek(oshandle_t handle, usize offset) { + if (!os_handle_valid(handle)) return false; + int res = fseeko((FILE*)handle.data, offset, SEEK_SET); + return res == 0; +} + +bool os_file_seek_end(oshandle_t handle) { + if (!os_handle_valid(handle)) return 0; + int res = fseek((FILE*)handle.data, 0, SEEK_END); + return res == 0; +} + +void os_file_rewind(oshandle_t handle) { + if (!os_handle_valid(handle)) return; + fseek((FILE*)handle.data, 0, SEEK_SET); +} + +usize os_file_tell(oshandle_t handle) { + if (!os_handle_valid(handle)) return 0; + off_t res = ftello((FILE*)handle.data); + info("> %zu", sizeof(off_t)); + return res != (off_t)-1 ? res : 0; +} + +usize os_file_size(oshandle_t handle) { + if (!os_handle_valid(handle)) return 0; + struct stat st = {0}; + int fd = fileno((FILE*)handle.data); + if (fstat(fd, &st) == 0) { + return st.st_size; + } + return 0; +} + +bool os_file_is_finished(oshandle_t handle) { + if (!os_handle_valid(handle)) return true; + char c = '\0'; + return fread(&c, 1, 1, (FILE*)handle.data) == 0; +} + +u64 os_file_time_fp(oshandle_t handle) { + if (!os_handle_valid(handle)) return 0; + struct stat st = {0}; + int fd = fileno((FILE*)handle.data); + if (fstat(fd, &st) == 0) { + return st.st_mtim.tv_nsec; + } + return 0; +} + +// == DIR WALKER ================================ + +struct dir_t { + DIR *ctx; + dir_entry_t next; +}; + +dir_t *os_dir_open(arena_t *arena, strview_t path) { + arena_t scratch = *arena; + str_t folder = str(&scratch, path); + + DIR *ctx = opendir(folder.buf); + if (!ctx) { + return NULL; + } + + dir_t *dir = alloc(arena, dir_t); + dir->ctx = ctx; + + return dir; +} + +void os_dir_close(dir_t *dir) { + if (!dir || !dir->ctx) return; + closedir(dir->ctx); + dir->ctx = NULL; +} + +bool os_dir_is_valid(dir_t *dir) { + return dir && dir->ctx; +} + +dir_entry_t *os_dir_next(arena_t *arena, dir_t *dir) { + struct dirent *data = readdir(dir->ctx); + if (!data) { + os_dir_close(dir); + return NULL; + } + + struct stat st = {0}; + stat(data->d_name, &st); + + dir->next.name = (str_t){ data->d_name, strlen(data->d_name) }; + dir->next.type = S_ISDIR(st.st_mode) ? DIRTYPE_DIR : DIRTYPE_FILE; + dir->next.file_size = st.st_size; + + return &dir->next; +} + +// == PROCESS =================================== + +void os_set_env_var(arena_t scratch, strview_t key, strview_t value) { + str_t var = str_fmt(&scratch, "%v=%v"); + putenv(var.buf); +} + +str_t os_get_env_var(arena_t *arena, strview_t key) { + arena_t scratch = *arena; + str_t key_str = str(&scratch, key); + const char *val = getenv(key_str.buf); + return str(arena, val); +} + +os_env_t *os_get_env(arena_t *arena) { + // TODO + return NULL; +} + +oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_cmd_options_t *options) { + // TODO + return os_handle_zero(); +} + +bool os_process_wait(oshandle_t proc, uint time, int *out_exit) { + // TODO + return false; +} + +// == MEMORY ==================================== + +void *os_alloc(usize size) { + return calloc(1, size); +} + +void os_free(void *ptr) { + free(ptr); +} + +void *os_reserve(usize size, usize *out_padded_size) { + usize alloc_size = os_pad_to_page(size); + + void *ptr = mmap( + NULL, + alloc_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0 + ); + + if (out_padded_size) { + *out_padded_size = alloc_size; + } + + return ptr; +} + +bool os_commit(void *ptr, usize num_of_pages) { + usize size = lin_data.info.page_size * num_of_pages; + int res = mprotect(ptr, size, PROT_READ | PROT_WRITE); + if (res == -1) { + err("os_commit error: %s", strerror(errno)); + } + return res != -1; +} + +bool os_release(void *ptr, usize size) { + if (!ptr) return false; + + int res = munmap(ptr, size); + + return res != -1; +} + +// == THREAD ==================================== + +void *os__lin_thread_entry_point(void *ptr) { + os_entity_t *entity = (os_entity_t *)ptr; + colla_assert(entity); + thread_func_t *func = entity->thread.func; + void *userdata = entity->thread.userdata; + int result = func(entity->thread.handle, userdata); + return (void*)((iptr)result); +} + +oshandle_t os_thread_launch(thread_func_t func, void *userdata) { + os_entity_t *entity = os__lin_alloc_entity(OS_KIND_THREAD); + + entity->thread.func = func; + entity->thread.userdata = userdata; + + int result = pthread_create(&entity->thread.handle, NULL, os__lin_thread_entry_point, entity); + + if (result) { + os__lin_free_entity(entity); + return os_handle_zero(); + } + + return (oshandle_t){ (uptr)entity }; +} + +bool os_thread_detach(oshandle_t thread) { + os_entity_t *entity = os__handle_to_entity(thread, OS_KIND_THREAD); + if (!entity) return false; + + bool result = pthread_detach(entity->thread.handle) == 0; + os__lin_free_entity(entity); + + return result; +} + +bool os_thread_join(oshandle_t thread, int *code) { + os_entity_t *entity = os__handle_to_entity(thread, OS_KIND_THREAD); + if (!entity) return false; + + void *outcode = NULL; + bool success = pthread_join(entity->thread.handle, &outcode) == 0; + + if (success && code) { + *code = (int)((iptr)outcode); + } + + os__lin_free_entity(entity); + + return success; +} + +u64 os_thread_get_id(oshandle_t thread) { + os_entity_t *entity = os__handle_to_entity(thread, OS_KIND_THREAD); + if (!entity) return 0; + + return entity->thread.handle; +} + +// == MUTEX ===================================== + +oshandle_t os_mutex_create(void) { + os_entity_t *entity = os__lin_alloc_entity(OS_KIND_MUTEX); + + if (pthread_mutex_init(&entity->mtx, NULL)) { + os__lin_free_entity(entity); + return os_handle_zero(); + } + + return (oshandle_t){ (uptr)entity }; +} + +void os_mutex_free(oshandle_t mutex) { + os_entity_t *entity = os__handle_to_entity(mutex, OS_KIND_MUTEX); + if (!entity) return; + + pthread_mutex_destroy(&entity->mtx); + os__lin_free_entity(entity); +} + +void os_mutex_lock(oshandle_t mutex) { + os_entity_t *entity = os__handle_to_entity(mutex, OS_KIND_MUTEX); + if (!entity) return; + + pthread_mutex_lock(&entity->mtx); +} + +void os_mutex_unlock(oshandle_t mutex) { + os_entity_t *entity = os__handle_to_entity(mutex, OS_KIND_MUTEX); + if (!entity) return; + + pthread_mutex_unlock(&entity->mtx); +} + +bool os_mutex_try_lock(oshandle_t mutex) { + os_entity_t *entity = os__handle_to_entity(mutex, OS_KIND_MUTEX); + if (!entity) return false; + + return pthread_mutex_trylock(&entity->mtx) == 0; +} + +#if !COLLA_NO_CONDITION_VARIABLE + +// == CONDITION VARIABLE ======================== + +oshandle_t os_cond_create(void) { + os_entity_t *entity = os__lin_alloc_entity(OS_KIND_CONDITION_VARIABLE); + + if (pthread_cond_init(&entity->cv, NULL)) { + os__lin_free_entity(entity); + return os_handle_zero(); + } + + return (oshandle_t){ (uptr)entity }; +} + +void os_cond_free(oshandle_t cond) { + os_entity_t *entity = os__handle_to_entity(cond, OS_KIND_CONDITION_VARIABLE); + if (!entity) return; + + pthread_cond_destroy(&entity->cv); + os__lin_free_entity(entity); +} + +void os_cond_signal(oshandle_t cond) { + os_entity_t *entity = os__handle_to_entity(cond, OS_KIND_CONDITION_VARIABLE); + if (!entity) return; + + pthread_cond_signal(&entity->cv); +} + +void os_cond_broadcast(oshandle_t cond) { + os_entity_t *entity = os__handle_to_entity(cond, OS_KIND_CONDITION_VARIABLE); + if (!entity) return; + + pthread_cond_broadcast(&entity->cv); +} + +void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds) { + os_entity_t *cond_entity = os__handle_to_entity(cond, OS_KIND_CONDITION_VARIABLE); + os_entity_t *mutex_entity = os__handle_to_entity(cond, OS_KIND_MUTEX); + if (!cond_entity) return; + if (!mutex_entity) return; + + pthread_cond_wait(&cond_entity->cv, &mutex_entity->mtx); +} + +#endif + +str_t str_os_from_str16(arena_t *arena, str16_t src) { + mbstate_t state = {0}; + + usize maxlen = src.len * 4; + char *buf = alloc(arena, char, maxlen); + usize len = 0; + + for (usize i = 0; i < src.len; ++i) { + usize to_add= c16rtomb(&buf[len], src.buf[i], &state); + if (to_add == (usize)-1) { + return STR_EMPTY; + } + + len += to_add; + + if (len >= maxlen) { + return STR_EMPTY; + } + } + + if (len > maxlen) { + usize extra = len - maxlen; + arena_pop(arena, extra - 1); + } + + return (str_t){ buf, len }; +} + +str16_t strv_os_to_str16(arena_t *arena, strview_t src) { + mbstate_t state = {0}; + + usize maxlen = src.len; + u16 *buf = alloc(arena, u16, maxlen); + + const char *in = src.buf; + const char *end = src.buf + src.len; + u16 *out = buf; + + for (usize rc; (rc = mbrtoc16(out, in, end - in, &state));) { + if (rc == (usize)-1) { + break; + } + else if(rc == (usize)-2) { + break; + } + else if (rc == (usize)-3) { + out += 1; + } + else { + in += rc; + out += 1; + } + } + + usize len = out - buf; + + if (len > maxlen) { + usize extra = len - maxlen; + arena_pop(arena, extra - 1); + } + + return (str16_t){ buf, len }; +} + +#if !COLLA_NO_NET + +// NETWORKING /////////////////////////////////// + +struct { + CURLcode last_error; +} net_lin = {0}; + +typedef struct lin_net_data_t lin_net_data_t; +struct lin_net_data_t { + outstream_t ostr; + http_request_callback_fn cb; + void *udata; +}; + +void net_init(void) { + curl_global_init(CURL_GLOBAL_DEFAULT); +} + +void net_cleanup(void) { + curl_global_cleanup(); +} + +iptr net_get_last_error(void) { + return net_lin.last_error; +} + +size_t lin__write_callback( + char *ptr, + size_t _, + size_t count, + void *userdata +) { + COLLA_UNUSED(_); + lin_net_data_t *data = userdata; + strview_t chunk = strv(ptr, count); + ostr_puts(&data->ostr, chunk); + + if (data->cb) { + data->cb(chunk, data->udata); + } + + return count; +} + +http_res_t http_request_cb( + http_request_desc_t *req, + http_request_callback_fn cb, + void *udata +) { + CURL *curl = curl_easy_init(); + + u8 tmpbuf[KB(5)]; + arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf); + + str_t url = str(&scratch, req->url); + struct curl_slist *headers = NULL; + + if (req->header_count) { + for (int i = 0; i < req->header_count; ++i) { + http_header_t *h = &req->headers[i]; + + struct curl_slist *header = alloc(&scratch, struct curl_slist); + header->data = str_fmt(&scratch, "%v: %v", h->key, h->value).buf; + list_push(headers, header); + } + } + else { + for_each(h, req->headers) { + struct curl_slist *header = alloc(&scratch, struct curl_slist); + header->data = str_fmt(&scratch, "%v: %v", h->key, h->value).buf; + list_push(headers, header); + } + } + + const char *method = http_get_method_string(req->request_type); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); + + if (!strv_is_empty(req->body)) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, req->body.len); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->body.buf); + } + + lin_net_data_t data = { + .ostr = ostr_init(req->arena), + .cb = cb, + .udata = udata, + }; + char *header_string = NULL; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, lin__write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &header_string); + + char* url_out = NULL; + long response_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url_out); + + net_lin.last_error = curl_easy_setopt( + curl, + CURLOPT_URL, + url.buf + ); + + curl_easy_cleanup(curl); + return (http_res_t){0}; +} + +// SOCKETS ////////////////////////// + +struct sockaddr_in sk__addrin_in(const char *ip, u16 port) { + struct sockaddr_in sk_addr = { + .sin_family = AF_INET, + .sin_port = htons(port), + }; + + if (!inet_pton(AF_INET, ip, &sk_addr.sin_addr)) { + err("inet_pton failed: %v", os_get_error_string(net_get_last_error())); + return (struct sockaddr_in){0}; + } + + return sk_addr; +} + +socket_t sk_open(sktype_e type) { + int sock_type = 0; + + switch(type) { + case SOCK_TCP: sock_type = SOCK_STREAM; break; + case SOCK_UDP: sock_type = SOCK_DGRAM; break; + default: fatal("skType not recognized: %d", type); break; + } + + return socket(AF_INET, sock_type, 0); +} + +socket_t sk_open_protocol(const char *protocol) { + struct protoent *proto = getprotobyname(protocol); + if(!proto) { + return -1; + } + return socket(AF_INET, SOCK_STREAM, proto->p_proto); +} + +bool sk_is_valid(socket_t sock) { + return sock != -1; +} + +bool sk_close(socket_t sock) { + return close(sock) != SOCKET_ERROR; +} + +bool sk_bind(socket_t sock, const char *ip, u16 port) { + struct sockaddr_in sk_addr = sk__addrin_in(ip, port); + if (sk_addr.sin_family == 0) { + return false; + } + return bind(sock, (struct sockaddr*)&sk_addr, sizeof(sk_addr)) != SOCKET_ERROR; +} + +bool sk_listen(socket_t sock, int backlog) { + return listen(sock, backlog) != SOCKET_ERROR; +} + +socket_t sk_accept(socket_t sock) { + struct sockaddr_in addr = {0}; + uint addr_size = sizeof(addr); + return accept(sock, (struct sockaddr*)&addr, &addr_size); +} + +bool sk_connect(socket_t sock, const char *server, u16 server_port) { + // TODO + return false; +} + +int sk_send(socket_t sock, const void *buf, int len) { + return send(sock, (const char *)buf, len, 0); +} + +int sk_recv(socket_t sock, void *buf, int len) { + return recv(sock, (char *)buf, len, 0); +} + +int sk_poll(skpoll_t *to_poll, int num_to_poll, int timeout) { + // TODO + return 0; +} + +oshandle_t sk_bind_event(socket_t sock, skevent_e event) { + // TODO + return os_handle_zero(); +} + +void sk_destroy_event(oshandle_t handle) { + // TODO +} + +void sk_reset_event(oshandle_t handle) { + // TODO +} + +#endif