Init repo
This commit is contained in:
115
src/build_tool/core/allocator.c
Normal file
115
src/build_tool/core/allocator.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef MA_CMalloc
|
||||
#include <stdlib.h>
|
||||
#define MA_CMalloc(x) malloc(x)
|
||||
#define MA_CFree(x) free(x)
|
||||
#define MA_CRealloc(p, size) realloc(p, size)
|
||||
#endif
|
||||
|
||||
MA_API M_Allocator MA_BootstrapExclusive(void) {
|
||||
MA_Arena bootstrap_arena = {0};
|
||||
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
|
||||
*arena = bootstrap_arena;
|
||||
arena->base_len = arena->len;
|
||||
return MA_GetExclusiveAllocator(arena);
|
||||
}
|
||||
|
||||
MA_API void *M__AllocNonZeroed(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M__Alloc(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
|
||||
MA_MemoryZero(p, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M__AllocCopy(M_Allocator allocator, void *p, size_t size) {
|
||||
void *copy_buffer = M__AllocNonZeroed(allocator, size);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API void M__Dealloc(M_Allocator allocator, void *p) {
|
||||
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0, 0);
|
||||
}
|
||||
|
||||
MA_API void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size) {
|
||||
void *result = allocator.p(allocator.obj, M_AllocatorOp_Reallocate, p, size, old_size);
|
||||
// @todo: add old_size? because we can't zero
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA_CMalloc(size);
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
MA_CFree(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Reallocate) {
|
||||
return MA_CRealloc(p, size);
|
||||
}
|
||||
|
||||
MA_Assertf(0, "MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA__PushSizeNonZeroed((MA_Arena *)allocator, size);
|
||||
}
|
||||
|
||||
else if (kind == M_AllocatorOp_Reallocate) {
|
||||
void *new_p = MA__PushSizeNonZeroed((MA_Arena *)allocator, size);
|
||||
MA_MemoryCopy(new_p, p, old_size);
|
||||
return new_p;
|
||||
}
|
||||
|
||||
else if (kind == M_AllocatorOp_Deallocate) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_Assertf(0, "MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
|
||||
MA_Arena *arena = (MA_Arena *)allocator;
|
||||
if (kind == M_AllocatorOp_Reallocate) {
|
||||
if (size > old_size) {
|
||||
size_t size_to_push = size - old_size;
|
||||
void *result = MA__PushSizeNonZeroed(arena, size_to_push);
|
||||
if (!p) p = result;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
MA_DeallocateArena(arena);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_Assertf(0, "MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena) {
|
||||
M_Allocator allocator = {(int *)arena, MA_ExclusiveAllocatorProc};
|
||||
return allocator;
|
||||
}
|
||||
|
||||
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena) {
|
||||
M_Allocator allocator = {(int *)arena, MA_AllocatorProc};
|
||||
return allocator;
|
||||
}
|
||||
|
||||
MA_API M_Allocator M_GetSystemAllocator(void) {
|
||||
M_Allocator allocator;
|
||||
allocator.obj = 0;
|
||||
allocator.p = M_ClibAllocatorProc;
|
||||
return allocator;
|
||||
}
|
||||
44
src/build_tool/core/allocator.h
Normal file
44
src/build_tool/core/allocator.h
Normal file
@@ -0,0 +1,44 @@
|
||||
typedef struct M_Allocator M_Allocator;
|
||||
|
||||
typedef enum M_AllocatorOp {
|
||||
M_AllocatorOp_Invalid,
|
||||
M_AllocatorOp_Allocate,
|
||||
M_AllocatorOp_Deallocate,
|
||||
M_AllocatorOp_Reallocate,
|
||||
} M_AllocatorOp;
|
||||
|
||||
typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
|
||||
MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
|
||||
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
|
||||
|
||||
struct M_Allocator {
|
||||
// it's int for type safety because C++ somehow allows this:
|
||||
// struct Array { M_Allocator allocator; }
|
||||
// Array = {arena}; WTF???!??!?
|
||||
// without actually passing M_Allocator but just a pointer
|
||||
int *obj;
|
||||
M_AllocatorProc *p;
|
||||
};
|
||||
|
||||
#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T))
|
||||
#define M_AllocArray(a, T, c) (T *)M_Alloc((a), sizeof(T) * (c))
|
||||
#define M_AllocStructNonZeroed(a, T) (T *)M_AllocNonZeroed((a), sizeof(T))
|
||||
#define M_AllocArrayNonZeroed(a, T, c) (T *)M_AllocNonZeroed((a), sizeof(T) * (c))
|
||||
#define M_AllocStructCopy(a, T, p) (T *)M_PushCopy(a, (p), sizeof(T))
|
||||
|
||||
#define M_Alloc(a, size) M__Alloc(a, size)
|
||||
#define M_AllocNonZeroed(a, size) M__AllocNonZeroed(a, size)
|
||||
#define M_AllocCopy(a, p, size) M__AllocCopy(a, p, size)
|
||||
#define M_Realloc(a, p, size, old_size) M__Realloc(a, p, size, old_size)
|
||||
#define M_Dealloc(a, p) M__Dealloc(a, p)
|
||||
|
||||
MA_API void *M__AllocNonZeroed(M_Allocator allocator, size_t size);
|
||||
MA_API void *M__Alloc(M_Allocator allocator, size_t size);
|
||||
MA_API void *M__AllocCopy(M_Allocator allocator, void *p, size_t size);
|
||||
MA_API void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size);
|
||||
MA_API void M__Dealloc(M_Allocator allocator, void *p);
|
||||
|
||||
MA_API M_Allocator M_GetSystemAllocator(void);
|
||||
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena);
|
||||
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena);
|
||||
MA_API M_Allocator MA_BootstrapExclusive(void);
|
||||
270
src/build_tool/core/array.hpp
Normal file
270
src/build_tool/core/array.hpp
Normal file
@@ -0,0 +1,270 @@
|
||||
// Iterating and removing elements
|
||||
//
|
||||
// ForArrayRemovable(array) {
|
||||
// ForArrayRemovablePrepare(array);
|
||||
// if (it == 4) ForArrayRemovableDeclare();
|
||||
// }
|
||||
//
|
||||
#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1)
|
||||
#define ForArrayRemovablePrepare(a) \
|
||||
auto &it = (a)[__i]; \
|
||||
bool remove_it = false; \
|
||||
defer { \
|
||||
if (remove_it) { \
|
||||
(a).ordered_remove(it); \
|
||||
__i -= 1; \
|
||||
} \
|
||||
}
|
||||
#define ForArrayRemovableDeclare() (remove_it = true)
|
||||
#define For2(it, array) for (auto &it : (array))
|
||||
#define For(array) For2(it, array)
|
||||
|
||||
template <class T>
|
||||
struct Array {
|
||||
M_Allocator allocator;
|
||||
T *data;
|
||||
int cap, len;
|
||||
|
||||
T &operator[](int index) {
|
||||
IO_Assert(index >= 0 && index < len);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
bool is_first(T &item) { return &item == first(); }
|
||||
bool is_last(T &item) { return &item == last(); }
|
||||
|
||||
bool contains(T &item) {
|
||||
bool result = &item >= data && &item < data + len;
|
||||
return result;
|
||||
}
|
||||
|
||||
int get_index(T &item) {
|
||||
IO_Assert((data <= &item) && ((data + len) > &item));
|
||||
size_t offset = &item - data;
|
||||
return (int)offset;
|
||||
}
|
||||
|
||||
void add(const T &item) {
|
||||
try_growing();
|
||||
data[len++] = item;
|
||||
}
|
||||
|
||||
// Struct needs to have 'value_to_sort_by' field
|
||||
void sorted_insert_decreasing(T item) {
|
||||
int insert_index = -1;
|
||||
For(*this) {
|
||||
if (it.value_to_sort_by <= item.value_to_sort_by) {
|
||||
insert_index = get_index(it);
|
||||
insert(item, insert_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (insert_index == -1) {
|
||||
add(item);
|
||||
}
|
||||
}
|
||||
|
||||
void bounded_add(T item) {
|
||||
IO_Assert(len + 1 <= cap);
|
||||
try_growing(); // in case of error
|
||||
data[len++] = item;
|
||||
}
|
||||
|
||||
T *alloc(const T &item) {
|
||||
try_growing();
|
||||
T *ref = data + len++;
|
||||
*ref = item;
|
||||
return ref;
|
||||
}
|
||||
|
||||
T *alloc() {
|
||||
try_growing();
|
||||
T *ref = data + len++;
|
||||
*ref = {};
|
||||
return ref;
|
||||
}
|
||||
|
||||
T *alloc_multiple(int size) {
|
||||
try_growing_to_fit_item_count(size);
|
||||
T *result = data + len;
|
||||
len += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void add_array(T *items, int item_count) {
|
||||
for (int i = 0; i < item_count; i += 1) {
|
||||
add(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_array(Array<T> items) {
|
||||
add_array(items.data, items.len);
|
||||
}
|
||||
|
||||
void reserve(int size) {
|
||||
if (size > cap) {
|
||||
if (!allocator.p) allocator = M_GetSystemAllocator();
|
||||
|
||||
void *p = M_Realloc(allocator, data, size * sizeof(T), cap * sizeof(T));
|
||||
IO_Assert(p);
|
||||
|
||||
data = (T *)p;
|
||||
cap = size;
|
||||
}
|
||||
}
|
||||
|
||||
void init(M_Allocator allocator, int size) {
|
||||
len = 0;
|
||||
cap = 0;
|
||||
data = 0;
|
||||
this->allocator = allocator;
|
||||
reserve(size);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
T pop() {
|
||||
IO_Assert(len > 0);
|
||||
return data[--len];
|
||||
}
|
||||
|
||||
void unordered_remove(T &item) { // DONT USE IN LOOPS !!!!
|
||||
IO_Assert(len > 0);
|
||||
IO_Assert(&item >= begin() && &item < end());
|
||||
item = data[--len];
|
||||
}
|
||||
|
||||
void unordered_remove_index(int index) {
|
||||
IO_Assert(index >= 0 && index < len);
|
||||
data[index] = data[--len];
|
||||
}
|
||||
|
||||
int get_index(const T &item) {
|
||||
ptrdiff_t index = (ptrdiff_t)(&item - data);
|
||||
IO_Assert(index >= 0 && index < len);
|
||||
// IO_Assert(index > INT_MIN && index < INT_MAX);
|
||||
return (int)index;
|
||||
}
|
||||
|
||||
void ordered_remove(T &item) { // DONT USE IN LOOPS !!!
|
||||
IO_Assert(len > 0);
|
||||
IO_Assert(&item >= begin() && &item < end());
|
||||
int index = get_index(item);
|
||||
ordered_remove_index(index);
|
||||
}
|
||||
|
||||
void ordered_remove_index(int index) {
|
||||
IO_Assert(index >= 0 && index < len);
|
||||
int right_len = len - index - 1;
|
||||
memmove(data + index, data + index + 1, right_len * sizeof(T));
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
void insert(T item, int index) {
|
||||
if (index == len) {
|
||||
add(item);
|
||||
return;
|
||||
}
|
||||
|
||||
IO_Assert(index < len);
|
||||
IO_Assert(index >= 0);
|
||||
|
||||
try_growing();
|
||||
int right_len = len - index;
|
||||
memmove(data + index + 1, data + index, sizeof(T) * right_len);
|
||||
data[index] = item;
|
||||
len += 1;
|
||||
}
|
||||
|
||||
void dealloc() {
|
||||
if (data) M_Dealloc(allocator, data);
|
||||
data = 0;
|
||||
len = cap = 0;
|
||||
}
|
||||
|
||||
Array<T> copy(M_Allocator allocator) {
|
||||
Array result = {};
|
||||
result.allocator = allocator;
|
||||
result.reserve(cap);
|
||||
|
||||
memmove(result.data, data, sizeof(T) * len);
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
Array<T> tight_copy(M_Allocator allocator) {
|
||||
Array result = {};
|
||||
result.allocator = allocator;
|
||||
result.reserve(len);
|
||||
|
||||
memmove(result.data, data, sizeof(T) * len);
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
T *first() {
|
||||
IO_Assert(len > 0);
|
||||
return data;
|
||||
}
|
||||
T *last() {
|
||||
IO_Assert(len > 0);
|
||||
return data + len - 1;
|
||||
}
|
||||
T *front() {
|
||||
IO_Assert(len > 0);
|
||||
return data;
|
||||
}
|
||||
T *back() {
|
||||
IO_Assert(len > 0);
|
||||
return data + len - 1;
|
||||
}
|
||||
T *begin() { return data; }
|
||||
T *end() { return data + len; }
|
||||
|
||||
// for (auto it = integers.begin(), end = integers.end(); it != end; ++it)
|
||||
struct Reverse_Iter {
|
||||
T *data;
|
||||
Array<T> *arr;
|
||||
|
||||
Reverse_Iter operator++(int) {
|
||||
Reverse_Iter ret = *this;
|
||||
data -= 1;
|
||||
return ret;
|
||||
}
|
||||
Reverse_Iter &operator++() {
|
||||
data -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T &operator*() { return data[0]; }
|
||||
T *operator->() { return data; }
|
||||
|
||||
friend bool operator==(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data == b.data; };
|
||||
friend bool operator!=(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data != b.data; };
|
||||
|
||||
Reverse_Iter begin() { return Reverse_Iter{arr->end() - 1, arr}; }
|
||||
Reverse_Iter end() { return Reverse_Iter{arr->begin() - 1, arr}; }
|
||||
};
|
||||
|
||||
Reverse_Iter reverse() { return {end() - 1, this}; }
|
||||
|
||||
void try_growing() {
|
||||
if (len + 1 > cap) {
|
||||
int new_size = cap * 2;
|
||||
if (new_size < 16) new_size = 16;
|
||||
|
||||
reserve(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
void try_growing_to_fit_item_count(int item_count) {
|
||||
if (len + item_count > cap) {
|
||||
int new_size = (cap + item_count) * 2;
|
||||
if (new_size < 16) new_size = 16;
|
||||
reserve(new_size);
|
||||
}
|
||||
}
|
||||
};
|
||||
186
src/build_tool/core/cmd.c
Normal file
186
src/build_tool/core/cmd.c
Normal file
@@ -0,0 +1,186 @@
|
||||
|
||||
CmdParser MakeCmdParser(MA_Arena *arena, int argc, char **argv, const char *custom_help) {
|
||||
CmdParser result = {argc, argv, arena, custom_help};
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddBool(CmdParser *p, bool *result, const char *name, const char *help) {
|
||||
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
|
||||
decl->kind = CmdDeclKind_Bool;
|
||||
decl->name = S8_MakeFromChar((char *)name);
|
||||
decl->help = S8_MakeFromChar((char *)help);
|
||||
decl->bool_result = result;
|
||||
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
|
||||
}
|
||||
|
||||
void AddInt(CmdParser *p, int *result, const char *name, const char *help) {
|
||||
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
|
||||
decl->kind = CmdDeclKind_Int;
|
||||
decl->name = S8_MakeFromChar((char *)name);
|
||||
decl->help = S8_MakeFromChar((char *)help);
|
||||
decl->int_result = result;
|
||||
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
|
||||
}
|
||||
|
||||
void AddList(CmdParser *p, S8_List *result, const char *name, const char *help) {
|
||||
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
|
||||
decl->kind = CmdDeclKind_List;
|
||||
decl->name = S8_MakeFromChar((char *)name);
|
||||
decl->help = S8_MakeFromChar((char *)help);
|
||||
decl->list_result = result;
|
||||
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
|
||||
}
|
||||
|
||||
void AddEnum(CmdParser *p, int *result, const char *name, const char *help, const char **enum_options, int enum_option_count) {
|
||||
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
|
||||
decl->kind = CmdDeclKind_Enum;
|
||||
decl->name = S8_MakeFromChar((char *)name);
|
||||
decl->help = S8_MakeFromChar((char *)help);
|
||||
decl->enum_result = result;
|
||||
decl->enum_options = enum_options;
|
||||
decl->enum_option_count = enum_option_count;
|
||||
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
|
||||
}
|
||||
|
||||
CmdDecl *FindDecl(CmdParser *p, S8_String name) {
|
||||
for (CmdDecl *it = p->fdecl; it; it = it->next) {
|
||||
if (S8_AreEqual(it->name, name, true)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
S8_String StrEnumValues(MA_Arena *arena, CmdDecl *decl) {
|
||||
S8_List list = {0};
|
||||
S8_AddF(arena, &list, "[");
|
||||
for (int i = 0; i < decl->enum_option_count; i += 1) {
|
||||
S8_AddF(arena, &list, "%s", decl->enum_options[i]);
|
||||
if (i != decl->enum_option_count - 1) S8_AddF(arena, &list, "|");
|
||||
}
|
||||
S8_AddF(arena, &list, "]");
|
||||
return S8_Merge(arena, list);
|
||||
}
|
||||
|
||||
void PrintCmdUsage(CmdParser *p) {
|
||||
IO_Printf("%s\nCommands:\n", p->custom_help);
|
||||
for (CmdDecl *it = p->fdecl; it; it = it->next) {
|
||||
IO_Printf(" ");
|
||||
if (it->kind == CmdDeclKind_List) {
|
||||
S8_String example = S8_Format(p->arena, "-%.*s a b c", S8_Expand(it->name));
|
||||
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
|
||||
} else if (it->kind == CmdDeclKind_Bool) {
|
||||
S8_String example = S8_Format(p->arena, "-%.*s", S8_Expand(it->name));
|
||||
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
|
||||
} else if (it->kind == CmdDeclKind_Enum) {
|
||||
S8_String enum_vals = StrEnumValues(p->arena, it);
|
||||
S8_String example = S8_Format(p->arena, "-%.*s %.*s", S8_Expand(it->name), S8_Expand(enum_vals));
|
||||
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
|
||||
} else if (it->kind == CmdDeclKind_Int) {
|
||||
S8_String example = S8_Format(p->arena, "-%.*s 8", S8_Expand(it->name));
|
||||
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
|
||||
} else IO_Todo();
|
||||
}
|
||||
}
|
||||
|
||||
bool InvalidCmdArg(CmdParser *p, S8_String arg) {
|
||||
IO_Printf("invalid command line argument: %.*s\n", S8_Expand(arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseCmd(MA_Arena *arg_arena, CmdParser *p) {
|
||||
for (int i = 1; i < p->argc; i += 1) {
|
||||
S8_String arg = S8_MakeFromChar(p->argv[i]);
|
||||
|
||||
if (S8_AreEqual(arg, S8_Lit("--help"), true) || S8_AreEqual(arg, S8_Lit("-h"), true) || S8_AreEqual(arg, S8_Lit("-help"), true)) {
|
||||
PrintCmdUsage(p);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg.str[0] == '-') {
|
||||
arg = S8_Skip(arg, 1);
|
||||
if (arg.str[0] == '-') {
|
||||
arg = S8_Skip(arg, 1);
|
||||
}
|
||||
|
||||
CmdDecl *decl = FindDecl(p, arg);
|
||||
if (!decl) return InvalidCmdArg(p, arg);
|
||||
|
||||
if (decl->kind == CmdDeclKind_Bool) {
|
||||
*decl->bool_result = !*decl->bool_result;
|
||||
} else if (decl->kind == CmdDeclKind_Int) {
|
||||
if (i + 1 >= p->argc) {
|
||||
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
|
||||
return false;
|
||||
}
|
||||
S8_String num = S8_MakeFromChar(p->argv[++i]);
|
||||
for (int i = 0; i < num.len; i += 1) {
|
||||
if (!CHAR_IsDigit(num.str[i])) {
|
||||
IO_Printf("expected argument to be a number, got instead: %.*s", S8_Expand(num));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int count = atoi(num.str);
|
||||
decl->int_result[0] = count;
|
||||
|
||||
} else if (decl->kind == CmdDeclKind_Enum) {
|
||||
if (i + 1 >= p->argc) {
|
||||
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
|
||||
return false;
|
||||
}
|
||||
S8_String option_from_cmd = S8_MakeFromChar(p->argv[++i]);
|
||||
|
||||
bool found_option = false;
|
||||
for (int i = 0; i < decl->enum_option_count; i += 1) {
|
||||
S8_String option = S8_MakeFromChar((char *)decl->enum_options[i]);
|
||||
if (S8_AreEqual(option, option_from_cmd, true)) {
|
||||
*decl->enum_result = i;
|
||||
found_option = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_option) {
|
||||
IO_Printf("expected one of the enum values: %.*s", S8_Expand(StrEnumValues(p->arena, decl)));
|
||||
IO_Printf(" got instead: %.*s\n", S8_Expand(option_from_cmd));
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (decl->kind == CmdDeclKind_List) {
|
||||
if (i + 1 >= p->argc) {
|
||||
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
|
||||
return false;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
for (int counter = 0; i < p->argc; i += 1, counter += 1) {
|
||||
S8_String arg = S8_MakeFromChar(p->argv[i]);
|
||||
if (arg.str[0] == '-') {
|
||||
if (counter == 0) {
|
||||
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
|
||||
return false;
|
||||
}
|
||||
i -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
S8_AddNode(arg_arena, decl->list_result, arg);
|
||||
}
|
||||
} else IO_Todo();
|
||||
|
||||
} else {
|
||||
if (p->require_one_standalone_arg && p->args.node_count == 0) {
|
||||
S8_AddNode(arg_arena, &p->args, arg);
|
||||
} else {
|
||||
return InvalidCmdArg(p, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p->require_one_standalone_arg && p->args.node_count == 0) {
|
||||
PrintCmdUsage(p);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
37
src/build_tool/core/cmd.h
Normal file
37
src/build_tool/core/cmd.h
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
typedef enum {
|
||||
CmdDeclKind_Bool,
|
||||
CmdDeclKind_Int,
|
||||
CmdDeclKind_List,
|
||||
CmdDeclKind_Enum,
|
||||
} CmdDeclKind;
|
||||
|
||||
typedef struct CmdDecl CmdDecl;
|
||||
struct CmdDecl {
|
||||
CmdDecl *next;
|
||||
CmdDeclKind kind;
|
||||
S8_String name;
|
||||
S8_String help;
|
||||
|
||||
bool *bool_result;
|
||||
S8_List *list_result;
|
||||
int *int_result;
|
||||
|
||||
int *enum_result;
|
||||
const char **enum_options;
|
||||
int enum_option_count;
|
||||
};
|
||||
|
||||
typedef struct CmdParser CmdParser;
|
||||
struct CmdParser {
|
||||
int argc;
|
||||
char **argv;
|
||||
MA_Arena *arena;
|
||||
const char *custom_help;
|
||||
|
||||
CmdDecl *fdecl;
|
||||
CmdDecl *ldecl;
|
||||
|
||||
bool require_one_standalone_arg;
|
||||
S8_List args;
|
||||
};
|
||||
23
src/build_tool/core/core.c
Normal file
23
src/build_tool/core/core.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "core.h"
|
||||
#include "../standalone_libraries/stb_sprintf.c"
|
||||
#define IO_VSNPRINTF stbsp_vsnprintf
|
||||
#define IO_SNPRINTF stbsp_snprintf
|
||||
#include "../standalone_libraries/io.c"
|
||||
#define MA_Assertf(x, ...) IO_Assertf(x, __VA_ARGS__)
|
||||
#include "../standalone_libraries/arena.c"
|
||||
#define RE_ASSERT(x) IO_Assert(x)
|
||||
#include "../standalone_libraries/regex.c"
|
||||
#include "../standalone_libraries/unicode.c"
|
||||
#define S8_VSNPRINTF stbsp_vsnprintf
|
||||
#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size)
|
||||
#define S8_ASSERT(x) IO_Assert(x)
|
||||
#define S8_MemoryCopy MA_MemoryCopy
|
||||
#include "../standalone_libraries/string.c"
|
||||
#define MU_ASSERT IO_Assert
|
||||
#include "../standalone_libraries/multimedia.h"
|
||||
#include "../standalone_libraries/hash.c"
|
||||
#include "../standalone_libraries/load_library.c"
|
||||
#include "filesystem.c"
|
||||
|
||||
#include "cmd.c"
|
||||
#include "allocator.c"
|
||||
1
src/build_tool/core/core.cpp
Normal file
1
src/build_tool/core/core.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "core.c"
|
||||
25
src/build_tool/core/core.h
Normal file
25
src/build_tool/core/core.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef FIRST_CORE_HEADER
|
||||
#define FIRST_CORE_HEADER
|
||||
|
||||
#include "../standalone_libraries/preproc_env.h"
|
||||
#include "../standalone_libraries/stb_sprintf.h"
|
||||
#include "../standalone_libraries/io.h"
|
||||
#include "../standalone_libraries/arena.h"
|
||||
#include "../standalone_libraries/unicode.h"
|
||||
#include "../standalone_libraries/string.h"
|
||||
#include "../standalone_libraries/hash.h"
|
||||
#include "../standalone_libraries/linked_list.h"
|
||||
#include "../standalone_libraries/regex.h"
|
||||
#include "../standalone_libraries/multimedia.h"
|
||||
#include "../standalone_libraries/load_library.h"
|
||||
#include "filesystem.h"
|
||||
#include "cmd.h"
|
||||
#include "allocator.h"
|
||||
|
||||
#if LANG_CPP
|
||||
#include "../standalone_libraries/defer.hpp"
|
||||
#include "table.hpp"
|
||||
#include "array.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
720
src/build_tool/core/filesystem.c
Normal file
720
src/build_tool/core/filesystem.c
Normal file
@@ -0,0 +1,720 @@
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
OS_API bool OS_EnableTerminalColors(void) {
|
||||
// Enable color terminal output
|
||||
{
|
||||
// Set output mode to handle virtual terminal sequences
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut != INVALID_HANDLE_VALUE) {
|
||||
DWORD dwMode = 0;
|
||||
if (GetConsoleMode(hOut, &dwMode)) {
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (SetConsoleMode(hOut, dwMode)) {
|
||||
return true;
|
||||
} else {
|
||||
IO_Printf("Failed to enable colored terminal output C\n");
|
||||
}
|
||||
} else {
|
||||
IO_Printf("Failed to enable colored terminal output B\n");
|
||||
}
|
||||
} else {
|
||||
IO_Printf("Failed to enable colored terminal output A\n");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OS_API bool OS_IsAbsolute(S8_String path) {
|
||||
bool result = path.len > 3 && CHAR_IsAlphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
|
||||
wchar_t wbuffer[1024];
|
||||
DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer));
|
||||
IO_Assert(wsize != 0);
|
||||
|
||||
S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize);
|
||||
S8_NormalizePathUnsafe(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String path = OS_GetExePath(scratch.arena);
|
||||
path = S8_ChopLastSlash(path);
|
||||
path = S8_Copy(arena, path);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return path;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
|
||||
wchar_t wbuffer[1024];
|
||||
DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer);
|
||||
IO_Assert(wsize != 0);
|
||||
IO_Assert(wsize < 1022);
|
||||
wbuffer[wsize++] = '/';
|
||||
wbuffer[wsize] = 0;
|
||||
|
||||
S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize);
|
||||
S8_NormalizePathUnsafe(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
OS_API void OS_SetWorkingDir(S8_String path) {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
SetCurrentDirectoryW(wpath);
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len);
|
||||
wchar_t wpath_abs[1024];
|
||||
DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0);
|
||||
if (written == 0)
|
||||
return S8_MakeEmpty();
|
||||
S8_String path = S8_FromWidecharEx(arena, wpath_abs, written);
|
||||
S8_NormalizePathUnsafe(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
OS_API bool OS_FileExists(S8_String path) {
|
||||
wchar_t wbuff[1024];
|
||||
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
|
||||
DWORD attribs = GetFileAttributesW(wbuff);
|
||||
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API bool OS_IsDir(S8_String path) {
|
||||
wchar_t wbuff[1024];
|
||||
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
|
||||
DWORD dwAttrib = GetFileAttributesW(wbuff);
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
OS_API bool OS_IsFile(S8_String path) {
|
||||
wchar_t wbuff[1024];
|
||||
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
|
||||
DWORD dwAttrib = GetFileAttributesW(wbuff);
|
||||
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
|
||||
}
|
||||
|
||||
OS_API double OS_GetTime(void) {
|
||||
static int64_t counts_per_second;
|
||||
if (counts_per_second == 0) {
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
counts_per_second = freq.QuadPart;
|
||||
}
|
||||
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
double result = (double)time.QuadPart / (double)counts_per_second;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
User needs to copy particular filename to keep it.
|
||||
|
||||
for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
typedef struct OS_Win32_FileIter {
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAW data;
|
||||
} OS_Win32_FileIter;
|
||||
|
||||
OS_API bool OS_IsValid(OS_FileIter it) {
|
||||
return it.is_valid;
|
||||
}
|
||||
|
||||
OS_API void OS_Advance(OS_FileIter *it) {
|
||||
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
|
||||
WIN32_FIND_DATAW *data = &it->w32->data;
|
||||
|
||||
// Skip '.' and '..'
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
|
||||
|
||||
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
it->filename = S8_FromWidecharEx(it->arena, data->cFileName, S8_WideLength(data->cFileName));
|
||||
const char *is_dir = it->is_directory ? "/" : "";
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir);
|
||||
it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path);
|
||||
it->is_valid = true;
|
||||
|
||||
if (it->is_directory) {
|
||||
IO_Assert(it->relative_path.str[it->relative_path.len - 1] == '/');
|
||||
IO_Assert(it->absolute_path.str[it->absolute_path.len - 1] == '/');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
DWORD error = GetLastError();
|
||||
IO_Assert(error == ERROR_NO_MORE_FILES);
|
||||
FindClose(it->w32->handle);
|
||||
}
|
||||
|
||||
OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) {
|
||||
OS_FileIter it = {0};
|
||||
it.arena = scratch_arena;
|
||||
it.path = path;
|
||||
|
||||
S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path));
|
||||
wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1);
|
||||
int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len);
|
||||
IO_Assert(wsize);
|
||||
|
||||
it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter);
|
||||
it.w32->handle = FindFirstFileW(wbuff, &it.w32->data);
|
||||
if (it.w32->handle == INVALID_HANDLE_VALUE) {
|
||||
it.is_valid = false;
|
||||
return it;
|
||||
}
|
||||
|
||||
IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0);
|
||||
OS_Advance(&it);
|
||||
return it;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_MakeDir(S8_String path) {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
BOOL success = CreateDirectoryW(wpath, NULL);
|
||||
OS_Result result = OS_SUCCESS;
|
||||
if (success == 0) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ALREADY_EXISTS) {
|
||||
result = OS_ALREADY_EXISTS;
|
||||
} else if (error == ERROR_PATH_NOT_FOUND) {
|
||||
result = OS_PATH_NOT_FOUND;
|
||||
} else {
|
||||
IO_Assert(0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) {
|
||||
wchar_t wfrom[1024];
|
||||
UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from.str, from.len);
|
||||
|
||||
wchar_t wto[1024];
|
||||
UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to.str, to.len);
|
||||
|
||||
BOOL fail_if_exists = !overwrite;
|
||||
BOOL success = CopyFileW(wfrom, wto, fail_if_exists);
|
||||
|
||||
OS_Result result = OS_SUCCESS;
|
||||
if (success == FALSE)
|
||||
result = OS_FAILURE;
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_DeleteFile(S8_String path) {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
BOOL success = DeleteFileW(wpath);
|
||||
OS_Result result = OS_SUCCESS;
|
||||
if (success == 0)
|
||||
result = OS_PATH_NOT_FOUND;
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
|
||||
IO_Todo();
|
||||
return OS_FAILURE;
|
||||
#if 0
|
||||
if (flags & OS_RECURSIVE) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE);
|
||||
S8_Node *dirs_to_remove = 0;
|
||||
for (S8_Node *it = list.first; it; it = it->next) {
|
||||
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) {
|
||||
OS_DeleteFile(it->string);
|
||||
}
|
||||
else {
|
||||
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
|
||||
SLL_STACK_ADD(dirs_to_remove, node);
|
||||
}
|
||||
}
|
||||
for (S8_Node *it = dirs_to_remove; it; it = it->next) {
|
||||
OS_DeleteDir(it->string, OS_NO_FLAGS);
|
||||
}
|
||||
OS_Result result = OS_DeleteDir(path, OS_NO_FLAGS);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
BOOL success = RemoveDirectoryW(wpath);
|
||||
OS_Result result = OS_SUCCESS;
|
||||
if (success == 0)
|
||||
result = OS_PATH_NOT_FOUND;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) {
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
OS_Result result = OS_FAILURE;
|
||||
|
||||
DWORD access = GENERIC_WRITE;
|
||||
DWORD creation_disposition = CREATE_ALWAYS;
|
||||
if (append) {
|
||||
access = FILE_APPEND_DATA;
|
||||
creation_disposition = OPEN_ALWAYS;
|
||||
}
|
||||
|
||||
HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
DWORD bytes_written = 0;
|
||||
IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
|
||||
BOOL error = WriteFile(handle, data.str, (DWORD)data.len, &bytes_written, NULL);
|
||||
if (error == TRUE) {
|
||||
if (bytes_written == data.len) {
|
||||
result = OS_SUCCESS;
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
} else result = OS_PATH_NOT_FOUND;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) {
|
||||
return OS__WriteFile(path, string, true);
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) {
|
||||
return OS__WriteFile(path, string, false);
|
||||
}
|
||||
|
||||
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) {
|
||||
bool success = false;
|
||||
S8_String result = S8_MakeEmpty();
|
||||
MA_Temp checkpoint = MA_BeginTemp(arena);
|
||||
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
|
||||
HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
LARGE_INTEGER file_size;
|
||||
if (GetFileSizeEx(handle, &file_size)) {
|
||||
if (file_size.QuadPart != 0) {
|
||||
result.len = (int64_t)file_size.QuadPart;
|
||||
result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1);
|
||||
DWORD read;
|
||||
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
|
||||
if (read == result.len) {
|
||||
success = true;
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
result = S8_MakeEmpty();
|
||||
MA_EndTemp(checkpoint);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API int64_t OS_GetFileModTime(S8_String file) {
|
||||
FILETIME time = {0};
|
||||
WIN32_FIND_DATAW data;
|
||||
|
||||
wchar_t wpath[1024];
|
||||
UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len);
|
||||
HANDLE handle = FindFirstFileW(wpath, &data);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
FindClose(handle);
|
||||
time = data.ftLastWriteTime;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Date OS_GetDate(void) {
|
||||
SYSTEMTIME local;
|
||||
GetLocalTime(&local);
|
||||
|
||||
OS_Date result = {0};
|
||||
result.year = local.wYear;
|
||||
result.month = local.wMonth;
|
||||
result.day = local.wDay;
|
||||
result.hour = local.wHour;
|
||||
result.second = local.wSecond;
|
||||
// result.milliseconds = local.wMilliseconds;
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif __linux__ || __APPLE__ || __unix__
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#if OS_MAC
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
|
||||
char buf[PATH_MAX];
|
||||
uint32_t bufsize = PATH_MAX;
|
||||
if (_NSGetExecutablePath(buf, &bufsize)) {
|
||||
return S8_MakeEmpty();
|
||||
}
|
||||
|
||||
S8_String result = S8_Copy(arena, S8_MakeFromChar(buf));
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
|
||||
char buffer[PATH_MAX] = {};
|
||||
if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) {
|
||||
return S8_MakeEmpty();
|
||||
}
|
||||
S8_String result = S8_Copy(arena, S8_MakeFromChar(buffer));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
OS_API bool OS_EnableTerminalColors(void) { return true; }
|
||||
|
||||
OS_API bool OS_IsAbsolute(S8_String path) {
|
||||
bool result = path.len >= 1 && path.str[0] == '/';
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String path = OS_GetExePath(scratch.arena);
|
||||
S8_String dir = S8_ChopLastSlash(path);
|
||||
S8_String copy = S8_Copy(arena, dir);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return copy;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
|
||||
char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
|
||||
char *cwd = getcwd(buffer, PATH_MAX);
|
||||
S8_String result = S8_MakeFromChar(cwd);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API void OS_SetWorkingDir(S8_String path) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String copy = S8_Copy(scratch.arena, path);
|
||||
chdir(copy.str);
|
||||
MA_ReleaseScratch(scratch);
|
||||
}
|
||||
|
||||
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
|
||||
MA_Temp scratch = MA_GetScratch1(arena);
|
||||
S8_String copy = S8_Copy(scratch.arena, relative);
|
||||
|
||||
char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
|
||||
realpath((char *)copy.str, buffer);
|
||||
S8_String result = S8_MakeFromChar(buffer);
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API bool OS_FileExists(S8_String path) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String copy = S8_Copy(scratch.arena, path);
|
||||
|
||||
bool result = false;
|
||||
if (access((char *)copy.str, F_OK) == 0) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API bool OS_IsDir(S8_String path) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String copy = S8_Copy(scratch.arena, path);
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy.str, &s) != 0)
|
||||
return false;
|
||||
|
||||
bool result = S_ISDIR(s.st_mode);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API bool OS_IsFile(S8_String path) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_String copy = S8_Copy(scratch.arena, path);
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy.str, &s) != 0)
|
||||
return false;
|
||||
bool result = S_ISREG(s.st_mode);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API double OS_GetTime(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
uint64_t timeu64 = (((uint64_t)ts.tv_sec) * 1000000ull) + ((uint64_t)ts.tv_nsec) / 1000ull;
|
||||
double timef = (double)timeu64;
|
||||
double result = timef / 1000000.0; // Microseconds to seconds
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API bool OS_IsValid(OS_FileIter it) {
|
||||
return it.is_valid;
|
||||
}
|
||||
|
||||
OS_API void OS_Advance(OS_FileIter *it) {
|
||||
struct dirent *file = 0;
|
||||
while ((file = readdir((DIR *)it->dir)) != NULL) {
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
|
||||
|
||||
it->is_directory = file->d_type == DT_DIR;
|
||||
it->filename = S8_CopyChar(it->arena, file->d_name);
|
||||
|
||||
const char *dir_char_ending = it->is_directory ? "/" : "";
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->relative_path = S8_Format(it->arena, "%.*s%s%s%s", S8_Expand(it->path), separator, file->d_name, dir_char_ending);
|
||||
it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path);
|
||||
if (it->is_directory) it->absolute_path = S8_Format(it->arena, "%.*s/", S8_Expand(it->absolute_path));
|
||||
it->is_valid = true;
|
||||
return;
|
||||
}
|
||||
it->is_valid = false;
|
||||
closedir((DIR *)it->dir);
|
||||
}
|
||||
|
||||
OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) {
|
||||
OS_FileIter it = {0};
|
||||
it.arena = arena;
|
||||
it.path = path = S8_Copy(arena, path);
|
||||
it.dir = (void *)opendir((char *)path.str);
|
||||
if (!it.dir) return it;
|
||||
|
||||
OS_Advance(&it);
|
||||
return it;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_MakeDir(S8_String path) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
path = S8_Copy(scratch.arena, path);
|
||||
int error = mkdir(path.str, 0755);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return error == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) {
|
||||
const char *ow = overwrite ? "-n" : "";
|
||||
int result = OS_SystemF("cp %s %.*s %.*s", ow, S8_Expand(from), S8_Expand(to));
|
||||
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_DeleteFile(S8_String path) {
|
||||
int result = OS_SystemF("rm %.*s", S8_Expand(path));
|
||||
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
|
||||
IO_Assert(flags & OS_RECURSIVE);
|
||||
int result = OS_SystemF("rm -r %.*s", S8_Expand(path));
|
||||
return result == 0 ? OS_SUCCESS : OS_FAILURE;
|
||||
}
|
||||
|
||||
OS_API int64_t OS_GetFileModTime(S8_String file) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
file = S8_Copy(scratch.arena, file);
|
||||
|
||||
struct stat attrib = {};
|
||||
stat(file.str, &attrib);
|
||||
struct timespec ts = attrib.IF_LINUX_ELSE(st_mtim, st_mtimespec);
|
||||
int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll;
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Date OS_GetDate(void) {
|
||||
time_t t = time(NULL);
|
||||
struct tm date = *localtime(&t);
|
||||
|
||||
OS_Date s = {0};
|
||||
s.second = date.tm_sec;
|
||||
s.year = date.tm_year;
|
||||
s.month = date.tm_mon;
|
||||
s.day = date.tm_mday;
|
||||
s.hour = date.tm_hour;
|
||||
s.minute = date.tm_min;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
path = S8_Copy(scratch.arena, path);
|
||||
|
||||
OS_Result result = OS_FAILURE;
|
||||
FILE *f = fopen((const char *)path.str, "a");
|
||||
if (f) {
|
||||
result = OS_SUCCESS;
|
||||
|
||||
size_t written = fwrite(string.str, 1, string.len, f);
|
||||
if (written < string.len) {
|
||||
result = OS_FAILURE;
|
||||
}
|
||||
|
||||
int error = fclose(f);
|
||||
if (error != 0) {
|
||||
result = OS_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) {
|
||||
S8_String result = {};
|
||||
|
||||
// ftell returns insane size if file is
|
||||
// a directory **on some machines** KEKW
|
||||
if (OS_IsDir(path)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_Temp scratch = MA_GetScratch1(arena);
|
||||
path = S8_Copy(scratch.arena, path);
|
||||
|
||||
FILE *f = fopen(path.str, "rb");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
result.len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1);
|
||||
fread(result.str, result.len, 1, f);
|
||||
result.str[result.len] = 0;
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
path = S8_Copy(scratch.arena, path);
|
||||
|
||||
OS_Result result = OS_FAILURE;
|
||||
FILE *f = fopen((const char *)path.str, "w");
|
||||
if (f) {
|
||||
result = OS_SUCCESS;
|
||||
|
||||
size_t written = fwrite(string.str, 1, string.len, f);
|
||||
if (written < string.len) {
|
||||
result = OS_FAILURE;
|
||||
}
|
||||
|
||||
int error = fclose(f);
|
||||
if (error != 0) {
|
||||
result = OS_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
MA_ReleaseScratch(scratch);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if _WIN32 || __linux__ || __APPLE__ || __unix__
|
||||
OS_API int OS_SystemF(const char *string, ...) {
|
||||
MA_Temp scratch = MA_GetScratch();
|
||||
S8_FORMAT(scratch.arena, string, result);
|
||||
IO_Printf("Executing: %.*s\n", S8_Expand(result));
|
||||
fflush(stdout);
|
||||
int error_code = system(result.str);
|
||||
MA_ReleaseScratch(scratch);
|
||||
return error_code;
|
||||
}
|
||||
|
||||
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) {
|
||||
S8_String c = OS_ReadFile(arena, filepath);
|
||||
if (c.str == 0) return false;
|
||||
S8_String path = S8_ChopLastSlash(filepath);
|
||||
S8_String include = S8_Lit("#include \"");
|
||||
for (;;) {
|
||||
int64_t idx = -1;
|
||||
if (S8_Seek(c, include, 0, &idx)) {
|
||||
S8_String str_to_add = S8_GetPrefix(c, idx);
|
||||
S8_AddNode(arena, out, str_to_add);
|
||||
S8_String save = c;
|
||||
c = S8_Skip(c, idx + include.len);
|
||||
|
||||
S8_String filename = c;
|
||||
filename.len = 0;
|
||||
while (filename.str[filename.len] != '"' && filename.len < c.len) {
|
||||
filename.len += 1;
|
||||
}
|
||||
|
||||
c = S8_Skip(c, filename.len + 1);
|
||||
S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename));
|
||||
if (!OS_ExpandIncludesList(arena, out, inc_path)) {
|
||||
S8_String s = S8_GetPrefix(save, save.len - c.len);
|
||||
S8_AddNode(arena, out, s);
|
||||
}
|
||||
} else {
|
||||
S8_AddNode(arena, out, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath) {
|
||||
S8_List out = S8_MakeEmptyList();
|
||||
S8_String result = S8_MakeEmpty();
|
||||
MA_ScratchScope(s) {
|
||||
OS_ExpandIncludesList(s.arena, &out, filepath);
|
||||
result = S8_Merge(arena, out);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
73
src/build_tool/core/filesystem.h
Normal file
73
src/build_tool/core/filesystem.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Quick and dirty filesystem operations
|
||||
|
||||
#ifndef OS_API
|
||||
#define OS_API
|
||||
#endif
|
||||
|
||||
typedef enum OS_Result {
|
||||
OS_SUCCESS,
|
||||
OS_ALREADY_EXISTS,
|
||||
OS_PATH_NOT_FOUND,
|
||||
OS_FAILURE,
|
||||
} OS_Result;
|
||||
|
||||
enum {
|
||||
OS_NO_FLAGS = 0,
|
||||
OS_RECURSIVE = 1,
|
||||
OS_RELATIVE_PATHS = 2,
|
||||
};
|
||||
|
||||
typedef struct OS_Date OS_Date;
|
||||
struct OS_Date {
|
||||
uint32_t year;
|
||||
uint32_t month;
|
||||
uint32_t day;
|
||||
uint32_t hour;
|
||||
uint32_t minute;
|
||||
uint32_t second;
|
||||
};
|
||||
|
||||
typedef struct OS_FileIter OS_FileIter;
|
||||
struct OS_FileIter {
|
||||
bool is_valid;
|
||||
bool is_directory;
|
||||
S8_String absolute_path;
|
||||
S8_String relative_path;
|
||||
S8_String filename;
|
||||
|
||||
S8_String path;
|
||||
MA_Arena *arena;
|
||||
union {
|
||||
struct OS_Win32_FileIter *w32;
|
||||
void *dir;
|
||||
};
|
||||
};
|
||||
|
||||
OS_API bool OS_IsAbsolute(S8_String path);
|
||||
OS_API S8_String OS_GetExePath(MA_Arena *arena);
|
||||
OS_API S8_String OS_GetExeDir(MA_Arena *arena);
|
||||
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena);
|
||||
OS_API void OS_SetWorkingDir(S8_String path);
|
||||
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative);
|
||||
OS_API bool OS_FileExists(S8_String path);
|
||||
OS_API bool OS_IsDir(S8_String path);
|
||||
OS_API bool OS_IsFile(S8_String path);
|
||||
OS_API double OS_GetTime(void);
|
||||
OS_API OS_Result OS_MakeDir(S8_String path);
|
||||
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite);
|
||||
OS_API OS_Result OS_DeleteFile(S8_String path);
|
||||
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags);
|
||||
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string);
|
||||
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string);
|
||||
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path);
|
||||
OS_API int OS_SystemF(const char *string, ...);
|
||||
OS_API int64_t OS_GetFileModTime(S8_String file);
|
||||
OS_API OS_Date OS_GetDate(void);
|
||||
OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize);
|
||||
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath);
|
||||
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath);
|
||||
OS_API bool OS_EnableTerminalColors(void);
|
||||
|
||||
OS_API bool OS_IsValid(OS_FileIter it);
|
||||
OS_API void OS_Advance(OS_FileIter *it);
|
||||
OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path);
|
||||
204
src/build_tool/core/table.hpp
Normal file
204
src/build_tool/core/table.hpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Hash table implementation:
|
||||
Pointers to values
|
||||
Open adressing
|
||||
Linear Probing
|
||||
Power of 2
|
||||
Robin Hood hashing
|
||||
Resizes on high probe count (min max load factor)
|
||||
|
||||
Hash 0 is reserved for empty hash table entry
|
||||
*/
|
||||
|
||||
template <class Value>
|
||||
struct Table {
|
||||
struct Entry {
|
||||
uint64_t hash;
|
||||
uint64_t key;
|
||||
size_t distance;
|
||||
Value value;
|
||||
};
|
||||
|
||||
M_Allocator allocator;
|
||||
size_t len, cap;
|
||||
Entry *values;
|
||||
|
||||
static const size_t max_load_factor = 80;
|
||||
static const size_t min_load_factor = 50;
|
||||
static const size_t significant_distance = 8;
|
||||
|
||||
// load factor calculation was rearranged
|
||||
// to get rid of division:
|
||||
//> 100 * len / cap = load_factor
|
||||
//> len * 100 = load_factor * cap
|
||||
inline bool reached_load_factor(size_t lfactor) {
|
||||
return (len + 1) * 100 >= lfactor * cap;
|
||||
}
|
||||
|
||||
inline bool is_empty(Entry *entry) { return entry->hash == 0; }
|
||||
inline bool is_occupied(Entry *entry) { return entry->hash != 0; }
|
||||
|
||||
void reserve(size_t size) {
|
||||
IO_Assert(size > cap && "New size is smaller then original size");
|
||||
IO_Assert(MA_IS_POW2(size));
|
||||
if (!allocator.p) allocator = M_GetSystemAllocator();
|
||||
|
||||
Entry *old_values = values;
|
||||
size_t old_cap = cap;
|
||||
|
||||
values = (Entry *)M_Alloc(allocator, sizeof(Entry) * size);
|
||||
for (int i = 0; i < size; i += 1) values[i] = {};
|
||||
cap = size;
|
||||
|
||||
IO_Assert(!(old_values == 0 && len != 0));
|
||||
if (len == 0) {
|
||||
if (old_values) M_Dealloc(allocator, old_values);
|
||||
return;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (size_t i = 0; i < old_cap; i += 1) {
|
||||
Entry *it = old_values + i;
|
||||
if (is_occupied(it)) {
|
||||
insert(it->key, it->value);
|
||||
}
|
||||
}
|
||||
M_Dealloc(allocator, old_values);
|
||||
}
|
||||
|
||||
Entry *get_table_entry(uint64_t key) {
|
||||
if (len == 0) return 0;
|
||||
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||
if (hash == 0) hash += 1;
|
||||
uint64_t index = WRAP_AROUND_POWER_OF_2(hash, cap);
|
||||
uint64_t i = index;
|
||||
uint64_t distance = 0;
|
||||
for (;;) {
|
||||
Entry *it = values + i;
|
||||
if (distance > it->distance) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (it->hash == hash && it->key == key) {
|
||||
return it;
|
||||
}
|
||||
|
||||
distance += 1;
|
||||
i = WRAP_AROUND_POWER_OF_2(i + 1, cap);
|
||||
if (i == index) return 0;
|
||||
}
|
||||
IO_Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
void insert(uint64_t key, const Value &value) {
|
||||
if (reached_load_factor(max_load_factor)) {
|
||||
if (cap == 0) cap = 16; // 32 cause cap*2
|
||||
reserve(cap * 2);
|
||||
}
|
||||
|
||||
uint64_t hash = HashBytes(&key, sizeof(key));
|
||||
if (hash == 0) hash += 1;
|
||||
uint64_t index = WRAP_AROUND_POWER_OF_2(hash, cap);
|
||||
uint64_t i = index;
|
||||
Entry to_insert = {hash, key, 0, value};
|
||||
for (;;) {
|
||||
Entry *it = values + i;
|
||||
if (is_empty(it)) {
|
||||
*it = to_insert;
|
||||
len += 1;
|
||||
// If we have more then 8 consecutive items we try to resize
|
||||
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||
reserve(cap * 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (it->hash == hash && it->key == key) {
|
||||
*it = to_insert;
|
||||
// If we have more then 8 consecutive items we try to resize
|
||||
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
|
||||
reserve(cap * 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Robin hood hashing
|
||||
if (to_insert.distance > it->distance) {
|
||||
Entry temp = to_insert;
|
||||
to_insert = *it;
|
||||
*it = temp;
|
||||
}
|
||||
|
||||
to_insert.distance += 1;
|
||||
i = WRAP_AROUND_POWER_OF_2(i + 1, cap);
|
||||
IO_Assert(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
|
||||
}
|
||||
IO_Assert(!"Invalid codepath");
|
||||
}
|
||||
|
||||
void remove(uint64_t key) {
|
||||
Entry *entry = get_table_entry(key);
|
||||
entry->hash = 0;
|
||||
entry->distance = 0;
|
||||
len -= 1;
|
||||
}
|
||||
|
||||
Value *get(uint64_t key) {
|
||||
Entry *v = get_table_entry(key);
|
||||
if (!v) return 0;
|
||||
return &v->value;
|
||||
}
|
||||
|
||||
Value get(uint64_t key, Value default_value) {
|
||||
Entry *v = get_table_entry(key);
|
||||
if (!v) return default_value;
|
||||
return v->value;
|
||||
}
|
||||
|
||||
Value *gets(char *str) {
|
||||
int len = S8_Length(str);
|
||||
uint64_t hash = HashBytes(str, len);
|
||||
return get(hash);
|
||||
}
|
||||
|
||||
Value gets(char *str, Value default_value) {
|
||||
int len = S8_Length(str);
|
||||
uint64_t hash = HashBytes(str, len);
|
||||
return get(hash, default_value);
|
||||
}
|
||||
|
||||
Value *get(S8_String s) {
|
||||
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
|
||||
return get(hash);
|
||||
}
|
||||
|
||||
Value get(S8_String s, Value default_value) {
|
||||
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
|
||||
return get(hash, default_value);
|
||||
}
|
||||
|
||||
void put(S8_String s, const Value &value) {
|
||||
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
|
||||
insert(hash, value);
|
||||
}
|
||||
|
||||
void puts(char *str, const Value &value) {
|
||||
int len = S8_Length(str);
|
||||
uint64_t hash = HashBytes(str, len);
|
||||
insert(hash, value);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
len = 0;
|
||||
for (size_t i = 0; i < cap; i += 1) {
|
||||
Entry *it = values + i;
|
||||
it->hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dealloc() {
|
||||
M_Dealloc(allocator, values);
|
||||
len = 0;
|
||||
cap = 0;
|
||||
values = 0;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user