summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-10-10 20:59:07 -0400
committerAndrew Opalach <andrew@akon.city> 2023-10-10 20:59:07 -0400
commita723efcc67b6c14ff0dd1efd1ba88596e959daaa (patch)
tree45384ce34e9601533fdc5bfbbdaf1b63ea7f1310 /src/util
downloadlibnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.tar.gz
libnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.tar.bz2
libnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.zip
Add libakiyo
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/util')
-rw-r--r--src/util/buffer.c42
-rw-r--r--src/util/buffer.h17
-rw-r--r--src/util/error.c7
-rw-r--r--src/util/error.h21
-rw-r--r--src/util/file/file.c0
-rw-r--r--src/util/file/file.h82
-rw-r--r--src/util/file/file_linux.c203
-rw-r--r--src/util/file/file_stdio.c100
-rw-r--r--src/util/file/util.c25
-rw-r--r--src/util/packet.c104
-rw-r--r--src/util/packet.h65
-rw-r--r--src/util/thread/thread.h66
-rw-r--r--src/util/thread/thread_linux.c86
-rw-r--r--src/util/thread/thread_windows.c81
-rw-r--r--src/util/timer/timer.h6
-rw-r--r--src/util/timer/timer_linux.c17
-rw-r--r--src/util/timer/timer_windows.c39
17 files changed, 961 insertions, 0 deletions
diff --git a/src/util/buffer.c b/src/util/buffer.c
new file mode 100644
index 0000000..26e242c
--- /dev/null
+++ b/src/util/buffer.c
@@ -0,0 +1,42 @@
+#include "buffer.h"
+
+#define INITIAL_SIZE 128
+
+void aki_buffer_ensure_size(struct aki_buffer *buf, size_t size)
+{
+ if (buf->alloc >= size) return;
+ if (size < INITIAL_SIZE) buf->alloc = INITIAL_SIZE;
+ else buf->alloc = al_next_power_of_two(size);
+ buf->data = buf->data ? al_realloc(buf->data, buf->alloc) : al_malloc(buf->alloc);
+}
+
+void aki_buffer_init(struct aki_buffer *buf)
+{
+ buf->data = NULL;
+ buf->size = 0;
+ buf->alloc = 0;
+}
+
+void aki_buffer_write(struct aki_buffer *buf, void *data, size_t index, size_t size)
+{
+ size_t reach = index + size;
+ aki_buffer_ensure_size(buf, reach);
+ if (reach > buf->size) buf->size = reach;
+ al_memcpy(&buf->data[index], data, size);
+}
+
+u8 *aki_buffer_get_ptr(struct aki_buffer *buf, size_t index)
+{
+ return &buf->data[index];
+}
+
+void aki_buffer_read(struct aki_buffer *buf, void *ptr, size_t index, size_t size)
+{
+ al_assert(index + size <= buf->size);
+ al_memcpy(ptr, &buf->data[index], size);
+}
+
+void aki_buffer_free(struct aki_buffer *buf)
+{
+ if (buf->data && buf->alloc > 0) al_free(buf->data);
+}
diff --git a/src/util/buffer.h b/src/util/buffer.h
new file mode 100644
index 0000000..3c40b3a
--- /dev/null
+++ b/src/util/buffer.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <al/types.h>
+#include <al/lib.h>
+
+struct aki_buffer {
+ u8 *data;
+ size_t size;
+ size_t alloc;
+};
+
+void aki_buffer_init(struct aki_buffer *buf);
+void aki_buffer_ensure_size(struct aki_buffer *buf, size_t size);
+void aki_buffer_write(struct aki_buffer *buf, void *data, size_t index, size_t size);
+u8 *aki_buffer_get_ptr(struct aki_buffer *buf, size_t index);
+void aki_buffer_read(struct aki_buffer *buf, void *ptr, size_t index, size_t size);
+void aki_buffer_free(struct aki_buffer *buf);
diff --git a/src/util/error.c b/src/util/error.c
new file mode 100644
index 0000000..f055416
--- /dev/null
+++ b/src/util/error.c
@@ -0,0 +1,7 @@
+#include "error.h"
+
+#ifndef _WIN32
+__thread char errorbuf[ERROR_STR_MAXLEN];
+#else
+__declspec(thread) char errorbuf[ERROR_STR_MAXLEN];
+#endif
diff --git a/src/util/error.h b/src/util/error.h
new file mode 100644
index 0000000..d014faa
--- /dev/null
+++ b/src/util/error.h
@@ -0,0 +1,21 @@
+#include <al/lib.h>
+
+#include <errno.h>
+
+#define ERROR_STR_MAXLEN 94
+
+#ifndef _WIN32
+extern __thread char errorbuf[ERROR_STR_MAXLEN];
+#else
+extern __declspec(thread) char errorbuf[ERROR_STR_MAXLEN];
+#endif
+
+static inline char *aki_strerror(s32 errnum)
+{
+#ifndef _WIN32
+ strerror_r(errnum, errorbuf, ERROR_STR_MAXLEN);
+#else
+ strerror_s(errorbuf, ERROR_STR_MAXLEN, errnum);
+#endif
+ return errorbuf;
+}
diff --git a/src/util/file/file.c b/src/util/file/file.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/util/file/file.c
diff --git a/src/util/file/file.h b/src/util/file/file.h
new file mode 100644
index 0000000..465d76a
--- /dev/null
+++ b/src/util/file/file.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#ifdef AKIYO_FILE_STDIO_COMPAT
+#include <stdio.h>
+#else
+#ifndef _WIN32
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#endif
+#endif
+
+#include <al/str.h>
+
+enum {
+ AKI_ENTRY_FILE = 0,
+ AKI_ENTRY_DIR,
+ AKI_ENTRY_OTHER
+};
+
+struct aki_file {
+#ifdef AKIYO_FILE_STDIO_COMPAT
+ FILE *file;
+#else
+#ifndef _WIN32
+ s32 fd;
+#else
+ // WINDOWS
+#endif
+#endif
+ size_t filesize;
+};
+
+struct aki_dir {
+#ifdef AKIYO_FILE_STDIO_COMPAT
+#else
+#ifndef _WIN32
+ DIR *dir;
+#else
+ // WINDOWS
+#endif
+#endif
+ str path;
+};
+
+struct aki_dir_entry {
+ u8 type;
+#ifdef AKIYO_FILE_STDIO_COMPAT
+#else
+#ifndef _WIN32
+ struct dirent *entry;
+#else
+ // WINDOWS
+#endif
+#endif
+ str name;
+ str path;
+};
+
+bool aki_file_open(struct aki_file *file, str *path, bool create);
+bool aki_file_exists(str *path);
+bool aki_file_truncate(struct aki_file *file, off_t size);
+off_t aki_file_seek(struct aki_file *file, off_t offset, s32 whence);
+size_t aki_file_get_filesize(struct aki_file *file);
+bool aki_file_write(struct aki_file *file, void *buf, size_t size);
+bool aki_file_read(struct aki_file *file, void *buf, size_t size);
+s32 aki_file_read_as_str(struct aki_file *file, str *out);
+s32 aki_file_read_as_c_str(struct aki_file *file, char **out);
+void *aki_file_mmap(struct aki_file *file);
+void *aki_file_mremap(struct aki_file *file, size_t size, void *old_map);
+void aki_file_munmap(struct aki_file *file, void *map);
+void aki_file_close(struct aki_file *file);
+
+bool aki_dir_open(struct aki_dir *dir, str *path);
+void aki_dir_close(struct aki_dir *dir);
+bool aki_dir_exists(str *path);
+bool aki_dir_create(str *path);
+bool aki_dir_read(struct aki_dir *dir, struct aki_dir_entry *entry);
+void aki_dir_entry_free(struct aki_dir_entry *entry);
diff --git a/src/util/file/file_linux.c b/src/util/file/file_linux.c
new file mode 100644
index 0000000..97e9688
--- /dev/null
+++ b/src/util/file/file_linux.c
@@ -0,0 +1,203 @@
+#include <sys/mman.h>
+#include <al/log.h>
+
+#include "../error.h"
+
+#include "file.h"
+
+bool aki_file_open(struct aki_file *file, str *path, bool create)
+{
+ char *c_str = al_str_to_c_str(path);
+ s32 flags = O_RDWR;
+ if (create) flags |= O_CREAT;
+ file->fd = open(c_str, flags, 0666);
+ al_free(c_str);
+ if (file->fd == -1) {
+ al_log_error("file", "open(%.*s) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ return false;
+ }
+ struct stat sb;
+ s32 res = fstat(file->fd, &sb);
+ if (res == -1) {
+ al_log_error("file", "fstat() failed (%s).", aki_strerror(errno));
+ close(file->fd);
+ return false;
+ }
+ file->filesize = sb.st_size;
+ return true;
+}
+
+bool aki_file_exists(str *path)
+{
+ char *c_str = al_str_to_c_str(path);
+ s32 ret = access(c_str, F_OK);
+ al_free(c_str);
+ if (ret != 0) {
+ al_log_error("file", "access(%.*s, F_OK) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ }
+ return ret == 0;
+}
+
+off_t aki_file_seek(struct aki_file *file, off_t offset, s32 whence)
+{
+ off_t res = lseek(file->fd, offset, whence);
+ if (res == (off_t)-1) {
+ al_log_error("file", "lseek(%jd, %d) failed (%s).", (intmax_t)offset, whence, aki_strerror(errno));
+ }
+ return res;
+}
+
+bool aki_file_truncate(struct aki_file *file, off_t size)
+{
+ s32 ret = ftruncate(file->fd, size);
+ if (ret != 0) {
+ al_log_error("file", "ftruncate(%jd) failed (%s).", (intmax_t)size, aki_strerror(errno));
+ return false;
+ }
+ file->filesize = size;
+ return true;
+}
+
+size_t aki_file_get_filesize(struct aki_file *file)
+{
+ return file->filesize;
+}
+
+#define file_io_loop(call, fail_case) \
+ size_t bc = 0; \
+ ssize_t res; \
+ do { \
+ res = call(file->fd, buf, size - bc); \
+ if (fail_case) { \
+ al_log_error("file", ""#call"() failed (%s).", aki_strerror(errno)); \
+ return false; \
+ } \
+ bc += res; \
+ } while (bc < size); \
+ return true
+
+bool aki_file_write(struct aki_file *file, void *buf, size_t size)
+{
+ file_io_loop(write, (res == -1));
+}
+
+bool aki_file_read(struct aki_file *file, void *buf, size_t size)
+{
+ file_io_loop(read, (res == -1));
+}
+
+void *aki_file_mmap(struct aki_file *file)
+{
+ void *map = mmap(NULL, file->filesize, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0L);
+ if (map == MAP_FAILED) {
+ al_log_error("file", "mmap() failed (%s).", aki_strerror(errno));
+ return NULL;
+ }
+ return map;
+}
+
+void *aki_file_mremap(struct aki_file *file, size_t size, void *old_map)
+{
+ printf("old_size: %zu, new_size: %zu\n", file->filesize, size);
+ void *map = mremap(old_map, file->filesize, size, MREMAP_MAYMOVE);
+ if (map == MAP_FAILED) {
+ al_log_error("file", "mremap(%p, %zu, %zu) failed (%s).", old_map,
+ file->filesize, size, aki_strerror(errno));
+ return NULL;
+ }
+ return map;
+}
+
+void aki_file_munmap(struct aki_file *file, void *map)
+{
+ s32 ret = munmap(map, file->filesize);
+ if (ret != 0) {
+ al_log_error("file", "munmap(%p, %zu) failed (%s).", map, file->filesize, aki_strerror(errno));
+ }
+}
+
+void aki_file_close(struct aki_file *file)
+{
+ close(file->fd);
+}
+
+bool aki_dir_open(struct aki_dir *dir, str *path)
+{
+ char *c_str = al_str_to_c_str(path);
+ dir->dir = opendir(c_str);
+ al_free(c_str);
+ if (!dir->dir) {
+ al_log_error("file", "opendir(%.*s) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ return false;
+ }
+ al_str_clone(&dir->path, path);
+ return true;
+}
+
+void aki_dir_close(struct aki_dir *dir)
+{
+ al_str_free(&dir->path);
+ closedir(dir->dir);
+}
+
+bool aki_dir_exists(str *path)
+{
+ struct aki_dir dir;
+ char *c_str = al_str_to_c_str(path);
+ dir.dir = opendir(c_str);
+ al_free(c_str);
+ if (!dir.dir) {
+ if (errno != ENOENT) {
+ al_log_error("file", "opendir(%.*s) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ }
+ return false;
+ }
+ return true;
+}
+
+bool aki_dir_create(str *path)
+{
+ char *c_str = al_str_to_c_str(path);
+ s32 ret = mkdir(c_str, 0700);
+ al_free(c_str);
+ if (ret == -1) {
+ al_log_error("file", "mkdir(%.*s) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool aki_dir_read(struct aki_dir *dir, struct aki_dir_entry *entry)
+{
+ errno = 0;
+ entry->entry = readdir(dir->dir);
+ if (!entry->entry) {
+ if (errno) {
+ al_log_error("file", "readdir() failed (%s).", aki_strerror(errno));
+ }
+ return false;
+ }
+ switch (entry->entry->d_type) {
+ case DT_REG:
+ case DT_UNKNOWN:
+ entry->type = AKI_ENTRY_FILE;
+ break;
+ case DT_DIR:
+ entry->type = AKI_ENTRY_DIR;
+ break;
+ default:
+ entry->type = AKI_ENTRY_OTHER;
+ break;
+ }
+ al_str_clone(&entry->name, al_str_c(entry->entry->d_name));
+ al_str_clone(&entry->path, &dir->path);
+ al_str_cat(&entry->path, al_str_c("/"));
+ al_str_cat(&entry->path, &entry->name);
+ return true;
+}
+
+void aki_dir_entry_free(struct aki_dir_entry *entry)
+{
+ al_str_free(&entry->name);
+ al_str_free(&entry->path);
+}
diff --git a/src/util/file/file_stdio.c b/src/util/file/file_stdio.c
new file mode 100644
index 0000000..52d8761
--- /dev/null
+++ b/src/util/file/file_stdio.c
@@ -0,0 +1,100 @@
+#include <al/log.h>
+
+#include "../error.h"
+
+#include "file.h"
+
+bool aki_file_open(struct aki_file *file, str *path, bool create)
+{
+ char *c_str = al_str_to_c_str(path);
+ const char *mode = create ? "wb+" : "rb+";
+#ifndef _WIN32
+ file->file = fopen(c_str, mode);
+#else
+ errno_t ret = fopen_s(&file->file, c_str, mode);
+#endif
+ al_free(c_str);
+#ifndef _WIN32
+ if (!file->file) {
+#else
+ if (ret != 0) {
+#endif
+ al_log_error("file_stdio", "fopen(%.*s) failed (%s).", AL_STR_PRINTF(path), aki_strerror(errno));
+ return false;
+ }
+ off_t pos = aki_file_seek(file, 0, SEEK_END);
+ if (pos == -1) {
+ aki_file_close(file);
+ return false;
+ }
+ file->filesize = pos;
+ rewind(file->file);
+ return true;
+}
+
+bool aki_file_exists(str *path)
+{
+ (void)path;
+ return false;
+}
+
+off_t aki_file_seek(struct aki_file *file, off_t offset, s32 whence)
+{
+ if (fseek(file->file, offset, whence) != 0) {
+ al_log_error("file_stdio", "fseek(%jd, %d) failed (%s).", (intmax_t)offset,
+ whence, aki_strerror(errno));
+ return -1;
+ }
+ return ftell(file->file);
+}
+
+bool aki_file_truncate(struct aki_file *file, off_t size)
+{
+ (void)file;
+ (void)size;
+ return true;
+}
+
+size_t aki_file_get_filesize(struct aki_file *file)
+{
+ return file->filesize;
+}
+
+bool aki_file_write(struct aki_file *file, void *buf, size_t size)
+{
+ size_t ret = fwrite(buf, size, 1, file->file);
+ if (ret == 0) {
+ al_log_error("file_stdio", "fwrite(%zu) failed (%s).", size, aki_strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool aki_file_read(struct aki_file *file, void *buf, size_t size)
+{
+ size_t ret = fread(buf, size, 1, file->file);
+ if (ret == 0) {
+ al_log_error("file_stdio", "fread(%zu) failed (%s).", size, aki_strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+void *aki_file_mmap(struct aki_file *file)
+{
+ (void)file;
+ al_assert(false);
+ return NULL;
+}
+
+void aki_file_close(struct aki_file *file)
+{
+ fclose(file->file);
+}
+
+bool aki_dir_open(struct aki_dir *dir, str *path) { (void)dir; (void)path; return false; }
+void aki_dir_close(struct aki_dir *dir) { (void)dir; }
+bool aki_dir_exists(str *path) { (void)path; return false; }
+bool aki_dir_create(str *path) { (void)path; return false; }
+bool aki_dir_read(struct aki_dir *dir, struct aki_dir_entry *entry) { (void)dir; (void)entry; return false; }
+void aki_dir_entry_free(struct aki_dir_entry *entry) { (void)entry; }
diff --git a/src/util/file/util.c b/src/util/file/util.c
new file mode 100644
index 0000000..e893de7
--- /dev/null
+++ b/src/util/file/util.c
@@ -0,0 +1,25 @@
+#include "file.h"
+
+s32 aki_file_read_as_str(struct aki_file *file, str *out)
+{
+ size_t size = file->filesize;
+ al_str_sized(out, size);
+ if (!aki_file_read(file, (void *)out->data, size)) {
+ al_str_free(out);
+ return -1;
+ }
+ out->len = size;
+ return size;
+}
+
+s32 aki_file_read_as_c_str(struct aki_file *file, char **out)
+{
+ size_t size = file->filesize;
+ *out = al_malloc(size + 1);
+ if (!aki_file_read(file, (void *)*out, size)) {
+ al_free(*out);
+ return -1;
+ }
+ (*out)[size] = '\n';
+ return size;
+}
diff --git a/src/util/packet.c b/src/util/packet.c
new file mode 100644
index 0000000..c58d656
--- /dev/null
+++ b/src/util/packet.c
@@ -0,0 +1,104 @@
+#include "packet.h"
+
+struct aki_packet *aki_packet_create(void)
+{
+ struct aki_packet *packet = (struct aki_packet *)al_malloc(sizeof(struct aki_packet));
+ aki_buffer_init(&packet->buffer);
+ aki_packet_reset(packet);
+ return packet;
+}
+
+struct aki_packet *aki_packet_clone(struct aki_packet *packet)
+{
+ struct aki_packet *c = aki_packet_create();
+ aki_buffer_ensure_size(&c->buffer, packet->buffer.size);
+ al_memcpy(aki_buffer_get_ptr(&c->buffer, 0), aki_buffer_get_ptr(&packet->buffer, 0), packet->buffer.size);
+ c->windex = packet->windex;
+ c->rindex = packet->rindex;
+ return c;
+}
+
+void aki_packet_reset(struct aki_packet *packet)
+{
+ aki_buffer_ensure_size(&packet->buffer, AKI_PACKET_HEADER_LENGTH);
+ packet->rindex = AKI_PACKET_HEADER_LENGTH;
+ packet->windex = AKI_PACKET_HEADER_LENGTH;
+}
+
+void aki_packet_write_size(struct aki_packet *packet)
+{
+ *((u32 *)aki_buffer_get_ptr(&packet->buffer, 0)) = packet->windex;
+}
+
+u32 aki_packet_size(struct aki_packet *packet)
+{
+ return *((u32 *)aki_buffer_get_ptr(&packet->buffer, 0));
+}
+
+#define DEFINE_PACKET_WRITE_FUNC(type) \
+ void aki_packet_write_##type(struct aki_packet *packet, type v) \
+ { \
+ AKI_PACKET_WRITE_TYPE(packet, type, v); \
+ }
+
+DEFINE_PACKET_WRITE_FUNC(u8)
+DEFINE_PACKET_WRITE_FUNC(s8)
+DEFINE_PACKET_WRITE_FUNC(u16)
+DEFINE_PACKET_WRITE_FUNC(s16)
+DEFINE_PACKET_WRITE_FUNC(u32)
+DEFINE_PACKET_WRITE_FUNC(s32)
+DEFINE_PACKET_WRITE_FUNC(u64)
+DEFINE_PACKET_WRITE_FUNC(s64)
+DEFINE_PACKET_WRITE_FUNC(f32)
+DEFINE_PACKET_WRITE_FUNC(f64)
+
+void aki_packet_write_string(struct aki_packet *packet, str *s)
+{
+ AKI_PACKET_WRITE_TYPE(packet, u32, s->len);
+ AKI_PACKET_WRITE_DATA(packet, s->data, s->len);
+}
+
+void aki_packet_write_buffer(struct aki_packet *packet, struct aki_buffer *buf)
+{
+ AKI_PACKET_WRITE_TYPE(packet, size_t, buf->size);
+ AKI_PACKET_WRITE_DATA(packet, aki_buffer_get_ptr(buf, 0), buf->size);
+}
+
+#define DEFINE_PACKET_READ_FUNC(type) \
+ type aki_packet_read_##type(struct aki_packet *packet) \
+ { \
+ type r; \
+ AKI_PACKET_READ_TYPE(packet, type, r); \
+ return r; \
+ }
+
+DEFINE_PACKET_READ_FUNC(u8)
+DEFINE_PACKET_READ_FUNC(s8)
+DEFINE_PACKET_READ_FUNC(u16)
+DEFINE_PACKET_READ_FUNC(s16)
+DEFINE_PACKET_READ_FUNC(u32)
+DEFINE_PACKET_READ_FUNC(s32)
+DEFINE_PACKET_READ_FUNC(u64)
+DEFINE_PACKET_READ_FUNC(s64)
+DEFINE_PACKET_READ_FUNC(f32)
+DEFINE_PACKET_READ_FUNC(f64)
+
+void aki_packet_read_string(struct aki_packet *packet, str *s)
+{
+ AKI_PACKET_READ_TYPE(packet, u32, s->len);
+ AKI_PACKET_READ_DATA(packet, s->len, s->data);
+ s->alloc = 0;
+}
+
+void aki_packet_read_buffer(struct aki_packet *packet, struct aki_buffer *buf)
+{
+ AKI_PACKET_READ_TYPE(packet, size_t, buf->size);
+ AKI_PACKET_READ_DATA(packet, buf->size, buf->data);
+ buf->alloc = 0;
+}
+
+void aki_packet_free(struct aki_packet *packet)
+{
+ aki_buffer_free(&packet->buffer);
+ al_free(packet);
+}
diff --git a/src/util/packet.h b/src/util/packet.h
new file mode 100644
index 0000000..14acf68
--- /dev/null
+++ b/src/util/packet.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <al/types.h>
+#include <al/str.h>
+#include <al/array.h>
+
+#include "../util/buffer.h"
+
+#define AKI_PACKET_HEADER_LENGTH (u32)(sizeof(u32))
+
+struct aki_packet {
+ struct aki_buffer buffer;
+ u32 rindex;
+ u32 windex;
+ void *userdata;
+};
+
+#define AKI_PACKET_WRITE_TYPE(p, type, v) \
+ aki_buffer_write(&(p)->buffer, &(v), (p)->windex, sizeof(type)); \
+ (p)->windex += (u32)sizeof(type)
+
+#define AKI_PACKET_WRITE_DATA(p, data, len) \
+ aki_buffer_write(&(p)->buffer, (data), (p)->windex, (len)); \
+ (p)->windex += (u32)len
+
+#define AKI_PACKET_READ_TYPE(p, type, r) \
+ r = *((type *)aki_buffer_get_ptr(&(p)->buffer, (p)->rindex)); \
+ (p)->rindex += (u32)sizeof(type);
+
+#define AKI_PACKET_READ_DATA(p, len, r) \
+ r = (void *)aki_buffer_get_ptr(&(p)->buffer, (p)->rindex); \
+ (p)->rindex += (u32)len;
+
+struct aki_packet *aki_packet_create(void);
+struct aki_packet *aki_packet_clone(struct aki_packet *packet);
+void aki_packet_reset(struct aki_packet *packet);
+
+void aki_packet_write_size(struct aki_packet *packet);
+u32 aki_packet_size(struct aki_packet *packet);
+
+void aki_packet_write_s8(struct aki_packet *packet, s8 v);
+void aki_packet_write_u8(struct aki_packet *packet, u8 v);
+void aki_packet_write_s16(struct aki_packet *packet, s16 v);
+void aki_packet_write_u16(struct aki_packet *packet, u16 v);
+void aki_packet_write_s32(struct aki_packet *packet, s32 v);
+void aki_packet_write_u32(struct aki_packet *packet, u32 v);
+void aki_packet_write_s64(struct aki_packet *packet, s64 v);
+void aki_packet_write_u64(struct aki_packet *packet, u64 v);
+void aki_packet_write_f32(struct aki_packet *packet, f32 v);
+void aki_packet_write_string(struct aki_packet *packet, str *s);
+void aki_packet_write_buffer(struct aki_packet *packet, struct aki_buffer *buf);
+
+s8 aki_packet_read_s8(struct aki_packet *packet);
+u8 aki_packet_read_u8(struct aki_packet *packet);
+s16 aki_packet_read_s16(struct aki_packet *packet);
+u16 aki_packet_read_u16(struct aki_packet *packet);
+s32 aki_packet_read_s32(struct aki_packet *packet);
+u32 aki_packet_read_u32(struct aki_packet *packet);
+s64 aki_packet_read_s64(struct aki_packet *packet);
+u64 aki_packet_read_u64(struct aki_packet *packet);
+f32 aki_packet_read_f32(struct aki_packet *packet);
+void aki_packet_read_string(struct aki_packet *packet, str *s);
+void aki_packet_read_buffer(struct aki_packet *packet, struct aki_buffer *buf);
+
+void aki_packet_free(struct aki_packet *packet);
diff --git a/src/util/thread/thread.h b/src/util/thread/thread.h
new file mode 100644
index 0000000..eb63b8d
--- /dev/null
+++ b/src/util/thread/thread.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <al/types.h>
+
+#ifndef _WIN32
+#include <pthread.h>
+#else
+#include "../../winwrap.h"
+#endif
+
+struct aki_thread {
+#ifndef _WIN32
+ pthread_t thread;
+#else
+ HANDLE thread;
+#endif
+};
+
+struct aki_mutex {
+#ifndef _WIN32
+ pthread_mutex_t mutex;
+#else
+ CRITICAL_SECTION mutex;
+#endif
+};
+
+struct aki_cond {
+#ifndef _WIN32
+ pthread_cond_t cond;
+#else
+ CONDITION_VARIABLE cond;
+#endif
+ bool condition;
+};
+
+typedef f64 aki_os_tstamp;
+
+#define AKI_TS_FROM_USEC(usec) (usec * 1e-6)
+
+#ifndef _WIN32
+#define AKI_THREADCALL
+typedef void * aki_thread_result;
+#else
+#define AKI_THREADCALL __stdcall
+typedef unsigned long aki_thread_result;
+#endif
+
+typedef aki_thread_result (AKI_THREADCALL * aki_thread_func)(void *);
+
+void aki_thread_sleep(aki_os_tstamp ts);
+
+void aki_thread_create(struct aki_thread *thread, aki_thread_func func, void *userdata);
+void aki_thread_join(struct aki_thread *thread);
+//void aki_thread_cancel(struct aki_thread *thread);
+//void aki_thread_detach(struct aki_thread *thread);
+
+void aki_mutex_init(struct aki_mutex *mutex);
+void aki_mutex_lock(struct aki_mutex *mutex);
+void aki_mutex_unlock(struct aki_mutex *mutex);
+void aki_mutex_destroy(struct aki_mutex *mutex);
+
+void aki_cond_init(struct aki_cond *cond);
+void aki_cond_wait(struct aki_cond *cond, struct aki_mutex *mutex);
+bool aki_cond_is_waiting(struct aki_cond *cond);
+void aki_cond_signal(struct aki_cond *cond);
+void aki_cond_destroy(struct aki_cond *cond);
diff --git a/src/util/thread/thread_linux.c b/src/util/thread/thread_linux.c
new file mode 100644
index 0000000..0214ed8
--- /dev/null
+++ b/src/util/thread/thread_linux.c
@@ -0,0 +1,86 @@
+#include <al/types.h>
+#include <time.h>
+
+#include "../error.h"
+
+#include "thread.h"
+
+void aki_thread_sleep(aki_os_tstamp ts)
+{
+ struct timespec tv;
+ tv.tv_sec = (s64)ts;
+ tv.tv_nsec = (s64)((ts - tv.tv_sec) * 1e9);
+ while (nanosleep(&tv, &tv) == -1 && errno == EINTR) {}
+}
+
+void aki_thread_create(struct aki_thread *thread, aki_thread_func func, void *userdata)
+{
+ pthread_create(&thread->thread, NULL, func, userdata);
+}
+
+void aki_thread_join(struct aki_thread *thread)
+{
+ pthread_join(thread->thread, NULL);
+}
+
+/*
+void aki_thread_cancel(struct aki_thread *thread)
+{
+ pthread_cancel(thread->thread);
+}
+
+void aki_thread_detach(struct aki_thread *thread)
+{
+ pthread_detach(thread->thread);
+}
+*/
+
+void aki_mutex_init(struct aki_mutex *mutex)
+{
+ pthread_mutex_init(&mutex->mutex, NULL);
+}
+
+void aki_mutex_lock(struct aki_mutex *mutex)
+{
+ pthread_mutex_lock(&mutex->mutex);
+}
+
+void aki_mutex_unlock(struct aki_mutex *mutex)
+{
+ pthread_mutex_unlock(&mutex->mutex);
+}
+
+void aki_mutex_destroy(struct aki_mutex *mutex)
+{
+ pthread_mutex_destroy(&mutex->mutex);
+}
+
+void aki_cond_init(struct aki_cond *cond)
+{
+ cond->condition = true;
+ pthread_cond_init(&cond->cond, NULL);
+}
+
+void aki_cond_wait(struct aki_cond *cond, struct aki_mutex *mutex)
+{
+ cond->condition = false;
+ while (!cond->condition) {
+ pthread_cond_wait(&cond->cond, &mutex->mutex);
+ }
+}
+
+bool aki_cond_is_waiting(struct aki_cond *cond)
+{
+ return !cond->condition;
+}
+
+void aki_cond_signal(struct aki_cond *cond)
+{
+ cond->condition = true;
+ pthread_cond_signal(&cond->cond);
+}
+
+void aki_cond_destroy(struct aki_cond *cond)
+{
+ pthread_cond_destroy(&cond->cond);
+}
diff --git a/src/util/thread/thread_windows.c b/src/util/thread/thread_windows.c
new file mode 100644
index 0000000..9ea9ead
--- /dev/null
+++ b/src/util/thread/thread_windows.c
@@ -0,0 +1,81 @@
+#include "thread.h"
+
+NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval);
+
+void aki_thread_sleep(aki_os_tstamp ts)
+{
+ LARGE_INTEGER intr;
+ intr.QuadPart = -1 * (s64)(ts * 1e7);
+ NtDelayExecution(false, &intr);
+}
+
+void aki_thread_create(struct aki_thread *thread, aki_thread_func func, void *userdata)
+{
+ thread->thread = CreateThread(NULL, 0, func, userdata, 0, NULL);
+}
+
+void aki_thread_join(struct aki_thread *thread)
+{
+ WaitForSingleObject(thread->thread, INFINITE);
+ CloseHandle(thread->thread);
+}
+
+/*
+void aki_thread_cancel(struct aki_thread *thread)
+{
+}
+
+void aki_thread_detach(struct aki_thread *thread)
+{
+}
+*/
+
+void aki_mutex_init(struct aki_mutex *mutex)
+{
+ InitializeCriticalSection(&mutex->mutex);
+}
+
+void aki_mutex_lock(struct aki_mutex *mutex)
+{
+ EnterCriticalSection(&mutex->mutex);
+}
+
+void aki_mutex_unlock(struct aki_mutex *mutex)
+{
+ LeaveCriticalSection(&mutex->mutex);
+}
+
+void aki_mutex_destroy(struct aki_mutex *mutex)
+{
+ DeleteCriticalSection(&mutex->mutex);
+}
+
+void aki_cond_init(struct aki_cond *cond)
+{
+ cond->condition = true;
+ InitializeConditionVariable(&cond->cond);
+}
+
+void aki_cond_wait(struct aki_cond *cond, struct aki_mutex *mutex)
+{
+ cond->condition = false;
+ while (!cond->condition) {
+ SleepConditionVariableCS(&cond->cond, &mutex->mutex, INFINITE);
+ }
+}
+
+bool aki_cond_is_waiting(struct aki_cond *cond)
+{
+ return !cond->condition;
+}
+
+void aki_cond_signal(struct aki_cond *cond)
+{
+ cond->condition = true;
+ WakeConditionVariable(&cond->cond);
+}
+
+void aki_cond_destroy(struct aki_cond *cond)
+{
+ (void)cond;
+}
diff --git a/src/util/timer/timer.h b/src/util/timer/timer.h
new file mode 100644
index 0000000..2bf6b95
--- /dev/null
+++ b/src/util/timer/timer.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <al/types.h>
+
+u64 aki_get_timestamp(void);
+f64 aki_get_tick(void);
diff --git a/src/util/timer/timer_linux.c b/src/util/timer/timer_linux.c
new file mode 100644
index 0000000..d93bfdc
--- /dev/null
+++ b/src/util/timer/timer_linux.c
@@ -0,0 +1,17 @@
+#include <time.h>
+
+#include "timer.h"
+
+u64 aki_get_timestamp(void)
+{
+ struct timespec spec;
+ clock_gettime(CLOCK_REALTIME, &spec);
+ return 1e6 * spec.tv_sec + spec.tv_nsec * 1e-3;
+}
+
+f64 aki_get_tick(void)
+{
+ struct timespec spec;
+ clock_gettime(CLOCK_MONOTONIC, &spec);
+ return spec.tv_sec + spec.tv_nsec * 1e-9;
+}
diff --git a/src/util/timer/timer_windows.c b/src/util/timer/timer_windows.c
new file mode 100644
index 0000000..37a8fd8
--- /dev/null
+++ b/src/util/timer/timer_windows.c
@@ -0,0 +1,39 @@
+#include <al/lib.h>
+
+#include "../../winwrap.h"
+
+#include "timer.h"
+
+f64 aki_get_tick(void)
+{
+ LARGE_INTEGER result;
+ QueryPerformanceCounter(&result);
+ return (result.QuadPart/(f64)performance_frequency.QuadPart);
+}
+
+static SYSTEMTIME unix_epoch = {
+ .wYear = 1970,
+ .wMonth = 1,
+ .wDayOfWeek = 4,
+ .wDay = 1,
+ .wHour = 0,
+ .wMinute = 0,
+ .wSecond = 0,
+ .wMilliseconds = 0
+};
+
+u64 aki_get_timestamp(void)
+{
+ FILETIME result0, result1;
+ SystemTimeToFileTime(&unix_epoch, &result0);
+ ULARGE_INTEGER li0 = {
+ .LowPart = result0.dwLowDateTime,
+ .HighPart = result0.dwHighDateTime
+ };
+ GetSystemTimePreciseAsFileTime(&result1);
+ ULARGE_INTEGER li1 = {
+ .LowPart = result1.dwLowDateTime,
+ .HighPart = result1.dwHighDateTime
+ };
+ return (li1.QuadPart - li0.QuadPart) / 10;
+}