diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dbe9c82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.vscode/
\ No newline at end of file
diff --git a/arena.c b/arena.c
index 1758638..c52f4e2 100644
--- a/arena.c
+++ b/arena.c
@@ -1,188 +1,230 @@
#include "arena.h"
#include
-#include
#include
-#include
-#include "vmem.h"
-#include "tracelog.h"
+#include "os.h"
-static uintptr_t arena__align(uintptr_t ptr, usize align) {
+static uptr arena__align(uptr ptr, usize align) {
return (ptr + (align - 1)) & ~(align - 1);
}
static arena_t arena__make_virtual(usize size);
static arena_t arena__make_malloc(usize size);
-static arena_t arena__make_static(byte *buf, usize len);
+static arena_t arena__make_static(u8 *buf, usize len);
+
+static void *arena__alloc_common(const arena_alloc_desc_t *desc);
+static void *arena__alloc_malloc_always(const arena_alloc_desc_t *desc);
static void arena__free_virtual(arena_t *arena);
static void arena__free_malloc(arena_t *arena);
-arena_t arenaInit(const arena_desc_t *desc) {
+arena_t malloc_arena = {
+ .type = ARENA_MALLOC_ALWAYS,
+};
+
+arena_t arena_init(const arena_desc_t *desc) {
arena_t out = {0};
if (desc) {
switch (desc->type) {
- case ARENA_VIRTUAL: out = arena__make_virtual(desc->allocation); break;
- case ARENA_MALLOC: out = arena__make_malloc(desc->allocation); break;
- case ARENA_STATIC: out = arena__make_static(desc->static_buffer, desc->allocation); break;
+ case ARENA_VIRTUAL: out = arena__make_virtual(desc->size); break;
+ case ARENA_MALLOC: out = arena__make_malloc(desc->size); break;
+ case ARENA_STATIC: out = arena__make_static(desc->static_buffer, desc->size); break;
+ default: break;
}
}
return out;
}
-void arenaCleanup(arena_t *arena) {
+void arena_cleanup(arena_t *arena) {
if (!arena) {
return;
}
-
+
switch (arena->type) {
case ARENA_VIRTUAL: arena__free_virtual(arena); break;
case ARENA_MALLOC: arena__free_malloc(arena); break;
// ARENA_STATIC does not need to be freed
- case ARENA_STATIC: break;
+ default: break;
}
-
- arena->start = NULL;
- arena->current = NULL;
- arena->end = NULL;
- arena->type = 0;
+
+ memset(arena, 0, sizeof(arena_t));
}
-arena_t arenaScratch(arena_t *arena, usize size) {
- uint8 *buffer = alloc(arena, uint8, size, ALLOC_SOFT_FAIL | ALLOC_NOZERO);
+arena_t arena_scratch(arena_t *arena, usize size) {
+ u8 *buffer = alloc(arena, u8, size, ALLOC_SOFT_FAIL | ALLOC_NOZERO);
return arena__make_static(buffer, buffer ? size : 0);
}
-void *arenaAlloc(const arena_alloc_desc_t *desc) {
+void *arena_alloc(const arena_alloc_desc_t *desc) {
if (!desc || !desc->arena || desc->arena->type == ARENA_TYPE_NONE) {
return NULL;
}
- usize total = desc->size * desc->count;
arena_t *arena = desc->arena;
- arena->current = (byte *)arena__align((uintptr_t)arena->current, desc->align);
+ u8 *ptr = NULL;
- if (total > arenaRemaining(arena)) {
- if (desc->flags & ALLOC_SOFT_FAIL) {
- return NULL;
- }
- fatal("finished space in arena, tried to allocate %_$$$dB out of %_$$$dB\n", total, arenaRemaining(arena));
- abort();
+ switch (arena->type) {
+ case ARENA_MALLOC_ALWAYS:
+ ptr = arena__alloc_malloc_always(desc);
+ break;
+ default:
+ ptr = arena__alloc_common(desc);
+ break;
}
- if (arena->type == ARENA_VIRTUAL) {
- usize allocated = arenaTell(arena);
- usize page_end = vmemPadToPage(allocated);
- usize new_cur = allocated + total;
-
- if (new_cur > page_end) {
- usize extra_mem = vmemPadToPage(new_cur - page_end);
- usize page_size = vmemGetPageSize();
- // TODO is this really correct?
- usize num_of_pages = (extra_mem / page_size) + 1;
-
- assert(num_of_pages > 0);
-
- if (!vmemCommit(arena->current, num_of_pages + 1)) {
- if (desc->flags & ALLOC_SOFT_FAIL) {
- return NULL;
- }
- printf("failed to commit memory for virtual arena, tried to commit %zu pages\n", num_of_pages);
- exit(1);
- }
- }
- }
-
- byte *ptr = arena->current;
- arena->current += total;
+ usize total = desc->size * desc->count;
return desc->flags & ALLOC_NOZERO ? ptr : memset(ptr, 0, total);
}
-usize arenaTell(arena_t *arena) {
- return arena ? arena->current - arena->start : 0;
+usize arena_tell(arena_t *arena) {
+ return arena ? arena->cur - arena->beg : 0;
}
-usize arenaRemaining(arena_t *arena) {
- return arena && (arena->current < arena->end) ? arena->end - arena->current : 0;
+usize arena_remaining(arena_t *arena) {
+ return arena && (arena->cur < arena->end) ? arena->end - arena->cur : 0;
}
-void arenaRewind(arena_t *arena, usize from_start) {
+usize arena_capacity(arena_t *arena) {
+ return arena ? arena->end - arena->beg : 0;
+}
+
+void arena_rewind(arena_t *arena, usize from_start) {
if (!arena) {
return;
}
- assert(arenaTell(arena) >= from_start);
+ assert(arena_tell(arena) >= from_start);
- arena->current = arena->start + from_start;
+ arena->cur = arena->beg + from_start;
}
-void arenaPop(arena_t *arena, usize amount) {
+void arena_pop(arena_t *arena, usize amount) {
if (!arena) {
return;
}
- usize position = arenaTell(arena);
+ usize position = arena_tell(arena);
if (!position) {
return;
}
- arenaRewind(arena, position - amount);
+ arena_rewind(arena, position - amount);
}
// == VIRTUAL ARENA ====================================================================================================
static arena_t arena__make_virtual(usize size) {
usize alloc_size = 0;
- byte *ptr = vmemInit(size, &alloc_size);
- if (!vmemCommit(ptr, 1)) {
- vmemRelease(ptr);
+ u8 *ptr = os_reserve(size, &alloc_size);
+ if (!os_commit(ptr, 1)) {
+ os_release(ptr, alloc_size);
ptr = NULL;
}
return (arena_t){
- .start = ptr,
- .current = ptr,
+ .beg = ptr,
+ .cur = ptr,
.end = ptr ? ptr + alloc_size : NULL,
.type = ARENA_VIRTUAL,
};
}
static void arena__free_virtual(arena_t *arena) {
- if (!arena->start) {
+ if (!arena->beg) {
return;
}
- bool success = vmemRelease(arena->start);
+ bool success = os_release(arena->beg, arena_capacity(arena));
assert(success && "Failed arena free");
}
-
// == MALLOC ARENA =====================================================================================================
+extern void *malloc(usize size);
+extern void free(void *ptr);
+
static arena_t arena__make_malloc(usize size) {
- byte *ptr = malloc(size);
+ u8 *ptr = malloc(size);
assert(ptr);
return (arena_t) {
- .start = ptr,
- .current = ptr,
+ .beg = ptr,
+ .cur = ptr,
.end = ptr ? ptr + size : NULL,
.type = ARENA_MALLOC,
};
}
static void arena__free_malloc(arena_t *arena) {
- free(arena->start);
+ free(arena->beg);
}
+// == ARENA ALLOC ======================================================================================================
+
+static void *arena__alloc_common(const arena_alloc_desc_t *desc) {
+ usize total = desc->size * desc->count;
+ arena_t *arena = desc->arena;
+
+ arena->cur = (u8 *)arena__align((uptr)arena->cur, desc->align);
+ bool soft_fail = desc->flags & ALLOC_SOFT_FAIL;
+
+ if (total > arena_remaining(arena)) {
+ if (!soft_fail) {
+ fatal("finished space in arena, tried to allocate %_$$$dB out of %_$$$dB\n", total, arena_remaining(arena));
+ }
+ return NULL;
+ }
+
+ if (arena->type == ARENA_VIRTUAL) {
+ usize allocated = arena_tell(arena);
+ usize page_end = os_pad_to_page(allocated);
+ usize new_cur = allocated + total;
+
+ if (new_cur > page_end) {
+ usize extra_mem = os_pad_to_page(new_cur - page_end);
+ usize page_size = os_get_system_info().page_size;
+ // TODO is this really correct?
+ usize num_of_pages = (extra_mem / page_size) + 1;
+
+ assert(num_of_pages > 0);
+
+ if (!os_commit(arena->cur, num_of_pages + 1)) {
+ if (!soft_fail) {
+ fatal("failed to commit memory for virtual arena, tried to commit %zu pages\n", num_of_pages);
+ }
+ return NULL;
+ }
+ }
+ }
+
+ u8 *ptr = arena->cur;
+ arena->cur += total;
+
+ return ptr;
+}
+
+static void *arena__alloc_malloc_always(const arena_alloc_desc_t *desc) {
+ usize total = desc->size * desc->count;
+
+ // TODO: alignment?
+ u8 *ptr = malloc(total);
+ if (!ptr && !(desc->flags & ALLOC_SOFT_FAIL)) {
+ fatal("malloc call failed for %_$$$dB", total);
+ }
+
+ return ptr;
+}
+
+
// == STATIC ARENA =====================================================================================================
-static arena_t arena__make_static(byte *buf, usize len) {
+static arena_t arena__make_static(u8 *buf, usize len) {
return (arena_t) {
- .start = buf,
- .current = buf,
+ .beg = buf,
+ .cur = buf,
.end = buf ? buf + len : NULL,
.type = ARENA_STATIC,
};
}
+
diff --git a/arena.h b/arena.h
index e3c0656..40b9130 100644
--- a/arena.h
+++ b/arena.h
@@ -1,66 +1,73 @@
-#pragma once
+#ifndef COLLA_ARENA_H
+#define COLLA_ARENA_H
-#include "collatypes.h"
+#include "core.h"
-#if COLLA_TCC
-#define alignof __alignof__
-#else
-#define alignof _Alignof
+#if COLLA_WIN && !COLLA_TCC
+#define alignof __alignof
#endif
-typedef enum {
+typedef enum arena_type_e {
ARENA_TYPE_NONE, // only here so that a 0 initialised arena is valid
ARENA_VIRTUAL,
ARENA_MALLOC,
+ ARENA_MALLOC_ALWAYS,
ARENA_STATIC,
} arena_type_e;
-typedef enum {
+typedef enum alloc_flags_e {
ALLOC_FLAGS_NONE = 0,
ALLOC_NOZERO = 1 << 0,
ALLOC_SOFT_FAIL = 1 << 1,
} alloc_flags_e;
-typedef struct arena_t {
- uint8 *start;
- uint8 *current;
- uint8 *end;
+typedef struct arena_t arena_t;
+struct arena_t {
+ u8 *beg;
+ u8 *cur;
+ u8 *end;
arena_type_e type;
-} arena_t;
+};
-typedef struct {
+typedef struct arena_desc_t arena_desc_t;
+struct arena_desc_t {
arena_type_e type;
- usize allocation;
- byte *static_buffer;
-} arena_desc_t;
+ usize size;
+ u8 *static_buffer;
+};
-typedef struct {
+typedef struct arena_alloc_desc_t arena_alloc_desc_t;
+struct arena_alloc_desc_t {
arena_t *arena;
usize count;
alloc_flags_e flags;
usize align;
usize size;
-} arena_alloc_desc_t;
-
-#ifndef ARENA_NO_SIZE_HELPERS
-#define KB(count) ( (count) * 1024)
-#define MB(count) (KB(count) * 1024)
-#define GB(count) (MB(count) * 1024)
-#endif
+};
// arena_type_e type, usize allocation, [ byte *static_buffer ]
-#define arenaMake(...) arenaInit(&(arena_desc_t){ __VA_ARGS__ })
+#define arena_make(...) arena_init(&(arena_desc_t){ __VA_ARGS__ })
// arena_t *arena, T type, [ usize count, alloc_flags_e flags, usize align, usize size ]
-#define alloc(arenaptr, type, ...) arenaAlloc(&(arena_alloc_desc_t){ .size = sizeof(type), .count = 1, .align = alignof(type), .arena = arenaptr, __VA_ARGS__ })
+#define alloc(arenaptr, type, ...) arena_alloc(&(arena_alloc_desc_t){ .size = sizeof(type), .count = 1, .align = alignof(type), .arena = arenaptr, __VA_ARGS__ })
-arena_t arenaInit(const arena_desc_t *desc);
-void arenaCleanup(arena_t *arena);
+// simple arena that always calls malloc internally, this is useful if you need
+// malloc for some reason but want to still use the arena interface
+// WARN: most arena functions outside of alloc/scratch won't work!
+// you also need to each allocation afterwards! this is still
+// malloc
+extern arena_t malloc_arena;
-arena_t arenaScratch(arena_t *arena, usize size);
+arena_t arena_init(const arena_desc_t *desc);
+void arena_cleanup(arena_t *arena);
-void *arenaAlloc(const arena_alloc_desc_t *desc);
-usize arenaTell(arena_t *arena);
-usize arenaRemaining(arena_t *arena);
-void arenaRewind(arena_t *arena, usize from_start);
-void arenaPop(arena_t *arena, usize amount);
+arena_t arena_scratch(arena_t *arena, usize size);
+
+void *arena_alloc(const arena_alloc_desc_t *desc);
+usize arena_tell(arena_t *arena);
+usize arena_remaining(arena_t *arena);
+usize arena_capacity(arena_t *arena);
+void arena_rewind(arena_t *arena, usize from_start);
+void arena_pop(arena_t *arena, usize amount);
+
+#endif
\ No newline at end of file
diff --git a/base64.c b/base64.c
deleted file mode 100644
index 4c78ba2..0000000
--- a/base64.c
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "base64.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include "arena.h"
-
-static unsigned char encoding_table[] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
- 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
- 'w', 'x', 'y', 'z', '0', '1', '2', '3',
- '4', '5', '6', '7', '8', '9', '+', '/'
-};
-
-static uint8 decoding_table[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0,
- 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-buffer_t base64Encode(arena_t *arena, buffer_t buffer) {
- usize outlen = ((buffer.len + 2) / 3) * 4;
- uint8 *out = alloc(arena, uint8, outlen);
-
- for (usize i = 0, j = 0; i < buffer.len;) {
- uint32 a = i < buffer.len ? buffer.data[i++] : 0;
- uint32 b = i < buffer.len ? buffer.data[i++] : 0;
- uint32 c = i < buffer.len ? buffer.data[i++] : 0;
-
- uint32 triple = (a << 16) | (b << 8) | c;
-
- out[j++] = encoding_table[(triple >> 18) & 0x3F];
- out[j++] = encoding_table[(triple >> 12) & 0x3F];
- out[j++] = encoding_table[(triple >> 6) & 0x3F];
- out[j++] = encoding_table[(triple >> 0) & 0x3F];
- }
-
- usize mod = buffer.len % 3;
- if (mod) {
- mod = 3 - mod;
- for (usize i = 0; i < mod; ++i) {
- out[outlen - 1 - i] = '=';
- }
- }
-
- return (buffer_t){
- .data = out,
- .len = outlen
- };
-}
-
-buffer_t base64Decode(arena_t *arena, buffer_t buffer) {
- uint8 *out = arena->current;
- usize start = arenaTell(arena);
-
- for (usize i = 0; i < buffer.len; i += 4) {
- uint8 a = decoding_table[buffer.data[i + 0]];
- uint8 b = decoding_table[buffer.data[i + 1]];
- uint8 c = decoding_table[buffer.data[i + 2]];
- uint8 d = decoding_table[buffer.data[i + 3]];
-
- uint32 triple =
- ((uint32)a << 18) |
- ((uint32)b << 12) |
- ((uint32)c << 6) |
- ((uint32)d);
-
- uint8 *bytes = alloc(arena, uint8, 3);
-
- bytes[0] = (triple >> 16) & 0xFF;
- bytes[1] = (triple >> 8) & 0xFF;
- bytes[2] = (triple >> 0) & 0xFF;
- }
-
- usize outlen = arenaTell(arena) - start;
-
- return (buffer_t){
- .data = out,
- .len = outlen,
- };
-}
-
-#include "warnings/colla_warn_end.h"
diff --git a/base64.h b/base64.h
deleted file mode 100644
index 71c4b03..0000000
--- a/base64.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-
-typedef struct arena_t arena_t;
-
-buffer_t base64Encode(arena_t *arena, buffer_t buffer);
-buffer_t base64Decode(arena_t *arena, buffer_t buffer);
\ No newline at end of file
diff --git a/bits.h b/bits.h
deleted file mode 100644
index 8cd15bd..0000000
--- a/bits.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-
-uint32 bitsCtz(uint32 v);
-uint32 bitsNextPow2(uint32 v);
-
-// == INLINE IMPLEMENTATION ==============================================================
-
-#if COLLA_MSVC
-#define BITS_WIN 1
-#define BITS_LIN 0
-#include
-#elif COLLA_GCC || COLLA_CLANG || COLLA_EMC
-#define BITS_WIN 0
-#define BITS_LIN 1
-#elif COLLA_TCC
-#define BITS_WIN 0
-#define BITS_LIN 0
-#else
-#error "bits header not supported on this compiler"
-#endif
-
-#include "tracelog.h"
-
-inline uint32 bitsCtz(uint32 v) {
-#if BITS_LIN
- return v ? __builtin_ctz(v) : 0;
-#elif BITS_WIN
- uint32 trailing = 0;
- return _BitScanForward((unsigned long *)&trailing, v) ? trailing : 32;
-#else
- if (v == 0) return 0;
- for (uint32 i = 0; i < 32; ++i) {
- if (v & (1 << i)) return i;
- }
- return 32;
-#endif
-}
-
-inline uint32 bitsNextPow2(uint32 v) {
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- v++;
-
- return v;
-}
-
-#undef BITS_WIN
-#undef BITS_LIN
diff --git a/build.c b/build.c
index a98ac51..843c4e7 100644
--- a/build.c
+++ b/build.c
@@ -1,52 +1,13 @@
-#if COLLA_ONLYCORE
- #define COLLA_NOTHREADS 1
- #define COLLA_NOSOCKETS 1
- #define COLLA_NOHTTP 1
- #define COLLA_NOSERVER 1
- #define COLLA_NOHOTRELOAD 1
-#endif
-
-#if COLLA_NOSOCKETS
- #undef COLLA_NOHTTP
- #undef COLLA_NOSERVER
- #define COLLA_NOHTTP 1
- #define COLLA_NOSERVER 1
+#include "core.h"
+
+#if COLLA_TCC
+#define COLLA_NO_CONDITION_VARIABLE 1
#endif
+#include "core.c"
+#include "os.c"
#include "arena.c"
-#include "base64.c"
-#include "file.c"
-#include "format.c"
-#include "ini.c"
-#include "json.c"
#include "str.c"
-#include "strstream.c"
-#include "tracelog.c"
-#include "utf8.c"
-#include "vmem.c"
-#include "xml.c"
-#include "sha1.c"
-#include "markdown.c"
-#include "highlight.c"
-#include "dir.c"
-
-#if !COLLA_NOTHREADS
-#include "cthreads.c"
-#endif
-
-#if !COLLA_NOSOCKETS
-#include "socket.c"
-#include "websocket.c"
-#endif
-
-#if !COLLA_NOHTTP
-#include "http.c"
-#endif
-
-#if !COLLA_NOSERVER
-#include "server.c"
-#endif
-
-#if !COLLA_NOHOTRELOAD
-#include "hot_reload.c"
-#endif
\ No newline at end of file
+#include "parsers.c"
+#include "net.c"
+#include "darr.h"
\ No newline at end of file
diff --git a/colladefines.h b/colladefines.h
deleted file mode 100644
index c499018..0000000
--- a/colladefines.h
+++ /dev/null
@@ -1,115 +0,0 @@
-#pragma once
-
-#define arrlen(a) (sizeof(a) / sizeof((a)[0]))
-#define for_each(it, list) for (typeof(list) it = list; it; it = it->next)
-
-#if defined(_DEBUG) || !defined(NDEBUG)
-#define COLLA_DEBUG 1
-#define COLLA_RELEASE 0
-#else
-#define COLLA_DEBUG 0
-#define COLLA_RELEASE 1
-#endif
-
-#if defined(_WIN32)
-
-#define COLLA_WIN 1
-#define COLLA_OSX 0
-#define COLLA_LIN 0
-#define COLLA_EMC 0
-
-#elif defined(__EMSCRIPTEN__)
-
-#define COLLA_WIN 0
-#define COLLA_OSX 0
-#define COLLA_LIN 0
-#define COLLA_EMC 1
-
-#elif defined(__linux__)
-
-#define COLLA_WIN 0
-#define COLLA_OSX 0
-#define COLLA_LIN 1
-#define COLLA_EMC 0
-
-#elif defined(__APPLE__)
-
-#define COLLA_WIN 0
-#define COLLA_OSX 1
-#define COLLA_LIN 0
-#define COLLA_EMC 0
-
-#endif
-
-#if defined(__COSMOPOLITAN__)
-#define COLLA_COSMO 1
-#else
-#define COLLA_COSMO 0
-#endif
-
-#define COLLA_POSIX (COLLA_OSX || COLLA_LIN || COLLA_COSMO)
-
-#if defined(__clang__)
-
-#define COLLA_CLANG 1
-#define COLLA_MSVC 0
-#define COLLA_TCC 0
-#define COLLA_GCC 0
-
-#elif defined(_MSC_VER)
-
-#define COLLA_CLANG 0
-#define COLLA_MSVC 1
-#define COLLA_TCC 0
-#define COLLA_GCC 0
-
-#elif defined(__TINYC__)
-
-#define COLLA_CLANG 0
-#define COLLA_MSVC 0
-#define COLLA_TCC 1
-#define COLLA_GCC 0
-
-#elif defined(__GNUC__)
-
-#define COLLA_CLANG 0
-#define COLLA_MSVC 0
-#define COLLA_TCC 0
-#define COLLA_GCC 1
-
-#endif
-
-#if COLLA_CLANG
-
-#define COLLA_CMT_LIB 0
-
-#elif COLLA_MSVC
-
-#define COLLA_CMT_LIB 1
-
-#elif COLLA_TCC
-
-#define COLLA_CMT_LIB 1
-
-#elif COLLA_GCC
-
-#define COLLA_CMT_LIB 0
-
-#endif
-
-
-#if COLLA_WIN
-
-#undef NOMINMAX
-#undef WIN32_LEAN_AND_MEAN
-
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-
-#endif
-
-#undef min
-#undef max
-
-#define min(a, b) ((a) < (b) ? (a) : (b))
-#define max(a, b) ((a) > (b) ? (a) : (b))
\ No newline at end of file
diff --git a/collatypes.h b/collatypes.h
deleted file mode 100644
index b62af8a..0000000
--- a/collatypes.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-#include "colladefines.h"
-
-typedef unsigned char uchar;
-typedef unsigned short ushort;
-typedef unsigned int uint;
-
-typedef uint8_t uint8;
-typedef uint16_t uint16;
-typedef uint32_t uint32;
-typedef uint64_t uint64;
-
-typedef int8_t int8;
-typedef int16_t int16;
-typedef int32_t int32;
-typedef int64_t int64;
-
-typedef size_t usize;
-typedef ptrdiff_t isize;
-
-typedef uint8 byte;
-
-typedef struct {
- uint8 *data;
- usize len;
-} buffer_t;
-
-#if COLLA_WIN && defined(UNICODE)
-typedef wchar_t TCHAR;
-#else
-typedef char TCHAR;
-#endif
diff --git a/core.c b/core.c
new file mode 100644
index 0000000..fd25d3a
--- /dev/null
+++ b/core.c
@@ -0,0 +1,75 @@
+#include "core.h"
+
+#include
+
+#if COLLA_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#endif
+
+#define STB_SPRINTF_DECORATE(name) colla_stb_##name
+#define STB_SPRINTF_NOUNALIGNED
+#define STB_SPRINTF_IMPLEMENTATION
+#include "stb/stb_sprintf.h"
+
+#if COLLA_CLANG
+#pragma clang diagnostic pop
+#endif
+
+colla_modules_e colla__initialised_modules = 0;
+
+extern void os_init(void);
+extern void net_init(void);
+extern void os_cleanup(void);
+extern void net_cleanup(void);
+
+static char *colla_fmt__stb_callback(const char *buf, void *ud, int len) {
+ fflush(stdout);
+ fwrite(buf, 1, len, stdout);
+ return (char *)ud;
+}
+
+void colla_init(colla_modules_e modules) {
+ colla__initialised_modules = modules;
+ if (modules & COLLA_OS) {
+ os_init();
+ }
+ if (modules & COLLA_NET) {
+ net_init();
+ }
+}
+
+void colla_cleanup(void) {
+ colla_modules_e modules = colla__initialised_modules;
+ if (modules & COLLA_OS) {
+ os_cleanup();
+ }
+ if (modules & COLLA_NET) {
+ net_cleanup();
+ }
+}
+
+int fmt_print(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int out = fmt_printv(fmt, args);
+ va_end(args);
+ return out;
+}
+
+int fmt_printv(const char *fmt, va_list args) {
+ char buffer[STB_SPRINTF_MIN] = {0};
+ return colla_stb_vsprintfcb(colla_fmt__stb_callback, buffer, buffer, fmt, args);
+}
+
+int fmt_buffer(char *buf, usize len, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int out = fmt_bufferv(buf, len, fmt, args);
+ va_end(args);
+ return out;
+}
+
+int fmt_bufferv(char *buf, usize len, const char *fmt, va_list args) {
+ return colla_stb_vsnprintf(buf, (int)len, fmt, args);
+}
diff --git a/core.h b/core.h
new file mode 100644
index 0000000..30db78f
--- /dev/null
+++ b/core.h
@@ -0,0 +1,199 @@
+#ifndef COLLA_CORE_H
+#define COLLA_CORE_H
+
+#include
+#include
+#include
+#include
+
+// CORE MODULES /////////////////////////////////
+
+typedef enum {
+ COLLA_CORE = 0,
+ COLLA_OS = 1 << 0,
+ COLLA_NET = 1 << 1,
+ COLLA_ALL = 0xff,
+} colla_modules_e;
+
+void colla_init(colla_modules_e modules);
+void colla_cleanup(void);
+
+/////////////////////////////////////////////////
+
+// USEFUL MACROS ////////////////////////////////
+
+#define arrlen(a) (sizeof(a) / sizeof((a)[0]))
+#define COLLA_UNUSED(v) (void)(v)
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define KB(n) (((u64)n) << 10)
+#define MB(n) (((u64)n) << 20)
+#define GB(n) (((u64)n) << 30)
+#define TB(n) (((u64)n) << 40)
+
+/////////////////////////////////////////////////
+
+// LINKED LISTS /////////////////////////////////
+
+#define list_push_n(list, item, next) ((item)->next=(list), (list)=(item))
+#define list_pop_n(list, next) ((list) = (list) ? (list)->next : NULL)
+
+#define list_push(list, item) list_push_n(list, item, next)
+#define list_pop(list) list_pop_n(list, next)
+
+#define dlist_push_pn(list, item, next, prev) if (item) (item)->next = (list); if (list) (list)->prev = (item); (list) = (item)
+#define dlist_pop_pn(list, item, next, prev) do { \
+ if (!(item)) break; \
+ if ((item)->prev) (item)->prev->next = (item)->next; \
+ if ((item)->next) (item)->next->prev = (item)->prev; \
+ if((item) == (list)) (list) = (item)->next; \
+} while (0)
+
+#define dlist_push(list, item) dlist_push_pn(list, item, next, prev)
+#define dlist_pop(list, item) dlist_pop_pn(list, item, next, prev)
+
+#define for_each(it, list) for (typeof(list) it = list; it; it = it->next)
+
+/////////////////////////////////////////////////
+
+// OS AND COMPILER MACROS ///////////////////////
+
+#if defined(_DEBUG) || !defined(NDEBUG)
+ #define COLLA_DEBUG 1
+ #define COLLA_RELEASE 0
+#else
+ #define COLLA_DEBUG 0
+ #define COLLA_RELEASE 1
+#endif
+
+#if defined(_WIN32)
+ #define COLLA_WIN 1
+ #define COLLA_OSX 0
+ #define COLLA_LIN 0
+ #define COLLA_EMC 0
+#elif defined(__EMSCRIPTEN__)
+ #define COLLA_WIN 0
+ #define COLLA_OSX 0
+ #define COLLA_LIN 0
+ #define COLLA_EMC 1
+#elif defined(__linux__)
+ #define COLLA_WIN 0
+ #define COLLA_OSX 0
+ #define COLLA_LIN 1
+ #define COLLA_EMC 0
+#elif defined(__APPLE__)
+ #define COLLA_WIN 0
+ #define COLLA_OSX 1
+ #define COLLA_LIN 0
+ #define COLLA_EMC 0
+#endif
+
+#if defined(__COSMOPOLITAN__)
+ #define COLLA_COSMO 1
+#else
+ #define COLLA_COSMO 0
+#endif
+
+#define COLLA_POSIX (COLLA_OSX || COLLA_LIN || COLLA_COSMO)
+
+#if defined(__clang__)
+ #define COLLA_CLANG 1
+ #define COLLA_MSVC 0
+ #define COLLA_TCC 0
+ #define COLLA_GCC 0
+#elif defined(_MSC_VER)
+ #define COLLA_CLANG 0
+ #define COLLA_MSVC 1
+ #define COLLA_TCC 0
+ #define COLLA_GCC 0
+#elif defined(__TINYC__)
+ #define COLLA_CLANG 0
+ #define COLLA_MSVC 0
+ #define COLLA_TCC 1
+ #define COLLA_GCC 0
+#elif defined(__GNUC__)
+ #define COLLA_CLANG 0
+ #define COLLA_MSVC 0
+ #define COLLA_TCC 0
+ #define COLLA_GCC 1
+#endif
+
+#if COLLA_CLANG
+ #define COLLA_CMT_LIB 0
+#elif COLLA_MSVC
+ #define COLLA_CMT_LIB 1
+#elif COLLA_TCC
+ #define COLLA_CMT_LIB 1
+#elif COLLA_GCC
+ #define COLLA_CMT_LIB 0
+#endif
+
+#if COLLA_TCC
+ #define alignof __alignof__
+#endif
+
+#if COLLA_WIN
+ #undef NOMINMAX
+ #undef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #define NOMINMAX
+
+ #ifdef UNICODE
+ #define COLLA_UNICODE 1
+ #else
+ #define COLLA_UNICODE 0
+ #endif
+
+#endif
+
+/////////////////////////////////////////////////
+
+// BASIC TYPES //////////////////////////////////
+
+#if COLLA_WIN && COLLA_UNICODE
+ typedef wchar_t TCHAR;
+#else
+ typedef char TCHAR;
+#endif
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+typedef size_t usize;
+typedef ptrdiff_t isize;
+
+typedef uintptr_t uptr;
+typedef intptr_t iptr;
+
+typedef struct {
+ u8 *data;
+ usize len;
+} buffer_t;
+
+typedef struct arena_t arena_t;
+
+/////////////////////////////////////////////////
+
+// FORMATTING ///////////////////////////////////
+
+int fmt_print(const char *fmt, ...);
+int fmt_printv(const char *fmt, va_list args);
+int fmt_buffer(char *buf, usize len, const char *fmt, ...);
+int fmt_bufferv(char *buf, usize len, const char *fmt, va_list args);
+
+/////////////////////////////////////////////////
+
+#endif
\ No newline at end of file
diff --git a/cthreads.c b/cthreads.c
deleted file mode 100644
index b910438..0000000
--- a/cthreads.c
+++ /dev/null
@@ -1,282 +0,0 @@
-#include "cthreads.h"
-
-#include
-
-// TODO: swap calloc for arenas
-
-typedef struct {
- cthread_func_t func;
- void *arg;
-} _thr_internal_t;
-
-#if COLLA_WIN
-#include
-
-// == THREAD ===========================================
-
-static DWORD WINAPI _thrFuncInternal(void *arg) {
- _thr_internal_t *params = (_thr_internal_t *)arg;
- cthread_func_t func = params->func;
- void *argument = params->arg;
- free(params);
- return (DWORD)func(argument);
-}
-
-cthread_t thrCreate(cthread_func_t func, void *arg) {
- HANDLE thread = INVALID_HANDLE_VALUE;
- _thr_internal_t *params = calloc(1, sizeof(_thr_internal_t));
-
- if(params) {
- params->func = func;
- params->arg = arg;
-
- thread = CreateThread(NULL, 0, _thrFuncInternal, params, 0, NULL);
- }
-
- return (cthread_t)thread;
-}
-
-bool thrValid(cthread_t ctx) {
- return (HANDLE)ctx != INVALID_HANDLE_VALUE;
-}
-
-bool thrDetach(cthread_t ctx) {
- return CloseHandle((HANDLE)ctx);
-}
-
-cthread_t thrCurrent(void) {
- return (cthread_t)GetCurrentThread();
-}
-
-int thrCurrentId(void) {
- return GetCurrentThreadId();
-}
-
-int thrGetId(cthread_t ctx) {
-#if COLLA_TCC
- return 0;
-#endif
- return GetThreadId((HANDLE)ctx);
-}
-
-void thrExit(int code) {
- ExitThread(code);
-}
-
-bool thrJoin(cthread_t ctx, int *code) {
- if(!ctx) return false;
- int return_code = WaitForSingleObject((HANDLE)ctx, INFINITE);
- if(code) *code = return_code;
- BOOL success = CloseHandle((HANDLE)ctx);
- return return_code != WAIT_FAILED && success;
-}
-
-// == MUTEX ============================================
-
-cmutex_t mtxInit(void) {
- CRITICAL_SECTION *crit_sec = calloc(1, sizeof(CRITICAL_SECTION));
- if(crit_sec) {
- InitializeCriticalSection(crit_sec);
- }
- return (cmutex_t)crit_sec;
-}
-
-void mtxFree(cmutex_t ctx) {
- DeleteCriticalSection((CRITICAL_SECTION *)ctx);
- free((CRITICAL_SECTION *)ctx);
-}
-
-bool mtxValid(cmutex_t ctx) {
- return (void *)ctx != NULL;
-}
-
-bool mtxLock(cmutex_t ctx) {
- EnterCriticalSection((CRITICAL_SECTION *)ctx);
- return true;
-}
-
-bool mtxTryLock(cmutex_t ctx) {
- return TryEnterCriticalSection((CRITICAL_SECTION *)ctx);
-}
-
-bool mtxUnlock(cmutex_t ctx) {
- LeaveCriticalSection((CRITICAL_SECTION *)ctx);
- return true;
-}
-
-#if !COLLA_NO_CONDITION_VAR
-// == CONDITION VARIABLE ===============================
-
-#include "tracelog.h"
-
-condvar_t condInit(void) {
- CONDITION_VARIABLE *cond = calloc(1, sizeof(CONDITION_VARIABLE));
- InitializeConditionVariable(cond);
- return (condvar_t)cond;
-}
-
-void condFree(condvar_t cond) {
- free((CONDITION_VARIABLE *)cond);
-}
-
-void condWake(condvar_t cond) {
- WakeConditionVariable((CONDITION_VARIABLE *)cond);
-}
-
-void condWakeAll(condvar_t cond) {
- WakeAllConditionVariable((CONDITION_VARIABLE *)cond);
-}
-
-void condWait(condvar_t cond, cmutex_t mtx) {
- SleepConditionVariableCS((CONDITION_VARIABLE *)cond, (CRITICAL_SECTION *)mtx, INFINITE);
-}
-
-void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds) {
- SleepConditionVariableCS((CONDITION_VARIABLE *)cond, (CRITICAL_SECTION *)mtx, milliseconds);
-}
-
-#endif
-
-#else
-#include
-#include
-#include
-#include
-
-// == THREAD ===========================================
-
-#define INT_TO_VOIDP(a) ((void *)((uintptr_t)(a)))
-
-static void *_thrFuncInternal(void *arg) {
- _thr_internal_t *params = (_thr_internal_t *)arg;
- cthread_func_t func = params->func;
- void *argument = params->arg;
- free(params);
- return INT_TO_VOIDP(func(argument));
-}
-
-cthread_t thrCreate(cthread_func_t func, void *arg) {
- pthread_t handle = (pthread_t)NULL;
-
- _thr_internal_t *params = calloc(1, sizeof(_thr_internal_t));
-
- if(params) {
- params->func = func;
- params->arg = arg;
-
- int result = pthread_create(&handle, NULL, _thrFuncInternal, params);
- if(result) handle = (pthread_t)NULL;
- }
-
- return (cthread_t)handle;
-}
-
-bool thrValid(cthread_t ctx) {
- return (void *)ctx != NULL;
-}
-
-bool thrDetach(cthread_t ctx) {
- return pthread_detach((pthread_t)ctx) == 0;
-}
-
-cthread_t thrCurrent(void) {
- return (cthread_t)pthread_self();
-}
-
-int thrCurrentId(void) {
- return (int)pthread_self();
-}
-
-int thrGetId(cthread_t ctx) {
- return (int)ctx;
-}
-
-void thrExit(int code) {
- pthread_exit(INT_TO_VOIDP(code));
-}
-
-bool thrJoin(cthread_t ctx, int *code) {
- void *result = code;
- return pthread_join((pthread_t)ctx, &result) != 0;
-}
-
-// == MUTEX ============================================
-
-cmutex_t mtxInit(void) {
- pthread_mutex_t *mutex = calloc(1, sizeof(pthread_mutex_t));
-
- if(mutex) {
- if(pthread_mutex_init(mutex, NULL)) {
- free(mutex);
- mutex = NULL;
- }
- }
-
- return (cmutex_t)mutex;
-}
-
-void mtxFree(cmutex_t ctx) {
- pthread_mutex_destroy((pthread_mutex_t *)ctx);
-}
-
-bool mtxValid(cmutex_t ctx) {
- return (void *)ctx != NULL;
-}
-
-bool mtxLock(cmutex_t ctx) {
- return pthread_mutex_lock((pthread_mutex_t *)ctx) == 0;
-}
-
-bool mtxTryLock(cmutex_t ctx) {
- return pthread_mutex_trylock((pthread_mutex_t *)ctx) == 0;
-}
-
-bool mtxUnlock(cmutex_t ctx) {
- return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0;
-}
-
-#if !COLLA_NO_CONDITION_VAR
-
-// == CONDITION VARIABLE ===============================
-
-condvar_t condInit(void) {
- pthread_cond_t *cond = calloc(1, sizeof(pthread_cond_t));
-
- if(cond) {
- if(pthread_cond_init(cond, NULL)) {
- free(cond);
- cond = NULL;
- }
- }
-
- return (condvar_t)cond;
-}
-
-void condFree(condvar_t cond) {
- if (!cond) return;
- pthread_cond_destroy((pthread_cond_t *)cond);
- free((pthread_cond_t *)cond);
-}
-
-void condWake(condvar_t cond) {
- pthread_cond_signal((pthread_cond_t *)cond);
-}
-
-void condWakeAll(condvar_t cond) {
- pthread_cond_broadcast((pthread_cond_t *)cond);
-}
-
-void condWait(condvar_t cond, cmutex_t mtx) {
- pthread_cond_wait((pthread_cond_t *)cond, (pthread_mutex_t *)mtx);
-}
-
-void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds) {
- struct timespec timeout;
- time(&timeout.tv_sec);
- timeout.tv_nsec += milliseconds * 1000000;
- pthread_cond_timedwait((pthread_cond_t *)cond, (pthread_mutex_t *)mtx, &timeout);
-}
-
-#endif
-
-#endif
diff --git a/cthreads.h b/cthreads.h
deleted file mode 100644
index 03202f2..0000000
--- a/cthreads.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-
-#if COLLA_TCC
-#define COLLA_NO_CONDITION_VAR 1
-#endif
-
-typedef struct arena_t arena_t;
-
-// == THREAD ===========================================
-
-typedef uintptr_t cthread_t;
-
-typedef int (*cthread_func_t)(void *);
-
-cthread_t thrCreate(cthread_func_t func, void *arg);
-bool thrValid(cthread_t ctx);
-bool thrDetach(cthread_t ctx);
-
-cthread_t thrCurrent(void);
-int thrCurrentId(void);
-int thrGetId(cthread_t ctx);
-
-void thrExit(int code);
-bool thrJoin(cthread_t ctx, int *code);
-
-// == MUTEX ============================================
-
-typedef uintptr_t cmutex_t;
-
-cmutex_t mtxInit(void);
-void mtxFree(cmutex_t ctx);
-
-bool mtxValid(cmutex_t ctx);
-
-bool mtxLock(cmutex_t ctx);
-bool mtxTryLock(cmutex_t ctx);
-bool mtxUnlock(cmutex_t ctx);
-
-#if !COLLA_NO_CONDITION_VAR
-// == CONDITION VARIABLE ===============================
-
-typedef uintptr_t condvar_t;
-
-#define COND_WAIT_INFINITE 0xFFFFFFFF
-
-condvar_t condInit(void);
-void condFree(condvar_t cond);
-
-void condWake(condvar_t cond);
-void condWakeAll(condvar_t cond);
-
-void condWait(condvar_t cond, cmutex_t mtx);
-void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds);
-
-#endif
\ No newline at end of file
diff --git a/darr.h b/darr.h
new file mode 100644
index 0000000..c48c898
--- /dev/null
+++ b/darr.h
@@ -0,0 +1,82 @@
+#ifndef COLLA_DARR_HEADER
+#define COLLA_DARR_HEADER
+
+/*
+dynamic chunked array which uses an arena to allocate,
+the structure needs to follow this exact format, if you want
+you can use the macro darr_define(struct_name, item_type) instead:
+
+////////////////////////////////////
+
+typedef struct arr_t arr_t;
+struct arr_t {
+ int *items;
+ usize block_size;
+ usize count;
+ arr_t *next;
+ arr_t *head;
+};
+// equivalent to
+
+darr_define(arr_t, int);
+
+////////////////////////////////////
+
+by default a chunk is 64 items long, you can change this default
+by modifying the arr.block_size value before adding to the array,
+or by defining DARRAY_DEFAULT_BLOCK_SIZE
+
+usage example:
+
+////////////////////////////////////
+
+darr_define(arr_t, int);
+
+arr_t *arr = NULL;
+
+for (int i = 0; i < 100; ++i) {
+ darr_push(&arena, arr, i);
+}
+
+for_each (chunk, arr) {
+ for (int i = 0; i < chunk->count; ++i) {
+ info("%d -> %d", i, chunk->items[i]);
+ }
+}
+*/
+
+#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; \
+ usize block_size; \
+ usize count; \
+ struct_name *next; \
+ struct_name *head; \
+ }
+
+#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)->items = alloc(arena, typeof(*arr->items), arr->block_size); \
+ assert((arr)->count == 0); \
+ } while (0)
+
+#define darr__alloc_block(arena, arr) do { \
+ typeof(arr) newarr = alloc(arena, typeof(*arr)); \
+ newarr->block_size = arr->block_size; \
+ newarr->items = alloc(arena, typeof(*arr->items), arr->block_size); \
+ newarr->head = arr->head; \
+ arr->next = newarr; \
+ arr = newarr; \
+ } while (0)
+
+#define darr_push(arena, arr, item) do { \
+ if (!(arr) || (arr)->items == NULL) darr__alloc_first(arena, arr); \
+ if ((arr)->count >= (arr)->block_size) darr__alloc_block(arena, arr); \
+ (arr)->items[(arr)->count++] = (item); \
+ } while (0)
+
+#endif
\ No newline at end of file
diff --git a/dir.c b/dir.c
deleted file mode 100644
index 3dd1b35..0000000
--- a/dir.c
+++ /dev/null
@@ -1,163 +0,0 @@
-#include "dir.h"
-
-#if COLLA_WIN
-
-#include
-
-typedef struct dir_t {
- WIN32_FIND_DATA find_data;
- HANDLE handle;
- dir_entry_t cur_entry;
- dir_entry_t next_entry;
-} dir_t;
-
-static dir_entry_t dir__entry_from_find_data(arena_t *arena, WIN32_FIND_DATA *fd) {
- dir_entry_t out = {0};
-
- out.name = str(arena, fd->cFileName);
-
- if (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- out.type = DIRTYPE_DIR;
- }
- else {
- LARGE_INTEGER filesize = {
- .LowPart = fd->nFileSizeLow,
- .HighPart = fd->nFileSizeHigh,
- };
- out.filesize = filesize.QuadPart;
- }
-
- return out;
-}
-
-dir_t *dirOpen(arena_t *arena, strview_t path) {
- uint8 tmpbuf[1024] = {0};
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
-
- TCHAR *winpath = strvToTChar(&scratch, path);
- // get a little extra leeway
- TCHAR fullpath[MAX_PATH + 16] = {0};
- DWORD pathlen = GetFullPathName(winpath, MAX_PATH, fullpath, NULL);
- // add asterisk at the end of the path
- if (fullpath[pathlen ] != '\\' && fullpath[pathlen] != '/') {
- fullpath[pathlen++] = '\\';
- }
- fullpath[pathlen++] = '*';
- fullpath[pathlen++] = '\0';
-
- dir_t *ctx = alloc(arena, dir_t);
- ctx->handle = FindFirstFile(fullpath, &ctx->find_data);
-
- if (ctx->handle == INVALID_HANDLE_VALUE) {
- arenaPop(arena, sizeof(dir_t));
- return NULL;
- }
-
- ctx->next_entry = dir__entry_from_find_data(arena, &ctx->find_data);
-
- return ctx;
-}
-
-void dirClose(dir_t *ctx) {
- FindClose(ctx->handle);
- ctx->handle = INVALID_HANDLE_VALUE;
-}
-
-bool dirIsValid(dir_t *ctx) {
- return ctx && ctx->handle != INVALID_HANDLE_VALUE;
-}
-
-dir_entry_t *dirNext(arena_t *arena, dir_t *ctx) {
- if (!dirIsValid(ctx)) {
- return NULL;
- }
-
- ctx->cur_entry = ctx->next_entry;
-
- ctx->next_entry = (dir_entry_t){0};
-
- if (FindNextFile(ctx->handle, &ctx->find_data)) {
- ctx->next_entry = dir__entry_from_find_data(arena, &ctx->find_data);
- }
- else {
- dirClose(ctx);
- }
-
- return &ctx->cur_entry;
-}
-
-#elif COLLA_POSIX
-
-#include
-#include
-#include
-#include
-
-// taken from https://sites.uclouvain.be/SystInfo/usr/include/dirent.h.html
-// hopefully shouldn't be needed
-#ifndef DT_DIR
- #define DT_DIR 4
-#endif
-#ifndef DT_REG
- #define DT_REG 8
-#endif
-
-typedef struct dir_t {
- DIR *dir;
- dir_entry_t next;
-} dir_t;
-
-dir_t *dirOpen(arena_t *arena, strview_t path) {
- if (strvIsEmpty(path)) {
- err("path cannot be null");
- return NULL;
- }
-
- uint8 tmpbuf[1024];
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
- str_t dirpath = str(&scratch, path);
-
- DIR *dir = opendir(dirpath.buf);
- if (!dir) {
- err("could not open dir (%v)", path);
- return NULL;
- }
-
- dir_t *ctx = alloc(arena, dir_t);
- ctx->dir = dir;
- return ctx;
-}
-
-void dirClose(dir_t *ctx) {
- if (ctx) {
- closedir(ctx->dir);
- }
-}
-
-bool dirIsValid(dir_t *ctx) {
- return ctx && ctx->dir;
-}
-
-dir_entry_t *dirNext(arena_t *arena, dir_t *ctx) {
- if (!ctx) return NULL;
-
- struct dirent *dp = readdir(ctx->dir);
- if (!dp) {
- dirClose(ctx);
- return NULL;
- }
-
- ctx->next.name = (str_t){ dp->d_name, strlen(dp->d_name) };
- ctx->next.type = dp->d_type == DT_DIR ? DIRTYPE_DIR : DIRTYPE_FILE;
- ctx->next.filesize = 0;
-
- if (dp->d_type == DT_REG) {
- struct stat file_info = {0};
- stat(dp->d_name, &file_info);
- ctx->next.filesize = file_info.st_size;
- }
-
- return &ctx->next;
-}
-
-#endif
diff --git a/dir.h b/dir.h
deleted file mode 100644
index e4ed540..0000000
--- a/dir.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "str.h"
-#include "arena.h"
-
-typedef struct dir_t dir_t;
-
-typedef enum {
- DIRTYPE_FILE,
- DIRTYPE_DIR,
-} dir_type_e;
-
-typedef struct {
- str_t name;
- dir_type_e type;
- usize filesize;
-} dir_entry_t;
-
-dir_t *dirOpen(arena_t *arena, strview_t path);
-// optional, only call this if you want to return before dirNext returns NULL
-void dirClose(dir_t *ctx);
-
-bool dirIsValid(dir_t *ctx);
-
-dir_entry_t *dirNext(arena_t *arena, dir_t *ctx);
diff --git a/docs/arena.md b/docs/arena.md
deleted file mode 100644
index 438b390..0000000
--- a/docs/arena.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Arena
------------
-
-An arena is a bump allocator, under the hood it can use one of 3 strategies to allocate its data:
-
-* `Virtual`: allocates with virtual memory, meaning that it reserves the data upfront, but only allocates one page at a time (usually 64 Kb). This is the preferred way to use the arena as it can freely allocate gigabytes of memory for free
-* `Malloc`: allocates the memory upfront using malloc
-* `Static`: uses a buffer passed by the user instead of allocating
-
-To help with allocating big chunks of data, `arena.h` defines the macros `KB`, `MB`, and `GB`. If you don't need them you can define `ARENA_NO_SIZE_HELPERS`
-
-To create an arena use the macro `arenaMake`:
-```c
-arena_t varena = arenaMake(ARENA_VIRTUAL, GB(1));
-uint8 buffer[1024];
-arena_t sarena = arenaMake(ARENA_STATIC, sizeof(buffer), buffer);
-```
-
-To allocate use the `alloc` macro. The parameters to allocate are:
-
-* A pointer to the arena
-* The type to allocate
-* (optional) the number of items to allocate
-* (optional) flags:
- * `ALLOC_NOZERO`: by default the arena sets the memory to zero before returning, use this if you want to skip it
- * `ALLOC_SOFT_FAIL`: by default the arena panics when an allocation fails, meaning that it never returns `NULL`.
-* (automatic) the align of the type
-* (automatic) the size of the type
-
-Example usage:
-
-```c
-// allocate 15 strings
-str_t *string_list = alloc(arena, str_t, 15);
-// allocate a 1024 bytes buffer
-uint8 *buffer = alloc(arena, uint8, 1024);
-// allocate a structure
-game_t *game = alloc(arena, game_t);
-```
-
-The strength of the arena is that it makes it much easier to reason about lifetimes, for instance:
-
-```c
-// pass a pointer to the arena for the data that we need to return
-WCHAR *win32_get_full_path(arena_t *arena, const char *rel_path) {
- // we need a small scratch arena for allocations that
- // will be thrown away at the end of this function
- uint8 scratch_buf[1024];
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(scratch_buf, scratch_buf));
-
- WCHAR *win32_rel_path = str_to_wchar(&scratch, rel_path);
-
- DWORD pathlen = GetFullPathName(win32_rel_path, 0, NULL, NULL);
- WCHAR *fullpath = alloc(arena, WCHAR, pathlen + 1);
- GetFullPath(win32_rel_path, pathlen + 1, fullpath, NULL);
-
- // notice how we don't need to free anything at the end
- return fullpath;
-}
-```
-
-There are a few helper functions:
-
-* `arenaScratch`: sub allocate an arena from another arena
-* `arenaTell`: returns the number of bytes allocated
-* `arenaRemaining`: returns the number of bytes that can still be allocated
-* `arenaRewind`: rewinds the arena to N bytes from the start (effectively "freeing" that memory)
-* `arenaPop`: pops N bytes from the arena (effectively "freeing" that memory)
-
-Here is an example usage of a full program:
-
-```c
-typedef struct {
- char *buf;
- usize len;
-} str_t;
-
-str_t read_file(arena_t *arena, const char *filename) {
- str_t out = {0};
-
- FILE *fp = fopen(filename, "rb");
- if (!fp) goto error;
-
- fseek(fp, 0, SEEK_END);
- out.len = ftell(fp);
- fseek(fp, 0, SEEK_SET);
-
- out.buf = alloc(arena, char, out.len + 1);
-
- fread(out.buf, 1, out.len, fp);
-
-error:
- return out;
-}
-
-void write_file(const char *filename, str_t data) {
- FILE *fp = fopen(filename, "wb");
- if (!fp) return;
- fwrite(data.buf, 1, data.len, fp);
-}
-
-int main() {
- arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
- str_t title = {0};
-
- {
- uint8 tmpbuf[KB(5)];
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
- str_t file = read_file(&scratch, "file.json");
- json_t json = json_parse(&scratch, file);
- title = str_dup(arena, json_get(json, "title"));
- }
-
- {
- // copying an arena by value effectively makes it a scratch arena,
- // as long as you don't use the original inside the same scope!
- arena_t scratch = arena;
- str_t to_write = str_fmt(
- &scratch,
- "{ \"title\": \"%s\" }", title.buf
- );
- write_file("out.json", to_write);
- }
-
- // cleanup all allocations at once
- arenaCleanup(&arena);
-}
-```
\ No newline at end of file
diff --git a/docs/base64.md b/docs/base64.md
deleted file mode 100644
index e238648..0000000
--- a/docs/base64.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Base 64
-----------
-
-The `base64.h` header has only two functions, one to encode and one to decode to/from base 64.
-
-Example usage:
-```c
-char *recv_callback(arena_t *arena, buffer_t msg) {
- buffer_t decoded = base64Decode(arena, msg);
- alloc(arena, char); // allocate an extra char for the null pointer
- return (char *)decoded.data;
-}
-
-buffer_t send_callback(arena_t *arena) {
- char msg[] = "Hello World!";
- buffer_t buf = {
- .data = msg,
- .len = arrlen(msg) - 1, // don't include the null pointer
- };
- buffer_t encoded = base64Encode(arena, buf);
- return encoded;
-}
-
-int main() {
- arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
- run_server(&arena, 8080, recv_callback, send_callback);
- arenaCleanup(&arena);
-}
-```
diff --git a/docs/cthreads.md b/docs/cthreads.md
deleted file mode 100644
index fee03d9..0000000
--- a/docs/cthreads.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Threads
-----------
-
-Cross platform threads, mutexes and conditional variables.
-The api is very similar to pthreads, here is a small example program that uses threads and mutexes.
-
-```c
-struct {
- bool exit;
- cmutex_t mtx;
-} state = {0};
-
-int f1(void *) {
- mtxLock(state.mtx);
- state.exit = true;
- mtxUnlock(state.mtx);
- return 0;
-}
-
-int f2(void *) {
- while (true) {
- bool exit = false;
- if (mtxTryLock(state.mtx)) {
- exit = state.exit;
- mtxUnlock(state.mtx);
- }
-
- if (exit) {
- break;
- }
- }
- return 0;
-}
-
-int main() {
- state.mtx = mtxInit();
-
- cthread_t t1 = thrCreate(f1, NULL);
- thrDetach(t1);
-
- cthread_t t2 = thrCreate(f2, NULL);
- thrJoin(t2, NULL);
-
- mtxFree(state.mtx);
-}
-```
\ No newline at end of file
diff --git a/docs/dir.md b/docs/dir.md
deleted file mode 100644
index ea481da..0000000
--- a/docs/dir.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Dir
-----------
-
-This header provides a simple directory walker, here is an example usage:
-
-```c
-typedef struct source_t {
- str_t filename;
- struct source_t *next;
-} source_t;
-
-sources_t get_sources(arena_t *arena, strview_t path) {
- uint8 tmpbuf[KB(5)] = {0};
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
-
- dir_t *dir = dirOpen(&scratch, path);
- dir_entry_t *entry = NULL;
-
- source_t *sources = NULL;
-
- while ((entry = dirNext(&scratch, dir))) {
- if (entry->type != DIRTYPE_FILE) {
- continue;
- }
-
- strview_t ext = fileGetExtension(strv(entry->name));
- if (!strvEquals(ext, strv(".c"))) {
- continue;
- }
-
- source_t *new_source = alloc(arena, source_t);
- new_source->filename = strFmt(arena, "%v/%v", path, entry->name);
- new_source->next = sources;
- sources = new_source;
- }
-
- return sources;
-}
-
-int main() {
- arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
- source_t *sources = get_sources(&arena, strv("src/colla"));
- while (sources) {
- info("> %v", sources->filename);
- sources = sources->next;
- }
- arenaCleanup(&arena);
-}
-```
\ No newline at end of file
diff --git a/docs/docs.com b/docs/docs.com
deleted file mode 100644
index 3bdaaf4..0000000
Binary files a/docs/docs.com and /dev/null differ
diff --git a/docs/file.md b/docs/file.md
deleted file mode 100644
index 79abefa..0000000
--- a/docs/file.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# File
-----------
-
-This header provides cross platform file functionality.
-It has all the basics that you can expect which work exactly like the stdio counterparts:
-
-* `fileOpen`
-* `fileClose`
-* `fileIsValid`
-* `fileRead`
-* `fileWrite`
-* `fileSeekEnd`
-* `fileRewind`
-* `fileTell`
-
-Then there are a few helpers functions for reading / writing:
-
-* Writing:
- * `filePutc`
- * `filePuts`
- * `filePrintf`
- * `fileWriteWhole`
-* Reading:
- * `fileReadWhole`
- * `fileReadWholeStr`
-
-There are also some functions to get info about a file without having to open it:
-
-* `fileExists`
-* `fileSize`
-* `fileGetTime`
-* `fileHasChanged`
-
-And finally, there are some helper functions:
-
-* `fileGetFullPath` (for windows)
-* `fileSplitPath` / `fileGetFilename` / `fileGetExtension`
-* `fileDelete`
diff --git a/docs/format.md b/docs/format.md
deleted file mode 100644
index 2afd810..0000000
--- a/docs/format.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Format
-----------
-
-Small formatting utility, it has 2 functions (and the `va_list` alternatives):
-
-* `fmtPrint`: equivalent to
-* `fmtBuffer`
-
-It uses [stb_sprintf](https://github.com/nothings/stb/blob/master/stb_sprintf.h) under the hood, but it also has support for printing buffers using `%v` (structures that are a pair of pointer/size)
-
-This means it can print strings and [string views](/str).
-
-In
-
-Usage example:
-
-```c
-int main() {
- strview_t v = strv("world");
- char buffer[1024] = {0};
-
- fmtPrint("Hello %v!", v);
- fmtBuffer(buffer, sizeof(buffer), "Hello %v!", v);
-}
-```
\ No newline at end of file
diff --git a/docs/highlight.md b/docs/highlight.md
deleted file mode 100644
index 42edc70..0000000
--- a/docs/highlight.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Highlight
-----------
-
-This header provides an highlighter for c-like languages (mostly c).
-The usage is simple, first create a highlight context using hlInit, for which you need an hl_config_t. The only mandatory argument is colors, which are the strings put before the keywords in the highlighted text.
-
-You can also pass some flags:
-* `HL_FLAG_HTML`: escapes html characters
-
-If you're using the offline documentation, this is the code used to highlight inside the markdown code blocks (simplified to remove actual markdown parsing):
-
-```c
-str_t highlight_code_block(arena_t *arena, strview_t code) {
- uint8 tmpbuf[KB(5)];
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
-
- hl_ctx_t *hl = hlInit(&scratch, &(hl_config_t){
- .colors = {
- [HL_COLOR_NORMAL] = strv(""),
- [HL_COLOR_PREPROC] = strv(""),
- [HL_COLOR_TYPES] = strv(""),
- [HL_COLOR_CUSTOM_TYPES] = strv(""),
- [HL_COLOR_KEYWORDS] = strv(""),
- [HL_COLOR_NUMBER] = strv(""),
- [HL_COLOR_STRING] = strv(""),
- [HL_COLOR_COMMENT] = strv(""),
- [HL_COLOR_FUNC] = strv(""),
- [HL_COLOR_SYMBOL] = strv(""),
- [HL_COLOR_MACRO] = strv(""),
- },
- .flags = HL_FLAG_HTML,
- });
-}
-```
\ No newline at end of file
diff --git a/docs/hot_reload.md b/docs/hot_reload.md
deleted file mode 100644
index 65a59b5..0000000
--- a/docs/hot_reload.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Hot Reload
-----------
-
-This header provides cross-platform library hot-reloading. To use it you need to have to entry points, one for the host and one for the client.
-
-In the client you can then implement these functions:
-
-* `hr_init`: called when the library is loaded (or reloaded)
-* `hr_loop`: called every "tick" (or whenever the host decides)
-* `hr_close`: called when the host finishes
-
-In the client you need to call these functions:
-
-* `hrOpen`: load the library and call `hr_init`
-* `hrStep`: call `hr_loop`
-* `hrReload`: check if the library has changed, and if so reload it and call `hr_init` again
-* `hrClose`: call `hr_close` and cleanup
-
-Example usage:
-
-### Client
-
-```c
-int hr_init(hr_t *ctx) {
- uint8 tmpbuf[KB(5)];
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
-
- state_t *state = ctx->userdata;
- uint64 timestamp = fileGetTime(scratch, strv("sprite.png"));
- if (timestamp > state->last_write) {
- state->last_write = timestamp;
- destroy_image(state->sprite);
- state->sprite = load_image(strv("sprite.png"));
- }
-}
-
-int hr_loop(hr_t *ctx) {
- state_t *state = ctx->userdata;
- draw_image(state->sprite, state->posx, state->posy);
-}
-
-int hr_close(hr_t *ctx) {
- state_t *state = ctx->userdata;
- destroy_image(state->sprite);
-}
-```
-
-### Host
-
-```c
-typedef struct {
- hr_t hr;
- image_t sprite;
- int posx;
- int posy;
- uint64 last_write;
-} state_t;
-
-int main() {
- arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
-
- state_t state = {0};
- state.hr.userdata = &state;
-
- if (!hrOpen(&state.hr, strv("bin/client.dll"))) {
- return 1;
- }
-
- while (game_poll()) {
- hrReload(&state.hr);
- hrStep(&state.hr);
- }
-
- hrClose(&state.hr, true);
-}
-```
\ No newline at end of file
diff --git a/docs/html.md b/docs/html.md
deleted file mode 100644
index 873736d..0000000
--- a/docs/html.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# HTML
-----------
-
-This header provides an easy (although debatably sane) way to generate html in c.
-
-There are three types of tags:
-* One and done tags, like `
` or `
` which only have an opening tag
-* Basic tags which follow this format: `content`
-* Tags where the content is probably other tags
-
-You can open and close any tags using `tagBeg` and `tagEnd`, you can also set attributes like this:
-
-```c
-tagBeg("div", .class="content", .id="main");
-```
-
-Finally, you can use the `htmlRaw` macro to write raw html.
-
-Example code:
-```c
-str_t generate_page(arena_t *arena, page_t *data) {
- str_t out = STR_EMPTY;
-
- htmlBeg(arena, &out);
- headBeg();
- title(data->title);
- htmlRaw()
- style(data->css);
- headEnd();
- bodyBeg();
- divBeg(.id="main");
- h1("Hello World!");
- hr();
- p("Some content blah blah");
- img(.src="image.png");
- divEnd();
- bodyEnd();
- htmlEnd();
-
- return out;
-}
-```
\ No newline at end of file
diff --git a/docs/http.md b/docs/http.md
deleted file mode 100644
index 95f751c..0000000
--- a/docs/http.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# HTTP
-----------
\ No newline at end of file
diff --git a/docs/ini.md b/docs/ini.md
deleted file mode 100644
index 62590b6..0000000
--- a/docs/ini.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Ini
-----------
\ No newline at end of file
diff --git a/docs/json.md b/docs/json.md
deleted file mode 100644
index e529cd9..0000000
--- a/docs/json.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Json
-----------
\ No newline at end of file
diff --git a/docs/markdown.md b/docs/markdown.md
deleted file mode 100644
index f65eab2..0000000
--- a/docs/markdown.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Markdown
-----------
\ No newline at end of file
diff --git a/docs/readme.md b/docs/readme.md
deleted file mode 100644
index ce9fbcc..0000000
--- a/docs/readme.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Colla
-----------
-
-Colla is a library that I use personally for all my C projects. It doesn't have one specific purpose, but more a collection of useful things that I've written.
\ No newline at end of file
diff --git a/docs/server.md b/docs/server.md
deleted file mode 100644
index 61d57d2..0000000
--- a/docs/server.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Server
-----------
\ No newline at end of file
diff --git a/docs/sha1.md b/docs/sha1.md
deleted file mode 100644
index f9f2ba1..0000000
--- a/docs/sha1.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# SHA-1
-----------
\ No newline at end of file
diff --git a/docs/socket.md b/docs/socket.md
deleted file mode 100644
index 6188d75..0000000
--- a/docs/socket.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Socket
-----------
\ No newline at end of file
diff --git a/docs/str.md b/docs/str.md
deleted file mode 100644
index 375e85d..0000000
--- a/docs/str.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Str
-----------
\ No newline at end of file
diff --git a/docs/strstream.md b/docs/strstream.md
deleted file mode 100644
index e619e1d..0000000
--- a/docs/strstream.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# StrStream
-----------
\ No newline at end of file
diff --git a/docs/tracelog.md b/docs/tracelog.md
deleted file mode 100644
index 0d12d1f..0000000
--- a/docs/tracelog.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Tracelog
-----------
\ No newline at end of file
diff --git a/docs/utf8.md b/docs/utf8.md
deleted file mode 100644
index 5a15b43..0000000
--- a/docs/utf8.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# UTF-8
-----------
\ No newline at end of file
diff --git a/docs/vec.md b/docs/vec.md
deleted file mode 100644
index 7500a52..0000000
--- a/docs/vec.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Vec
-----------
\ No newline at end of file
diff --git a/docs/vmem.md b/docs/vmem.md
deleted file mode 100644
index 694c7e2..0000000
--- a/docs/vmem.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# VMem
-----------
\ No newline at end of file
diff --git a/docs/websocket.md b/docs/websocket.md
deleted file mode 100644
index f590eb5..0000000
--- a/docs/websocket.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# WebSocket
-----------
\ No newline at end of file
diff --git a/docs/xml.md b/docs/xml.md
deleted file mode 100644
index 493d0f9..0000000
--- a/docs/xml.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Xml
-----------
\ No newline at end of file
diff --git a/file.c b/file.c
deleted file mode 100644
index c24fb14..0000000
--- a/file.c
+++ /dev/null
@@ -1,398 +0,0 @@
-#include "file.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include "tracelog.h"
-#include "format.h"
-
-#define FILE_MAKE_SCRATCH() \
- uint8 tmpbuf[KB(1)]; \
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf)
-
-#if COLLA_WIN
-
-#include
-
-#undef FILE_BEGIN
-#undef FILE_CURRENT
-#undef FILE_END
-
-#define FILE_BEGIN 0
-#define FILE_CURRENT 1
-#define FILE_END 2
-
-#if COLLA_TCC
-#include "tcc/colla_tcc.h"
-#endif
-
-static DWORD file__mode_to_access(filemode_e mode) {
- if (mode & FILE_APPEND) return FILE_APPEND_DATA;
-
- DWORD out = 0;
- if (mode & FILE_READ) out |= GENERIC_READ;
- if (mode & FILE_WRITE) out |= GENERIC_WRITE;
- return out;
-}
-
-static DWORD file__mode_to_creation(filemode_e mode) {
- if (mode == FILE_READ) return OPEN_EXISTING;
- if (mode == FILE_WRITE) return CREATE_ALWAYS;
- return OPEN_ALWAYS;
-}
-
-bool fileExists(strview_t path) {
- FILE_MAKE_SCRATCH();
- str_t name = str(&scratch, path);
- return GetFileAttributesA(name.buf) != INVALID_FILE_ATTRIBUTES;
-}
-
-TCHAR *fileGetFullPath(arena_t *arena, strview_t filename) {
- FILE_MAKE_SCRATCH();
-
- TCHAR long_path_prefix[] = TEXT("\\\\?\\");
- const usize prefix_len = arrlen(long_path_prefix) - 1;
-
- TCHAR *rel_path = strvToTChar(&scratch, filename);
- DWORD pathlen = GetFullPathName(rel_path, 0, NULL, NULL);
-
- TCHAR *full_path = alloc(arena, TCHAR, pathlen + prefix_len + 1);
- memcpy(full_path, long_path_prefix, prefix_len * sizeof(TCHAR));
-
- GetFullPathName(rel_path, pathlen + 1, full_path + prefix_len, NULL);
-
- return full_path;
-}
-
-bool fileDelete(strview_t filename) {
- FILE_MAKE_SCRATCH();
- wchar_t *wfname = strvToWChar(&scratch, filename, NULL);
- return DeleteFileW(wfname);
-}
-
-file_t fileOpen(strview_t name, filemode_e mode) {
- FILE_MAKE_SCRATCH();
-
- TCHAR *full_path = fileGetFullPath(&scratch, name);
-
- HANDLE handle = CreateFile(
- full_path,
- file__mode_to_access(mode),
- FILE_SHARE_READ,
- NULL,
- file__mode_to_creation(mode),
- FILE_ATTRIBUTE_NORMAL,
- NULL
- );
-
- return (file_t){
- .handle = (uintptr_t)handle,
- };
-}
-
-void fileClose(file_t ctx) {
- if (!fileIsValid(ctx)) return;
- CloseHandle((HANDLE)ctx.handle);
-}
-
-bool fileIsValid(file_t ctx) {
- return (HANDLE)ctx.handle != 0 &&
- (HANDLE)ctx.handle != INVALID_HANDLE_VALUE;
-}
-
-usize fileRead(file_t ctx, void *buf, usize len) {
- if (!fileIsValid(ctx)) return 0;
- DWORD read = 0;
- ReadFile((HANDLE)ctx.handle, buf, (DWORD)len, &read, NULL);
- return (usize)read;
-}
-
-usize fileWrite(file_t ctx, const void *buf, usize len) {
- if (!fileIsValid(ctx)) return 0;
- DWORD written = 0;
- WriteFile((HANDLE)ctx.handle, buf, (DWORD)len, &written, NULL);
- return (usize)written;
-}
-
-bool fileSeek(file_t ctx, usize pos) {
- if (!fileIsValid(ctx)) return false;
- LARGE_INTEGER offset = {
- .QuadPart = pos,
- };
- DWORD result = SetFilePointer((HANDLE)ctx.handle, offset.LowPart, &offset.HighPart, FILE_BEGIN);
- return result != INVALID_SET_FILE_POINTER;
-}
-
-bool fileSeekEnd(file_t ctx) {
- if (!fileIsValid(ctx)) return false;
- DWORD result = SetFilePointer((HANDLE)ctx.handle, 0, NULL, FILE_END);
- return result != INVALID_SET_FILE_POINTER;
-}
-
-void fileRewind(file_t ctx) {
- if (!fileIsValid(ctx)) return;
- SetFilePointer((HANDLE)ctx.handle, 0, NULL, FILE_BEGIN);
-}
-
-usize fileTell(file_t ctx) {
- if (!fileIsValid(ctx)) return 0;
- LARGE_INTEGER tell = {0};
- BOOL result = SetFilePointerEx((HANDLE)ctx.handle, (LARGE_INTEGER){0}, &tell, FILE_CURRENT);
- return result == TRUE ? (usize)tell.QuadPart : 0;
-}
-
-usize fileSize(file_t ctx) {
- if (!fileIsValid(ctx)) return 0;
- LARGE_INTEGER size = {0};
- BOOL result = GetFileSizeEx((HANDLE)ctx.handle, &size);
- return result == TRUE ? (usize)size.QuadPart : 0;
-}
-
-uint64 fileGetTimeFP(file_t ctx) {
- if (!fileIsValid(ctx)) return 0;
- FILETIME time = {0};
- GetFileTime((HANDLE)ctx.handle, NULL, NULL, &time);
- ULARGE_INTEGER utime = {
- .HighPart = time.dwHighDateTime,
- .LowPart = time.dwLowDateTime,
- };
- return (uint64)utime.QuadPart;
-}
-
-#else
-
-#include
-
-static const char *file__mode_to_stdio(filemode_e mode) {
- if (mode == FILE_READ) return "rb";
- if (mode == FILE_WRITE) return "wb";
- if (mode == FILE_APPEND) return "ab";
- if (mode == (FILE_READ | FILE_WRITE)) return "rb+";
-
- return "ab+";
-}
-
-bool fileExists(strview_t path) {
- FILE_MAKE_SCRATCH();
- str_t name = str(&scratch, path);
-
- FILE *fp = fopen(name.buf, "rb");
- bool exists = fp != NULL;
- fclose(fp);
-
- return exists;
-}
-
-bool fileDelete(strview_t filename) {
- FILE_MAKE_SCRATCH();
- str_t name = str(&scratch, filename);
- return remove(name.buf) == 0;
-}
-
-file_t fileOpen(strview_t name, filemode_e mode) {
- FILE_MAKE_SCRATCH();
- str_t filename = str(&scratch, name);
- return (file_t) {
- .handle = (uintptr_t)fopen(filename.buf, file__mode_to_stdio(mode))
- };
-}
-
-void fileClose(file_t ctx) {
- FILE *fp = (FILE *)ctx.handle;
- if (fp) {
- fclose(fp);
- }
-}
-
-bool fileIsValid(file_t ctx) {
- bool is_valid = (FILE *)ctx.handle != NULL;
- if (!is_valid) warn("file not valid");
- return is_valid;
-}
-
-usize fileRead(file_t ctx, void *buf, usize len) {
- if (!fileIsValid(ctx)) return 0;
- return fread(buf, 1, len, (FILE *)ctx.handle);
-}
-
-usize fileWrite(file_t ctx, const void *buf, usize len) {
- if (!fileIsValid(ctx)) return 0;
- return fwrite(buf, 1, len, (FILE *)ctx.handle);
-}
-
-bool fileSeek(file_t ctx, usize pos) {
- assert(pos < INT32_MAX);
- if (!fileIsValid(ctx)) return false;
- return fseek((FILE *)ctx.handle, (long)pos, SEEK_SET) == 0;
-}
-
-bool fileSeekEnd(file_t ctx) {
- if (!fileIsValid(ctx)) return false;
- return fseek((FILE *)ctx.handle, 0, SEEK_END) == 0;
-}
-
-void fileRewind(file_t ctx) {
- if (!fileIsValid(ctx)) return;
- fseek((FILE *)ctx.handle, 0, SEEK_SET);
-}
-
-usize fileTell(file_t ctx) {
- if (!fileIsValid(ctx)) return 0;
- return ftell((FILE *)ctx.handle);
-}
-
-usize fileSize(file_t ctx) {
- if (!fileIsValid(ctx)) return 0;
- FILE *fp = (FILE *)ctx.handle;
- fseek(fp, 0, SEEK_END);
- long len = ftell(fp);
- fseek(fp, 0, SEEK_SET);
- return (usize)len;
-}
-
-uint64 fileGetTimeFP(file_t ctx) {
-#if COLLA_LIN
- return 0;
-#else
- fatal("fileGetTime not implemented yet outside of linux and windows");
- return 0;
-#endif
-}
-
-#endif
-
-strview_t fileGetFilename(strview_t path) {
- usize last_lin = strvRFind(path, '/', 0);
- usize last_win = strvRFind(path, '\\', 0);
- last_lin = last_lin != SIZE_MAX ? last_lin : 0;
- last_win = last_win != SIZE_MAX ? last_win : 0;
- usize last = max(last_lin, last_win);
- return strvSub(path, last ? last + 1 : last, SIZE_MAX);
-}
-
-strview_t fileGetExtension(strview_t path) {
- usize ext_pos = strvRFind(path, '.', 0);
- return strvSub(path, ext_pos, SIZE_MAX);
-}
-
-void fileSplitPath(strview_t path, strview_t *dir, strview_t *name, strview_t *ext) {
- usize dir_lin = strvRFind(path, '/', 0);
- usize dir_win = strvRFind(path, '\\', 0);
- dir_lin = dir_lin != STR_NONE ? dir_lin : 0;
- dir_win = dir_win != STR_NONE ? dir_win : 0;
- usize dir_pos = max(dir_lin, dir_win);
-
- usize ext_pos = strvRFind(path, '.', 0);
-
- if (dir) {
- *dir = strvSub(path, 0, dir_pos);
- }
- if (name) {
- *name = strvSub(path, dir_pos ? dir_pos + 1 : dir_pos, ext_pos);
- }
- if (ext) {
- *ext = strvSub(path, ext_pos, SIZE_MAX);
- }
-}
-
-bool filePutc(file_t ctx, char c) {
- return fileWrite(ctx, &c, 1) == 1;
-}
-
-bool filePuts(file_t ctx, strview_t v) {
- return fileWrite(ctx, v.buf, v.len) == v.len;
-}
-
-bool filePrintf(arena_t scratch, file_t ctx, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- bool result = filePrintfv(scratch, ctx, fmt, args);
- va_end(args);
- return result;
-}
-
-bool filePrintfv(arena_t scratch, file_t ctx, const char *fmt, va_list args) {
- str_t string = strFmtv(&scratch, fmt, args);
- return fileWrite(ctx, string.buf, string.len) == string.len;
-}
-
-buffer_t fileReadWhole(arena_t *arena, strview_t name) {
- file_t fp = fileOpen(name, FILE_READ);
- if (!fileIsValid(fp)) {
- err("could not open file: %v", name);
- return (buffer_t){0};
- }
- buffer_t out = fileReadWholeFP(arena, fp);
- fileClose(fp);
- return out;
-}
-
-buffer_t fileReadWholeFP(arena_t *arena, file_t ctx) {
- if (!fileIsValid(ctx)) return (buffer_t){0};
- buffer_t out = {0};
-
- out.len = fileSize(ctx);
- out.data = alloc(arena, uint8, out.len);
- usize read = fileRead(ctx, out.data, out.len);
-
- if (read != out.len) {
- err("fileReadWholeFP: fileRead failed, should be %zu but is %zu", out.len, read);
- arenaPop(arena, out.len);
- return (buffer_t){0};
- }
-
- return out;
-}
-
-str_t fileReadWholeStr(arena_t *arena, strview_t name) {
- file_t fp = fileOpen(name, FILE_READ);
- if (!fileIsValid(fp)) {
- warn("could not open file (%v)", name);
- }
- str_t out = fileReadWholeStrFP(arena, fp);
- fileClose(fp);
- return out;
-}
-
-str_t fileReadWholeStrFP(arena_t *arena, file_t ctx) {
- if (!fileIsValid(ctx)) return STR_EMPTY;
-
- str_t out = {0};
-
- out.len = fileSize(ctx);
- out.buf = alloc(arena, uint8, out.len + 1);
- usize read = fileRead(ctx, out.buf, out.len);
-
- if (read != out.len) {
- err("fileReadWholeStrFP: fileRead failed, should be %zu but is %zu", out.len, read);
- arenaPop(arena, out.len + 1);
- return STR_EMPTY;
- }
-
- return out;
-}
-
-bool fileWriteWhole(strview_t name, const void *buf, usize len) {
- file_t fp = fileOpen(name, FILE_WRITE);
- if (!fileIsValid(fp)) {
- return false;
- }
- usize written = fileWrite(fp, buf, len);
- fileClose(fp);
- return written == len;
-}
-
-uint64 fileGetTime(strview_t name) {
- file_t fp = fileOpen(name, FILE_READ);
- uint64 result = fileGetTimeFP(fp);
- fileClose(fp);
- return result;
-}
-
-bool fileHasChanged(strview_t name, uint64 last_timestamp) {
- uint64 timestamp = fileGetTime(name);
- return timestamp > last_timestamp;
-}
-
-#include "warnings/colla_warn_end.h"
-
-#undef FILE_MAKE_SCRATCH
\ No newline at end of file
diff --git a/file.h b/file.h
deleted file mode 100644
index 088f656..0000000
--- a/file.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#pragma once
-
-#include
-
-#include "collatypes.h"
-#include "str.h"
-#include "arena.h"
-
-typedef enum {
- FILE_READ = 1 << 0,
- FILE_WRITE = 1 << 1,
- FILE_APPEND = 1 << 2,
-} filemode_e;
-
-typedef struct {
- uintptr_t handle;
-} file_t;
-
-bool fileExists(strview_t path);
-TCHAR *fileGetFullPath(arena_t *arena, strview_t filename);
-strview_t fileGetFilename(strview_t path);
-strview_t fileGetExtension(strview_t path);
-void fileSplitPath(strview_t path, strview_t *dir, strview_t *name, strview_t *ext);
-bool fileDelete(strview_t filename);
-
-file_t fileOpen(strview_t name, filemode_e mode);
-void fileClose(file_t ctx);
-
-bool fileIsValid(file_t ctx);
-
-bool filePutc(file_t ctx, char c);
-bool filePuts(file_t ctx, strview_t v);
-bool filePrintf(arena_t scratch, file_t ctx, const char *fmt, ...);
-bool filePrintfv(arena_t scratch, file_t ctx, const char *fmt, va_list args);
-
-usize fileRead(file_t ctx, void *buf, usize len);
-usize fileWrite(file_t ctx, const void *buf, usize len);
-
-bool fileSeek(file_t ctx, usize pos);
-bool fileSeekEnd(file_t ctx);
-void fileRewind(file_t ctx);
-
-usize fileTell(file_t ctx);
-usize fileSize(file_t ctx);
-
-buffer_t fileReadWhole(arena_t *arena, strview_t name);
-buffer_t fileReadWholeFP(arena_t *arena, file_t ctx);
-
-str_t fileReadWholeStr(arena_t *arena, strview_t name);
-str_t fileReadWholeStrFP(arena_t *arena, file_t ctx);
-
-bool fileWriteWhole(strview_t name, const void *buf, usize len);
-
-uint64 fileGetTime(strview_t name);
-uint64 fileGetTimeFP(file_t ctx);
-bool fileHasChanged(strview_t name, uint64 last_timestamp);
\ No newline at end of file
diff --git a/format.c b/format.c
deleted file mode 100644
index 7ed9d21..0000000
--- a/format.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "format.h"
-
-#define STB_SPRINTF_DECORATE(name) stb_##name
-#define STB_SPRINTF_NOUNALIGNED
-#define STB_SPRINTF_IMPLEMENTATION
-#include "stb/stb_sprintf.h"
-
-#include "arena.h"
-
-static char *fmt__stb_callback(const char *buf, void *ud, int len) {
- (void)len;
- printf("%.*s", len, buf);
- return (char *)ud;
-}
-
-int fmtPrint(const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- int out = fmtPrintv(fmt, args);
- va_end(args);
- return out;
-}
-
-int fmtPrintv(const char *fmt, va_list args) {
- char buffer[STB_SPRINTF_MIN] = {0};
- return stb_vsprintfcb(fmt__stb_callback, buffer, buffer, fmt, args);
-}
-
-int fmtBuffer(char *buffer, usize buflen, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- int out = fmtBufferv(buffer, buflen, fmt, args);
- va_end(args);
- return out;
-}
-
-int fmtBufferv(char *buffer, usize buflen, const char *fmt, va_list args) {
- return stb_vsnprintf(buffer, (int)buflen, fmt, args);
-}
-
-#if 0
-str_t fmtStr(Arena *arena, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- str_t out = fmtStrv(arena, fmt, args);
- va_end(args);
- return out;
-}
-
-str_t fmtStrv(Arena *arena, const char *fmt, va_list args) {
- va_list vcopy;
- va_copy(vcopy, args);
- int len = stb_vsnprintf(NULL, 0, fmt, vcopy);
- va_end(vcopy);
-
- char *buffer = alloc(arena, char, len + 1);
- stb_vsnprintf(buffer, len + 1, fmt, args);
-
- return (str_t){ .buf = buffer, .len = (usize)len };
-}
-#endif
\ No newline at end of file
diff --git a/format.h b/format.h
deleted file mode 100644
index 4d566ad..0000000
--- a/format.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include
-
-#include "collatypes.h"
-
-typedef struct arena_t arena_t;
-
-int fmtPrint(const char *fmt, ...);
-int fmtPrintv(const char *fmt, va_list args);
-
-int fmtBuffer(char *buffer, usize buflen, const char *fmt, ...);
-int fmtBufferv(char *buffer, usize buflen, const char *fmt, va_list args);
diff --git a/highlight.c b/highlight.c
deleted file mode 100644
index 466f242..0000000
--- a/highlight.c
+++ /dev/null
@@ -1,621 +0,0 @@
-#include "highlight.h"
-
-// based on https://github.com/Theldus/kat
-
-#include
-
-#include "arena.h"
-#include "tracelog.h"
-#include "strstream.h"
-
-typedef enum {
- HL_STATE_DEFAULT,
- HL_STATE_KEYWORD,
- HL_STATE_NUMBER,
- HL_STATE_CHAR,
- HL_STATE_STRING,
- HL_STATE_COMMENT_MULTI,
- HL_STATE_PREPROCESSOR,
- HL_STATE_PREPROCESSOR_INCLUDE,
- HL_STATE_PREPROCESSOR_INCLUDE_STRING,
-} hl_state_e;
-
-typedef enum {
- HL_HTABLE_FAILED,
- HL_HTABLE_REPLACED,
- HL_HTABLE_ADDED,
-} hl_htable_result_e;
-
-typedef struct hl_node_t {
- strview_t key;
- hl_color_e value;
- struct hl_node_t *next;
-} hl_node_t;
-
-typedef struct {
- hl_node_t **buckets;
- uint count;
- uint used;
- uint collisions;
-} hl_hashtable_t;
-
-static hl_hashtable_t hl_htable_init(arena_t *arena, uint pow2_exp);
-static hl_htable_result_e hl_htable_add(arena_t *arena, hl_hashtable_t *table, strview_t key, hl_color_e value);
-static hl_node_t *hl_htable_get(hl_hashtable_t *table, strview_t key);
-static uint64 hl_htable_hash(const void *bytes, usize count);
-
-typedef struct hl_ctx_t {
- hl_state_e state;
- hl_flags_e flags;
- usize kw_beg;
- strview_t colors[HL_COLOR__COUNT]; // todo: maybe should be str_t?
- outstream_t ostr;
- hl_hashtable_t kw_htable;
- bool symbol_table[256];
-} hl_ctx_t;
-
-#define KW(str, col) { { str, sizeof(str)-1 }, HL_COLOR_##col }
-
-static hl_keyword_t hl_default_kwrds[] = {
- /* C Types. */
- KW("double", TYPES),
- KW("int", TYPES),
- KW("long", TYPES),
- KW("char", TYPES),
- KW("float", TYPES),
- KW("short", TYPES),
- KW("unsigned", TYPES),
- KW("signed", TYPES),
- KW("bool", TYPES),
-
- /* Common typedefs. */
- KW("int8", TYPES), KW("uint8", TYPES),
- KW("int16", TYPES), KW("uint16", TYPES),
- KW("int32", TYPES), KW("uint32", TYPES),
- KW("int64", TYPES), KW("uint64", TYPES),
-
- /* Colla keywords */
- KW("uchar", TYPES),
- KW("ushort", TYPES),
- KW("uint", TYPES),
- KW("usize", TYPES),
- KW("isize", TYPES),
- KW("byte", TYPES),
-
- /* Other keywords. */
- KW("auto", KEYWORDS), KW("struct", KEYWORDS), KW("break", KEYWORDS),
- KW("else", KEYWORDS), KW("switch", KEYWORDS), KW("case", KEYWORDS),
- KW("enum", KEYWORDS), KW("register", KEYWORDS), KW("typedef", KEYWORDS),
- KW("extern", KEYWORDS), KW("return", KEYWORDS), KW("union", KEYWORDS),
- KW("const", KEYWORDS), KW("continue", KEYWORDS), KW("for", KEYWORDS),
- KW("void", KEYWORDS), KW("default", KEYWORDS), KW("goto", KEYWORDS),
- KW("sizeof", KEYWORDS), KW("volatile", KEYWORDS), KW("do", KEYWORDS),
- KW("if", KEYWORDS), KW("static", KEYWORDS), KW("inline", KEYWORDS),
- KW("while", KEYWORDS),
-};
-
-#undef KW
-
-static bool hl_default_symbols_table[256] = {
- ['['] = true, [']'] = true, ['('] = true,
- [')'] = true, ['{'] = true, ['}'] = true,
- ['*'] = true, [':'] = true, ['='] = true,
- [';'] = true, ['-'] = true, ['>'] = true,
- ['&'] = true, ['+'] = true, ['~'] = true,
- ['!'] = true, ['/'] = true, ['%'] = true,
- ['<'] = true, ['^'] = true, ['|'] = true,
- ['?'] = true, ['#'] = true,
-};
-
-static void hl_write_char(hl_ctx_t *ctx, char c);
-static void hl_write(hl_ctx_t *ctx, strview_t v);
-static bool hl_is_char_keyword(char c);
-static bool hl_highlight_symbol(hl_ctx_t *ctx, char c);
-static hl_color_e hl_get_keyword_color(hl_ctx_t *ctx, strview_t keyword);
-static bool hl_is_capitalised(strview_t string);
-static strview_t hl_finish_keyword(hl_ctx_t *ctx, usize beg, instream_t *in);
-static void hl_print_keyword(hl_ctx_t *ctx, strview_t keyword, hl_color_e color);
-
-hl_ctx_t *hlInit(arena_t *arena, hl_config_t *config) {
- if (!config) {
- err(" cannot be null");
- return NULL;
- }
-
- hl_ctx_t *out = alloc(arena, hl_ctx_t);
-
- out->flags = config->flags;
-
- memcpy(out->symbol_table, hl_default_symbols_table, sizeof(hl_default_symbols_table));
- memcpy(out->colors, config->colors, sizeof(config->colors));
-
- int kw_count = arrlen(hl_default_kwrds);
-
- out->kw_htable = hl_htable_init(arena, 8);
-
- for (int i = 0; i < kw_count; ++i) {
- hl_keyword_t *kw = &hl_default_kwrds[i];
- hl_htable_add(arena, &out->kw_htable, kw->keyword, kw->color);
- }
-
- for (int i = 0; i < config->kwrds_count; ++i) {
- hl_keyword_t *kw = &config->extra_kwrds[i];
- hl_htable_add(arena, &out->kw_htable, kw->keyword, kw->color);
- }
-
- return out;
-}
-
-void hl_next_char(hl_ctx_t *ctx, instream_t *in) {
- char cur = istrGet(in);
- bool is_last = istrIsFinished(*in);
-
- switch (ctx->state) {
- case HL_STATE_DEFAULT:
- {
- /*
- * If potential keyword.
- *
- * A valid C keyword may contain numbers, but *not*
- * as a suffix.
- */
- if (hl_is_char_keyword(cur) && !isdigit(cur)) {
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_KEYWORD;
- }
-
- // potential number
- else if (isdigit(cur)) {
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_NUMBER;
- }
-
- // potential char
- else if (cur == '\'') {
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_CHAR;
- }
-
- // potential string
- else if (cur == '"') {
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_STRING;
- }
-
- // line or multiline comment
- else if (cur == '/') {
- // single line comment
- if (istrPeek(in) == '/') {
- // rewind before comment begins
- istrRewindN(in, 1);
-
- // comment until the end of line
- hl_print_keyword(ctx, istrGetLine(in), HL_COLOR_COMMENT);
- }
-
- // multiline comment
- else if (istrPeek(in) == '*') {
- ctx->state = HL_STATE_COMMENT_MULTI;
- ctx->kw_beg = istrTell(*in);
- istrSkip(in, 1); // skip *
- }
-
- else {
- // maybe a symbol?
- hl_highlight_symbol(ctx, cur);
- }
- }
-
- // preprocessor
- else if (cur == '#') {
- // print the # as a symbol
- hl_highlight_symbol(ctx, cur);
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_PREPROCESSOR;
- }
-
- // other suppored symbols
- else if (hl_highlight_symbol(ctx, cur)) {
- // noop
- }
-
- else {
- hl_write_char(ctx, cur);
- }
-
- break;
- }
-
- case HL_STATE_KEYWORD:
- {
- // end of keyword, check if it really is a valid keyword
- if (!hl_is_char_keyword(cur)) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
- hl_color_e kw_color = hl_get_keyword_color(ctx, keyword);
-
- if (kw_color != HL_COLOR__COUNT) {
- hl_print_keyword(ctx, keyword, kw_color);
-
- // maybe we should highlight this remaining char.
- if (!hl_highlight_symbol(ctx, cur)) {
- hl_write_char(ctx, cur);
- }
- }
-
- /*
- * If not keyword, maybe its a function call.
- *
- * Important to note that this is hacky and will only work
- * if there is no space between keyword and '('.
- */
- else if (cur == '(') {
- hl_print_keyword(ctx, keyword, HL_COLOR_FUNC);
-
- // Opening parenthesis will always be highlighted
- hl_highlight_symbol(ctx, cur);
- }
- else {
- if (hl_is_capitalised(keyword)) {
- hl_print_keyword(ctx, keyword, HL_COLOR_MACRO);
- }
- else {
- hl_write(ctx, keyword);
- }
- if (!hl_highlight_symbol(ctx, cur)) {
- hl_write_char(ctx, cur);
- }
- }
- }
- break;
- }
-
- case HL_STATE_NUMBER:
- {
- char c = (char)tolower(cur);
-
- /*
- * Should we end the state?.
- *
- * Very important observation:
- * Although the number highlight works fine for most (if not all)
- * of the possible cases, it also assumes that the code is written
- * correctly and the source is able to compile, meaning that:
- *
- * Numbers like: 123, 0xABC123, 12.3e4f, 123ULL....
- * will be correctly identified and highlighted
- *
- * But, 'numbers' like: 123ABC, 0xxxxABCxx123, 123UUUUU....
- * will also be highlighted.
- *
- * It also assumes that no keyword will start with a number
- * and everything starting with a number (except inside strings or
- * comments) will be a number.
- */
- if (!isdigit(c) &&
- (c < 'a' || c > 'f') &&
- c != 'b' && c != 'x' &&
- c != 'u' && c != 'l' &&
- c != '.'
- ) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
-
- // if not a valid char keyword: valid number
- if (!hl_is_char_keyword(cur)) {
- hl_print_keyword(ctx, keyword, HL_COLOR_NUMBER);
- }
- else {
- hl_write(ctx, keyword);
- }
-
- // maybe we should highlight this remaining char.
- if (!hl_highlight_symbol(ctx, cur)) {
- hl_write_char(ctx, cur);
- }
- }
-
- break;
- }
-
- case HL_STATE_CHAR:
- {
- if (is_last || (cur == '\'' && istrPeek(in) != '\'')) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
- keyword.len++;
-
- hl_print_keyword(ctx, keyword, HL_COLOR_STRING);
- }
- break;
- }
-
- case HL_STATE_STRING:
- {
- if (is_last || (cur == '"' && istrPrevPrev(in) != '\\')) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
- keyword.len++;
-
- hl_print_keyword(ctx, keyword, HL_COLOR_STRING);
- }
- break;
- }
-
- case HL_STATE_COMMENT_MULTI:
- {
- /*
- * If we are at the end of line _or_ have identified
- * an end of comment...
- */
- if (is_last || (cur == '*' && istrPeek(in) == '/')) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
-
- hl_print_keyword(ctx, keyword, HL_COLOR_COMMENT);
- }
- break;
- }
-
- case HL_STATE_PREPROCESSOR:
- {
-
- if (!hl_is_char_keyword(cur)) {
- hl_write_char(ctx, cur);
- break;
- }
-
-#define hl_check(str, new_state) \
- if (cur == str[0]) { \
- instream_t temp = *in; \
- strview_t a = strvInitLen(&(str[1]), sizeof(str) - 2); \
- strview_t b = istrGetViewLen(&temp, a.len); \
- if (strvEquals(a, b)) { \
- *in = temp; \
- hl_print_keyword(ctx, strvInitLen(str, sizeof(str) - 1), HL_COLOR_PREPROC); \
- ctx->state = new_state; \
- break; \
- } \
- }
- if (is_last) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
- hl_print_keyword(ctx, keyword, HL_COLOR_PREPROC);
- break;
- }
-
- hl_check("include", HL_STATE_PREPROCESSOR_INCLUDE)
- hl_check("define", HL_STATE_DEFAULT)
- hl_check("undef", HL_STATE_DEFAULT)
- hl_check("ifdef", HL_STATE_DEFAULT)
- hl_check("ifndef", HL_STATE_DEFAULT)
- hl_check("if", HL_STATE_DEFAULT)
- hl_check("endif", HL_STATE_DEFAULT)
- hl_check("pragma", HL_STATE_DEFAULT)
-
-#undef hl_check
- break;
- }
-
-
- /*
- * Preprocessor/Preprocessor include
- *
- * This is a 'dumb' preprocessor highlighter:
- * it highlights everything with the same color
- * and if and only if an '#include' is detected
- * the included header will be handled as string
- * and thus, will have the same color as the string.
- *
- * In fact, it is somehow similar to what GtkSourceView
- * does (Mousepad, Gedit...) but with one silly difference:
- * single-line/multi-line comments will not be handled
- * while inside the preprocessor state, meaning that
- * comments will also have the same color as the remaining
- * of the line, yeah, ugly.
- */
- case HL_STATE_PREPROCESSOR_INCLUDE:
- {
- if (cur == '<' || cur == '"' || is_last) {
- ctx->kw_beg = istrTell(*in);
- ctx->state = HL_STATE_PREPROCESSOR_INCLUDE_STRING;
- }
- else {
- hl_write_char(ctx, cur);
- }
- break;
- }
- case HL_STATE_PREPROCESSOR_INCLUDE_STRING:
- {
- if (cur == '>' || cur == '"' || is_last) {
- strview_t keyword = hl_finish_keyword(ctx, ctx->kw_beg, in);
- keyword.len += 1;
- hl_print_keyword(ctx, keyword, HL_COLOR_STRING);
- }
- break;
- }
- }
-}
-
-str_t hlHighlight(arena_t *arena, hl_ctx_t *ctx, strview_t data) {
- ctx->ostr = ostrInit(arena);
-
- ctx->state = HL_STATE_DEFAULT;
- ctx->kw_beg = 0;
-
- instream_t in = istrInitLen(data.buf, data.len);
-
- while (!istrIsFinished(in)) {
- hl_next_char(ctx, &in);
- }
-
- hl_next_char(ctx, &in);
-
- return ostrAsStr(&ctx->ostr);
-}
-
-void hlSetSymbolInTable(hl_ctx_t *ctx, char symbol, bool value) {
- if (!ctx) return;
- ctx->symbol_table[(unsigned char)symbol] = value;
-}
-
-void hlAddKeyword(arena_t *arena, hl_ctx_t *ctx, hl_keyword_t *keyword) {
- hl_htable_add(arena, &ctx->kw_htable, keyword->keyword, keyword->color);
-}
-
-//// HASH TABLE ///////////////////////////////////////////////////
-
-static hl_hashtable_t hl_htable_init(arena_t *arena, uint pow2_exp) {
- uint count = 1 << pow2_exp;
- return (hl_hashtable_t) {
- .count = count,
- .buckets = alloc(arena, hl_node_t*, count),
- };
-}
-
-static hl_htable_result_e hl_htable_add(arena_t *arena, hl_hashtable_t *table, strview_t key, hl_color_e value) {
- if (!table) {
- return HL_HTABLE_FAILED;
- }
-
- if ((float)table->used >= table->count * 0.6f) {
- warn("more than 60%% of the hashmap is being used: %d/%d", table->used, table->count);
- }
-
- uint64 hash = hl_htable_hash(key.buf, key.len);
- usize index = hash & (table->count - 1);
- hl_node_t *bucket = table->buckets[index];
- if (bucket) table->collisions++;
- while (bucket) {
- // already exists
- if (strvEquals(bucket->key, key)) {
- bucket->value = value;
- return HL_HTABLE_REPLACED;
- }
- bucket = bucket->next;
- }
-
- bucket = alloc(arena, hl_node_t);
-
- bucket->key = key;
- bucket->value = value;
- bucket->next = table->buckets[index];
-
- table->buckets[index] = bucket;
- table->used++;
-
- return HL_HTABLE_ADDED;
-}
-
-static hl_node_t *hl_htable_get(hl_hashtable_t *table, strview_t key) {
- if (!table || table->used == 0) {
- return NULL;
- }
-
- uint64 hash = hl_htable_hash(key.buf, key.len);
- usize index = hash & (table->count - 1);
- hl_node_t *bucket = table->buckets[index];
- while (bucket) {
- if (strvEquals(bucket->key, key)) {
- return bucket;
- }
- bucket = bucket->next;
- }
-
- return NULL;
-}
-
-// uses the sdbm algorithm
-static uint64 hl_htable_hash(const void *bytes, usize count) {
- const uint8 *data = bytes;
- uint64 hash = 0;
-
- for (usize i = 0; i < count; ++i) {
- hash = data[i] + (hash << 6) + (hash << 16) - hash;
- }
-
- return hash;
-}
-
-//// STATIC FUNCTIONS /////////////////////////////////////////////
-
-static inline void hl_escape_html(outstream_t *out, char c) {
- switch (c) {
- case '&':
- ostrPuts(out, strv("&"));
- break;
- case '<':
- ostrPuts(out, strv("<"));
- break;
- case '>':
- ostrPuts(out, strv(">"));
- break;
- default:
- ostrPutc(out, c);
- break;
- }
-}
-
-static void hl_write_char(hl_ctx_t *ctx, char c) {
- if (ctx->flags & HL_FLAG_HTML) {
- hl_escape_html(&ctx->ostr, c);
- }
- else {
- ostrPutc(&ctx->ostr, c);
- }
-}
-
-static void hl_write(hl_ctx_t *ctx, strview_t v) {
- if (ctx->flags & HL_FLAG_HTML) {
- for (usize i = 0; i < v.len; ++i) {
- hl_escape_html(&ctx->ostr, v.buf[i]);
- }
- }
- else {
- ostrPuts(&ctx->ostr, v);
- }
-}
-
-static bool hl_is_char_keyword(char c) {
- return isalpha(c) || isdigit(c) || c == '_';
-}
-
-static bool hl_highlight_symbol(hl_ctx_t *ctx, char c) {
- if (!ctx->symbol_table[(unsigned char)c]) {
- return false;
- }
-
- ostrPuts(&ctx->ostr, ctx->colors[HL_COLOR_SYMBOL]);
- hl_write_char(ctx, c);
- ostrPuts(&ctx->ostr, ctx->colors[HL_COLOR_NORMAL]);
-
- return true;
-}
-
-static hl_color_e hl_get_keyword_color(hl_ctx_t *ctx, strview_t keyword) {
- // todo: make this an option?
- if (strvEndsWithView(keyword, strv("_t"))) {
- return HL_COLOR_CUSTOM_TYPES;
- }
-
- hl_node_t *node = hl_htable_get(&ctx->kw_htable, keyword);
- return node ? node->value : HL_COLOR__COUNT;
-}
-
-static bool hl_is_capitalised(strview_t string) {
- for (usize i = 0; i < string.len; ++i) {
- char c = string.buf[i];
- if (!isdigit(c) && c != '_' && (c < 'A' || c > 'Z')) {
- return false;
- }
- }
- return true;
-}
-
-static strview_t hl_finish_keyword(hl_ctx_t *ctx, usize beg, instream_t *in) {
- ctx->state = HL_STATE_DEFAULT;
- beg -= 1;
- usize end = istrTell(*in) - 1;
-
- return strv(in->start + beg, end - beg);
-}
-
-static void hl_print_keyword(hl_ctx_t *ctx, strview_t keyword, hl_color_e color) {
- ostrPuts(&ctx->ostr, ctx->colors[color]);
- hl_write(ctx, keyword);
- ostrPuts(&ctx->ostr, ctx->colors[HL_COLOR_NORMAL]);
-}
\ No newline at end of file
diff --git a/highlight.h b/highlight.h
deleted file mode 100644
index 4ef28a8..0000000
--- a/highlight.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include "str.h"
-
-typedef enum {
- HL_COLOR_NORMAL,
- HL_COLOR_PREPROC,
- HL_COLOR_TYPES,
- HL_COLOR_CUSTOM_TYPES,
- HL_COLOR_KEYWORDS,
- HL_COLOR_NUMBER,
- HL_COLOR_STRING,
- HL_COLOR_COMMENT,
- HL_COLOR_FUNC,
- HL_COLOR_SYMBOL,
- HL_COLOR_MACRO,
-
- HL_COLOR__COUNT,
-} hl_color_e;
-
-typedef enum {
- HL_FLAG_NONE = 0,
- HL_FLAG_HTML = 1 << 0,
-} hl_flags_e;
-
-typedef struct {
- strview_t keyword;
- hl_color_e color;
-} hl_keyword_t;
-
-typedef struct {
- usize idx;
- usize size;
-} hl_line_t;
-
-typedef struct {
- strview_t colors[HL_COLOR__COUNT];
- hl_keyword_t *extra_kwrds;
- int kwrds_count;
- hl_flags_e flags;
-} hl_config_t;
-
-typedef struct hl_ctx_t hl_ctx_t;
-
-hl_ctx_t *hlInit(arena_t *arena, hl_config_t *config);
-str_t hlHighlight(arena_t *arena, hl_ctx_t *ctx, strview_t str);
-
-void hlSetSymbolInTable(hl_ctx_t *ctx, char symbol, bool value);
-void hlAddKeyword(arena_t *arena, hl_ctx_t *ctx, hl_keyword_t *keyword);
diff --git a/hot_reload.c b/hot_reload.c
deleted file mode 100644
index 217bd1f..0000000
--- a/hot_reload.c
+++ /dev/null
@@ -1,227 +0,0 @@
-#include "hot_reload.h"
-
-#include "arena.h"
-#include "file.h"
-#include "tracelog.h"
-
-// todo linux support?
-#if COLLA_WIN
-#include
-#else
-// patch stuff up for cross platform for now, the actual program should not really call anything for now
-#define HMODULE void*
-#endif
-
-typedef int (*hr_f)(hr_t *ctx);
-
-typedef struct {
- arena_t arena;
- str_t path;
- uint64 last_timestamp;
- HMODULE handle;
- hr_f hr_init;
- hr_f hr_loop;
- hr_f hr_close;
-} hr_internal_t;
-
-static bool hr__os_reload(hr_internal_t *hr, str_t libpath);
-static void hr__os_free(hr_internal_t *hr);
-
-static bool hr__file_copy(arena_t scratch, strview_t src, strview_t dst) {
- buffer_t srcbuf = fileReadWhole(&scratch, src);
- if (srcbuf.data == NULL || srcbuf.len == 0) {
- err("fileReadWhole(%v) returned an empty buffer", src);
- return false;
- }
- if (!fileWriteWhole(dst, srcbuf.data, srcbuf.len)) {
- err("fileWriteWhole failed");
- return false;
- }
- return true;
-}
-
-static bool hr__reload(hr_t *ctx) {
-#ifdef HR_DISABLE
- return true;
-#endif
-
- hr_internal_t *hr = ctx->p;
- arena_t scratch = hr->arena;
-
- if (!fileExists(strv(hr->path))) {
- err("dll file %v does not exist anymore!", hr->path);
- return false;
- }
-
- uint64 now = fileGetTime(strv(hr->path));
- if (now <= hr->last_timestamp) {
- return false;
- }
-
- ctx->version = ctx->last_working_version + 1;
-
- // can't copy the dll directly, make a temporary one based on the version
- strview_t dir, name, ext;
- fileSplitPath(strv(hr->path), &dir, &name, &ext);
- str_t libpath = strFmt(&scratch, "%v/%v-%d%v", dir, name, ctx->version, ext);
-
- if (!hr__file_copy(scratch, strv(hr->path), strv(libpath))) {
- err("failed to copy %v to %v", hr->path, libpath);
- return false;
- }
-
- info("loading library: %v", libpath);
-
- bool success = hr__os_reload(hr, libpath);
- if (success) {
- info("Reloaded, version: %d", ctx->version);
- ctx->last_working_version = ctx->version;
- hr->last_timestamp = now;
- hr->hr_init(ctx);
- }
-
- return success;
-}
-
-bool hrOpen(hr_t *ctx, strview_t path) {
-#ifdef HR_DISABLE
- cr_init(ctx);
- return true;
-#endif
-
- if (!fileExists(path)) {
- err("dll file: %v does not exist", path);
- return false;
- }
-
- arena_t arena = arenaMake(ARENA_VIRTUAL, MB(1));
-
- hr_internal_t *hr = alloc(&arena, hr_internal_t);
- hr->arena = arena;
- hr->path = str(&arena, path);
-
- ctx->p = hr;
- ctx->last_working_version = 0;
-
- return hr__reload(ctx);
-}
-
-void hrClose(hr_t *ctx, bool clean_temp_files) {
-#ifdef HR_DISABLE
- hr_close(ctx);
- return;
-#endif
-
- hr_internal_t *hr = ctx->p;
- if (hr->hr_close) {
- hr->hr_close(ctx);
- }
-
- hr__os_free(hr);
-
- hr->handle = NULL;
- hr->hr_init = hr->hr_loop = hr->hr_close = NULL;
-
- if (clean_temp_files) {
- arena_t scratch = hr->arena;
-
- strview_t dir, name, ext;
- fileSplitPath(strv(hr->path), &dir, &name, &ext);
-
- for (int i = 0; i < ctx->last_working_version; ++i) {
- str_t fname = strFmt(&scratch, "%v/%v-%d%v", dir, name, i + 1, ext);
- if (!fileDelete(strv(fname))) {
- err("couldn't delete %v", fname);
- }
- }
- }
-
- arena_t arena = hr->arena;
- arenaCleanup(&arena);
-
- ctx->p = NULL;
-}
-
-int hrStep(hr_t *ctx) {
-#ifdef CR_DISABLE
- hr_loop(ctx);
- return 0;
-#endif
- hr_internal_t *hr = ctx->p;
-
- int result = -1;
- if (hr->hr_loop) {
- result = hr->hr_loop(ctx);
- }
- return result;
-}
-
-bool hrReload(hr_t *ctx) {
- return hr__reload(ctx);
-}
-
-//// OS SPECIFIC ////////////////////////////////////////
-
-#if COLLA_WIN
-
-static bool hr__os_reload(hr_internal_t *hr, str_t libpath) {
- if (hr->handle) {
- FreeLibrary(hr->handle);
- }
-
- hr->handle = LoadLibraryA(libpath.buf);
- if (!hr->handle) {
- err("couldn't load %v: %u", libpath, GetLastError());
- return true;
- }
-
- hr->hr_init = (hr_f)GetProcAddress(hr->handle, "hr_init");
- DWORD init_err = GetLastError();
- hr->hr_loop = (hr_f)GetProcAddress(hr->handle, "hr_loop");
- DWORD loop_err = GetLastError();
- hr->hr_close = (hr_f)GetProcAddress(hr->handle, "hr_close");
- DWORD close_err = GetLastError();
-
- if (!hr->hr_init) {
- err("couldn't load address for hr_init: %u", init_err);
- goto error;
- }
-
- if (!hr->hr_loop) {
- err("couldn't load address for hr_loop: %u", loop_err);
- goto error;
- }
-
- if (!hr->hr_close) {
- err("couldn't load address for hr_close: %u", close_err);
- goto error;
- }
-
-
- return true;
-
-error:
- if (hr->handle) FreeLibrary(hr->handle);
- hr->handle = NULL;
- hr->hr_init = hr->hr_loop = hr->hr_close = NULL;
- return false;
-}
-
-static void hr__os_free(hr_internal_t *hr) {
- if (hr->handle) {
- FreeLibrary(hr->handle);
- }
-}
-
-#elif COLLA_LIN
-
-static bool hr__os_reload(hr_internal_t *hr, str_t libpath) {
- fatal("todo: linux hot reload not implemented yet");
- return true;
-}
-
-static void hr__os_free(hr_internal_t *hr) {
- fatal("todo: linux hot reload not implemented yet");
-}
-
-#endif
\ No newline at end of file
diff --git a/hot_reload.h b/hot_reload.h
deleted file mode 100644
index 3c03f88..0000000
--- a/hot_reload.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-// api functions:
-// int hr_init(hr_t *ctx)
-// int hr_loop(hr_t *ctx)
-// int hr_close(hr_t *ctx)
-
-// you can turn off hot reloading and run the program
-// "as normal" by defining
-// HR_DISABLE
-
-#include "str.h"
-
-#if COLLA_WIN
-#define HR_EXPORT __declspec(dllexport)
-#else
-// todo linux support?
-#define HR_EXPORT
-#endif
-
-typedef struct hr_t {
- void *p;
- void *userdata;
- int version;
- int last_working_version;
-} hr_t;
-
-bool hrOpen(hr_t *ctx, strview_t path);
-void hrClose(hr_t *ctx, bool clean_temp_files);
-int hrStep(hr_t *ctx);
-bool hrReload(hr_t *ctx);
diff --git a/html.h b/html.h
deleted file mode 100644
index bc3c0c6..0000000
--- a/html.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-
-#include "strstream.h"
-
-typedef struct {
- outstream_t stream;
- str_t *output;
-} html_context_t;
-
-strview_t html__strv_copy(strview_t src) { return src; }
-
-#define html__str_or_strv(str) _Generic(str, \
- strview_t: html__strv_copy, \
- str_t: strvInitStr, \
- const char *: strvInit, \
- char *: strvInit \
- )(str)
-
-#define htmlPrintf(...) ostrPrintf(&__ctx.stream, __VA_ARGS__)
-#define htmlPuts(str) ostrPuts(&__ctx.stream, html__str_or_strv(str))
-
-#define htmlBeg(arena_ptr, str_ptr) { \
- html_context_t __ctx = { .stream = ostrInit(arena_ptr), .output = str_ptr }; \
- htmlPrintf("\n");
-#define htmlEnd() htmlPrintf(""); *__ctx.output = ostrAsStr(&__ctx.stream); }
-
-#define html__args() \
- X(class) \
- X(id) \
- X(style) \
- X(onclick) \
- X(href) \
- X(src) \
-
-typedef struct {
-#define X(name) const char *name;
- html__args()
-#undef X
-} html_tag_t;
-
-static void html__tag(html_context_t *ctx, const char *tag, html_tag_t *args) {
- ostrPrintf(&ctx->stream, "<%s", tag);
-
-#define X(name, ...) if (args->name) { ostrPrintf(&ctx->stream, " " #name "=\"%s\"", args->name); }
- html__args()
-#undef X
-
- ostrPutc(&ctx->stream, '>');
-}
-
-#define tagBeg(tag, ...) do { html_tag_t args = {0, __VA_ARGS__}; html__tag(&__ctx, tag, &args); } while (0)
-#define tagEnd(tag) htmlPrintf(""tag">")
-
-#define html__strv_or_str(s) _Generic(s, str_t: NULL)
-
-#define html__simple_tag(tag, text, ...) do { tagBeg(tag, __VA_ARGS__); htmlPuts(text); tagEnd(tag); } while (0)
-
-#define headBeg(...) tagBeg("head", __VA_ARGS__)
-#define headEnd() tagEnd("head")
-
-#define bodyBeg(...) tagBeg("body", __VA_ARGS__)
-#define bodyEnd() tagEnd("body")
-
-#define divBeg(...) tagBeg("div", __VA_ARGS__)
-#define divEnd() tagEnd("div")
-
-#define htmlRaw(data) ostrPuts(&__ctx.stream, strv(#data))
-
-#define title(text, ...) html__simple_tag("title", text, __VA_ARGS__)
-#define h1(text, ...) html__simple_tag("h1", text, __VA_ARGS__)
-#define p(text, ...) html__simple_tag("p", text, __VA_ARGS__)
-#define span(text, ...) html__simple_tag("span", text, __VA_ARGS__)
-#define a(text, ...) html__simple_tag("a", text, __VA_ARGS__)
-#define img(...) tagBeg("img", __VA_ARGS__)
-#define style(text, ...) html__simple_tag("style", text, __VA_ARGS__)
-
-#define hr() htmlPuts("
")
diff --git a/http.c b/http.c
deleted file mode 100644
index 4415e00..0000000
--- a/http.c
+++ /dev/null
@@ -1,556 +0,0 @@
-#include "http.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include
-#include
-
-#include "arena.h"
-#include "str.h"
-#include "strstream.h"
-#include "format.h"
-#include "socket.h"
-#include "tracelog.h"
-
-#if COLLA_WIN
- #if COLLA_CMT_LIB
- #pragma comment(lib, "Wininet")
- #endif
-
- #include
- #if !COLLA_TCC
- #include
- #endif
-#endif
-
-static const TCHAR *https__get_method_str(http_method_e method);
-
-static http_header_t *http__parse_headers(arena_t *arena, instream_t *in) {
- http_header_t *head = NULL;
- strview_t line = STRV_EMPTY;
-
- do {
- line = istrGetView(in, '\r');
-
- usize pos = strvFind(line, ':', 0);
- if (pos != STR_NONE) {
- http_header_t *h = alloc(arena, http_header_t);
-
- h->key = strvSub(line, 0, pos);
- h->value = strvSub(line, pos + 2, SIZE_MAX);
-
- h->next = head;
- head = h;
- }
-
- istrSkip(in, 2); // skip \r\n
- } while (line.len > 2); // while line != "\r\n"
-
- return head;
-}
-
-const char *httpGetStatusString(int status) {
- switch (status) {
- case 200: return "OK";
- case 201: return "CREATED";
- case 202: return "ACCEPTED";
- case 204: return "NO CONTENT";
- case 205: return "RESET CONTENT";
- case 206: return "PARTIAL CONTENT";
-
- case 300: return "MULTIPLE CHOICES";
- case 301: return "MOVED PERMANENTLY";
- case 302: return "MOVED TEMPORARILY";
- case 304: return "NOT MODIFIED";
-
- case 400: return "BAD REQUEST";
- case 401: return "UNAUTHORIZED";
- case 403: return "FORBIDDEN";
- case 404: return "NOT FOUND";
- case 407: return "RANGE NOT SATISFIABLE";
-
- case 500: return "INTERNAL SERVER_ERROR";
- case 501: return "NOT IMPLEMENTED";
- case 502: return "BAD GATEWAY";
- case 503: return "SERVICE NOT AVAILABLE";
- case 504: return "GATEWAY TIMEOUT";
- case 505: return "VERSION NOT SUPPORTED";
- }
-
- return "UNKNOWN";
-}
-
-int httpVerNumber(http_version_t ver) {
- return (ver.major * 10) + ver.minor;
-}
-
-http_req_t httpParseReq(arena_t *arena, strview_t request) {
- http_req_t req = {0};
- instream_t in = istrInitLen(request.buf, request.len);
-
- strview_t method = strvTrim(istrGetView(&in, '/'));
- istrSkip(&in, 1); // skip /
- req.url = strvTrim(istrGetView(&in, ' '));
- strview_t http = strvTrim(istrGetView(&in, '\n'));
-
- istrSkip(&in, 1); // skip \n
-
- req.headers = http__parse_headers(arena, &in);
-
- req.body = strvTrim(istrGetViewLen(&in, SIZE_MAX));
-
- strview_t methods[5] = { strv("GET"), strv("POST"), strv("HEAD"), strv("PUT"), strv("DELETE") };
- usize methods_count = arrlen(methods);
-
- for (usize i = 0; i < methods_count; ++i) {
- if (strvEquals(method, methods[i])) {
- req.method = (http_method_e)i;
- break;
- }
- }
-
- in = istrInitLen(http.buf, http.len);
- istrIgnoreAndSkip(&in, '/'); // skip HTTP/
- istrGetU8(&in, &req.version.major);
- istrSkip(&in, 1); // skip .
- istrGetU8(&in, &req.version.minor);
-
- return req;
-}
-
-http_res_t httpParseRes(arena_t *arena, strview_t response) {
- http_res_t res = {0};
- instream_t in = istrInitLen(response.buf, response.len);
-
- strview_t http = istrGetViewLen(&in, 5);
- if (!strvEquals(http, strv("HTTP"))) {
- err("response doesn't start with 'HTTP', instead with %v", http);
- return (http_res_t){0};
- }
- istrSkip(&in, 1); // skip /
- istrGetU8(&in, &res.version.major);
- istrSkip(&in, 1); // skip .
- istrGetU8(&in, &res.version.minor);
- istrGetI32(&in, (int32*)&res.status_code);
-
- istrIgnore(&in, '\n');
- istrSkip(&in, 1); // skip \n
-
- res.headers = http__parse_headers(arena, &in);
-
- strview_t encoding = httpGetHeader(res.headers, strv("transfer-encoding"));
- if (!strvEquals(encoding, strv("chunked"))) {
- res.body = istrGetViewLen(&in, SIZE_MAX);
- }
- else {
- err("chunked encoding not implemented yet! body ignored");
- }
-
- return res;
-}
-
-str_t httpReqToStr(arena_t *arena, http_req_t *req) {
- outstream_t out = ostrInit(arena);
-
- const char *method = NULL;
- switch (req->method) {
- case HTTP_GET: method = "GET"; break;
- case HTTP_POST: method = "POST"; break;
- case HTTP_HEAD: method = "HEAD"; break;
- case HTTP_PUT: method = "PUT"; break;
- case HTTP_DELETE: method = "DELETE"; break;
- default: err("unrecognised method: %d", method); return STR_EMPTY;
- }
-
- ostrPrintf(
- &out,
- "%s /%v HTTP/%hhu.%hhu\r\n",
- method, req->url, req->version.major, req->version.minor
- );
-
- http_header_t *h = req->headers;
- while (h) {
- ostrPrintf(&out, "%v: %v\r\n", h->key, h->value);
- h = h->next;
- }
-
- ostrPuts(&out, strv("\r\n"));
- ostrPuts(&out, req->body);
-
- return ostrAsStr(&out);
-}
-
-str_t httpResToStr(arena_t *arena, http_res_t *res) {
- outstream_t out = ostrInit(arena);
-
- ostrPrintf(
- &out,
- "HTTP/%hhu.%hhu %d %s\r\n",
- res->version.major,
- res->version.minor,
- res->status_code,
- httpGetStatusString(res->status_code)
- );
- ostrPuts(&out, strv("\r\n"));
- ostrPuts(&out, res->body);
-
- return ostrAsStr(&out);
-}
-
-bool httpHasHeader(http_header_t *headers, strview_t key) {
- http_header_t *h = headers;
- while (h) {
- if (strvEquals(h->key, key)) {
- return true;
- }
- h = h->next;
- }
- return false;
-}
-
-void httpSetHeader(http_header_t *headers, strview_t key, strview_t value) {
- http_header_t *h = headers;
- while (h) {
- if (strvEquals(h->key, key)) {
- h->value = value;
- break;
- }
- h = h->next;
- }
-}
-
-strview_t httpGetHeader(http_header_t *headers, strview_t key) {
- http_header_t *h = headers;
- while (h) {
- if (strvEquals(h->key, key)) {
- return h->value;
- }
- h = h->next;
- }
- return STRV_EMPTY;
-}
-
-str_t httpMakeUrlSafe(arena_t *arena, strview_t string) {
- strview_t chars = strv(" !\"#$%%&'()*+,/:;=?@[]");
- usize final_len = string.len;
-
- // find final string length first
- for (usize i = 0; i < string.len; ++i) {
- if (strvContains(chars, string.buf[i])) {
- final_len += 2;
- }
- }
-
- str_t out = {
- .buf = alloc(arena, char, final_len + 1),
- .len = final_len
- };
- usize cur = 0;
- // substitute characters
- for (usize i = 0; i < string.len; ++i) {
- if (strvContains(chars, string.buf[i])) {
- fmtBuffer(out.buf + cur, 4, "%%%X", string.buf[i]);
- cur += 3;
- }
- else {
- out.buf[cur++] = string.buf[i];
- }
- }
-
- return out;
-}
-
-str_t httpDecodeUrlSafe(arena_t *arena, strview_t string) {
- usize final_len = string.len;
-
- for (usize i = 0; i < string.len; ++i) {
- if (string.buf[i] == '%') {
- final_len -= 2;
- i += 2;
- }
- }
-
- assert(final_len <= string.len);
-
- str_t out = {
- .buf = alloc(arena, char, final_len + 1),
- .len = final_len
- };
-
- usize k = 0;
-
- for (usize i = 0; i < string.len; ++i) {
- if (string.buf[i] == '%') {
- // skip %
- ++i;
-
- unsigned int ch = 0;
- int result = sscanf(string.buf + i, "%02X", &ch);
- if (result != 1 || ch > UINT8_MAX) {
- err("malformed url at %zu (%s)", i, string.buf + i);
- return STR_EMPTY;
- }
- out.buf[k++] = (char)ch;
-
- // skip first char of hex
- ++i;
- }
- else {
- out.buf[k++] = string.buf[i];
- }
- }
-
- return out;
-}
-
-http_url_t httpSplitUrl(strview_t url) {
- http_url_t out = {0};
-
- if (strvStartsWithView(url, strv("https://"))) {
- url = strvRemovePrefix(url, 8);
- }
- else if (strvStartsWithView(url, strv("http://"))) {
- url = strvRemovePrefix(url, 7);
- }
-
- out.host = strvSub(url, 0, strvFind(url, '/', 0));
- out.uri = strvSub(url, out.host.len, SIZE_MAX);
-
- return out;
-}
-
-http_res_t httpRequest(http_request_desc_t *request) {
- usize arena_begin = arenaTell(request->arena);
-
- http_req_t req = {
- .version = (http_version_t){ 1, 1 },
- .url = request->url,
- .body = request->body,
- .method = request->request_type,
- };
-
- http_header_t *h = NULL;
-
- for (int i = 0; i < request->header_count; ++i) {
- http_header_t *header = request->headers + i;
- header->next = h;
- h = header;
- }
-
- req.headers = h;
-
- http_url_t url = httpSplitUrl(req.url);
-
- if (strvEndsWith(url.host, '/')) {
- url.host = strvRemoveSuffix(url.host, 1);
- }
-
- if (!httpHasHeader(req.headers, strv("Host"))) {
- httpSetHeader(req.headers, strv("Host"), url.host);
- }
- if (!httpHasHeader(req.headers, strv("Content-Length"))) {
- char tmp[16] = {0};
- fmtBuffer(tmp, arrlen(tmp), "%zu", req.body.len);
- httpSetHeader(req.headers, strv("Content-Length"), strv(tmp));
- }
- if (req.method == HTTP_POST && !httpHasHeader(req.headers, strv("Content-Type"))) {
- httpSetHeader(req.headers, strv("Content-Type"), strv("application/x-www-form-urlencoded"));
- }
- if (!httpHasHeader(req.headers, strv("Connection"))) {
- httpSetHeader(req.headers, strv("Connection"), strv("close"));
- }
-
- if (!skInit()) {
- err("couldn't initialise sockets: %s", skGetErrorString());
- goto error;
- }
-
- socket_t sock = skOpen(SOCK_TCP);
- if (!skIsValid(sock)) {
- err("couldn't open socket: %s", skGetErrorString());
- goto error;
- }
-
- char hostname[64] = {0};
- assert(url.host.len < arrlen(hostname));
- memcpy(hostname, url.host.buf, url.host.len);
-
- const uint16 DEFAULT_HTTP_PORT = 80;
- if (!skConnect(sock, hostname, DEFAULT_HTTP_PORT)) {
- err("Couldn't connect to host %s: %s", hostname, skGetErrorString());
- goto error;
- }
-
- str_t reqstr = httpReqToStr(request->arena, &req);
- if (strIsEmpty(reqstr)) {
- err("couldn't get string from request");
- goto error;
- }
-
- if (skSend(sock, reqstr.buf, (int)reqstr.len) == SOCKET_ERROR) {
- err("couldn't send request to socket: %s", skGetErrorString());
- goto error;
- }
-
- outstream_t response = ostrInit(request->arena);
- char buffer[4096];
- int read = 0;
- do {
- read = skReceive(sock, buffer, arrlen(buffer));
- if (read == SOCKET_ERROR) {
- err("couldn't get the data from the server: %s", skGetErrorString());
- goto error;
- }
- ostrPuts(&response, strv(buffer, read));
- } while (read != 0);
-
- if (!skClose(sock)) {
- err("couldn't close socket: %s", skGetErrorString());
- }
-
- if (!skCleanup()) {
- err("couldn't clean up sockets: %s", skGetErrorString());
- }
-
- return httpParseRes(request->arena, ostrAsView(&response));
-
-error:
- arenaRewind(request->arena, arena_begin);
- skCleanup();
- return (http_res_t){0};
-}
-
-#if COLLA_WIN
-
-buffer_t httpsRequest(http_request_desc_t *req) {
- HINTERNET internet = InternetOpen(
- TEXT("COLLA"),
- INTERNET_OPEN_TYPE_PRECONFIG,
- NULL,
- NULL,
- 0
- );
- if (!internet) {
- fatal("call to InternetOpen failed: %u", GetLastError());
- }
-
- http_url_t split = httpSplitUrl(req->url);
- strview_t server = split.host;
- strview_t page = split.uri;
-
- if (strvStartsWithView(server, strv("http://"))) {
- server = strvRemovePrefix(server, 7);
- }
-
- if (strvStartsWithView(server, strv("https://"))) {
- server = strvRemovePrefix(server, 8);
- }
-
- arena_t scratch = *req->arena;
- const TCHAR *tserver = strvToTChar(&scratch, server);
- const TCHAR *tpage = strvToTChar(&scratch, page);
-
- HINTERNET connection = InternetConnect(
- internet,
- tserver,
- INTERNET_DEFAULT_HTTPS_PORT,
- NULL,
- NULL,
- INTERNET_SERVICE_HTTP,
- 0,
- (DWORD_PTR)NULL // userdata
- );
- if (!connection) {
- fatal("call to InternetConnect failed: %u", GetLastError());
- }
-
- const TCHAR *accepted_types[] = { TEXT("*/*"), NULL };
-
- HINTERNET request = HttpOpenRequest(
- connection,
- https__get_method_str(req->request_type),
- tpage,
- TEXT("HTTP/1.1"),
- NULL,
- accepted_types,
- INTERNET_FLAG_SECURE,
- (DWORD_PTR)NULL // userdata
- );
- if (!request) {
- fatal("call to HttpOpenRequest failed: %u", GetLastError());
- }
-
- outstream_t header = ostrInit(&scratch);
-
- for (int i = 0; i < req->header_count; ++i) {
- http_header_t *h = &req->headers[i];
- ostrClear(&header);
- ostrPrintf(
- &header,
- "%.*s: %.*s\r\n",
- h->key.len, h->key.buf,
- h->value.len, h->value.buf
- );
- str_t header_str = ostrAsStr(&header);
- HttpAddRequestHeadersA(
- request,
- header_str.buf,
- (DWORD)header_str.len,
- 0
- );
- }
-
- BOOL request_sent = HttpSendRequest(
- request,
- NULL,
- 0,
- (void *)req->body.buf,
- (DWORD)req->body.len
- );
- if (!request_sent) {
- fatal("call to HttpSendRequest failed: %u", GetLastError());
- }
-
- outstream_t out = ostrInit(req->arena);
-
- while (true) {
- DWORD bytes_read = 0;
- char buffer[4096];
- BOOL read = InternetReadFile(
- request,
- buffer,
- sizeof(buffer),
- &bytes_read
- );
- if (!read || bytes_read == 0) {
- break;
- }
- ostrPuts(&out, strv(buffer, bytes_read));
- }
-
- InternetCloseHandle(request);
- InternetCloseHandle(connection);
- InternetCloseHandle(internet);
-
- str_t outstr = ostrAsStr(&out);
-
- return (buffer_t) {
- .data = (uint8 *)outstr.buf,
- .len = outstr.len
- };
-}
-
-static const TCHAR *https__get_method_str(http_method_e method) {
- switch (method) {
- case HTTP_GET: return TEXT("GET");
- case HTTP_POST: return TEXT("POST");
- case HTTP_HEAD: return TEXT("HEAD");
- case HTTP_PUT: return TEXT("PUT");
- case HTTP_DELETE: return TEXT("DELETE");
- }
- // default GET
- return NULL;
-}
-#endif
-
-#include "warnings/colla_warn_end.h"
diff --git a/http.h b/http.h
deleted file mode 100644
index cfb65c2..0000000
--- a/http.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-#include "str.h"
-
-typedef struct arena_t arena_t;
-typedef uintptr_t socket_t;
-
-typedef enum {
- HTTP_GET,
- HTTP_POST,
- HTTP_HEAD,
- HTTP_PUT,
- HTTP_DELETE
-} http_method_e;
-
-const char *httpGetStatusString(int status);
-
-typedef struct {
- uint8 major;
- uint8 minor;
-} http_version_t;
-
-// translates a http_version_t to a single readable number (e.g. 1.1 -> 11, 1.0 -> 10, etc)
-int httpVerNumber(http_version_t ver);
-
-typedef struct http_header_t {
- strview_t key;
- strview_t value;
- struct http_header_t *next;
-} http_header_t;
-
-typedef struct {
- http_method_e method;
- http_version_t version;
- http_header_t *headers;
- strview_t url;
- strview_t body;
-} http_req_t;
-
-typedef struct {
- int status_code;
- http_version_t version;
- http_header_t *headers;
- strview_t body;
-} http_res_t;
-
-// strview_t request needs to be valid for http_req_t to be valid!
-http_req_t httpParseReq(arena_t *arena, strview_t request);
-http_res_t httpParseRes(arena_t *arena, strview_t response);
-
-str_t httpReqToStr(arena_t *arena, http_req_t *req);
-str_t httpResToStr(arena_t *arena, http_res_t *res);
-
-bool httpHasHeader(http_header_t *headers, strview_t key);
-void httpSetHeader(http_header_t *headers, strview_t key, strview_t value);
-strview_t httpGetHeader(http_header_t *headers, strview_t key);
-
-str_t httpMakeUrlSafe(arena_t *arena, strview_t string);
-str_t httpDecodeUrlSafe(arena_t *arena, strview_t string);
-
-typedef struct {
- strview_t host;
- strview_t uri;
-} http_url_t;
-
-http_url_t httpSplitUrl(strview_t url);
-
-typedef struct {
- arena_t *arena;
- strview_t url;
- http_method_e request_type;
- http_header_t *headers;
- int header_count;
- strview_t body;
-} http_request_desc_t;
-
-// arena_t *arena, strview_t url, [ http_header_t *headers, int header_count, strview_t body ]
-#define httpGet(arena, url, ...) httpRequest(&(http_request_desc_t){ arena, url, .request_type = HTTP_GET, __VA_ARGS__ })
-#define httpsGet(arena, url, ...) httpsRequest(&(http_request_desc_t){ arena, url, .request_type = HTTP_GET, __VA_ARGS__ })
-
-http_res_t httpRequest(http_request_desc_t *request);
-buffer_t httpsRequest(http_request_desc_t *request);
diff --git a/ini.c b/ini.c
deleted file mode 100644
index da515cc..0000000
--- a/ini.c
+++ /dev/null
@@ -1,279 +0,0 @@
-#include "ini.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include
-
-#include "strstream.h"
-
-static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options);
-
-ini_t iniParse(arena_t *arena, strview_t filename, const iniopts_t *options) {
- file_t fp = fileOpen(filename, FILE_READ);
- ini_t out = iniParseFile(arena, fp, options);
- fileClose(fp);
- return out;
-}
-
-ini_t iniParseFile(arena_t *arena, file_t file, const iniopts_t *options) {
- str_t data = fileReadWholeStrFP(arena, file);
- return iniParseStr(arena, strv(data), options);
-}
-
-ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options) {
- ini_t out = {
- .text = str,
- .tables = NULL,
- };
- ini__parse(arena, &out, options);
- return out;
-}
-
-bool iniIsValid(ini_t *ctx) {
- return ctx && !strvIsEmpty(ctx->text);
-}
-
-initable_t *iniGetTable(ini_t *ctx, strview_t name) {
- initable_t *t = ctx ? ctx->tables : NULL;
- while (t) {
- if (strvEquals(t->name, name)) {
- return t;
- }
- t = t->next;
- }
- return NULL;
-}
-
-inivalue_t *iniGet(initable_t *ctx, strview_t key) {
- inivalue_t *v = ctx ? ctx->values : NULL;
- while (v) {
- if (strvEquals(v->key, key)) {
- return v;
- }
- v = v->next;
- }
- return NULL;
-}
-
-iniarray_t iniAsArr(arena_t *arena, inivalue_t *value, char delim) {
- strview_t v = value ? value->value : STRV_EMPTY;
- if (!delim) delim = ' ';
-
- strview_t *beg = (strview_t *)arena->current;
- usize count = 0;
-
- usize start = 0;
- for (usize i = 0; i < v.len; ++i) {
- if (v.buf[i] == delim) {
- strview_t arrval = strvTrim(strvSub(v, start, i));
- if (!strvIsEmpty(arrval)) {
- strview_t *newval = alloc(arena, strview_t);
- *newval = arrval;
- ++count;
- }
- start = i + 1;
- }
- }
-
- strview_t last = strvTrim(strvSub(v, start, SIZE_MAX));
- if (!strvIsEmpty(last)) {
- strview_t *newval = alloc(arena, strview_t);
- *newval = last;
- ++count;
- }
-
- return (iniarray_t){
- .values = beg,
- .count = count,
- };
-}
-
-uint64 iniAsUInt(inivalue_t *value) {
- strview_t v = value ? value->value : STRV_EMPTY;
- instream_t in = istrInitLen(v.buf, v.len);
- uint64 out = 0;
- if (!istrGetU64(&in, &out)) {
- out = 0;
- }
- return out;
-}
-
-int64 iniAsInt(inivalue_t *value) {
- strview_t v = value ? value->value : STRV_EMPTY;
- instream_t in = istrInitLen(v.buf, v.len);
- int64 out = 0;
- if (!istrGetI64(&in, &out)) {
- out = 0;
- }
- return out;
-}
-
-double iniAsNum(inivalue_t *value) {
- strview_t v = value ? value->value : STRV_EMPTY;
- instream_t in = istrInitLen(v.buf, v.len);
- double out = 0;
- if (!istrGetDouble(&in, &out)) {
- out = 0.0;
- }
- return out;
-}
-
-bool iniAsBool(inivalue_t *value) {
- strview_t v = value ? value->value : STRV_EMPTY;
- instream_t in = istrInitLen(v.buf, v.len);
- bool out = 0;
- if (!istrGetBool(&in, &out)) {
- out = false;
- }
- return out;
-}
-
-// == PRIVATE FUNCTIONS ==============================================================================
-
-#define INIPUSH(head, tail, val) \
- do { \
- if (!head) { \
- head = val; \
- tail = val; \
- } \
- else { \
- tail->next = val; \
- val = tail; \
- } \
- } while (0)
-
-static iniopts_t ini__get_options(const iniopts_t *options) {
- iniopts_t out = {
- .key_value_divider = '=',
- };
-
-#define SETOPT(v) out.v = options->v ? options->v : out.v
-
- if (options) {
- SETOPT(key_value_divider);
- SETOPT(merge_duplicate_keys);
- SETOPT(merge_duplicate_tables);
- }
-
-#undef SETOPT
-
- return out;
-}
-
-static void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopts_t *opts) {
- assert(table);
-
- strview_t key = strvTrim(istrGetView(in, opts->key_value_divider));
- istrSkip(in, 1);
- strview_t value = strvTrim(istrGetViewEither(in, strv("\n#;")));
- istrSkip(in, 1);
- inivalue_t *newval = NULL;
-
-
- if (opts->merge_duplicate_keys) {
- newval = table->values;
- while (newval) {
- if (strvEquals(newval->key, key)) {
- break;
- }
- newval = newval->next;
- }
- }
-
- if (newval) {
- newval->value = value;
- }
- else {
- newval = alloc(arena, inivalue_t);
- newval->key = key;
- newval->value = value;
-
- if (!table->values) {
- table->values = newval;
- }
- else {
- table->tail->next = newval;
- }
-
- table->tail = newval;
- }
-}
-
-static void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopts_t *options) {
- istrSkip(in, 1); // skip [
- strview_t name = istrGetView(in, ']');
- istrSkip(in, 1); // skip ]
- initable_t *table = NULL;
-
- if (options->merge_duplicate_tables) {
- table = ctx->tables;
- while (table) {
- if (strvEquals(table->name, name)) {
- break;
- }
- table = table->next;
- }
- }
-
- if (!table) {
- table = alloc(arena, initable_t);
-
- table->name = name;
-
- if (!ctx->tables) {
- ctx->tables = table;
- }
- else {
- ctx->tail->next = table;
- }
-
- ctx->tail = table;
- }
-
- istrIgnoreAndSkip(in, '\n');
- while (!istrIsFinished(*in)) {
- switch (istrPeek(in)) {
- case '\n': // fallthrough
- case '\r':
- return;
- case '#': // fallthrough
- case ';':
- istrIgnoreAndSkip(in, '\n');
- break;
- default:
- ini__add_value(arena, table, in, options);
- break;
- }
- }
-}
-
-static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options) {
- iniopts_t opts = ini__get_options(options);
-
- initable_t *root = alloc(arena, initable_t);
- root->name = INI_ROOT;
- ini->tables = root;
- ini->tail = root;
-
- instream_t in = istrInitLen(ini->text.buf, ini->text.len);
-
- while (!istrIsFinished(in)) {
- istrSkipWhitespace(&in);
- switch (istrPeek(&in)) {
- case '[':
- ini__add_table(arena, ini, &in, &opts);
- break;
- case '#': // fallthrough
- case ';':
- istrIgnoreAndSkip(&in, '\n');
- break;
- default:
- ini__add_value(arena, ini->tables, &in, &opts);
- break;
- }
- }
-}
-
-#undef INIPUSH
-
-#include "warnings/colla_warn_end.h"
diff --git a/ini.h b/ini.h
deleted file mode 100644
index 359a7b6..0000000
--- a/ini.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-#include "str.h"
-#include "file.h"
-
-typedef struct arena_t arena_t;
-
-typedef struct inivalue_t {
- strview_t key;
- strview_t value;
- struct inivalue_t *next;
-} inivalue_t;
-
-typedef struct initable_t {
- strview_t name;
- inivalue_t *values;
- inivalue_t *tail;
- struct initable_t *next;
-} initable_t;
-
-typedef struct ini_t {
- strview_t text;
- initable_t *tables;
- initable_t *tail;
-} ini_t;
-
-typedef struct {
- bool merge_duplicate_tables; // default false
- bool merge_duplicate_keys; // default false
- char key_value_divider; // default =
-} iniopts_t;
-
-ini_t iniParse(arena_t *arena, strview_t filename, const iniopts_t *options);
-ini_t iniParseFile(arena_t *arena, file_t file, const iniopts_t *options);
-ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options);
-
-bool iniIsValid(ini_t *ctx);
-
-#define INI_ROOT strv("__ROOT__")
-
-initable_t *iniGetTable(ini_t *ctx, strview_t name);
-inivalue_t *iniGet(initable_t *ctx, strview_t key);
-
-typedef struct {
- strview_t *values;
- usize count;
-} iniarray_t;
-
-iniarray_t iniAsArr(arena_t *arena, inivalue_t *value, char delim);
-uint64 iniAsUInt(inivalue_t *value);
-int64 iniAsInt(inivalue_t *value);
-double iniAsNum(inivalue_t *value);
-bool iniAsBool(inivalue_t *value);
\ No newline at end of file
diff --git a/json.c b/json.c
deleted file mode 100644
index 84ec155..0000000
--- a/json.c
+++ /dev/null
@@ -1,386 +0,0 @@
-#include "json.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include
-
-#include "strstream.h"
-#include "file.h"
-#include "tracelog.h"
-
-// #define json__logv() warn("%s:%d", __FILE__, __LINE__)
-#define json__logv()
-#define json__ensure(c) json__check_char(in, c)
-
-static bool json__check_char(instream_t *in, char c) {
- if (istrGet(in) == c) {
- return true;
- }
- istrRewindN(in, 1);
- err("wrong character at %zu, should be '%c' but is 0x%02x '%c'", istrTell(*in), c, istrPeek(in), istrPeek(in));
- json__logv();
- return false;
-}
-
-static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out);
-static bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out);
-
-static bool json__is_value_finished(instream_t *in) {
- usize old_pos = istrTell(*in);
-
- istrSkipWhitespace(in);
- switch(istrPeek(in)) {
- case '}': // fallthrough
- case ']': // fallthrough
- case ',':
- return true;
- }
-
- in->cur = in->start + old_pos;
- return false;
-}
-
-static bool json__parse_null(instream_t *in) {
- strview_t null_view = istrGetViewLen(in, 4);
- bool is_valid = true;
-
- if (!strvEquals(null_view, strv("null"))) {
- err("should be null but is: (%.*s) at %zu", null_view.len, null_view.buf, istrTell(*in));
- is_valid = false;
- }
-
- if (!json__is_value_finished(in)) {
- err("null, should be finished, but isn't at %zu", istrTell(*in));
- is_valid = false;
- }
-
- if (!is_valid) json__logv();
-
- return is_valid;
-}
-
-static bool json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) {
- jsonval_t *head = NULL;
-
- if (!json__ensure('[')) {
- json__logv();
- goto fail;
- }
-
- istrSkipWhitespace(in);
-
- // if it is an empty array
- if (istrPeek(in) == ']') {
- istrSkip(in, 1);
- goto success;
- }
-
- if (!json__parse_value(arena, in, flags, &head)) {
- json__logv();
- goto fail;
- }
-
- jsonval_t *cur = head;
-
- while (true) {
- istrSkipWhitespace(in);
- switch (istrGet(in)) {
- case ']':
- return head;
- case ',':
- {
- istrSkipWhitespace(in);
- // trailing comma
- if (istrPeek(in) == ']') {
- if (flags & JSON_NO_TRAILING_COMMAS) {
- err("trailing comma in array at at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur);
- json__logv();
- goto fail;
- }
- else {
- continue;
- }
- }
-
- jsonval_t *next = NULL;
- if (!json__parse_value(arena, in, flags, &next)) {
- json__logv();
- goto fail;
- }
- cur->next = next;
- next->prev = cur;
- cur = next;
- break;
- }
- default:
- istrRewindN(in, 1);
- err("unknown char after array at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur);
- json__logv();
- goto fail;
- }
- }
-
-success:
- *out = head;
- return true;
-fail:
- *out = NULL;
- return false;
-}
-
-static bool json__parse_string(arena_t *arena, instream_t *in, str_t *out) {
- istrSkipWhitespace(in);
-
- if (!json__ensure('"')) {
- json__logv();
- goto fail;
- }
-
- const char *from = in->cur;
-
- for (; !istrIsFinished(*in) && *in->cur != '"'; ++in->cur) {
- if (istrPeek(in) == '\\') {
- ++in->cur;
- }
- }
-
- usize len = in->cur - from;
-
- *out = str(arena, from, len);
-
- if (!json__ensure('"')) {
- json__logv();
- goto fail;
- }
-
- return true;
-fail:
- *out = STR_EMPTY;
- return false;
-}
-
-static bool json__parse_number(instream_t *in, double *out) {
- return istrGetDouble(in, out);
-}
-
-static bool json__parse_bool(instream_t *in, bool *out) {
- size_t remaining = istrRemaining(*in);
- if (remaining >= 4 && memcmp(in->cur, "true", 4) == 0) {
- istrSkip(in, 4);
- *out = true;
- }
- else if (remaining >= 5 && memcmp(in->cur, "false", 5) == 0) {
- istrSkip(in, 5);
- *out = false;
- }
- else {
- err("unknown boolean at %zu: %.10s", istrTell(*in), in->cur);
- json__logv();
- return false;
- }
-
- return true;
-}
-
-static bool json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) {
- if (!json__ensure('{')) {
- json__logv();
- goto fail;
- }
-
- istrSkipWhitespace(in);
-
- // if it is an empty object
- if (istrPeek(in) == '}') {
- istrSkip(in, 1);
- *out = NULL;
- return true;
- }
-
- jsonval_t *head = NULL;
- if (!json__parse_pair(arena, in, flags, &head)) {
- json__logv();
- goto fail;
- }
- jsonval_t *cur = head;
-
- while (true) {
- istrSkipWhitespace(in);
- switch (istrGet(in)) {
- case '}':
- goto success;
- case ',':
- {
- istrSkipWhitespace(in);
- // trailing commas
- if (!(flags & JSON_NO_TRAILING_COMMAS) && istrPeek(in) == '}') {
- goto success;
- }
-
- jsonval_t *next = NULL;
- if (!json__parse_pair(arena, in, flags, &next)) {
- json__logv();
- goto fail;
- }
- cur->next = next;
- next->prev = cur;
- cur = next;
- break;
- }
- default:
- istrRewindN(in, 1);
- err("unknown char after object at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur);
- json__logv();
- goto fail;
- }
- }
-
-success:
- *out = head;
- return true;
-fail:
- *out = NULL;
- return false;
-}
-
-static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) {
- str_t key = {0};
- if (!json__parse_string(arena, in, &key)) {
- json__logv();
- goto fail;
- }
-
- // skip preamble
- istrSkipWhitespace(in);
- if (!json__ensure(':')) {
- json__logv();
- goto fail;
- }
-
- if (!json__parse_value(arena, in, flags, out)) {
- json__logv();
- goto fail;
- }
-
- (*out)->key = key;
- return true;
-
-fail:
- *out = NULL;
- return false;
-}
-
-static bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) {
- jsonval_t *val = alloc(arena, jsonval_t);
-
- istrSkipWhitespace(in);
-
- switch (istrPeek(in)) {
- // object
- case '{':
- if (!json__parse_obj(arena, in, flags, &val->object)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_OBJECT;
- break;
- // array
- case '[':
- if (!json__parse_array(arena, in, flags, &val->array)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_ARRAY;
- break;
- // string
- case '"':
- if (!json__parse_string(arena, in, &val->string)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_STRING;
- break;
- // boolean
- case 't': // fallthrough
- case 'f':
- if (!json__parse_bool(in, &val->boolean)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_BOOL;
- break;
- // null
- case 'n':
- if (!json__parse_null(in)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_NULL;
- break;
- // comment
- case '/':
- err("TODO comments");
- break;
- // number
- default:
- if (!json__parse_number(in, &val->number)) {
- json__logv();
- goto fail;
- }
- val->type = JSON_NUMBER;
- break;
- }
-
- *out = val;
- return true;
-fail:
- *out = NULL;
- return false;
-}
-
-json_t jsonParse(arena_t *arena, arena_t scratch, strview_t filename, jsonflags_e flags) {
- str_t data = fileReadWholeStr(&scratch, filename);
- return NULL;
- json_t json = jsonParseStr(arena, strv(data), flags);
- return json;
-}
-
-json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags) {
- arena_t before = *arena;
-
- jsonval_t *root = alloc(arena, jsonval_t);
- root->type = JSON_OBJECT;
-
- instream_t in = istrInitLen(jsonstr.buf, jsonstr.len);
-
- if (!json__parse_obj(arena, &in, flags, &root->object)) {
- // reset arena
- *arena = before;
- json__logv();
- return NULL;
- }
-
- return root;
-}
-
-jsonval_t *jsonGet(jsonval_t *node, strview_t key) {
- if (!node) return NULL;
-
- if (node->type != JSON_OBJECT) {
- err("passed type is not an object");
- return NULL;
- }
-
- node = node->object;
-
- while (node) {
- if (strvEquals(strv(node->key), key)) {
- return node;
- }
- node = node->next;
- }
-
- return NULL;
-}
-
-#include "warnings/colla_warn_end.h"
\ No newline at end of file
diff --git a/json.h b/json.h
deleted file mode 100644
index 762e53a..0000000
--- a/json.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "str.h"
-#include "arena.h"
-
-typedef enum {
- JSON_NULL,
- JSON_ARRAY,
- JSON_STRING,
- JSON_NUMBER,
- JSON_BOOL,
- JSON_OBJECT,
-} jsontype_e;
-
-typedef enum {
- JSON_DEFAULT = 0,
- JSON_NO_TRAILING_COMMAS = 1 << 0,
- JSON_NO_COMMENTS = 1 << 1,
-} jsonflags_e;
-
-typedef struct jsonval_t jsonval_t;
-
-typedef struct jsonval_t {
- jsonval_t *next;
- jsonval_t *prev;
-
- str_t key;
-
- union {
- jsonval_t *array;
- str_t string;
- double number;
- bool boolean;
- jsonval_t *object;
- };
- jsontype_e type;
-} jsonval_t;
-
-typedef jsonval_t *json_t;
-
-json_t jsonParse(arena_t *arena, arena_t scratch, strview_t filename, jsonflags_e flags);
-json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags);
-
-jsonval_t *jsonGet(jsonval_t *node, strview_t key);
-
-#define json_check(val, js_type) ((val) && (val)->type == js_type)
-#define json_for(name, arr) for (jsonval_t *name = json_check(arr, JSON_ARRAY) ? arr->array : NULL; name; name = name->next)
diff --git a/lz4/lz4.c b/lz4/lz4.c
deleted file mode 100644
index 2aa4661..0000000
--- a/lz4/lz4.c
+++ /dev/null
@@ -1,2829 +0,0 @@
-/*
- LZ4 - Fast LZ compression algorithm
- Copyright (C) 2011-2023, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following disclaimer
- in the documentation and/or other materials provided with the
- distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- You can contact the author at :
- - LZ4 homepage : http://www.lz4.org
- - LZ4 source repository : https://github.com/lz4/lz4
-*/
-
-/*-************************************
-* Tuning parameters
-**************************************/
-/*
- * LZ4_HEAPMODE :
- * Select how stateless compression functions like `LZ4_compress_default()`
- * allocate memory for their hash table,
- * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
- */
-#ifndef LZ4_HEAPMODE
-# define LZ4_HEAPMODE 0
-#endif
-
-/*
- * LZ4_ACCELERATION_DEFAULT :
- * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
- */
-#define LZ4_ACCELERATION_DEFAULT 1
-/*
- * LZ4_ACCELERATION_MAX :
- * Any "acceleration" value higher than this threshold
- * get treated as LZ4_ACCELERATION_MAX instead (fix #876)
- */
-#define LZ4_ACCELERATION_MAX 65537
-
-
-/*-************************************
-* CPU Feature Detection
-**************************************/
-/* LZ4_FORCE_MEMORY_ACCESS
- * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
- * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
- * The below switch allow to select different access method for improved performance.
- * Method 0 (default) : use `memcpy()`. Safe and portable.
- * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
- * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
- * Method 2 : direct access. This method is portable but violate C standard.
- * It can generate buggy code on targets which assembly generation depends on alignment.
- * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
- * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
- * Prefer these methods in priority order (0 > 1 > 2)
- */
-#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */
-# if defined(__GNUC__) && \
- ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
- || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
-# define LZ4_FORCE_MEMORY_ACCESS 2
-# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) || defined(_MSC_VER)
-# define LZ4_FORCE_MEMORY_ACCESS 1
-# endif
-#endif
-
-/*
- * LZ4_FORCE_SW_BITCOUNT
- * Define this parameter if your target system or compiler does not support hardware bit count
- */
-#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */
-# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */
-# define LZ4_FORCE_SW_BITCOUNT
-#endif
-
-
-
-/*-************************************
-* Dependency
-**************************************/
-/*
- * LZ4_SRC_INCLUDED:
- * Amalgamation flag, whether lz4.c is included
- */
-#ifndef LZ4_SRC_INCLUDED
-# define LZ4_SRC_INCLUDED 1
-#endif
-
-#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS
-# define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */
-#endif
-
-#ifndef LZ4_STATIC_LINKING_ONLY
-# define LZ4_STATIC_LINKING_ONLY
-#endif
-#include "lz4.h"
-/* see also "memory routines" below */
-
-
-/*-************************************
-* Compiler Options
-**************************************/
-#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */
-# include /* only present in VS2005+ */
-# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
-# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */
-# pragma warning(disable : 6239) /* disable: C6239: ( && ) always evaluates to the result of */
-# pragma warning(disable : 6240) /* disable: C6240: ( && ) always evaluates to the result of */
-# pragma warning(disable : 6326) /* disable: C6326: Potential comparison of a constant with another constant */
-#endif /* _MSC_VER */
-
-#ifndef LZ4_FORCE_INLINE
-# if defined (_MSC_VER) && !defined (__clang__) /* MSVC */
-# define LZ4_FORCE_INLINE static __forceinline
-# else
-# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
-# if defined (__GNUC__) || defined (__clang__)
-# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
-# else
-# define LZ4_FORCE_INLINE static inline
-# endif
-# else
-# define LZ4_FORCE_INLINE static
-# endif /* __STDC_VERSION__ */
-# endif /* _MSC_VER */
-#endif /* LZ4_FORCE_INLINE */
-
-/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE
- * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,
- * together with a simple 8-byte copy loop as a fall-back path.
- * However, this optimization hurts the decompression speed by >30%,
- * because the execution does not go to the optimized loop
- * for typical compressible data, and all of the preamble checks
- * before going to the fall-back path become useless overhead.
- * This optimization happens only with the -O3 flag, and -O2 generates
- * a simple 8-byte copy loop.
- * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8
- * functions are annotated with __attribute__((optimize("O2"))),
- * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute
- * of LZ4_wildCopy8 does not affect the compression speed.
- */
-#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)
-# define LZ4_FORCE_O2 __attribute__((optimize("O2")))
-# undef LZ4_FORCE_INLINE
-# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline))
-#else
-# define LZ4_FORCE_O2
-#endif
-
-#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
-# define expect(expr,value) (__builtin_expect ((expr),(value)) )
-#else
-# define expect(expr,value) (expr)
-#endif
-
-#ifndef likely
-#define likely(expr) expect((expr) != 0, 1)
-#endif
-#ifndef unlikely
-#define unlikely(expr) expect((expr) != 0, 0)
-#endif
-
-/* Should the alignment test prove unreliable, for some reason,
- * it can be disabled by setting LZ4_ALIGN_TEST to 0 */
-#ifndef LZ4_ALIGN_TEST /* can be externally provided */
-# define LZ4_ALIGN_TEST 1
-#endif
-
-
-/*-************************************
-* Memory routines
-**************************************/
-
-/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :
- * Disable relatively high-level LZ4/HC functions that use dynamic memory
- * allocation functions (malloc(), calloc(), free()).
- *
- * Note that this is a compile-time switch. And since it disables
- * public/stable LZ4 v1 API functions, we don't recommend using this
- * symbol to generate a library for distribution.
- *
- * The following public functions are removed when this symbol is defined.
- * - lz4 : LZ4_createStream, LZ4_freeStream,
- * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)
- * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,
- * LZ4_createHC (deprecated), LZ4_freeHC (deprecated)
- * - lz4frame, lz4file : All LZ4F_* functions
- */
-#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-# define ALLOC(s) lz4_error_memory_allocation_is_disabled
-# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled
-# define FREEMEM(p) lz4_error_memory_allocation_is_disabled
-#elif defined(LZ4_USER_MEMORY_FUNCTIONS)
-/* memory management functions can be customized by user project.
- * Below functions must exist somewhere in the Project
- * and be available at link time */
-void* LZ4_malloc(size_t s);
-void* LZ4_calloc(size_t n, size_t s);
-void LZ4_free(void* p);
-# define ALLOC(s) LZ4_malloc(s)
-# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)
-# define FREEMEM(p) LZ4_free(p)
-#else
-# include /* malloc, calloc, free */
-# define ALLOC(s) malloc(s)
-# define ALLOC_AND_ZERO(s) calloc(1,s)
-# define FREEMEM(p) free(p)
-#endif
-
-#if ! LZ4_FREESTANDING
-# include /* memset, memcpy */
-#endif
-#if !defined(LZ4_memset)
-# define LZ4_memset(p,v,s) memset((p),(v),(s))
-#endif
-#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s))
-
-
-/*-************************************
-* Common Constants
-**************************************/
-#define MINMATCH 4
-
-#define WILDCOPYLENGTH 8
-#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
-#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
-#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */
-#define FASTLOOP_SAFE_DISTANCE 64
-static const int LZ4_minLength = (MFLIMIT+1);
-
-#define LZ_KB *(1 <<10)
-#define LZ_MB *(1 <<20)
-#define LZ_GB *(1U<<30)
-
-#define LZ4_DISTANCE_ABSOLUTE_MAX 65535
-#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */
-# error "LZ4_DISTANCE_MAX is too big : must be <= 65535"
-#endif
-
-#define ML_BITS 4
-#define ML_MASK ((1U<=1)
-# include
-#else
-# ifndef assert
-# define assert(condition) ((void)0)
-# endif
-#endif
-
-#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */
-
-#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)
-# include
- static int g_debuglog_enable = 1;
-# define DEBUGLOG(l, ...) { \
- if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
- fprintf(stderr, __FILE__ " %i: ", __LINE__); \
- fprintf(stderr, __VA_ARGS__); \
- fprintf(stderr, " \n"); \
- } }
-#else
-# define DEBUGLOG(l, ...) {} /* disabled */
-#endif
-
-static int LZ4_isAligned(const void* ptr, size_t alignment)
-{
- return ((size_t)ptr & (alignment -1)) == 0;
-}
-
-
-/*-************************************
-* Types
-**************************************/
-#include
-#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-# include
- typedef uint8_t BYTE;
- typedef uint16_t U16;
- typedef uint32_t U32;
- typedef int32_t S32;
- typedef uint64_t U64;
- typedef uintptr_t uptrval;
-#else
-# if UINT_MAX != 4294967295UL
-# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"
-# endif
- typedef unsigned char BYTE;
- typedef unsigned short U16;
- typedef unsigned int U32;
- typedef signed int S32;
- typedef unsigned long long U64;
- typedef size_t uptrval; /* generally true, except OpenVMS-64 */
-#endif
-
-#if defined(__x86_64__)
- typedef U64 reg_t; /* 64-bits in x32 mode */
-#else
- typedef size_t reg_t; /* 32-bits in x32 mode */
-#endif
-
-typedef enum {
- notLimited = 0,
- limitedOutput = 1,
- fillOutput = 2
-} limitedOutput_directive;
-
-
-/*-************************************
-* Reading and writing into memory
-**************************************/
-
-/**
- * LZ4 relies on memcpy with a constant size being inlined. In freestanding
- * environments, the compiler can't assume the implementation of memcpy() is
- * standard compliant, so it can't apply its specialized memcpy() inlining
- * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze
- * memcpy() as if it were standard compliant, so it can inline it in freestanding
- * environments. This is needed when decompressing the Linux Kernel, for example.
- */
-#if !defined(LZ4_memcpy)
-# if defined(__GNUC__) && (__GNUC__ >= 4)
-# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)
-# else
-# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)
-# endif
-#endif
-
-#if !defined(LZ4_memmove)
-# if defined(__GNUC__) && (__GNUC__ >= 4)
-# define LZ4_memmove __builtin_memmove
-# else
-# define LZ4_memmove memmove
-# endif
-#endif
-
-static unsigned LZ4_isLittleEndian(void)
-{
- const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
- return one.c[0];
-}
-
-#if defined(__GNUC__) || defined(__INTEL_COMPILER)
-#define LZ4_PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
-#elif defined(_MSC_VER)
-#define LZ4_PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
-#endif
-
-#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
-/* lie to the compiler about data alignment; use with caution */
-
-static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
-static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
-static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
-
-static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
-static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
-
-#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
-
-/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
-/* currently only defined for gcc and icc */
-LZ4_PACK(typedef struct { U16 u16; }) LZ4_unalign16;
-LZ4_PACK(typedef struct { U32 u32; }) LZ4_unalign32;
-LZ4_PACK(typedef struct { reg_t uArch; }) LZ4_unalignST;
-
-static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; }
-static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; }
-static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; }
-
-static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; }
-static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; }
-
-#else /* safe and portable access using memcpy() */
-
-static U16 LZ4_read16(const void* memPtr)
-{
- U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
-}
-
-static U32 LZ4_read32(const void* memPtr)
-{
- U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
-}
-
-static reg_t LZ4_read_ARCH(const void* memPtr)
-{
- reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
-}
-
-static void LZ4_write16(void* memPtr, U16 value)
-{
- LZ4_memcpy(memPtr, &value, sizeof(value));
-}
-
-static void LZ4_write32(void* memPtr, U32 value)
-{
- LZ4_memcpy(memPtr, &value, sizeof(value));
-}
-
-#endif /* LZ4_FORCE_MEMORY_ACCESS */
-
-
-static U16 LZ4_readLE16(const void* memPtr)
-{
- if (LZ4_isLittleEndian()) {
- return LZ4_read16(memPtr);
- } else {
- const BYTE* p = (const BYTE*)memPtr;
- return (U16)((U16)p[0] | (p[1]<<8));
- }
-}
-
-#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT
-static U32 LZ4_readLE32(const void* memPtr)
-{
- if (LZ4_isLittleEndian()) {
- return LZ4_read32(memPtr);
- } else {
- const BYTE* p = (const BYTE*)memPtr;
- return (U32)p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
- }
-}
-#endif
-
-static void LZ4_writeLE16(void* memPtr, U16 value)
-{
- if (LZ4_isLittleEndian()) {
- LZ4_write16(memPtr, value);
- } else {
- BYTE* p = (BYTE*)memPtr;
- p[0] = (BYTE) value;
- p[1] = (BYTE)(value>>8);
- }
-}
-
-/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
-LZ4_FORCE_INLINE
-void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)
-{
- BYTE* d = (BYTE*)dstPtr;
- const BYTE* s = (const BYTE*)srcPtr;
- BYTE* const e = (BYTE*)dstEnd;
-
- do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */
-LZ4_FORCE_INLINE void
-LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)
-{
- BYTE* d = (BYTE*)dstPtr;
- const BYTE* s = (const BYTE*)srcPtr;
- BYTE* const e = (BYTE*)dstEnd;
-
- do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH
- * - there is at least 12 bytes available to write after dstEnd */
-LZ4_FORCE_INLINE void
-LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
-{
- BYTE v[8];
-
- assert(dstEnd >= dstPtr + MINMATCH);
-
- switch(offset) {
- case 1:
- MEM_INIT(v, *srcPtr, 8);
- break;
- case 2:
- LZ4_memcpy(v, srcPtr, 2);
- LZ4_memcpy(&v[2], srcPtr, 2);
-#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */
-# pragma warning(push)
-# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */
-#endif
- LZ4_memcpy(&v[4], v, 4);
-#if defined(_MSC_VER) && (_MSC_VER <= 1937) /* MSVC 2022 ver 17.7 or earlier */
-# pragma warning(pop)
-#endif
- break;
- case 4:
- LZ4_memcpy(v, srcPtr, 4);
- LZ4_memcpy(&v[4], srcPtr, 4);
- break;
- default:
- LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);
- return;
- }
-
- LZ4_memcpy(dstPtr, v, 8);
- dstPtr += 8;
- while (dstPtr < dstEnd) {
- LZ4_memcpy(dstPtr, v, 8);
- dstPtr += 8;
- }
-}
-#endif
-
-
-/*-************************************
-* Common functions
-**************************************/
-static unsigned LZ4_NbCommonBytes (reg_t val)
-{
- assert(val != 0);
- if (LZ4_isLittleEndian()) {
- if (sizeof(val) == 8) {
-# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)
-/*-*************************************************************************************************
-* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.
-* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics
-* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.
-****************************************************************************************************/
-# if defined(__clang__) && (__clang_major__ < 10)
- /* Avoid undefined clang-cl intrinsics issue.
- * See https://github.com/lz4/lz4/pull/1017 for details. */
- return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;
-# else
- /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */
- return (unsigned)_tzcnt_u64(val) >> 3;
-# endif
-# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanForward64(&r, (U64)val);
- return (unsigned)r >> 3;
-# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
- ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
- !defined(LZ4_FORCE_SW_BITCOUNT)
- return (unsigned)__builtin_ctzll((U64)val) >> 3;
-# else
- const U64 m = 0x0101010101010101ULL;
- val ^= val - 1;
- return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);
-# endif
- } else /* 32 bits */ {
-# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r;
- _BitScanForward(&r, (U32)val);
- return (unsigned)r >> 3;
-# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
- ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
- !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (unsigned)__builtin_ctz((U32)val) >> 3;
-# else
- const U32 m = 0x01010101;
- return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;
-# endif
- }
- } else /* Big Endian CPU */ {
- if (sizeof(val)==8) {
-# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
- ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
- !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (unsigned)__builtin_clzll((U64)val) >> 3;
-# else
-#if 1
- /* this method is probably faster,
- * but adds a 128 bytes lookup table */
- static const unsigned char ctz7_tab[128] = {
- 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
- };
- U64 const mask = 0x0101010101010101ULL;
- U64 const t = (((val >> 8) - mask) | val) & mask;
- return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];
-#else
- /* this method doesn't consume memory space like the previous one,
- * but it contains several branches,
- * that may end up slowing execution */
- static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits.
- Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.
- Note that this code path is never triggered in 32-bits mode. */
- unsigned r;
- if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
-#endif
-# endif
- } else /* 32 bits */ {
-# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
- ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
- !defined(LZ4_FORCE_SW_BITCOUNT)
- return (unsigned)__builtin_clz((U32)val) >> 3;
-# else
- val >>= 8;
- val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |
- (val + 0x00FF0000)) >> 24;
- return (unsigned)val ^ 3;
-# endif
- }
- }
-}
-
-
-#define STEPSIZE sizeof(reg_t)
-LZ4_FORCE_INLINE
-unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
-{
- const BYTE* const pStart = pIn;
-
- if (likely(pIn < pInLimit-(STEPSIZE-1))) {
- reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
- if (!diff) {
- pIn+=STEPSIZE; pMatch+=STEPSIZE;
- } else {
- return LZ4_NbCommonBytes(diff);
- } }
-
- while (likely(pIn < pInLimit-(STEPSIZE-1))) {
- reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
- if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
- pIn += LZ4_NbCommonBytes(diff);
- return (unsigned)(pIn - pStart);
- }
-
- if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
- if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
- if ((pIn compression run slower on incompressible data */
-
-
-/*-************************************
-* Local Structures and types
-**************************************/
-typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
-
-/**
- * This enum distinguishes several different modes of accessing previous
- * content in the stream.
- *
- * - noDict : There is no preceding content.
- * - withPrefix64k : Table entries up to ctx->dictSize before the current blob
- * blob being compressed are valid and refer to the preceding
- * content (of length ctx->dictSize), which is available
- * contiguously preceding in memory the content currently
- * being compressed.
- * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere
- * else in memory, starting at ctx->dictionary with length
- * ctx->dictSize.
- * - usingDictCtx : Everything concerning the preceding content is
- * in a separate context, pointed to by ctx->dictCtx.
- * ctx->dictionary, ctx->dictSize, and table entries
- * in the current context that refer to positions
- * preceding the beginning of the current compression are
- * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
- * ->dictSize describe the location and size of the preceding
- * content, and matches are found by looking in the ctx
- * ->dictCtx->hashTable.
- */
-typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
-typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
-
-
-/*-************************************
-* Local Utils
-**************************************/
-int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
-const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
-int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
-int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }
-
-
-/*-****************************************
-* Internal Definitions, used only in Tests
-*******************************************/
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);
-
-int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
- int compressedSize, int maxOutputSize,
- const void* dictStart, size_t dictSize);
-int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
- int compressedSize, int targetOutputSize, int dstCapacity,
- const void* dictStart, size_t dictSize);
-#if defined (__cplusplus)
-}
-#endif
-
-/*-******************************
-* Compression functions
-********************************/
-LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
-{
- if (tableType == byU16)
- return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
- else
- return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
-}
-
-LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
-{
- const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
- if (LZ4_isLittleEndian()) {
- const U64 prime5bytes = 889523592379ULL;
- return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
- } else {
- const U64 prime8bytes = 11400714785074694791ULL;
- return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
- }
-}
-
-LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
-{
- if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
-
-#ifdef LZ4_STATIC_LINKING_ONLY_ENDIANNESS_INDEPENDENT_OUTPUT
- return LZ4_hash4(LZ4_readLE32(p), tableType);
-#else
- return LZ4_hash4(LZ4_read32(p), tableType);
-#endif
-}
-
-LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)
-{
- switch (tableType)
- {
- default: /* fallthrough */
- case clearedTable: { /* illegal! */ assert(0); return; }
- case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }
- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }
- case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }
- }
-}
-
-LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
-{
- switch (tableType)
- {
- default: /* fallthrough */
- case clearedTable: /* fallthrough */
- case byPtr: { /* illegal! */ assert(0); return; }
- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
- case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }
- }
-}
-
-/* LZ4_putPosition*() : only used in byPtr mode */
-LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,
- void* tableBase, tableType_t const tableType)
-{
- const BYTE** const hashTable = (const BYTE**)tableBase;
- assert(tableType == byPtr); (void)tableType;
- hashTable[h] = p;
-}
-
-LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType)
-{
- U32 const h = LZ4_hashPosition(p, tableType);
- LZ4_putPositionOnHash(p, h, tableBase, tableType);
-}
-
-/* LZ4_getIndexOnHash() :
- * Index of match position registered in hash table.
- * hash position must be calculated by using base+index, or dictBase+index.
- * Assumption 1 : only valid if tableType == byU32 or byU16.
- * Assumption 2 : h is presumed valid (within limits of hash table)
- */
-LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
-{
- LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
- if (tableType == byU32) {
- const U32* const hashTable = (const U32*) tableBase;
- assert(h < (1U << (LZ4_MEMORY_USAGE-2)));
- return hashTable[h];
- }
- if (tableType == byU16) {
- const U16* const hashTable = (const U16*) tableBase;
- assert(h < (1U << (LZ4_MEMORY_USAGE-1)));
- return hashTable[h];
- }
- assert(0); return 0; /* forbidden case */
-}
-
-static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType)
-{
- assert(tableType == byPtr); (void)tableType;
- { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
-}
-
-LZ4_FORCE_INLINE const BYTE*
-LZ4_getPosition(const BYTE* p,
- const void* tableBase, tableType_t tableType)
-{
- U32 const h = LZ4_hashPosition(p, tableType);
- return LZ4_getPositionOnHash(h, tableBase, tableType);
-}
-
-LZ4_FORCE_INLINE void
-LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
- const int inputSize,
- const tableType_t tableType) {
- /* If the table hasn't been used, it's guaranteed to be zeroed out, and is
- * therefore safe to use no matter what mode we're in. Otherwise, we figure
- * out if it's safe to leave as is or whether it needs to be reset.
- */
- if ((tableType_t)cctx->tableType != clearedTable) {
- assert(inputSize >= 0);
- if ((tableType_t)cctx->tableType != tableType
- || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)
- || ((tableType == byU32) && cctx->currentOffset > 1 LZ_GB)
- || tableType == byPtr
- || inputSize >= 4 LZ_KB)
- {
- DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
- MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
- cctx->currentOffset = 0;
- cctx->tableType = (U32)clearedTable;
- } else {
- DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
- }
- }
-
- /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,
- * is faster than compressing without a gap.
- * However, compressing with currentOffset == 0 is faster still,
- * so we preserve that case.
- */
- if (cctx->currentOffset != 0 && tableType == byU32) {
- DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
- cctx->currentOffset += 64 LZ_KB;
- }
-
- /* Finally, clear history */
- cctx->dictCtx = NULL;
- cctx->dictionary = NULL;
- cctx->dictSize = 0;
-}
-
-/** LZ4_compress_generic_validated() :
- * inlined, to ensure branches are decided at compilation time.
- * The following conditions are presumed already validated:
- * - source != NULL
- * - inputSize > 0
- */
-LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
- LZ4_stream_t_internal* const cctx,
- const char* const source,
- char* const dest,
- const int inputSize,
- int* inputConsumed, /* only written when outputDirective == fillOutput */
- const int maxOutputSize,
- const limitedOutput_directive outputDirective,
- const tableType_t tableType,
- const dict_directive dictDirective,
- const dictIssue_directive dictIssue,
- const int acceleration)
-{
- int result;
- const BYTE* ip = (const BYTE*)source;
-
- U32 const startIndex = cctx->currentOffset;
- const BYTE* base = (const BYTE*)source - startIndex;
- const BYTE* lowLimit;
-
- const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
- const BYTE* const dictionary =
- dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
- const U32 dictSize =
- dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
- const U32 dictDelta =
- (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with indexes in current context */
-
- int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
- U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
- const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;
- const BYTE* anchor = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
- const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- /* the dictCtx currentOffset is indexed on the start of the dictionary,
- * while a dictionary in the current context precedes the currentOffset */
- const BYTE* dictBase = (dictionary == NULL) ? NULL :
- (dictDirective == usingDictCtx) ?
- dictionary + dictSize - dictCtx->currentOffset :
- dictionary + dictSize - startIndex;
-
- BYTE* op = (BYTE*) dest;
- BYTE* const olimit = op + maxOutputSize;
-
- U32 offset = 0;
- U32 forwardH;
-
- DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType);
- assert(ip != NULL);
- if (tableType == byU16) assert(inputSize= 1);
-
- lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
-
- /* Update context state */
- if (dictDirective == usingDictCtx) {
- /* Subsequent linked blocks can't use the dictionary. */
- /* Instead, they use the block we just compressed. */
- cctx->dictCtx = NULL;
- cctx->dictSize = (U32)inputSize;
- } else {
- cctx->dictSize += (U32)inputSize;
- }
- cctx->currentOffset += (U32)inputSize;
- cctx->tableType = (U32)tableType;
-
- if (inputSizehashTable, byPtr);
- } else {
- LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType);
- } }
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; ) {
- const BYTE* match;
- BYTE* token;
- const BYTE* filledIp;
-
- /* Find a match */
- if (tableType == byPtr) {
- const BYTE* forwardIp = ip;
- int step = 1;
- int searchMatchNb = acceleration << LZ4_skipTrigger;
- do {
- U32 const h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
- assert(ip < mflimitPlusOne);
-
- match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType);
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType);
-
- } while ( (match+LZ4_DISTANCE_MAX < ip)
- || (LZ4_read32(match) != LZ4_read32(ip)) );
-
- } else { /* byU32, byU16 */
-
- const BYTE* forwardIp = ip;
- int step = 1;
- int searchMatchNb = acceleration << LZ4_skipTrigger;
- do {
- U32 const h = forwardH;
- U32 const current = (U32)(forwardIp - base);
- U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
- assert(matchIndex <= current);
- assert(forwardIp - base < (ptrdiff_t)(2 LZ_GB - 1));
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
- assert(ip < mflimitPlusOne);
-
- if (dictDirective == usingDictCtx) {
- if (matchIndex < startIndex) {
- /* there was no match, try the dictionary */
- assert(tableType == byU32);
- matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
- match = dictBase + matchIndex;
- matchIndex += dictDelta; /* make dictCtx index comparable with current context */
- lowLimit = dictionary;
- } else {
- match = base + matchIndex;
- lowLimit = (const BYTE*)source;
- }
- } else if (dictDirective == usingExtDict) {
- if (matchIndex < startIndex) {
- DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);
- assert(startIndex - matchIndex >= MINMATCH);
- assert(dictBase);
- match = dictBase + matchIndex;
- lowLimit = dictionary;
- } else {
- match = base + matchIndex;
- lowLimit = (const BYTE*)source;
- }
- } else { /* single continuous memory segment */
- match = base + matchIndex;
- }
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
-
- DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex);
- if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */
- assert(matchIndex < current);
- if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))
- && (matchIndex+LZ4_DISTANCE_MAX < current)) {
- continue;
- } /* too far */
- assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */
-
- if (LZ4_read32(match) == LZ4_read32(ip)) {
- if (maybe_extMem) offset = current - matchIndex;
- break; /* match found */
- }
-
- } while(1);
- }
-
- /* Catch up */
- filledIp = ip;
- assert(ip > anchor); /* this is always true as ip has been advanced before entering the main loop */
- if ((match > lowLimit) && unlikely(ip[-1] == match[-1])) {
- do { ip--; match--; } while (((ip > anchor) & (match > lowLimit)) && (unlikely(ip[-1] == match[-1])));
- }
-
- /* Encode Literals */
- { unsigned const litLength = (unsigned)(ip - anchor);
- token = op++;
- if ((outputDirective == limitedOutput) && /* Check output buffer overflow */
- (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {
- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
- }
- if ((outputDirective == fillOutput) &&
- (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {
- op--;
- goto _last_literals;
- }
- if (litLength >= RUN_MASK) {
- unsigned len = litLength - RUN_MASK;
- *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength< olimit)) {
- /* the match was too close to the end, rewind and go to last literals */
- op = token;
- goto _last_literals;
- }
-
- /* Encode Offset */
- if (maybe_extMem) { /* static test */
- DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source));
- assert(offset <= LZ4_DISTANCE_MAX && offset > 0);
- LZ4_writeLE16(op, (U16)offset); op+=2;
- } else {
- DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match));
- assert(ip-match <= LZ4_DISTANCE_MAX);
- LZ4_writeLE16(op, (U16)(ip - match)); op+=2;
- }
-
- /* Encode MatchLength */
- { unsigned matchCode;
-
- if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)
- && (lowLimit==dictionary) /* match within extDict */ ) {
- const BYTE* limit = ip + (dictEnd-match);
- assert(dictEnd > match);
- if (limit > matchlimit) limit = matchlimit;
- matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
- ip += (size_t)matchCode + MINMATCH;
- if (ip==limit) {
- unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
- matchCode += more;
- ip += more;
- }
- DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);
- } else {
- matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
- ip += (size_t)matchCode + MINMATCH;
- DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);
- }
-
- if ((outputDirective) && /* Check output buffer overflow */
- (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {
- if (outputDirective == fillOutput) {
- /* Match description too long : reduce it */
- U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;
- ip -= matchCode - newMatchCode;
- assert(newMatchCode < matchCode);
- matchCode = newMatchCode;
- if (unlikely(ip <= filledIp)) {
- /* We have already filled up to filledIp so if ip ends up less than filledIp
- * we have positions in the hash table beyond the current position. This is
- * a problem if we reuse the hash table. So we have to remove these positions
- * from the hash table.
- */
- const BYTE* ptr;
- DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));
- for (ptr = ip; ptr <= filledIp; ++ptr) {
- U32 const h = LZ4_hashPosition(ptr, tableType);
- LZ4_clearHash(h, cctx->hashTable, tableType);
- }
- }
- } else {
- assert(outputDirective == limitedOutput);
- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
- }
- }
- if (matchCode >= ML_MASK) {
- *token += ML_MASK;
- matchCode -= ML_MASK;
- LZ4_write32(op, 0xFFFFFFFF);
- while (matchCode >= 4*255) {
- op+=4;
- LZ4_write32(op, 0xFFFFFFFF);
- matchCode -= 4*255;
- }
- op += matchCode / 255;
- *op++ = (BYTE)(matchCode % 255);
- } else
- *token += (BYTE)(matchCode);
- }
- /* Ensure we have enough space for the last literals. */
- assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));
-
- anchor = ip;
-
- /* Test end of chunk */
- if (ip >= mflimitPlusOne) break;
-
- /* Fill table */
- { U32 const h = LZ4_hashPosition(ip-2, tableType);
- if (tableType == byPtr) {
- LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr);
- } else {
- U32 const idx = (U32)((ip-2) - base);
- LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType);
- } }
-
- /* Test next position */
- if (tableType == byPtr) {
-
- match = LZ4_getPosition(ip, cctx->hashTable, tableType);
- LZ4_putPosition(ip, cctx->hashTable, tableType);
- if ( (match+LZ4_DISTANCE_MAX >= ip)
- && (LZ4_read32(match) == LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- } else { /* byU32, byU16 */
-
- U32 const h = LZ4_hashPosition(ip, tableType);
- U32 const current = (U32)(ip-base);
- U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
- assert(matchIndex < current);
- if (dictDirective == usingDictCtx) {
- if (matchIndex < startIndex) {
- /* there was no match, try the dictionary */
- assert(tableType == byU32);
- matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
- match = dictBase + matchIndex;
- lowLimit = dictionary; /* required for match length counter */
- matchIndex += dictDelta;
- } else {
- match = base + matchIndex;
- lowLimit = (const BYTE*)source; /* required for match length counter */
- }
- } else if (dictDirective==usingExtDict) {
- if (matchIndex < startIndex) {
- assert(dictBase);
- match = dictBase + matchIndex;
- lowLimit = dictionary; /* required for match length counter */
- } else {
- match = base + matchIndex;
- lowLimit = (const BYTE*)source; /* required for match length counter */
- }
- } else { /* single memory segment */
- match = base + matchIndex;
- }
- LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
- assert(matchIndex < current);
- if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
- && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))
- && (LZ4_read32(match) == LZ4_read32(ip)) ) {
- token=op++;
- *token=0;
- if (maybe_extMem) offset = current - matchIndex;
- DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
- (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
- goto _next_match;
- }
- }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
-
- }
-
-_last_literals:
- /* Encode Last Literals */
- { size_t lastRun = (size_t)(iend - anchor);
- if ( (outputDirective) && /* Check output buffer overflow */
- (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {
- if (outputDirective == fillOutput) {
- /* adapt lastRun to fill 'dst' */
- assert(olimit >= op);
- lastRun = (size_t)(olimit-op) - 1/*token*/;
- lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/
- } else {
- assert(outputDirective == limitedOutput);
- return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
- }
- }
- DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun);
- if (lastRun >= RUN_MASK) {
- size_t accumulator = lastRun - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- } else {
- *op++ = (BYTE)(lastRun< 0);
- DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result);
- return result;
-}
-
-/** LZ4_compress_generic() :
- * inlined, to ensure branches are decided at compilation time;
- * takes care of src == (NULL, 0)
- * and forward the rest to LZ4_compress_generic_validated */
-LZ4_FORCE_INLINE int LZ4_compress_generic(
- LZ4_stream_t_internal* const cctx,
- const char* const src,
- char* const dst,
- const int srcSize,
- int *inputConsumed, /* only written when outputDirective == fillOutput */
- const int dstCapacity,
- const limitedOutput_directive outputDirective,
- const tableType_t tableType,
- const dict_directive dictDirective,
- const dictIssue_directive dictIssue,
- const int acceleration)
-{
- DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i",
- srcSize, dstCapacity);
-
- if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */
- if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */
- if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */
- DEBUGLOG(5, "Generating an empty block");
- assert(outputDirective == notLimited || dstCapacity >= 1);
- assert(dst != NULL);
- dst[0] = 0;
- if (outputDirective == fillOutput) {
- assert (inputConsumed != NULL);
- *inputConsumed = 0;
- }
- return 1;
- }
- assert(src != NULL);
-
- return LZ4_compress_generic_validated(cctx, src, dst, srcSize,
- inputConsumed, /* only written into if outputDirective == fillOutput */
- dstCapacity, outputDirective,
- tableType, dictDirective, dictIssue, acceleration);
-}
-
-
-int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;
- assert(ctx != NULL);
- if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
- if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
- if (maxOutputSize >= LZ4_compressBound(inputSize)) {
- if (inputSize < LZ4_64Klimit) {
- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
- } else {
- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
- }
- } else {
- if (inputSize < LZ4_64Klimit) {
- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- } else {
- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
- return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
- }
- }
-}
-
-/**
- * LZ4_compress_fast_extState_fastReset() :
- * A variant of LZ4_compress_fast_extState().
- *
- * Using this variant avoids an expensive initialization step. It is only safe
- * to call if the state buffer is known to be correctly initialized already
- * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
- * "correctly initialized").
- */
-int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
-{
- LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse;
- if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
- if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
- assert(ctx != NULL);
-
- if (dstCapacity >= LZ4_compressBound(srcSize)) {
- if (srcSize < LZ4_64Klimit) {
- const tableType_t tableType = byU16;
- LZ4_prepareTable(ctx, srcSize, tableType);
- if (ctx->currentOffset) {
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);
- } else {
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
- }
- } else {
- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
- LZ4_prepareTable(ctx, srcSize, tableType);
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
- }
- } else {
- if (srcSize < LZ4_64Klimit) {
- const tableType_t tableType = byU16;
- LZ4_prepareTable(ctx, srcSize, tableType);
- if (ctx->currentOffset) {
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
- } else {
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
- }
- } else {
- const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
- LZ4_prepareTable(ctx, srcSize, tableType);
- return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
- }
- }
-}
-
-
-int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)
-{
- int result;
-#if (LZ4_HEAPMODE)
- LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
- if (ctxPtr == NULL) return 0;
-#else
- LZ4_stream_t ctx;
- LZ4_stream_t* const ctxPtr = &ctx;
-#endif
- result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration);
-
-#if (LZ4_HEAPMODE)
- FREEMEM(ctxPtr);
-#endif
- return result;
-}
-
-
-int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity)
-{
- return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1);
-}
-
-
-/* Note!: This function leaves the stream in an unclean/broken state!
- * It is not safe to subsequently use the same state with a _fastReset() or
- * _continue() call without resetting it. */
-static int LZ4_compress_destSize_extState_internal(LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration)
-{
- void* const s = LZ4_initStream(state, sizeof (*state));
- assert(s != NULL); (void)s;
-
- if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
- return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, acceleration);
- } else {
- if (*srcSizePtr < LZ4_64Klimit) {
- return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, acceleration);
- } else {
- tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
- return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, acceleration);
- } }
-}
-
-int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration)
-{
- int const r = LZ4_compress_destSize_extState_internal((LZ4_stream_t*)state, src, dst, srcSizePtr, targetDstSize, acceleration);
- /* clean the state on exit */
- LZ4_initStream(state, sizeof (LZ4_stream_t));
- return r;
-}
-
-
-int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
-{
-#if (LZ4_HEAPMODE)
- LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
- if (ctx == NULL) return 0;
-#else
- LZ4_stream_t ctxBody;
- LZ4_stream_t* const ctx = &ctxBody;
-#endif
-
- int result = LZ4_compress_destSize_extState_internal(ctx, src, dst, srcSizePtr, targetDstSize, 1);
-
-#if (LZ4_HEAPMODE)
- FREEMEM(ctx);
-#endif
- return result;
-}
-
-
-
-/*-******************************
-* Streaming functions
-********************************/
-
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-LZ4_stream_t* LZ4_createStream(void)
-{
- LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
- LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));
- DEBUGLOG(4, "LZ4_createStream %p", lz4s);
- if (lz4s == NULL) return NULL;
- LZ4_initStream(lz4s, sizeof(*lz4s));
- return lz4s;
-}
-#endif
-
-static size_t LZ4_stream_t_alignment(void)
-{
-#if LZ4_ALIGN_TEST
- typedef struct { char c; LZ4_stream_t t; } t_a;
- return sizeof(t_a) - sizeof(LZ4_stream_t);
-#else
- return 1; /* effectively disabled */
-#endif
-}
-
-LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)
-{
- DEBUGLOG(5, "LZ4_initStream");
- if (buffer == NULL) { return NULL; }
- if (size < sizeof(LZ4_stream_t)) { return NULL; }
- if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;
- MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));
- return (LZ4_stream_t*)buffer;
-}
-
-/* resetStream is now deprecated,
- * prefer initStream() which is more general */
-void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
-{
- DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
- MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));
-}
-
-void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
- LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
-}
-
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
-{
- if (!LZ4_stream) return 0; /* support free on NULL */
- DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
- FREEMEM(LZ4_stream);
- return (0);
-}
-#endif
-
-
-typedef enum { _ld_fast, _ld_slow } LoadDict_mode_e;
-#define HASH_UNIT sizeof(reg_t)
-int LZ4_loadDict_internal(LZ4_stream_t* LZ4_dict,
- const char* dictionary, int dictSize,
- LoadDict_mode_e _ld)
-{
- LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
- const tableType_t tableType = byU32;
- const BYTE* p = (const BYTE*)dictionary;
- const BYTE* const dictEnd = p + dictSize;
- U32 idx32;
-
- DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
-
- /* It's necessary to reset the context,
- * and not just continue it with prepareTable()
- * to avoid any risk of generating overflowing matchIndex
- * when compressing using this dictionary */
- LZ4_resetStream(LZ4_dict);
-
- /* We always increment the offset by 64 LZ_KB, since, if the dict is longer,
- * we truncate it to the last 64k, and if it's shorter, we still want to
- * advance by a whole window length so we can provide the guarantee that
- * there are only valid offsets in the window, which allows an optimization
- * in LZ4_compress_fast_continue() where it uses noDictIssue even when the
- * dictionary isn't a full 64k. */
- dict->currentOffset += 64 LZ_KB;
-
- if (dictSize < (int)HASH_UNIT) {
- return 0;
- }
-
- if ((dictEnd - p) > 64 LZ_KB) p = dictEnd - 64 LZ_KB;
- dict->dictionary = p;
- dict->dictSize = (U32)(dictEnd - p);
- dict->tableType = (U32)tableType;
- idx32 = dict->currentOffset - dict->dictSize;
-
- while (p <= dictEnd-HASH_UNIT) {
- U32 const h = LZ4_hashPosition(p, tableType);
- /* Note: overwriting => favors positions end of dictionary */
- LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
- p+=3; idx32+=3;
- }
-
- if (_ld == _ld_slow) {
- /* Fill hash table with additional references, to improve compression capability */
- p = dict->dictionary;
- idx32 = dict->currentOffset - dict->dictSize;
- while (p <= dictEnd-HASH_UNIT) {
- U32 const h = LZ4_hashPosition(p, tableType);
- U32 const limit = dict->currentOffset - 64 LZ_KB;
- if (LZ4_getIndexOnHash(h, dict->hashTable, tableType) <= limit) {
- /* Note: not overwriting => favors positions beginning of dictionary */
- LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
- }
- p++; idx32++;
- }
- }
-
- return (int)dict->dictSize;
-}
-
-int LZ4_loadDict(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
-{
- return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_fast);
-}
-
-int LZ4_loadDictSlow(LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
-{
- return LZ4_loadDict_internal(LZ4_dict, dictionary, dictSize, _ld_slow);
-}
-
-void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)
-{
- const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :
- &(dictionaryStream->internal_donotuse);
-
- DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",
- workingStream, dictionaryStream,
- dictCtx != NULL ? dictCtx->dictSize : 0);
-
- if (dictCtx != NULL) {
- /* If the current offset is zero, we will never look in the
- * external dictionary context, since there is no value a table
- * entry can take that indicate a miss. In that case, we need
- * to bump the offset to something non-zero.
- */
- if (workingStream->internal_donotuse.currentOffset == 0) {
- workingStream->internal_donotuse.currentOffset = 64 LZ_KB;
- }
-
- /* Don't actually attach an empty dictionary.
- */
- if (dictCtx->dictSize == 0) {
- dictCtx = NULL;
- }
- }
- workingStream->internal_donotuse.dictCtx = dictCtx;
-}
-
-
-static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
-{
- assert(nextSize >= 0);
- if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */
- /* rescale hash table */
- U32 const delta = LZ4_dict->currentOffset - 64 LZ_KB;
- const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
- int i;
- DEBUGLOG(4, "LZ4_renormDictT");
- for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0;
- else LZ4_dict->hashTable[i] -= delta;
- }
- LZ4_dict->currentOffset = 64 LZ_KB;
- if (LZ4_dict->dictSize > 64 LZ_KB) LZ4_dict->dictSize = 64 LZ_KB;
- LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
- }
-}
-
-
-int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
- const char* source, char* dest,
- int inputSize, int maxOutputSize,
- int acceleration)
-{
- const tableType_t tableType = byU32;
- LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;
- const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;
-
- DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize);
-
- LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */
- if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
- if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
-
- /* invalidate tiny dictionaries */
- if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */
- && (dictEnd != source) /* prefix mode */
- && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */
- && (streamPtr->dictCtx == NULL) /* usingDictCtx */
- ) {
- DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);
- /* remove dictionary existence from history, to employ faster prefix mode */
- streamPtr->dictSize = 0;
- streamPtr->dictionary = (const BYTE*)source;
- dictEnd = source;
- }
-
- /* Check overlapping input/dictionary space */
- { const char* const sourceEnd = source + inputSize;
- if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {
- streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
- if (streamPtr->dictSize > 64 LZ_KB) streamPtr->dictSize = 64 LZ_KB;
- if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
- streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;
- }
- }
-
- /* prefix mode : source data follows dictionary */
- if (dictEnd == source) {
- if ((streamPtr->dictSize < 64 LZ_KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
- else
- return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
- }
-
- /* external dictionary mode */
- { int result;
- if (streamPtr->dictCtx) {
- /* We depend here on the fact that dictCtx'es (produced by
- * LZ4_loadDict) guarantee that their tables contain no references
- * to offsets between dictCtx->currentOffset - 64 LZ_KB and
- * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
- * to use noDictIssue even when the dict isn't a full 64 LZ_KB.
- */
- if (inputSize > 4 LZ_KB) {
- /* For compressing large blobs, it is faster to pay the setup
- * cost to copy the dictionary's tables into the active context,
- * so that the compression loop is only looking into one table.
- */
- LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
- } else {
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
- }
- } else { /* small data <= 4 LZ_KB */
- if ((streamPtr->dictSize < 64 LZ_KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
- } else {
- result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
- }
- }
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- return result;
- }
-}
-
-
-/* Hidden debug function, to force-test external dictionary mode */
-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
-{
- LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse;
- int result;
-
- LZ4_renormDictT(streamPtr, srcSize);
-
- if ((streamPtr->dictSize < 64 LZ_KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
- result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
- } else {
- result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
- }
-
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)srcSize;
-
- return result;
-}
-
-
-/*! LZ4_saveDict() :
- * If previously compressed data block is not guaranteed to remain available at its memory location,
- * save it into a safer place (char* safeBuffer).
- * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,
- * one can therefore call LZ4_compress_fast_continue() right after.
- * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
- */
-int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
-{
- LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
-
- DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer);
-
- if ((U32)dictSize > 64 LZ_KB) { dictSize = 64 LZ_KB; } /* useless to define a dictionary > 64 LZ_KB */
- if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }
-
- if (safeBuffer == NULL) assert(dictSize == 0);
- if (dictSize > 0) {
- const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
- assert(dict->dictionary);
- LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);
- }
-
- dict->dictionary = (const BYTE*)safeBuffer;
- dict->dictSize = (U32)dictSize;
-
- return dictSize;
-}
-
-
-
-/*-*******************************
- * Decompression functions
- ********************************/
-
-typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
-
-#undef MIN
-#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
-
-
-/* variant for decompress_unsafe()
- * does not know end of input
- * presumes input is well formed
- * note : will consume at least one byte */
-static size_t read_long_length_no_check(const BYTE** pp)
-{
- size_t b, l = 0;
- do { b = **pp; (*pp)++; l += b; } while (b==255);
- DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1)
- return l;
-}
-
-/* core decoder variant for LZ4_decompress_fast*()
- * for legacy support only : these entry points are deprecated.
- * - Presumes input is correctly formed (no defense vs malformed inputs)
- * - Does not know input size (presume input buffer is "large enough")
- * - Decompress a full block (only)
- * @return : nb of bytes read from input.
- * Note : this variant is not optimized for speed, just for maintenance.
- * the goal is to remove support of decompress_fast*() variants by v2.0
-**/
-LZ4_FORCE_INLINE int
-LZ4_decompress_unsafe_generic(
- const BYTE* const istart,
- BYTE* const ostart,
- int decompressedSize,
-
- size_t prefixSize,
- const BYTE* const dictStart, /* only if dict==usingExtDict */
- const size_t dictSize /* note: =0 if dictStart==NULL */
- )
-{
- const BYTE* ip = istart;
- BYTE* op = (BYTE*)ostart;
- BYTE* const oend = ostart + decompressedSize;
- const BYTE* const prefixStart = ostart - prefixSize;
-
- DEBUGLOG(5, "LZ4_decompress_unsafe_generic");
- if (dictStart == NULL) assert(dictSize == 0);
-
- while (1) {
- /* start new sequence */
- unsigned token = *ip++;
-
- /* literals */
- { size_t ll = token >> ML_BITS;
- if (ll==15) {
- /* long literal length */
- ll += read_long_length_no_check(&ip);
- }
- if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */
- LZ4_memmove(op, ip, ll); /* support in-place decompression */
- op += ll;
- ip += ll;
- if ((size_t)(oend-op) < MFLIMIT) {
- if (op==oend) break; /* end of block */
- DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op);
- /* incorrect end of block :
- * last match must start at least MFLIMIT==12 bytes before end of output block */
- return -1;
- } }
-
- /* match */
- { size_t ml = token & 15;
- size_t const offset = LZ4_readLE16(ip);
- ip+=2;
-
- if (ml==15) {
- /* long literal length */
- ml += read_long_length_no_check(&ip);
- }
- ml += MINMATCH;
-
- if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */
-
- { const BYTE* match = op - offset;
-
- /* out of range */
- if (offset > (size_t)(op - prefixStart) + dictSize) {
- DEBUGLOG(6, "offset out of range");
- return -1;
- }
-
- /* check special case : extDict */
- if (offset > (size_t)(op - prefixStart)) {
- /* extDict scenario */
- const BYTE* const dictEnd = dictStart + dictSize;
- const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));
- size_t const extml = (size_t)(dictEnd - extMatch);
- if (extml > ml) {
- /* match entirely within extDict */
- LZ4_memmove(op, extMatch, ml);
- op += ml;
- ml = 0;
- } else {
- /* match split between extDict & prefix */
- LZ4_memmove(op, extMatch, extml);
- op += extml;
- ml -= extml;
- }
- match = prefixStart;
- }
-
- /* match copy - slow variant, supporting overlap copy */
- { size_t u;
- for (u=0; u= ipmax before start of loop. Returns initial_error if so.
- * @error (output) - error code. Must be set to 0 before call.
-**/
-typedef size_t Rvl_t;
-static const Rvl_t rvl_error = (Rvl_t)(-1);
-LZ4_FORCE_INLINE Rvl_t
-read_variable_length(const BYTE** ip, const BYTE* ilimit,
- int initial_check)
-{
- Rvl_t s, length = 0;
- assert(ip != NULL);
- assert(*ip != NULL);
- assert(ilimit != NULL);
- if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */
- return rvl_error;
- }
- s = **ip;
- (*ip)++;
- length += s;
- if (unlikely((*ip) > ilimit)) { /* read limit reached */
- return rvl_error;
- }
- /* accumulator overflow detection (32-bit mode only) */
- if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
- return rvl_error;
- }
- if (likely(s != 255)) return length;
- do {
- s = **ip;
- (*ip)++;
- length += s;
- if (unlikely((*ip) > ilimit)) { /* read limit reached */
- return rvl_error;
- }
- /* accumulator overflow detection (32-bit mode only) */
- if ((sizeof(length) < 8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
- return rvl_error;
- }
- } while (s == 255);
-
- return length;
-}
-
-/*! LZ4_decompress_generic() :
- * This generic decompression function covers all use cases.
- * It shall be instantiated several times, using different sets of directives.
- * Note that it is important for performance that this function really get inlined,
- * in order to remove useless branches during compilation optimization.
- */
-LZ4_FORCE_INLINE int
-LZ4_decompress_generic(
- const char* const src,
- char* const dst,
- int srcSize,
- int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
-
- earlyEnd_directive partialDecoding, /* full, partial */
- dict_directive dict, /* noDict, withPrefix64k, usingExtDict */
- const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
- const BYTE* const dictStart, /* only if dict==usingExtDict */
- const size_t dictSize /* note : = 0 if noDict */
- )
-{
- if ((src == NULL) || (outputSize < 0)) { return -1; }
-
- { const BYTE* ip = (const BYTE*) src;
- const BYTE* const iend = ip + srcSize;
-
- BYTE* op = (BYTE*) dst;
- BYTE* const oend = op + outputSize;
- BYTE* cpy;
-
- const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;
-
- const int checkOffset = (dictSize < (int)(64 LZ_KB));
-
-
- /* Set up the "end" pointers for the shortcut. */
- const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;
- const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;
-
- const BYTE* match;
- size_t offset;
- unsigned token;
- size_t length;
-
-
- DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);
-
- /* Special cases */
- assert(lowPrefix <= op);
- if (unlikely(outputSize==0)) {
- /* Empty output buffer */
- if (partialDecoding) return 0;
- return ((srcSize==1) && (*ip==0)) ? 0 : -1;
- }
- if (unlikely(srcSize==0)) { return -1; }
-
- /* LZ4_FAST_DEC_LOOP:
- * designed for modern OoO performance cpus,
- * where copying reliably 32-bytes is preferable to an unpredictable branch.
- * note : fast loop may show a regression for some client arm chips. */
-#if LZ4_FAST_DEC_LOOP
- if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {
- DEBUGLOG(6, "move to safe decode loop");
- goto safe_decode;
- }
-
- /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */
- DEBUGLOG(6, "using fast decode loop");
- while (1) {
- /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */
- assert(oend - op >= FASTLOOP_SAFE_DISTANCE);
- assert(ip < iend);
- token = *ip++;
- length = token >> ML_BITS; /* literal length */
- DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
-
- /* decode literal length */
- if (length == RUN_MASK) {
- size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
- if (addl == rvl_error) {
- DEBUGLOG(6, "error reading long literal length");
- goto _output_error;
- }
- length += addl;
- if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
- if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
-
- /* copy literals */
- LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
- if ((op+length>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }
- LZ4_wildCopy32(op, ip, op+length);
- ip += length; op += length;
- } else if (ip <= iend-(16 + 1/*max lit + offset + nextToken*/)) {
- /* We don't need to check oend, since we check it once for each loop below */
- DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
- /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */
- LZ4_memcpy(op, ip, 16);
- ip += length; op += length;
- } else {
- goto safe_literal_copy;
- }
-
- /* get offset */
- offset = LZ4_readLE16(ip); ip+=2;
- DEBUGLOG(6, "blockPos%6u: offset = %u", (unsigned)(op-(BYTE*)dst), (unsigned)offset);
- match = op - offset;
- assert(match <= op); /* overflow check */
-
- /* get matchlength */
- length = token & ML_MASK;
- DEBUGLOG(7, " match length token = %u (len==%u)", (unsigned)length, (unsigned)length+MINMATCH);
-
- if (length == ML_MASK) {
- size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
- if (addl == rvl_error) {
- DEBUGLOG(5, "error reading long match length");
- goto _output_error;
- }
- length += addl;
- length += MINMATCH;
- DEBUGLOG(7, " long match length == %u", (unsigned)length);
- if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */
- if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
- goto safe_match_copy;
- }
- } else {
- length += MINMATCH;
- if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
- DEBUGLOG(7, "moving to safe_match_copy (ml==%u)", (unsigned)length);
- goto safe_match_copy;
- }
-
- /* Fastpath check: skip LZ4_wildCopy32 when true */
- if ((dict == withPrefix64k) || (match >= lowPrefix)) {
- if (offset >= 8) {
- assert(match >= lowPrefix);
- assert(match <= op);
- assert(op + 18 <= oend);
-
- LZ4_memcpy(op, match, 8);
- LZ4_memcpy(op+8, match+8, 8);
- LZ4_memcpy(op+16, match+16, 2);
- op += length;
- continue;
- } } }
-
- if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) {
- DEBUGLOG(5, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match);
- goto _output_error;
- }
- /* match starting within external dictionary */
- if ((dict==usingExtDict) && (match < lowPrefix)) {
- assert(dictEnd != NULL);
- if (unlikely(op+length > oend-LASTLITERALS)) {
- if (partialDecoding) {
- DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd");
- length = MIN(length, (size_t)(oend-op));
- } else {
- DEBUGLOG(6, "end-of-block condition violated")
- goto _output_error;
- } }
-
- if (length <= (size_t)(lowPrefix-match)) {
- /* match fits entirely within external dictionary : just copy */
- LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
- op += length;
- } else {
- /* match stretches into both external dictionary and current block */
- size_t const copySize = (size_t)(lowPrefix - match);
- size_t const restSize = length - copySize;
- LZ4_memcpy(op, dictEnd - copySize, copySize);
- op += copySize;
- if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
- BYTE* const endOfMatch = op + restSize;
- const BYTE* copyFrom = lowPrefix;
- while (op < endOfMatch) { *op++ = *copyFrom++; }
- } else {
- LZ4_memcpy(op, lowPrefix, restSize);
- op += restSize;
- } }
- continue;
- }
-
- /* copy match within block */
- cpy = op + length;
-
- assert((op <= oend) && (oend-op >= 32));
- if (unlikely(offset<16)) {
- LZ4_memcpy_using_offset(op, match, cpy, offset);
- } else {
- LZ4_wildCopy32(op, match, cpy);
- }
-
- op = cpy; /* wildcopy correction */
- }
- safe_decode:
-#endif
-
- /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */
- DEBUGLOG(6, "using safe decode loop");
- while (1) {
- assert(ip < iend);
- token = *ip++;
- length = token >> ML_BITS; /* literal length */
- DEBUGLOG(7, "blockPos%6u: litLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
-
- /* A two-stage shortcut for the most common case:
- * 1) If the literal length is 0..14, and there is enough space,
- * enter the shortcut and copy 16 bytes on behalf of the literals
- * (in the fast mode, only 8 bytes can be safely copied this way).
- * 2) Further if the match length is 4..18, copy 18 bytes in a similar
- * manner; but we ensure that there's enough space in the output for
- * those 18 bytes earlier, upon entering the shortcut (in other words,
- * there is a combined check for both stages).
- */
- if ( (length != RUN_MASK)
- /* strictly "less than" on input, to re-enter the loop with at least one byte */
- && likely((ip < shortiend) & (op <= shortoend)) ) {
- /* Copy the literals */
- LZ4_memcpy(op, ip, 16);
- op += length; ip += length;
-
- /* The second stage: prepare for match copying, decode full info.
- * If it doesn't work out, the info won't be wasted. */
- length = token & ML_MASK; /* match length */
- DEBUGLOG(7, "blockPos%6u: matchLength token = %u (len=%u)", (unsigned)(op-(BYTE*)dst), (unsigned)length, (unsigned)length + 4);
- offset = LZ4_readLE16(ip); ip += 2;
- match = op - offset;
- assert(match <= op); /* check overflow */
-
- /* Do not deal with overlapping matches. */
- if ( (length != ML_MASK)
- && (offset >= 8)
- && (dict==withPrefix64k || match >= lowPrefix) ) {
- /* Copy the match. */
- LZ4_memcpy(op + 0, match + 0, 8);
- LZ4_memcpy(op + 8, match + 8, 8);
- LZ4_memcpy(op +16, match +16, 2);
- op += length + MINMATCH;
- /* Both stages worked, load the next token. */
- continue;
- }
-
- /* The second stage didn't work out, but the info is ready.
- * Propel it right to the point of match copying. */
- goto _copy_match;
- }
-
- /* decode literal length */
- if (length == RUN_MASK) {
- size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
- if (addl == rvl_error) { goto _output_error; }
- length += addl;
- if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
- if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
- }
-
-#if LZ4_FAST_DEC_LOOP
- safe_literal_copy:
-#endif
- /* copy literals */
- cpy = op+length;
-
- LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
- if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {
- /* We've either hit the input parsing restriction or the output parsing restriction.
- * In the normal scenario, decoding a full block, it must be the last sequence,
- * otherwise it's an error (invalid input or dimensions).
- * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.
- */
- if (partialDecoding) {
- /* Since we are partial decoding we may be in this block because of the output parsing
- * restriction, which is not valid since the output buffer is allowed to be undersized.
- */
- DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end")
- DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length);
- DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op));
- DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip));
- /* Finishing in the middle of a literals segment,
- * due to lack of input.
- */
- if (ip+length > iend) {
- length = (size_t)(iend-ip);
- cpy = op + length;
- }
- /* Finishing in the middle of a literals segment,
- * due to lack of output space.
- */
- if (cpy > oend) {
- cpy = oend;
- assert(op<=oend);
- length = (size_t)(oend-op);
- }
- } else {
- /* We must be on the last sequence (or invalid) because of the parsing limitations
- * so check that we exactly consume the input and don't overrun the output buffer.
- */
- if ((ip+length != iend) || (cpy > oend)) {
- DEBUGLOG(5, "should have been last run of literals")
- DEBUGLOG(5, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);
- DEBUGLOG(5, "or cpy(%p) > (oend-MFLIMIT)(%p)", cpy, oend-MFLIMIT);
- DEBUGLOG(5, "after writing %u bytes / %i bytes available", (unsigned)(op-(BYTE*)dst), outputSize);
- goto _output_error;
- }
- }
- LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */
- ip += length;
- op += length;
- /* Necessarily EOF when !partialDecoding.
- * When partialDecoding, it is EOF if we've either
- * filled the output buffer or
- * can't proceed with reading an offset for following match.
- */
- if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {
- break;
- }
- } else {
- LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */
- ip += length; op = cpy;
- }
-
- /* get offset */
- offset = LZ4_readLE16(ip); ip+=2;
- match = op - offset;
-
- /* get matchlength */
- length = token & ML_MASK;
- DEBUGLOG(7, "blockPos%6u: matchLength token = %u", (unsigned)(op-(BYTE*)dst), (unsigned)length);
-
- _copy_match:
- if (length == ML_MASK) {
- size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
- if (addl == rvl_error) { goto _output_error; }
- length += addl;
- if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
- }
- length += MINMATCH;
-
-#if LZ4_FAST_DEC_LOOP
- safe_match_copy:
-#endif
- if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */
- /* match starting within external dictionary */
- if ((dict==usingExtDict) && (match < lowPrefix)) {
- assert(dictEnd != NULL);
- if (unlikely(op+length > oend-LASTLITERALS)) {
- if (partialDecoding) length = MIN(length, (size_t)(oend-op));
- else goto _output_error; /* doesn't respect parsing restriction */
- }
-
- if (length <= (size_t)(lowPrefix-match)) {
- /* match fits entirely within external dictionary : just copy */
- LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
- op += length;
- } else {
- /* match stretches into both external dictionary and current block */
- size_t const copySize = (size_t)(lowPrefix - match);
- size_t const restSize = length - copySize;
- LZ4_memcpy(op, dictEnd - copySize, copySize);
- op += copySize;
- if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
- BYTE* const endOfMatch = op + restSize;
- const BYTE* copyFrom = lowPrefix;
- while (op < endOfMatch) *op++ = *copyFrom++;
- } else {
- LZ4_memcpy(op, lowPrefix, restSize);
- op += restSize;
- } }
- continue;
- }
- assert(match >= lowPrefix);
-
- /* copy match within block */
- cpy = op + length;
-
- /* partialDecoding : may end anywhere within the block */
- assert(op<=oend);
- if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
- size_t const mlen = MIN(length, (size_t)(oend-op));
- const BYTE* const matchEnd = match + mlen;
- BYTE* const copyEnd = op + mlen;
- if (matchEnd > op) { /* overlap copy */
- while (op < copyEnd) { *op++ = *match++; }
- } else {
- LZ4_memcpy(op, match, mlen);
- }
- op = copyEnd;
- if (op == oend) { break; }
- continue;
- }
-
- if (unlikely(offset<8)) {
- LZ4_write32(op, 0); /* silence msan warning when offset==0 */
- op[0] = match[0];
- op[1] = match[1];
- op[2] = match[2];
- op[3] = match[3];
- match += inc32table[offset];
- LZ4_memcpy(op+4, match, 4);
- match -= dec64table[offset];
- } else {
- LZ4_memcpy(op, match, 8);
- match += 8;
- }
- op += 8;
-
- if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
- BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);
- if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
- if (op < oCopyLimit) {
- LZ4_wildCopy8(op, match, oCopyLimit);
- match += oCopyLimit - op;
- op = oCopyLimit;
- }
- while (op < cpy) { *op++ = *match++; }
- } else {
- LZ4_memcpy(op, match, 8);
- if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }
- }
- op = cpy; /* wildcopy correction */
- }
-
- /* end of decoding */
- DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));
- return (int) (((char*)op)-dst); /* Nb of output bytes decoded */
-
- /* Overflow error detected */
- _output_error:
- return (int) (-(((const char*)ip)-src))-1;
- }
-}
-
-
-/*===== Instantiate the API decoding functions. =====*/
-
-LZ4_FORCE_O2
-int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
- decode_full_block, noDict,
- (BYTE*)dest, NULL, 0);
-}
-
-LZ4_FORCE_O2
-int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)
-{
- dstCapacity = MIN(targetOutputSize, dstCapacity);
- return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,
- partial_decode,
- noDict, (BYTE*)dst, NULL, 0);
-}
-
-LZ4_FORCE_O2
-int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
-{
- DEBUGLOG(5, "LZ4_decompress_fast");
- return LZ4_decompress_unsafe_generic(
- (const BYTE*)source, (BYTE*)dest, originalSize,
- 0, NULL, 0);
-}
-
-/*===== Instantiate a few more decoding cases, used more than once. =====*/
-
-LZ4_FORCE_O2 /* Exported, an obsolete API function. */
-int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- decode_full_block, withPrefix64k,
- (BYTE*)dest - 64 LZ_KB, NULL, 0);
-}
-
-LZ4_FORCE_O2
-static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)
-{
- dstCapacity = MIN(targetOutputSize, dstCapacity);
- return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
- partial_decode, withPrefix64k,
- (BYTE*)dest - 64 LZ_KB, NULL, 0);
-}
-
-/* Another obsolete API function, paired with the previous one. */
-int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
-{
- return LZ4_decompress_unsafe_generic(
- (const BYTE*)source, (BYTE*)dest, originalSize,
- 64 LZ_KB, NULL, 0);
-}
-
-LZ4_FORCE_O2
-static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,
- size_t prefixSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- decode_full_block, noDict,
- (BYTE*)dest-prefixSize, NULL, 0);
-}
-
-LZ4_FORCE_O2
-static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,
- size_t prefixSize)
-{
- dstCapacity = MIN(targetOutputSize, dstCapacity);
- return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
- partial_decode, noDict,
- (BYTE*)dest-prefixSize, NULL, 0);
-}
-
-LZ4_FORCE_O2
-int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
- int compressedSize, int maxOutputSize,
- const void* dictStart, size_t dictSize)
-{
- DEBUGLOG(5, "LZ4_decompress_safe_forceExtDict");
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- decode_full_block, usingExtDict,
- (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-LZ4_FORCE_O2
-int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
- int compressedSize, int targetOutputSize, int dstCapacity,
- const void* dictStart, size_t dictSize)
-{
- dstCapacity = MIN(targetOutputSize, dstCapacity);
- return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
- partial_decode, usingExtDict,
- (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-LZ4_FORCE_O2
-static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,
- const void* dictStart, size_t dictSize)
-{
- return LZ4_decompress_unsafe_generic(
- (const BYTE*)source, (BYTE*)dest, originalSize,
- 0, (const BYTE*)dictStart, dictSize);
-}
-
-/* The "double dictionary" mode, for use with e.g. ring buffers: the first part
- * of the dictionary is passed as prefix, and the second via dictStart + dictSize.
- * These routines are used only once, in LZ4_decompress_*_continue().
- */
-LZ4_FORCE_INLINE
-int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,
- size_t prefixSize, const void* dictStart, size_t dictSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- decode_full_block, usingExtDict,
- (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
-}
-
-/*===== streaming decompression functions =====*/
-
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-LZ4_streamDecode_t* LZ4_createStreamDecode(void)
-{
- LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));
- return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
-}
-
-int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
-{
- if (LZ4_stream == NULL) { return 0; } /* support free on NULL */
- FREEMEM(LZ4_stream);
- return 0;
-}
-#endif
-
-/*! LZ4_setStreamDecode() :
- * Use this function to instruct where to find the dictionary.
- * This function is not necessary if previous data is still available where it was decoded.
- * Loading a size of 0 is allowed (same effect as no dictionary).
- * @return : 1 if OK, 0 if error
- */
-int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
- lz4sd->prefixSize = (size_t)dictSize;
- if (dictSize) {
- assert(dictionary != NULL);
- lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
- } else {
- lz4sd->prefixEnd = (const BYTE*) dictionary;
- }
- lz4sd->externalDict = NULL;
- lz4sd->extDictSize = 0;
- return 1;
-}
-
-/*! LZ4_decoderRingBufferSize() :
- * when setting a ring buffer for streaming decompression (optional scenario),
- * provides the minimum size of this ring buffer
- * to be compatible with any source respecting maxBlockSize condition.
- * Note : in a ring buffer scenario,
- * blocks are presumed decompressed next to each other.
- * When not enough space remains for next block (remainingSize < maxBlockSize),
- * decoding resumes from beginning of ring buffer.
- * @return : minimum ring buffer size,
- * or 0 if there is an error (invalid maxBlockSize).
- */
-int LZ4_decoderRingBufferSize(int maxBlockSize)
-{
- if (maxBlockSize < 0) return 0;
- if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;
- if (maxBlockSize < 16) maxBlockSize = 16;
- return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);
-}
-
-/*
-*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks must still be available at the memory position where they were decoded.
- If it's not possible, save the relevant part of decoded data into a safe buffer,
- and indicate where it stands using LZ4_setStreamDecode()
-*/
-LZ4_FORCE_O2
-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
- int result;
-
- if (lz4sd->prefixSize == 0) {
- /* The first call, no dictionary yet. */
- assert(lz4sd->extDictSize == 0);
- result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = (size_t)result;
- lz4sd->prefixEnd = (BYTE*)dest + result;
- } else if (lz4sd->prefixEnd == (BYTE*)dest) {
- /* They're rolling the current segment. */
- if (lz4sd->prefixSize >= 64 LZ_KB - 1)
- result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
- else if (lz4sd->extDictSize == 0)
- result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,
- lz4sd->prefixSize);
- else
- result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,
- lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += (size_t)result;
- lz4sd->prefixEnd += result;
- } else {
- /* The buffer wraps around, or they're switching to another buffer. */
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,
- lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = (size_t)result;
- lz4sd->prefixEnd = (BYTE*)dest + result;
- }
-
- return result;
-}
-
-LZ4_FORCE_O2 int
-LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,
- const char* source, char* dest, int originalSize)
-{
- LZ4_streamDecode_t_internal* const lz4sd =
- (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);
- int result;
-
- DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize);
- assert(originalSize >= 0);
-
- if (lz4sd->prefixSize == 0) {
- DEBUGLOG(5, "first invocation : no prefix nor extDict");
- assert(lz4sd->extDictSize == 0);
- result = LZ4_decompress_fast(source, dest, originalSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = (size_t)originalSize;
- lz4sd->prefixEnd = (BYTE*)dest + originalSize;
- } else if (lz4sd->prefixEnd == (BYTE*)dest) {
- DEBUGLOG(5, "continue using existing prefix");
- result = LZ4_decompress_unsafe_generic(
- (const BYTE*)source, (BYTE*)dest, originalSize,
- lz4sd->prefixSize,
- lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += (size_t)originalSize;
- lz4sd->prefixEnd += originalSize;
- } else {
- DEBUGLOG(5, "prefix becomes extDict");
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_fast_extDict(source, dest, originalSize,
- lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = (size_t)originalSize;
- lz4sd->prefixEnd = (BYTE*)dest + originalSize;
- }
-
- return result;
-}
-
-
-/*
-Advanced decoding functions :
-*_usingDict() :
- These decoding functions work the same as "_continue" ones,
- the dictionary must be explicitly provided within parameters
-*/
-
-int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- if (dictSize==0)
- return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
- if (dictStart+dictSize == dest) {
- if (dictSize >= 64 LZ_KB - 1) {
- return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
- }
- assert(dictSize >= 0);
- return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);
- }
- assert(dictSize >= 0);
- return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);
-}
-
-int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)
-{
- if (dictSize==0)
- return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);
- if (dictStart+dictSize == dest) {
- if (dictSize >= 64 LZ_KB - 1) {
- return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);
- }
- assert(dictSize >= 0);
- return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);
- }
- assert(dictSize >= 0);
- return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);
-}
-
-int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
-{
- if (dictSize==0 || dictStart+dictSize == dest)
- return LZ4_decompress_unsafe_generic(
- (const BYTE*)source, (BYTE*)dest, originalSize,
- (size_t)dictSize, NULL, 0);
- assert(dictSize >= 0);
- return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);
-}
-
-
-/*=*************************************************
-* Obsolete Functions
-***************************************************/
-/* obsolete compression functions */
-int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)
-{
- return LZ4_compress_default(source, dest, inputSize, maxOutputSize);
-}
-int LZ4_compress(const char* src, char* dest, int srcSize)
-{
- return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));
-}
-int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)
-{
- return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);
-}
-int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)
-{
- return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);
-}
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)
-{
- return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);
-}
-int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)
-{
- return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);
-}
-
-/*
-These decompression functions are deprecated and should no longer be used.
-They are only provided here for compatibility with older user programs.
-- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
-- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
-*/
-int LZ4_uncompress (const char* source, char* dest, int outputSize)
-{
- return LZ4_decompress_fast(source, dest, outputSize);
-}
-int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)
-{
- return LZ4_decompress_safe(source, dest, isize, maxOutputSize);
-}
-
-/* Obsolete Streaming functions */
-
-int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }
-
-int LZ4_resetStreamState(void* state, char* inputBuffer)
-{
- (void)inputBuffer;
- LZ4_resetStream((LZ4_stream_t*)state);
- return 0;
-}
-
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-void* LZ4_create (char* inputBuffer)
-{
- (void)inputBuffer;
- return LZ4_createStream();
-}
-#endif
-
-char* LZ4_slideInputBuffer (void* state)
-{
- /* avoid const char * -> char * conversion warning */
- return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
-}
-
-#endif /* LZ4_COMMONDEFS_ONLY */
\ No newline at end of file
diff --git a/lz4/lz4.h b/lz4/lz4.h
deleted file mode 100644
index 8b7482d..0000000
--- a/lz4/lz4.h
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * LZ4 - Fast LZ compression algorithm
- * Header File
- * Copyright (C) 2011-2023, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following disclaimer
- in the documentation and/or other materials provided with the
- distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- You can contact the author at :
- - LZ4 homepage : http://www.lz4.org
- - LZ4 source repository : https://github.com/lz4/lz4
-*/
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-#ifndef LZ4_H_2983827168210
-#define LZ4_H_2983827168210
-
-/* --- Dependency --- */
-#include /* size_t */
-
-
-/**
- Introduction
-
- LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
- scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
- multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
-
- The LZ4 compression library provides in-memory compression and decompression functions.
- It gives full buffer control to user.
- Compression can be done in:
- - a single step (described as Simple Functions)
- - a single step, reusing a context (described in Advanced Functions)
- - unbounded multiple steps (described as Streaming compression)
-
- lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
- Decompressing such a compressed block requires additional metadata.
- Exact metadata depends on exact decompression function.
- For the typical case of LZ4_decompress_safe(),
- metadata includes block's compressed size, and maximum bound of decompressed size.
- Each application is free to encode and pass such metadata in whichever way it wants.
-
- lz4.h only handle blocks, it can not generate Frames.
-
- Blocks are different from Frames (doc/lz4_Frame_format.md).
- Frames bundle both blocks and metadata in a specified manner.
- Embedding metadata is required for compressed data to be self-contained and portable.
- Frame format is delivered through a companion API, declared in lz4frame.h.
- The `lz4` CLI can only manage frames.
-*/
-
-/*^***************************************************************
-* Export parameters
-*****************************************************************/
-/*
-* LZ4_DLL_EXPORT :
-* Enable exporting of functions when building a Windows DLL
-* LZ4LIB_VISIBILITY :
-* Control library symbols visibility.
-*/
-#ifndef LZ4LIB_VISIBILITY
-# if defined(__GNUC__) && (__GNUC__ >= 4)
-# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
-# else
-# define LZ4LIB_VISIBILITY
-# endif
-#endif
-#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
-# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
-#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
-# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
-#else
-# define LZ4LIB_API LZ4LIB_VISIBILITY
-#endif
-
-/*! LZ4_FREESTANDING :
- * When this macro is set to 1, it enables "freestanding mode" that is
- * suitable for typical freestanding environment which doesn't support
- * standard C library.
- *
- * - LZ4_FREESTANDING is a compile-time switch.
- * - It requires the following macros to be defined:
- * LZ4_memcpy, LZ4_memmove, LZ4_memset.
- * - It only enables LZ4/HC functions which don't use heap.
- * All LZ4F_* functions are not supported.
- * - See tests/freestanding.c to check its basic setup.
- */
-#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
-# define LZ4_HEAPMODE 0
-# define LZ4HC_HEAPMODE 0
-# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
-# if !defined(LZ4_memcpy)
-# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
-# endif
-# if !defined(LZ4_memset)
-# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
-# endif
-# if !defined(LZ4_memmove)
-# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
-# endif
-#elif ! defined(LZ4_FREESTANDING)
-# define LZ4_FREESTANDING 0
-#endif
-
-
-/*------ Version ------*/
-#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
-#define LZ4_VERSION_MINOR 10 /* for new (non-breaking) interface capabilities */
-#define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */
-
-#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
-
-#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
-#define LZ4_QUOTE(str) #str
-#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
-#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
-
-LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
-LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
-
-
-/*-************************************
-* Tuning memory usage
-**************************************/
-/*!
- * LZ4_MEMORY_USAGE :
- * Can be selected at compile time, by setting LZ4_MEMORY_USAGE.
- * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB)
- * Increasing memory usage improves compression ratio, generally at the cost of speed.
- * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
- * Default value is 14, for 16KB, which nicely fits into most L1 caches.
- */
-#ifndef LZ4_MEMORY_USAGE
-# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
-#endif
-
-/* These are absolute limits, they should not be changed by users */
-#define LZ4_MEMORY_USAGE_MIN 10
-#define LZ4_MEMORY_USAGE_DEFAULT 14
-#define LZ4_MEMORY_USAGE_MAX 20
-
-#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
-# error "LZ4_MEMORY_USAGE is too small !"
-#endif
-
-#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
-# error "LZ4_MEMORY_USAGE is too large !"
-#endif
-
-/*-************************************
-* Simple Functions
-**************************************/
-/*! LZ4_compress_default() :
- * Compresses 'srcSize' bytes from buffer 'src'
- * into already allocated 'dst' buffer of size 'dstCapacity'.
- * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
- * It also runs faster, so it's a recommended setting.
- * If the function cannot compress 'src' into a more limited 'dst' budget,
- * compression stops *immediately*, and the function result is zero.
- * In which case, 'dst' content is undefined (invalid).
- * srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
- * dstCapacity : size of buffer 'dst' (which must be already allocated)
- * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
- * or 0 if compression fails
- * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
- */
-LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
-
-/*! LZ4_decompress_safe() :
- * @compressedSize : is the exact complete size of the compressed block.
- * @dstCapacity : is the size of destination buffer (which must be already allocated),
- * presumed an upper bound of decompressed size.
- * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
- * If destination buffer is not large enough, decoding will stop and output an error code (negative value).
- * If the source stream is detected malformed, the function will stop decoding and return a negative result.
- * Note 1 : This function is protected against malicious data packets :
- * it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
- * even if the compressed block is maliciously modified to order the decoder to do these actions.
- * In such case, the decoder stops immediately, and considers the compressed block malformed.
- * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
- * The implementation is free to send / store / derive this information in whichever way is most beneficial.
- * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
- */
-LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
-
-
-/*-************************************
-* Advanced Functions
-**************************************/
-#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
-#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
-
-/*! LZ4_compressBound() :
- Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
- This function is primarily useful for memory allocation purposes (destination buffer size).
- Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
- Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
- inputSize : max supported value is LZ4_MAX_INPUT_SIZE
- return : maximum output size in a "worst case" scenario
- or 0, if input size is incorrect (too large or negative)
-*/
-LZ4LIB_API int LZ4_compressBound(int inputSize);
-
-/*! LZ4_compress_fast() :
- Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
- The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
- It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
- An acceleration value of "1" is the same as regular LZ4_compress_default()
- Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
- Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
-*/
-LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
-
-
-/*! LZ4_compress_fast_extState() :
- * Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
- * Use LZ4_sizeofState() to know how much memory must be allocated,
- * and allocate it on 8-bytes boundaries (using `malloc()` typically).
- * Then, provide this buffer as `void* state` to compression function.
- */
-LZ4LIB_API int LZ4_sizeofState(void);
-LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
-
-/*! LZ4_compress_destSize() :
- * Reverse the logic : compresses as much data as possible from 'src' buffer
- * into already allocated buffer 'dst', of size >= 'dstCapacity'.
- * This function either compresses the entire 'src' content into 'dst' if it's large enough,
- * or fill 'dst' buffer completely with as much data as possible from 'src'.
- * note: acceleration parameter is fixed to "default".
- *
- * *srcSizePtr : in+out parameter. Initially contains size of input.
- * Will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
- * New value is necessarily <= input value.
- * @return : Nb bytes written into 'dst' (necessarily <= dstCapacity)
- * or 0 if compression fails.
- *
- * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
- * the produced compressed content could, in specific circumstances,
- * require to be decompressed into a destination buffer larger
- * by at least 1 byte than the content to decompress.
- * If an application uses `LZ4_compress_destSize()`,
- * it's highly recommended to update liblz4 to v1.9.2 or better.
- * If this can't be done or ensured,
- * the receiving decompression function should provide
- * a dstCapacity which is > decompressedSize, by at least 1 byte.
- * See https://github.com/lz4/lz4/issues/859 for details
- */
-LZ4LIB_API int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize);
-
-/*! LZ4_decompress_safe_partial() :
- * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
- * into destination buffer 'dst' of size 'dstCapacity'.
- * Up to 'targetOutputSize' bytes will be decoded.
- * The function stops decoding on reaching this objective.
- * This can be useful to boost performance
- * whenever only the beginning of a block is required.
- *
- * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
- * If source stream is detected malformed, function returns a negative result.
- *
- * Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
- *
- * Note 2 : targetOutputSize must be <= dstCapacity
- *
- * Note 3 : this function effectively stops decoding on reaching targetOutputSize,
- * so dstCapacity is kind of redundant.
- * This is because in older versions of this function,
- * decoding operation would still write complete sequences.
- * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
- * it could write more bytes, though only up to dstCapacity.
- * Some "margin" used to be required for this operation to work properly.
- * Thankfully, this is no longer necessary.
- * The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
- *
- * Note 4 : If srcSize is the exact size of the block,
- * then targetOutputSize can be any value,
- * including larger than the block's decompressed size.
- * The function will, at most, generate block's decompressed size.
- *
- * Note 5 : If srcSize is _larger_ than block's compressed size,
- * then targetOutputSize **MUST** be <= block's decompressed size.
- * Otherwise, *silent corruption will occur*.
- */
-LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
-
-
-/*-*********************************************
-* Streaming Compression Functions
-***********************************************/
-typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
-
-/*!
- Note about RC_INVOKED
-
- - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
- https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
-
- - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
- and reports warning "RC4011: identifier truncated".
-
- - To eliminate the warning, we surround long preprocessor symbol with
- "#if !defined(RC_INVOKED) ... #endif" block that means
- "skip this block when rc.exe is trying to read it".
-*/
-#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
-LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
-#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
-#endif
-
-/*! LZ4_resetStream_fast() : v1.9.0+
- * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
- * (e.g., LZ4_compress_fast_continue()).
- *
- * An LZ4_stream_t must be initialized once before usage.
- * This is automatically done when created by LZ4_createStream().
- * However, should the LZ4_stream_t be simply declared on stack (for example),
- * it's necessary to initialize it first, using LZ4_initStream().
- *
- * After init, start any new stream with LZ4_resetStream_fast().
- * A same LZ4_stream_t can be re-used multiple times consecutively
- * and compress multiple streams,
- * provided that it starts each new stream with LZ4_resetStream_fast().
- *
- * LZ4_resetStream_fast() is much faster than LZ4_initStream(),
- * but is not compatible with memory regions containing garbage data.
- *
- * Note: it's only useful to call LZ4_resetStream_fast()
- * in the context of streaming compression.
- * The *extState* functions perform their own resets.
- * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
- */
-LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
-
-/*! LZ4_loadDict() :
- * Use this function to reference a static dictionary into LZ4_stream_t.
- * The dictionary must remain available during compression.
- * LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
- * The same dictionary will have to be loaded on decompression side for successful decoding.
- * Dictionary are useful for better compression of small data (KB range).
- * While LZ4 itself accepts any input as dictionary, dictionary efficiency is also a topic.
- * When in doubt, employ the Zstandard's Dictionary Builder.
- * Loading a size of 0 is allowed, and is the same as reset.
- * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
- */
-LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
-
-/*! LZ4_loadDictSlow() : v1.10.0+
- * Same as LZ4_loadDict(),
- * but uses a bit more cpu to reference the dictionary content more thoroughly.
- * This is expected to slightly improve compression ratio.
- * The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions.
- * @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
- */
-LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
-
-/*! LZ4_attach_dictionary() : stable since v1.10.0
- *
- * This allows efficient re-use of a static dictionary multiple times.
- *
- * Rather than re-loading the dictionary buffer into a working context before
- * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
- * working LZ4_stream_t, this function introduces a no-copy setup mechanism,
- * in which the working stream references @dictionaryStream in-place.
- *
- * Several assumptions are made about the state of @dictionaryStream.
- * Currently, only states which have been prepared by LZ4_loadDict() or
- * LZ4_loadDictSlow() should be expected to work.
- *
- * Alternatively, the provided @dictionaryStream may be NULL,
- * in which case any existing dictionary stream is unset.
- *
- * If a dictionary is provided, it replaces any pre-existing stream history.
- * The dictionary contents are the only history that can be referenced and
- * logically immediately precede the data compressed in the first subsequent
- * compression call.
- *
- * The dictionary will only remain attached to the working stream through the
- * first compression call, at the end of which it is cleared.
- * @dictionaryStream stream (and source buffer) must remain in-place / accessible / unchanged
- * through the completion of the compression session.
- *
- * Note: there is no equivalent LZ4_attach_*() method on the decompression side
- * because there is no initialization cost, hence no need to share the cost across multiple sessions.
- * To decompress LZ4 blocks using dictionary, attached or not,
- * just employ the regular LZ4_setStreamDecode() for streaming,
- * or the stateless LZ4_decompress_safe_usingDict() for one-shot decompression.
- */
-LZ4LIB_API void
-LZ4_attach_dictionary(LZ4_stream_t* workingStream,
- const LZ4_stream_t* dictionaryStream);
-
-/*! LZ4_compress_fast_continue() :
- * Compress 'src' content using data from previously compressed blocks, for better compression ratio.
- * 'dst' buffer must be already allocated.
- * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- *
- * @return : size of compressed block
- * or 0 if there is an error (typically, cannot fit into 'dst').
- *
- * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
- * Each block has precise boundaries.
- * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
- * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
- *
- * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
- *
- * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
- * Make sure that buffers are separated, by at least one byte.
- * This construction ensures that each block only depends on previous block.
- *
- * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
- *
- * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
- */
-LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
-
-/*! LZ4_saveDict() :
- * If last 64KB data cannot be guaranteed to remain available at its current memory location,
- * save it into a safer place (char* safeBuffer).
- * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
- * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
- * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
- */
-LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
-
-
-/*-**********************************************
-* Streaming Decompression Functions
-* Bufferless synchronous API
-************************************************/
-typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
-
-/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
- * creation / destruction of streaming decompression tracking context.
- * A tracking context can be re-used multiple times.
- */
-#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
-#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
-LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
-LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
-#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
-#endif
-
-/*! LZ4_setStreamDecode() :
- * An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
- * Use this function to start decompression of a new stream of blocks.
- * A dictionary can optionally be set. Use NULL or size 0 for a reset order.
- * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
- * @return : 1 if OK, 0 if error
- */
-LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
-
-/*! LZ4_decoderRingBufferSize() : v1.8.2+
- * Note : in a ring buffer scenario (optional),
- * blocks are presumed decompressed next to each other
- * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
- * at which stage it resumes from beginning of ring buffer.
- * When setting such a ring buffer for streaming decompression,
- * provides the minimum size of this ring buffer
- * to be compatible with any source respecting maxBlockSize condition.
- * @return : minimum ring buffer size,
- * or 0 if there is an error (invalid maxBlockSize).
- */
-LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
-#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
-
-/*! LZ4_decompress_safe_continue() :
- * This decoding function allows decompression of consecutive blocks in "streaming" mode.
- * The difference with the usual independent blocks is that
- * new blocks are allowed to find references into former blocks.
- * A block is an unsplittable entity, and must be presented entirely to the decompression function.
- * LZ4_decompress_safe_continue() only accepts one block at a time.
- * It's modeled after `LZ4_decompress_safe()` and behaves similarly.
- *
- * @LZ4_streamDecode : decompression state, tracking the position in memory of past data
- * @compressedSize : exact complete size of one compressed block.
- * @dstCapacity : size of destination buffer (which must be already allocated),
- * must be an upper bound of decompressed size.
- * @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
- * If destination buffer is not large enough, decoding will stop and output an error code (negative value).
- * If the source stream is detected malformed, the function will stop decoding and return a negative result.
- *
- * The last 64KB of previously decoded data *must* remain available and unmodified
- * at the memory position where they were previously decoded.
- * If less than 64KB of data has been decoded, all the data must be present.
- *
- * Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
- * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
- * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
- * In which case, encoding and decoding buffers do not need to be synchronized.
- * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
- * - Synchronized mode :
- * Decompression buffer size is _exactly_ the same as compression buffer size,
- * and follows exactly same update rule (block boundaries at same positions),
- * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
- * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
- * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
- * In which case, encoding and decoding buffers do not need to be synchronized,
- * and encoding ring buffer can have any size, including small ones ( < 64 KB).
- *
- * Whenever these conditions are not possible,
- * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
- * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
-*/
-LZ4LIB_API int
-LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
- const char* src, char* dst,
- int srcSize, int dstCapacity);
-
-
-/*! LZ4_decompress_safe_usingDict() :
- * Works the same as
- * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()
- * However, it's stateless: it doesn't need any LZ4_streamDecode_t state.
- * Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
- * Performance tip : Decompression speed can be substantially increased
- * when dst == dictStart + dictSize.
- */
-LZ4LIB_API int
-LZ4_decompress_safe_usingDict(const char* src, char* dst,
- int srcSize, int dstCapacity,
- const char* dictStart, int dictSize);
-
-/*! LZ4_decompress_safe_partial_usingDict() :
- * Behaves the same as LZ4_decompress_safe_partial()
- * with the added ability to specify a memory segment for past data.
- * Performance tip : Decompression speed can be substantially increased
- * when dst == dictStart + dictSize.
- */
-LZ4LIB_API int
-LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
- int compressedSize,
- int targetOutputSize, int maxOutputSize,
- const char* dictStart, int dictSize);
-
-#endif /* LZ4_H_2983827168210 */
-
-
-/*^*************************************
- * !!!!!! STATIC LINKING ONLY !!!!!!
- ***************************************/
-
-/*-****************************************************************************
- * Experimental section
- *
- * Symbols declared in this section must be considered unstable. Their
- * signatures or semantics may change, or they may be removed altogether in the
- * future. They are therefore only safe to depend on when the caller is
- * statically linked against the library.
- *
- * To protect against unsafe usage, not only are the declarations guarded,
- * the definitions are hidden by default
- * when building LZ4 as a shared/dynamic library.
- *
- * In order to access these declarations,
- * define LZ4_STATIC_LINKING_ONLY in your application
- * before including LZ4's headers.
- *
- * In order to make their implementations accessible dynamically, you must
- * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
- ******************************************************************************/
-
-#ifdef LZ4_STATIC_LINKING_ONLY
-
-#ifndef LZ4_STATIC_3504398509
-#define LZ4_STATIC_3504398509
-
-#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
-# define LZ4LIB_STATIC_API LZ4LIB_API
-#else
-# define LZ4LIB_STATIC_API
-#endif
-
-
-/*! LZ4_compress_fast_extState_fastReset() :
- * A variant of LZ4_compress_fast_extState().
- *
- * Using this variant avoids an expensive initialization step.
- * It is only safe to call if the state buffer is known to be correctly initialized already
- * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
- * From a high level, the difference is that
- * this function initializes the provided state with a call to something like LZ4_resetStream_fast()
- * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
- */
-LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
-
-/*! LZ4_compress_destSize_extState() : introduced in v1.10.0
- * Same as LZ4_compress_destSize(), but using an externally allocated state.
- * Also: exposes @acceleration
- */
-int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration);
-
-/*! In-place compression and decompression
- *
- * It's possible to have input and output sharing the same buffer,
- * for highly constrained memory environments.
- * In both cases, it requires input to lay at the end of the buffer,
- * and decompression to start at beginning of the buffer.
- * Buffer size must feature some margin, hence be larger than final size.
- *
- * |<------------------------buffer--------------------------------->|
- * |<-----------compressed data--------->|
- * |<-----------decompressed size------------------>|
- * |<----margin---->|
- *
- * This technique is more useful for decompression,
- * since decompressed size is typically larger,
- * and margin is short.
- *
- * In-place decompression will work inside any buffer
- * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
- * This presumes that decompressedSize > compressedSize.
- * Otherwise, it means compression actually expanded data,
- * and it would be more efficient to store such data with a flag indicating it's not compressed.
- * This can happen when data is not compressible (already compressed, or encrypted).
- *
- * For in-place compression, margin is larger, as it must be able to cope with both
- * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
- * and data expansion, which can happen when input is not compressible.
- * As a consequence, buffer size requirements are much higher,
- * and memory savings offered by in-place compression are more limited.
- *
- * There are ways to limit this cost for compression :
- * - Reduce history size, by modifying LZ4_DISTANCE_MAX.
- * Note that it is a compile-time constant, so all compressions will apply this limit.
- * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
- * so it's a reasonable trick when inputs are known to be small.
- * - Require the compressor to deliver a "maximum compressed size".
- * This is the `dstCapacity` parameter in `LZ4_compress*()`.
- * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
- * in which case, the return code will be 0 (zero).
- * The caller must be ready for these cases to happen,
- * and typically design a backup scheme to send data uncompressed.
- * The combination of both techniques can significantly reduce
- * the amount of margin required for in-place compression.
- *
- * In-place compression can work in any buffer
- * which size is >= (maxCompressedSize)
- * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
- * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
- * so it's possible to reduce memory requirements by playing with them.
- */
-
-#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
-#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
-
-#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
-# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
-#endif
-
-#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
-#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
-
-#endif /* LZ4_STATIC_3504398509 */
-#endif /* LZ4_STATIC_LINKING_ONLY */
-
-
-
-#ifndef LZ4_H_98237428734687
-#define LZ4_H_98237428734687
-
-/*-************************************************************
- * Private Definitions
- **************************************************************
- * Do not use these definitions directly.
- * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
- * Accessing members will expose user code to API and/or ABI break in future versions of the library.
- **************************************************************/
-#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
-#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
-#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
-
-#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-# include
- typedef int8_t LZ4_i8;
- typedef uint8_t LZ4_byte;
- typedef uint16_t LZ4_u16;
- typedef uint32_t LZ4_u32;
-#else
- typedef signed char LZ4_i8;
- typedef unsigned char LZ4_byte;
- typedef unsigned short LZ4_u16;
- typedef unsigned int LZ4_u32;
-#endif
-
-/*! LZ4_stream_t :
- * Never ever use below internal definitions directly !
- * These definitions are not API/ABI safe, and may change in future versions.
- * If you need static allocation, declare or allocate an LZ4_stream_t object.
-**/
-
-typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
-struct LZ4_stream_t_internal {
- LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
- const LZ4_byte* dictionary;
- const LZ4_stream_t_internal* dictCtx;
- LZ4_u32 currentOffset;
- LZ4_u32 tableType;
- LZ4_u32 dictSize;
- /* Implicit padding to ensure structure is aligned */
-};
-
-#define LZ4_STREAM_MINSIZE ((1UL << (LZ4_MEMORY_USAGE)) + 32) /* static size, for inter-version compatibility */
-union LZ4_stream_u {
- char minStateSize[LZ4_STREAM_MINSIZE];
- LZ4_stream_t_internal internal_donotuse;
-}; /* previously typedef'd to LZ4_stream_t */
-
-
-/*! LZ4_initStream() : v1.9.0+
- * An LZ4_stream_t structure must be initialized at least once.
- * This is automatically done when invoking LZ4_createStream(),
- * but it's not when the structure is simply declared on stack (for example).
- *
- * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
- * It can also initialize any arbitrary buffer of sufficient size,
- * and will @return a pointer of proper type upon initialization.
- *
- * Note : initialization fails if size and alignment conditions are not respected.
- * In which case, the function will @return NULL.
- * Note2: An LZ4_stream_t structure guarantees correct alignment and size.
- * Note3: Before v1.9.0, use LZ4_resetStream() instead
-**/
-LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* stateBuffer, size_t size);
-
-
-/*! LZ4_streamDecode_t :
- * Never ever use below internal definitions directly !
- * These definitions are not API/ABI safe, and may change in future versions.
- * If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
-**/
-typedef struct {
- const LZ4_byte* externalDict;
- const LZ4_byte* prefixEnd;
- size_t extDictSize;
- size_t prefixSize;
-} LZ4_streamDecode_t_internal;
-
-#define LZ4_STREAMDECODE_MINSIZE 32
-union LZ4_streamDecode_u {
- char minStateSize[LZ4_STREAMDECODE_MINSIZE];
- LZ4_streamDecode_t_internal internal_donotuse;
-} ; /* previously typedef'd to LZ4_streamDecode_t */
-
-
-
-/*-************************************
-* Obsolete Functions
-**************************************/
-
-/*! Deprecation warnings
- *
- * Deprecated functions make the compiler generate a warning when invoked.
- * This is meant to invite users to update their source code.
- * Should deprecation warnings be a problem, it is generally possible to disable them,
- * typically with -Wno-deprecated-declarations for gcc
- * or _CRT_SECURE_NO_WARNINGS in Visual.
- *
- * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
- * before including the header file.
- */
-#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
-# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
-#else
-# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
-# define LZ4_DEPRECATED(message) [[deprecated(message)]]
-# elif defined(_MSC_VER)
-# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
-# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
-# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
-# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
-# define LZ4_DEPRECATED(message) __attribute__((deprecated))
-# else
-# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
-# define LZ4_DEPRECATED(message) /* disabled */
-# endif
-#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
-
-/*! Obsolete compression functions (since v1.7.3) */
-LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
-LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
-LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
-
-/*! Obsolete decompression functions (since v1.8.0) */
-LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
-LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
-
-/* Obsolete streaming functions (since v1.7.0)
- * degraded functionality; do not use!
- *
- * In order to perform streaming compression, these functions depended on data
- * that is no longer tracked in the state. They have been preserved as well as
- * possible: using them will still produce a correct output. However, they don't
- * actually retain any history between compression calls. The compression ratio
- * achieved will therefore be no better than compressing each chunk
- * independently.
- */
-LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
-LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
-LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
-LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
-
-/*! Obsolete streaming decoding functions (since v1.7.0) */
-LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
-LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
-
-/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
- * These functions used to be faster than LZ4_decompress_safe(),
- * but this is no longer the case. They are now slower.
- * This is because LZ4_decompress_fast() doesn't know the input size,
- * and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
- * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
- * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
- *
- * The last remaining LZ4_decompress_fast() specificity is that
- * it can decompress a block without knowing its compressed size.
- * Such functionality can be achieved in a more secure manner
- * by employing LZ4_decompress_safe_partial().
- *
- * Parameters:
- * originalSize : is the uncompressed size to regenerate.
- * `dst` must be already allocated, its size must be >= 'originalSize' bytes.
- * @return : number of bytes read from source buffer (== compressed size).
- * The function expects to finish at block's end exactly.
- * If the source stream is detected malformed, the function stops decoding and returns a negative result.
- * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
- * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
- * Also, since match offsets are not validated, match reads from 'src' may underflow too.
- * These issues never happen if input (compressed) data is correct.
- * But they may happen if input data is invalid (error or intentional tampering).
- * As a consequence, use these functions in trusted environments with trusted data **only**.
- */
-LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial() instead")
-LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
-LZ4_DEPRECATED("This function is deprecated and unsafe. Consider migrating towards LZ4_decompress_safe_continue() instead. "
- "Note that the contract will change (requires block's compressed size, instead of decompressed size)")
-LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
-LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial_usingDict() instead")
-LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
-
-/*! LZ4_resetStream() :
- * An LZ4_stream_t structure must be initialized at least once.
- * This is done with LZ4_initStream(), or LZ4_resetStream().
- * Consider switching to LZ4_initStream(),
- * invoking LZ4_resetStream() will trigger deprecation warnings in the future.
- */
-LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
-
-
-#endif /* LZ4_H_98237428734687 */
-
-
-#if defined (__cplusplus)
-}
-#endif
\ No newline at end of file
diff --git a/markdown.c b/markdown.c
deleted file mode 100644
index ed8d0ab..0000000
--- a/markdown.c
+++ /dev/null
@@ -1,504 +0,0 @@
-#include "markdown.h"
-
-#include "arena.h"
-#include "str.h"
-#include "strstream.h"
-#include "file.h"
-#include "ini.h"
-#include "tracelog.h"
-
-#ifndef MD_LIST_MAX_DEPTH
- #define MD_LIST_MAX_DEPTH 8
-#endif
-
-typedef struct {
- struct {
- int indent;
- int count;
- bool list_is_ordered[MD_LIST_MAX_DEPTH];
- } list;
- struct {
- bool is_in_block;
- strview_t lang;
- } code;
- bool is_bold;
- bool is_italic;
- bool is_in_paragraph;
- strview_t raw_line;
- md_options_t *options;
- md_parser_t *curparser;
-} markdown_ctx_t;
-
-static void markdown__parse_config(arena_t *arena, instream_t *in, ini_t *out);
-static int markdown__count_chars(strview_t *line, char c);
-static void markdown__parse_line(markdown_ctx_t *md, strview_t line, outstream_t *out, bool add_newline, bool is_line_start);
-static strview_t markdown__parse_header(markdown_ctx_t *md, strview_t line, outstream_t *out);
-static strview_t markdown__parse_ulist_or_line(markdown_ctx_t *md, strview_t line, outstream_t *out);
-static strview_t markdown__parse_olist(markdown_ctx_t *md, strview_t line, outstream_t *out);
-static strview_t markdown__parse_code_block(markdown_ctx_t *md, strview_t line, outstream_t *out);
-static bool markdown__try_parse_url(instream_t *in, strview_t *out_url, strview_t *out_text);
-static void markdown__empty_line(markdown_ctx_t *md, outstream_t *out);
-static void markdown__close_list(markdown_ctx_t *md, outstream_t *out);
-static void markdown__escape(strview_t view, outstream_t *out);
-
-str_t markdown(arena_t *arena, arena_t scratch, strview_t filename, md_options_t *options) {
- str_t text = fileReadWholeStr(&scratch, filename);
- return markdownStr(arena, strv(text), options);
-}
-
-str_t markdownStr(arena_t *arena, strview_t markdown_str, md_options_t *options) {
- instream_t in = istrInitLen(markdown_str.buf, markdown_str.len);
-
- markdown__parse_config(arena, &in, options ? options->out_config : NULL);
-
- outstream_t out = ostrInit(arena);
-
- markdown_ctx_t md = {
- .list = {
- .indent = -1,
- },
- .options = options,
- };
-
- while (!istrIsFinished(in)) {
- md.raw_line = istrGetLine(&in);
- markdown__parse_line(&md, strvTrimLeft(md.raw_line), &out, true, true);
- }
-
- markdown__empty_line(&md, &out);
-
- return ostrAsStr(&out);
-}
-
-// == PRIVATE FUNCTIONS ==================================================
-
-static void markdown__parse_config(arena_t *arena, instream_t *in, ini_t *out) {
- strview_t first_line = strvTrim(istrGetLine(in));
- if (!strvEquals(first_line, strv("---"))) {
- istrRewind(in);
- return;
- }
-
- strview_t ini_data = strvInitLen(in->cur, 0);
- usize data_beg = istrTell(*in);
- while (!istrIsFinished(*in)) {
- strview_t line = istrGetViewEither(in, strv("\r\n"));
- if (strvEquals(strvTrim(line), strv("---"))) {
- break;
- }
- istrSkipWhitespace(in);
- }
- usize data_end = istrTell(*in);
- ini_data.len = data_end - data_beg - 3;
-
- if (out) {
- // allocate the string as ini_t only as a copy
- str_t ini_str = str(arena, ini_data);
- *out = iniParseStr(arena, strv(ini_str), NULL);
- }
-}
-
-static int markdown__count_chars(strview_t *line, char c) {
- strview_t temp = *line;
- int n = 0;
- while (strvFront(temp) == c) {
- n++;
- temp = strvRemovePrefix(temp, 1);
- }
-
- *line = temp;
- return n;
-}
-
-static strview_t markdown__parse_header(markdown_ctx_t* md, strview_t line, outstream_t *out) {
- int n = markdown__count_chars(&line, '#');
- line = strvTrimLeft(line);
-
- ostrPrintf(out, "", n);
- markdown__parse_line(md, line, out, false, false);
- ostrPrintf(out, "", n);
-
- return STRV_EMPTY;
-}
-
-static strview_t markdown__parse_ulist_or_line(markdown_ctx_t *md, strview_t line, outstream_t *out) {
- // check if there is anything before this character, if there is
- // it means we're in the middle of a line and we should ignore
- strview_t prev = strvSub(md->raw_line, 0, line.buf - md->raw_line.buf);
- int space_count;
- for (space_count = 0; space_count < prev.len; ++space_count) {
- if (prev.buf[space_count] != ' ') break;
- }
-
- if (space_count < prev.len) {
- return line;
- }
-
- // if its only * or -, this is a list
- if (line.len > 1 && line.buf[1] == ' ') {
- strview_t raw_line = md->raw_line;
- int cur_indent = markdown__count_chars(&raw_line, ' ');
- // start of list
- if (md->list.indent < cur_indent) {
- if (md->list.count >= MD_LIST_MAX_DEPTH) {
- fatal("markdown: too many list levels, max is %d, define MD_LIST_MAX_DEPTH to an higher number", MD_LIST_MAX_DEPTH);
- }
- md->list.list_is_ordered[md->list.count++] = false;
- ostrPuts(out, strv("\n"));
- }
- else if (md->list.indent > cur_indent) {
- markdown__close_list(md, out);
- }
-
- md->list.indent = cur_indent;
- ostrPuts(out, strv("- "));
- markdown__parse_line(md, strvRemovePrefix(line, 2), out, false, false);
- ostrPuts(out, strv("
"));
- goto read_whole_line;
- }
-
- // check if it is an
- char hr_char = strvFront(line);
- strview_t hr = strvTrim(line);
- bool is_hr = true;
- for (usize i = 0; i < hr.len; ++i) {
- if (hr.buf[i] != hr_char) {
- is_hr = false;
- break;
- }
- }
-
- if (is_hr) {
- ostrPuts(out, strv("
"));
- goto read_whole_line;
- }
- else {
- strview_t to_print = line;
- int n = markdown__count_chars(&line, strvFront(line));
- to_print = strvSub(to_print, 0, n);
- line = strvSub(line, n, SIZE_MAX);
- ostrPuts(out, to_print);
- }
-
- return line;
-read_whole_line:
- return STRV_EMPTY;
-}
-
-static strview_t markdown__parse_olist(markdown_ctx_t *md, strview_t line, outstream_t *out) {
- instream_t in = istrInitLen(line.buf, line.len);
-
- int32 number = 0;
- if (!istrGetI32(&in, &number)) {
- return line;
- }
-
- if (istrPeek(&in) != '.') {
- return line;
- }
-
- istrSkip(&in, 1);
-
- if (istrPeek(&in) != ' ') {
- return line;
- }
-
- istrSkip(&in, 1);
-
- strview_t raw_line = md->raw_line;
- int cur_indent = markdown__count_chars(&raw_line, ' ');
- // start of list
- if (md->list.indent < cur_indent) {
- if (md->list.count >= MD_LIST_MAX_DEPTH) {
- fatal("markdown: too many list levels, max is %d, define MD_LIST_MAX_DEPTH to an higher number", MD_LIST_MAX_DEPTH);
- }
- md->list.list_is_ordered[md->list.count++] = true;
- ostrPuts(out, strv("\n"));
- }
- else if (md->list.indent > cur_indent) {
- markdown__close_list(md, out);
- }
-
- md->list.indent = cur_indent;
- ostrPuts(out, strv("- "));
- markdown__parse_line(md, strvRemovePrefix(line, istrTell(in)), out, false, false);
- ostrPuts(out, strv("
"));
-
- return STRV_EMPTY;
-}
-
-static strview_t markdown__parse_code_block(markdown_ctx_t *md, strview_t line, outstream_t *out) {
- strview_t line_copy = line;
- int ticks = markdown__count_chars(&line_copy, '`');
-
- if (ticks != 3) {
- goto finish;
- }
-
- if (md->code.is_in_block) {
- md->code.is_in_block = false;
- if (md->curparser) {
- md_parser_t *p = md->curparser;
- if (p->finish) {
- p->finish(p->userdata);
- }
- }
- ostrPuts(out, strv("
\n"));
- line = line_copy;
- goto finish;
- }
-
- instream_t in = istrInitLen(line_copy.buf, line_copy.len);
- strview_t lang = istrGetLine(&in);
-
- if (!strvIsEmpty(lang)) {
- md->curparser = NULL;
- md_options_t *opt = md->options;
- if (opt) {
- for (int i = 0; i < opt->parsers_count; ++i) {
- if (strvEquals(lang, opt->parsers->lang)) {
- md->curparser = &opt->parsers[i];
- break;
- }
- }
- }
-
- if (!md->curparser) {
- warn("markdown: no parser found for code block in language (%v)", lang);
- }
- else {
- md_parser_t *p = md->curparser;
- if (p->init) {
- p->init(p->userdata);
- }
- }
- }
-
- ostrPuts(out, strv("
"));
- md->code.is_in_block = true;
-
- return STRV_EMPTY;
-finish:
- return line;
-}
-
-static bool markdown__try_parse_url(instream_t *in, strview_t *out_url, strview_t *out_text) {
- istrSkip(in, 1); // skip [
-
- strview_t text = istrGetView(in, ']');
- istrSkip(in, 1); // skip ]
-
- strview_t url = STRV_EMPTY;
- if (istrPeek(in) == '(') {
- istrSkip(in, 1); // skip (
- url = istrGetView(in, ')');
- istrSkip(in, 1); // skip )
- }
-
- bool success = !strvIsEmpty(url);
-
- if (success) {
- *out_url = url;
- *out_text = text;
- }
-
- return success;
-}
-
-static void markdown__parse_line(markdown_ctx_t *md, strview_t line, outstream_t *out, bool add_newline, bool is_line_start) {
- if (md->code.is_in_block && strvFront(line) != '`') {
- md_parser_t *p = md->curparser;
- if (p && p->callback) {
- p->callback(md->raw_line, out, p->userdata);
- }
- else {
- ostrPrintf(out, "%v\n", md->raw_line);
- }
- return;
- }
-
- if (strvIsEmpty(line)) {
- markdown__empty_line(md, out);
- return;
- }
-
- switch (strvFront(line)) {
- // header
- case '#':
- line = markdown__parse_header(md, line, out);
- break;
- // unordered list or
- case '-': case '*': case '_':
- line = markdown__parse_ulist_or_line(md, line, out);
- break;
- // ordered list
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- line = markdown__parse_olist(md, line, out);
- break;
- // code block
- case '`':
- line = markdown__parse_code_block(md, line, out);
- break;
- default:
- break;
- }
-
- if (!strvIsEmpty(line) && is_line_start && !md->is_in_paragraph) {
- md->is_in_paragraph = true;
- ostrPuts(out, strv("\n"));
- }
-
- for (usize i = 0; i < line.len; ++i) {
- switch (line.buf[i]) {
- // escape next character
- case '\\':
- if (++i < line.len) {
- ostrPutc(out, line.buf[i]);
- }
- break;
- // bold or italic
- case '*':
- {
- strview_t sub = strvSub(line, i, SIZE_MAX);
- int n = markdown__count_chars(&sub, '*');
-
- bool is_both = n >= 3;
- bool is_italic = n == 1 || is_both;
- bool is_bold = n == 2 || is_both;
-
- if (is_italic) {
- ostrPrintf(out, "<%s>", md->is_italic ? "/i" : "i");
- md->is_italic = !md->is_italic;
- }
- if (is_bold) {
- ostrPrintf(out, "<%s>", md->is_bold ? "/b" : "b");
- md->is_bold = !md->is_bold;
- }
- if (is_both) {
- for (int k = 3; k < n; ++k) {
- ostrPutc(out, '*');
- }
- }
- i += n - 1;
- break;
- }
- // url
- case '[':
- {
- instream_t in = istrInitLen(line.buf + i, line.len - i);
- strview_t url = STRV_EMPTY;
- strview_t text = STRV_EMPTY;
- if (markdown__try_parse_url(&in, &url, &text)) {
- ostrPrintf(out, "%v", url, strvIsEmpty(text) ? url : text);
- i += istrTell(in) - 1;
- }
- else{
- ostrPutc(out, line.buf[i]);
- }
- break;
- }
- // image
- case '!':
- {
- instream_t in = istrInitLen(line.buf + i, line.len - i);
- strview_t url = STRV_EMPTY;
- strview_t text = STRV_EMPTY;
-
- istrSkip(&in, 1); // skip !
-
- if (markdown__try_parse_url(&in, &url, &text)) {
- ostrPrintf(out, "
');
- i += istrTell(in) - 1;
- }
- else{
- ostrPutc(out, line.buf[i]);
- }
- break;
- }
- // code block
- case '`':
- {
- bool is_escaped = false;
- if ((i + 1) < line.len) {
- is_escaped = line.buf[i + 1] == '`';
- }
- instream_t in = istrInitLen(line.buf + i, line.len - i);
-
- istrSkip(&in, is_escaped ? 2 : 1); // skip `
- ostrPuts(out, strv(""));
- while (!istrIsFinished(in)) {
- strview_t code = istrGetView(&in, '`');
- markdown__escape(code, out);
- if (!is_escaped || istrPeek(&in) == '`') {
- break;
- }
- ostrPutc(out, '`');
- }
- ostrPuts(out, strv(""));
- i += istrTell(in);
- break;
- }
- default:
- ostrPutc(out, line.buf[i]);
- break;
- }
- }
-
- if (add_newline && !md->code.is_in_block) {
- ostrPutc(out, '\n');
- }
-}
-
-static void markdown__empty_line(markdown_ctx_t *md, outstream_t *out) {
- // close lists
- while (md->list.count > 0) {
- if (md->list.list_is_ordered[--md->list.count]) {
- ostrPuts(out, strv("
\n"));
- }
- else {
- ostrPuts(out, strv("\n"));
- }
- }
- md->list.indent = -1;
-
- // close paragraph
- if (md->is_in_paragraph) {
- ostrPuts(out, strv("
\n"));
- }
- md->is_in_paragraph = false;
-}
-
-static void markdown__close_list(markdown_ctx_t *md, outstream_t *out) {
- if (md->list.count > 0) {
- if (md->list.list_is_ordered[--md->list.count]) {
- ostrPuts(out, strv("\n"));
- }
- else {
- ostrPuts(out, strv("\n"));
- }
- }
-}
-
-static void markdown__escape(strview_t view, outstream_t *out) {
- for (usize i = 0; i < view.len; ++i) {
- switch (view.buf[i]){
- case '&':
- ostrPuts(out, strv("&"));
- break;
- case '<':
- ostrPuts(out, strv("<"));
- break;
- case '>':
- ostrPuts(out, strv(">"));
- break;
- default:
- ostrPutc(out, view.buf[i]);
- break;
- }
- }
-}
\ No newline at end of file
diff --git a/markdown.h b/markdown.h
deleted file mode 100644
index a4184ca..0000000
--- a/markdown.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-
-#include "str.h"
-
-typedef struct outstream_t outstream_t;
-
-typedef struct {
- strview_t lang;
- void *userdata;
- void (*init)(void *userdata);
- void (*finish)(void *userdata);
- void (*callback)(strview_t line, outstream_t *out, void *userdata);
-} md_parser_t;
-
-typedef struct {
- md_parser_t *parsers;
- int parsers_count;
- ini_t *out_config;
-} md_options_t;
-
-typedef struct ini_t ini_t;
-
-str_t markdown(arena_t *arena, arena_t scratch, strview_t filename, md_options_t *options);
-str_t markdownStr(arena_t *arena, strview_t markdown_str, md_options_t *options);
-
-/*
-md-lite
-a subset of markdown that can be parsed line by line
-rules:
- begin of file:
- [ ] if there are three dashes (---), everythin until the next three dashes will be read as an ini config
-
- begin of line:
- [x] n # ->
- [x] *** or --- or ___ on their own line ->
- [x] - or * -> unordered list
- [x] n. -> ordered list
- [x] ```xyz and newline -> code block of language (xyz is optional)
-
- mid of line:
- [x] * -> italic
- [x] ** -> bold
- [x] *** -> bold and italic
- [x] [text](link) -> link
- [x]  -> image
- [x] ` -> code block until next backtick
-
- other:
- [x] empty line ->
- [x] \ -> escape character
-
- todo?:
- [ ] two space at end of line or \ ->
- [ ] indent inside list -> continue in point
- [ ] 4 spaces -> line code block (does NOT work with multiline, use ``` instead)
- [ ] -> link
- [ ] [text](link "title") -> link
- [ ] fix ***both***
-*/
\ No newline at end of file
diff --git a/net.c b/net.c
new file mode 100644
index 0000000..6a511ee
--- /dev/null
+++ b/net.c
@@ -0,0 +1,632 @@
+#include "net.h"
+#include "arena.h"
+
+#if COLLA_WIN
+#include "win/net_win32.c"
+#else
+#error "platform not supported"
+#endif
+
+const char *http_get_method_string(http_method_e method) {
+ switch (method) {
+ case HTTP_GET: return "GET";
+ case HTTP_POST: return "POST";
+ case HTTP_HEAD: return "HEAD";
+ case HTTP_PUT: return "PUT";
+ case HTTP_DELETE: return "DELETE";
+ }
+ return "GET";
+}
+
+const char *http_get_status_string(int status) {
+ switch (status) {
+ case 200: return "OK";
+ case 201: return "CREATED";
+ case 202: return "ACCEPTED";
+ case 204: return "NO CONTENT";
+ case 205: return "RESET CONTENT";
+ case 206: return "PARTIAL CONTENT";
+
+ case 300: return "MULTIPLE CHOICES";
+ case 301: return "MOVED PERMANENTLY";
+ case 302: return "MOVED TEMPORARILY";
+ case 304: return "NOT MODIFIED";
+
+ case 400: return "BAD REQUEST";
+ case 401: return "UNAUTHORIZED";
+ case 403: return "FORBIDDEN";
+ case 404: return "NOT FOUND";
+ case 407: return "RANGE NOT SATISFIABLE";
+
+ case 500: return "INTERNAL SERVER_ERROR";
+ case 501: return "NOT IMPLEMENTED";
+ case 502: return "BAD GATEWAY";
+ case 503: return "SERVICE NOT AVAILABLE";
+ case 504: return "GATEWAY TIMEOUT";
+ case 505: return "VERSION NOT SUPPORTED";
+ }
+
+ return "UNKNOWN";
+}
+
+http_header_t *http__parse_headers_instream(arena_t *arena, instream_t *in) {
+ http_header_t *head = NULL;
+
+ while (!istr_is_finished(in)) {
+ strview_t line = istr_get_line(in);
+
+ usize pos = strv_find(line, ':', 0);
+ if (pos != STR_NONE) {
+ http_header_t *new_head = alloc(arena, http_header_t);
+
+ new_head->key = strv_sub(line, 0, pos);
+ new_head->value = strv_sub(line, pos + 2, SIZE_MAX);
+
+ list_push(head, new_head);
+ }
+ }
+
+ return head;
+}
+
+http_header_t *http_parse_headers(arena_t *arena, strview_t header_string) {
+ instream_t in = istr_init(header_string);
+ return http__parse_headers_instream(arena, &in);
+}
+
+http_req_t http_parse_req(arena_t *arena, strview_t request) {
+ http_req_t req = {0};
+ instream_t in = istr_init(request);
+
+ strview_t method = strv_trim(istr_get_view(&in, '/'));
+ istr_skip(&in, 1); // skip /
+ req.url = strv_trim(istr_get_view(&in, ' '));
+ strview_t http = strv_trim(istr_get_view(&in, '\n'));
+
+ istr_skip(&in, 1); // skip \n
+
+ req.headers = http__parse_headers_instream(arena, &in);
+
+ req.body = strv_trim(istr_get_view_len(&in, SIZE_MAX));
+
+ strview_t methods[5] = { strv("GET"), strv("POST"), strv("HEAD"), strv("PUT"), strv("DELETE") };
+ usize methods_count = arrlen(methods);
+
+ for (usize i = 0; i < methods_count; ++i) {
+ if (strv_equals(method, methods[i])) {
+ req.method = (http_method_e)i;
+ break;
+ }
+ }
+
+ in = istr_init(http);
+ istr_ignore_and_skip(&in, '/'); // skip HTTP/
+ istr_get_u8(&in, &req.version.major);
+ istr_skip(&in, 1); // skip .
+ istr_get_u8(&in, &req.version.minor);
+
+ return req;
+}
+
+http_res_t http_parse_res(arena_t *arena, strview_t response) {
+ http_res_t res = {0};
+ instream_t in = istr_init(response);
+
+ strview_t http = istr_get_view_len(&in, 5);
+ if (!strv_equals(http, strv("HTTP"))) {
+ err("response doesn't start with 'HTTP', instead with %v", http);
+ return (http_res_t){0};
+ }
+ istr_skip(&in, 1); // skip /
+ istr_get_u8(&in, &res.version.major);
+ istr_skip(&in, 1); // skip .
+ istr_get_u8(&in, &res.version.minor);
+ istr_get_i32(&in, (i32*)&res.status_code);
+
+ istr_ignore(&in, '\n');
+ istr_skip(&in, 1); // skip \n
+
+ res.headers = http__parse_headers_instream(arena, &in);
+
+ strview_t encoding = http_get_header(res.headers, strv("transfer-encoding"));
+ if (!strv_equals(encoding, strv("chunked"))) {
+ res.body = istr_get_view_len(&in, SIZE_MAX);
+ }
+ else {
+ err("chunked encoding not implemented yet! body ignored");
+ }
+
+ return res;
+}
+
+str_t http_req_to_str(arena_t *arena, http_req_t *req) {
+ outstream_t out = ostr_init(arena);
+
+ const char *method = NULL;
+ switch (req->method) {
+ case HTTP_GET: method = "GET"; break;
+ case HTTP_POST: method = "POST"; break;
+ case HTTP_HEAD: method = "HEAD"; break;
+ case HTTP_PUT: method = "PUT"; break;
+ case HTTP_DELETE: method = "DELETE"; break;
+ default: err("unrecognised method: %d", method); return STR_EMPTY;
+ }
+
+ ostr_print(
+ &out,
+ "%s /%v HTTP/%hhu.%hhu\r\n",
+ method, req->url, req->version.major, req->version.minor
+ );
+
+ http_header_t *h = req->headers;
+ while (h) {
+ ostr_print(&out, "%v: %v\r\n", h->key, h->value);
+ h = h->next;
+ }
+
+ ostr_puts(&out, strv("\r\n"));
+ ostr_puts(&out, req->body);
+
+ return ostr_to_str(&out);
+}
+
+str_t http_res_to_str(arena_t *arena, http_res_t *res) {
+ outstream_t out = ostr_init(arena);
+
+ ostr_print(
+ &out,
+ "HTTP/%hhu.%hhu %d %s\r\n",
+ res->version.major,
+ res->version.minor,
+ res->status_code,
+ http_get_status_string(res->status_code)
+ );
+ ostr_puts(&out, strv("\r\n"));
+ ostr_puts(&out, res->body);
+
+ return ostr_to_str(&out);
+}
+
+bool http_has_header(http_header_t *headers, strview_t key) {
+ for_each(h, headers) {
+ if (strv_equals(h->key, key)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void http_set_header(http_header_t *headers, strview_t key, strview_t value) {
+ http_header_t *h = headers;
+ while (h) {
+ if (strv_equals(h->key, key)) {
+ h->value = value;
+ break;
+ }
+ h = h->next;
+ }
+}
+
+strview_t http_get_header(http_header_t *headers, strview_t key) {
+ http_header_t *h = headers;
+ while (h) {
+ if (strv_equals(h->key, key)) {
+ return h->value;
+ }
+ h = h->next;
+ }
+ return STRV_EMPTY;
+}
+
+str_t http_make_url_safe(arena_t *arena, strview_t string) {
+ strview_t chars = strv(" !\"#$%%&'()*+,/:;=?@[]");
+ usize final_len = string.len;
+
+ // find final string length first
+ for (usize i = 0; i < string.len; ++i) {
+ if (strv_contains(chars, string.buf[i])) {
+ final_len += 2;
+ }
+ }
+
+ str_t out = {
+ .buf = alloc(arena, char, final_len + 1),
+ .len = final_len
+ };
+ usize cur = 0;
+ // substitute characters
+ for (usize i = 0; i < string.len; ++i) {
+ if (strv_contains(chars, string.buf[i])) {
+ fmt_buffer(out.buf + cur, 4, "%%%X", string.buf[i]);
+ cur += 3;
+ }
+ else {
+ out.buf[cur++] = string.buf[i];
+ }
+ }
+
+ return out;
+}
+
+str_t http_decode_url_safe(arena_t *arena, strview_t string) {
+ usize final_len = string.len;
+
+ for (usize i = 0; i < string.len; ++i) {
+ if (string.buf[i] == '%') {
+ final_len -= 2;
+ i += 2;
+ }
+ }
+
+ assert(final_len <= string.len);
+
+ str_t out = {
+ .buf = alloc(arena, char, final_len + 1),
+ .len = final_len
+ };
+
+ usize k = 0;
+
+ for (usize i = 0; i < string.len; ++i) {
+ if (string.buf[i] == '%') {
+ // skip %
+ ++i;
+
+ unsigned int ch = 0;
+ int result = sscanf(string.buf + i, "%02X", &ch);
+ if (result != 1 || ch > UINT8_MAX) {
+ err("malformed url at %zu (%s)", i, string.buf + i);
+ return STR_EMPTY;
+ }
+ out.buf[k++] = (char)ch;
+
+ // skip first char of hex
+ ++i;
+ }
+ else {
+ out.buf[k++] = string.buf[i];
+ }
+ }
+
+ return out;
+}
+
+http_url_t http_split_url(strview_t url) {
+ http_url_t out = {0};
+
+ if (strv_starts_with_view(url, strv("https://"))) {
+ url = strv_remove_prefix(url, 8);
+ }
+ else if (strv_starts_with_view(url, strv("http://"))) {
+ url = strv_remove_prefix(url, 7);
+ }
+
+ out.host = strv_sub(url, 0, strv_find(url, '/', 0));
+ out.uri = strv_sub(url, out.host.len, SIZE_MAX);
+
+ return out;
+}
+
+// WEBSOCKETS ///////////////////////
+
+#define WEBSOCKET_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+#define WEBSOCKET_HTTP_KEY "Sec-WebSocket-Key"
+
+bool websocket_init(arena_t scratch, socket_t socket, strview_t key) {
+ str_t full_key = str_fmt(&scratch, "%v" WEBSOCKET_MAGIC, key);
+
+ sha1_t sha1_ctx = sha1_init();
+ sha1_result_t sha1_data = sha1(&sha1_ctx, full_key.buf, full_key.len);
+
+ // convert to big endian for network communication
+ for (int i = 0; i < 5; ++i) {
+ sha1_data.digest[i] = htonl(sha1_data.digest[i]);
+ }
+
+ buffer_t encoded_key = base64_encode(&scratch, (buffer_t){ (u8 *)sha1_data.digest, sizeof(sha1_data.digest) });
+
+ str_t response = str_fmt(
+ &scratch,
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Connection: Upgrade\r\n"
+ "Upgrade: websocket\r\n"
+ "Sec-WebSocket-Accept: %v\r\n"
+ "\r\n",
+ encoded_key
+ );
+
+ int result = sk_send(socket, response.buf, (int)response.len);
+ return result != SOCKET_ERROR;
+}
+
+buffer_t websocket_encode(arena_t *arena, strview_t message) {
+ int extra = 6;
+ if (message.len > UINT16_MAX) extra += sizeof(u64);
+ else if (message.len > UINT8_MAX) extra += sizeof(u16);
+ u8 *bytes = alloc(arena, u8, message.len + extra);
+ bytes[0] = 0b10000001;
+ bytes[1] = 0b10000000;
+ int offset = 2;
+ if (message.len > UINT16_MAX) {
+ bytes[1] |= 0b01111111;
+ u64 len = htonll(message.len);
+ memmove(bytes + 2, &len, sizeof(len));
+ offset += sizeof(u64);
+ }
+ else if (message.len > UINT8_MAX) {
+ bytes[1] |= 0b01111110;
+ u16 len = htons((u16)message.len);
+ memmove(bytes + 2, &len, sizeof(len));
+ offset += sizeof(u16);
+ }
+ else {
+ bytes[1] |= (u8)message.len;
+ }
+
+ u32 mask = 0;
+ memmove(bytes + offset, &mask, sizeof(mask));
+ offset += sizeof(mask);
+ memmove(bytes + offset, message.buf, message.len);
+
+ return (buffer_t){ bytes, message.len + extra };
+}
+
+str_t websocket_decode(arena_t *arena, buffer_t message) {
+ str_t out = STR_EMPTY;
+ u8 *bytes = message.data;
+
+ bool mask = bytes[1] & 0b10000000;
+ int offset = 2;
+ u64 msglen = bytes[1] & 0b01111111;
+
+ // 16bit msg len
+ if (msglen == 126) {
+ u16 be_len = 0;
+ memmove(&be_len, bytes + 2, sizeof(be_len));
+ msglen = ntohs(be_len);
+ offset += sizeof(u16);
+ }
+ // 64bit msg len
+ else if (msglen == 127) {
+ u64 be_len = 0;
+ memmove(&be_len, bytes + 2, sizeof(be_len));
+ msglen = ntohll(be_len);
+ offset += sizeof(u64);
+ }
+
+ if (msglen == 0) {
+ warn("message length = 0");
+ }
+ else if (mask) {
+ u8 *decoded = alloc(arena, u8, msglen + 1);
+ u8 masks[4] = {0};
+ memmove(masks, bytes + offset, sizeof(masks));
+ offset += 4;
+
+ for (u64 i = 0; i < msglen; ++i) {
+ decoded[i] = bytes[offset + i] ^ masks[i % 4];
+ }
+
+ out = (str_t){ (char *)decoded, msglen };
+ }
+ else {
+ warn("mask bit not set!");
+ }
+
+ return out;
+}
+
+
+// SHA 1 ////////////////////////////
+
+sha1_t sha1_init(void) {
+ return (sha1_t) {
+ .digest = {
+ 0x67452301,
+ 0xEFCDAB89,
+ 0x98BADCFE,
+ 0x10325476,
+ 0xC3D2E1F0,
+ },
+ };
+}
+
+u32 sha1__left_rotate(u32 value, u32 count) {
+ return (value << count) ^ (value >> (32 - count));
+}
+
+void sha1__process_block(sha1_t *ctx) {
+ u32 w [80];
+ for (usize i = 0; i < 16; ++i) {
+ w[i] = ctx->block[i * 4 + 0] << 24;
+ w[i] |= ctx->block[i * 4 + 1] << 16;
+ w[i] |= ctx->block[i * 4 + 2] << 8;
+ w[i] |= ctx->block[i * 4 + 3] << 0;
+ }
+
+ for (usize i = 16; i < 80; ++i) {
+ w[i] = sha1__left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
+ }
+
+ u32 a = ctx->digest[0];
+ u32 b = ctx->digest[1];
+ u32 c = ctx->digest[2];
+ u32 d = ctx->digest[3];
+ u32 e = ctx->digest[4];
+
+ for (usize i = 0; i < 80; ++i) {
+ u32 f = 0;
+ u32 k = 0;
+
+ if (i<20) {
+ f = (b & c) | (~b & d);
+ k = 0x5A827999;
+ } else if (i<40) {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ } else if (i<60) {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ } else {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+ u32 temp = sha1__left_rotate(a, 5) + f + e + k + w[i];
+ e = d;
+ d = c;
+ c = sha1__left_rotate(b, 30);
+ b = a;
+ a = temp;
+ }
+
+ ctx->digest[0] += a;
+ ctx->digest[1] += b;
+ ctx->digest[2] += c;
+ ctx->digest[3] += d;
+ ctx->digest[4] += e;
+}
+
+void sha1__process_byte(sha1_t *ctx, u8 b) {
+ ctx->block[ctx->block_index++] = b;
+ ++ctx->byte_count;
+ if (ctx->block_index == 64) {
+ ctx->block_index = 0;
+ sha1__process_block(ctx);
+ }
+}
+
+sha1_result_t sha1(sha1_t *ctx, const void *buf, usize len) {
+ const u8 *block = buf;
+
+ for (usize i = 0; i < len; ++i) {
+ sha1__process_byte(ctx, block[i]);
+ }
+
+ usize bitcount = ctx->byte_count * 8;
+ sha1__process_byte(ctx, 0x80);
+
+ if (ctx->block_index > 56) {
+ while (ctx->block_index != 0) {
+ sha1__process_byte(ctx, 0);
+ }
+ while (ctx->block_index < 56) {
+ sha1__process_byte(ctx, 0);
+ }
+ } else {
+ while (ctx->block_index < 56) {
+ sha1__process_byte(ctx, 0);
+ }
+ }
+ sha1__process_byte(ctx, 0);
+ sha1__process_byte(ctx, 0);
+ sha1__process_byte(ctx, 0);
+ sha1__process_byte(ctx, 0);
+ sha1__process_byte(ctx, (uchar)((bitcount >> 24) & 0xFF));
+ sha1__process_byte(ctx, (uchar)((bitcount >> 16) & 0xFF));
+ sha1__process_byte(ctx, (uchar)((bitcount >> 8 ) & 0xFF));
+ sha1__process_byte(ctx, (uchar)((bitcount >> 0 ) & 0xFF));
+
+ sha1_result_t result = {0};
+ memcpy(result.digest, ctx->digest, sizeof(result.digest));
+ return result;
+}
+
+str_t sha1Str(arena_t *arena, sha1_t *ctx, const void *buf, usize len) {
+ sha1_result_t result = sha1(ctx, buf, len);
+ return str_fmt(arena, "%08x%08x%08x%08x%08x", result.digest[0], result.digest[1], result.digest[2], result.digest[3], result.digest[4]);
+}
+
+// BASE 64 //////////////////////////
+
+unsigned char b64__encoding_table[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+u8 b64__decoding_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+buffer_t base64_encode(arena_t *arena, buffer_t buffer) {
+ usize outlen = ((buffer.len + 2) / 3) * 4;
+ u8 *out = alloc(arena, u8, outlen);
+
+ for (usize i = 0, j = 0; i < buffer.len;) {
+ u32 a = i < buffer.len ? buffer.data[i++] : 0;
+ u32 b = i < buffer.len ? buffer.data[i++] : 0;
+ u32 c = i < buffer.len ? buffer.data[i++] : 0;
+
+ u32 triple = (a << 16) | (b << 8) | c;
+
+ out[j++] = b64__encoding_table[(triple >> 18) & 0x3F];
+ out[j++] = b64__encoding_table[(triple >> 12) & 0x3F];
+ out[j++] = b64__encoding_table[(triple >> 6) & 0x3F];
+ out[j++] = b64__encoding_table[(triple >> 0) & 0x3F];
+ }
+
+ usize mod = buffer.len % 3;
+ if (mod) {
+ mod = 3 - mod;
+ for (usize i = 0; i < mod; ++i) {
+ out[outlen - 1 - i] = '=';
+ }
+ }
+
+ return (buffer_t){
+ .data = out,
+ .len = outlen
+ };
+}
+
+buffer_t base64_decode(arena_t *arena, buffer_t buffer) {
+ u8 *out = arena->cur;
+ usize start = arena_tell(arena);
+
+ for (usize i = 0; i < buffer.len; i += 4) {
+ u8 a = b64__decoding_table[buffer.data[i + 0]];
+ u8 b = b64__decoding_table[buffer.data[i + 1]];
+ u8 c = b64__decoding_table[buffer.data[i + 2]];
+ u8 d = b64__decoding_table[buffer.data[i + 3]];
+
+ u32 triple =
+ ((u32)a << 18) |
+ ((u32)b << 12) |
+ ((u32)c << 6) |
+ ((u32)d);
+
+ u8 *bytes = alloc(arena, u8, 3);
+
+ bytes[0] = (triple >> 16) & 0xFF;
+ bytes[1] = (triple >> 8) & 0xFF;
+ bytes[2] = (triple >> 0) & 0xFF;
+ }
+
+ usize outlen = arena_tell(arena) - start;
+
+ return (buffer_t){
+ .data = out,
+ .len = outlen,
+ };
+}
\ No newline at end of file
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..0cfd9f0
--- /dev/null
+++ b/net.h
@@ -0,0 +1,190 @@
+#ifndef COLLA_NET_HEADER
+#define COLLA_NET_HEADER
+
+#include "core.h"
+#include "str.h"
+#include "os.h"
+
+void net_init(void);
+void net_cleanup(void);
+iptr net_get_last_error(void);
+
+typedef enum http_method_e {
+ HTTP_GET,
+ HTTP_POST,
+ HTTP_HEAD,
+ HTTP_PUT,
+ HTTP_DELETE
+} http_method_e;
+
+const char *http_get_method_string(http_method_e method);
+const char *http_get_status_string(int status);
+
+typedef struct http_version_t http_version_t;
+struct http_version_t {
+ u8 major;
+ u8 minor;
+};
+
+typedef struct http_header_t http_header_t;
+struct http_header_t {
+ strview_t key;
+ strview_t value;
+ http_header_t *next;
+};
+
+typedef struct http_req_t http_req_t;
+struct http_req_t {
+ http_method_e method;
+ http_version_t version;
+ http_header_t *headers;
+ strview_t url;
+ strview_t body;
+};
+
+typedef struct http_res_t http_res_t;
+struct http_res_t {
+ int status_code;
+ http_version_t version;
+ http_header_t *headers;
+ strview_t body;
+};
+
+http_header_t *http_parse_headers(arena_t *arena, strview_t header_string);
+
+http_req_t http_parse_req(arena_t *arena, strview_t request);
+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);
+
+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);
+
+str_t http_make_url_safe(arena_t *arena, strview_t string);
+str_t http_decode_url_safe(arena_t *arena, strview_t string);
+
+typedef struct {
+ strview_t host;
+ strview_t uri;
+} http_url_t;
+
+http_url_t http_split_url(strview_t url);
+
+typedef struct {
+ arena_t *arena;
+ strview_t url;
+ http_version_t version; // 1.1 by default
+ http_method_e request_type;
+ http_header_t *headers;
+ int header_count;
+ strview_t body;
+} http_request_desc_t;
+
+// arena_t *arena, strview_t url, [ http_header_t *headers, int header_count, strview_t body ]
+#define http_get(arena, url, ...) http_request(&(http_request_desc_t){ arena, url, .request_type = HTTP_GET, .version = { 1, 1 }, __VA_ARGS__ })
+
+http_res_t http_request(http_request_desc_t *request);
+
+// SOCKETS //////////////////////////
+
+typedef uintptr_t socket_t;
+typedef struct skpoll_t skpoll_t;
+
+#define SK_ADDR_LOOPBACK "127.0.0.1"
+#define SK_ADDR_ANY "0.0.0.0"
+#define SK_ADDR_BROADCAST "255.255.255.255"
+
+struct skpoll_t {
+ uintptr_t socket;
+ short events;
+ short revents;
+};
+
+#define SOCKET_ERROR (-1)
+
+typedef enum {
+ SOCK_TCP,
+ SOCK_UDP,
+} sktype_e;
+
+typedef enum {
+ SOCK_EVENT_NONE,
+ SOCK_EVENT_READ = 1 << 0,
+ SOCK_EVENT_WRITE = 1 << 1,
+ SOCK_EVENT_ACCEPT = 1 << 2,
+ SOCK_EVENT_CONNECT = 1 << 3,
+ SOCK_EVENT_CLOSE = 1 << 4,
+} skevent_e;
+
+// Opens a socket
+socket_t sk_open(sktype_e type);
+// Opens a socket using 'protocol', options are
+// ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp
+socket_t sk_open_protocol(const char *protocol);
+
+// Checks that a opened socket is valid, returns true on success
+bool sk_is_valid(socket_t sock);
+
+// Closes a socket, returns true on success
+bool sk_close(socket_t sock);
+
+// Fill out a sk_addrin_t structure with "ip" and "port"
+// skaddrin_t sk_addrin_init(const char *ip, uint16_t port);
+
+// Associate a local address with a socket
+bool sk_bind(socket_t sock, const char *ip, u16 port);
+
+// Place a socket in a state in which it is listening for an incoming connection
+bool sk_listen(socket_t sock, int backlog);
+
+// Permits an incoming connection attempt on a socket
+socket_t sk_accept(socket_t sock);
+
+// Connects to a server (e.g. "127.0.0.1" or "google.com") with a port(e.g. 1234), returns true on success
+bool sk_connect(socket_t sock, const char *server, u16 server_port);
+
+// Sends data on a socket, returns true on success
+int sk_send(socket_t sock, const void *buf, int len);
+// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
+int sk_recv(socket_t sock, void *buf, int len);
+
+// Wait for an event on some sockets
+int sk_poll(skpoll_t *to_poll, int num_to_poll, int timeout);
+
+oshandle_t sk_bind_event(socket_t sock, skevent_e event);
+void sk_destroy_event(oshandle_t handle);
+void sk_reset_event(oshandle_t handle);
+
+// WEBSOCKETS ///////////////////////
+
+bool websocket_init(arena_t scratch, socket_t socket, strview_t key);
+buffer_t websocket_encode(arena_t *arena, strview_t message);
+str_t websocket_decode(arena_t *arena, buffer_t message);
+
+// SHA 1 ////////////////////////////
+
+typedef struct sha1_t sha1_t;
+struct sha1_t {
+ u32 digest[5];
+ u8 block[64];
+ usize block_index;
+ usize byte_count;
+};
+
+typedef struct sha1_result_t sha1_result_t;
+struct sha1_result_t {
+ u32 digest[5];
+};
+
+sha1_t sha1_init(void);
+sha1_result_t sha1(sha1_t *ctx, const void *buf, usize len);
+str_t sha1_str(arena_t *arena, sha1_t *ctx, const void *buf, usize len);
+
+// BASE 64 //////////////////////////
+
+buffer_t base64_encode(arena_t *arena, buffer_t buffer);
+buffer_t base64_decode(arena_t *arena, buffer_t buffer);
+
+#endif
\ No newline at end of file
diff --git a/os.c b/os.c
new file mode 100644
index 0000000..aef91b8
--- /dev/null
+++ b/os.c
@@ -0,0 +1,235 @@
+#include "os.h"
+
+#if COLLA_WIN
+ #include "win/os_win32.c"
+#else
+ #error "platform not supported yet"
+#endif
+
+// == HANDLE ====================================
+
+oshandle_t os_handle_zero(void) {
+ return (oshandle_t){0};
+}
+
+bool os_handle_match(oshandle_t a, oshandle_t b) {
+ return a.data == b.data;
+}
+
+bool os_handle_valid(oshandle_t handle) {
+ return !os_handle_match(handle, os_handle_zero());
+}
+
+// == 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;
+ }
+ return colour;
+}
+
+void os_log_print(os_log_level_e level, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ os_log_printv(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;
+ }
+
+ 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);
+
+ fmt_printv(fmt, args);
+ fmt_print("\n");
+
+ if (level == LOG_FATAL) {
+ os_abort(1);
+ }
+}
+
+// == FILE ======================================
+
+void os_file_split_path(strview_t path, strview_t *dir, strview_t *name, strview_t *ext) {
+ usize dir_lin = strv_rfind(path, '/', 0);
+ usize dir_win = strv_rfind(path, '\\', 0);
+ dir_lin = dir_lin != STR_NONE ? dir_lin : 0;
+ dir_win = dir_win != STR_NONE ? dir_win : 0;
+ usize dir_pos = MAX(dir_lin, dir_win);
+
+ usize ext_pos = strv_rfind(path, '.', 0);
+
+ if (dir) {
+ *dir = strv_sub(path, 0, dir_pos);
+ }
+ if (name) {
+ *name = strv_sub(path, dir_pos ? dir_pos + 1 : 0, ext_pos);
+ }
+ if (ext) {
+ *ext = strv_sub(path, ext_pos, SIZE_MAX);
+ }
+}
+
+bool os_file_putc(oshandle_t handle, char c) {
+ return os_file_write(handle, &c, sizeof(c)) == sizeof(c);
+}
+
+bool os_file_puts(oshandle_t handle, strview_t str) {
+ return os_file_write(handle, str.buf, str.len) == str.len;
+}
+
+bool os_file_print(arena_t scratch, oshandle_t handle, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ bool result = os_file_printv(scratch, handle, fmt, args);
+ va_end(args);
+ return result;
+}
+
+bool os_file_printv(arena_t scratch, oshandle_t handle, const char *fmt, va_list args) {
+ str_t s = str_fmtv(&scratch, fmt, args);
+ return os_file_puts(handle, strv(s));
+}
+
+usize os_file_read_buf(oshandle_t handle, buffer_t *buf) {
+ return os_file_read(handle, buf->data, buf->len);
+}
+
+usize os_file_write_buf(oshandle_t handle, buffer_t buf) {
+ return os_file_write(handle, buf.data, buf.len);
+}
+
+buffer_t os_file_read_all(arena_t *arena, strview_t path) {
+ oshandle_t fp = os_file_open(path, FILEMODE_READ);
+ if (!os_handle_valid(fp)) {
+ err("could not open file: %v", path);
+ return (buffer_t){0};
+ }
+ buffer_t out = os_file_read_all_fp(arena, fp);
+ os_file_close(fp);
+ return out;
+}
+
+buffer_t os_file_read_all_fp(arena_t *arena, oshandle_t handle) {
+ if (!os_handle_valid(handle)) return (buffer_t){0};
+ buffer_t out = {0};
+
+ out.len = os_file_size(handle);
+ out.data = alloc(arena, u8, out.len);
+ usize read = os_file_read_buf(handle, &out);
+
+ if (read != out.len) {
+ err("os_file_read_all_fp: read failed, should be %zu but is %zu", out.len, read);
+ arena_pop(arena, out.len);
+ return (buffer_t){0};
+ }
+
+ return out;
+}
+
+str_t os_file_read_all_str(arena_t *arena, strview_t path) {
+ oshandle_t fp = os_file_open(path, FILEMODE_READ);
+ if (!os_handle_valid(fp)) {
+ err("could not open file %v", path);
+ return STR_EMPTY;
+ }
+ str_t out = os_file_read_all_str_fp(arena, fp);
+ os_file_close(fp);
+ return out;
+}
+
+str_t os_file_read_all_str_fp(arena_t *arena, oshandle_t handle) {
+ if (!os_handle_valid(handle)) {
+ return STR_EMPTY;
+ }
+
+ str_t out = STR_EMPTY;
+
+ out.len = os_file_size(handle);
+ out.buf = alloc(arena, u8, out.len + 1);
+
+ usize read = os_file_read(handle, out.buf, out.len);
+ if (read != out.len) {
+ err("os_file_read_all_str_fp: read failed, should be %zu but is %zu", out.len, read);
+ arena_pop(arena, out.len + 1);
+ return STR_EMPTY;
+ }
+
+ return out;
+}
+
+bool os_file_write_all(strview_t name, buffer_t buffer) {
+ oshandle_t fp = os_file_open(name, FILEMODE_WRITE);
+ bool result = os_file_write_all_fp(fp, buffer);
+ os_file_close(fp);
+ return result;
+}
+
+bool os_file_write_all_fp(oshandle_t handle, buffer_t buffer) {
+ return os_file_write(handle, buffer.data, buffer.len) == buffer.len;
+}
+
+bool os_file_write_all_str(strview_t name, strview_t data) {
+ oshandle_t fp = os_file_open(name, FILEMODE_WRITE);
+ bool result = os_file_write_all_str_fp(fp, data);
+ os_file_close(fp);
+ return result;
+}
+
+bool os_file_write_all_str_fp(oshandle_t handle, strview_t data) {
+ return os_file_write(handle, data.buf, data.len) == data.len;
+}
+
+u64 os_file_time(strview_t path) {
+ oshandle_t fp = os_file_open(path, FILEMODE_READ);
+ u64 result = os_file_time_fp(fp);
+ os_file_close(fp);
+ return result;
+}
+
+bool os_file_has_changed(strview_t path, u64 last_change) {
+ u64 timestamp = os_file_time(path);
+ return timestamp > last_change;
+}
+
+// == PROCESS ===================================
+
+bool os_run_cmd(arena_t scratch, os_cmd_t *cmd, os_env_t *optional_env) {
+ oshandle_t proc = os_run_cmd_async(scratch, cmd, optional_env);
+ return os_handle_valid(proc) ? os_process_wait(proc, OS_WAIT_INFINITE, NULL) : false;
+}
+
+// == VMEM ======================================
+
+usize os_pad_to_page(usize byte_count) {
+ usize page_size = os_get_system_info().page_size;
+
+ if (byte_count == 0) {
+ return page_size;
+ }
+
+ usize padding = page_size - (byte_count & (page_size - 1));
+ if (padding == page_size) {
+ padding = 0;
+ }
+ return byte_count + padding;
+}
diff --git a/os.h b/os.h
new file mode 100644
index 0000000..95dafa8
--- /dev/null
+++ b/os.h
@@ -0,0 +1,216 @@
+#ifndef COLLA_OS_H
+#define COLLA_OS_H
+
+#include "core.h"
+#include "str.h"
+#include "arena.h"
+
+#define OS_ARENA_SIZE (MB(1))
+#define OS_WAIT_INFINITE (0xFFFFFFFF)
+
+typedef struct oshandle_t oshandle_t;
+struct oshandle_t {
+ uptr data;
+};
+
+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;
+};
+
+void os_init(void);
+void os_cleanup(void);
+os_system_info_t os_get_system_info(void);
+void os_abort(int code);
+
+iptr os_get_last_error(void);
+// NOT thread safe
+str_t os_get_error_string(iptr error);
+
+// == HANDLE ====================================
+
+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,
+ OS_WAIT_TIMEOUT,
+ OS_WAIT_FAILED
+} os_wait_result_e;
+
+typedef struct {
+ os_wait_result_e result;
+ u32 index;
+} os_wait_t;
+
+os_wait_t os_wait_on_handles(oshandle_t *handles, int count, bool wait_all, u32 milliseconds);
+
+// == LOGGING ===================================
+
+typedef enum os_log_level_e {
+ LOG_BASIC,
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_WARN,
+ LOG_ERR,
+ LOG_FATAL,
+} os_log_level_e;
+
+typedef enum os_log_colour_e {
+ LOG_COL_RESET,
+ LOG_COL_BLACK,
+ LOG_COL_BLUE,
+ LOG_COL_GREEN,
+ LOG_COL_CYAN,
+ LOG_COL_RED,
+ LOG_COL_MAGENTA,
+ LOG_COL_YELLOW,
+ LOG_COL_WHITE,
+} 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);
+void os_log_set_colour(os_log_colour_e colour);
+
+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__)
+
+// == FILE ======================================
+
+typedef enum filemode_e {
+ FILEMODE_READ = 1 << 0,
+ FILEMODE_WRITE = 1 << 1,
+} filemode_e;
+
+bool os_file_exists(strview_t path);
+tstr_t os_file_fullpath(arena_t *arena, strview_t filename);
+void os_file_split_path(strview_t path, strview_t *dir, strview_t *name, strview_t *ext);
+bool os_file_delete(strview_t path);
+
+oshandle_t os_file_open(strview_t path, filemode_e mode);
+void os_file_close(oshandle_t handle);
+
+bool os_file_putc(oshandle_t handle, char c);
+bool os_file_puts(oshandle_t handle, strview_t str);
+bool os_file_print(arena_t scratch, oshandle_t handle, const char *fmt, ...);
+bool os_file_printv(arena_t scratch, oshandle_t handle, const char *fmt, va_list args);
+
+usize os_file_read(oshandle_t handle, void *buf, usize len);
+usize os_file_write(oshandle_t handle, const void *buf, usize len);
+
+usize os_file_read_buf(oshandle_t handle, buffer_t *buf);
+usize os_file_write_buf(oshandle_t handle, buffer_t buf);
+
+bool os_file_seek(oshandle_t handle, usize offset);
+bool os_file_seek_end(oshandle_t handle);
+void os_file_rewind(oshandle_t handle);
+usize os_file_tell(oshandle_t handle);
+usize os_file_size(oshandle_t handle);
+bool os_file_is_finished(oshandle_t handle);
+
+buffer_t os_file_read_all(arena_t *arena, strview_t path);
+buffer_t os_file_read_all_fp(arena_t *arena, oshandle_t handle);
+
+str_t os_file_read_all_str(arena_t *arena, strview_t path);
+str_t os_file_read_all_str_fp(arena_t *arena, oshandle_t handle);
+
+bool os_file_write_all(strview_t name, buffer_t buffer);
+bool os_file_write_all_fp(oshandle_t handle, buffer_t buffer);
+
+bool os_file_write_all_str(strview_t name, strview_t data);
+bool os_file_write_all_str_fp(oshandle_t handle, strview_t data);
+
+u64 os_file_time(strview_t path);
+u64 os_file_time_fp(oshandle_t handle);
+bool os_file_has_changed(strview_t path, u64 last_change);
+
+// == DIR WALKER ================================
+
+typedef enum dir_type_e {
+ DIRTYPE_FILE,
+ DIRTYPE_DIR,
+} dir_type_e;
+
+typedef struct dir_entry_t dir_entry_t;
+struct dir_entry_t {
+ str_t name;
+ dir_type_e type;
+ usize file_size;
+};
+
+#define dir_foreach(arena, it, dir) for (dir_entry_t *it = os_dir_next(arena, dir); it; it = os_dir_next(arena, dir))
+
+typedef struct dir_t dir_t;
+dir_t *os_dir_open(arena_t *arena, strview_t path);
+bool os_dir_is_valid(dir_t *dir);
+// optional, only call this if you want to return before os_dir_next returns NULL
+void os_dir_close(dir_t *dir);
+
+dir_entry_t *os_dir_next(arena_t *arena, dir_t *dir);
+
+// == PROCESS ===================================
+
+typedef struct os_env_t os_env_t;
+typedef strv_list_t os_cmd_t;
+#define os_make_cmd(...) &(os_cmd_t){ .items = (strview_t[]){ __VA_ARGS__ }, .count = arrlen(((strview_t[]){ __VA_ARGS__ })) }
+
+void os_set_env_var(arena_t scratch, strview_t key, strview_t value);
+str_t os_get_env_var(arena_t *arena, strview_t key);
+os_env_t *os_get_env(arena_t *arena);
+bool os_run_cmd(arena_t scratch, os_cmd_t *cmd, os_env_t *optional_env);
+oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_env_t *optional_env);
+bool os_process_wait(oshandle_t proc, uint time, int *out_exit);
+
+// == VMEM ======================================
+
+void *os_reserve(usize size, usize *out_padded_size);
+bool os_commit(void *ptr, usize num_of_pages);
+bool os_release(void *ptr, usize size);
+usize os_pad_to_page(usize byte_count);
+
+// == THREAD ====================================
+
+typedef int (thread_func_t)(u64 thread_id, void *userdata);
+
+oshandle_t os_thread_launch(thread_func_t func, void *userdata);
+bool os_thread_detach(oshandle_t thread);
+bool os_thread_join(oshandle_t thread, int *code);
+
+u64 os_thread_get_id(oshandle_t thread);
+
+// == MUTEX =====================================
+
+oshandle_t os_mutex_create(void);
+void os_mutex_free(oshandle_t mutex);
+void os_mutex_lock(oshandle_t mutex);
+void os_mutex_unlock(oshandle_t mutex);
+bool os_mutex_try_lock(oshandle_t mutex);
+
+#if !COLLA_NO_CONDITION_VARIABLE
+// == CONDITION VARIABLE ========================
+
+oshandle_t os_cond_create(void);
+void os_cond_free(oshandle_t cond);
+
+void os_cond_signal(oshandle_t cond);
+void os_cond_broadcast(oshandle_t cond);
+
+void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds);
+
+#endif
+
+#endif
\ No newline at end of file
diff --git a/parsers.c b/parsers.c
new file mode 100644
index 0000000..807fb5f
--- /dev/null
+++ b/parsers.c
@@ -0,0 +1,844 @@
+#include "parsers.h"
+
+#include "os.h"
+
+// == INI ============================================
+
+void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options);
+
+ini_t ini_parse(arena_t *arena, strview_t filename, iniopt_t *opt) {
+ oshandle_t fp = os_file_open(filename, FILEMODE_READ);
+ ini_t out = ini_parse_fp(arena, fp, opt);
+ os_file_close(fp);
+ return out;
+}
+
+ini_t ini_parse_fp(arena_t *arena, oshandle_t file, iniopt_t *opt) {
+ str_t data = os_file_read_all_str_fp(arena, file);
+ return ini_parse_str(arena, strv(data), opt);
+}
+
+ini_t ini_parse_str(arena_t *arena, strview_t str, iniopt_t *opt) {
+ ini_t out = {
+ .text = str,
+ .tables = NULL,
+ };
+ ini__parse(arena, &out, opt);
+ return out;
+}
+
+bool ini_is_valid(ini_t *ini) {
+ return ini && !strv_is_empty(ini->text);
+}
+
+initable_t *ini_get_table(ini_t *ini, strview_t name) {
+ initable_t *t = ini ? ini->tables : NULL;
+ while (t) {
+ if (strv_equals(t->name, name)) {
+ return t;
+ }
+ t = t->next;
+ }
+ return NULL;
+}
+
+inivalue_t *ini_get(initable_t *table, strview_t key) {
+ inivalue_t *v = table ? table->values : NULL;
+ while (v) {
+ if (strv_equals(v->key, key)) {
+ return v;
+ }
+ v = v->next;
+ }
+ return NULL;
+}
+
+iniarray_t ini_as_arr(arena_t *arena, inivalue_t *value, char delim) {
+ strview_t v = value ? value->value : STRV_EMPTY;
+ if (!delim) delim = ' ';
+
+ strview_t *beg = (strview_t *)arena->cur;
+ usize count = 0;
+
+ usize start = 0;
+ for (usize i = 0; i < v.len; ++i) {
+ if (v.buf[i] == delim) {
+ strview_t arrval = strv_trim(strv_sub(v, start, i));
+ if (!strv_is_empty(arrval)) {
+ strview_t *newval = alloc(arena, strview_t);
+ *newval = arrval;
+ ++count;
+ }
+ start = i + 1;
+ }
+ }
+
+ strview_t last = strv_trim(strv_sub(v, start, SIZE_MAX));
+ if (!strv_is_empty(last)) {
+ strview_t *newval = alloc(arena, strview_t);
+ *newval = last;
+ ++count;
+ }
+
+ return (iniarray_t){
+ .values = beg,
+ .count = count,
+ };
+}
+
+u64 ini_as_uint(inivalue_t *value) {
+ strview_t v = value ? value->value : STRV_EMPTY;
+ instream_t in = istr_init(v);
+ u64 out = 0;
+ if (!istr_get_u64(&in, &out)) {
+ out = 0;
+ }
+ return out;
+}
+
+i64 ini_as_int(inivalue_t *value) {
+ strview_t v = value ? value->value : STRV_EMPTY;
+ instream_t in = istr_init(v);
+ i64 out = 0;
+ if (!istr_get_i64(&in, &out)) {
+ out = 0;
+ }
+ return out;
+}
+
+double ini_as_num(inivalue_t *value) {
+ strview_t v = value ? value->value : STRV_EMPTY;
+ instream_t in = istr_init(v);
+ double out = 0;
+ if (!istr_get_num(&in, &out)) {
+ out = 0;
+ }
+ return out;
+}
+
+bool ini_as_bool(inivalue_t *value) {
+ strview_t v = value ? value->value : STRV_EMPTY;
+ instream_t in = istr_init(v);
+ bool out = 0;
+ if (!istr_get_bool(&in, &out)) {
+ out = 0;
+ }
+ return out;
+}
+
+///// ini-private ////////////////////////////////////
+
+iniopt_t ini__get_options(const iniopt_t *options) {
+ iniopt_t out = {
+ .key_value_divider = '=',
+ .comment_vals = strv(";#"),
+ };
+
+#define SETOPT(v) out.v = options->v ? options->v : out.v
+
+ if (options) {
+ SETOPT(key_value_divider);
+ SETOPT(merge_duplicate_keys);
+ SETOPT(merge_duplicate_tables);
+ out.comment_vals = strv_is_empty(options->comment_vals) ? out.comment_vals : options->comment_vals;
+ }
+
+#undef SETOPT
+
+ return out;
+}
+
+
+void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, iniopt_t *opts) {
+ assert(table);
+
+ strview_t key = strv_trim(istr_get_view(in, opts->key_value_divider));
+ istr_skip(in, 1);
+
+ strview_t value = strv_trim(istr_get_view(in, '\n'));
+ usize comment_pos = strv_find_either(value, opts->comment_vals, 0);
+ if (comment_pos != STR_NONE) {
+ value = strv_sub(value, 0, comment_pos);
+ }
+ istr_skip(in, 1);
+ inivalue_t *newval = NULL;
+
+ if (opts->merge_duplicate_keys) {
+ newval = table->values;
+ while (newval) {
+ if (strv_equals(newval->key, key)) {
+ break;
+ }
+ newval = newval->next;
+ }
+ }
+
+ if (newval) {
+ newval->value = value;
+ }
+ else {
+ newval = alloc(arena, inivalue_t);
+ newval->key = key;
+ newval->value = value;
+
+ if (!table->values) {
+ table->values = newval;
+ }
+ else {
+ table->tail->next = newval;
+ }
+
+ table->tail = newval;
+ }
+}
+
+void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopt_t *options) {
+ istr_skip(in, 1); // skip [
+ strview_t name = istr_get_view(in, ']');
+ istr_skip(in, 1); // skip ]
+ initable_t *table = NULL;
+
+ if (options->merge_duplicate_tables) {
+ table = ctx->tables;
+ while (table) {
+ if (strv_equals(table->name, name)) {
+ break;
+ }
+ table = table->next;
+ }
+ }
+
+ if (!table) {
+ table = alloc(arena, initable_t);
+
+ table->name = name;
+
+ if (!ctx->tables) {
+ ctx->tables = table;
+ }
+ else {
+ ctx->tail->next = table;
+ }
+
+ ctx->tail = table;
+ }
+
+ istr_ignore_and_skip(in, '\n');
+ while (!istr_is_finished(in)) {
+ switch (istr_peek(in)) {
+ case '\n': // fallthrough
+ case '\r':
+ return;
+ case '#': // fallthrough
+ case ';':
+ istr_ignore_and_skip(in, '\n');
+ break;
+ default:
+ ini__add_value(arena, table, in, options);
+ break;
+ }
+ }
+}
+
+void ini__parse(arena_t *arena, ini_t *ini, const iniopt_t *options) {
+ iniopt_t opts = ini__get_options(options);
+
+ initable_t *root = alloc(arena, initable_t);
+ root->name = INI_ROOT;
+ ini->tables = root;
+ ini->tail = root;
+
+ instream_t in = istr_init(ini->text);
+
+ while (!istr_is_finished(&in)) {
+ istr_skip_whitespace(&in);
+ switch (istr_peek(&in)) {
+ case '[':
+ ini__add_table(arena, ini, &in, &opts);
+ break;
+ case '#': // fallthrough
+ case ';':
+ istr_ignore_and_skip(&in, '\n');
+ break;
+ default:
+ ini__add_value(arena, ini->tables, &in, &opts);
+ break;
+ }
+ }
+}
+
+// == JSON ===========================================
+
+bool json__parse_obj(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);
+ return json_parse_str(arena, strv(data), flags);
+}
+
+json_t *json_parse_str(arena_t *arena, strview_t str, jsonflags_e flags) {
+ arena_t before = *arena;
+
+ json_t *root = alloc(arena, json_t);
+ root->type = JSON_OBJECT;
+
+ instream_t in = istr_init(str);
+
+ if (!json__parse_obj(arena, &in, flags, &root->object)) {
+ // reset arena
+ *arena = before;
+ return NULL;
+ }
+
+ return root;
+}
+
+json_t *json_get(json_t *node, strview_t key) {
+ if (!node) return NULL;
+
+ if (node->type != JSON_OBJECT) {
+ err("passed type is not an object");
+ return NULL;
+ }
+
+ node = node->object;
+
+ while (node) {
+ if (strv_equals(node->key, key)) {
+ return node;
+ }
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+void json__pretty_print_value(json_t *value, int indent, const json_pretty_opts_t *options);
+
+void json_pretty_print(json_t *root, const json_pretty_opts_t *options) {
+ json_pretty_opts_t default_options = { 0 };
+ if (options) {
+ memmove(&default_options, options, sizeof(json_pretty_opts_t));
+ }
+
+ if (!os_handle_valid(default_options.custom_target)) {
+ default_options.custom_target = os_stdout();
+ }
+ if (!default_options.use_custom_colours) {
+ os_log_colour_e default_col[JSON_PRETTY_COLOUR__COUNT] = {
+ LOG_COL_YELLOW, // JSON_PRETTY_COLOUR_KEY,
+ LOG_COL_CYAN, // JSON_PRETTY_COLOUR_STRING,
+ LOG_COL_BLUE, // JSON_PRETTY_COLOUR_NUM,
+ LOG_COL_BLACK, // JSON_PRETTY_COLOUR_NULL,
+ LOG_COL_GREEN, // JSON_PRETTY_COLOUR_TRUE,
+ LOG_COL_RED, // JSON_PRETTY_COLOUR_FALSE,
+ };
+ memmove(default_options.colours, default_col, sizeof(default_col));
+ }
+
+ json__pretty_print_value(root, 0, &default_options);
+ os_file_putc(default_options.custom_target, '\n');
+}
+
+///// json-private ///////////////////////////////////
+
+#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;
+ }
+ istr_rewind_n(in, 1);
+ err("wrong character at %zu, should be '%c' but is 0x%02x '%c'", istr_tell(in), c, istr_peek(in), istr_peek(in));
+ return false;
+}
+
+bool json__is_value_finished(instream_t *in) {
+ usize old_pos = istr_tell(in);
+
+ istr_skip_whitespace(in);
+ switch(istr_peek(in)) {
+ case '}': // fallthrough
+ case ']': // fallthrough
+ case ',':
+ return true;
+ }
+
+ in->cur = in->beg + old_pos;
+ return false;
+}
+
+bool json__parse_null(instream_t *in) {
+ strview_t null_view = istr_get_view_len(in, 4);
+ bool is_valid = true;
+
+ if (!strv_equals(null_view, strv("null"))) {
+ err("should be null but is: (%.*s) at %zu", null_view.len, null_view.buf, istr_tell(in));
+ is_valid = false;
+ }
+
+ if (!json__is_value_finished(in)) {
+ err("null, should be finished, but isn't at %zu", istr_tell(in));
+ is_valid = false;
+ }
+
+ return is_valid;
+}
+
+bool json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out) {
+ json_t *head = NULL;
+
+ if (!json__ensure('[')) {
+ goto fail;
+ }
+
+ istr_skip_whitespace(in);
+
+ // if it is an empty array
+ if (istr_peek(in) == ']') {
+ istr_skip(in, 1);
+ goto success;
+ }
+
+ if (!json__parse_value(arena, in, flags, &head)) {
+ goto fail;
+ }
+
+ json_t *cur = head;
+
+ while (true) {
+ istr_skip_whitespace(in);
+ switch (istr_get(in)) {
+ case ']':
+ goto success;
+ case ',':
+ {
+ istr_skip_whitespace(in);
+ // trailing comma
+ if (istr_peek(in) == ']') {
+ if (flags & JSON_NO_TRAILING_COMMAS) {
+ err("trailing comma in array at at %zu: (%c)(%d)", istr_tell(in), *in->cur, *in->cur);
+ goto fail;
+ }
+ else {
+ continue;
+ }
+ }
+
+ json_t *next = NULL;
+ if (!json__parse_value(arena, in, flags, &next)) {
+ goto fail;
+ }
+ cur->next = next;
+ next->prev = cur;
+ cur = next;
+ break;
+ }
+ default:
+ istr_rewind_n(in, 1);
+ err("unknown char after array at %zu: (%c)(%d)", istr_tell(in), *in->cur, *in->cur);
+ goto fail;
+ }
+ }
+
+success:
+ *out = head;
+ return true;
+fail:
+ *out = NULL;
+ return false;
+}
+
+bool json__parse_string(arena_t *arena, instream_t *in, strview_t *out) {
+ COLLA_UNUSED(arena);
+ *out = STRV_EMPTY;
+
+ istr_skip_whitespace(in);
+
+ if (!json__ensure('"')) {
+ goto fail;
+ }
+
+ const char *from = in->cur;
+
+ for (; !istr_is_finished(in) && *in->cur != '"'; ++in->cur) {
+ if (istr_peek(in) == '\\') {
+ ++in->cur;
+ }
+ }
+
+ usize len = in->cur - from;
+
+ *out = strv(from, len);
+
+ if (!json__ensure('"')) {
+ goto fail;
+ }
+
+ return true;
+fail:
+ return false;
+}
+
+bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out) {
+ strview_t key = {0};
+ if (!json__parse_string(arena, in, &key)) {
+ goto fail;
+ }
+
+ // skip preamble
+ istr_skip_whitespace(in);
+ if (!json__ensure(':')) {
+ goto fail;
+ }
+
+ if (!json__parse_value(arena, in, flags, out)) {
+ goto fail;
+ }
+
+ (*out)->key = key;
+ return true;
+
+fail:
+ *out = NULL;
+ return false;
+}
+
+bool json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out) {
+ if (!json__ensure('{')) {
+ goto fail;
+ }
+
+ istr_skip_whitespace(in);
+
+ // if it is an empty object
+ if (istr_peek(in) == '}') {
+ istr_skip(in, 1);
+ *out = NULL;
+ return true;
+ }
+
+ json_t *head = NULL;
+ if (!json__parse_pair(arena, in, flags, &head)) {
+ goto fail;
+ }
+ json_t *cur = head;
+
+ while (true) {
+ istr_skip_whitespace(in);
+ switch (istr_get(in)) {
+ case '}':
+ goto success;
+ case ',':
+ {
+ istr_skip_whitespace(in);
+ // trailing commas
+ if (!(flags & JSON_NO_TRAILING_COMMAS) && istr_peek(in) == '}') {
+ goto success;
+ }
+
+ json_t *next = NULL;
+ if (!json__parse_pair(arena, in, flags, &next)) {
+ goto fail;
+ }
+ cur->next = next;
+ next->prev = cur;
+ cur = next;
+ break;
+ }
+ default:
+ istr_rewind_n(in, 1);
+ err("unknown char after object at %zu: (%c)(%d)", istr_tell(in), *in->cur, *in->cur);
+ goto fail;
+ }
+ }
+
+success:
+ *out = head;
+ return true;
+fail:
+ *out = NULL;
+ return false;
+}
+
+bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, json_t **out) {
+ json_t *val = alloc(arena, json_t);
+
+ istr_skip_whitespace(in);
+
+ switch (istr_peek(in)) {
+ // object
+ case '{':
+ if (!json__parse_obj(arena, in, flags, &val->object)) {
+ goto fail;
+ }
+ val->type = JSON_OBJECT;
+ break;
+ // array
+ case '[':
+ if (!json__parse_array(arena, in, flags, &val->array)) {
+ goto fail;
+ }
+ val->type = JSON_ARRAY;
+ break;
+ // string
+ case '"':
+ if (!json__parse_string(arena, in, &val->string)) {
+ goto fail;
+ }
+ val->type = JSON_STRING;
+ break;
+ // boolean
+ case 't': // fallthrough
+ case 'f':
+ if (!istr_get_bool(in, &val->boolean)) {
+ goto fail;
+ }
+ val->type = JSON_BOOL;
+ break;
+ // null
+ case 'n':
+ if (!json__parse_null(in)) {
+ goto fail;
+ }
+ val->type = JSON_NULL;
+ break;
+ // comment
+ case '/':
+ err("TODO comments");
+ break;
+ // number
+ default:
+ if (!istr_get_num(in, &val->number)) {
+ goto fail;
+ }
+ val->type = JSON_NUMBER;
+ break;
+ }
+
+ *out = val;
+ return true;
+fail:
+ *out = NULL;
+ return false;
+}
+
+#undef json__ensure
+
+#define JSON_PRETTY_INDENT(ind) for (int i = 0; i < ind; ++i) os_file_puts(options->custom_target, strv(" "))
+
+void json__pretty_print_value(json_t *value, int indent, const json_pretty_opts_t *options) {
+ switch (value->type) {
+ case JSON_NULL:
+ os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_NULL]);
+ os_file_puts(options->custom_target, strv("null"));
+ os_log_set_colour(LOG_COL_RESET);
+ break;
+ case JSON_ARRAY:
+ os_file_puts(options->custom_target, strv("[\n"));
+ for_each (node, value->array) {
+ JSON_PRETTY_INDENT(indent + 1);
+ json__pretty_print_value(node, indent + 1, options);
+ if (node->next) {
+ os_file_putc(options->custom_target, ',');
+ }
+ os_file_putc(options->custom_target, '\n');
+ }
+ JSON_PRETTY_INDENT(indent);
+ os_file_putc(options->custom_target, ']');
+ break;
+ case JSON_STRING:
+ os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_STRING]);
+ os_file_putc(options->custom_target, '\"');
+ os_file_puts(options->custom_target, value->string);
+ os_file_putc(options->custom_target, '\"');
+ os_log_set_colour(LOG_COL_RESET);
+ break;
+ case JSON_NUMBER:
+ {
+ os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_NUM]);
+ u8 scratchbuf[256];
+ arena_t scratch = arena_make(ARENA_STATIC, sizeof(scratchbuf), scratchbuf);
+ os_file_print(
+ scratch,
+ options->custom_target,
+ "%g",
+ value->number
+ );
+ os_log_set_colour(LOG_COL_RESET);
+ break;
+ }
+ case JSON_BOOL:
+ os_log_set_colour(options->colours[value->boolean ? JSON_PRETTY_COLOUR_TRUE : JSON_PRETTY_COLOUR_FALSE]);
+ os_file_puts(options->custom_target, value->boolean ? strv("true") : strv("false"));
+ os_log_set_colour(LOG_COL_RESET);
+ break;
+ case JSON_OBJECT:
+ os_file_puts(options->custom_target, strv("{\n"));
+ for_each(node, value->object) {
+ JSON_PRETTY_INDENT(indent + 1);
+ os_log_set_colour(options->colours[JSON_PRETTY_COLOUR_KEY]);
+ os_file_putc(options->custom_target, '\"');
+ os_file_puts(options->custom_target, node->key);
+ os_file_putc(options->custom_target, '\"');
+ os_log_set_colour(LOG_COL_RESET);
+
+ os_file_puts(options->custom_target, strv(": "));
+
+ json__pretty_print_value(node, indent + 1, options);
+ if (node->next) {
+ os_file_putc(options->custom_target, ',');
+ }
+ os_file_putc(options->custom_target, '\n');
+ }
+ JSON_PRETTY_INDENT(indent);
+ os_file_putc(options->custom_target, '}');
+ break;
+ }
+}
+
+#undef JSON_PRETTY_INDENT
+
+
+// == XML ============================================
+
+xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in);
+
+xml_t xml_parse(arena_t *arena, strview_t filename) {
+ str_t str = os_file_read_all_str(arena, filename);
+ return xml_parse_str(arena, strv(str));
+}
+
+xml_t xml_parse_str(arena_t *arena, strview_t xmlstr) {
+ xml_t out = {
+ .text = xmlstr,
+ .root = alloc(arena, xmltag_t),
+ };
+
+ instream_t in = istr_init(xmlstr);
+
+ while (!istr_is_finished(&in)) {
+ xmltag_t *tag = xml__parse_tag(arena, &in);
+
+ if (out.tail) out.tail->next = tag;
+ else out.root->child = tag;
+
+ out.tail = tag;
+ }
+
+ return out;
+}
+
+xmltag_t *xml_get_tag(xmltag_t *parent, strview_t key, bool recursive) {
+ xmltag_t *t = parent ? parent->child : NULL;
+ while (t) {
+ if (strv_equals(key, t->key)) {
+ return t;
+ }
+ if (recursive && t->child) {
+ xmltag_t *out = xml_get_tag(t, key, recursive);
+ if (out) {
+ return out;
+ }
+ }
+ t = t->next;
+ }
+ return NULL;
+}
+
+strview_t xml_get_attribute(xmltag_t *tag, strview_t key) {
+ xmlattr_t *a = tag ? tag->attributes : NULL;
+ while (a) {
+ if (strv_equals(key, a->key)) {
+ return a->value;
+ }
+ a = a->next;
+ }
+ return STRV_EMPTY;
+}
+
+///// xml-private ////////////////////////////////////
+
+xmlattr_t *xml__parse_attr(arena_t *arena, instream_t *in) {
+ if (istr_peek(in) != ' ') {
+ return NULL;
+ }
+
+ strview_t key = strv_trim(istr_get_view(in, '='));
+ istr_skip(in, 2); // skip = and "
+ strview_t val = strv_trim(istr_get_view(in, '"'));
+ istr_skip(in, 1); // skip "
+
+ if (strv_is_empty(key) || strv_is_empty(val)) {
+ warn("key or value empty");
+ return NULL;
+ }
+
+ xmlattr_t *attr = alloc(arena, xmlattr_t);
+ attr->key = key;
+ attr->value = val;
+ return attr;
+}
+
+xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in) {
+ istr_skip_whitespace(in);
+
+ // we're either parsing the body, or we have finished the object
+ if (istr_peek(in) != '<' || istr_peek_next(in) == '/') {
+ return NULL;
+ }
+
+ istr_skip(in, 1); // skip <
+
+ // meta tag, we don't care about these
+ if (istr_peek(in) == '?') {
+ istr_ignore_and_skip(in, '\n');
+ return NULL;
+ }
+
+ xmltag_t *tag = alloc(arena, xmltag_t);
+
+ tag->key = strv_trim(istr_get_view_either(in, strv(" >")));
+
+ xmlattr_t *attr = xml__parse_attr(arena, in);
+ while (attr) {
+ attr->next = tag->attributes;
+ tag->attributes = attr;
+ attr = xml__parse_attr(arena, in);
+ }
+
+ // this tag does not have children, return
+ if (istr_peek(in) == '/') {
+ istr_skip(in, 2); // skip / and >
+ return tag;
+ }
+
+ istr_skip(in, 1); // skip >
+
+ xmltag_t *child = xml__parse_tag(arena, in);
+ while (child) {
+ if (tag->tail) {
+ tag->tail->next = child;
+ tag->tail = child;
+ }
+ else {
+ tag->child = tag->tail = child;
+ }
+ child = xml__parse_tag(arena, in);
+ }
+
+ // parse content
+ istr_skip_whitespace(in);
+ tag->content = istr_get_view(in, '<');
+
+ // closing tag
+ istr_skip(in, 2); // skip < and /
+ strview_t closing = strv_trim(istr_get_view(in, '>'));
+ if (!strv_equals(tag->key, closing)) {
+ warn("opening and closing tags are different!: (%v) != (%v)", tag->key, closing);
+ }
+ istr_skip(in, 1); // skip >
+ return tag;
+}
diff --git a/parsers.h b/parsers.h
new file mode 100644
index 0000000..2c17ee1
--- /dev/null
+++ b/parsers.h
@@ -0,0 +1,156 @@
+#ifndef COLLA_PARSERS_H
+#define COLLA_PARSERS_H
+
+#include "core.h"
+#include "os.h"
+#include "str.h"
+
+// == INI ============================================
+
+typedef struct inivalue_t inivalue_t;
+struct inivalue_t {
+ strview_t key;
+ strview_t value;
+ inivalue_t *next;
+};
+
+typedef struct initable_t initable_t;
+struct initable_t {
+ strview_t name;
+ inivalue_t *values;
+ inivalue_t *tail;
+ initable_t *next;
+};
+
+typedef struct ini_t ini_t;
+struct ini_t {
+ strview_t text;
+ initable_t *tables;
+ initable_t *tail;
+};
+
+typedef struct iniopt_t iniopt_t;
+struct iniopt_t {
+ bool merge_duplicate_tables; // default false
+ bool merge_duplicate_keys; // default false
+ char key_value_divider; // default =
+ strview_t comment_vals; // default ;#
+};
+
+typedef struct iniarray_t iniarray_t;
+struct iniarray_t {
+ strview_t *values;
+ usize count;
+};
+
+#define INI_ROOT strv("__ROOT__")
+
+ini_t ini_parse(arena_t *arena, strview_t filename, iniopt_t *opt);
+ini_t ini_parse_fp(arena_t *arena, oshandle_t file, iniopt_t *opt);
+ini_t ini_parse_str(arena_t *arena, strview_t str, iniopt_t *opt);
+
+bool ini_is_valid(ini_t *ini);
+
+initable_t *ini_get_table(ini_t *ini, strview_t name);
+inivalue_t *ini_get(initable_t *table, strview_t key);
+
+iniarray_t ini_as_arr(arena_t *arena, inivalue_t *value, char delim);
+u64 ini_as_uint(inivalue_t *value);
+i64 ini_as_int(inivalue_t *value);
+double ini_as_num(inivalue_t *value);
+bool ini_as_bool(inivalue_t *value);
+
+// == JSON ===========================================
+
+typedef enum jsontype_e {
+ JSON_NULL,
+ JSON_ARRAY,
+ JSON_STRING,
+ JSON_NUMBER,
+ JSON_BOOL,
+ JSON_OBJECT,
+} jsontype_e;
+
+typedef enum jsonflags_e {
+ JSON_DEFAULT = 0,
+ JSON_NO_TRAILING_COMMAS = 1 << 0,
+ JSON_NO_COMMENTS = 1 << 1,
+} jsonflags_e;
+
+typedef struct json_t json_t;
+struct json_t {
+ json_t *next;
+ json_t *prev;
+
+ strview_t key;
+
+ union {
+ json_t *array;
+ strview_t string;
+ double number;
+ bool boolean;
+ json_t *object;
+ };
+ jsontype_e type;
+};
+
+json_t *json_parse(arena_t *arena, strview_t filename, jsonflags_e flags);
+json_t *json_parse_str(arena_t *arena, strview_t str, jsonflags_e flags);
+
+json_t *json_get(json_t *node, strview_t key);
+
+#define json_check(val, js_type) ((val) && (val)->type == js_type)
+#define json_for(it, arr) for (json_t *it = json_check(arr, JSON_ARRAY) ? arr->array : NULL; it; it = it->next)
+
+typedef enum json_pretty_colours_e {
+ JSON_PRETTY_COLOUR_KEY,
+ JSON_PRETTY_COLOUR_STRING,
+ JSON_PRETTY_COLOUR_NUM,
+ JSON_PRETTY_COLOUR_NULL,
+ JSON_PRETTY_COLOUR_TRUE,
+ JSON_PRETTY_COLOUR_FALSE,
+ JSON_PRETTY_COLOUR__COUNT,
+} json_pretty_colours_e;
+
+typedef struct json_pretty_opts_t json_pretty_opts_t;
+struct json_pretty_opts_t {
+ oshandle_t custom_target;
+ bool use_custom_colours;
+ os_log_colour_e colours[JSON_PRETTY_COLOUR__COUNT];
+};
+
+void json_pretty_print(json_t *root, const json_pretty_opts_t *options);
+
+// == XML ============================================
+
+typedef struct xmlattr_t xmlattr_t;
+struct xmlattr_t {
+ strview_t key;
+ strview_t value;
+ xmlattr_t *next;
+};
+
+typedef struct xmltag_t xmltag_t;
+struct xmltag_t {
+ strview_t key;
+ xmlattr_t *attributes;
+ strview_t content;
+ xmltag_t *child;
+ xmltag_t *tail;
+ xmltag_t *next;
+};
+
+typedef struct xml_t xml_t;
+struct xml_t {
+ strview_t text;
+ xmltag_t *root;
+ xmltag_t *tail;
+};
+
+xml_t xml_parse(arena_t *arena, strview_t filename);
+xml_t xml_parse_str(arena_t *arena, strview_t xmlstr);
+
+xmltag_t *xml_get_tag(xmltag_t *parent, strview_t key, bool recursive);
+strview_t xml_get_attribute(xmltag_t *tag, strview_t key);
+
+#endif
\ No newline at end of file
diff --git a/server.c b/server.c
deleted file mode 100644
index 5a57cc3..0000000
--- a/server.c
+++ /dev/null
@@ -1,454 +0,0 @@
-#include "server.h"
-
-#include
-
-#include "socket.h"
-#include "tracelog.h"
-#include "strstream.h"
-#include "arena.h"
-
-#if COLLA_NOHTTP
-const char *httpGetStatusString(int status) {
- switch (status) {
- case 200: return "OK";
- case 201: return "CREATED";
- case 202: return "ACCEPTED";
- case 204: return "NO CONTENT";
- case 205: return "RESET CONTENT";
- case 206: return "PARTIAL CONTENT";
-
- case 300: return "MULTIPLE CHOICES";
- case 301: return "MOVED PERMANENTLY";
- case 302: return "MOVED TEMPORARILY";
- case 304: return "NOT MODIFIED";
-
- case 400: return "BAD REQUEST";
- case 401: return "UNAUTHORIZED";
- case 403: return "FORBIDDEN";
- case 404: return "NOT FOUND";
- case 407: return "RANGE NOT SATISFIABLE";
-
- case 500: return "INTERNAL SERVER_ERROR";
- case 501: return "NOT IMPLEMENTED";
- case 502: return "BAD GATEWAY";
- case 503: return "SERVICE NOT AVAILABLE";
- case 504: return "GATEWAY TIMEOUT";
- case 505: return "VERSION NOT SUPPORTED";
- }
-
- return "UNKNOWN";
-}
-#endif
-
-#define SERVER_BUFSZ 4096
-
-typedef enum {
- PARSE_REQ_BEGIN,
- PARSE_REQ_PAGE,
- PARSE_REQ_VERSION,
- PARSE_REQ_FIELDS,
- PARSE_REQ_FINISHED,
- PARSE_REQ_FAILED,
-} server__req_status_e;
-
-typedef struct {
- server_req_t req;
- server__req_status_e status;
- char fullbuf[SERVER_BUFSZ * 2];
- usize prevbuf_len;
-} server__req_ctx_t;
-
-
-typedef struct server__route_t {
- str_t page;
- server_route_f fn;
- void *userdata;
- struct server__route_t *next;
-} server__route_t;
-
-typedef struct server_t {
- socket_t socket;
- server__route_t *routes;
- server__route_t *routes_default;
- socket_t current_client;
- bool should_stop;
- uint16 port;
-} server_t;
-
-bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SERVER_BUFSZ], usize buflen) {
- memcpy(ctx->fullbuf + ctx->prevbuf_len, buffer, buflen);
- usize fulllen = ctx->prevbuf_len + buflen;
-
- instream_t in = istrInitLen(ctx->fullbuf, fulllen);
-
-#define RESET_STREAM() in.cur = in.start + begin
-#define BEGIN_STREAM() begin = istrTell(in)
-
- usize begin = istrTell(in);
-
- switch (ctx->status) {
- case PARSE_REQ_BEGIN:
- {
- BEGIN_STREAM();
-
- strview_t method = istrGetView(&in, ' ');
- if (istrGet(&in) != ' ') {
- RESET_STREAM();
- break;
- }
-
- if (strvEquals(method, strv("GET"))) {
- ctx->req.method = HTTP_GET;
- }
- else if(strvEquals(method, strv("POST"))) {
- ctx->req.method = HTTP_POST;
- }
- else {
- err("unknown method: (%.*s)", method.len, method.buf);
- ctx->status = PARSE_REQ_FAILED;
- break;
- }
-
- ctx->status = PARSE_REQ_PAGE;
- }
- // fallthrough
- case PARSE_REQ_PAGE:
- {
- BEGIN_STREAM();
-
- strview_t page = istrGetView(&in, ' ');
- if (istrGet(&in) != ' ') {
- RESET_STREAM();
- break;
- }
-
- ctx->req.page = str(arena, page);
-
- ctx->status = PARSE_REQ_VERSION;
- }
- // fallthrough
- case PARSE_REQ_VERSION:
- {
- BEGIN_STREAM();
-
- strview_t version = istrGetView(&in, '\n');
- if (istrGet(&in) != '\n') {
- RESET_STREAM();
- break;
- }
-
- if (version.len < 8) {
- err("version too short: (%.*s)", version.len, version.buf);
- ctx->status = PARSE_REQ_FAILED;
- break;
- }
-
- if (!strvEquals(strvSub(version, 0, 4), strv("HTTP"))) {
- err("version does not start with HTTP: (%.4s)", version.buf);
- ctx->status = PARSE_REQ_FAILED;
- break;
- }
-
- // skip HTTP
- version = strvRemovePrefix(version, 4);
-
- uint8 major, minor;
- int scanned = sscanf(version.buf, "/%hhu.%hhu", &major, &minor);
-
- if (scanned != 2) {
- err("could not scan both major and minor from version: (%.*s)", version.len, version.buf);
- ctx->status = PARSE_REQ_FAILED;
- break;
- }
-
- ctx->req.version_major = major;
- ctx->req.version_minor = minor;
-
- ctx->status = PARSE_REQ_FIELDS;
- }
- // fallthrough
- case PARSE_REQ_FIELDS:
- {
- bool finished_parsing = false;
-
- while (true) {
- BEGIN_STREAM();
-
- strview_t field = istrGetView(&in, '\n');
- if (istrGet(&in) != '\n') {
- RESET_STREAM();
- break;
- }
-
- instream_t field_in = istrInitLen(field.buf, field.len);
-
- strview_t key = istrGetView(&field_in, ':');
- if (istrGet(&field_in) != ':') {
- err("field does not have ':' (%.*s)", field.len, field.buf);
- ctx->status = PARSE_REQ_FAILED;
- break;
- }
-
- istrSkipWhitespace(&field_in);
-
- strview_t value = istrGetView(&field_in, '\r');
- if (istrGet(&field_in) != '\r') {
- warn("field does not finish with \\r: (%.*s)", field.len, field.buf);
- RESET_STREAM();
- break;
- }
-
- server_field_t *new_field = alloc(arena, server_field_t);
- new_field->key = str(arena, key);
- new_field->value = str(arena, value);
-
- if (!ctx->req.fields) {
- ctx->req.fields = new_field;
- }
-
- if (ctx->req.fields_tail) {
- ctx->req.fields_tail->next = new_field;
- }
-
- ctx->req.fields_tail = new_field;
-
- // check if we finished parsing the fields
- if (istrGet(&in) == '\r') {
- if (istrGet(&in) == '\n') {
- finished_parsing = true;
- break;
- }
- else {
- istrRewindN(&in, 2);
- warn("should have finished parsing field, but apparently not?: (%.*s)", in.cur, istrRemaining(in));
- }
- }
- else {
- istrRewindN(&in, 1);
- }
- }
-
- if (!finished_parsing) {
- break;
- }
-
- ctx->status = PARSE_REQ_FINISHED;
- break;
- }
- default: break;
- }
-
-#undef RESET_STREAM
-
- ctx->prevbuf_len = istrRemaining(in);
-
- memmove(ctx->fullbuf, ctx->fullbuf + istrTell(in), ctx->prevbuf_len);
-
- return ctx->status >= PARSE_REQ_FINISHED;
-}
-
-void server__parse_req_url(arena_t *arena, server_req_t *req) {
- instream_t in = istrInitLen(req->page.buf, req->page.len);
- istrIgnore(&in, '?');
- // no fields in url
- if (istrGet(&in) != '?') {
- return;
- }
-
- req->page.len = istrTell(in) - 1;
- req->page.buf[req->page.len] = '\0';
-
- while (!istrIsFinished(in)) {
- strview_t field = istrGetView(&in, '&');
- istrSkip(&in, 1); // skip &
- usize pos = strvFind(field, '=', 0);
- if (pos == SIZE_MAX) {
- fatal("url parameter does not include =: %.*s", field.buf, field.len);
- }
- strview_t key = strvSub(field, 0, pos);
- strview_t val = strvSub(field, pos + 1, SIZE_MAX);
-
- server_field_t *f = alloc(arena, server_field_t);
- f->key = str(arena, key);
- f->value = str(arena, val);
- f->next = req->page_fields;
- req->page_fields = f;
- }
-}
-
-server_t *serverSetup(arena_t *arena, uint16 port, bool try_next_port) {
- if (!skInit()) {
- fatal("couldn't initialise sockets: %s", skGetErrorString());
- }
-
- socket_t sk = skOpen(SOCK_TCP);
- if (!skIsValid(sk)) {
- fatal("couldn't open socket: %s", skGetErrorString());
- }
-
- bool bound = false;
-
- while (!bound) {
- skaddrin_t addr = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = INADDR_ANY,
- .sin_port = htons(port),
- };
-
- bound = skBindPro(sk, (skaddr_t *)&addr, sizeof(addr));
-
- if (!bound && try_next_port) {
- port++;
- }
- }
-
- if (!bound) {
- fatal("couldn't open socket: %s", skGetErrorString());
- }
-
- if (!skListenPro(sk, 10)) {
- fatal("could not listen on socket: %s", skGetErrorString());
- }
-
- server_t *server = alloc(arena, server_t);
-
- server->socket = sk;
- server->port = port;
-
- return server;
-}
-
-void serverRoute(arena_t *arena, server_t *server, strview_t page, server_route_f cb, void *userdata) {
- // check if a route for that page already exists
- server__route_t *r = server->routes;
-
- while (r) {
- if (strvEquals(strv(r->page), page)) {
- r->fn = cb;
- r->userdata = userdata;
- break;
- }
- r = r->next;
- }
-
- // no route found, make a new one
- if (!r) {
- r = alloc(arena, server__route_t);
- r->next = server->routes;
- server->routes = r;
-
- r->page = str(arena, page);
- }
-
- r->fn = cb;
- r->userdata = userdata;
-}
-
-void serverRouteDefault(arena_t *arena, server_t *server, server_route_f cb, void *userdata) {
- server__route_t *r = server->routes_default;
-
- if (!r) {
- r = alloc(arena, server__route_t);
- server->routes_default = r;
- }
-
- r->fn = cb;
- r->userdata = userdata;
-}
-
-void serverStart(arena_t scratch, server_t *server) {
- usize start = arenaTell(&scratch);
-
- info("Server started at (http://localhost:%d)!", server->port);
-
- while (!server->should_stop) {
- socket_t client = skAccept(server->socket);
- if (!skIsValid(client)) {
- err("accept failed");
- continue;
- }
-
- arenaRewind(&scratch, start);
-
- server__req_ctx_t req_ctx = {0};
-
- char buffer[SERVER_BUFSZ];
- int read = 0;
- do {
- read = skReceive(client, buffer, sizeof(buffer));
- if(read == SOCKET_ERROR) {
- fatal("couldn't get the data from the server: %s", skGetErrorString());
- }
- if (server__parse_chunk(&scratch, &req_ctx, buffer, read)) {
- break;
- }
- } while(read != 0);
-
- if (req_ctx.status == PARSE_REQ_FAILED || req_ctx.status == PARSE_REQ_BEGIN) {
- err("failed to parse request!");
- goto end_connection;
- }
-
- server_req_t req = req_ctx.req;
-
- server__parse_req_url(&scratch, &req);
-
- server__route_t *route = server->routes;
- while (route) {
- if (strEquals(route->page, req.page)) {
- break;
- }
- route = route->next;
- }
-
- if (!route) {
- route = server->routes_default;
- }
-
- server->current_client = client;
- str_t response = route->fn(scratch, server, &req, route->userdata);
-
- if (!skIsValid(server->current_client)) {
- continue;
- }
-
- skSend(client, response.buf, (int)response.len);
-
-end_connection:
- skClose(client);
- }
-
- if (!skClose(server->socket)) {
- fatal("couldn't close socket: %s", skGetErrorString());
- }
-}
-
-void serverStop(server_t *server) {
- server->should_stop = true;
-}
-
-str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type, strview_t body) {
- return strFmt(
- arena,
-
- "HTTP/1.1 %d %s\r\n"
- "Content-Type: %v\r\n"
- "\r\n"
- "%v",
-
- status_code, httpGetStatusString(status_code),
- content_type,
- body
- );
-}
-
-socket_t serverGetClient(server_t *server) {
- return server->current_client;
-}
-
-void serverSetClient(server_t *server, socket_t client) {
- server->current_client = client;
-}
-
-
-#undef SERVER_BUFSZ
\ No newline at end of file
diff --git a/server.h b/server.h
deleted file mode 100644
index 0cd59b1..0000000
--- a/server.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-#include "str.h"
-#include "http.h"
-
-typedef struct arena_t arena_t;
-typedef struct server_t server_t;
-
-typedef struct server_field_t {
- str_t key;
- str_t value;
- struct server_field_t *next;
-} server_field_t;
-
-typedef struct {
- http_method_e method;
- str_t page;
- server_field_t *page_fields;
- uint8 version_minor;
- uint8 version_major;
- server_field_t *fields;
- server_field_t *fields_tail;
- // buffer_t body;
-} server_req_t;
-
-typedef str_t (*server_route_f)(arena_t scratch, server_t *server, server_req_t *req, void *userdata);
-
-server_t *serverSetup(arena_t *arena, uint16 port, bool try_next_port);
-void serverRoute(arena_t *arena, server_t *server, strview_t page, server_route_f cb, void *userdata);
-void serverRouteDefault(arena_t *arena, server_t *server, server_route_f cb, void *userdata);
-void serverStart(arena_t scratch, server_t *server);
-void serverStop(server_t *server);
-
-str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type, strview_t body);
-socket_t serverGetClient(server_t *server);
-void serverSetClient(server_t *server, socket_t client);
\ No newline at end of file
diff --git a/sha1.c b/sha1.c
deleted file mode 100644
index 498759b..0000000
--- a/sha1.c
+++ /dev/null
@@ -1,120 +0,0 @@
-#include "sha1.h"
-
-sha1_t sha1_init(void) {
- return (sha1_t) {
- .digest = {
- 0x67452301,
- 0xEFCDAB89,
- 0x98BADCFE,
- 0x10325476,
- 0xC3D2E1F0,
- },
- };
-}
-
-uint32 sha1_left_rotate(uint32 value, uint32 count) {
- return (value << count) ^ (value >> (32 - count));
-}
-
-void sha1_process_block(sha1_t *ctx) {
- uint32 w [80];
- for (usize i = 0; i < 16; ++i) {
- w[i] = ctx->block[i * 4 + 0] << 24;
- w[i] |= ctx->block[i * 4 + 1] << 16;
- w[i] |= ctx->block[i * 4 + 2] << 8;
- w[i] |= ctx->block[i * 4 + 3] << 0;
- }
-
- for (usize i = 16; i < 80; ++i) {
- w[i] = sha1_left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
- }
-
- uint32 a = ctx->digest[0];
- uint32 b = ctx->digest[1];
- uint32 c = ctx->digest[2];
- uint32 d = ctx->digest[3];
- uint32 e = ctx->digest[4];
-
- for (usize i = 0; i < 80; ++i) {
- uint32 f = 0;
- uint32 k = 0;
-
- if (i<20) {
- f = (b & c) | (~b & d);
- k = 0x5A827999;
- } else if (i<40) {
- f = b ^ c ^ d;
- k = 0x6ED9EBA1;
- } else if (i<60) {
- f = (b & c) | (b & d) | (c & d);
- k = 0x8F1BBCDC;
- } else {
- f = b ^ c ^ d;
- k = 0xCA62C1D6;
- }
- uint32 temp = sha1_left_rotate(a, 5) + f + e + k + w[i];
- e = d;
- d = c;
- c = sha1_left_rotate(b, 30);
- b = a;
- a = temp;
- }
-
- ctx->digest[0] += a;
- ctx->digest[1] += b;
- ctx->digest[2] += c;
- ctx->digest[3] += d;
- ctx->digest[4] += e;
-}
-
-void sha1_process_byte(sha1_t *ctx, uint8 b) {
- ctx->block[ctx->block_index++] = b;
- ++ctx->byte_count;
- if (ctx->block_index == 64) {
- ctx->block_index = 0;
- sha1_process_block(ctx);
- }
-}
-
-sha1_result_t sha1(sha1_t *ctx, const void *buf, usize len) {
- const uint8 *block = buf;
-
- for (usize i = 0; i < len; ++i) {
- sha1_process_byte(ctx, block[i]);
- }
-
- usize bitcount = ctx->byte_count * 8;
- sha1_process_byte(ctx, 0x80);
-
- if (ctx->block_index > 56) {
- while (ctx->block_index != 0) {
- sha1_process_byte(ctx, 0);
- }
- while (ctx->block_index < 56) {
- sha1_process_byte(ctx, 0);
- }
- } else {
- while (ctx->block_index < 56) {
- sha1_process_byte(ctx, 0);
- }
- }
- sha1_process_byte(ctx, 0);
- sha1_process_byte(ctx, 0);
- sha1_process_byte(ctx, 0);
- sha1_process_byte(ctx, 0);
- sha1_process_byte(ctx, (uchar)((bitcount >> 24) & 0xFF));
- sha1_process_byte(ctx, (uchar)((bitcount >> 16) & 0xFF));
- sha1_process_byte(ctx, (uchar)((bitcount >> 8 ) & 0xFF));
- sha1_process_byte(ctx, (uchar)((bitcount >> 0 ) & 0xFF));
-
- // memcpy(digest, m_digest, 5 * sizeof(uint32_t));#
-
- sha1_result_t result = {0};
- memcpy(result.digest, ctx->digest, sizeof(result.digest));
- return result;
-}
-
-str_t sha1Str(arena_t *arena, sha1_t *ctx, const void *buf, usize len) {
- sha1_result_t result = sha1(ctx, buf, len);
- return strFmt(arena, "%08x%08x%08x%08x%08x", result.digest[0], result.digest[1], result.digest[2], result.digest[3], result.digest[4]);
-}
\ No newline at end of file
diff --git a/sha1.h b/sha1.h
deleted file mode 100644
index b240f84..0000000
--- a/sha1.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include "str.h"
-
-typedef struct {
- uint32 digest[5];
- uint8 block[64];
- usize block_index;
- usize byte_count;
-} sha1_t;
-
-typedef struct {
- uint32 digest[5];
-} sha1_result_t;
-
-sha1_t sha1_init(void);
-sha1_result_t sha1(sha1_t *ctx, const void *buf, usize len);
-str_t sha1Str(arena_t *arena, sha1_t *ctx, const void *buf, usize len);
diff --git a/socket.c b/socket.c
deleted file mode 100644
index 2ac3e24..0000000
--- a/socket.c
+++ /dev/null
@@ -1,286 +0,0 @@
-#include "socket.h"
-
-#if COLLA_WIN && COLLA_CMT_LIB
-#pragma comment(lib, "Ws2_32")
-#endif
-
-#if COLLA_WIN
-
-typedef int socklen_t;
-
-bool skInit(void) {
- WSADATA w;
- int error = WSAStartup(0x0202, &w);
- return error == 0;
-}
-
-bool skCleanup(void) {
- return WSACleanup() == 0;
-}
-
-bool skClose(socket_t sock) {
- return closesocket(sock) != SOCKET_ERROR;
-}
-
-int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout) {
- return WSAPoll(to_poll, num_to_poll, timeout);
-}
-
-int skGetError(void) {
- return WSAGetLastError();
-}
-
-#else
-
-#include
-#include
-#include
-#include
-#include // strerror
-#include
-
-bool skInit(void) {
- return true;
-}
-
-bool skCleanup(void) {
- return true;
-}
-
-bool skClose(socket_t sock) {
- return close(sock) != SOCKET_ERROR;
-}
-
-int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout) {
- return poll(to_poll, num_to_poll, timeout);
-}
-
-int skGetError(void) {
- return errno;
-}
-
-const char *skGetErrorString(void) {
- return strerror(errno);
-}
-
-#endif
-
-socket_t skOpen(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 skOpenPro(AF_INET, sock_type, 0);
-}
-
-socket_t skOpenEx(const char *protocol) {
- struct protoent *proto = getprotobyname(protocol);
- if(!proto) {
- return (socket_t){0};
- }
- return skOpenPro(AF_INET, SOCK_STREAM, proto->p_proto);
-}
-
-socket_t skOpenPro(int af, int type, int protocol) {
- return socket(af, type, protocol);
-}
-
-bool skIsValid(socket_t sock) {
- return sock != SOCKET_ERROR;
-}
-
-skaddrin_t skAddrinInit(const char *ip, uint16_t port) {
- return (skaddrin_t){
- .sin_family = AF_INET,
- .sin_port = htons(port),
- // TODO use inet_pton instead
- .sin_addr.s_addr = inet_addr(ip),
- };
-}
-
-bool skBind(socket_t sock, const char *ip, uint16_t port) {
- skaddrin_t addr = skAddrinInit(ip, port);
- return skBindPro(sock, (skaddr_t *)&addr, sizeof(addr));
-}
-
-bool skBindPro(socket_t sock, const skaddr_t *name, int namelen) {
- return bind(sock, name, namelen) != SOCKET_ERROR;
-}
-
-bool skListen(socket_t sock) {
- return skListenPro(sock, 1);
-}
-
-bool skListenPro(socket_t sock, int backlog) {
- return listen(sock, backlog) != SOCKET_ERROR;
-}
-
-socket_t skAccept(socket_t sock) {
- skaddrin_t addr = {0};
- int addr_size = sizeof(addr);
- return skAcceptPro(sock, (skaddr_t *)&addr, &addr_size);
-}
-
-socket_t skAcceptPro(socket_t sock, skaddr_t *addr, int *addrlen) {
- return accept(sock, addr, (socklen_t *)addrlen);
-}
-
-bool skConnect(socket_t sock, const char *server, unsigned short server_port) {
- // TODO use getaddrinfo insetad
- struct hostent *host = gethostbyname(server);
- // if gethostbyname fails, inet_addr will also fail and return an easier to debug error
- const char *address = server;
- if(host) {
- address = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
- }
-
- skaddrin_t addr = skAddrinInit(address, server_port);
-
- return skConnectPro(sock, (skaddr_t *)&addr, sizeof(addr));
-}
-
-bool skConnectPro(socket_t sock, const skaddr_t *name, int namelen) {
- return connect(sock, name, namelen) != SOCKET_ERROR;
-}
-
-int skSend(socket_t sock, const void *buf, int len) {
- return skSendPro(sock, buf, len, 0);
-}
-
-int skSendPro(socket_t sock, const void *buf, int len, int flags) {
- return send(sock, buf, len, flags);
-}
-
-int skSendTo(socket_t sock, const void *buf, int len, const skaddrin_t *to) {
- return skSendToPro(sock, buf, len, 0, (skaddr_t*)to, sizeof(skaddrin_t));
-}
-
-int skSendToPro(socket_t sock, const void *buf, int len, int flags, const skaddr_t *to, int tolen) {
- return sendto(sock, buf, len, flags, to, tolen);
-}
-
-int skReceive(socket_t sock, void *buf, int len) {
- return skReceivePro(sock, buf, len, 0);
-}
-
-int skReceivePro(socket_t sock, void *buf, int len, int flags) {
- return recv(sock, buf, len, flags);
-}
-
-int skReceiveFrom(socket_t sock, void *buf, int len, skaddrin_t *from) {
- int fromlen = sizeof(skaddr_t);
- return skReceiveFromPro(sock, buf, len, 0, (skaddr_t*)from, &fromlen);
-}
-
-int skReceiveFromPro(socket_t sock, void *buf, int len, int flags, skaddr_t *from, int *fromlen) {
- return recvfrom(sock, buf, len, flags, from, (socklen_t *)fromlen);
-}
-
-// put this at the end of file to not make everything else unreadable
-#if COLLA_WIN
-const char *skGetErrorString(void) {
- switch(skGetError()) {
- case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
- case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
- case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
- case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
- case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
- case WSA_IO_PENDING: return "Overlapped operations will complete later.";
- case WSAEINTR: return "Interrupted function call.";
- case WSAEBADF: return "File handle is not valid.";
- case WSAEACCES: return "Permission denied.";
- case WSAEFAULT: return "Bad address.";
- case WSAEINVAL: return "Invalid argument.";
- case WSAEMFILE: return "Too many open files.";
- case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
- case WSAEINPROGRESS: return "Operation now in progress.";
- case WSAEALREADY: return "Operation already in progress.";
- case WSAENOTSOCK: return "Socket operation on nonsocket.";
- case WSAEDESTADDRREQ: return "Destination address required.";
- case WSAEMSGSIZE: return "Message too long.";
- case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
- case WSAENOPROTOOPT: return "Bad protocol option.";
- case WSAEPROTONOSUPPORT: return "Protocol not supported.";
- case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
- case WSAEOPNOTSUPP: return "Operation not supported.";
- case WSAEPFNOSUPPORT: return "Protocol family not supported.";
- case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
- case WSAEADDRINUSE: return "Address already in use.";
- case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
- case WSAENETDOWN: return "Network is down.";
- case WSAENETUNREACH: return "Network is unreachable.";
- case WSAENETRESET: return "Network dropped connection on reset.";
- case WSAECONNABORTED: return "Software caused connection abort.";
- case WSAECONNRESET: return "Connection reset by peer.";
- case WSAENOBUFS: return "No buffer space available.";
- case WSAEISCONN: return "Socket is already connected.";
- case WSAENOTCONN: return "Socket is not connected.";
- case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
- case WSAETOOMANYREFS: return "Too many references.";
- case WSAETIMEDOUT: return "Connection timed out.";
- case WSAECONNREFUSED: return "Connection refused.";
- case WSAELOOP: return "Cannot translate name.";
- case WSAENAMETOOLONG: return "Name too long.";
- case WSAEHOSTDOWN: return "Host is down.";
- case WSAEHOSTUNREACH: return "No route to host.";
- case WSAENOTEMPTY: return "Directory not empty.";
- case WSAEPROCLIM: return "Too many processes.";
- case WSAEUSERS: return "User quota exceeded.";
- case WSAEDQUOT: return "Disk quota exceeded.";
- case WSAESTALE: return "Stale file handle reference.";
- case WSAEREMOTE: return "Item is remote.";
- case WSASYSNOTREADY: return "Network subsystem is unavailable.";
- case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range.";
- case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
- case WSAEDISCON: return "Graceful shutdown in progress.";
- case WSAENOMORE: return "No more results.";
- case WSAECANCELLED: return "Call has been canceled.";
- case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid.";
- case WSAEINVALIDPROVIDER: return "Service provider is invalid.";
- case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize.";
- case WSASYSCALLFAILURE: return "System call failure.";
- case WSASERVICE_NOT_FOUND: return "Service not found.";
- case WSATYPE_NOT_FOUND: return "Class type not found.";
- case WSA_E_NO_MORE: return "No more results.";
- case WSA_E_CANCELLED: return "Call was canceled.";
- case WSAEREFUSED: return "Database query was refused.";
- case WSAHOST_NOT_FOUND: return "Host not found.";
- case WSATRY_AGAIN: return "Nonauthoritative host not found.";
- case WSANO_RECOVERY: return "This is a nonrecoverable error.";
- case WSANO_DATA: return "Valid name, no data record of requested type.";
- case WSA_QOS_RECEIVERS: return "QoS receivers.";
- case WSA_QOS_SENDERS: return "QoS senders.";
- case WSA_QOS_NO_SENDERS: return "No QoS senders.";
- case WSA_QOS_NO_RECEIVERS: return "QoS no receivers.";
- case WSA_QOS_REQUEST_CONFIRMED: return "QoS request confirmed.";
- case WSA_QOS_ADMISSION_FAILURE: return "QoS admission error.";
- case WSA_QOS_POLICY_FAILURE: return "QoS policy failure.";
- case WSA_QOS_BAD_STYLE: return "QoS bad style.";
- case WSA_QOS_BAD_OBJECT: return "QoS bad object.";
- case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QoS traffic control error.";
- case WSA_QOS_GENERIC_ERROR: return "QoS generic error.";
- case WSA_QOS_ESERVICETYPE: return "QoS service type error.";
- case WSA_QOS_EFLOWSPEC: return "QoS flowspec error.";
- case WSA_QOS_EPROVSPECBUF: return "Invalid QoS provider buffer.";
- case WSA_QOS_EFILTERSTYLE: return "Invalid QoS filter style.";
- case WSA_QOS_EFILTERTYPE: return "Invalid QoS filter type.";
- case WSA_QOS_EFILTERCOUNT: return "Incorrect QoS filter count.";
- case WSA_QOS_EOBJLENGTH: return "Invalid QoS object length.";
- case WSA_QOS_EFLOWCOUNT: return "Incorrect QoS flow count.";
- case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QoS object.";
- case WSA_QOS_EPOLICYOBJ: return "Invalid QoS policy object.";
- case WSA_QOS_EFLOWDESC: return "Invalid QoS flow descriptor.";
- case WSA_QOS_EPSFLOWSPEC: return "Invalid QoS provider-specific flowspec.";
- case WSA_QOS_EPSFILTERSPEC: return "Invalid QoS provider-specific filterspec.";
- case WSA_QOS_ESDMODEOBJ: return "Invalid QoS shape discard mode object.";
- case WSA_QOS_ESHAPERATEOBJ: return "Invalid QoS shaping rate object.";
- case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QoS element type.";
- }
-
- return "(nothing)";
-}
-#endif
\ No newline at end of file
diff --git a/socket.h b/socket.h
deleted file mode 100644
index 024685c..0000000
--- a/socket.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-
-#if COLLA_TCC
-#include "tcc/colla_tcc.h"
-#elif COLLA_WIN
-#define _WINSOCK_DEPRECATED_NO_WARNINGS
-#include
-#elif COLLA_LIN || COLLA_OSX
-#include
-#include
-#endif
-
-typedef uintptr_t socket_t;
-typedef struct sockaddr skaddr_t;
-typedef struct sockaddr_in skaddrin_t;
-typedef struct pollfd skpoll_t;
-
-#define SOCKET_ERROR (-1)
-
-typedef enum {
- SOCK_TCP,
- SOCK_UDP,
-} sktype_e;
-
-// Initialize sockets, returns true on success
-bool skInit(void);
-// Terminates sockets, returns true on success
-bool skCleanup(void);
-
-// Opens a socket, check socket_t with skValid
-socket_t skOpen(sktype_e type);
-// Opens a socket using 'protocol', options are
-// ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp
-// check socket_t with skValid
-socket_t skOpenEx(const char *protocol);
-// Opens a socket, check socket_t with skValid
-socket_t skOpenPro(int af, int type, int protocol);
-
-// Checks that a opened socket is valid, returns true on success
-bool skIsValid(socket_t sock);
-
-// Closes a socket, returns true on success
-bool skClose(socket_t sock);
-
-// Fill out a sk_addrin_t structure with "ip" and "port"
-skaddrin_t skAddrinInit(const char *ip, uint16_t port);
-
-// Associate a local address with a socket
-bool skBind(socket_t sock, const char *ip, uint16_t port);
-// Associate a local address with a socket
-bool skBindPro(socket_t sock, const skaddr_t *name, int namelen);
-
-// Place a socket in a state in which it is listening for an incoming connection
-bool skListen(socket_t sock);
-// Place a socket in a state in which it is listening for an incoming connection
-bool skListenPro(socket_t sock, int backlog);
-
-// Permits an incoming connection attempt on a socket
-socket_t skAccept(socket_t sock);
-// Permits an incoming connection attempt on a socket
-socket_t skAcceptPro(socket_t sock, skaddr_t *addr, int *addrlen);
-
-// Connects to a server (e.g. "127.0.0.1" or "google.com") with a port(e.g. 1234), returns true on success
-bool skConnect(socket_t sock, const char *server, unsigned short server_port);
-// Connects to a server, returns true on success
-bool skConnectPro(socket_t sock, const skaddr_t *name, int namelen);
-
-// Sends data on a socket, returns true on success
-int skSend(socket_t sock, const void *buf, int len);
-// Sends data on a socket, returns true on success
-int skSendPro(socket_t sock, const void *buf, int len, int flags);
-// Sends data to a specific destination
-int skSendTo(socket_t sock, const void *buf, int len, const skaddrin_t *to);
-// Sends data to a specific destination
-int skSendToPro(socket_t sock, const void *buf, int len, int flags, const skaddr_t *to, int tolen);
-// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
-int skReceive(socket_t sock, void *buf, int len);
-// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
-int skReceivePro(socket_t sock, void *buf, int len, int flags);
-// Receives a datagram and stores the source address.
-int skReceiveFrom(socket_t sock, void *buf, int len, skaddrin_t *from);
-// Receives a datagram and stores the source address.
-int skReceiveFromPro(socket_t sock, void *buf, int len, int flags, skaddr_t *from, int *fromlen);
-
-// Wait for an event on some sockets
-int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout);
-
-// Returns latest socket error, returns 0 if there is no error
-int skGetError(void);
-// Returns a human-readable string from a skGetError
-const char *skGetErrorString(void);
diff --git a/str.c b/str.c
index 3ee1018..acca8f9 100644
--- a/str.c
+++ b/str.c
@@ -1,135 +1,143 @@
#include "str.h"
-#include "warnings/colla_warn_beg.h"
-
-#include "arena.h"
-#include "format.h"
-#include "tracelog.h"
+#include
+#include
+#include
#if COLLA_WIN
-
-#include
-
+#include "win/str_win32.c"
#else
-
-#include
-
-#endif
-
-#if COLLA_TCC
-#include "tcc/colla_tcc.h"
+#error "platform not supported"
#endif
// == STR_T ========================================================
-str_t strInit(arena_t *arena, const char *buf) {
- return buf ? strInitLen(arena, buf, strlen(buf)) : STR_EMPTY;
+str_t str_init(arena_t *arena, const char *buf) {
+ return str_init_len(arena, buf, buf ? strlen(buf) : 0);
}
-str_t strInitLen(arena_t *arena, const char *buf, usize len) {
- if (!buf || !len) return STR_EMPTY;
-
- str_t out = {
- .buf = alloc(arena, char, len + 1),
- .len = len
- };
-
- memcpy(out.buf, buf, len);
-
- return out;
+str_t str_init_len(arena_t *arena, const char *buf, usize len) {
+ if (!buf || !len) return STR_EMPTY;
+ char *str = alloc(arena, char, len + 1);
+ memmove(str, buf, len);
+ return (str_t){ str, len };
}
-str_t strInitView(arena_t *arena, strview_t view) {
- return strInitLen(arena, view.buf, view.len);
+str_t str_init_view(arena_t *arena, strview_t view) {
+ return str_init_len(arena, view.buf, view.len);
}
-str_t strFmt(arena_t *arena, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- str_t out = strFmtv(arena, fmt, args);
- va_end(args);
- return out;
+str_t str_fmt(arena_t *arena, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ str_t out = str_fmtv(arena, fmt, args);
+ va_end(args);
+ return out;
}
-str_t strFmtv(arena_t *arena, const char *fmt, va_list args) {
+str_t str_fmtv(arena_t *arena, const char *fmt, va_list args) {
va_list vcopy;
va_copy(vcopy, args);
// stb_vsnprintf returns the length + null_term
- int len = fmtBufferv(NULL, 0, fmt, vcopy);
+ int len = fmt_bufferv(NULL, 0, fmt, vcopy);
va_end(vcopy);
char *buffer = alloc(arena, char, len + 1);
- fmtBufferv(buffer, len + 1, fmt, args);
+ fmt_bufferv(buffer, len + 1, fmt, args);
return (str_t) { .buf = buffer, .len = (usize)len };
}
-str_t strFromWChar(arena_t *arena, const wchar_t *src) {
- return strFromWCharLen(arena, src, 0);
+tstr_t tstr_init(TCHAR *str, usize optional_len) {
+ if (str && !optional_len) {
+#if COLLA_UNICODE
+ optional_len = wcslen(str);
+#else
+ optional_len = strlen(str);
+#endif
+ }
+ return (tstr_t){
+ .buf = str,
+ .len = optional_len,
+ };
}
-str_t strFromWCharLen(arena_t *arena, const wchar_t *src, usize srclen) {
- if (!src) return STR_EMPTY;
- if (!srclen) srclen = wcslen(src);
-
- str_t out = {0};
-
-#if COLLA_WIN
- int outlen = WideCharToMultiByte(
- CP_UTF8, 0,
- src, (int)srclen,
- NULL, 0,
- NULL, NULL
- );
-
- if (outlen == 0) {
- unsigned long error = GetLastError();
- if (error == ERROR_NO_UNICODE_TRANSLATION) {
- err("couldn't translate wide string (%S) to utf8, no unicode translation", src);
- }
- else {
- err("couldn't translate wide string (%S) to utf8, %u", error);
- }
-
- return STR_EMPTY;
+str16_t str16_init(u16 *str, usize optional_len) {
+ if (str && !optional_len) {
+ optional_len = wcslen(str);
}
+ return (str16_t){
+ .buf = str,
+ .len = optional_len,
+ };
+}
- out.buf = alloc(arena, char, outlen + 1);
- WideCharToMultiByte(
- CP_UTF8, 0,
- src, (int)srclen,
- out.buf, outlen,
- NULL, NULL
- );
+str_t str_from_str16(arena_t *arena, str16_t src) {
+ if (!src.buf) return STR_EMPTY;
+ if (!src.len) return STR_EMPTY;
- out.len = outlen;
-
-#elif COLLA_LIN
- fatal("strFromWChar not implemented yet!");
-#endif
+ str_t out = str_os_from_str16(arena, src);
return out;
}
-bool strEquals(str_t a, str_t b) {
- return strCompare(a, b) == 0;
+str_t str_from_tstr(arena_t *arena, tstr_t src) {
+#if COLLA_UNICODE
+ return str_from_str16(arena, src);
+#else
+ return str(arena, strv(src));
+#endif
}
-int strCompare(str_t a, str_t b) {
- return a.len == b.len ?
- memcmp(a.buf, b.buf, a.len) :
- (int)(a.len - b.len);
+str16_t str16_from_str(arena_t *arena, str_t src) {
+ return strv_to_str16(arena, strv(src));
}
-str_t strDup(arena_t *arena, str_t src) {
- return strInitLen(arena, src.buf, src.len);
+bool str_equals(str_t a, str_t b) {
+ return str_compare(a, b) == 0;
}
-bool strIsEmpty(str_t ctx) {
- return ctx.len == 0 || ctx.buf == NULL;
+int str_compare(str_t a, str_t b) {
+ // TODO unsinged underflow if a.len < b.len
+ return a.len == b.len ? memcmp(a.buf, b.buf, a.len) : (int)(a.len - b.len);
}
-void strReplace(str_t *ctx, char from, char to) {
+str_t str_dup(arena_t *arena, str_t src) {
+ return str_init_len(arena, src.buf, src.len);
+}
+
+str_t str_cat(arena_t *arena, str_t a, str_t b) {
+ str_t out = STR_EMPTY;
+
+ out.len += a.len + b.len;
+ out.buf = alloc(arena, char, out.len + 1);
+ memcpy(out.buf, a.buf, a.len);
+ memcpy(out.buf + a.len, b.buf, b.len);
+
+ return out;
+}
+
+bool str_is_empty(str_t ctx) {
+ return !ctx.buf || !ctx.len;
+}
+
+void str_lower(str_t *src) {
+ for (usize i = 0; i < src->len; ++i) {
+ if (src->buf[i] >= 'A' && src->buf[i] <= 'Z') {
+ src->buf[i] += 'a' - 'A';
+ }
+ }
+}
+
+void str_upper(str_t *src) {
+ for (usize i = 0; i < src->len; ++i) {
+ if (src->buf[i] >= 'a' && src->buf[i] <= 'z') {
+ src->buf[i] -= 'a' - 'A';
+ }
+ }
+}
+
+void str_replace(str_t *ctx, char from, char to) {
if (!ctx) return;
char *buf = ctx->buf;
for (usize i = 0; i < ctx->len; ++i) {
@@ -137,145 +145,68 @@ void strReplace(str_t *ctx, char from, char to) {
}
}
-strview_t strSub(str_t ctx, usize from, usize to) {
+strview_t str_sub(str_t ctx, usize from, usize to) {
if (to > ctx.len) to = ctx.len;
if (from > to) from = to;
return (strview_t){ ctx.buf + from, to - from };
}
-void strLower(str_t *ctx) {
- char *buf = ctx->buf;
- for (usize i = 0; i < ctx->len; ++i) {
- buf[i] = (buf[i] >= 'A' && buf[i] <= 'Z') ?
- buf[i] += 'a' - 'A' :
- buf[i];
- }
-}
-
-void strUpper(str_t *ctx) {
- char *buf = ctx->buf;
- for (usize i = 0; i < ctx->len; ++i) {
- buf[i] = (buf[i] >= 'a' && buf[i] <= 'z') ?
- buf[i] -= 'a' - 'A' :
- buf[i];
- }
-}
-
-str_t strToLower(arena_t *arena, str_t ctx) {
- strLower(&ctx);
- return strDup(arena, ctx);
-}
-
-str_t strToUpper(arena_t *arena, str_t ctx) {
- strUpper(&ctx);
- return strDup(arena, ctx);
-}
-
-
// == STRVIEW_T ====================================================
-strview_t strvInit(const char *cstr) {
- return (strview_t){
- .buf = cstr,
- .len = cstr ? strlen(cstr) : 0,
- };
+strview_t strv_init(const char *cstr) {
+ return strv_init_len(cstr, cstr ? strlen(cstr) : 0);
}
-strview_t strvInitLen(const char *buf, usize size) {
+strview_t strv_init_len(const char *buf, usize size) {
return (strview_t){
.buf = buf,
.len = size,
};
}
-strview_t strvInitStr(str_t str) {
+strview_t strv_init_str(str_t str) {
return (strview_t){
.buf = str.buf,
.len = str.len
};
}
-
-bool strvIsEmpty(strview_t ctx) {
+bool strv_is_empty(strview_t ctx) {
return ctx.len == 0 || !ctx.buf;
}
-bool strvEquals(strview_t a, strview_t b) {
- return strvCompare(a, b) == 0;
+bool strv_equals(strview_t a, strview_t b) {
+ return strv_compare(a, b) == 0;
}
-int strvCompare(strview_t a, strview_t b) {
+int strv_compare(strview_t a, strview_t b) {
+ // TODO unsinged underflow if a.len < b.len
return a.len == b.len ?
memcmp(a.buf, b.buf, a.len) :
(int)(a.len - b.len);
}
-char strvFront(strview_t ctx) {
+char strv_front(strview_t ctx) {
return ctx.len > 0 ? ctx.buf[0] : '\0';
}
-char strvBack(strview_t ctx) {
+char strv_back(strview_t ctx) {
return ctx.len > 0 ? ctx.buf[ctx.len - 1] : '\0';
}
-
-wchar_t *strvToWChar(arena_t *arena, strview_t ctx, usize *outlen) {
- wchar_t *out = NULL;
- int len = 0;
-
- if (strvIsEmpty(ctx)) {
- goto error;
- }
-
-#if COLLA_WIN
- len = MultiByteToWideChar(
- CP_UTF8, 0,
- ctx.buf, (int)ctx.len,
- NULL, 0
- );
-
- if (len == 0) {
- unsigned long error = GetLastError();
- if (error == ERROR_NO_UNICODE_TRANSLATION) {
- err("couldn't translate string (%v) to a wide string, no unicode translation", ctx);
- }
- else {
- err("couldn't translate string (%v) to a wide string, %u", ctx, error);
- }
-
- goto error;
- }
-
- out = alloc(arena, wchar_t, len + 1);
-
- MultiByteToWideChar(
- CP_UTF8, 0,
- ctx.buf, (int)ctx.len,
- out, len
- );
-
-#elif COLLA_LIN
- fatal("strFromWChar not implemented yet!");
-#endif
-
-error:
- if (outlen) {
- *outlen = (usize)len;
- }
- return out;
+str16_t strv_to_str16(arena_t *arena, strview_t src) {
+ return strv_os_to_str16(arena, src);
}
-TCHAR *strvToTChar(arena_t *arena, strview_t str) {
+tstr_t strv_to_tstr(arena_t *arena, strview_t src) {
#if UNICODE
- return strvToWChar(arena, str, NULL);
+ return strv_to_str16(arena, src);
#else
- char *cstr = alloc(arena, char, str.len + 1);
- memcpy(cstr, str.buf, str.len);
- return cstr;
+ return str(arena, src);
#endif
}
-strview_t strvRemovePrefix(strview_t ctx, usize n) {
+strview_t strv_remove_prefix(strview_t ctx, usize n) {
if (n > ctx.len) n = ctx.len;
return (strview_t){
.buf = ctx.buf + n,
@@ -283,7 +214,7 @@ strview_t strvRemovePrefix(strview_t ctx, usize n) {
};
}
-strview_t strvRemoveSuffix(strview_t ctx, usize n) {
+strview_t strv_remove_suffix(strview_t ctx, usize n) {
if (n > ctx.len) n = ctx.len;
return (strview_t){
.buf = ctx.buf,
@@ -291,11 +222,11 @@ strview_t strvRemoveSuffix(strview_t ctx, usize n) {
};
}
-strview_t strvTrim(strview_t ctx) {
- return strvTrimLeft(strvTrimRight(ctx));
+strview_t strv_trim(strview_t ctx) {
+ return strv_trim_left(strv_trim_right(ctx));
}
-strview_t strvTrimLeft(strview_t ctx) {
+strview_t strv_trim_left(strview_t ctx) {
strview_t out = ctx;
for (usize i = 0; i < ctx.len; ++i) {
char c = ctx.buf[i];
@@ -308,7 +239,7 @@ strview_t strvTrimLeft(strview_t ctx) {
return out;
}
-strview_t strvTrimRight(strview_t ctx) {
+strview_t strv_trim_right(strview_t ctx) {
strview_t out = ctx;
for (isize i = ctx.len - 1; i >= 0; --i) {
char c = ctx.buf[i];
@@ -320,29 +251,30 @@ strview_t strvTrimRight(strview_t ctx) {
return out;
}
-strview_t strvSub(strview_t ctx, usize from, usize to) {
+strview_t strv_sub(strview_t ctx, usize from, usize to) {
+ if (ctx.len == 0) return STRV_EMPTY;
if (to > ctx.len) to = ctx.len;
if (from > to) from = to;
return (strview_t){ ctx.buf + from, to - from };
}
-bool strvStartsWith(strview_t ctx, char c) {
+bool strv_starts_with(strview_t ctx, char c) {
return ctx.len > 0 && ctx.buf[0] == c;
}
-bool strvStartsWithView(strview_t ctx, strview_t view) {
+bool strv_starts_with_view(strview_t ctx, strview_t view) {
return ctx.len >= view.len && memcmp(ctx.buf, view.buf, view.len) == 0;
}
-bool strvEndsWith(strview_t ctx, char c) {
+bool strv_ends_with(strview_t ctx, char c) {
return ctx.len > 0 && ctx.buf[ctx.len - 1] == c;
}
-bool strvEndsWithView(strview_t ctx, strview_t view) {
+bool strv_ends_with_view(strview_t ctx, strview_t view) {
return ctx.len >= view.len && memcmp(ctx.buf + ctx.len - view.len, view.buf, view.len) == 0;
}
-bool strvContains(strview_t ctx, char c) {
+bool strv_contains(strview_t ctx, char c) {
for(usize i = 0; i < ctx.len; ++i) {
if(ctx.buf[i] == c) {
return true;
@@ -351,7 +283,7 @@ bool strvContains(strview_t ctx, char c) {
return false;
}
-bool strvContainsView(strview_t ctx, strview_t view) {
+bool strv_contains_view(strview_t ctx, strview_t view) {
if (ctx.len < view.len) return false;
usize end = ctx.len - view.len;
for (usize i = 0; i < end; ++i) {
@@ -362,7 +294,17 @@ bool strvContainsView(strview_t ctx, strview_t view) {
return false;
}
-usize strvFind(strview_t ctx, char c, usize from) {
+bool strv_contains_either(strview_t ctx, strview_t chars) {
+ for (usize i = 0; i < ctx.len; ++i) {
+ if (strv_contains(chars, ctx.buf[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+usize strv_find(strview_t ctx, char c, usize from) {
for (usize i = from; i < ctx.len; ++i) {
if (ctx.buf[i] == c) {
return i;
@@ -371,8 +313,7 @@ usize strvFind(strview_t ctx, char c, usize from) {
return STR_NONE;
}
-usize strvFindView(strview_t ctx, strview_t view, usize from) {
- if (ctx.len < view.len) return STR_NONE;
+usize strv_find_view(strview_t ctx, strview_t view, usize from) {
usize end = ctx.len - view.len;
for (usize i = from; i < end; ++i) {
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
@@ -382,7 +323,20 @@ usize strvFindView(strview_t ctx, strview_t view, usize from) {
return STR_NONE;
}
-usize strvRFind(strview_t ctx, char c, usize from_right) {
+usize strv_find_either(strview_t ctx, strview_t chars, usize from) {
+ if (from > ctx.len) from = ctx.len;
+
+ for (usize i = from; i < ctx.len; ++i) {
+ if (strv_contains(chars, ctx.buf[i])) {
+ return i;
+ }
+ }
+
+ return STR_NONE;
+}
+
+usize strv_rfind(strview_t ctx, char c, usize from_right) {
+ if (ctx.len == 0) return STR_NONE;
if (from_right > ctx.len) from_right = ctx.len;
isize end = (isize)(ctx.len - from_right);
for (isize i = end; i >= 0; --i) {
@@ -393,7 +347,8 @@ usize strvRFind(strview_t ctx, char c, usize from_right) {
return STR_NONE;
}
-usize strvRFindView(strview_t ctx, strview_t view, usize from_right) {
+usize strv_rfind_view(strview_t ctx, strview_t view, usize from_right) {
+ if (ctx.len == 0) return STR_NONE;
if (from_right > ctx.len) from_right = ctx.len;
isize end = (isize)(ctx.len - from_right);
if (end < (isize)view.len) return STR_NONE;
@@ -405,4 +360,403 @@ usize strvRFindView(strview_t ctx, strview_t view, usize from_right) {
return STR_NONE;
}
-#include "warnings/colla_warn_beg.h"
\ No newline at end of file
+// == CTYPE ========================================================
+
+bool char_is_space(char c) {
+ return (c >= '\t' && c <= '\r') || c == ' ';
+}
+
+bool char_is_alpha(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+bool char_is_num(char c) {
+ return c >= '0' && c <= '9';
+}
+
+// == INPUT STREAM =================================================
+
+instream_t istr_init(strview_t str) {
+ return (instream_t) {
+ .beg = str.buf,
+ .cur = str.buf,
+ .len = str.len,
+ };
+}
+
+char istr_get(instream_t *ctx) {
+ return istr_remaining(ctx) ? *ctx->cur++ : '\0';
+}
+
+char istr_peek(instream_t *ctx) {
+ return istr_remaining(ctx) ? *ctx->cur : '\0';
+}
+
+char istr_peek_next(instream_t *ctx) {
+ return istr_remaining(ctx) > 1 ? *(ctx->cur + 1) : '\0';
+}
+
+char istr_prev(instream_t *ctx) {
+ return istr_tell(ctx) ? *(ctx->cur - 1) : '\0';
+}
+
+char istr_prev_prev(instream_t *ctx) {
+ return istr_tell(ctx) > 1 ? *(ctx->cur - 2) : '\0';
+}
+
+void istr_ignore(instream_t *ctx, char delim) {
+ while (!istr_is_finished(ctx) && *ctx->cur != delim) {
+ ctx->cur++;
+ }
+}
+
+void istr_ignore_and_skip(instream_t *ctx, char delim) {
+ istr_ignore(ctx, delim);
+ istr_skip(ctx, 1);
+}
+
+void istr_skip(instream_t *ctx, usize n) {
+ if (!ctx) return;
+ usize rem = istr_remaining(ctx);
+ if (n > rem) n = rem;
+ ctx->cur += n;
+}
+
+void istr_skip_whitespace(instream_t *ctx) {
+ while (!istr_is_finished(ctx) && char_is_space(*ctx->cur)) {
+ ctx->cur++;
+ }
+}
+
+void istr_rewind(instream_t *ctx) {
+ if (ctx) ctx->cur = ctx->beg;
+}
+
+void istr_rewind_n(instream_t *ctx, usize amount) {
+ if (!ctx) return;
+ usize rem = istr_remaining(ctx);
+ ctx->cur -= MIN(amount, rem);
+}
+
+usize istr_tell(instream_t *ctx) {
+ return ctx ? ctx->cur - ctx->beg : 0;
+}
+
+usize istr_remaining(instream_t *ctx) {
+ return ctx ? ctx->len - (ctx->cur - ctx->beg) : 0;
+}
+
+bool istr_is_finished(instream_t *ctx) {
+ return !(ctx && istr_remaining(ctx) > 0);
+}
+
+bool istr_get_bool(instream_t *ctx, bool *val) {
+ if (!ctx || !ctx->cur || !val) return false;
+ usize rem = istr_remaining(ctx);
+ if (rem >= 4 && memcmp(ctx->cur, "true", 4) == 0) {
+ *val = true;
+ ctx->cur += 4;
+ return true;
+ }
+ if (rem >= 5 && memcmp(ctx->cur, "false", 5) == 0) {
+ *val = false;
+ ctx->cur += 5;
+ return true;
+ }
+ return false;
+}
+
+bool istr_get_u8(instream_t *ctx, u8 *val) {
+ u64 out = 0;
+ bool result = istr_get_u64(ctx, &out);
+ if (result && out < UINT8_MAX) {
+ *val = (u8)out;
+ }
+ return result;
+}
+
+bool istr_get_u16(instream_t *ctx, u16 *val) {
+ u64 out = 0;
+ bool result = istr_get_u64(ctx, &out);
+ if (result && out < UINT16_MAX) {
+ *val = (u16)out;
+ }
+ return result;
+}
+
+bool istr_get_u32(instream_t *ctx, u32 *val) {
+ u64 out = 0;
+ bool result = istr_get_u64(ctx, &out);
+ if (result && out < UINT32_MAX) {
+ *val = (u32)out;
+ }
+ return result;
+}
+
+
+bool istr_get_u64(instream_t *ctx, u64 *val) {
+ if (!ctx || !ctx->cur || !val) return false;
+ char *end = NULL;
+ *val = strtoull(ctx->cur, &end, 0);
+
+ if (ctx->cur == end) {
+ return false;
+ }
+ else if (*val == ULLONG_MAX) {
+ return false;
+ }
+
+ ctx->cur = end;
+ return true;
+}
+
+bool istr_get_i8(instream_t *ctx, i8 *val) {
+ i64 out = 0;
+ bool result = istr_get_i64(ctx, &out);
+ if (result && out > INT8_MIN && out < INT8_MAX) {
+ *val = (i8)out;
+ }
+ return result;
+}
+
+bool istr_get_i16(instream_t *ctx, i16 *val) {
+ i64 out = 0;
+ bool result = istr_get_i64(ctx, &out);
+ if (result && out > INT16_MIN && out < INT16_MAX) {
+ *val = (i16)out;
+ }
+ return result;
+}
+
+bool istr_get_i32(instream_t *ctx, i32 *val) {
+ i64 out = 0;
+ bool result = istr_get_i64(ctx, &out);
+ if (result && out > INT32_MIN && out < INT32_MAX) {
+ *val = (i32)out;
+ }
+ return result;
+}
+
+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);
+
+ if (ctx->cur == end) {
+ return false;
+ }
+ else if(*val == INT64_MAX || *val == INT64_MIN) {
+ return false;
+ }
+
+ ctx->cur = end;
+ return true;
+}
+
+bool istr_get_num(instream_t *ctx, double *val) {
+ if (!ctx || !ctx->cur || !val) return false;
+ char *end = NULL;
+ *val = strtod(ctx->cur, &end);
+
+ if(ctx->cur == end) {
+ warn("istrGetDouble: no valid conversion could be performed (%.5s)", ctx->cur);
+ return false;
+ }
+ else if(*val == HUGE_VAL || *val == -HUGE_VAL) {
+ warn("istrGetDouble: value read is out of the range of representable values");
+ return false;
+ }
+
+ ctx->cur = end;
+ return true;
+}
+
+strview_t istr_get_view(instream_t *ctx, char delim) {
+ if (!ctx || !ctx->cur) return STRV_EMPTY;
+ const char *from = ctx->cur;
+ istr_ignore(ctx, delim);
+ usize len = ctx->cur - from;
+ return strv(from, len);
+}
+
+strview_t istr_get_view_either(instream_t *ctx, strview_t chars) {
+ if (!ctx || !ctx->cur) return STRV_EMPTY;
+ const char *from = ctx->cur;
+ while (!istr_is_finished(ctx) && !strv_contains(chars, *ctx->cur)) {
+ ctx->cur++;
+ }
+
+ usize len = ctx->cur - from;
+ return strv(from, len);
+}
+
+strview_t istr_get_view_len(instream_t *ctx, usize len) {
+ if (!ctx || !ctx->cur) return STRV_EMPTY;
+ const char *from = ctx->cur;
+ istr_skip(ctx, len);
+ usize buflen = ctx->cur - from;
+ return (strview_t){ from, buflen };
+}
+
+strview_t istr_get_line(instream_t *ctx) {
+ strview_t line = istr_get_view(ctx, '\n');
+ istr_skip(ctx, 1);
+ if (strv_ends_with(line, '\r')) {
+ line = strv_remove_suffix(line, 1);
+ }
+ return line;
+}
+
+// == OUTPUT STREAM ================================================
+
+outstream_t ostr_init(arena_t *exclusive_arena) {
+ return (outstream_t) {
+ .beg = (char *)(exclusive_arena ? exclusive_arena->cur : NULL),
+ .arena = exclusive_arena,
+ };
+}
+
+void ostr_clear(outstream_t *ctx) {
+ arena_pop(ctx->arena, ostr_tell(ctx));
+}
+
+usize ostr_tell(outstream_t *ctx) {
+ return ctx->arena ? (char *)ctx->arena->cur - ctx->beg : 0;
+}
+
+char ostr_back(outstream_t *ctx) {
+ usize len = ostr_tell(ctx);
+ return len ? ctx->beg[len - 1] : '\0';
+}
+
+str_t ostr_to_str(outstream_t *ctx) {
+ ostr_putc(ctx, '\0');
+
+ str_t out = {
+ .buf = ctx->beg,
+ .len = ostr_tell(ctx) - 1,
+ };
+
+ memset(ctx, 0, sizeof(outstream_t));
+ return out;
+}
+
+strview_t ostr_as_view(outstream_t *ctx) {
+ return strv(ctx->beg, ostr_tell(ctx));
+}
+
+void ostr_pop(outstream_t *ctx, usize count) {
+ if (!ctx->arena) return;
+ arena_pop(ctx->arena, count);
+}
+
+void ostr_print(outstream_t *ctx, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ostr_printv(ctx, fmt, args);
+ va_end(args);
+}
+
+void ostr_printv(outstream_t *ctx, const char *fmt, va_list args) {
+ if (!ctx->arena) return;
+ str_fmtv(ctx->arena, fmt, args);
+ // remove null terminator
+ arena_pop(ctx->arena, 1);
+}
+
+void ostr_putc(outstream_t *ctx, char c) {
+ if (!ctx->arena) return;
+ char *newc = alloc(ctx->arena, char);
+ *newc = c;
+}
+
+void ostr_puts(outstream_t *ctx, strview_t v) {
+ if (strv_is_empty(v)) return;
+ str(ctx->arena, v);
+ // remove null terminator
+ arena_pop(ctx->arena, 1);
+}
+
+void ostr_append_bool(outstream_t *ctx, bool val) {
+ ostr_puts(ctx, val ? strv("true") : strv("false"));
+}
+
+void ostr_append_uint(outstream_t *ctx, u64 val) {
+ ostr_print(ctx, "%I64u", val);
+}
+
+void ostr_append_int(outstream_t *ctx, i64 val) {
+ ostr_print(ctx, "%I64d", val);
+}
+
+void ostr_append_num(outstream_t *ctx, double val) {
+ ostr_print(ctx, "%g", val);
+}
+
+// == INPUT BINARY STREAM ==========================================
+
+ibstream_t ibstr_init(buffer_t buffer) {
+ return (ibstream_t){
+ .beg = buffer.data,
+ .cur = buffer.data,
+ .len = buffer.len,
+ };
+}
+
+bool ibstr_is_finished(ibstream_t *ib) {
+ return !(ib && ibstr_remaining(ib) > 0);
+}
+
+usize ibstr_tell(ibstream_t *ib) {
+ return ib && ib->cur ? ib->cur - ib->beg : 0;
+}
+
+usize ibstr_remaining(ibstream_t *ib) {
+ return ib ? ib->len - ibstr_tell(ib) : 0;
+}
+
+usize ibstr_read(ibstream_t *ib, void *buffer, usize len) {
+ usize rem = ibstr_remaining(ib);
+ if (len > rem) len = rem;
+ memmove(buffer, ib->cur, len);
+ ib->cur += len;
+ return len;
+}
+
+void ibstr_skip(ibstream_t *ib, usize count) {
+ usize rem = ibstr_remaining(ib);
+ if (count > rem) count = rem;
+ ib->cur += count;
+}
+
+bool ibstr_get_u8(ibstream_t *ib, u8 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_u16(ibstream_t *ib, u16 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_u32(ibstream_t *ib, u32 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_u64(ibstream_t *ib, u64 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_i8(ibstream_t *ib, i8 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_i16(ibstream_t *ib, i16 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_i32(ibstream_t *ib, i32 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
+
+bool ibstr_get_i64(ibstream_t *ib, i64 *out) {
+ return ibstr_read(ib, out, sizeof(*out)) == sizeof(*out);
+}
diff --git a/str.h b/str.h
index db216ae..bd72244 100644
--- a/str.h
+++ b/str.h
@@ -1,36 +1,50 @@
-#pragma once
+#ifndef COLLA_STR_H
+#define COLLA_STR_H
-#include // va_list
#include // strlen
-#include "collatypes.h"
-
-typedef struct arena_t arena_t;
+#include "core.h"
+#include "darr.h"
#define STR_NONE SIZE_MAX
-typedef struct {
+typedef struct str_t str_t;
+struct str_t {
char *buf;
usize len;
-} str_t;
+};
-typedef struct {
+typedef struct str16_t str16_t;
+struct str16_t {
+ u16 *buf;
+ usize len;
+};
+
+#if COLLA_UNICODE
+typedef str16_t tstr_t;
+#else
+typedef str_t tstr_t;
+#endif
+
+typedef struct strview_t strview_t;
+struct strview_t {
const char *buf;
usize len;
-} strview_t;
+};
+
+darr_define(str_list_t, str_t);
+darr_define(strv_list_t, strview_t);
// == STR_T ========================================================
-#define str__1(arena, x) \
- _Generic((x), \
- const char *: strInit, \
- char *: strInit, \
- const wchar_t *: strFromWChar, \
- wchar_t *: strFromWChar, \
- strview_t: strInitView \
+#define str__1(arena, x) \
+ _Generic((x), \
+ const char *: str_init, \
+ char *: str_init, \
+ strview_t: str_init_view \
)(arena, x)
-#define str__2(arena, cstr, clen) strInitLen(arena, cstr, clen)
+#define str__2(arena, cstr, clen) str_init_len(arena, cstr, clen)
#define str__impl(_1, _2, n, ...) str__##n
// either:
@@ -40,80 +54,223 @@ typedef struct {
#define STR_EMPTY (str_t){0}
-str_t strInit(arena_t *arena, const char *buf);
-str_t strInitLen(arena_t *arena, const char *buf, usize len);
-str_t strInitView(arena_t *arena, strview_t view);
-str_t strFmt(arena_t *arena, const char *fmt, ...);
-str_t strFmtv(arena_t *arena, const char *fmt, va_list args);
+str_t str_init(arena_t *arena, const char *buf);
+str_t str_init_len(arena_t *arena, const char *buf, usize len);
+str_t str_init_view(arena_t *arena, strview_t view);
+str_t str_fmt(arena_t *arena, const char *fmt, ...);
+str_t str_fmtv(arena_t *arena, const char *fmt, va_list args);
-str_t strFromWChar(arena_t *arena, const wchar_t *src);
-str_t strFromWCharLen(arena_t *arena, const wchar_t *src, usize srclen);
+tstr_t tstr_init(TCHAR *str, usize optional_len);
+str16_t str16_init(u16 *str, usize optional_len);
-bool strEquals(str_t a, str_t b);
-int strCompare(str_t a, str_t b);
+str_t str_from_str16(arena_t *arena, str16_t src);
+str_t str_from_tstr(arena_t *arena, tstr_t src);
+str16_t str16_from_str(arena_t *arena, str_t src);
-str_t strDup(arena_t *arena, str_t src);
-bool strIsEmpty(str_t ctx);
+bool str_equals(str_t a, str_t b);
+int str_compare(str_t a, str_t b);
-void strReplace(str_t *ctx, char from, char to);
+str_t str_dup(arena_t *arena, str_t src);
+str_t str_cat(arena_t *arena, str_t a, str_t b);
+bool str_is_empty(str_t ctx);
+
+void str_lower(str_t *src);
+void str_upper(str_t *src);
+
+void str_replace(str_t *ctx, char from, char to);
// if len == SIZE_MAX, copies until end
-strview_t strSub(str_t ctx, usize from, usize to);
-
-void strLower(str_t *ctx);
-void strUpper(str_t *ctx);
-
-str_t strToLower(arena_t *arena, str_t ctx);
-str_t strToUpper(arena_t *arena, str_t ctx);
+strview_t str_sub(str_t ctx, usize from, usize to);
// == STRVIEW_T ====================================================
-#define strv__1(x) \
- _Generic((x), \
- char *: strvInit, \
- const char *: strvInit, \
- str_t: strvInitStr \
- )(x)
-
-#define strv__2(cstr, clen) strvInitLen(cstr, clen)
-#define strv__impl(_1, _2, n, ...) strv__##n
-
-#define strv(...) strv__impl(__VA_ARGS__, 2, 1, 0)(__VA_ARGS__)
+// these macros might be THE worst code ever written, but they work ig
+// detects if you're trying to create a string view from either:
+// - a str_t -> calls strv_init_str
+// - a string literal -> calls strv_init_len with comptime size
+// - a c string -> calls strv_init with runtime size
#define STRV_EMPTY (strview_t){0}
-strview_t strvInit(const char *cstr);
-strview_t strvInitLen(const char *buf, usize size);
-strview_t strvInitStr(str_t str);
+// needed for strv__init_literal _Generic implementation, it's never actually called
+inline strview_t strv__ignore(str_t s, size_t l) {
+ COLLA_UNUSED(s); COLLA_UNUSED(l);
+ return STRV_EMPTY;
+}
-bool strvIsEmpty(strview_t ctx);
-bool strvEquals(strview_t a, strview_t b);
-int strvCompare(strview_t a, strview_t b);
+#define strv__check(x, ...) ((#x)[0] == '"')
+#define strv__init_literal(x, ...) \
+ _Generic((x), \
+ char *: strv_init_len, \
+ const char *: strv_init_len, \
+ str_t: strv__ignore \
+ )(x, sizeof(x) - 1)
-char strvFront(strview_t ctx);
-char strvBack(strview_t ctx);
+#define strv__1(x) \
+ _Generic((x), \
+ char *: strv_init, \
+ const char *: strv_init, \
+ str_t: strv_init_str \
+ )(x)
-wchar_t *strvToWChar(arena_t *arena, strview_t ctx, usize *outlen);
-TCHAR *strvToTChar(arena_t *arena, strview_t str);
+#define strv__2(cstr, clen) strv_init_len(cstr, clen)
-strview_t strvRemovePrefix(strview_t ctx, usize n);
-strview_t strvRemoveSuffix(strview_t ctx, usize n);
-strview_t strvTrim(strview_t ctx);
-strview_t strvTrimLeft(strview_t ctx);
-strview_t strvTrimRight(strview_t ctx);
+#define strv__impl(_1, _2, n, ...) strv__##n
-strview_t strvSub(strview_t ctx, usize from, usize to);
+#define strv(...) strv__check(__VA_ARGS__) ? strv__init_literal(__VA_ARGS__) : strv__impl(__VA_ARGS__, 2, 1, 0)(__VA_ARGS__)
-bool strvStartsWith(strview_t ctx, char c);
-bool strvStartsWithView(strview_t ctx, strview_t view);
+strview_t strv_init(const char *cstr);
+strview_t strv_init_len(const char *buf, usize size);
+strview_t strv_init_str(str_t str);
-bool strvEndsWith(strview_t ctx, char c);
-bool strvEndsWithView(strview_t ctx, strview_t view);
+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);
-bool strvContains(strview_t ctx, char c);
-bool strvContainsView(strview_t ctx, strview_t view);
+char strv_front(strview_t ctx);
+char strv_back(strview_t ctx);
-usize strvFind(strview_t ctx, char c, usize from);
-usize strvFindView(strview_t ctx, strview_t view, usize from);
+str16_t strv_to_str16(arena_t *arena, strview_t src);
+tstr_t strv_to_tstr(arena_t *arena, strview_t src);
-usize strvRFind(strview_t ctx, char c, usize from_right);
-usize strvRFindView(strview_t ctx, strview_t view, usize from_right);
+strview_t strv_remove_prefix(strview_t ctx, usize n);
+strview_t strv_remove_suffix(strview_t ctx, usize n);
+strview_t strv_trim(strview_t ctx);
+strview_t strv_trim_left(strview_t ctx);
+strview_t strv_trim_right(strview_t ctx);
+
+strview_t strv_sub(strview_t ctx, usize from, usize to);
+
+bool strv_starts_with(strview_t ctx, char c);
+bool strv_starts_with_view(strview_t ctx, strview_t view);
+
+bool strv_ends_with(strview_t ctx, char c);
+bool strv_ends_with_view(strview_t ctx, strview_t view);
+
+bool strv_contains(strview_t ctx, char c);
+bool strv_contains_view(strview_t ctx, strview_t view);
+bool strv_contains_either(strview_t ctx, strview_t chars);
+
+usize strv_find(strview_t ctx, char c, usize from);
+usize strv_find_view(strview_t ctx, strview_t view, usize from);
+usize strv_find_either(strview_t ctx, strview_t chars, usize from);
+
+usize strv_rfind(strview_t ctx, char c, usize from_right);
+usize strv_rfind_view(strview_t ctx, strview_t view, usize from_right);
+
+// == CTYPE ========================================================
+
+bool char_is_space(char c);
+bool char_is_alpha(char c);
+bool char_is_num(char c);
+
+// == INPUT STREAM =================================================
+
+typedef struct instream_t instream_t;
+struct instream_t {
+ const char *beg;
+ const char *cur;
+ usize len;
+};
+
+instream_t istr_init(strview_t str);
+
+// get the current character and advance
+char istr_get(instream_t *ctx);
+// get the current character but don't advance
+char istr_peek(instream_t *ctx);
+// get the next character but don't advance
+char istr_peek_next(instream_t *ctx);
+// returns the previous character
+char istr_prev(instream_t *ctx);
+// returns the character before the previous
+char istr_prev_prev(instream_t *ctx);
+// ignore characters until the delimiter
+void istr_ignore(instream_t *ctx, char delim);
+// ignore characters until the delimiter and skip it
+void istr_ignore_and_skip(instream_t *ctx, char delim);
+// skip n characters
+void istr_skip(instream_t *ctx, usize n);
+// skips whitespace (' ', '\\n', '\\t', '\\r')
+void istr_skip_whitespace(instream_t *ctx);
+// returns to the beginning of the stream
+void istr_rewind(instream_t *ctx);
+// returns back characters
+void istr_rewind_n(instream_t *ctx, usize amount);
+// returns the number of bytes read from beginning of stream
+usize istr_tell(instream_t *ctx);
+// returns the number of bytes left to read in the stream
+usize istr_remaining(instream_t *ctx);
+// return true if the stream doesn't have any new bytes to read
+bool istr_is_finished(instream_t *ctx);
+
+bool istr_get_bool(instream_t *ctx, bool *val);
+bool istr_get_u8(instream_t *ctx, u8 *val);
+bool istr_get_u16(instream_t *ctx, u16 *val);
+bool istr_get_u32(instream_t *ctx, u32 *val);
+bool istr_get_u64(instream_t *ctx, u64 *val);
+bool istr_get_i8(instream_t *ctx, i8 *val);
+bool istr_get_i16(instream_t *ctx, i16 *val);
+bool istr_get_i32(instream_t *ctx, i32 *val);
+bool istr_get_i64(instream_t *ctx, i64 *val);
+bool istr_get_num(instream_t *ctx, double *val);
+strview_t istr_get_view(instream_t *ctx, char delim);
+strview_t istr_get_view_either(instream_t *ctx, strview_t chars);
+strview_t istr_get_view_len(instream_t *ctx, usize len);
+strview_t istr_get_line(instream_t *ctx);
+
+// == OUTPUT STREAM ================================================
+
+typedef struct outstream_t outstream_t;
+struct outstream_t {
+ char *beg;
+ arena_t *arena;
+};
+
+outstream_t ostr_init(arena_t *exclusive_arena);
+void ostr_clear(outstream_t *ctx);
+
+usize ostr_tell(outstream_t *ctx);
+
+char ostr_back(outstream_t *ctx);
+str_t ostr_to_str(outstream_t *ctx);
+strview_t ostr_as_view(outstream_t *ctx);
+
+void ostr_pop(outstream_t *ctx, usize count);
+
+void ostr_print(outstream_t *ctx, const char *fmt, ...);
+void ostr_printv(outstream_t *ctx, const char *fmt, va_list args);
+void ostr_putc(outstream_t *ctx, char c);
+void ostr_puts(outstream_t *ctx, strview_t v);
+
+void ostr_append_bool(outstream_t *ctx, bool val);
+void ostr_append_uint(outstream_t *ctx, u64 val);
+void ostr_append_int(outstream_t *ctx, i64 val);
+void ostr_append_num(outstream_t *ctx, double val);
+
+// == INPUT BINARY STREAM ==========================================
+
+typedef struct {
+ const u8 *beg;
+ const u8 *cur;
+ usize len;
+} ibstream_t;
+
+ibstream_t ibstr_init(buffer_t buffer);
+
+bool ibstr_is_finished(ibstream_t *ib);
+usize ibstr_tell(ibstream_t *ib);
+usize ibstr_remaining(ibstream_t *ib);
+usize ibstr_read(ibstream_t *ib, void *buffer, usize len);
+void ibstr_skip(ibstream_t *ib, usize count);
+
+bool ibstr_get_u8(ibstream_t *ib, u8 *out);
+bool ibstr_get_u16(ibstream_t *ib, u16 *out);
+bool ibstr_get_u32(ibstream_t *ib, u32 *out);
+bool ibstr_get_u64(ibstream_t *ib, u64 *out);
+
+bool ibstr_get_i8(ibstream_t *ib, i8 *out);
+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);
+
+#endif
\ No newline at end of file
diff --git a/strstream.c b/strstream.c
deleted file mode 100644
index 1e90bc8..0000000
--- a/strstream.c
+++ /dev/null
@@ -1,655 +0,0 @@
-#include "strstream.h"
-
-#include "warnings/colla_warn_beg.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include // HUGE_VALF
-
-#include "tracelog.h"
-#include "arena.h"
-
-#if COLLA_WIN && COLLA_TCC
-#define strtoull _strtoui64
-#define strtoll _strtoi64
-#define strtof strtod
-#endif
-
-/* == INPUT STREAM ============================================ */
-
-instream_t istrInit(const char *str) {
- return istrInitLen(str, strlen(str));
-}
-
-instream_t istrInitLen(const char *str, usize len) {
- instream_t res;
- res.start = res.cur = str;
- res.size = len;
- return res;
-}
-
-char istrGet(instream_t *ctx) {
- return ctx && ctx->cur ? *ctx->cur++ : '\0';
-}
-
-void istrIgnore(instream_t *ctx, char delim) {
- for (; !istrIsFinished(*ctx) && *ctx->cur != delim; ++ctx->cur) {
-
- }
-}
-
-void istrIgnoreAndSkip(instream_t *ctx, char delim) {
- istrIgnore(ctx, delim);
- istrSkip(ctx, 1);
-}
-
-char istrPeek(instream_t *ctx) {
- return ctx && ctx->cur ? *ctx->cur : '\0';
-}
-
-char istrPeekNext(instream_t *ctx) {
- if (!ctx || !ctx->cur) return '\0';
- usize offset = (ctx->cur - ctx->start) + 1;
- return offset > ctx->size ? '\0' : *(ctx->cur + 1);
-}
-
-char istrPrev(instream_t *ctx) {
- if (!ctx || ctx->cur == ctx->start) return '\0';
- return *(ctx->cur - 1);
-}
-
-char istrPrevPrev(instream_t *ctx) {
- if (!ctx || (ctx->cur - 1) == ctx->start) return '\0';
- return *(ctx->cur - 2);
-}
-
-void istrSkip(instream_t *ctx, usize n) {
- if (!ctx || !ctx->cur) return;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- if(n > remaining) {
- return;
- }
- ctx->cur += n;
-}
-
-void istrSkipWhitespace(instream_t *ctx) {
- if (!ctx || !ctx->cur) return;
- while (*ctx->cur && isspace(*ctx->cur)) {
- ++ctx->cur;
- }
-}
-
-void istrRead(instream_t *ctx, char *buf, usize len) {
- if (!ctx || !ctx->cur) return;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- if(len > remaining) {
- warn("istrRead: trying to read len %zu from remaining %zu", len, remaining);
- return;
- }
- memcpy(buf, ctx->cur, len);
- ctx->cur += len;
-}
-
-usize istrReadMax(instream_t *ctx, char *buf, usize len) {
- if (!ctx || !ctx->cur) return 0;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- len = remaining < len ? remaining : len;
- memcpy(buf, ctx->cur, len);
- ctx->cur += len;
- return len;
-}
-
-void istrRewind(instream_t *ctx) {
- ctx->cur = ctx->start;
-}
-
-void istrRewindN(instream_t *ctx, usize amount) {
- if (!ctx || !ctx->cur) return;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- if (amount > remaining) amount = remaining;
- ctx->cur -= amount;
-}
-
-usize istrTell(instream_t ctx) {
- return ctx.cur ? ctx.cur - ctx.start : 0;
-}
-
-usize istrRemaining(instream_t ctx) {
- return ctx.cur ? ctx.size - (ctx.cur - ctx.start) : 0;
-}
-
-bool istrIsFinished(instream_t ctx) {
- return ctx.cur ? (usize)(ctx.cur - ctx.start) >= ctx.size : true;
-}
-
-bool istrGetBool(instream_t *ctx, bool *val) {
- if (!ctx || !ctx->cur) return false;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- if(strncmp(ctx->cur, "true", remaining) == 0) {
- *val = true;
- return true;
- }
- if(strncmp(ctx->cur, "false", remaining) == 0) {
- *val = false;
- return true;
- }
- return false;
-}
-
-bool istrGetU8(instream_t *ctx, uint8 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (uint8) strtoul(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetU8: no valid conversion could be performed");
- return false;
- }
- else if(*val == UINT8_MAX) {
- warn("istrGetU8: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetU16(instream_t *ctx, uint16 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (uint16) strtoul(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetU16: no valid conversion could be performed");
- return false;
- }
- else if(*val == UINT16_MAX) {
- warn("istrGetU16: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetU32(instream_t *ctx, uint32 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (uint32) strtoul(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetU32: no valid conversion could be performed");
- return false;
- }
- else if(*val == UINT32_MAX) {
- warn("istrGetU32: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetU64(instream_t *ctx, uint64 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = strtoull(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetU64: no valid conversion could be performed");
- return false;
- }
- else if(*val == ULLONG_MAX) {
- warn("istrGetU64: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetI8(instream_t *ctx, int8 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (int8) strtol(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetI8: no valid conversion could be performed");
- return false;
- }
- else if(*val == INT8_MAX || *val == INT8_MIN) {
- warn("istrGetI8: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetI16(instream_t *ctx, int16 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (int16) strtol(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetI16: no valid conversion could be performed");
- return false;
- }
- else if(*val == INT16_MAX || *val == INT16_MIN) {
- warn("istrGetI16: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetI32(instream_t *ctx, int32 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = (int32) strtol(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetI32: no valid conversion could be performed");
- return false;
- }
- else if(*val == INT32_MAX || *val == INT32_MIN) {
- warn("istrGetI32: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetI64(instream_t *ctx, int64 *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = strtoll(ctx->cur, &end, 0);
-
- if(ctx->cur == end) {
- warn("istrGetI64: no valid conversion could be performed");
- return false;
- }
- else if(*val == INT64_MAX || *val == INT64_MIN) {
- warn("istrGetI64: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetFloat(instream_t *ctx, float *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = strtof(ctx->cur, &end);
-
- if(ctx->cur == end) {
- warn("istrGetFloat: no valid conversion could be performed");
- return false;
- }
- else if(*val == HUGE_VALF || *val == -HUGE_VALF) {
- warn("istrGetFloat: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-bool istrGetDouble(instream_t *ctx, double *val) {
- if (!ctx || !ctx->cur) return false;
- char *end = NULL;
- *val = strtod(ctx->cur, &end);
-
- if(ctx->cur == end) {
- warn("istrGetDouble: no valid conversion could be performed (%.5s)", ctx->cur);
- return false;
- }
- else if(*val == HUGE_VAL || *val == -HUGE_VAL) {
- warn("istrGetDouble: value read is out of the range of representable values");
- return false;
- }
-
- ctx->cur = end;
- return true;
-}
-
-str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim) {
- if (!ctx || !ctx->cur) return STR_EMPTY;
- const char *from = ctx->cur;
- istrIgnore(ctx, delim);
- // if it didn't actually find it, it just reached the end of the string
- if(*ctx->cur != delim) {
- return STR_EMPTY;
- }
- usize len = ctx->cur - from;
- str_t out = {
- .buf = alloc(arena, char, len + 1),
- .len = len
- };
- memcpy(out.buf, from, len);
- return out;
-}
-
-usize istrGetBuf(instream_t *ctx, char *buf, usize buflen) {
- if (!ctx || !ctx->cur) return 0;
- usize remaining = ctx->size - (ctx->cur - ctx->start);
- buflen -= 1;
- buflen = remaining < buflen ? remaining : buflen;
- memcpy(buf, ctx->cur, buflen);
- buf[buflen] = '\0';
- ctx->cur += buflen;
- return buflen;
-}
-
-strview_t istrGetView(instream_t *ctx, char delim) {
- if (!ctx || !ctx->cur) return STRV_EMPTY;
- const char *from = ctx->cur;
- istrIgnore(ctx, delim);
- usize len = ctx->cur - from;
- return strvInitLen(from, len);
-}
-
-strview_t istrGetViewEither(instream_t *ctx, strview_t chars) {
- if (!ctx || !ctx->cur) return STRV_EMPTY;
- const char *from = ctx->cur;
- for (; !istrIsFinished(*ctx) && !strvContains(chars, *ctx->cur); ++ctx->cur) {
-
- }
- usize len = ctx->cur - from;
- return strvInitLen(from, len);
-}
-
-strview_t istrGetViewLen(instream_t *ctx, usize len) {
- if (!ctx || !ctx->cur) return STRV_EMPTY;
- const char *from = ctx->cur;
- istrSkip(ctx, len);
- usize buflen = ctx->cur - from;
- return (strview_t){ from, buflen };
-}
-
-strview_t istrGetLine(instream_t *ctx) {
- strview_t line = istrGetView(ctx, '\n');
- istrSkip(ctx, 1);
- if (strvEndsWith(line, '\r')) {
- line = strvRemoveSuffix(line, 1);
- }
- return line;
-}
-
-/* == OUTPUT STREAM =========================================== */
-
-static void ostr__remove_null(outstream_t *o) {
- usize len = ostrTell(o);
- if (len && o->beg[len - 1] == '\0') {
- arenaPop(o->arena, 1);
- }
-}
-
-outstream_t ostrInit(arena_t *arena) {
- return (outstream_t){
- .beg = (char *)(arena ? arena->current : NULL),
- .arena = arena,
- };
-}
-
-void ostrClear(outstream_t *ctx) {
- arenaPop(ctx->arena, ostrTell(ctx));
-}
-
-usize ostrTell(outstream_t *ctx) {
- return ctx->arena ? (char *)ctx->arena->current - ctx->beg : 0;
-}
-
-char ostrBack(outstream_t *ctx) {
- usize len = ostrTell(ctx);
- return len ? ctx->beg[len - 1] : '\0';
-}
-
-str_t ostrAsStr(outstream_t *ctx) {
- if (ostrTell(ctx) == 0 || ostrBack(ctx) != '\0') {
- ostrPutc(ctx, '\0');
- }
-
- str_t out = {
- .buf = ctx->beg,
- .len = ostrTell(ctx) - 1
- };
-
- ctx->beg = NULL;
- ctx->arena = NULL;
- return out;
-}
-
-strview_t ostrAsView(outstream_t *ctx) {
- bool is_null_terminated = ostrBack(ctx) == '\0';
- return (strview_t){
- .buf = ctx->beg,
- .len = ostrTell(ctx) - is_null_terminated
- };
-}
-
-void ostrPrintf(outstream_t *ctx, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- ostrPrintfV(ctx, fmt, args);
- va_end(args);
-}
-
-void ostrPrintfV(outstream_t *ctx, const char *fmt, va_list args) {
- if (!ctx->arena) return;
- ostr__remove_null(ctx);
- strFmtv(ctx->arena, fmt, args);
- // remove null termination
- arenaPop(ctx->arena, 1);
-}
-
-void ostrPutc(outstream_t *ctx, char c) {
- if (!ctx->arena) return;
- ostr__remove_null(ctx);
- char *newc = alloc(ctx->arena, char);
- *newc = c;
-}
-
-void ostrPuts(outstream_t *ctx, strview_t v) {
- if (strvIsEmpty(v)) return;
- ostr__remove_null(ctx);
- str(ctx->arena, v);
-}
-
-void ostrAppendBool(outstream_t *ctx, bool val) {
- ostrPuts(ctx, val ? strv("true") : strv("false"));
-}
-
-void ostrAppendUInt(outstream_t *ctx, uint64 val) {
- ostrPrintf(ctx, "%I64u", val);
-}
-
-void ostrAppendInt(outstream_t *ctx, int64 val) {
- ostrPrintf(ctx, "%I64d", val);
-}
-
-void ostrAppendNum(outstream_t *ctx, double val) {
- ostrPrintf(ctx, "%g", val);
-}
-
-/* == OUT BYTE STREAM ========================================= */
-
-obytestream_t obstrInit(arena_t *exclusive_arena) {
- return (obytestream_t){
- .beg = exclusive_arena ? exclusive_arena->current : NULL,
- .arena = exclusive_arena,
- };
-}
-
-void obstrClear(obytestream_t *ctx) {
- if (ctx->arena) {
- ctx->arena->current = ctx->beg;
- }
-}
-
-usize obstrTell(obytestream_t *ctx) {
- return ctx->arena ? ctx->arena->current - ctx->beg : 0;
-}
-
-buffer_t obstrAsBuf(obytestream_t *ctx) {
- return (buffer_t){ .data = ctx->beg, .len = obstrTell(ctx) };
-}
-
-void obstrWrite(obytestream_t *ctx, const void *buf, usize buflen) {
- uint8 *dst = alloc(ctx->arena, uint8, buflen, ALLOC_NOZERO);
- memcpy(dst, buf, buflen);
-}
-
-void obstrPuts(obytestream_t *ctx, strview_t str) {
- obstrWrite(ctx, str.buf, str.len);
-}
-
-void obstrAppendU8(obytestream_t *ctx, uint8 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendU16(obytestream_t *ctx, uint16 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendU32(obytestream_t *ctx, uint32 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendU64(obytestream_t *ctx, uint64 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendI8(obytestream_t *ctx, int8 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendI16(obytestream_t *ctx, int16 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendI32(obytestream_t *ctx, int32 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-void obstrAppendI64(obytestream_t *ctx, int64 value) {
- obstrWrite(ctx, &value, sizeof(value));
-}
-
-/* == IN BYTE STREAM ========================================== */
-
-ibytestream_t ibstrInit(const void *buf, usize len) {
- return (ibytestream_t) {
- .cur = buf,
- .start = buf,
- .size = len,
- };
-}
-
-usize ibstrRemaining(ibytestream_t *ctx) {
- return ctx->size - (ctx->cur - ctx->start);
-}
-
-usize ibstrTell(ibytestream_t *ctx) {
- return ctx->cur ? ctx->cur - ctx->start : 0;
-}
-
-usize ibstrRead(ibytestream_t *ctx, void *buf, usize buflen) {
- if (!ctx->cur) return 0;
- usize remaining = ibstrRemaining(ctx);
- if (buflen > remaining) buflen = remaining;
- memcpy(buf, ctx->cur, buflen);
- ctx->cur += buflen;
- return buflen;
-}
-
-uint8 ibstrGetU8(ibytestream_t *ctx) {
- uint8 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-uint16 ibstrGetU16(ibytestream_t *ctx) {
- uint16 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-uint32 ibstrGetU32(ibytestream_t *ctx) {
- uint32 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-uint64 ibstrGetU64(ibytestream_t *ctx) {
- uint64 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-int8 ibstrGetI8(ibytestream_t *ctx) {
- int8 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-int16 ibstrGetI16(ibytestream_t *ctx) {
- int16 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-int32 ibstrGetI32(ibytestream_t *ctx) {
- int32 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-int64 ibstrGetI64(ibytestream_t *ctx) {
- int64 value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-float ibstrGetFloat(ibytestream_t *ctx) {
- float value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-double ibstrGetDouble(ibytestream_t *ctx) {
- double value = 0;
- usize read = ibstrRead(ctx, &value, sizeof(value));
- return read == sizeof(value) ? value : 0;
-}
-
-strview_t ibstrGetView(ibytestream_t *ctx, usize lensize) {
- uint64 len = 0;
- usize read = ibstrRead(ctx, &len, lensize);
- if (read != lensize) {
- warn("couldn't read %zu bytes, instead read %zu for string", lensize, read);
- return STRV_EMPTY;
- }
- usize remaining = ibstrRemaining(ctx);
- if (len > remaining) {
- warn("trying to read a string of length %zu, but only %zu bytes remaining", len, remaining);
- len = remaining;
- }
-
- const char *str = (const char *)ctx->cur;
- ctx->cur += len;
-
- return (strview_t){
- .buf = str,
- .len = len,
- };
-}
-
-#include "warnings/colla_warn_end.h"
\ No newline at end of file
diff --git a/strstream.h b/strstream.h
deleted file mode 100644
index e0d4e26..0000000
--- a/strstream.h
+++ /dev/null
@@ -1,160 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-
-#include "collatypes.h"
-#include "str.h"
-
-typedef struct arena_t arena_t;
-
-/* == INPUT STREAM ============================================ */
-
-typedef struct instream_t {
- const char *start;
- const char *cur;
- usize size;
-} instream_t;
-
-// initialize with null-terminated string
-instream_t istrInit(const char *str);
-instream_t istrInitLen(const char *str, usize len);
-
-// get the current character and advance
-char istrGet(instream_t *ctx);
-// get the current character but don't advance
-char istrPeek(instream_t *ctx);
-// get the next character but don't advance
-char istrPeekNext(instream_t *ctx);
-// returns the previous character
-char istrPrev(instream_t *ctx);
-// returns the character before the previous
-char istrPrevPrev(instream_t *ctx);
-// ignore characters until the delimiter
-void istrIgnore(instream_t *ctx, char delim);
-// ignore characters until the delimiter and skip it
-void istrIgnoreAndSkip(instream_t *ctx, char delim);
-// skip n characters
-void istrSkip(instream_t *ctx, usize n);
-// skips whitespace (' ', '\\n', '\\t', '\\r')
-void istrSkipWhitespace(instream_t *ctx);
-// read len bytes into buffer, the buffer will not be null terminated
-void istrRead(instream_t *ctx, char *buf, usize len);
-// read a maximum of len bytes into buffer, the buffer will not be null terminated
-// returns the number of bytes read
-usize istrReadMax(instream_t *ctx, char *buf, usize len);
-// returns to the beginning of the stream
-void istrRewind(instream_t *ctx);
-// returns back characters
-void istrRewindN(instream_t *ctx, usize amount);
-// returns the number of bytes read from beginning of stream
-usize istrTell(instream_t ctx);
-// returns the number of bytes left to read in the stream
-usize istrRemaining(instream_t ctx);
-// return true if the stream doesn't have any new bytes to read
-bool istrIsFinished(instream_t ctx);
-
-bool istrGetBool(instream_t *ctx, bool *val);
-bool istrGetU8(instream_t *ctx, uint8 *val);
-bool istrGetU16(instream_t *ctx, uint16 *val);
-bool istrGetU32(instream_t *ctx, uint32 *val);
-bool istrGetU64(instream_t *ctx, uint64 *val);
-bool istrGetI8(instream_t *ctx, int8 *val);
-bool istrGetI16(instream_t *ctx, int16 *val);
-bool istrGetI32(instream_t *ctx, int32 *val);
-bool istrGetI64(instream_t *ctx, int64 *val);
-bool istrGetFloat(instream_t *ctx, float *val);
-bool istrGetDouble(instream_t *ctx, double *val);
-str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim);
-// get a string of maximum size len, the string is not allocated by the function and will be null terminated
-usize istrGetBuf(instream_t *ctx, char *buf, usize buflen);
-strview_t istrGetView(instream_t *ctx, char delim);
-strview_t istrGetViewEither(instream_t *ctx, strview_t chars);
-strview_t istrGetViewLen(instream_t *ctx, usize len);
-strview_t istrGetLine(instream_t *ctx);
-
-/* == OUTPUT STREAM =========================================== */
-
-typedef struct outstream_t {
- char *beg;
- arena_t *arena;
-} outstream_t;
-
-outstream_t ostrInit(arena_t *exclusive_arena);
-void ostrClear(outstream_t *ctx);
-
-usize ostrTell(outstream_t *ctx);
-
-char ostrBack(outstream_t *ctx);
-str_t ostrAsStr(outstream_t *ctx);
-strview_t ostrAsView(outstream_t *ctx);
-
-void ostrPrintf(outstream_t *ctx, const char *fmt, ...);
-void ostrPrintfV(outstream_t *ctx, const char *fmt, va_list args);
-void ostrPutc(outstream_t *ctx, char c);
-void ostrPuts(outstream_t *ctx, strview_t v);
-
-void ostrAppendBool(outstream_t *ctx, bool val);
-void ostrAppendUInt(outstream_t *ctx, uint64 val);
-void ostrAppendInt(outstream_t *ctx, int64 val);
-void ostrAppendNum(outstream_t *ctx, double val);
-
-/* == OUT BYTE STREAM ========================================= */
-
-typedef struct {
- uint8 *beg;
- arena_t *arena;
-} obytestream_t;
-
-obytestream_t obstrInit(arena_t *exclusive_arena);
-void obstrClear(obytestream_t *ctx);
-
-usize obstrTell(obytestream_t *ctx);
-buffer_t obstrAsBuf(obytestream_t *ctx);
-
-void obstrWrite(obytestream_t *ctx, const void *buf, usize buflen);
-void obstrPuts(obytestream_t *ctx, strview_t str);
-void obstrAppendU8(obytestream_t *ctx, uint8 value);
-void obstrAppendU16(obytestream_t *ctx, uint16 value);
-void obstrAppendU32(obytestream_t *ctx, uint32 value);
-void obstrAppendU64(obytestream_t *ctx, uint64 value);
-void obstrAppendI8(obytestream_t *ctx, int8 value);
-void obstrAppendI16(obytestream_t *ctx, int16 value);
-void obstrAppendI32(obytestream_t *ctx, int32 value);
-void obstrAppendI64(obytestream_t *ctx, int64 value);
-
-/* == IN BYTE STREAM ========================================== */
-
-typedef struct {
- const uint8 *start;
- const uint8 *cur;
- usize size;
-} ibytestream_t;
-
-ibytestream_t ibstrInit(const void *buf, usize len);
-
-usize ibstrRemaining(ibytestream_t *ctx);
-usize ibstrTell(ibytestream_t *ctx);
-usize ibstrRead(ibytestream_t *ctx, void *buf, usize buflen);
-
-uint8 ibstrGetU8(ibytestream_t *ctx);
-uint16 ibstrGetU16(ibytestream_t *ctx);
-uint32 ibstrGetU32(ibytestream_t *ctx);
-uint64 ibstrGetU64(ibytestream_t *ctx);
-int8 ibstrGetI8(ibytestream_t *ctx);
-int16 ibstrGetI16(ibytestream_t *ctx);
-int32 ibstrGetI32(ibytestream_t *ctx);
-int64 ibstrGetI64(ibytestream_t *ctx);
-float ibstrGetFloat(ibytestream_t *ctx);
-double ibstrGetDouble(ibytestream_t *ctx);
-
-// reads a string, before reads bytes for the length (e.g. sizeof(uint32))
-// then reads sizeof(char) * strlen
-strview_t ibstrGetView(ibytestream_t *ctx, usize lensize);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
diff --git a/tcc/colla_tcc.h b/tcc/colla_tcc.h
index 2be284c..daea037 100644
--- a/tcc/colla_tcc.h
+++ b/tcc/colla_tcc.h
@@ -3,6 +3,7 @@
#if COLLA_TCC && COLLA_WIN
#include
+#include
//// FILE.H ////////////////////////////////////////////////////////////////////////////////////
@@ -20,6 +21,15 @@ static BOOL tcc_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) {
//// STR.H /////////////////////////////////////////////////////////////////////////////////////
#define CP_UTF8 65001
+
+#define strtoull _strtoui64
+#define strtoll _strtoi64
+#define strtof strtod
+
+extern unsigned __int64 __stdcall _strtoui64(const char *strSource, char **endptr, int base);
+extern __int64 __stdcall _strtoi64(const char *strSource, char **endptr, int base);
+// extern double __cdecl strtod(const char *strSource, char **endptr);
+
extern int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
extern int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
@@ -264,6 +274,9 @@ extern BOOL __stdcall SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVari
#define INTERNET_DEFAULT_HTTPS_PORT 443
#define INTERNET_SERVICE_HTTP 3
#define INTERNET_FLAG_SECURE 0x00800000
+#define HTTP_QUERY_STATUS_CODE 19
+#define HTTP_QUERY_RAW_HEADERS_CRLF 22
+#define HTTP_QUERY_FLAG_NUMBER 0x20000000
typedef LPVOID HINTERNET;
@@ -274,27 +287,35 @@ typedef WORD INTERNET_PORT;
#define InternetConnect InternetConnectW
#define HttpOpenRequest HttpOpenRequestW
#define HttpSendRequest HttpSendRequestW
+ #define HttpAddRequestHeaders HttpAddRequestHeadersW
+ #define HttpQueryInfo HttpQueryInfoW
#else
#define InternetOpen InternetOpenA
#define InternetConnect InternetConnectA
#define HttpOpenRequest HttpOpenRequestA
#define HttpSendRequest HttpSendRequestA
+ #define HttpAddRequestHeaders HttpAddRequestHeadersA
+ #define HttpQueryInfo HttpQueryInfoA
#endif
extern HINTERNET __stdcall InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags);
extern HINTERNET __stdcall InternetConnectW(HINTERNET hInternet, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, LPCWSTR lpszUserName, LPCWSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
extern HINTERNET __stdcall HttpOpenRequestW(HINTERNET hConnect, LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, LPCWSTR lpszReferrer, LPCWSTR *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext);
extern BOOL __stdcall HttpSendRequestW(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength);
+extern BOOL __stdcall HttpAddRequestHeadersW(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
+extern BOOL __stdcall HttpQueryInfoW(HINTERNET hRequest, DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);
extern HINTERNET __stdcall InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags);
extern HINTERNET __stdcall InternetConnectA(HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
extern HINTERNET __stdcall HttpOpenRequestA(HINTERNET hConnect, LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion, LPCSTR lpszReferrer, LPCSTR *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext);
extern BOOL __stdcall HttpSendRequestA(HINTERNET hRequest, LPCSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength);
-
extern BOOL __stdcall HttpAddRequestHeadersA(HINTERNET hRequest, LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
+extern BOOL __stdcall HttpQueryInfoA(HINTERNET hRequest, DWORD dwInfoLevel, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);
+
extern BOOL __stdcall InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead);
extern BOOL __stdcall InternetCloseHandle(HINTERNET hInternet);
+
#endif
#endif
\ No newline at end of file
diff --git a/tools/build b/tools/build
deleted file mode 100644
index bed761b..0000000
--- a/tools/build
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-# cosmocc -Os -mtiny -o docs.com docs.c
-
-# optimised version for x86/64 only, shaves off ~300KB
-x86_64-unknown-cosmo-cc -Os -mtiny -o ../docs/docs.com docs.c
-
-rm ../docs/docs.com.dbg
\ No newline at end of file
diff --git a/tools/docs.c b/tools/docs.c
deleted file mode 100644
index b20ef50..0000000
--- a/tools/docs.c
+++ /dev/null
@@ -1,465 +0,0 @@
-#include "../arena.c"
-#include "../file.c"
-#include "../format.c"
-#include "../ini.c"
-#include "../str.c"
-#include "../strstream.c"
-#include "../tracelog.c"
-#include "../vmem.c"
-#include "../markdown.c"
-#include "../highlight.c"
-#include "../dir.c"
-#include "../socket.c"
-#include "../http.c"
-#include "../server.c"
-
-#include "../html.h"
-
-const char *raw_css;
-
-struct {
- bool gen;
- strview_t gendir;
-} options = {0};
-
-typedef struct page_t {
- str_t title;
- str_t url;
- str_t data;
- struct page_t *next;
-} page_t;
-
-typedef struct {
- uint8 arenabuf[KB(5)];
- arena_t arena;
- hl_ctx_t *hl;
- int line;
-} cparser_ctx_t;
-
-void md_cparser_init(void *udata) {
- cparser_ctx_t *cparser = udata;
- cparser->line = 1;
- if (cparser->hl) {
- return;
- }
- cparser->arena = arenaMake(ARENA_STATIC, sizeof(cparser->arenabuf), cparser->arenabuf);
- cparser->hl = hlInit(&cparser->arena, &(hl_config_t){
- .colors = {
- [HL_COLOR_NORMAL] = strv(""),
- [HL_COLOR_PREPROC] = strv(""),
- [HL_COLOR_TYPES] = strv(""),
- [HL_COLOR_CUSTOM_TYPES] = strv(""),
- [HL_COLOR_KEYWORDS] = strv(""),
- [HL_COLOR_NUMBER] = strv(""),
- [HL_COLOR_STRING] = strv(""),
- [HL_COLOR_COMMENT] = strv(""),
- [HL_COLOR_FUNC] = strv(""),
- [HL_COLOR_SYMBOL] = strv(""),
- [HL_COLOR_MACRO] = strv(""),
- },
- .flags = HL_FLAG_HTML,
- });
-}
-
-void md_cparser_callback(strview_t line, outstream_t *out, void *udata) {
- cparser_ctx_t *cparser = udata;
-
- arena_t scratch = cparser->arena;
- str_t highlighted = hlHighlight(&scratch, cparser->hl, line);
- ostrPrintf(out, "%v", highlighted);
-}
-
-page_t *get_pages(arena_t *arena, strview_t path, strview_t default_page) {
- arena_t scratch = arenaMake(ARENA_VIRTUAL, MB(1));
-
- dir_t *dir = dirOpen(&scratch, path);
- dir_entry_t *entry = NULL;
-
- page_t *first = NULL;
- page_t *head = NULL;
- page_t *tail = NULL;
-
- cparser_ctx_t cparser = {0};
-
- while ((entry = dirNext(&scratch, dir))) {
- if (entry->type != DIRTYPE_FILE) {
- continue;
- }
-
- strview_t name, ext;
- fileSplitPath(strv(entry->name), NULL, &name, &ext);
-
- if (!strvEquals(ext, strv(".md"))) {
- continue;
- }
-
- str_t fullname = strFmt(&scratch, "%v/%v", path, entry->name);
- str_t markdown_str = fileReadWholeStr(&scratch, strv(fullname));
-
- str_t md = markdownStr(&scratch, strv(markdown_str), &(md_options_t){
- .parsers = (md_parser_t[]){
- {
- .init = md_cparser_init,
- .callback = md_cparser_callback,
- .userdata = &cparser,
- .lang = strv("c"),
- },
- },
- .parsers_count = 1,
- });
-
- page_t *page = alloc(arena, page_t);
- page->data = md;
- page->url = str(arena, name);
-
- usize line_end = strvFind(strv(markdown_str), '\n', 0);
- strview_t line = strvSub(strv(markdown_str), 0, line_end);
- strview_t page_title = strvTrim(strvRemovePrefix(line, 1));
- page->title = strFmt(arena, "%v", page_title);
-
- if (!first && strvEquals(name, default_page)) {
- first = page;
- }
- else {
- if (!head) head = page;
- if (tail) tail->next = page;
- tail = page;
- }
- }
-
- if (first) {
- first->next = head;
- head = first;
- }
-
- strview_t css = strv(raw_css);
-
- for_each(page, head) {
- str_t html = STR_EMPTY;
-
- htmlBeg(arena, &html);
- headBeg();
- title(page->title);
- style(css);
- headEnd();
- bodyBeg();
- divBeg(.id="main");
- divBeg(.class="content");
- divBeg(.class="pages-container");
- divBeg(.class="pages");
- for_each(item, head) {
- str_t class = strFmt(&scratch, "page-item%s", item == page ? " page-current" : "");
- str_t href = STR_EMPTY;
- if (options.gen) {
- href = strFmt(&scratch, "%v.html", item->url);
- }
- else {
- href = strFmt(&scratch, "%v", item->url);
- }
-
- a(
- item->title,
- .href = href.buf,
- .class = class.buf,
- );
- }
- divEnd();
- divEnd();
-
- divBeg(.class="document-container");
- divBeg(.class="document");
- htmlPuts(page->data);
- htmlRaw();
- divEnd();
- divEnd();
- divEnd();
- divEnd();
- bodyEnd();
- htmlEnd();
-
- page->data = html;
- }
-
- arenaCleanup(&scratch);
-
- return head;
-}
-
-str_t server_default(arena_t scratch, server_t *server, server_req_t *req, void *userdata) {
- strview_t needle = strv(req->page);
- if (strvFront(needle) == '/') {
- needle = strvRemovePrefix(strv(req->page), 1);
- }
-
- page_t *page = userdata;
- while (page) {
- if (strvEquals(strv(page->url), needle)) {
- break;
- }
- page = page->next;
- }
-
- // if the url is invalid, return the default page
- if (!page) {
- page = userdata;
- }
-
- return serverMakeResponse(&scratch, 200, strv("text/html"), strv(page->data));
-}
-
-str_t server_quit(arena_t scratch, server_t *server, server_req_t *req, void *userdata) {
- serverStop(server);
- return STR_EMPTY;
-}
-
-int main(int argc, char **argv) {
- arena_t arena = arenaMake(ARENA_VIRTUAL, MB(1));
-
- for (int i = 1; i < argc; ++i) {
- strview_t arg = strv(argv[i]);
- if (strvEquals(arg, strv("-h"))) {
- info("usage: %s [-h, -gen ]", argv[0]);
- return 0;
- }
- else if (strvEquals(arg, strv("-gen"))) {
- options.gen = true;
- if ((i + 1) < argc) {
- options.gendir = strv(argv[++i]);
- }
- }
- }
-
- page_t *pages = get_pages(&arena, strv("."), strv("readme"));
- if (!pages) {
- err("could not get pages");
- return 1;
- }
-
- if (options.gen) {
- if (strvBack(options.gendir) == '/' || strvBack(options.gendir) == '\\') {
- options.gendir.len -= 1;
- }
-
- for_each(page, pages) {
- arena_t scratch = arena;
- str_t fname = strFmt(&scratch, "%v/%v.html", options.gendir, page->url);
- if (!fileWriteWhole(strv(fname), page->data.buf, page->data.len)) {
- err("couldn't save page %v", fname);
- }
- else {
- info("saved %v", fname);
- }
- }
-
- return 0;
- }
-
- server_t *s = serverSetup(&arena, 8080, true);
- serverRouteDefault(&arena, s, server_default, pages);
- serverRoute(&arena, s, strv("/quit"), server_quit, NULL);
- serverStart(arena, s);
-
- arenaCleanup(&arena);
-}
-
-//// HTML GENERATION STUFF ///////////////////////////////
-
-const char *raw_css = ""
-"html, body, #main {\n"
-" margin: 0;\n"
-" width: 100%;\n"
-" height: 100%;\n"
-" font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','Noto Sans',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji';\n"
-" \n"
-" --black: #121212;\n"
-" --dark-gray: #212121;\n"
-" --gray: #303030;\n"
-" --light-gray: #424242;\n"
-" --accent: #FFA500;\n"
-" /* --accent: #F98334; */\n"
-"\n"
-" --blue: #45559E;\n"
-" --orange: #F98334;\n"
-" --yellow: #FABD2F;\n"
-" --red: #FB4934;\n"
-" --pink: #D3869B;\n"
-" --green: #B8BB26;\n"
-" --azure: #7FA375;\n"
-" --white: #FBF1C7;\n"
-" --light-blue: #83A598;\n"
-"}\n"
-"\n"
-"hr {\n"
-" color: white;\n"
-" opacity: 0.5;\n"
-"}\n"
-"\n"
-"a {\n"
-" text-decoration: none;\n"
-" font-weight: bold;\n"
-" color: var(--red);\n"
-"}\n"
-"\n"
-"a:hover {\n"
-" text-decoration: underline;\n"
-"}\n"
-"\n"
-".content {\n"
-" width: 100%;\n"
-" height: 100%;\n"
-" background-color: var(--black);\n"
-" display: flex;\n"
-" gap: 20px;\n"
-" align-items: stretch;\n"
-" color: white;\n"
-" overflow-x: scroll;\n"
-"}\n"
-"\n"
-".pages-container {\n"
-" position: sticky;\n"
-" top: 0;\n"
-" min-width: 200px;\n"
-" background-color: var(--dark-gray);\n"
-" border-right: 1px solid var(--light-gray);\n"
-" box-shadow: 0 0 20px rgba(0, 0, 0, 1.0);\n"
-" overflow-y: scroll;\n"
-"}\n"
-"\n"
-".document-container {\n"
-" display: flex;\n"
-" justify-content: center;\n"
-" width: 100%;\n"
-"}\n"
-"\n"
-".document {\n"
-" width: 100%;\n"
-" max-width: 700px;\n"
-" line-height: 1.5;\n"
-"}\n"
-"\n"
-".document-padding {\n"
-" height: 50px;\n"
-"}\n"
-"\n"
-".pages {\n"
-" position: relative;\n"
-" background-color: var(--dark-gray);\n"
-"}\n"
-"\n"
-".page-item:last-of-type {\n"
-" border-bottom: 0;\n"
-"}\n"
-"\n"
-".page-item {\n"
-" text-decoration: none;\n"
-" display: block;\n"
-" color: rgba(255, 255, 255, 0.5);\n"
-" padding: 0.2em 0 0.2em 1.5em;\n"
-" border-bottom: 1px solid var(--light-gray);\n"
-"}\n"
-"\n"
-".page-item:hover {\n"
-" background-color: var(--light-gray);\n"
-" cursor: pointer;\n"
-" color: white;\n"
-" opacity: 1;\n"
-"}\n"
-"\n"
-".page-current {\n"
-" color: var(--accent);\n"
-" opacity: 1;\n"
-"}\n"
-"\n"
-".page-current:hover {\n"
-" color: var(--accent);\n"
-"}\n"
-"\n"
-".page-spacing {\n"
-" height: 25px; \n"
-"}\n"
-"\n"
-"code {\n"
-" color: var(--accent);\n"
-" background-color: var(--dark-gray);\n"
-" padding: 0.2em 0.5em 0.2em 0.5em;\n"
-" border-radius: 0.5em;\n"
-"}\n"
-"\n"
-"pre {\n"
-" margin: 0;\n"
-" margin-top: 2em;\n"
-" background-color: var(--dark-gray);\n"
-" padding: 16px;\n"
-" border-radius: 0.3em;\n"
-" overflow-x: scroll;\n"
-"\n"
-" border: 1px solid var(--light-gray);\n"
-" box-shadow: 0 0 20px rgba(0, 0, 0, 1.0);\n"
-"}\n"
-"\n"
-"pre > code {\n"
-" color: #FBF1C7;\n"
-" padding: 0;\n"
-" background-color: transparent;\n"
-"}\n"
-"\n"
-"pre ol {\n"
-" counter-reset: item;\n"
-" padding-left: 0;\n"
-"}\n"
-"\n"
-"pre li {\n"
-" display: block;\n"
-" margin-left: 0em;\n"
-"}\n"
-"\n"
-"pre li::before {\n"
-" display: inline-block;\n"
-" content: counter(item);\n"
-" counter-increment: item;\n"
-" width: 2em;\n"
-" padding-right: 1.5em;\n"
-" text-align: right;\n"
-"}\n"
-"\n"
-"/* code block colors */\n"
-".c-preproc {\n"
-" color: #45559E;\n"
-"}\n"
-"\n"
-".c-types {\n"
-" color: #F98334;\n"
-"}\n"
-"\n"
-".c-custom-types {\n"
-" color: #FABD2F;\n"
-"}\n"
-"\n"
-".c-kwrds {\n"
-" color: #FB4934;\n"
-"}\n"
-"\n"
-".c-num {\n"
-" color: #D3869B;\n"
-"}\n"
-"\n"
-".c-str {\n"
-" color: #B8BB26;\n"
-"}\n"
-"\n"
-".c-cmnt {\n"
-" color: #928374;\n"
-"}\n"
-"\n"
-".c-func {\n"
-" color: #7FA375;\n"
-"}\n"
-"\n"
-".c-sym {\n"
-" color: #FBF1C7;\n"
-"}\n"
-"\n"
-".c-macro {\n"
-" color: #83A598;\n"
-"}\n"
-"";
\ No newline at end of file
diff --git a/tools/nob.c b/tools/nob.c
new file mode 100644
index 0000000..090beb7
--- /dev/null
+++ b/tools/nob.c
@@ -0,0 +1,418 @@
+#define COLLA_NO_CONDITION_VARIABLE 1
+
+#include "../build.c"
+#include
+#include
+
+#if COLLA_TCC
+
+WINBASEAPI LPCH WINAPI GetEnvironmentStringsj(VOID);
+WINBASEAPI LPWCH WINAPI GetEnvironmentStringsW(VOID);
+WINBASEAPI BOOL WINAPI FreeEnvironmentStringsA(LPCH penv);
+WINBASEAPI BOOL WINAPI FreeEnvironmentStringsW(LPWCH penv);
+#ifdef UNICODE
+ #define GetEnvironmentStrings GetEnvironmentStringsW
+ #define FreeEnvironmentStrings FreeEnvironmentStringsW
+#else
+ #define GetEnvironmentStrings GetEnvironmentStringsA
+ #define FreeEnvironmentStrings FreeEnvironmentStringsA
+#endif
+
+#endif
+
+int strv_to_int(strview_t strv) {
+ instream_t in = istr_init(strv);
+ i32 value = 0;
+ if (!istr_get_i32(&in, &value)) {
+ return 0;
+ }
+ return value;
+}
+
+str_t find_vcvars_path(arena_t *arena) {
+ strview_t base_path = strv("C:/Program Files/Microsoft Visual Studio");
+ // find year
+ int year = 0;
+ {
+ arena_t tmp = *arena;
+
+ dir_t *dir = os_dir_open(&tmp, base_path);
+ if (!os_dir_is_valid(dir)) {
+ err("couldn't open directory (%v)", base_path);
+ return STR_EMPTY;
+ }
+
+ dir_foreach(&tmp, entry, dir) {
+ if (entry->type != DIRTYPE_DIR) continue;
+
+ int number = strv_to_int(strv(entry->name));
+ if (number > year) year = number;
+ }
+ }
+
+ if (year == 0) {
+ err("couldn't find visual studio year version");
+ return STR_EMPTY;
+ }
+
+ str_t path_with_year = str_fmt(arena, "%v/%d", base_path, year);
+
+ // find edition
+ const char *editions[] = {
+ "Enterprise",
+ "Professional",
+ "Community",
+ };
+
+ int edition = 0;
+
+ for (; edition < arrlen(editions); ++edition) {
+ arena_t tmp = *arena;
+ str_t path = str_fmt(&tmp, "%v/%s", path_with_year, editions[edition]);
+ if (os_file_exists(strv(path))) {
+ break;
+ }
+ }
+
+ if (edition >= arrlen(editions)) {
+ err("couldn't find visual studio edition");
+ return STR_EMPTY;
+ }
+
+ str_t vcvars = str_fmt(arena, "%v/%s/VC/Auxiliary/Build/vcvars64.bat", path_with_year, editions[edition]);
+
+ return vcvars;
+}
+
+bool load_cache(arena_t *arena) {
+ if (!os_file_exists(strv("build/cache.ini"))) {
+ err("build/cache.ini doesn't exist");
+ return false;
+ }
+
+ {
+ arena_t scratch = *arena;
+
+ ini_t ini = ini_parse(&scratch, strv("build/cache.ini"), &(iniopt_t){ .comment_vals = strv("#") });
+ initable_t *root = ini_get_table(&ini, INI_ROOT);
+ if (!root) fatal("fail");
+
+ for_each (val, root->values) {
+ os_set_env_var(scratch, val->key, val->value);
+ }
+ }
+
+ return true;
+}
+
+typedef enum optimise_level_e {
+ OPTIMISE_NONE,
+ OPTIMISE_FAST,
+ OPTIMISE_SMALL,
+ OPTIMISE__COUNT,
+} optimise_level_e;
+
+typedef enum warning_level_e {
+ WARNING_NONE,
+ WARNING_DEFAULT,
+ WARNING_ALL,
+ WARNING__COUNT,
+} warning_level_e;
+
+typedef enum sanitiser_e {
+ SANITISER_NONE,
+ SANITISER_ADDRESS,
+ SANITISER__COUNT,
+} sanitiser_e;
+
+typedef enum cversion_e {
+ CVERSION_LATEST,
+ CVERSION_17,
+ CVERSION_11,
+ CVERSION__COUNT
+} cversion_e;
+
+typedef struct options_t options_t;
+struct options_t {
+ strview_t input_fname;
+ strview_t out_fname;
+ optimise_level_e optimisation;
+ warning_level_e warnings;
+ bool warnings_as_error;
+ sanitiser_e sanitiser;
+ bool fast_math;
+ bool debug;
+ strv_list_t *defines;
+ cversion_e cstd;
+ bool run;
+ strv_list_t *run_args;
+ bool is_cpp;
+};
+
+void print_help_message(void) {
+ puts("usage:");
+ puts(" -r / -run [input.c] [args...] compiles and runs , forwards ");
+ puts(" -h / -help print this message");
+ puts(" -o / -out [filename] output filename (default: build/.exe)");
+ puts(" -O / -optimise [fast,small] optimisation level");
+ puts(" -w / -warning [default,all] warning level");
+ puts(" -werror treat warnings as errors");
+ puts(" -fsanitize [address] turn on sanitiser");
+ puts(" -fastmath turn on fast math");
+ puts(" -g / -debug generate debug information");
+ puts(" -D / -define [key=value,key] add a preprocessor define ");
+ puts(" -std [c11,c17,clatest] select c standard (default: clatest)");
+ puts(" -cpp compile c++ instead of c");
+ exit(0);
+}
+
+optimise_level_e get_optimisation_level(strview_t arg) {
+ if (strv_equals(arg, strv("fast"))) {
+ return OPTIMISE_FAST;
+ }
+ else if (strv_equals(arg, strv("small"))) {
+ return OPTIMISE_SMALL;
+ }
+ warn("unrecognised optimisation level: (%v)", arg);
+ return OPTIMISE_NONE;
+}
+
+warning_level_e get_warning_level(strview_t arg) {
+ if (strv_equals(arg, strv("default"))) {
+ return WARNING_DEFAULT;
+ }
+ else if (strv_equals(arg, strv("all"))) {
+ return WARNING_ALL;
+ }
+ warn("unrecognised warning level: (%v)", arg);
+ return WARNING_NONE;
+}
+
+sanitiser_e get_sanitiser(strview_t arg) {
+ if (strv_equals(arg, strv("address"))) {
+ return SANITISER_ADDRESS;
+ }
+ warn("unrecognised sanitiser: (%v)", arg);
+ return SANITISER_NONE;
+}
+
+cversion_e get_cversion(strview_t arg) {
+ if (strv_equals(arg, strv("clatest"))) {
+ return CVERSION_LATEST;
+ }
+ else if (strv_equals(arg, strv("c17"))) {
+ return CVERSION_17;
+ }
+ else if (strv_equals(arg, strv("c11"))) {
+ return CVERSION_11;
+ }
+ warn("unrecognised c std version: (%v)", arg);
+ return CVERSION_LATEST;
+}
+
+options_t parse_options(arena_t *arena, int argc, char **argv) {
+ options_t out = {0};
+
+ for (int i = 1; i < argc; ++i) {
+ strview_t arg = strv(argv[i]);
+
+#define CHECK_OPT_BEG() if (false) {}
+#define CHECK_OPT1(opt) else if (strv_equals(arg, strv("-" opt)))
+#define CHECK_OPT2(small, big) else if (strv_equals(arg, strv("-" small)) || strv_equals(arg, strv("-" big)))
+
+#define GET_NEXT_ARG() (i + 1) < argc ? strv(argv[++i]) : STRV_EMPTY
+
+ CHECK_OPT_BEG()
+ CHECK_OPT2("h", "help") {
+ print_help_message();
+ }
+ CHECK_OPT2("o", "out") {
+ strview_t out_fname = GET_NEXT_ARG();
+ str_t out_fname_str = str_fmt(arena, "build/%v", out_fname);
+ out.out_fname = strv(out_fname_str);
+ }
+ CHECK_OPT2("O", "optimise") {
+ out.optimisation = get_optimisation_level(GET_NEXT_ARG());
+ }
+ CHECK_OPT2("w", "warning") {
+ out.warnings = get_warning_level(GET_NEXT_ARG());
+ }
+ CHECK_OPT1("werror") {
+ out.warnings_as_error = true;
+ }
+ CHECK_OPT1("fsanitize") {
+ out.sanitiser = get_sanitiser(GET_NEXT_ARG());
+ }
+ CHECK_OPT1("fastmath") {
+ out.fast_math = true;
+ }
+ CHECK_OPT2("g", "debug") {
+ out.debug = true;
+ }
+ CHECK_OPT2("D", "define") {
+ darr_push(arena, out.defines, GET_NEXT_ARG());
+ }
+ CHECK_OPT1("std") {
+ out.cstd = get_cversion(GET_NEXT_ARG());
+ }
+ CHECK_OPT1("cpp") {
+ out.is_cpp = true;
+ }
+ CHECK_OPT2("r", "run") {
+ out.run = true;
+ out.input_fname = GET_NEXT_ARG();
+ for (i += 1; i < argc; ++i) {
+ darr_push(arena, out.run_args, strv(argv[i]));
+ }
+ }
+ else {
+ out.input_fname = arg;
+ }
+ }
+
+#undef CHECK_OPT_BEG
+#undef CHECK_OPT1
+#undef CHECK_OPT2
+#undef GET_NEXT_ARG
+
+ if (strv_is_empty(out.out_fname)) {
+ strview_t name;
+ os_file_split_path(out.input_fname, NULL, &name, NULL);
+ str_t out_fname = str_fmt(arena, "build\\%v", name);
+ out.out_fname = strv(out_fname);
+ }
+
+ return out;
+}
+
+int main(int argc, char **argv) {
+ os_init();
+
+ arena_t arena = arena_make(ARENA_VIRTUAL, GB(1));
+
+ if (argc < 2) {
+ print_help_message();
+ }
+
+ options_t opt = parse_options(&arena, argc, argv);
+
+ if (!os_file_exists(strv("build/"))) {
+ info("creating build folder");
+ _mkdir("build");
+ }
+
+ if (!os_file_exists(strv("build/cache.ini"))) {
+ info("couldn't find cache.ini, creating it now");
+
+ arena_t scratch = arena;
+ str_t vcvars_path = find_vcvars_path(&scratch);
+
+ if (!os_run_cmd(scratch, os_make_cmd(strv(vcvars_path), strv("&&"), strv("set"), strv(">"), strv("build\\cache.ini")), NULL)) {
+ fatal("failed to run vcvars64.bat");
+ os_abort(1);
+ }
+ }
+
+ {
+ arena_t scratch = arena;
+
+ if (!load_cache(&scratch)) {
+ os_abort(1);
+ }
+
+ os_cmd_t *cmd = NULL;
+
+ darr_push(&scratch, cmd, strv("cl"));
+ darr_push(&scratch, cmd, strv("/nologo"));
+ darr_push(&scratch, cmd, strv("/utf-8"));
+ if (!opt.is_cpp) {
+ darr_push(&scratch, cmd, strv("/TC"));
+ }
+
+ str_t output = str_fmt(&scratch, "/Fe:%v.exe", opt.out_fname);
+ str_t object = str_fmt(&scratch, "/Fo:%v.obj", opt.out_fname);
+ darr_push(&scratch, cmd, strv(output));
+ darr_push(&scratch, cmd, strv(object));
+
+ strview_t optimisations[OPTIMISE__COUNT] = {
+ strv("/Od"), // disabled
+ strv("/O2"), // fast code
+ strv("/O1"), // small code
+ };
+ darr_push(&scratch, cmd, optimisations[opt.optimisation]);
+
+ strview_t warnings[WARNING__COUNT] = {
+ strv("/W0"),
+ strv("/W3"),
+ strv("/W4"),
+ };
+ darr_push(&scratch, cmd, warnings[opt.warnings]);
+
+ if (opt.warnings_as_error) {
+ darr_push(&scratch, cmd, strv("/WX"));
+ }
+
+ if (opt.sanitiser) {
+ strview_t sanitisers[SANITISER__COUNT] = {
+ strv(""),
+ strv("/fsanitize=address"),
+ };
+ darr_push(&scratch, cmd, sanitisers[opt.sanitiser]);
+ }
+
+ if (opt.fast_math) {
+ darr_push(&scratch, cmd, strv("/fp:fast"));
+ }
+
+ if (opt.debug) {
+ darr_push(&scratch, cmd, strv("/Zi"));
+ }
+
+ for_each (def, opt.defines) {
+ for (int i = 0; i < def->count; ++i) {
+ str_t define = str_fmt(&scratch, "/D%v", def->items[i]);
+ darr_push(&scratch, cmd, strv(define));
+ }
+ }
+
+ strview_t cversion[CVERSION__COUNT] = {
+ strv("clatest"),
+ strv("c17"),
+ strv("c11"),
+ };
+
+ str_t cstd = str_fmt(&scratch, "/std:%v", cversion[opt.cstd]);
+ darr_push(&scratch, cmd, strv(cstd));
+
+ darr_push(&scratch, cmd, opt.input_fname);
+
+ // /LD -> create dynamic lib
+ // /LDd -> create debug dynamic lib
+ // /link
+
+ if (!os_run_cmd(scratch, cmd, NULL)) {
+ return 1;
+ }
+ }
+
+ if (opt.run) {
+ arena_t scratch = arena;
+ os_cmd_t *cmd = NULL;
+
+ darr_push(&scratch, cmd, opt.out_fname);
+
+ for_each (arg, opt.run_args) {
+ for (int i = 0; i < arg->count; ++i) {
+ darr_push(&scratch, cmd, arg->items[i]);
+ }
+ }
+
+ if (!os_run_cmd(scratch, cmd, NULL)) {
+ return 1;
+ }
+ }
+
+ arena_cleanup(&arena);
+
+ os_cleanup();
+}
\ No newline at end of file
diff --git a/tracelog.c b/tracelog.c
deleted file mode 100644
index 9867cc7..0000000
--- a/tracelog.c
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "tracelog.h"
-
-#include
-#include
-#include
-
-#include "format.h"
-
-#if COLLA_WIN
- #if COLLA_MSVC
- #pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS.
- #endif
-
- #include
-
-#if COLLA_CMT_LIB
- #pragma comment(lib, "User32")
-#endif
-
-#if COLLA_TCC
- #include "tcc/colla_tcc.h"
-#endif
-
- //#ifndef TLOG_NO_COLOURS
- // #define TLOG_NO_COLOURS
- //#endif
-#endif
-
-#if COLLA_EMC
- #define TLOG_NO_COLOURS
-#endif
-
-#if COLLA_EMC
- #define COLOUR_BLACK ""
- #define COLOUR_RED ""
- #define COLOUR_GREEN ""
- #define COLOUR_YELLOW ""
- #define COLOUR_BLUE ""
- #define COLOUR_MAGENTA ""
- #define COLOUR_CYAN ""
- #define COLOUR_WHITE ""
- #define COLOUR_RESET ""
- #define COLOUR_BOLD ""
-#elif defined(TLOG_NO_COLOURS)
- #define COLOUR_BLACK ""
- #define COLOUR_RED ""
- #define COLOUR_GREEN ""
- #define COLOUR_YELLOW ""
- #define COLOUR_BLUE ""
- #define COLOUR_MAGENTA ""
- #define COLOUR_CYAN ""
- #define COLOUR_WHITE ""
- #define COLOUR_RESET ""
- #define COLOUR_BOLD ""
-#else
- #define COLOUR_BLACK "\033[30m"
- #define COLOUR_RED "\033[31m"
- #define COLOUR_GREEN "\033[32m"
- #define COLOUR_YELLOW "\033[33m"
- #define COLOUR_BLUE "\033[22;34m"
- #define COLOUR_MAGENTA "\033[35m"
- #define COLOUR_CYAN "\033[36m"
- #define COLOUR_WHITE "\033[37m"
- #define COLOUR_RESET "\033[0m"
- #define COLOUR_BOLD "\033[1m"
-#endif
-
-static bool tl_use_newline = true;
-
-#if COLLA_WIN
-static void setLevelColour(int level) {
- WORD attribute = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
- switch (level) {
- case LogDebug: attribute = FOREGROUND_BLUE; break;
- case LogInfo: attribute = FOREGROUND_GREEN; break;
- case LogWarning: attribute = FOREGROUND_RED | FOREGROUND_GREEN; break;
- case LogError: attribute = FOREGROUND_RED; break;
- case LogFatal: attribute = FOREGROUND_RED; break;
- }
-
- HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE);
- SetConsoleTextAttribute(hc, attribute);
-}
-#else
-static void setLevelColour(int level) {
- switch (level) {
- case LogDebug: printf(COLOUR_BLUE); break;
- case LogInfo: printf(COLOUR_GREEN); break;
- case LogWarning: printf(COLOUR_YELLOW); break;
- case LogError: printf(COLOUR_RED); break;
- case LogFatal: printf(COLOUR_MAGENTA); break;
- default: printf(COLOUR_RESET); break;
- }
-}
-#endif
-
-void traceLog(int level, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
- traceLogVaList(level, fmt, args);
- va_end(args);
-}
-
-#ifdef TLOG_MUTEX
-#include "cthreads.h"
-static cmutex_t g_mtx = 0;
-#endif
-
-void traceLogVaList(int level, const char *fmt, va_list args) {
- #ifdef TLOG_MUTEX
- if (!g_mtx) g_mtx = mtxInit();
- mtxLock(g_mtx);
- #endif
-
- const char *beg = "";
- switch (level) {
- case LogAll: beg = "[ALL" ; break;
- case LogTrace: beg = "[TRACE" ; break;
- case LogDebug: beg = "[DEBUG" ; break;
- case LogInfo: beg = "[INFO" ; break;
- case LogWarning: beg = "[WARNING" ; break;
- case LogError: beg = "[ERROR" ; break;
- case LogFatal: beg = "[FATAL" ; break;
- default: break;
- }
-
-#if COLLA_WIN
- SetConsoleOutputCP(CP_UTF8);
-#endif
- setLevelColour(level);
- printf("%s", beg);
-
-#if GAME_CLIENT
- putchar(':');
- traceSetColour(COL_CYAN);
- printf("CLIENT");
-#elif GAME_HOST
- putchar(':');
- traceSetColour(COL_MAGENTA);
- printf("HOST");
-#endif
-
- setLevelColour(level);
- printf("]: ");
-
- // set back to white
- setLevelColour(LogTrace);
- fmtPrintv(fmt, args);
-
- if(tl_use_newline) {
-#if COLLA_EMC
- puts("
");
-#else
- puts("");
-#endif
- }
-
-#ifndef TLOG_DONT_EXIT_ON_FATAL
- if (level == LogFatal) {
-
-#ifndef TLOG_NO_MSGBOX
-
-#if COLLA_WIN
- char errbuff[1024];
- fmtBufferv(errbuff, sizeof(errbuff), fmt, args);
-
- char captionbuf[] =
-#if GAME_HOST
- "Fatal Host Error";
-#elif GAME_CLIENT
- "Fatal Client Error";
-#else
- "Fatal Error";
-#endif
- MessageBoxA(
- NULL,
- errbuff, captionbuf,
- MB_ICONERROR | MB_OK
- );
-#endif
-
-#endif
- fflush(stdout);
- abort();
- }
-#endif
-
-#ifdef TLOG_MUTEX
- mtxUnlock(g_mtx);
-#endif
-}
-
-void traceUseNewline(bool newline) {
- tl_use_newline = newline;
-}
-
-void traceSetColour(colour_e colour) {
-#if COLLA_WIN
- SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (WORD)colour);
-#else
- switch (colour) {
- case COL_RESET: printf(COLOUR_RESET); break;
- case COL_BLACK: printf(COLOUR_BLACK); break;
- case COL_BLUE: printf(COLOUR_BLUE); break;
- case COL_GREEN: printf(COLOUR_GREEN); break;
- case COL_CYAN: printf(COLOUR_CYAN); break;
- case COL_RED: printf(COLOUR_RED); break;
- case COL_MAGENTA: printf(COLOUR_MAGENTA); break;
- case COL_YELLOW: printf(COLOUR_YELLOW); break;
- }
-#endif
-}
\ No newline at end of file
diff --git a/tracelog.h b/tracelog.h
deleted file mode 100644
index 800fe49..0000000
--- a/tracelog.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Define any of this to turn on the option
- * -> TLOG_NO_COLOURS: print without using colours
- * -> TLOG_VS: print to visual studio console, also turns on TLOG_NO_COLOURS
- * -> TLOG_DONT_EXIT_ON_FATAL: don't call 'exit(1)' when using LogFatal
- * -> TLOG_DISABLE: turn off all logging, useful for release builds
- * -> TLOG_MUTEX: use a mutex on every traceLog call
-*/
-
-#include "collatypes.h"
-#include
-
-enum {
- LogAll, LogTrace, LogDebug, LogInfo, LogWarning, LogError, LogFatal
-};
-
-typedef enum {
- COL_RESET = 15,
- COL_BLACK = 8,
- COL_BLUE = 9,
- COL_GREEN = 10,
- COL_CYAN = 11,
- COL_RED = 12,
- COL_MAGENTA = 13,
- COL_YELLOW = 14,
- COL_WHITE = 15
-} colour_e;
-
-void traceLog(int level, const char *fmt, ...);
-void traceLogVaList(int level, const char *fmt, va_list args);
-void traceUseNewline(bool use_newline);
-void traceSetColour(colour_e colour);
-
-#ifdef TLOG_DISABLE
-#define tall(...)
-#define trace(...)
-#define debug(...)
-#define info(...)
-#define warn(...)
-#define err(...)
-#define fatal(...)
-#else
-#define tall(...) traceLog(LogAll, __VA_ARGS__)
-#define trace(...) traceLog(LogTrace, __VA_ARGS__)
-#define debug(...) traceLog(LogDebug, __VA_ARGS__)
-#define info(...) traceLog(LogInfo, __VA_ARGS__)
-#define warn(...) traceLog(LogWarning, __VA_ARGS__)
-#define err(...) traceLog(LogError, __VA_ARGS__)
-#define fatal(...) traceLog(LogFatal, __VA_ARGS__)
-#endif
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
diff --git a/utf8.c b/utf8.c
deleted file mode 100644
index 5d47c0a..0000000
--- a/utf8.c
+++ /dev/null
@@ -1,172 +0,0 @@
-#include "utf8.h"
-
-static const uint8 masks[] = {
- 0x7f, // 0111-1111
- 0x1f, // 0001-1111
- 0x0f, // 0000-1111
- 0x07, // 0000-0111
- 0x03, // 0000-0011
- 0x01 // 0000-0001
-};
-
-static struct {
- uint8 mask;
- uint8 result;
- int octets;
-} sizes[] = {
- { 0x80, 0x00, 1 }, // 1000-0000, 0000-0000
- { 0xE0, 0xC0, 2 }, // 1110-0000, 1100-0000
- { 0xF0, 0xE0, 3 }, // 1111-0000, 1110-0000
- { 0xF8, 0xF0, 4 }, // 1111-1000, 1111-0000
- { 0xFC, 0xF8, 5 }, // 1111-1100, 1111-1000
- { 0xFE, 0xF8, 6 }, // 1111-1110, 1111-1000
- { 0x80, 0x80, -1 }, // 1000-0000, 1000-0000
-};
-
-
-/*
-UTF-8 codepoints are encoded using the first bits of the first character
-
-byte 1 | byte 2 | byte 3 | byte 4
-0xxx xxxx | | |
-110x xxxx | 10xx xxxx | |
-1110 xxxx | 10xx xxxx | 10xx xxxx |
-1111 0xxx | 10xx xxxx | 10xx xxxx | 10xx xxxx
-
-so when we decode it we first find the size of the codepoint (from 1 to 4)
-then we apply the mask to the first byte to get the first character
-then we keep shifting the rune left 6 and applying the next byte to the mask
-until the codepoint is finished (size is 0)
-
-## EXAMPLE
-
-utf8 string (€) = 1110-0010 1000-0010 1010-1100
-
-cp = 0000-0000 0000-0000 0000-0000 0000-0000
-size = 3
-mask = 0x0f -> 0000-1111
-cp = *s & mask = 1110-0010 & 0000-1111 = 0000-0000 0000-0000 0000-0000 0000-0010
-++s = 1000-0010
-
---size = 2
-cp <<= 6 = 0000-0000 0000-0000 0000-0000 1000-0000
-cp |= *s & 0x3f = 1000-0010 & 0011-1111 = 0000-0000 0000-0000 0000-0000 1000-0010
-++s = 1010-1100
-
---size = 1
-cp <<= 6 = 0000-0000 0000-0000 0010-0000 1000-0000
-cp |= *s & 0x3f = 1010-1100 & 0011-1111 = 0000-0000 0000-0000 0010-0000 1010-1100
-++s = ----------
-
-final codepoint = 0010-0000 1010-1100
-€ codepoint = 0010-0000 1010-1100
-*/
-
-rune utf8Decode(const char **char_str) {
- const uint8 **s = (const uint8 **)char_str;
-
- rune ch = 0;
- // if is ascii
- if (**s < 128) {
- ch = **s;
- ++*s;
- return ch;
- }
- int size = utf8Size((const char *)*s);
- if (size == -1) {
- ++*s;
- return UTF8_INVALID;
- }
- uint8 mask = masks[size - 1];
- ch = **s & mask;
- ++*s;
- while(--size) {
- ch <<= 6;
- ch |= **s & 0x3f; // 0011-1111
- ++*s;
- }
- return ch;
-}
-
-
-/*
-to encode a codepoint in a utf8 string we first need to find
-the length of the codepoint
-then we start from the rightmost byte and loop for each byte of the codepoint
-using the length we got before until the first byte (which we skip)
-> and (&) with 0x3f so we ignore the first to bits of the codepoint
-> or (|) with 0x80 so we make sure that the first two bits are 10
-> bitshift the codepoint right 6
-
-finally, we apply the correct length-mask to the first byte
-
-## EXAMPLE
-
-ch € = 0010-0000 1010-1100
-ch < 0x10000
- first = 0xe0 = 1110-0000
- len = 3
-
-str[2] = (ch & 0x3f) | 0x80 = 1010-1100 & 0011-1111 | 1000-0000
- = 1010-1100
-ch >>= 6 = 0010-0000 1010-1100 >> 6 = 1000-0010
-
-str[1] = (ch & 0x3f) | 0x80 = 1000-0010 & 0011-1111 | 1000-000
- = 1000-0010
-ch >>= 6 = 1000-0010 >> 6 = 0000-0010
-
-str[0] = ch | first_mask = 0000-0010 | 1111-0000
- = 1111-0010
-
-str = 1111-0010 1000-0010 1010-1100
-utf8 € = 1110-0010 1000-0010 1010-1100
-*/
-
-usize utf8Encode(char *str, rune codepoint) {
- usize len = 0;
- uint8 first;
-
- if (codepoint < 0x80) { // 0000-0000 0000-0000 0000-0000 1000-0000
- first = 0;
- len = 1;
- }
- else if (codepoint < 0x800) { // 0000-0000 0000-0000 0000-1000 0000-0000
- first = 0xc0; // 1100-0000
- len = 2;
- }
- else if (codepoint < 0x10000) { // 0000-0000 0000-0001 0000-0000 0000-0000
- first = 0xe0; // 1110-0000
- len = 3;
- }
- else {
- first = 0xf0; // 1111-0000
- len = 4;
- }
-
- for (usize i = len - 1; i > 0; --i) {
- // 0x3f -> 0011-1111
- // 0x80 -> 1000-0000
- str[i] = (codepoint & 0x3f) | 0x80;
- codepoint >>= 6;
- }
-
- str[0] = (char)(codepoint | first);
- return len;
-}
-
-int utf8Size(const char *str) {
- uint8 c = (uint8)*str;
- for(usize i = 0; i < (sizeof(sizes) / sizeof(*sizes)); ++i) {
- if ((c & sizes[i].mask) == sizes[i].result) {
- return sizes[i].octets;
- }
- }
- return -1;
-}
-
-usize utf8CpSize(rune ch) {
- if (ch < 0x80) return 1;
- else if (ch < 0x800) return 2;
- else if (ch < 0x10000) return 3;
- return 4;
-}
diff --git a/utf8.h b/utf8.h
deleted file mode 100644
index 907e1b4..0000000
--- a/utf8.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "collatypes.h"
-
-typedef uint32 rune;
-
-enum {
- UTF8_MAX_SIZE = 4,
- UTF8_INVALID = 0x80
-};
-
-// grabs the next UTF-8 codepoint and advances string ptr
-rune utf8Decode(const char **str);
-// encodes a codepoint as UTF-8 and returns the length
-usize utf8Encode(char *str, rune ch);
-// returns the size of the next UTF-8 codepoint
-int utf8Size(const char *str);
-// returns the size of a UTF-8 codepoint
-usize utf8CpSize(rune ch);
diff --git a/vec.h b/vec.h
deleted file mode 100644
index 8982c9b..0000000
--- a/vec.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define vec(T) T *
-
-#define vecFree(vec) ((vec) ? free(_vecheader(vec)), NULL : NULL)
-#define vecCopy(src, dest) (vecFree(dest), vecAdd(dest, vecCount(src)), memcpy(dest, src, vecCount(src)))
-
-#define vecAppend(vec, ...) (_vecmaygrow(vec, 1), (vec)[_veclen(vec)] = (__VA_ARGS__), _veclen(vec)++)
-#define vecRemove(vec, ind) ((vec) ? (vec)[(ind)] = (vec)[--_veclen(vec)], NULL : 0)
-#define vecRemoveIt(vec, it) (vecRemove((vec), (it) - (vec)))
-#define vecLen(vec) ((vec) ? _veclen(vec) : 0)
-#define vecCap(vec) ((vec) ? _veccap(vec) : 0)
-
-#define vecBeg(vec) (vec)
-#define vecEnd(vec) ((vec) ? (vec) + _veclen(vec) : NULL)
-#define vecBack(vec) ((vec)[_veclen(vec) - 1])
-
-#define vecAdd(vec, n) (_vecmaygrow(vec, (n)), _veclen(vec) += (size_type)(n), &(vec)[_veclen(vec)-(n)])
-#define vecReserve(vec, n) (_vecmaygrow(vec, (n)))
-#define vecShrink(vec) (_vecshrink((void **)&(vec), _veclen(vec), sizeof(*(vec))))
-
-#define vecClear(vec) ((vec) ? _veclen(vec) = 0 : 0)
-#define vecPop(vec) ((vec)[--_veclen(vec)])
-
-// == IMPLEMENTATION ==========================================================================================
-
-#include
-#include
-#include
-
-#ifndef size_type
- #define size_type uint32_t
-#endif
-
-#define _vecheader(vec) ((size_type *)(vec) - 2)
-#define _veccap(vec) _vecheader(vec)[0]
-#define _veclen(vec) _vecheader(vec)[1]
-
-#define _vecneedgrow(vec, n) ((vec) == NULL || _veclen(vec) + n >= _veccap(vec))
-#define _vecmaygrow(vec, n) (_vecneedgrow(vec, (n)) ? _vecgrow(vec, (size_type)(n)) : (void)0)
-#define _vecgrow(vec, n) _vecgrowimpl((void **)&(vec), (n), sizeof(*(vec)))
-
-inline static void _vecgrowimpl(void **arr, size_type increment, size_type itemsize) {
- int newcap = *arr ? 2 * _veccap(*arr) + increment : increment + 1;
- void *ptr = realloc(*arr ? _vecheader(*arr) : 0, itemsize * newcap + sizeof(size_type) * 2);
- assert(ptr);
- if (ptr) {
- if (!*arr) ((size_type *)ptr)[1] = 0;
- *arr = (void *) ((size_type *)ptr + 2);
- _veccap(*arr) = newcap;
- }
-}
-
-inline static void _vecshrink(void **arr, size_type newcap, size_t itemsize) {
- if (newcap == _veccap(*arr) || !*arr) return;
- void *ptr = realloc(_vecheader(*arr), itemsize * newcap + sizeof(size_type) * 2);
- assert(ptr);
- if (ptr) {
- *arr = (void *) ((size_type *)ptr + 2);
- if (_veclen(*arr) < newcap) _veclen(*arr) = newcap;
- _veccap(*arr) = newcap;
- }
-}
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
diff --git a/vfs.c b/vfs.c
deleted file mode 100644
index 473564d..0000000
--- a/vfs.c
+++ /dev/null
@@ -1,635 +0,0 @@
-#include "vfs.h"
-
-#include "lz4/lz4.c"
-
-#include "file.h"
-#include "strstream.h"
-#include "arena.h"
-#include "dir.h"
-#include "bits.h"
-
-/*
-vfs format:
-=====================
-header:
------------
-magic: VFS
-u32 header_size
-u32 file_count
-u8 flags
-for each file:
- u64 hash // todo remove
- u8 namelen
- char *name
- u64 offset
- u64 size
- u64 compressed_size
------------
-* binary data *
-*/
-
-typedef struct vfsfile_t vfsfile_t;
-
-struct vfsfile_t {
- vfsfile_t *next;
- str_t path;
- uint64 size;
- uint64 comp_size;
-};
-
-typedef struct vfshmap_t vfshmap_t;
-typedef struct vfshnode_t vfshnode_t;
-
-typedef struct {
- uint64 offset;
- uint64 size;
- uint64 compsize;
-} vfsdata_t;
-
-struct vfshnode_t {
- vfshnode_t *next;
- uint64 hash;
- str_t key;
- uint32 index;
-};
-
-struct vfshmap_t {
- vfshnode_t **buckets;
- vfsdata_t *values;
-
- uint32 size;
- uint32 count;
- uint32 collisions;
- uint32 max_values;
-};
-
-struct virtualfs_t {
- file_t fp;
- buffer_t buffer;
- vfshmap_t hmap;
- uint64 base_offset;
- vfsflags_e flags;
-};
-
-vfsflags_e vfs_validate_flags(vfsflags_e flags);
-bool vfs_read(arena_t *arena, virtualfs_t *vfs, vfsdata_t *data, buffer_t *out, bool null_terminate);
-vfsfile_t *vfs_add_dir(arena_t *arena, strview_t path, vfsfile_t *tail, uint32 *count, uint64 *bytesize);
-vfshmap_t vfs_hmap_init(arena_t *arena, int pow2, uint32 max_values);
-void vfs_hmap_add(arena_t *arena, vfshmap_t *hmap, strview_t key, vfsdata_t value);
-vfsdata_t *vfs_hmap_get(vfshmap_t *hmap, strview_t key);
-uint64 sdbm_hash(const void *bytes, usize count);
-uint64 djb2_hash(const void *bytes, usize count);
-
-bool vfsVirtualiseDir(arena_t scratch, strview_t dirpath, strview_t outfile, vfsflags_e flags) {
- bool success = false;
-
- flags = vfs_validate_flags(flags);
-
- if (strvBack(dirpath) != '/') {
- str_t newpath = strFmt(&scratch, "%v/", dirpath);
- dirpath = strv(newpath);
- }
-
- uint32 count = 0;
- uint64 bytesize = 0;
- vfsfile_t file_head = {0};
- vfs_add_dir(&scratch, dirpath, &file_head, &count, &bytesize);
- vfsfile_t *files = file_head.next;
-
- arena_t comp_arena = {0};
-
- if (flags & VFS_FLAGS_COMPRESSED) {
- arena_t comp_arena = arenaMake(ARENA_VIRTUAL, GB(1));
-
- for_each (file, files) {
- arena_t tmp = scratch;
- buffer_t buf = fileReadWhole(&tmp, strv(file->path));
- usize maxlen = LZ4_compressBound(buf.len);
-
- uint8 *compressed = alloc(&comp_arena, uint8, maxlen);
-
- int actual_len = LZ4_compress_default(buf.data, compressed, buf.len, maxlen);
- assert(actual_len > 0 && actual_len <= maxlen);
- usize pop = maxlen - (usize)actual_len;
-
- // pop extra bytes that were allocated but not used
- arenaPop(&comp_arena, pop);
-
- file->comp_size = actual_len;
- }
- }
-
- obytestream_t header = obstrInit(&scratch);
-
- obstrAppendU32(&header, count);
- obstrAppendU8(&header, flags);
-
- uint64 offset = 0;
- for_each (file, files) {
- assert(file->path.len < 256);
- uint64 hash = djb2_hash(file->path.buf, file->path.len);
-
- obstrAppendU64(&header, hash);
- obstrAppendU8(&header, file->path.len);
- obstrPuts(&header, strv(file->path));
- obstrAppendU64(&header, offset);
- obstrAppendU64(&header, file->size);
- obstrAppendU64(&header, file->comp_size);
-
- offset += file->comp_size;
- }
-
- buffer_t headerbuf = obstrAsBuf(&header);
-
- buffer_t binbuf = {0};
-
- file_t fp = fileOpen(outfile, FILE_WRITE);
-
- if (!fileIsValid(fp)) {
- err("could not open file %v", outfile);
- goto failed;
- }
-
- uint32 header_size = headerbuf.len + 3 + sizeof(uint32); // + strlen("VFS") + sizeof(header_size)
-
- filePuts(fp, strv("VFS"));
- fileWrite(fp, &header_size, sizeof(header_size));
- fileWrite(fp, headerbuf.data, headerbuf.len);
-
- if (flags & VFS_FLAGS_COMPRESSED) {
- buffer_t compressed = {
- .data = comp_arena.start,
- .len = arenaTell(&comp_arena)
- };
- fileWrite(fp, compressed.data, compressed.len);
- }
- else {
- for_each (file, files) {
- arena_t tmp = scratch;
- buffer_t bin = fileReadWhole(&tmp, strv(file->path));
- if (flags & VFS_FLAGS_NULL_TERMINATE_FILES) {
- alloc(&tmp, uint8);
- bin.len += 1;
- }
- fileWrite(fp, bin.data, bin.len);
- }
- }
-
- fileClose(fp);
-
- success = true;
-failed:
- arenaCleanup(&comp_arena);
- arenaCleanup(&scratch);
- return success;
-}
-
-virtualfs_t *vfsReadFromFile(arena_t *arena, strview_t vfs_file, vfsflags_e flags) {
- usize pos_before = arenaTell(arena);
-
- virtualfs_t *vfs = alloc(arena, virtualfs_t);
-
- file_t fp = fileOpen(vfs_file, FILE_READ);
- if (!fileIsValid(fp)) {
- goto failed;
- }
-
- // read header
- struct {
- char magic[3];
- uint32 size;
- uint32 file_count;
- uint8 flags;
- } header = {0};
-
- fileRead(fp, &header.magic, sizeof(header.magic));
- fileRead(fp, &header.size, sizeof(header.size));
- fileRead(fp, &header.file_count, sizeof(header.file_count));
- fileRead(fp, &header.flags, sizeof(header.flags));
-
- if (memcmp(header.magic, "VFS", 3) != 0) {
- err("VirtualFS: magic characters are wrong: %.3s (0x%x%x%x)", header.magic, header.magic[0], header.magic[1], header.magic[2]);
- goto failed;
- }
-
- uint32 default_pow2 = 1 << 10;
- uint32 pow2 = bitsNextPow2(header.file_count);
- pow2 = bitsCtz(max(pow2, default_pow2));
-
- vfs->hmap = vfs_hmap_init(arena, pow2, header.file_count);
-
- for (uint32 i = 0; i < header.file_count; ++i) {
- struct {
- uint64 hash;
- char name[256];
- uint64 offset;
- uint64 size;
- uint64 comp;
- } file = {0};
-
- uint8 namelen = 0;
-
- fileRead(fp, &file.hash, sizeof(file.hash));
- fileRead(fp, &namelen, sizeof(namelen));
- fileRead(fp, &file.name, namelen);
- fileRead(fp, &file.offset, sizeof(file.offset));
- fileRead(fp, &file.size, sizeof(file.size));
- fileRead(fp, &file.comp, sizeof(file.comp));
-
- vfsdata_t data = {
- .offset = file.offset,
- .size = file.size,
- .compsize = file.comp,
- };
-
- strview_t path = strvInitLen(file.name, namelen);
-
- vfs_hmap_add(arena, &vfs->hmap, path, data);
- }
-
- vfs->flags = vfs_validate_flags(header.flags | flags);
- vfs->base_offset = header.size;
-
- if (vfs->flags & VFS_FLAGS_DONT_STREAM) {
- // get remaining size of the file
- usize pos = fileTell(fp);
- fileSeekEnd(fp);
- usize endpos = fileTell(fp);
- fileSeek(fp, pos);
- usize binsize = endpos - pos;
- // read binary data and save it to buffer for later
- buffer_t buf = {
- .data = alloc(arena, uint8, binsize),
- .len = binsize,
- };
- usize read = fileRead(fp, buf.data, buf.len);
- if (read != buf.len) {
- err("couldn't read all of the binary data, expected %zu bytes but got %zu", buf.len, read);
- goto failed;
- }
- fileClose(fp);
-
- vfs->buffer = buf;
- }
- else {
- vfs->fp = fp;
- }
-
- return vfs;
-failed:
- fileClose(fp);
- arenaRewind(arena, pos_before);
- return NULL;
-}
-
-buffer_t vfsRead(arena_t *arena, virtualfs_t *vfs, strview_t path) {
- buffer_t out = {0};
- usize pos_before = arenaTell(arena);
-
- if (!vfs) {
- goto failed;
- }
-
- vfsdata_t *data = vfs_hmap_get(&vfs->hmap, path);
- if (!data) {
- goto failed;
- }
-
- if (!vfs_read(arena, vfs, data, &out, false)) {
- goto failed;
- }
-
- return out;
-failed:
- arenaRewind(arena, pos_before);
- return (buffer_t){0};
-}
-
-str_t vfsReadStr(arena_t *arena, virtualfs_t *vfs, strview_t path) {
- buffer_t buf = {0};
- usize pos_before = arenaTell(arena);
-
- if (!vfs) {
- goto failed;
- }
-
- vfsdata_t *data = vfs_hmap_get(&vfs->hmap, path);
- if (!data) {
- goto failed;
- }
-
- if (!vfs_read(arena, vfs, data, &buf, true)) {
- goto failed;
- }
-
- return (str_t){
- .buf = buf.data,
- .len = buf.len,
- };
-failed:
- arenaRewind(arena, pos_before);
- return STR_EMPTY;
-}
-
-// == VFS FILE API ===================================
-
-virtualfs_t *g_vfs = NULL;
-
-void vfsSetGlobalVirtualFS(virtualfs_t *vfs) {
- g_vfs = vfs;
-}
-
-bool vfsFileExists(strview_t path) {
- if (!g_vfs) return false;
- return vfs_hmap_get(&g_vfs->hmap, path) != NULL;
-}
-
-vfs_file_t vfsFileOpen(strview_t name, int mode) {
- if (!g_vfs) goto failed;
- if (mode != FILE_READ) {
- err("VirtualFS: trying to open file (%v) for write, VirtualFS is read only!", name);
- goto failed;
- }
-
- vfsdata_t *data = vfs_hmap_get(&g_vfs->hmap, name);
-
- return (vfs_file_t){
- .handle = (uintptr_t)data,
- };
-
-failed:
- return (vfs_file_t){0};
-}
-
-void vfsFileClose(vfs_file_t ctx) {
- (void)ctx;
-}
-
-bool vfsFileIsValid(vfs_file_t ctx) {
- return g_vfs && ctx.handle != 0;
-}
-
-usize vfsFileSize(vfs_file_t ctx) {
- if (!vfsFileIsValid(ctx)) return 0;
- vfsdata_t *data = (vfsdata_t *)ctx.handle;
- return data->size;
-}
-
-buffer_t vfsFileReadWhole(arena_t *arena, strview_t name) {
- return vfsRead(arena, g_vfs, name);
-}
-
-buffer_t vfsFileReadWholeFP(arena_t *arena, vfs_file_t ctx) {
- if (!vfsFileIsValid(ctx)) return (buffer_t){0};
- vfsdata_t *data = (vfsdata_t *)ctx.handle;
- buffer_t out = {0};
- usize pos_before = arenaTell(arena);
- if (!vfs_read(arena, g_vfs, data, &out, false)) {
- arenaRewind(arena, pos_before);
- return (buffer_t){0};
- }
- return out;
-}
-
-str_t vfsFileReadWholeStr(arena_t *arena, strview_t name) {
- return vfsReadStr(arena, g_vfs, name);
-}
-
-str_t vfsFileReadWholeStrFP(arena_t *arena, vfs_file_t ctx) {
- if (!vfsFileIsValid(ctx)) return STR_EMPTY;
- vfsdata_t *data = (vfsdata_t *)ctx.handle;
- buffer_t buf = {0};
- usize pos_before = arenaTell(arena);
- if (!vfs_read(arena, g_vfs, data, &buf, true)) {
- arenaRewind(arena, pos_before);
- return STR_EMPTY;
- }
- return (str_t){
- .buf = buf.data,
- .len = buf.len,
- };
-}
-
-// == PRIVATE FUNCTIONS ==============================
-
-vfsflags_e vfs_validate_flags(vfsflags_e flags) {
- if (flags & VFS_FLAGS_COMPRESSED && flags & VFS_FLAGS_NULL_TERMINATE_FILES) {
- warn("VirtualFS: both COMPRESSEd and NULL_TERMINATE_FILES flags are set to ON, but they are mutually exclusive. turning NULL_TERMINATE_FILES off");
- flags &= ~VFS_FLAGS_NULL_TERMINATE_FILES;
- }
-
- return flags;
-}
-
-bool vfs_read(arena_t *arena, virtualfs_t *vfs, vfsdata_t *data, buffer_t *out, bool null_terminate) {
- if (!vfs || !data || !out) {
- return false;
- }
-
- bool is_allocated = true;
- out->len = data->size;
-
- if (vfs->flags & VFS_FLAGS_COMPRESSED) {
- out->data = alloc(arena, uint8, out->len);
-
- uint8 *compressed = NULL;
-
- if (vfs->flags & VFS_FLAGS_DONT_STREAM) {
- assert((data->offset + data->compsize) < vfs->buffer.len);
- compressed = vfs->buffer.data + data->offset;
- }
- else {
- uint64 offset = vfs->base_offset + data->offset;
- fileSeek(vfs->fp, offset);
-
- arena_t scratch = *arena;
- uint8 *compressed = alloc(&scratch, uint8, data->compsize);
- usize read = fileRead(vfs->fp, compressed, data->compsize);
- if (read != data->compsize) {
- err("VirtualFS: read %zu bytes, but should have read %zu", read, data->compsize);
- return false;
- }
- }
-
- int decompsize = LZ4_decompress_safe(compressed, out->data, data->compsize, out->len);
- if (decompsize < 0) {
- err("VirtualFS: couldn't decompress buffer: %d", decompsize);
- return false;
- }
- }
- else {
- if (vfs->flags & VFS_FLAGS_DONT_STREAM) {
- assert((data->offset + data->size) < vfs->buffer.len);
- out->data = vfs->buffer.data + data->offset;
- is_allocated = false;
- }
- else {
- out->data = alloc(arena, uint8, data->size);
-
- uint64 offset = vfs->base_offset + data->offset;
- fileSeek(vfs->fp, offset);
- usize read = fileRead(vfs->fp, out->data, out->len);
- if (read != out->len) {
- err("VirtualFS: read %zu bytes, but should have read %zu", read, out->len);
- return false;
- }
- }
- }
-
- if (null_terminate && !(vfs->flags & VFS_FLAGS_NULL_TERMINATE_FILES)) {
- if (is_allocated) {
- alloc(arena, char);
- }
- else {
- uint8 *buf = alloc(arena, uint8, out->len + 1);
- memcpy(buf, out->data, out->len);
- out->data = buf;
- }
- out->len += 1;
- }
-
- return true;
-}
-
-vfsfile_t *vfs_add_dir(arena_t *arena, strview_t path, vfsfile_t *tail, uint32 *count, uint64 *bytesize) {
- uint8 tmpbuf[KB(1)];
-
- dir_t *dir = dirOpen(arena, path);
- dir_entry_t *entry = NULL;
-
- if (strvEquals(path, strv("./"))) {
- path = STRV_EMPTY;
- }
-
- vfsfile_t *head = tail;
- vfsfile_t *cur = tail;
-
- while ((entry = dirNext(arena, dir))) {
- arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
-
- vfsfile_t *newfile = NULL;
-
- if (entry->type == DIRTYPE_DIR) {
- if (strvEquals(strv(entry->name), strv(".")) ||
- strvEquals(strv(entry->name), strv(".."))
- ) {
- continue;
- }
- str_t fullpath = strFmt(&scratch, "%v%v/", path, entry->name);
- newfile = vfs_add_dir(arena, strv(fullpath), cur, count, bytesize);
- }
- else {
- newfile = alloc(arena, vfsfile_t);
- newfile->path = strFmt(arena, "%v%v", path, entry->name);
- newfile->size = entry->filesize;
- newfile->comp_size = newfile->size;
-
- if (cur) cur->next = newfile;
- (*count)++;
- (*bytesize) += newfile->size;
- }
-
- if (!head) head = newfile;
- cur = newfile;
- }
-
- return cur;
-}
-
-
-// == HASH MAP =======================================
-
-vfshmap_t vfs_hmap_init(arena_t *arena, int pow2, uint32 max_values) {
- uint size = 1 << pow2;
- return (vfshmap_t) {
- .size = size,
- .max_values = max_values,
- .buckets = alloc(arena, vfshnode_t*, size),
- .values = alloc(arena, vfsdata_t, max_values),
- };
-}
-
-void vfs_hmap_add(arena_t *arena, vfshmap_t *hmap, strview_t key, vfsdata_t value) {
- if (!hmap) return;
-
- if ((float)hmap->count >= (float)hmap->size * 0.6f) {
- warn("more than 60%% of the hashmap is being used: %d/%d", hmap->count, hmap->size);
- }
-
- uint64 hash = djb2_hash(key.buf, key.len);
- usize index = hash & (hmap->size - 1);
- vfshnode_t *bucket = hmap->buckets[index];
- if (bucket) hmap->collisions++;
- while (bucket) {
- // already exists
- if (bucket->hash == hash && strvEquals(strv(bucket->key), key)) {
- hmap->values[bucket->index] = value;
- return;
- }
- bucket = bucket->next;
- }
-
- assert(hmap->count < hmap->max_values);
-
- bucket = alloc(arena, vfshnode_t);
-
- bucket->hash = hash;
- bucket->key = str(arena, key);
- bucket->index = hmap->count;
- bucket->next = hmap->buckets[index];
-
- hmap->values[hmap->count++] = value;
- hmap->buckets[index] = bucket;
-}
-
-vfsdata_t *vfs_hmap_get(vfshmap_t *hmap, strview_t key) {
- if (!hmap || hmap->count == 0) {
- return NULL;
- }
-
- uint64 hash = djb2_hash(key.buf, key.len);
- usize index = hash & (hmap->size - 1);
- vfshnode_t *bucket = hmap->buckets[index];
- while (bucket) {
- if (bucket->hash == hash && strvEquals(strv(bucket->key), key)) {
- return &hmap->values[bucket->index];
- }
- bucket = bucket->next;
- }
-
- return NULL;
-}
-
-uint64 sdbm_hash(const void *bytes, usize count) {
- const uint8 *data = bytes;
- uint64 hash = 0;
-
- for (usize i = 0; i < count; ++i) {
- hash = data[i] + (hash << 6) + (hash << 16) - hash;
- }
-
- return hash;
-}
-
-uint64 djb2_hash(const void *bytes, usize count) {
- const uint8 *data = bytes;
- uint64 hash = 5381;
- int c;
-
- for (usize i = 0; i < count; ++i) {
- hash = ((hash << 5) + hash) + data[i];
- }
-
- return hash;
-}
-
-uint64 java_hash(const void *bytes, usize count) {
- const uint8 *data = bytes;
- uint64 hash = 1125899906842597L;
-
- for (usize i = 0; i < count; ++i) {
- hash = ((hash << 5) - hash) + data[i];
- }
-
- return hash;
-}
diff --git a/vfs.h b/vfs.h
deleted file mode 100644
index b09aa60..0000000
--- a/vfs.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include "str.h"
-#include "arena.h"
-
-typedef enum {
- VFS_FLAGS_NONE = 0,
- VFS_FLAGS_COMPRESSED = 1 << 0,
- VFS_FLAGS_NULL_TERMINATE_FILES = 1 << 1, // only valid when compress is false
- VFS_FLAGS_DONT_STREAM = 1 << 2,
-} vfsflags_e;
-
-typedef struct virtualfs_t virtualfs_t;
-
-bool vfsVirtualiseDir(arena_t scratch, strview_t dirpath, strview_t outfile, vfsflags_e flags);
-virtualfs_t *vfsReadFromFile(arena_t *arena, strview_t vfs_file, vfsflags_e flags);
-buffer_t vfsRead(arena_t *arena, virtualfs_t *vfs, strview_t path);
-str_t vfsReadStr(arena_t *arena, virtualfs_t *vfs, strview_t path);
-
-// vfs replacement for the file api
-
-typedef struct {
- uintptr_t handle;
-} vfs_file_t;
-
-void vfsSetGlobalVirtualFS(virtualfs_t *vfs);
-
-bool vfsFileExists(strview_t path);
-vfs_file_t vfsFileOpen(strview_t name, int mode);
-void vfsFileClose(vfs_file_t ctx);
-bool vfsFileIsValid(vfs_file_t ctx);
-usize vfsFileSize(vfs_file_t ctx);
-buffer_t vfsFileReadWhole(arena_t *arena, strview_t name);
-buffer_t vfsFileReadWholeFP(arena_t *arena, vfs_file_t ctx);
-str_t vfsFileReadWholeStr(arena_t *arena, strview_t name);
-str_t vfsFileReadWholeStrFP(arena_t *arena, vfs_file_t ctx);
\ No newline at end of file
diff --git a/vfs_file.h b/vfs_file.h
deleted file mode 100644
index 8f63c84..0000000
--- a/vfs_file.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "vfs.h"
-#include "tracelog.h"
-
-#define fileExists vfsFileExists
-#define fileOpen vfsFileOpen
-#define fileClose vfsFileClose
-#define fileIsValid vfsFileIsValid
-#define fileSize vfsFileSize
-#define fileReadWhole vfsFileReadWhole
-#define fileReadWholeFP vfsFileReadWholeFP
-#define fileReadWholeStr vfsFileReadWholeStr
-#define fileReadWholeStrFP vfsFileReadWholeStrFP
-
-#define VFS_FAIL_READONLY(fn) err("VirtualFS: trying to call function " #fn "which requires writing to files. VirtualFS is read only!")
-#define VFS_FAIL_STATELESS(fn) err("VirtualFS: trying to call function " #fn "which requires state. VirtualFS only works on stateless file's commands!")
-
-#define fileDelete(...) VFS_FAIL_READONLY(fileDelete)
-#define filePutc(...) VFS_FAIL_READONLY(filePutc)
-#define filePuts(...) VFS_FAIL_READONLY(filePuts)
-#define filePrintf(...) VFS_FAIL_READONLY(filePrintf)
-#define filePrintfv(...) VFS_FAIL_READONLY(filePrintfv)
-#define fileWrite(...) VFS_FAIL_READONLY(fileWrite)
-#define fileWriteWhole(...) VFS_FAIL_READONLY(fileWriteWhole)
-#define fileGetTime(...) VFS_FAIL_READONLY(fileGetTime)
-#define fileGetTimeFP(...) VFS_FAIL_READONLY(fileGetTimeFP)
-#define fileHasChanged(...) VFS_FAIL_READONLY(fileHasChanged)
-
-#define fileTell(...) VFS_FAIL_STATELESS(fileTell)
-#define fileRead(...) VFS_FAIL_STATELESS(fileRead)
-#define fileSeek(...) VFS_FAIL_STATELESS(fileSeek)
-#define fileSeekEnd(...) VFS_FAIL_STATELESS(fileSeekEnd)
-#define fileRewind(...) VFS_FAIL_STATELESS(fileRewind)
\ No newline at end of file
diff --git a/vmem.c b/vmem.c
deleted file mode 100644
index b7cfd9c..0000000
--- a/vmem.c
+++ /dev/null
@@ -1,133 +0,0 @@
-#include "vmem.h"
-
-#include
-
-#include "tracelog.h"
-
-static usize vmem__page_size = 0;
-static void vmem__update_page_size(void);
-
-// platform generic functions
-
-usize vmemGetPageSize(void) {
- if (!vmem__page_size) {
- vmem__update_page_size();
- }
- return vmem__page_size;
-}
-
-usize vmemPadToPage(usize byte_count) {
- if (!vmem__page_size) {
- vmem__update_page_size();
- }
-
- if (byte_count == 0) {
- return vmem__page_size;
- }
-
- // bit twiddiling, vmem__page_size MUST be a power of 2
- usize padding = vmem__page_size - (byte_count & (vmem__page_size - 1));
- if (padding == vmem__page_size) {
- padding = 0;
- }
- return byte_count + padding;
-}
-
-#if COLLA_WIN
-
-#include
-
-void *vmemInit(usize size, usize *out_padded_size) {
- usize alloc_size = vmemPadToPage(size);
-
- void *ptr = VirtualAlloc(NULL, alloc_size, MEM_RESERVE, PAGE_NOACCESS);
-
- if (out_padded_size) {
- *out_padded_size = alloc_size;
- }
-
- return ptr;
-}
-
-bool vmemRelease(void *base_ptr) {
- return VirtualFree(base_ptr, 0, MEM_RELEASE);
-}
-
-bool vmemCommit(void *ptr, usize num_of_pages) {
- usize page_size = vmemGetPageSize();
-
- void *new_ptr = VirtualAlloc(ptr, num_of_pages * page_size, MEM_COMMIT, PAGE_READWRITE);
-
- if (!new_ptr) {
- debug("ERROR: failed to commit memory: %lu\n", GetLastError());
- }
-
- return new_ptr != NULL;
-}
-
-static void vmem__update_page_size(void) {
- SYSTEM_INFO info = {0};
- GetSystemInfo(&info);
- vmem__page_size = info.dwPageSize;
-}
-
-// #elif COLLA_LIN
-#else
-
-#include
-#include
-#include
-#include
-
-typedef struct {
- usize len;
-} vmem__header;
-
-void *vmemInit(usize size, usize *out_padded_size) {
- size += sizeof(vmem__header);
- usize alloc_size = vmemPadToPage(size);
-
- vmem__header *header = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-
- if (header == MAP_FAILED) {
- fatal("could not map %zu memory: %s", size, strerror(errno));
- }
-
- if (out_padded_size) {
- *out_padded_size = alloc_size;
- }
-
- header->len = alloc_size;
-
- return header + 1;
-}
-
-bool vmemRelease(void *base_ptr) {
- if (!base_ptr) return false;
- vmem__header *header = (vmem__header *)base_ptr - 1;
-
- int res = munmap(header, header->len);
- if (res == -1) {
- err("munmap failed: %s", strerror(errno));
- }
- return res != -1;
-}
-
-bool vmemCommit(void *ptr, usize num_of_pages) {
- // mmap doesn't need a commit step
- (void)ptr;
- (void)num_of_pages;
- return true;
-}
-
-static void vmem__update_page_size(void) {
- long lin_page_size = sysconf(_SC_PAGE_SIZE);
-
- if (lin_page_size < 0) {
- fatal("could not get page size: %s", strerror(errno));
- }
-
- vmem__page_size = (usize)lin_page_size;
-}
-
-#endif
\ No newline at end of file
diff --git a/vmem.h b/vmem.h
deleted file mode 100644
index 39c81ed..0000000
--- a/vmem.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef VIRTUAL_MEMORY_HEADER
-#define VIRTUAL_MEMORY_HEADER
-
-#include "collatypes.h"
-
-void *vmemInit(usize size, usize *out_padded_size);
-bool vmemRelease(void *base_ptr);
-bool vmemCommit(void *ptr, usize num_of_pages);
-usize vmemGetPageSize(void);
-usize vmemPadToPage(usize byte_count);
-
-#endif // VIRTUAL_MEMORY_HEADER
\ No newline at end of file
diff --git a/warnings/colla_warn_beg.h b/warnings/colla_warn_beg.h
deleted file mode 100644
index 4747736..0000000
--- a/warnings/colla_warn_beg.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#if COLLA_CLANG
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Winitializer-overrides"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#endif
\ No newline at end of file
diff --git a/warnings/colla_warn_end.h b/warnings/colla_warn_end.h
deleted file mode 100644
index e45a222..0000000
--- a/warnings/colla_warn_end.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#if COLLA_CLANG
-
-#pragma clang diagnostic pop
-
-#endif
\ No newline at end of file
diff --git a/websocket.c b/websocket.c
deleted file mode 100644
index f370a57..0000000
--- a/websocket.c
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "websocket.h"
-
-#include "arena.h"
-#include "str.h"
-#include "server.h"
-#include "socket.h"
-#include "base64.h"
-#include "strstream.h"
-
-#include "sha1.h"
-
-#if !COLLA_MSVC
-extern uint16_t ntohs(uint16_t hostshort);
-extern uint16_t htons(uint16_t hostshort);
-extern uint32_t htonl(uint32_t hostlong);
-
-uint64_t ntohll(uint64_t input) {
- uint64_t rval = 0;
- uint8_t *data = (uint8_t *)&rval;
-
- data[0] = input >> 56;
- data[1] = input >> 48;
- data[2] = input >> 40;
- data[3] = input >> 32;
- data[4] = input >> 24;
- data[5] = input >> 16;
- data[6] = input >> 8;
- data[7] = input >> 0;
-
- return rval;
-}
-
-uint64_t htonll(uint64_t input) {
- return ntohll(input);
-}
-
-#endif
-
-bool wsInitialiseSocket(arena_t scratch, socket_t websocket, strview_t key) {
- str_t full_key = strFmt(&scratch, "%v" WEBSOCKET_MAGIC, key, WEBSOCKET_MAGIC);
-
- sha1_t sha1_ctx = sha1_init();
- sha1_result_t sha1_data = sha1(&sha1_ctx, full_key.buf, full_key.len);
-
- // convert to big endian for network communication
- for (int i = 0; i < 5; ++i) {
- sha1_data.digest[i] = htonl(sha1_data.digest[i]);
- }
-
- buffer_t encoded_key = base64Encode(&scratch, (buffer_t){ (uint8 *)sha1_data.digest, sizeof(sha1_data.digest) });
-
- str_t response = strFmt(
- &scratch,
-
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Sec-WebSocket-Accept: %v\r\n"
- "\r\n",
- encoded_key
- );
-
- bool success = skSend(websocket, response.buf, response.len);
- return success;
-}
-
-buffer_t wsEncodeMessage(arena_t *arena, strview_t message) {
- int extra = 6;
- if (message.len > UINT16_MAX) extra += sizeof(uint64);
- else if (message.len > UINT8_MAX) extra += sizeof(uint16);
- uint8 *bytes = alloc(arena, uint8, message.len + extra);
- bytes[0] = 0b10000001;
- bytes[1] = 0b10000000;
- int offset = 2;
- if (message.len > UINT16_MAX) {
- bytes[1] |= 0b01111111;
- uint64 len = htonll(message.len);
- memcpy(bytes + 2, &len, sizeof(len));
- offset += sizeof(uint64);
- }
- else if (message.len > UINT8_MAX) {
- bytes[1] |= 0b01111110;
- uint16 len = htons((uint16)message.len);
- memcpy(bytes + 2, &len, sizeof(len));
- offset += sizeof(uint16);
- }
- else {
- bytes[1] |= (uint8)message.len;
- }
-
- uint32 mask = 0;
- memcpy(bytes + offset, &mask, sizeof(mask));
- offset += sizeof(mask);
- memcpy(bytes + offset, message.buf, message.len);
-
- return (buffer_t){ bytes, message.len + extra };
-}
-
-str_t wsDecodeMessage(arena_t *arena, buffer_t message) {
- str_t out = STR_EMPTY;
- uint8 *bytes = message.data;
-
- bool mask = bytes[1] & 0b10000000;
- int offset = 2;
- uint64 msglen = bytes[1] & 0b01111111;
-
- // 16bit msg len
- if (msglen == 126) {
- uint64 be_len = 0;
- memcpy(&be_len, bytes + 2, sizeof(be_len));
- msglen = ntohs(be_len);
- offset += sizeof(uint16);
- }
- // 64bit msg len
- else if (msglen == 127) {
- uint64 be_len = 0;
- memcpy(&be_len, bytes + 2, sizeof(be_len));
- msglen = ntohll(be_len);
- offset += sizeof(uint64);
- }
-
- if (msglen == 0) {
- warn("message length = 0");
- }
- else if (mask) {
- uint8 *decoded = alloc(arena, uint8, msglen + 1);
- uint8 masks[4] = {0};
- memcpy(masks, bytes + offset, sizeof(masks));
- offset += 4;
-
- for (uint64 i = 0; i < msglen; ++i) {
- decoded[i] = bytes[offset + i] ^ masks[i % 4];
- }
-
- out = (str_t){ (char *)decoded, msglen };
- }
- else {
- warn("mask bit not set!");
- }
-
- return out;
-}
diff --git a/websocket.h b/websocket.h
deleted file mode 100644
index 3758e5f..0000000
--- a/websocket.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include "arena.h"
-#include "str.h"
-
-#define WEBSOCKET_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-#define WEBSOCKET_HTTP_KEY "Sec-WebSocket-Key"
-
-typedef uintptr_t socket_t;
-
-bool wsInitialiseSocket(arena_t scratch, socket_t websocket, strview_t key);
-buffer_t wsEncodeMessage(arena_t *arena, strview_t message);
-str_t wsDecodeMessage(arena_t *arena, buffer_t message);
\ No newline at end of file
diff --git a/win/net_win32.c b/win/net_win32.c
new file mode 100644
index 0000000..f209ba9
--- /dev/null
+++ b/win/net_win32.c
@@ -0,0 +1,337 @@
+#include "../net.h"
+#include "../os.h"
+
+#include
+
+#if !COLLA_TCC
+ #include
+ #include
+ #include
+#else
+ #include "../tcc/colla_tcc.h"
+#endif
+
+#if COLLA_CMT_LIB
+ #pragma comment(lib, "Wininet")
+ #pragma comment(lib, "Ws2_32")
+#endif
+
+struct {
+ HINTERNET internet;
+} http_win = {0};
+
+void net_init(void) {
+ if (http_win.internet) return;
+ http_win.internet = InternetOpen(
+ TEXT("COLLA_WININET"),
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL,
+ NULL,
+ 0
+ );
+
+ WSADATA wsdata = {0};
+ if (WSAStartup(0x0202, &wsdata)) {
+ fatal("couldn't startup sockets: %v", os_get_error_string(WSAGetLastError()));
+ }
+}
+
+void net_cleanup(void) {
+ if (!http_win.internet) return;
+ InternetCloseHandle(http_win.internet);
+ http_win.internet = NULL;
+ WSACleanup();
+}
+
+iptr net_get_last_error(void) {
+ return WSAGetLastError();
+}
+
+http_res_t http_request(http_request_desc_t *req) {
+ HINTERNET connection = NULL;
+ HINTERNET request = NULL;
+ BOOL result = FALSE;
+ bool success = false;
+ http_res_t res = {0};
+ arena_t arena_before = *req->arena;
+
+ if (!http_win.internet) {
+ err("net_init has not been called");
+ goto failed;
+ }
+
+ http_url_t split = http_split_url(req->url);
+ strview_t server = split.host;
+ strview_t page = split.uri;
+
+ if (strv_starts_with_view(server, strv("http://"))) {
+ server = strv_remove_prefix(server, 7);
+ }
+
+ if (strv_starts_with_view(server, strv("https://"))) {
+ server = strv_remove_prefix(server, 8);
+ }
+
+ {
+ arena_t scratch = *req->arena;
+
+ if (req->version.major == 0) req->version.major = 1;
+ if (req->version.minor == 0) req->version.minor = 1;
+
+ const TCHAR *accepted_types[] = { TEXT("*/*"), NULL };
+ const char *method = http_get_method_string(req->request_type);
+ str_t http_ver = str_fmt(&scratch, "HTTP/%u.%u", req->version.major, req->version.minor);
+
+ tstr_t tserver = strv_to_tstr(&scratch, server);
+ tstr_t tpage = strv_to_tstr(&scratch, page);
+ tstr_t tmethod = strv_to_tstr(&scratch, strv(method));
+ tstr_t thttp_ver = strv_to_tstr(&scratch, strv(http_ver));
+
+ connection = InternetConnect(
+ http_win.internet,
+ tserver.buf,
+ INTERNET_DEFAULT_HTTPS_PORT,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ 0,
+ (DWORD_PTR)NULL // userdata
+ );
+ if (!connection) {
+ err("call to InternetConnect failed: %u", os_get_error_string(os_get_last_error()));
+ goto failed;
+ }
+
+ request = HttpOpenRequest(
+ connection,
+ tmethod.buf,
+ tpage.buf,
+ thttp_ver.buf,
+ NULL,
+ accepted_types,
+ INTERNET_FLAG_SECURE,
+ (DWORD_PTR)NULL // userdata
+ );
+ if (!request) {
+ err("call to HttpOpenRequest failed: %v", os_get_error_string(os_get_last_error()));
+ goto failed;
+ }
+ }
+
+ for (int i = 0; i < req->header_count; ++i) {
+ http_header_t *h = &req->headers[i];
+ arena_t scratch = *req->arena;
+
+ str_t header = str_fmt(&scratch, "%v: %v\r\n", h->key, h->value);
+ tstr_t theader = strv_to_tstr(&scratch, strv(header));
+ HttpAddRequestHeaders(request, theader.buf, (DWORD)theader.len, 0);
+ }
+
+ result = HttpSendRequest(
+ request,
+ NULL,
+ 0,
+ (void *)req->body.buf,
+ (DWORD)req->body.len
+ );
+ if (!result) {
+ err("call to HttpSendRequest failed: %v", os_get_error_string(os_get_last_error()));
+ goto failed;
+ }
+
+ u8 smallbuf[KB(5)];
+ DWORD bufsize = sizeof(smallbuf);
+
+ u8 *buffer = smallbuf;
+
+ // try and read it into a static buffer
+ result = HttpQueryInfo(request, HTTP_QUERY_RAW_HEADERS_CRLF, smallbuf, &bufsize, NULL);
+
+ // 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);
+ }
+
+ if (!result) {
+ err("couldn't get headers");
+ goto failed;
+ }
+
+ tstr_t theaders = { (TCHAR *)buffer, bufsize };
+ str_t headers = str_from_tstr(req->arena, theaders);
+
+ res.headers = http_parse_headers(req->arena, strv(headers));
+ res.version = req->version;
+
+ DWORD status_code = 0;
+ DWORD status_code_len = sizeof(status_code);
+ result = HttpQueryInfo(request, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &status_code, &status_code_len, 0);
+ if (!result) {
+ err("couldn't get status code");
+ goto failed;
+ }
+
+ res.status_code = status_code;
+
+ outstream_t body = ostr_init(req->arena);
+
+ while (true) {
+ DWORD read = 0;
+ char read_buffer[4096];
+ BOOL read_success = InternetReadFile(
+ request, read_buffer, sizeof(read_buffer), &read
+ );
+ if (!read_success || read == 0) {
+ break;
+ }
+ ostr_puts(&body, strv(read_buffer, read));
+ }
+
+ res.body = strv(ostr_to_str(&body));
+
+ success = true;
+
+failed:
+ if (request) InternetCloseHandle(request);
+ if (connection) InternetCloseHandle(connection);
+ if (!success) *req->arena = arena_before;
+ return res;
+}
+
+// SOCKETS //////////////////////////
+
+SOCKADDR_IN sk__addrin_in(const char *ip, u16 port) {
+ 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 (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 INVALID_SOCKET;
+ }
+ return socket(AF_INET, SOCK_STREAM, proto->p_proto);
+}
+
+bool sk_is_valid(socket_t sock) {
+ return sock != INVALID_SOCKET;
+}
+
+bool sk_close(socket_t sock) {
+ return closesocket(sock) != SOCKET_ERROR;
+}
+
+bool sk_bind(socket_t sock, const char *ip, u16 port) {
+ SOCKADDR_IN sk_addr = sk__addrin_in(ip, port);
+ if (sk_addr.sin_family == 0) {
+ return false;
+ }
+ return bind(sock, (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) {
+ SOCKADDR_IN addr = {0};
+ int addr_size = sizeof(addr);
+ return accept(sock, (SOCKADDR*)&addr, &addr_size);
+}
+
+bool sk_connect(socket_t sock, const char *server, u16 server_port) {
+ u8 tmpbuf[1024] = {0};
+ arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
+
+ str16_t wserver = strv_to_str16(&scratch, strv(server));
+
+ ADDRINFOW *addrinfo = NULL;
+ int result = GetAddrInfoW(wserver.buf, NULL, NULL, &addrinfo);
+ if (result) {
+ return false;
+ }
+
+ char ip_str[1024] = {0};
+ inet_ntop(addrinfo->ai_family, addrinfo->ai_addr, ip_str, sizeof(ip_str));
+
+ SOCKADDR_IN sk_addr = sk__addrin_in(ip_str, server_port);
+ if (sk_addr.sin_family == 0) {
+ return false;
+ }
+
+ return connect(sock, (SOCKADDR*)&sk_addr, sizeof(sk_addr)) != SOCKET_ERROR;
+}
+
+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) {
+ return WSAPoll((WSAPOLLFD*)to_poll, (ULONG)num_to_poll, timeout);
+}
+
+oshandle_t sk_bind_event(socket_t sock, skevent_e event) {
+ if (event == SOCK_EVENT_NONE) {
+ return os_handle_zero();
+ }
+
+ HANDLE handle = WSACreateEvent();
+ if (handle == WSA_INVALID_EVENT) {
+ return os_handle_zero();
+ }
+
+ int wsa_event = 0;
+ if (event & SOCK_EVENT_READ) wsa_event |= FD_READ;
+ if (event & SOCK_EVENT_WRITE) wsa_event |= FD_WRITE;
+ if (event & SOCK_EVENT_ACCEPT) wsa_event |= FD_ACCEPT;
+ if (event & SOCK_EVENT_CONNECT) wsa_event |= FD_CONNECT;
+ if (event & SOCK_EVENT_CLOSE) wsa_event |= FD_CLOSE;
+
+ if (WSAEventSelect(sock, handle, wsa_event) != 0) {
+ WSACloseEvent(handle);
+ return os_handle_zero();
+ }
+
+ return (oshandle_t){ .data = (uptr)handle };
+}
+
+void sk_reset_event(oshandle_t handle) {
+ if (!os_handle_valid(handle)) {
+ warn("invalid handle");
+ return;
+ }
+ WSAResetEvent((HANDLE)handle.data);
+}
+
+void sk_destroy_event(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return;
+ WSACloseEvent((HANDLE)handle.data);
+}
+
diff --git a/win/os_win32.c b/win/os_win32.c
new file mode 100644
index 0000000..9f17762
--- /dev/null
+++ b/win/os_win32.c
@@ -0,0 +1,693 @@
+#include
+#include
+
+#include "../os.h"
+#include "../net.h"
+
+#if COLLA_TCC
+#include "../tcc/colla_tcc.h"
+#endif
+
+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 {
+ HANDLE handle;
+ thread_func_t *func;
+ void *userdata;
+ DWORD id;
+ } thread;
+ CRITICAL_SECTION mutex;
+ CONDITION_VARIABLE cv;
+ };
+};
+
+struct {
+ arena_t arena;
+ os_system_info_t info;
+ os_entity_t *entity_free;
+ oshandle_t hstdout;
+ oshandle_t hstdin;
+} w32_data = {0};
+
+os_entity_t *os__win_alloc_entity(os_entity_kind_e kind) {
+ os_entity_t *entity = w32_data.entity_free;
+ if (entity) {
+ list_pop(w32_data.entity_free);
+ }
+ else {
+ entity = alloc(&w32_data.arena, os_entity_t);
+ }
+ entity->kind = kind;
+ return entity;
+}
+
+void os__win_free_entity(os_entity_t *entity) {
+ entity->kind = OS_KIND_NULL;
+ list_push(w32_data.entity_free, entity);
+}
+
+void os_init(void) {
+ SetConsoleOutputCP(CP_UTF8);
+
+ SYSTEM_INFO sysinfo = {0};
+ GetSystemInfo(&sysinfo);
+
+ os_system_info_t *info = &w32_data.info;
+ info->processor_count = (u64)sysinfo.dwNumberOfProcessors;
+ info->page_size = sysinfo.dwPageSize;
+
+ w32_data.arena = arena_make(ARENA_VIRTUAL, OS_ARENA_SIZE);
+
+ TCHAR namebuf[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD namebuflen = sizeof(namebuf);
+ BOOL result = GetComputerName(namebuf, &namebuflen);
+ if (!result) {
+ err("failed to get computer name: %v", os_get_error_string(os_get_last_error()));
+ }
+
+ info->machine_name = str_from_tstr(&w32_data.arena, (tstr_t){ namebuf, namebuflen});
+
+ HANDLE hstdout = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ HANDLE hstdin = CreateFile(TEXT("CONIN$"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+
+ if (hstdout == INVALID_HANDLE_VALUE) err("couldn't open CONOUT$");
+ else w32_data.hstdout.data = (uptr)hstdout;
+
+ if (hstdin == INVALID_HANDLE_VALUE) err("couldn't open CONIN$");
+ else w32_data.hstdin.data = (uptr)hstdin;
+}
+
+void os_cleanup(void) {
+ os_file_close(w32_data.hstdout);
+ os_file_close(w32_data.hstdin);
+
+ arena_cleanup(&w32_data.arena);
+}
+
+void os_abort(int code) {
+ ExitProcess(code);
+}
+
+iptr os_get_last_error(void) {
+ return (iptr)GetLastError();
+}
+
+str_t os_get_error_string(iptr error) {
+ static u8 tmpbuf[1024] = {0};
+ arena_t arena = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
+ DWORD code = LOWORD(error);
+
+ WCHAR msgbuf[512];
+ DWORD chars;
+ chars = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, 0, msgbuf, arrlen(msgbuf), NULL);
+ if (chars == 0) {
+ err("FormatMessageW: %ld", os_get_last_error());
+ return STR_EMPTY;
+ }
+
+ debug("error: %zi 0x%zX", error, error);
+
+ // remove \r\n at the end
+ return str_from_str16(&arena, str16_init(msgbuf, chars - 2));
+}
+
+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};
+ assert(count < MAXIMUM_WAIT_OBJECTS);
+
+ for (int i = 0; i < count; ++i) {
+ win_handles[i] = (HANDLE)(handles[i].data);
+ }
+
+ DWORD result = WaitForMultipleObjects(count, win_handles, wait_all, milliseconds);
+
+ os_wait_t out = {0};
+
+ if (result == WAIT_FAILED) {
+ out.result = OS_WAIT_FAILED;
+ }
+ else if (result == WAIT_TIMEOUT) {
+ out.result = OS_WAIT_TIMEOUT;
+ }
+ else if (result >= WAIT_ABANDONED_0) {
+ out.result = OS_WAIT_ABANDONED;
+ out.index = result - WAIT_ABANDONED_0;
+ }
+ else {
+ out.result = OS_WAIT_FINISHED;
+ out.index = result - WAIT_OBJECT_0;
+ }
+
+ return out;
+}
+
+os_system_info_t os_get_system_info(void) {
+ return w32_data.info;
+}
+
+void os_log_set_colour(os_log_colour_e colour) {
+ WORD attribute = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ switch (colour) {
+ case LOG_COL_BLACK: attribute = 0; break;
+ case LOG_COL_RED: attribute = FOREGROUND_RED; break;
+ case LOG_COL_GREEN: attribute = FOREGROUND_GREEN; break;
+ case LOG_COL_BLUE: attribute = FOREGROUND_BLUE; break;
+ case LOG_COL_MAGENTA: attribute = FOREGROUND_RED | FOREGROUND_BLUE; break;
+ case LOG_COL_YELLOW: attribute = FOREGROUND_RED | FOREGROUND_GREEN; break;
+ case LOG_COL_CYAN: attribute = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ default: break;
+ }
+
+ HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleTextAttribute(hc, attribute | FOREGROUND_INTENSITY);
+}
+
+oshandle_t os_stdout(void) {
+ return w32_data.hstdout;
+}
+
+oshandle_t os_stdin(void) {
+ return w32_data.hstdin;
+}
+
+// == FILE ======================================
+
+#define OS_SMALL_SCRATCH() \
+u8 tmpbuf[KB(1)]; \
+arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf)
+
+DWORD os__win_mode_to_access(filemode_e mode) {
+ DWORD out = 0;
+ 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 == FILEMODE_READ) return OPEN_EXISTING;
+ if (mode == FILEMODE_WRITE) return CREATE_ALWAYS;
+ return OPEN_ALWAYS;
+}
+
+bool os_file_exists(strview_t path) {
+ OS_SMALL_SCRATCH();
+ tstr_t name = strv_to_tstr(&scratch, path);
+ DWORD attributes = GetFileAttributes(name.buf);
+ return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+tstr_t os_file_fullpath(arena_t *arena, strview_t filename) {
+ OS_SMALL_SCRATCH();
+
+ TCHAR long_path_prefix[] = TEXT("\\\\?\\");
+ const usize prefix_len = arrlen(long_path_prefix) - 1;
+
+ tstr_t rel_path = strv_to_tstr(&scratch, filename);
+ DWORD pathlen = GetFullPathName(rel_path.buf, 0, NULL, NULL);
+
+ tstr_t full_path = {
+ .buf = alloc(arena, TCHAR, pathlen + prefix_len + 1),
+ .len = pathlen + prefix_len,
+ };
+ memcpy(full_path.buf, long_path_prefix, prefix_len * sizeof(TCHAR));
+
+ GetFullPathName(rel_path.buf, pathlen + 1, full_path.buf + prefix_len, NULL);
+
+ return full_path;
+}
+
+bool os_file_delete(strview_t path) {
+ OS_SMALL_SCRATCH();
+ tstr_t fname = strv_to_tstr(&scratch, path);
+ return DeleteFile(fname.buf);
+}
+
+oshandle_t os_file_open(strview_t path, filemode_e mode) {
+ OS_SMALL_SCRATCH();
+
+ tstr_t full_path = os_file_fullpath(&scratch, path);
+
+ HANDLE handle = CreateFile(
+ full_path.buf,
+ os__win_mode_to_access(mode),
+ FILE_SHARE_READ,
+ NULL,
+ os__win_mode_to_creation(mode),
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ handle = NULL;
+ }
+
+ return (oshandle_t){
+ .data = (uptr)handle
+ };
+}
+
+void os_file_close(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return;
+ CloseHandle((HANDLE)handle.data);
+}
+
+usize os_file_read(oshandle_t handle, void *buf, usize len) {
+ if (!os_handle_valid(handle)) return 0;
+ DWORD read = 0;
+ ReadFile((HANDLE)handle.data, buf, (DWORD)len, &read, NULL);
+ return (usize)read;
+}
+
+usize os_file_write(oshandle_t handle, const void *buf, usize len) {
+ if (!os_handle_valid(handle)) return 0;
+ DWORD written = 0;
+ WriteFile((HANDLE)handle.data, buf, (DWORD)len, &written, NULL);
+ return (usize)written;
+}
+
+bool os_file_seek(oshandle_t handle, usize offset) {
+ if (!os_handle_valid(handle)) return false;
+ LARGE_INTEGER offset_large = {
+ .QuadPart = offset,
+ };
+ DWORD result = SetFilePointer((HANDLE)handle.data, offset_large.LowPart, &offset_large.HighPart, FILE_BEGIN);
+ return result != INVALID_SET_FILE_POINTER;
+}
+
+bool os_file_seek_end(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return false;
+ DWORD result = SetFilePointer((HANDLE)handle.data, 0, NULL, FILE_END);
+ return result != INVALID_SET_FILE_POINTER;
+}
+
+void os_file_rewind(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return;
+ SetFilePointer((HANDLE)handle.data, 0, NULL, FILE_BEGIN);
+}
+
+usize os_file_tell(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return 0;
+ LARGE_INTEGER tell = {0};
+ BOOL result = SetFilePointerEx((HANDLE)handle.data, (LARGE_INTEGER){0}, &tell, FILE_CURRENT);
+ return result == TRUE ? (usize)tell.QuadPart : 0;
+}
+
+usize os_file_size(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return 0;
+ LARGE_INTEGER size = {0};
+ BOOL result = GetFileSizeEx((HANDLE)handle.data, &size);
+ return result == TRUE ? (usize)size.QuadPart : 0;
+}
+
+bool os_file_is_finished(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return 0;
+
+ char tmp = 0;
+ DWORD read = 0;
+ BOOL success = ReadFile((HANDLE)handle.data, &tmp, sizeof(tmp), &read, NULL);
+ bool is_finished = success && read == 0;
+
+ if (!is_finished) {
+ SetFilePointer((HANDLE)handle.data, -1, NULL, FILE_CURRENT);
+ }
+
+ return is_finished;
+}
+
+u64 os_file_time_fp(oshandle_t handle) {
+ if (!os_handle_valid(handle)) return 0;
+ FILETIME time = {0};
+ GetFileTime((HANDLE)handle.data, NULL, NULL, &time);
+ ULARGE_INTEGER utime = {
+ .HighPart = time.dwHighDateTime,
+ .LowPart = time.dwLowDateTime,
+ };
+ return (u64)utime.QuadPart;
+}
+
+// == DIR WALKER ================================
+
+typedef struct dir_t {
+ WIN32_FIND_DATA 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 out = {0};
+
+ out.name = str_from_tstr(arena, tstr_init(fd->cFileName, 0));
+
+ if (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ out.type = DIRTYPE_DIR;
+ }
+ else {
+ LARGE_INTEGER filesize = {
+ .LowPart = fd->nFileSizeLow,
+ .HighPart = fd->nFileSizeHigh,
+ };
+ out.file_size = filesize.QuadPart;
+ }
+
+ return out;
+}
+
+dir_t *os_dir_open(arena_t *arena, strview_t path) {
+ u8 tmpbuf[KB(1)] = {0};
+ arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
+
+ 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++] = '\\';
+ }
+ fullpath[pathlen++] = '*';
+ fullpath[pathlen++] = '\0';
+
+ dir_t *ctx = alloc(arena, dir_t);
+ ctx->handle = FindFirstFile(fullpath, &ctx->find_data);
+
+ if (ctx->handle == INVALID_HANDLE_VALUE) {
+ arena_pop(arena, sizeof(dir_t));
+ return NULL;
+ }
+
+ ctx->next_entry = os__dir_entry_from_find_data(arena, &ctx->find_data);
+
+ return ctx;
+}
+
+void os_dir_close(dir_t *dir) {
+ FindClose(dir->handle);
+ dir->handle = INVALID_HANDLE_VALUE;
+}
+
+bool os_dir_is_valid(dir_t *dir) {
+ return dir && dir->handle != INVALID_HANDLE_VALUE;
+}
+
+dir_entry_t *os_dir_next(arena_t *arena, dir_t *dir) {
+ if (!os_dir_is_valid(dir)) {
+ return NULL;
+ }
+
+ dir->cur_entry = dir->next_entry;
+
+ dir->next_entry = (dir_entry_t){0};
+
+ if (FindNextFile(dir->handle, &dir->find_data)) {
+ dir->next_entry = os__dir_entry_from_find_data(arena, &dir->find_data);
+ }
+ else {
+ os_dir_close(dir);
+ }
+
+ return &dir->cur_entry;
+}
+
+// == PROCESS ===================================
+
+struct os_env_t {
+ void *data;
+};
+
+void os_set_env_var(arena_t scratch, strview_t key, strview_t value) {
+ str16_t k = strv_to_str16(&scratch, key);
+ str16_t v = strv_to_str16(&scratch, value);
+ SetEnvironmentVariableW(k.buf, v.buf);
+}
+
+str_t os_get_env_var(arena_t *arena, strview_t key) {
+ u8 tmpbuf[KB(1)];
+ arena_t scratch = arena_make(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
+
+ wchar_t static_buf[1024] = {0};
+ wchar_t *buf = static_buf;
+
+ str16_t k = strv_to_str16(&scratch, key);
+ DWORD len = GetEnvironmentVariableW(k.buf, static_buf, arrlen(static_buf));
+
+ if (len > arrlen(static_buf)) {
+ buf = alloc(&scratch, wchar_t, len);
+ len = GetEnvironmentVariableW(k.buf, buf, len);
+ }
+
+ return str_from_str16(arena, str16_init(buf, len));
+}
+
+os_env_t *os_get_env(arena_t *arena) {
+ os_env_t *out = alloc(arena, os_env_t);
+ out->data = GetEnvironmentStringsW();
+ return out;
+}
+
+oshandle_t os_run_cmd_async(arena_t scratch, os_cmd_t *cmd, os_env_t *optional_env) {
+ STARTUPINFOW start_info = {
+ .cb = sizeof(STARTUPINFO),
+ .hStdError = GetStdHandle(STD_ERROR_HANDLE),
+ .hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE),
+ .hStdInput = GetStdHandle(STD_INPUT_HANDLE),
+ .dwFlags = STARTF_USESTDHANDLES,
+ };
+
+ PROCESS_INFORMATION proc_info = {0};
+
+ outstream_t cmdline = ostr_init(&scratch);
+
+ for_each (cur, cmd->head ? cmd->head : cmd) {
+ for (int i = 0; i < cur->count; ++i) {
+ strview_t arg = cur->items[i];
+ if (strv_contains(arg, ' ')) {
+ ostr_print(&cmdline, "\"%v\"", arg);
+ }
+ else {
+ ostr_puts(&cmdline, arg);
+ }
+ ostr_putc(&cmdline, ' ');
+ }
+ }
+
+ ostr_pop(&cmdline, 1);
+ ostr_putc(&cmdline, '\0');
+
+ strview_t cmd_view = ostr_as_view(&cmdline);
+ str16_t command = strv_to_str16(&scratch, cmd_view);
+
+ WCHAR *env = optional_env ? optional_env->data : NULL;
+
+ BOOL success = CreateProcessW(
+ NULL,
+ command.buf,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ env,
+ NULL,
+ &start_info,
+ &proc_info
+ );
+
+ if (env) {
+ FreeEnvironmentStringsW(env);
+ optional_env->data = NULL;
+ }
+
+ if (!success) {
+ err("couldn't create process (%v): %v", cmd_view, os_get_error_string(os_get_last_error()));
+ return os_handle_zero();
+ }
+
+ CloseHandle(proc_info.hThread);
+
+ return (oshandle_t) {
+ .data = (uptr)proc_info.hProcess
+ };
+}
+
+bool os_process_wait(oshandle_t proc, uint time, int *out_exit) {
+ if (!os_handle_valid(proc)) {
+ err("waiting on invalid handle");
+ return false;
+ }
+
+ DWORD result = WaitForSingleObject((HANDLE)proc.data, (DWORD)time);
+
+ if (result == WAIT_TIMEOUT) {
+ return false;
+ }
+
+ if (result != WAIT_OBJECT_0) {
+ err("could not wait for proces: %v", os_get_error_string(os_get_last_error()));
+ return false;
+ }
+
+ DWORD exit_status;
+ if (!GetExitCodeProcess((HANDLE)proc.data, &exit_status)) {
+ err("could not get exit status from process: %v", os_get_error_string(os_get_last_error()));
+ return false;
+ }
+
+ CloseHandle((HANDLE)proc.data);
+
+ if (out_exit) {
+ *out_exit = exit_status;
+ }
+
+ return exit_status == 0;
+}
+
+
+// == VMEM ======================================
+
+void *os_reserve(usize size, usize *out_padded_size) {
+ usize alloc_size = os_pad_to_page(size);
+ void *ptr = VirtualAlloc(NULL, alloc_size, MEM_RESERVE, PAGE_NOACCESS);
+ if (out_padded_size) {
+ *out_padded_size = alloc_size;
+ }
+ return ptr;
+}
+
+bool os_commit(void *ptr, usize num_of_pages) {
+ usize page_size = os_get_system_info().page_size;
+ void *new_ptr = VirtualAlloc(ptr, num_of_pages * page_size, MEM_COMMIT, PAGE_READWRITE);
+ return new_ptr != NULL;
+}
+
+bool os_release(void *ptr, usize size) {
+ COLLA_UNUSED(size);
+ return VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+// == THREAD ====================================
+
+DWORD os__win_thread_entry_point(void *ptr) {
+ os_entity_t *entity = (os_entity_t *)ptr;
+ thread_func_t *func = entity->thread.func;
+ void *userdata = entity->thread.userdata;
+ u64 id = entity->thread.id;
+ return func(id, userdata);
+}
+
+oshandle_t os_thread_launch(thread_func_t func, void *userdata) {
+ os_entity_t *entity = os__win_alloc_entity(OS_KIND_THREAD);
+
+ entity->thread.func = func;
+ entity->thread.userdata = userdata;
+ entity->thread.handle = CreateThread(NULL, 0, os__win_thread_entry_point, entity, 0, &entity->thread.id);
+
+ return (oshandle_t){ (uptr)entity };
+}
+
+bool os_thread_detach(oshandle_t thread) {
+ if (!os_handle_valid(thread)) return false;
+ os_entity_t *entity = (os_entity_t *)thread.data;
+ BOOL result = CloseHandle(entity->thread.handle);
+ os__win_free_entity(entity);
+ return result;
+}
+
+bool os_thread_join(oshandle_t thread, int *code) {
+ if (!os_handle_valid(thread)) return false;
+ os_entity_t *entity = (os_entity_t *)thread.data;
+ int return_code = WaitForSingleObject(entity->thread.handle, INFINITE);
+ if (code) *code = return_code;
+ BOOL result = CloseHandle(entity->thread.handle);
+ os__win_free_entity(entity);
+ return return_code != WAIT_FAILED && result;
+}
+
+u64 os_thread_get_id(oshandle_t thread) {
+ if (!os_handle_valid(thread)) return 0;
+ os_entity_t *entity = (os_entity_t *)thread.data;
+ return entity->thread.id;
+}
+
+// == MUTEX =====================================
+
+oshandle_t os_mutex_create(void) {
+ os_entity_t *entity = os__win_alloc_entity(OS_KIND_MUTEX);
+
+ InitializeCriticalSection(&entity->mutex);
+
+ return (oshandle_t){ (uptr)entity };
+}
+
+void os_mutex_free(oshandle_t mutex) {
+ if (!os_handle_valid(mutex)) return;
+ os_entity_t *entity = (os_entity_t *)mutex.data;
+ DeleteCriticalSection(&entity->mutex);
+ os__win_free_entity(entity);
+}
+
+void os_mutex_lock(oshandle_t mutex) {
+ if (!os_handle_valid(mutex)) return;
+ os_entity_t *entity = (os_entity_t *)mutex.data;
+ EnterCriticalSection(&entity->mutex);
+}
+
+void os_mutex_unlock(oshandle_t mutex) {
+ if (!os_handle_valid(mutex)) return;
+ os_entity_t *entity = (os_entity_t *)mutex.data;
+ LeaveCriticalSection(&entity->mutex);
+}
+
+bool os_mutex_try_lock(oshandle_t mutex) {
+ if (!os_handle_valid(mutex)) return false;
+ os_entity_t *entity = (os_entity_t *)mutex.data;
+ return TryEnterCriticalSection(&entity->mutex);
+}
+
+#if !COLLA_NO_CONDITION_VARIABLE
+
+// == CONDITION VARIABLE ========================
+
+oshandle_t os_cond_create(void) {
+ os_entity_t *entity = os__win_alloc_entity(OS_KIND_CONDITION_VARIABLE);
+
+ InitializeConditionVariable(&entity->cv);
+
+ return (oshandle_t){ (uptr)entity };
+}
+
+void os_cond_free(oshandle_t cond) {
+ if (!os_handle_valid(cond)) return;
+ os_entity_t *entity = (os_entity_t *)cond.data;
+ os__win_free_entity(entity);
+}
+
+void os_cond_signal(oshandle_t cond) {
+ if (!os_handle_valid(cond)) return;
+ os_entity_t *entity = (os_entity_t *)cond.data;
+ WakeConditionVariable(&entity->cv);
+}
+
+void os_cond_broadcast(oshandle_t cond) {
+ if (!os_handle_valid(cond)) return;
+ os_entity_t *entity = (os_entity_t *)cond.data;
+ WakeAllConditionVariable(&entity->cv);
+}
+
+void os_cond_wait(oshandle_t cond, oshandle_t mutex, int milliseconds) {
+ if (!os_handle_valid(cond)) return;
+ os_entity_t *entity_cv = (os_entity_t *)cond.data;
+ os_entity_t *entity_mtx = (os_entity_t *)mutex.data;
+ SleepConditionVariableCS(&entity_cv->cv, &entity_mtx->mutex, milliseconds);
+}
+
+#endif
diff --git a/win/str_win32.c b/win/str_win32.c
new file mode 100644
index 0000000..4785ecb
--- /dev/null
+++ b/win/str_win32.c
@@ -0,0 +1,81 @@
+#include "../str.h"
+#include "../arena.h"
+
+#include
+
+#if COLLA_TCC
+#include "../tcc/colla_tcc.h"
+#endif
+
+str_t str_os_from_str16(arena_t *arena, str16_t src) {
+ str_t out = {0};
+
+ int outlen = WideCharToMultiByte(
+ CP_UTF8, 0,
+ src.buf, (int)src.len,
+ NULL, 0,
+ NULL, NULL
+ );
+
+ if (outlen == 0) {
+ unsigned long error = GetLastError();
+ if (error == ERROR_NO_UNICODE_TRANSLATION) {
+ err("couldn't translate wide string (%S) to utf8, no unicode translation", src.buf);
+ }
+ else {
+ err("couldn't translate wide string (%S) to utf8, %v", src.buf, os_get_error_string(os_get_last_error()));
+ }
+
+ return STR_EMPTY;
+ }
+
+ out.buf = alloc(arena, char, outlen + 1);
+ WideCharToMultiByte(
+ CP_UTF8, 0,
+ src.buf, (int)src.len,
+ out.buf, outlen,
+ NULL, NULL
+ );
+
+ out.len = outlen;
+
+ return out;
+}
+
+str16_t strv_os_to_str16(arena_t *arena, strview_t src) {
+ str16_t out = {0};
+
+ if (strv_is_empty(src)) {
+ return out;
+ }
+
+ int len = MultiByteToWideChar(
+ CP_UTF8, 0,
+ src.buf, (int)src.len,
+ NULL, 0
+ );
+
+ if (len == 0) {
+ unsigned long error = GetLastError();
+ if (error == ERROR_NO_UNICODE_TRANSLATION) {
+ err("couldn't translate string (%v) to a wide string, no unicode translation", src);
+ }
+ else {
+ err("couldn't translate string (%v) to a wide string, %v", src, os_get_error_string(os_get_last_error()));
+ }
+
+ return out;
+ }
+
+ out.buf = alloc(arena, wchar_t, len + 1);
+
+ MultiByteToWideChar(
+ CP_UTF8, 0,
+ src.buf, (int)src.len,
+ out.buf, len
+ );
+
+ out.len = len;
+
+ return out;
+}
\ No newline at end of file
diff --git a/xml.c b/xml.c
deleted file mode 100644
index 0b9ab95..0000000
--- a/xml.c
+++ /dev/null
@@ -1,145 +0,0 @@
-#include "xml.h"
-
-#include "file.h"
-#include "strstream.h"
-#include "tracelog.h"
-
-static xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in);
-
-xml_t xmlParse(arena_t *arena, strview_t filename) {
- return xmlParseStr(arena, fileReadWholeStr(arena, filename));
-}
-
-xml_t xmlParseStr(arena_t *arena, str_t xmlstr) {
- xml_t out = {
- .text = xmlstr,
- .root = alloc(arena, xmltag_t),
- };
- return out;
-
-
- instream_t in = istrInitLen(xmlstr.buf, xmlstr.len);
-
- while (!istrIsFinished(in)) {
- xmltag_t *tag = xml__parse_tag(arena, &in);
-
- if (out.tail) out.tail->next = tag;
- else out.root->child = tag;
-
- out.tail = tag;
- }
-
- return out;
-}
-
-xmltag_t *xmlGetTag(xmltag_t *parent, strview_t key, bool recursive) {
- xmltag_t *t = parent ? parent->child : NULL;
- while (t) {
- if (strvEquals(key, t->key)) {
- return t;
- }
- if (recursive && t->child) {
- xmltag_t *out = xmlGetTag(t, key, recursive);
- if (out) {
- return out;
- }
- }
- t = t->next;
- }
- return NULL;
-}
-
-strview_t xmlGetAttribute(xmltag_t *tag, strview_t key) {
- xmlattr_t *a = tag ? tag->attributes : NULL;
- while (a) {
- if (strvEquals(key, a->key)) {
- return a->value;
- }
- a = a->next;
- }
- return STRV_EMPTY;
-}
-
-// == PRIVATE FUNCTIONS ========================================================================
-
-static xmlattr_t *xml__parse_attr(arena_t *arena, instream_t *in) {
- if (istrPeek(in) != ' ') {
- return NULL;
- }
-
- strview_t key = strvTrim(istrGetView(in, '='));
- istrSkip(in, 2); // skip = and "
- strview_t val = strvTrim(istrGetView(in, '"'));
- istrSkip(in, 1); // skip "
-
- if (strvIsEmpty(key) || strvIsEmpty(val)) {
- warn("key or value empty");
- return NULL;
- }
-
- xmlattr_t *attr = alloc(arena, xmlattr_t);
- attr->key = key;
- attr->value = val;
- return attr;
-}
-
-static xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in) {
- istrSkipWhitespace(in);
-
- // we're either parsing the body, or we have finished the object
- if (istrPeek(in) != '<' || istrPeekNext(in) == '/') {
- return NULL;
- }
-
- istrSkip(in, 1); // skip <
-
- // meta tag, we don't care about these
- if (istrPeek(in) == '?') {
- istrIgnoreAndSkip(in, '\n');
- return NULL;
- }
-
- xmltag_t *tag = alloc(arena, xmltag_t);
-
- tag->key = strvTrim(istrGetViewEither(in, strv(" >")));
-
- xmlattr_t *attr = xml__parse_attr(arena, in);
- while (attr) {
- attr->next = tag->attributes;
- tag->attributes = attr;
- attr = xml__parse_attr(arena, in);
- }
-
- // this tag does not have children, return
- if (istrPeek(in) == '/') {
- istrSkip(in, 2); // skip / and >
- return tag;
- }
-
- istrSkip(in, 1); // skip >
-
- xmltag_t *child = xml__parse_tag(arena, in);
- while (child) {
- if (tag->tail) {
- tag->tail->next = child;
- tag->tail = child;
- }
- else {
- tag->child = tag->tail = child;
- }
- child = xml__parse_tag(arena, in);
- }
-
- // parse content
- istrSkipWhitespace(in);
- tag->content = istrGetView(in, '<');
-
- // closing tag
- istrSkip(in, 2); // skip < and /
- strview_t closing = strvTrim(istrGetView(in, '>'));
- if (!strvEquals(tag->key, closing)) {
- warn("opening and closing tags are different!: (%v) != (%v)", tag->key, closing);
- }
- istrSkip(in, 1); // skip >
- return tag;
-}
diff --git a/xml.h b/xml.h
deleted file mode 100644
index 573ec33..0000000
--- a/xml.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "str.h"
-#include "arena.h"
-
-typedef struct xmlattr_t {
- strview_t key;
- strview_t value;
- struct xmlattr_t *next;
-} xmlattr_t;
-
-typedef struct xmltag_t {
- strview_t key;
- xmlattr_t *attributes;
- strview_t content;
- struct xmltag_t *child;
- struct xmltag_t *tail;
- struct xmltag_t *next;
-} xmltag_t;
-
-typedef struct {
- str_t text;
- xmltag_t *root;
- xmltag_t *tail;
-} xml_t;
-
-xml_t xmlParse(arena_t *arena, strview_t filename);
-xml_t xmlParseStr(arena_t *arena, str_t xmlstr);
-
-xmltag_t *xmlGetTag(xmltag_t *parent, strview_t key, bool recursive);
-strview_t xmlGetAttribute(xmltag_t *tag, strview_t key);
\ No newline at end of file