2016-07-07 07:04:24 +00:00
|
|
|
#include "util.h"
|
2022-03-30 19:31:39 +00:00
|
|
|
#include <string.h>
|
2016-07-07 07:04:24 +00:00
|
|
|
#include <stdlib.h>
|
2024-03-27 19:48:35 +00:00
|
|
|
#include <stdbool.h>
|
2021-02-11 23:37:55 +00:00
|
|
|
#include <stdatomic.h>
|
2024-03-27 19:48:35 +00:00
|
|
|
#include <setjmp.h>
|
2024-03-11 21:38:00 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
// Allocation
|
2024-03-27 19:48:35 +00:00
|
|
|
|
2024-03-11 21:38:00 +00:00
|
|
|
void* lovrMalloc(size_t size) {
|
|
|
|
void* data = malloc(size);
|
2024-03-27 19:48:35 +00:00
|
|
|
lovrAssert(data, "Out of memory");
|
2024-03-11 21:38:00 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* lovrCalloc(size_t size) {
|
|
|
|
void* data = calloc(1, size);
|
2024-03-27 19:48:35 +00:00
|
|
|
lovrAssert(data, "Out of memory");
|
2024-03-11 21:38:00 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* lovrRealloc(void* old, size_t size) {
|
|
|
|
void* data = realloc(old, size);
|
2024-03-27 19:48:35 +00:00
|
|
|
lovrAssert(data, "Out of memory");
|
2024-03-11 21:38:00 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrFree(void* data) {
|
|
|
|
free(data);
|
|
|
|
}
|
2016-07-07 07:04:24 +00:00
|
|
|
|
2024-03-27 19:48:35 +00:00
|
|
|
// Refcounting
|
|
|
|
|
|
|
|
#if ATOMIC_INT_LOCK_FREE != 2
|
|
|
|
#error "Lock-free integer atomics are not supported on this platform, but are required for refcounting"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void lovrRetain(void* object) {
|
|
|
|
if (object) {
|
|
|
|
atomic_fetch_add((atomic_uint*) object, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrRelease(void* object, void (*destructor)(void*)) {
|
|
|
|
if (object && atomic_fetch_sub((atomic_uint*) object, 1) == 1) {
|
|
|
|
destructor(object);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Defer
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
void (*fn)(void*);
|
|
|
|
void* arg;
|
|
|
|
} Closure;
|
|
|
|
|
|
|
|
static LOVR_THREAD_LOCAL struct {
|
|
|
|
Closure stack[16];
|
|
|
|
uint16_t releaseMask;
|
|
|
|
uint16_t errMask;
|
|
|
|
uint32_t top;
|
|
|
|
} defer;
|
|
|
|
|
|
|
|
uint32_t lovrDeferPush(void) {
|
|
|
|
return defer.top;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void deferPop(uint32_t base, bool err) {
|
|
|
|
while (defer.top > base) {
|
|
|
|
uint32_t index = --defer.top;
|
|
|
|
Closure c = defer.stack[index];
|
|
|
|
if (err || (defer.errMask & (1u << index)) == 0) {
|
|
|
|
if (defer.releaseMask & (1u << index)) {
|
|
|
|
lovrRelease(c.arg, c.fn);
|
|
|
|
} else {
|
|
|
|
c.fn(c.arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrDeferPop(uint32_t base) {
|
|
|
|
deferPop(base, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrDefer(void (*fn)(void*), void* arg) {
|
|
|
|
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
|
|
|
defer.releaseMask &= ~(1u << defer.top);
|
|
|
|
defer.errMask &= ~(1u << defer.top);
|
|
|
|
defer.stack[defer.top++] = (Closure) { fn, arg };
|
|
|
|
}
|
2018-09-26 17:39:17 +00:00
|
|
|
|
2024-03-27 19:48:35 +00:00
|
|
|
void lovrErrDefer(void (*fn)(void*), void* arg) {
|
|
|
|
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
|
|
|
defer.releaseMask &= ~(1u << defer.top);
|
|
|
|
defer.errMask |= (1u << defer.top);
|
|
|
|
defer.stack[defer.top++] = (Closure) { fn, arg };
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrDeferRelease(void* object, void (*destructor)(void*)) {
|
|
|
|
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
|
|
|
defer.releaseMask |= (1u << defer.top);
|
|
|
|
defer.errMask &= ~(1u << defer.top);
|
|
|
|
defer.stack[defer.top++] = (Closure) { destructor, object };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exceptions
|
|
|
|
|
|
|
|
typedef struct Handler {
|
|
|
|
struct Handler* prev;
|
|
|
|
uint32_t baseDefer;
|
|
|
|
void (*catch)(void* arg, const char* format, va_list args);
|
|
|
|
void* arg;
|
|
|
|
jmp_buf env;
|
|
|
|
} Handler;
|
|
|
|
|
|
|
|
static LOVR_THREAD_LOCAL Handler* lovrHandler;
|
|
|
|
|
|
|
|
void lovrTry(void (*fn)(void*), void* arg, void(*catch)(void*, const char*, va_list), void* catchArg) {
|
|
|
|
lovrHandler = &(Handler) {
|
|
|
|
.prev = lovrHandler,
|
|
|
|
.baseDefer = defer.top,
|
|
|
|
.catch = catch,
|
|
|
|
.arg = arg
|
|
|
|
};
|
|
|
|
|
|
|
|
if (setjmp(lovrHandler->env) == 0) {
|
|
|
|
fn(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
lovrHandler = lovrHandler->prev;
|
2018-09-26 17:39:17 +00:00
|
|
|
}
|
2017-08-10 07:08:29 +00:00
|
|
|
|
|
|
|
void lovrThrow(const char* format, ...) {
|
2024-03-27 19:48:35 +00:00
|
|
|
deferPop(lovrHandler->baseDefer, true);
|
2019-08-22 02:02:02 +00:00
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
2024-03-27 19:48:35 +00:00
|
|
|
lovrHandler->catch(lovrHandler->arg, format, args);
|
2019-08-22 02:02:02 +00:00
|
|
|
va_end(args);
|
2024-03-27 19:48:35 +00:00
|
|
|
longjmp(lovrHandler->env, 1);
|
2017-08-10 07:08:29 +00:00
|
|
|
}
|
lovr.log;
lovr.log is a new callback that is invoked whenever LÖVR wants to
send the project a message. For example, this could be a performance
warning from the graphics module, an error message from one of the
headset backends, or an API deprecation notice.
The callback's signature is (message, level, tag). The message is a
string containing the message to log, level is a string that is currently
one of "debug", "info", "warn", "error", and tag is an optional string
that is used to indicate the source of the message for grouping purposes.
The default implementation of the callback just prints the message,
but the callback can be overridden to do things like filter messages,
write them to a file, or even render them in VR. Projects can also
invoke the callback directly to log their own messages.
2020-07-06 22:20:55 +00:00
|
|
|
|
|
|
|
// Logging
|
|
|
|
|
2024-03-27 19:48:35 +00:00
|
|
|
static fn_log* lovrLogCallback;
|
|
|
|
static void* lovrLogUserdata;
|
|
|
|
|
|
|
|
void lovrSetLogCallback(fn_log* callback, void* userdata) {
|
lovr.log;
lovr.log is a new callback that is invoked whenever LÖVR wants to
send the project a message. For example, this could be a performance
warning from the graphics module, an error message from one of the
headset backends, or an API deprecation notice.
The callback's signature is (message, level, tag). The message is a
string containing the message to log, level is a string that is currently
one of "debug", "info", "warn", "error", and tag is an optional string
that is used to indicate the source of the message for grouping purposes.
The default implementation of the callback just prints the message,
but the callback can be overridden to do things like filter messages,
write them to a file, or even render them in VR. Projects can also
invoke the callback directly to log their own messages.
2020-07-06 22:20:55 +00:00
|
|
|
lovrLogCallback = callback;
|
|
|
|
lovrLogUserdata = userdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lovrLog(int level, const char* tag, const char* format, ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
lovrLogCallback(lovrLogUserdata, level, tag, format, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
2021-02-09 03:55:51 +00:00
|
|
|
|
2022-03-30 19:31:39 +00:00
|
|
|
// Hashmap
|
2024-03-27 19:48:35 +00:00
|
|
|
|
2022-03-30 19:31:39 +00:00
|
|
|
static void map_rehash(map_t* map) {
|
|
|
|
map_t old = *map;
|
|
|
|
map->size <<= 1;
|
|
|
|
map->hashes = malloc(2 * map->size * sizeof(uint64_t));
|
|
|
|
map->values = map->hashes + map->size;
|
|
|
|
lovrAssert(map->size && map->hashes, "Out of memory");
|
|
|
|
memset(map->hashes, 0xff, 2 * map->size * sizeof(uint64_t));
|
|
|
|
|
|
|
|
if (old.hashes) {
|
|
|
|
uint64_t mask = map->size - 1;
|
|
|
|
for (uint32_t i = 0; i < old.size; i++) {
|
|
|
|
if (old.hashes[i] != MAP_NIL) {
|
|
|
|
uint64_t index = old.hashes[i] & mask;
|
|
|
|
while (map->hashes[index] != MAP_NIL) {
|
|
|
|
index = (index + 1) & mask;
|
|
|
|
}
|
|
|
|
map->hashes[index] = old.hashes[i];
|
|
|
|
map->values[index] = old.values[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(old.hashes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint64_t map_find(map_t* map, uint64_t hash) {
|
|
|
|
uint64_t mask = map->size - 1;
|
|
|
|
uint64_t h = hash & mask;
|
|
|
|
|
|
|
|
while (map->hashes[h] != hash && map->hashes[h] != MAP_NIL) {
|
|
|
|
h = (h + 1) & mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
void map_init(map_t* map, uint32_t n) {
|
2022-06-14 23:52:19 +00:00
|
|
|
map->size = 1;
|
|
|
|
while (map->size + (map->size >> 1) < n) {
|
|
|
|
map->size <<= 1;
|
|
|
|
}
|
2022-03-30 19:31:39 +00:00
|
|
|
map->used = 0;
|
|
|
|
map->hashes = NULL;
|
|
|
|
map_rehash(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
void map_free(map_t* map) {
|
|
|
|
free(map->hashes);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t map_get(map_t* map, uint64_t hash) {
|
|
|
|
return map->values[map_find(map, hash)];
|
|
|
|
}
|
|
|
|
|
|
|
|
void map_set(map_t* map, uint64_t hash, uint64_t value) {
|
|
|
|
if (map->used >= (map->size >> 1) + (map->size >> 2)) {
|
|
|
|
map_rehash(map);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t h = map_find(map, hash);
|
|
|
|
map->used += map->hashes[h] == MAP_NIL;
|
|
|
|
map->hashes[h] = hash;
|
|
|
|
map->values[h] = value;
|
|
|
|
}
|
|
|
|
|
2021-02-09 03:55:51 +00:00
|
|
|
// UTF-8
|
|
|
|
// https://github.com/starwing/luautf8
|
2024-03-27 19:48:35 +00:00
|
|
|
|
2021-02-09 03:55:51 +00:00
|
|
|
size_t utf8_decode(const char *s, const char *e, unsigned *pch) {
|
|
|
|
unsigned ch;
|
|
|
|
|
|
|
|
if (s >= e) {
|
|
|
|
*pch = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ch = (unsigned char)s[0];
|
|
|
|
if (ch < 0xC0) goto fallback;
|
|
|
|
if (ch < 0xE0) {
|
|
|
|
if (s+1 >= e || (s[1] & 0xC0) != 0x80)
|
|
|
|
goto fallback;
|
|
|
|
*pch = ((ch & 0x1F) << 6) |
|
|
|
|
(s[1] & 0x3F);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if (ch < 0xF0) {
|
|
|
|
if (s+2 >= e || (s[1] & 0xC0) != 0x80
|
|
|
|
|| (s[2] & 0xC0) != 0x80)
|
|
|
|
goto fallback;
|
|
|
|
*pch = ((ch & 0x0F) << 12) |
|
|
|
|
((s[1] & 0x3F) << 6) |
|
|
|
|
(s[2] & 0x3F);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int count = 0; /* to count number of continuation bytes */
|
|
|
|
unsigned res = 0;
|
|
|
|
while ((ch & 0x40) != 0) { /* still have continuation bytes? */
|
|
|
|
int cc = (unsigned char)s[++count];
|
|
|
|
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
|
|
|
goto fallback; /* invalid byte sequence, fallback */
|
|
|
|
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
|
|
|
ch <<= 1; /* to test next bit */
|
|
|
|
}
|
|
|
|
if (count > 5)
|
|
|
|
goto fallback; /* invalid byte sequence */
|
|
|
|
res |= ((ch & 0x7F) << (count * 5)); /* add first byte */
|
|
|
|
*pch = res;
|
|
|
|
return count+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fallback:
|
|
|
|
*pch = ch;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void utf8_encode(uint32_t c, char s[4]) {
|
|
|
|
if (c <= 0x7f) {
|
|
|
|
s[0] = c;
|
|
|
|
} else if (c <= 0x7ff) {
|
|
|
|
s[0] = (0xc0 | ((c >> 6) & 0x1f));
|
|
|
|
s[1] = (0x80 | (c & 0x3f));
|
|
|
|
} else if (c <= 0xffff) {
|
|
|
|
s[0] = (0xe0 | ((c >> 12) & 0x0f));
|
|
|
|
s[1] = (0x80 | ((c >> 6) & 0x3f));
|
|
|
|
s[2] = (0x80 | (c & 0x3f));
|
|
|
|
} else if (c <= 0x10ffff) {
|
|
|
|
s[1] = (0xf0 | ((c >> 18) & 0x07));
|
|
|
|
s[1] = (0x80 | ((c >> 12) & 0x3f));
|
|
|
|
s[2] = (0x80 | ((c >> 6) & 0x3f));
|
|
|
|
s[3] = (0x80 | (c & 0x3f));
|
|
|
|
}
|
|
|
|
}
|
2022-04-27 07:19:44 +00:00
|
|
|
|
|
|
|
// f16
|
|
|
|
// http://fox-toolkit.org/ftp/fasthalffloatconversion.pdf
|
|
|
|
|
|
|
|
// f32 to f16 tables
|
|
|
|
static uint16_t base[512];
|
|
|
|
static uint8_t shift[512];
|
|
|
|
|
|
|
|
// f16 to f32 tables
|
|
|
|
static uint32_t mantissa[2048];
|
|
|
|
static uint32_t exponent[64];
|
|
|
|
static uint16_t offset[64];
|
|
|
|
|
2023-04-26 04:45:30 +00:00
|
|
|
void float16Init(void) {
|
2022-04-27 07:19:44 +00:00
|
|
|
for (uint32_t i = 0; i < 256; i++) {
|
|
|
|
int e = i - 127;
|
|
|
|
if (e < -24) {
|
|
|
|
base[i | 0x000] = 0x0000;
|
|
|
|
base[i | 0x100] = 0x8000;
|
|
|
|
shift[i | 0x000] = 24;
|
|
|
|
shift[i | 0x100] = 24;
|
|
|
|
} else if (e < -14) {
|
|
|
|
base[i | 0x000] = (0x0400 >> (-e - 14));
|
|
|
|
base[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000;
|
|
|
|
shift[i | 0x000] = -e - 1;
|
|
|
|
shift[i | 0x100] = -e - 1;
|
|
|
|
} else if (e <= 15) {
|
|
|
|
base[i | 0x000] = ((e + 15) << 10);
|
|
|
|
base[i | 0x100] = ((e + 15) << 10) | 0x8000;
|
|
|
|
shift[i | 0x000] = 13;
|
|
|
|
shift[i | 0x100] = 13;
|
|
|
|
} else if (e < 128) {
|
|
|
|
base[i | 0x000] = 0x7C00;
|
|
|
|
base[i | 0x100] = 0xFC00;
|
|
|
|
shift[i | 0x000] = 24;
|
|
|
|
shift[i | 0x100] = 24;
|
|
|
|
} else {
|
|
|
|
base[i | 0x000] = 0x7C00;
|
|
|
|
base[i | 0x100] = 0xFC00;
|
|
|
|
shift[i | 0x000] = 13;
|
|
|
|
shift[i | 0x100] = 13;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 2048; i++) {
|
|
|
|
if (i == 0) {
|
|
|
|
mantissa[i] = 0;
|
|
|
|
} else if (i < 1024) {
|
|
|
|
uint32_t m = i << 13;
|
|
|
|
uint32_t e = 0;
|
|
|
|
while ((m & 0x00800000) == 0) {
|
|
|
|
e -= 0x00800000;
|
|
|
|
m <<= 1;
|
|
|
|
}
|
|
|
|
e += 0x38800000;
|
|
|
|
m &= ~0x00800000;
|
|
|
|
mantissa[i] = e | m;
|
|
|
|
} else {
|
|
|
|
mantissa[i] = 0x38000000 + ((i - 1024) << 13);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 64; i++) {
|
|
|
|
if (i == 0) exponent[i] = 0;
|
|
|
|
else if (i < 31) exponent[i] = i << 23;
|
|
|
|
else if (i == 31) exponent[i] = 0x47800000;
|
|
|
|
else if (i == 32) exponent[i] = 0x80000000;
|
|
|
|
else if (i < 63) exponent[i] = 0x80000000 + ((i - 32) << 23);
|
|
|
|
else exponent[i] = 0xC7800000;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < 64; i++) {
|
|
|
|
offset[i] = (i == 0 || i == 32) ? 0 : 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float16 float32to16(float32 f) {
|
|
|
|
uint32_t u = ((union { float f; uint32_t u; }) { f }).u;
|
|
|
|
return base[(u >> 23) & 0x1ff] + ((u & 0x7fffff) >> shift[(u >> 23) & 0x1ff]);
|
|
|
|
}
|
|
|
|
|
|
|
|
float32 float16to32(float16 f) {
|
|
|
|
uint32_t u = mantissa[offset[f >> 10] + (f & 0x3ff)] + exponent[f >> 10];
|
|
|
|
return ((union { uint32_t u; float f; }) { u }).f;
|
|
|
|
}
|