Start refactor, restructure basic

This commit is contained in:
Krzosa Karol
2025-11-27 09:00:10 +01:00
parent 38b9b7df10
commit 781c2dc53c
30 changed files with 5716 additions and 3490 deletions

View File

@@ -13,19 +13,17 @@ if not exist "luaunity.obj" (
cl -Zi -nologo -I../src/external/lua/src -I../src/external/glad ../src/external/luaunity.c ../src/external/glad/glad.c -c
)
if not exist "metaprogram.exe" (
cl /WX /W3 /wd4200 /diagnostics:column -FC -Zi -nologo -Fe:metaprogram.exe -I../src ../src/metaprogram/metaprogram.cpp ../src/basic/win32.cpp
)
cl /WX /W3 /wd4200 /diagnostics:column -FC -Zi -nologo -Fe:metaprogram.exe -I../src ../src/metaprogram/metaprogram.cpp
metaprogram.exe
set sdl=../src/external/SDL/win32-static/SDL3-static.lib ../src/external/SDL/win32-static/SDL_uclibc.lib
cl /EHsc- /MD /Zi /FC /nologo /WX /W3 /wd4200 /diagnostics:column %profile_flags% ../src/text_editor/text_editor.cpp ../src/basic/win32.cpp -Fe:te.exe -I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad -I../src/ luaunity.obj glad.obj kernel32.lib gdi32.lib user32.lib Imm32.lib ole32.lib Shell32.lib OleAut32.lib Cfgmgr32.lib Setupapi.lib Advapi32.lib version.lib winmm.lib %sdl% -link /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:MSVCRTD
rem set sdl=../src/external/SDL/win32-static/SDL3-static.lib ../src/external/SDL/win32-static/SDL_uclibc.lib
rem cl /EHsc- /MD /Zi /FC /nologo /WX /W3 /wd4200 /diagnostics:column %profile_flags% ../src/text_editor/text_editor.cpp ../src/basic/win32.cpp -Fe:te.exe -I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad -I../src/ luaunity.obj glad.obj kernel32.lib gdi32.lib user32.lib Imm32.lib ole32.lib Shell32.lib OleAut32.lib Cfgmgr32.lib Setupapi.lib Advapi32.lib version.lib winmm.lib %sdl% -link /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:MSVCRTD
if "%1"=="release" (
copy te.exe ..\data\te.exe
echo written ..\data\te.exe
) else (
copy te.exe ..\data\te_debug.exe
echo written ..\data\te_debug.exe
)
rem if "%1"=="release" (
rem copy te.exe ..\data\te.exe
rem echo written ..\data\te.exe
rem ) else (
rem copy te.exe ..\data\te_debug.exe
rem echo written ..\data\te_debug.exe
rem )

9
src/basic/basic.cpp Normal file
View File

@@ -0,0 +1,9 @@
#define STB_SPRINTF_IMPLEMENTATION
#include "stb_sprintf.h"
#include "basic_string.cpp"
#include "basic_string16.cpp"
#include "basic_unicode.cpp"
#include "basic_alloc.cpp"
#include "basic_os.cpp"
#include "math.cpp"
#include "math_int.cpp"

File diff suppressed because it is too large Load Diff

248
src/basic/basic_alloc.cpp Normal file
View File

@@ -0,0 +1,248 @@
#if defined(USE_ADDRESS_SANITIZER)
#include <sanitizer/asan_interface.h>
#endif
#if !defined(ASAN_POISON_MEMORY_REGION)
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#else
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
#endif
#if OS_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
void *VReserve(size_t size) {
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return result;
}
bool VCommit(void *p, size_t size) {
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
return result ? true : false;
}
bool VRelease(void *p, size_t size) {
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
return result ? true : false;
}
bool VDecommit(void *p, size_t size) {
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
return result ? true : false;
}
#elif OS_LINUX || OS_MAC
void *VReserve(size_t size) {
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
return result == (void *)-1 ? 0 : result;
}
bool VCommit(void *p, size_t size) {
int result = mprotect(p, size, PROT_READ | PROT_WRITE);
return result == 0;
}
bool VRelease(void *p, size_t size) {
int result = munmap(p, size);
return result == 0;
}
bool VDecommit(void *p, size_t size) {
mprotect(p, size, PROT_NONE);
madvise(p, size, MADV_DONTNEED);
return true;
}
#else
void *VReserve(size_t size) {
InvalidCodepath();
return NULL;
}
bool VCommit(void *p, size_t size) {
InvalidCodepath();
return false;
}
bool VRelease(void *p, size_t size) {
InvalidCodepath();
return false;
}
bool VDecommit(void *p, size_t size) {
InvalidCodepath();
return false;
}
#endif
void InitArena(Arena *arena, size_t reserve) {
reserve = AlignUp(reserve, PAGE_SIZE);
arena->align = DEFAULT_ALIGNMENT;
arena->data = (uint8_t *)VReserve(reserve);
if (arena->data) {
arena->reserve = reserve;
}
}
Arena *AllocArena(Allocator allocator, size_t size) {
Arena *result = AllocType(allocator, Arena);
result->data = (uint8_t *)AllocSize(allocator, size);
result->reserve = size;
result->commit = size;
result->align = DEFAULT_ALIGNMENT;
return result;
}
Arena *AllocArena(size_t reserve) {
Arena *result = NULL;
void *data = VReserve(reserve);
if (!data) return result;
bool success = VCommit(data, PAGE_SIZE);
if (!success) {
VRelease(data, reserve);
return result;
}
result = (Arena *)data;
result->data = (uint8_t *)data;
result->reserve = reserve;
result->commit = PAGE_SIZE;
result->len = result->base_len = sizeof(Arena);
result->align = DEFAULT_ALIGNMENT;
return result;
}
void *PushSize(Arena *arena, size_t size) {
// base_len is used for bootstraping arenas, it denotes the
// space occupied by the arena. If len is smaller then base_len then
// we start to overwrite the arena itself - pure barbarism.
Assert(arena->len >= arena->base_len);
size_t align_offset = 0;
if (arena->align) {
align_offset = GetAlignOffset((uintptr_t)arena->data + arena->len, arena->align);
}
size_t size_with_alignment = size + align_offset;
size_t new_len = arena->len + size_with_alignment;
if (new_len > arena->commit) {
size_t new_len_aligned_to_page_size = AlignUp(new_len, PAGE_SIZE);
size_t to_commit = new_len_aligned_to_page_size - arena->commit;
size_t to_commit_clamped = ClampTop(to_commit, arena->reserve);
if (to_commit_clamped > 0) {
bool success = VCommit(arena->data + arena->commit, to_commit_clamped);
if (success) {
MA_ASAN_UNPOISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped);
arena->commit += to_commit_clamped;
}
}
if (new_len > arena->commit) {
return NULL;
}
}
uint8_t *result = arena->data + arena->len + align_offset;
arena->len = new_len;
MA_ASAN_UNPOISON_MEMORY_REGION(result, size);
return (void *)result;
}
void Release(Arena *arena) {
if (arena == NULL || arena->data == NULL) return;
bool zero_memory = (uint8_t *)arena != arena->data;
VRelease(arena->data, arena->reserve);
if (zero_memory) MemoryZero(arena, sizeof(Arena));
}
void PopToPos(Arena *arena, size_t pos) {
// base_len is used for bootstraping arenas, it denotes the
// space occupied by the arena. If len is smaller then base_len then
// we start to overwrite the arena itself - pure barbarism.
Assert(arena->len >= arena->base_len);
pos = Clamp(pos, arena->base_len, arena->len);
size_t size = arena->len - pos;
arena->len = pos;
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size);
}
void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
if (kind == AllocatorKind_Allocate) {
return PushSize((Arena *)object, size);
} else if (AllocatorKind_Deallocate) {
} else {
Assert(!"invalid codepath");
}
return NULL;
}
thread_local Arena *ScratchArenaPool[4];
#if OS_WASM
void InitScratch() {
Allocator sys_allocator = GetSystemAllocator();
ScratchArenaPool[0] = AllocArena(sys_allocator, MiB(16));
ScratchArenaPool[1] = AllocArena(sys_allocator, MiB(8));
ScratchArenaPool[3] = AllocArena(sys_allocator, MiB(2));
ScratchArenaPool[3] = AllocArena(sys_allocator, MiB(1));
}
#else
void InitScratch() {
for (int i = 0; i < Lengthof(ScratchArenaPool); i += 1) {
ScratchArenaPool[i] = AllocArena();
}
}
#endif
TempArena GetScratchEx(Arena **conflicts, int conflict_count) {
Arena *unoccupied = 0;
for (int i = 0; i < Lengthof(ScratchArenaPool); i += 1) {
Arena *from_pool = ScratchArenaPool[i];
unoccupied = from_pool;
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
Arena *from_conflict = conflicts[conflict_i];
if (from_pool == from_conflict) {
unoccupied = 0;
break;
}
}
if (unoccupied) {
break;
}
}
// Failed to get free scratch memory, this is a fatal error, this shouldnt happen
Assert(unoccupied);
TempArena result = BeginTemp(unoccupied);
return result;
}
#include <stdlib.h>
void *SystemAllocator_Alloc(void *object, int kind, void *p, size_t size) {
void *result = NULL;
if (kind == AllocatorKind_Allocate) {
result = malloc(size);
Assert(result);
} else if (kind == AllocatorKind_Deallocate) {
free(p);
}
return result;
}
Allocator GetSystemAllocator() {
Allocator result = {SystemAllocator_Alloc};
return result;
}

95
src/basic/basic_alloc.h Normal file
View File

@@ -0,0 +1,95 @@
#pragma once
const int AllocatorKind_Allocate = 1;
const int AllocatorKind_Deallocate = 2;
struct Allocator {
void *(*proc)(void *object, int kind, void *p, size_t size);
void *object;
};
#define AllocType(alo, Type) (Type *)AllocSize(alo, sizeof(Type))
#define AllocArray(alo, Type, count) (Type *)AllocSize(alo, sizeof(Type) * (count))
inline void *AllocSize(Allocator alo, size_t size) {
void *result = alo.proc(alo.object, AllocatorKind_Allocate, NULL, size);
memset(result, 0, size);
return result;
}
template <class T>
void Dealloc(Allocator alo, T **p) {
if (*p == NULL) return;
alo.proc(alo.object, AllocatorKind_Deallocate, *p, 0);
*p = NULL;
}
#define DeallocEx(alo, p) (alo).proc((alo).object, AllocatorKind_Deallocate, (p), 0);
Allocator GetSystemAllocator();
#define MemoryZero(x, size) memset(x, 0, size)
#define MemoryCopy(dst, src, size) memcpy(dst, src, size)
#define MemoryMove(dst, src, size) memmove(dst, src, size)
const int PAGE_SIZE = 4096;
const int DEFAULT_ALIGNMENT = sizeof(void *);
struct Arena {
uint8_t *data;
size_t len;
size_t base_len; // to prevent self deleting the arena
size_t reserve;
size_t commit;
size_t align;
operator Allocator() {
void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size);
return {ArenaAllocatorProc, this};
}
};
struct TempArena {
Arena *arena;
size_t len;
};
inline void SetLen(Arena *arena, size_t len) { arena->len = Clamp(len, arena->base_len, arena->len); }
inline void Pop(Arena *arena, size_t size) { SetLen(arena, arena->len - size); }
inline TempArena BeginTemp(Arena *arena) { return {arena, arena->len}; }
inline void EndTemp(TempArena temp) { SetLen(temp.arena, temp.len); }
inline void Clear(Arena *arena) { SetLen(arena, 0); }
void *VReserve(size_t size);
bool VCommit(void *p, size_t size);
bool VRelease(void *p, size_t size);
bool VDecommit(void *p, size_t size);
void InitArena(Arena *arena, size_t reserve = MiB(256));
Arena *AllocArena(size_t reserve = MiB(256));
Arena *AllocArena(Allocator allocator, size_t size);
void *PushSize(Arena *arena, size_t size);
void Release(Arena *arena);
void InitScratch();
TempArena GetScratchEx(Arena **conflicts, int conflict_count);
inline TempArena GetScratch(Arena *c1 = NULL, Arena *c2 = NULL) {
int count = c1 ? 1 : 0;
count += c2 ? 1 : 0;
Arena *conflicts[] = {c1, c2};
return GetScratchEx(conflicts, count);
}
struct Scratch {
TempArena checkpoint;
Scratch() { this->checkpoint = GetScratch(); }
Scratch(Arena *conflict) { this->checkpoint = GetScratch(conflict); }
Scratch(Arena *c1, Arena *c2) { this->checkpoint = GetScratch(c1, c2); }
Scratch(Allocator conflict) { this->checkpoint = GetScratch((Arena *)conflict.object); }
Scratch(Allocator c1, Allocator c2) { this->checkpoint = GetScratch((Arena *)c1.object, (Arena *)c2.object); }
~Scratch() { EndTemp(checkpoint); }
operator Arena *() { return checkpoint.arena; }
operator Allocator() { return *checkpoint.arena; }
private: // @Note: Disable copy constructors, cause its error prone
Scratch(Scratch &arena);
Scratch(Scratch &arena, Scratch &a2);
};

497
src/basic/basic_array.h Normal file
View File

@@ -0,0 +1,497 @@
#pragma once
/*
// Iterating and removing elements
for (int i = 0; i < array.len; i += 1) {
auto &it = array[i];
bool remove_item = false;
defer {
if (remove_item) {
array.ordered_remove(it);
i -= 1;
}
}
}
// Simple delete
IterRemove(arr) {
IterRemovePrepare(arr);
remove_item = true;
}
// Deleting backwards
For(arr.reverse_iter()) {
defer{ arr.unordered_remove(it); };
}
*/
#define IterRemove(a) for (int i = 0; i < (a).len; i += 1)
#define IterRemovePrepare(a) \
auto &it = (a)[i]; \
bool remove_item = false; \
defer { \
if (remove_item) { \
Remove(&(a), it); \
i -= 1; \
} \
}
#define ForItem(it, array) for (auto &it : (array))
#define For(array) ForItem(it, array)
template <class T>
struct Slice {
T *data;
int64_t len;
Slice() = default;
Slice(T *s, int64_t l) : data(s), len(l) {}
T &operator[](int64_t index) {
Assert(index < len);
return data[index];
}
T *begin() { return data; }
T *end() { return data + len; }
};
template <class T>
Slice<T> Copy(Allocator alo, Slice<T> array) {
Slice<T> result = {};
result.data = AllocArray(alo, T, array.len);
memcpy(result.data, array.data, sizeof(T) * array.len);
result.len = array.len;
return result;
}
template <class T>
T Pop(Slice<T> *arr) {
Assert(arr->len > 0);
return arr->data[--arr->len];
}
template <class T>
bool Contains(Slice<T> &arr, T item) {
For(arr) if (it == item) return true;
return false;
}
template <class T>
int64_t GetIndex(Slice<T> &arr, const T &item) {
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
Assert(index >= 0 && index < arr.len);
return (int64_t)index;
}
template <class T>
T Get(Slice<T> &arr, int64_t i, T default_value = {}) {
T result = default_value;
if (i >= 0 && i < arr.len) result = arr[i];
return result;
}
template <class T>
bool IsLast(Slice<T> &arr, T &item) {
bool result = arr.last() == &item;
return result;
}
template <class T>
bool IsFirst(Slice<T> &arr, T &item) {
bool result = arr.first() == &item;
return result;
}
template <class T>
T *GetFirst(Slice<T> &arr) {
Assert(arr.len > 0);
return arr.data;
}
template <class T>
T *GetLast(Slice<T> &arr) {
Assert(arr.len > 0);
return arr.data + arr.len - 1;
}
template <class T>
Slice<T> Chop(Slice<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data, arr.len - len};
return result;
}
template <class T>
Slice<T> Skip(Slice<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data + len, arr.len - len};
return result;
}
template <class T>
Slice<T> GetPostfix(Slice<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
int64_t remain_len = arr.len - len;
Slice<T> result = {arr.data + remain_len, len};
return result;
}
template <class T>
Slice<T> GetPrefix(Slice<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data, len};
return result;
}
template <class T>
Slice<T> GetSlice(Slice<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
// Negative indexes work in python style, they return you the index counting from end of list
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
if (first_index == SLICE_LAST) first_index = arr.len;
if (first_index < 0) first_index = arr.len + first_index;
Slice<T> result = {arr.data, arr.len};
if (arr.len > 0) {
if (one_past_last_index > first_index) {
first_index = ClampTop(first_index, arr.len - 1);
one_past_last_index = ClampTop(one_past_last_index, arr.len);
result.data += first_index;
result.len = one_past_last_index - first_index;
} else {
result.len = 0;
}
}
return result;
}
// Make arrays resize on every item
#define ARRAY_DEBUG 0
#if ARRAY_DEBUG
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) IF
#else
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) ELSE
#endif
template <class T>
struct Array {
Allocator allocator;
int64_t cap;
union {
Slice<T> slice;
struct {
T *data;
int64_t len;
};
};
T &operator[](int64_t index) {
Assert(index >= 0 && index < len);
return data[index];
}
T *begin() { return data; }
T *end() { return data + len; }
};
template <class T>
T *GetFirst(Array<T> &arr) {
Assert(arr.len > 0);
return arr.data;
}
template <class T>
T *GetLast(Array<T> &arr) {
Assert(arr.len > 0);
return arr.data + arr.len - 1;
}
template <class T>
void Reserve(Array<T> *arr, int64_t size) {
if (size > arr->cap) {
if (!arr->allocator.proc) arr->allocator = GetSystemAllocator();
T *new_data = AllocArray(arr->allocator, T, size);
Assert(new_data);
memcpy(new_data, arr->data, arr->len * sizeof(T));
Dealloc(arr->allocator, &arr->data);
arr->data = new_data;
arr->cap = size;
}
}
template <class T>
void TryGrowing(Array<T> *arr) {
if (arr->len + 1 > arr->cap) {
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
int64_t new_size = ClampBottom(initial_size, arr->cap ARRAY_IF_DEBUG_ELSE(+1, *2));
Reserve(arr, new_size);
}
}
template <class T>
void TryGrowing(Array<T> *arr, int64_t item_count) {
if (arr->len + item_count > arr->cap) {
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
int64_t new_size = ClampBottom(initial_size, (arr->cap + item_count) ARRAY_IF_DEBUG_ELSE(+1, *2));
Reserve(arr, new_size);
}
}
template <class T>
void Add(Array<T> *arr, T item) {
TryGrowing(arr);
arr->data[arr->len++] = item;
}
template <class T>
void Add(Array<T> *arr, Array<T> &another) {
For(another) Add(arr, it);
}
template <class T>
void Add(Array<T> *arr, T *items, int64_t item_count) {
for (int64_t i = 0; i < item_count; i += 1) Add(arr, items[i]);
}
template <class T>
void BoundedAdd(Array<T> *arr, T item) {
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
}
template <class T>
void BoundedAddError(Array<T> *arr, T item) {
Assert(arr->len + 1 <= arr->cap);
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
}
template <class T>
void Insert(Array<T> *arr, T item, int64_t index) {
if (index == arr->len) {
Add(arr, item);
return;
}
Assert(index < arr->len);
Assert(index >= 0);
TryGrowing(arr);
int64_t right_len = arr->len - index;
memmove(arr->data + index + 1, arr->data + index, sizeof(T) * right_len);
arr->data[index] = item;
arr->len += 1;
}
template <class T>
Array<T> Copy(Allocator alo, Array<T> array) {
Array<T> result = {alo};
Reserve(&result, array.cap);
memcpy(result.data, array.data, sizeof(T) * array.len);
result.len = array.len;
return result;
}
template <class T>
Array<T> TightCopy(Allocator alo, Array<T> array) {
Array<T> result = {alo};
Reserve(&result, array.len);
memcpy(result.data, array.data, sizeof(T) * array.len);
result.len = array.len;
return result;
}
template <class T>
T Pop(Array<T> *arr) {
Assert(arr->len > 0);
return arr->data[--arr->len];
}
template <class T>
bool Contains(Array<T> &arr, T item) {
For(arr) if (it == item) return true;
return false;
}
template <class T>
int64_t GetIndex(Array<T> &arr, const T &item) {
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
Assert(index >= 0 && index < arr.len);
return (int64_t)index;
}
template <class T>
void RemoveByIndex(Array<T> *arr, int64_t index) {
Assert(index >= 0 && index < arr->len);
int64_t right_len = arr->len - index - 1;
memmove(arr->data + index, arr->data + index + 1, right_len * sizeof(T));
arr->len -= 1;
}
template <class T>
void RemoveManyByIndex(Array<T> *arr, int64_t index, int64_t count) {
if (count == 0) return;
Assert(index >= 0 && index < arr->len);
Assert((index + count) > 0 && (index + count) <= arr->len);
int64_t right_len = arr->len - index - count;
memmove(arr->data + index, arr->data + index + count, right_len * sizeof(T));
arr->len -= count;
}
template <class T>
void RemoveMany(Array<T> *arr, T &item, int64_t count) {
Assert(arr->len > 0);
Assert(&item >= arr->begin() && &item < arr->end());
int64_t index = GetIndex(*arr, item);
RemoveManyByIndex(arr, index, count);
}
template <class T>
void Remove(Array<T> *arr, T &item) {
Assert(arr->len > 0);
Assert(&item >= arr->begin() && &item < arr->end());
int64_t index = GetIndex(*arr, item);
RemoveByIndex(arr, index);
}
template <class T>
void UnorderedRemove(Array<T> *arr, T &item) {
Assert(arr->len > 0);
Assert((&item >= arr->begin()) && (&item < arr->end()));
item = arr->data[--arr->len];
}
template <class T>
void UnorderedRemoveByIndex(Array<T> *arr, int64_t index) {
Assert(arr->len > 0);
Assert(index >= 0 && index < arr->len);
arr->data[index] = arr->data[--arr->len];
}
template <class T>
void InsertArray(Array<T> *arr, T *items, int64_t count, int64_t index) {
if (index == arr->len) {
Add(arr, items, count);
return;
}
Assert(index < arr->len);
TryGrowing(arr, count);
T *gap_begin = arr->data + index;
T *gap_end = gap_begin + count;
int64_t item_count = arr->len - index;
memmove(gap_end, gap_begin, item_count * sizeof(T));
for (int64_t i = 0; i < count; i += 1) arr->data[index + i] = items[i];
arr->len += count;
}
template <class T>
T Get(Array<T> &arr, int64_t i, T default_value = {}) {
T result = default_value;
if (i >= 0 && i < arr.len) result = arr[i];
return result;
}
template <class T>
void Dealloc(Array<T> *arr) {
if (arr->data) Dealloc(arr->allocator, &arr->data);
arr->len = arr->cap = 0;
}
template <class T>
bool IsLast(Array<T> &arr, T &item) {
bool result = GetLast(arr) == &item;
return result;
}
template <class T>
bool IsFirst(Array<T> &arr, T &item) {
bool result = GetFirst(arr) == &item;
return result;
}
template <class T>
Slice<T> Chop(Array<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data, arr.len - len};
return result;
}
template <class T>
Slice<T> Skip(Array<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data + len, arr.len - len};
return result;
}
template <class T>
Slice<T> GetPostfix(Array<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
int64_t remain_len = arr.len - len;
Slice<T> result = {arr.data + remain_len, len};
return result;
}
template <class T>
Slice<T> GetPrefix(Array<T> &arr, int64_t len) {
len = ClampTop(len, arr.len);
Slice<T> result = {arr.data, len};
return result;
}
template <class T>
Slice<T> GetSlice(Array<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
// Negative indexes work in python style, they return you the index counting from end of list
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
if (first_index == SLICE_LAST) first_index = arr.len;
if (first_index < 0) first_index = arr.len + first_index;
Slice<T> result = {arr.data, arr.len};
if (arr.len > 0) {
if (one_past_last_index > first_index) {
first_index = ClampTop(first_index, arr.len - 1);
one_past_last_index = ClampTop(one_past_last_index, arr.len);
result.data += first_index;
result.len = one_past_last_index - first_index;
} else {
result.len = 0;
}
}
return result;
}
template <class T>
struct ReverseIter {
T *data;
Slice<T> *arr;
ReverseIter operator++(int) {
ReverseIter ret = *this;
data -= 1;
return ret;
}
ReverseIter &operator++() {
data -= 1;
return *this;
}
T &operator*() { return data[0]; }
T *operator->() { return data; }
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
};
template <class T>
ReverseIter<T> IterateInReverse(Array<T> *arr) {
return {arr->end() - 1, &arr->slice};
}
template <class T>
ReverseIter<T> IterateInReverse(Slice<T> *slice) {
return {slice->end() - 1, slice};
}

215
src/basic/basic_head.h Normal file
View File

@@ -0,0 +1,215 @@
#pragma once
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <stddef.h>
#if defined(__APPLE__) && defined(__MACH__)
#define OS_POSIX 1
#define OS_MAC 1
#elif defined(_WIN32)
#define OS_WINDOWS 1
#elif defined(__linux__)
#define OS_POSIX 1
#define OS_LINUX 1
#elif defined(__EMSCRIPTEN__)
#define OS_WASM 1
#else
#error Unsupported platform
#endif
#if defined(__clang__)
#define COMPILER_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
#define COMPILER_GCC 1
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#else
#error Unsupported compiler
#endif
#ifndef OS_WASM
#define OS_WASM 0
#endif
#ifndef OS_MAC
#define OS_MAC 0
#endif
#ifndef OS_WINDOWS
#define OS_WINDOWS 0
#endif
#ifndef OS_LINUX
#define OS_LINUX 0
#endif
#ifndef OS_POSIX
#define OS_POSIX 0
#endif
#ifndef COMPILER_MSVC
#define COMPILER_MSVC 0
#endif
#ifndef COMPILER_CLANG
#define COMPILER_CLANG 0
#endif
#ifndef COMPILER_GCC
#define COMPILER_GCC 0
#endif
#if OS_WINDOWS
#define BREAK() __debugbreak()
#elif OS_LINUX
#define BREAK() raise(SIGTRAP)
#elif OS_WASM
#include <emscripten.h>
EM_JS(void, JS_Breakpoint, (), {
debugger;
})
#define BREAK() JS_Breakpoint()
#endif
#define API
#define Assert(x) \
if (!(x)) { \
BREAK(); \
}
#define InvalidCodepath() Assert(!"invalid codepath")
#define ElseInvalidCodepath() else {InvalidCodepath()}
#define KiB(x) ((x##ull) * 1024ull)
#define MiB(x) (KiB(x) * 1024ull)
#define GiB(x) (MiB(x) * 1024ull)
#define TiB(x) (GiB(x) * 1024ull)
#define Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0]))))
#define SLICE_LAST INT64_MAX
using U8 = uint8_t;
using U16 = uint16_t;
using U32 = uint32_t;
using U64 = uint64_t;
using S8 = int8_t;
using S16 = int16_t;
using S32 = int32_t;
using S64 = int64_t;
using Int = S64;
using UInt = U64;
template <class T>
T Min(T a, T b) {
if (a > b) return b;
return a;
}
template <class T>
T ClampTop(T a, T top) {
return Min(a, top);
}
template <class T>
T Max(T a, T b) {
if (a > b) return a;
return b;
}
template <class T>
T ClampBottom(T bottom, T b) {
return Max(bottom, b);
}
template <class T>
T Clamp(T value, T min, T max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
template <class T>
void Swap(T *a, T *b) {
T temp = *a;
*a = *b;
*b = temp;
}
inline bool IsPowerOf2(size_t x) {
size_t result = (((x) & ((x)-1)) == 0);
return result;
}
inline size_t WrapAroundPowerOf2(size_t x, size_t pow2) {
Assert(IsPowerOf2(pow2));
size_t result = (((x) & ((pow2)-1llu)));
return result;
}
inline uint64_t HashBytes(void *data, unsigned size) {
uint8_t *data8 = (uint8_t *)data;
uint64_t hash = (uint64_t)14695981039346656037ULL;
for (unsigned i = 0; i < size; i++) {
hash = hash ^ (uint64_t)(data8[i]);
hash = hash * (uint64_t)1099511628211ULL;
}
return hash;
}
inline size_t GetAlignOffset(size_t size, size_t align) {
Assert(IsPowerOf2(align));
size_t mask = align - 1;
size_t val = size & mask;
if (val) {
val = align - val;
}
return val;
}
inline size_t AlignUp(size_t size, size_t align) {
size_t result = size + GetAlignOffset(size, align);
return result;
}
inline size_t AlignDown(size_t size, size_t align) {
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
size_t result = size - (align - GetAlignOffset(size, align));
return result;
}
template <typename T>
struct DEFER_ExitScope {
T lambda;
DEFER_ExitScope(T lambda) : lambda(lambda) {}
~DEFER_ExitScope() { lambda(); }
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
private:
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
};
class DEFER_ExitScopeHelp {
public:
template <typename T>
DEFER_ExitScope<T> operator+(T t) { return t; }
};
#define DEFER_CONCAT_INTERNAL(x, y) x##y
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
struct RandomSeed {
uint64_t a;
};
inline uint64_t GetRandomU64(RandomSeed *state) {
uint64_t x = state->a;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return state->a = x;
}

View File

@@ -1,5 +1,4 @@
#ifndef FIRST_LL_HEADER
#define FIRST_LL_HEADER
#pragma once
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
do { \
(n)->next = 0; \
@@ -116,4 +115,3 @@
} while (0)
#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
#endif

59
src/basic/basic_math.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
struct Vec2 {
float x;
float y;
};
union Vec4 {
struct {
float x;
float y;
float z;
float w;
};
struct {
float r;
float g;
float b;
float a;
};
};
union Vec3 {
struct {
float x;
float y;
float z;
};
struct {
float r;
float g;
float b;
};
};
struct Rect2 {
Vec2 min;
Vec2 max;
};
struct Vec2I {
Int x;
Int y;
};
struct Rect2I {
Vec2I min;
Vec2I max;
};
union Color {
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uint32_t value;
};

1101
src/basic/basic_os.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
#pragma once
#include "../basic/basic.h"
struct FileIter {
bool is_valid;
@@ -50,8 +49,7 @@ void KillProcess(Process *process);
String PollStdout(Allocator allocator, Process *process, bool force_read);
void WriteStdin(Process *process, String string);
void CloseStdin(Process *process);
double get_time_in_micros(void);
double GetTimeMicros(void);
enum MakeDirResult {
MakeDirResult_Success,

336
src/basic/basic_string.cpp Normal file
View File

@@ -0,0 +1,336 @@
API char ToLowerCase(char a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
API char ToUpperCase(char a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
API bool IsWhitespace(char w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
API bool IsAlphabetic(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
API bool IsIdent(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
API bool IsDigit(char a) {
bool result = a >= '0' && a <= '9';
return result;
}
API bool IsAlphanumeric(char a) {
bool result = IsDigit(a) || IsAlphabetic(a);
return result;
}
API bool IsSymbol(char w) {
bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
return result;
}
API bool IsNonWord(char w) {
if (w == '_') return false;
bool result = IsSymbol(w) || IsWhitespace(w);
return result;
}
API bool IsWord(char w) {
bool result = !IsNonWord(w);
return result;
}
API bool IsBrace(char c) {
bool result = c == '{' || c == '}';
return result;
}
API bool IsParen(char c) {
bool result = c == '(' || c == ')';
return result;
}
API String Chop(String a, int64_t len) {
len = ClampTop(len, a.len);
String result = {a.data, a.len - len};
return result;
}
API String Skip(String a, int64_t len) {
len = ClampTop(len, a.len);
String result = {a.data + len, a.len - len};
return result;
}
API String GetPostfix(String a, int64_t len) {
len = ClampTop(len, a.len);
int64_t remain_len = a.len - len;
String result = {a.data + remain_len, len};
return result;
}
API String GetPrefix(String a, int64_t len) {
len = ClampTop(len, a.len);
String result = {a.data, len};
return result;
}
API String GetSlice(String arr, int64_t first_index, int64_t one_past_last_index) {
// Negative indexes work in python style, they return you the index counting from end of list
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
if (first_index == SLICE_LAST) first_index = arr.len;
if (first_index < 0) first_index = arr.len + first_index;
String result = {arr.data, arr.len};
if (arr.len > 0) {
if (one_past_last_index > first_index) {
first_index = ClampTop(first_index, arr.len - 1);
one_past_last_index = ClampTop(one_past_last_index, arr.len);
result.data += first_index;
result.len = one_past_last_index - first_index;
} else {
result.len = 0;
}
}
return result;
}
API bool AreEqual(String a, String b, unsigned ignore_case) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
char A = a.data[i];
char B = b.data[i];
if (ignore_case) {
A = ToLowerCase(A);
B = ToLowerCase(B);
}
if (A != B)
return false;
}
return true;
}
API bool EndsWith(String a, String end, unsigned ignore_case) {
String a_end = GetPostfix(a, end.len);
bool result = AreEqual(end, a_end, ignore_case);
return result;
}
API bool StartsWith(String a, String start, unsigned ignore_case) {
String a_start = GetPrefix(a, start.len);
bool result = AreEqual(start, a_start, ignore_case);
return result;
}
API String Trim(String string) {
if (string.len == 0)
return string;
int64_t whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!IsWhitespace(string.data[whitespace_begin])) {
break;
}
}
int64_t whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
if (whitespace_begin == whitespace_end) {
string.len = 0;
} else {
string = GetSlice(string, whitespace_begin, whitespace_end);
}
return string;
}
API String TrimEnd(String string) {
int64_t whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
String result = GetPrefix(string, whitespace_end);
return result;
}
API String Copy(Allocator allocator, String string) {
char *copy = (char *)AllocSize(allocator, sizeof(char) * (string.len + 1));
memcpy(copy, string.data, string.len);
copy[string.len] = 0;
String result = {copy, string.len};
return result;
}
API String Copy(Allocator allocator, char *string) {
return Copy(allocator, {string, (int64_t)strlen(string)});
}
API void NormalizePathInPlace(String s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.data[i] == '\\')
s.data[i] = '/';
}
}
API String NormalizePath(Allocator allocator, String s) {
String copy = Copy(allocator, s);
NormalizePathInPlace(copy);
return copy;
}
API bool Seek(String string, String find, int64_t *index_out, SeekFlag flags) {
bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false;
bool result = false;
if (flags & SeekFlag_MatchFindLast) {
for (int64_t i = string.len; i != 0; i--) {
int64_t index = i - 1;
String substring = GetSlice(string, index, index + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
} else {
for (int64_t i = 0; i < string.len; i++) {
String substring = GetSlice(string, i, i + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
API String CutLastSlash(String *s) {
String result = *s;
Seek(*s, "/", &s->len, SeekFlag_MatchFindLast);
result = Skip(result, s->len);
return result;
}
API String ChopLastSlash(String s) {
String result = s;
Seek(s, "/", &result.len, SeekFlag_MatchFindLast);
return result;
}
API String ChopLastPeriod(String s) {
String result = s;
Seek(s, ".", &result.len, SeekFlag_MatchFindLast);
return result;
}
API String SkipToLastSlash(String s) {
int64_t pos;
String result = s;
if (Seek(s, "/", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
API String SkipToLastPeriod(String s) {
int64_t pos;
String result = s;
if (Seek(s, ".", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
API String CutPrefix(String *string, int64_t len) {
String result = GetPrefix(*string, len);
*string = Skip(*string, len);
return result;
}
API String CutPostfix(String *string, int64_t len) {
String result = GetPostfix(*string, len);
*string = Chop(*string, len);
return result;
}
API String FormatV(Allocator allocator, const char *data, va_list args1) {
va_list args2;
va_copy(args2, args1);
int64_t len = stbsp_vsnprintf(0, 0, data, args2);
va_end(args2);
char *result = (char *)AllocSize(allocator, sizeof(char) * (len + 1));
stbsp_vsnprintf(result, (int)(len + 1), data, args1);
String res = {result, len};
return res;
}
API String Format(Allocator allocator, const char *data, ...) {
STRING_FORMAT(allocator, data, result);
return result;
}
API Array<String> Split(Allocator allocator, String string, String delimiter) {
Array<String> result = {allocator};
int64_t index = 0;
while (Seek(string, delimiter, &index)) {
String before_match = {string.data, index};
Add(&result, before_match);
string = Skip(string, index + delimiter.len);
}
Add(&result, string);
return result;
}
API String Merge(Allocator allocator, Array<String> list, String separator) {
int64_t char_count = 0;
For(list) char_count += it.len;
if (char_count == 0) return {};
int64_t node_count = list.len;
int64_t base_size = (char_count + 1);
int64_t sep_size = (node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char *buff = (char *)AllocSize(allocator, sizeof(char) * (size + 1));
String string = {buff, 0};
For(list) {
Assert(string.len + it.len <= size);
memcpy(string.data + string.len, it.data, it.len);
string.len += it.len;
if (!IsLast(list, it)) {
memcpy(string.data + string.len, separator.data, separator.len);
string.len += separator.len;
}
}
Assert(string.len == size - 1);
string.data[size] = 0;
return string;
}
API Int GetSize(Array<String> array) {
Int result = 0;
For (array) result += it.len;
return result;
}

106
src/basic/basic_string.h Normal file
View File

@@ -0,0 +1,106 @@
#pragma once
struct String {
char *data;
int64_t len;
String() = default;
String(char *s) : data(s), len(strlen(s)) {}
String(char *s, int64_t l) : data((char *)s), len(l) {}
String(const char *s) : data((char *)s), len(strlen((char *)s)) {}
String(const char *s, int64_t l) : data((char *)s), len(l) {}
char &operator[](int64_t index) {
Assert(index < len);
return data[index];
}
char *begin() { return data; }
char *end() { return data + len; }
struct ReverseIter {
char *data;
String *arr;
ReverseIter operator++(int) {
ReverseIter ret = *this;
data -= 1;
return ret;
}
ReverseIter &operator++() {
data -= 1;
return *this;
}
char &operator*() { return data[0]; }
char *operator->() { return data; }
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
};
ReverseIter reverse() { return {this->end() - 1, this}; }
};
API char ToLowerCase(char a);
API char ToUpperCase(char a);
API bool IsWhitespace(char w);
API bool IsAlphabetic(char a);
API bool IsIdent(char a);
API bool IsDigit(char a);
API bool IsAlphanumeric(char a);
API bool IsSymbol(char w);
API bool IsNonWord(char w);
API bool IsWord(char w);
API bool IsBrace(char c);
API bool IsParen(char c);
API bool EndsWith(String a, String end, unsigned ignore_case = false);
API bool StartsWith(String a, String start, unsigned ignore_case = false);
API bool AreEqual(String a, String b, unsigned ignore_case = false);
inline bool operator==(String a, String b) { return AreEqual(a, b); }
inline bool operator!=(String a, String b) { return !AreEqual(a, b); }
API String Trim(String string);
API String TrimEnd(String string);
API String Chop(String a, int64_t len);
API String Skip(String a, int64_t len);
API String GetPostfix(String a, int64_t len);
API String GetPrefix(String a, int64_t len);
API String GetSlice(String arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST);
API String CutLastSlash(String *s);
API String ChopLastSlash(String s);
API String ChopLastPeriod(String s);
API String SkipToLastSlash(String s);
API String SkipToLastPeriod(String s);
API String CutPrefix(String *string, int64_t len);
API String CutPostfix(String *string, int64_t len);
API String Copy(Allocator allocator, String string);
API String Copy(Allocator allocator, char *string);
API void NormalizePathInPlace(String s);
API String NormalizePath(Allocator allocator, String s);
#define FmtString(string) (int)(string).len, (string).data
API String FormatV(Allocator allocator, const char *data, va_list args1);
API String Format(Allocator allocator, const char *data, ...);
#define STRING_FORMAT(allocator, data, result) \
va_list args1; \
va_start(args1, data); \
String result = FormatV(allocator, data, args1); \
va_end(args1)
typedef int SeekFlag;
enum {
SeekFlag_None = 0,
SeekFlag_IgnoreCase = 1,
SeekFlag_MatchFindLast = 2,
};
API bool Seek(String string, String find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None);
API Array<String> Split(Allocator allocator, String string, String delimiter = " ");
API String Merge(Allocator allocator, Array<String> list, String separator);
API Int GetSize(Array<String> array);

View File

@@ -0,0 +1,422 @@
#include <stdlib.h>
API int64_t WideLength(char16_t *string) {
if (!string) return 0;
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
API char16_t ToLowerCase(char16_t a) {
if (a >= u'A' && a <= u'Z') a += 32;
return a;
}
API char16_t ToUpperCase(char16_t a) {
if (a >= u'a' && a <= u'z') a -= 32;
return a;
}
API bool IsWhitespace(char16_t w) {
bool result = w == u'\n' || w == u' ' || w == u'\t' || w == u'\v' || w == u'\r';
return result;
}
API bool IsAlphabetic(char16_t a) {
bool result = (a >= u'a' && a <= u'z') || (a >= u'A' && a <= u'Z');
return result;
}
API bool IsIdent(char16_t a) {
bool result = (a >= u'a' && a <= u'z') || (a >= u'A' && a <= u'Z') || a == u'_';
return result;
}
API bool IsDigit(char16_t a) {
bool result = a >= u'0' && a <= u'9';
return result;
}
API bool IsAlphanumeric(char16_t a) {
bool result = IsDigit(a) || IsAlphabetic(a);
return result;
}
API bool IsSymbol(char16_t w) {
bool result = (w >= u'!' && w <= u'/') || (w >= u':' && w <= u'@') || (w >= u'[' && w <= u'`') || (w >= u'{' && w <= u'~');
return result;
}
API bool IsNonWord(char16_t w) {
if (w == u'_') return false;
bool result = IsSymbol(w) || IsWhitespace(w);
return result;
}
API bool IsWord(char16_t w) {
bool result = !IsNonWord(w);
return result;
}
API bool IsBrace(char16_t c) {
bool result = c == u'{' || c == u'}';
return result;
}
API bool IsParen(char16_t c) {
bool result = c == u'(' || c == u')';
return result;
}
API String16 Chop(String16 a, int64_t len) {
len = ClampTop(len, a.len);
String16 result = {a.data, a.len - len};
return result;
}
API String16 Skip(String16 a, int64_t len) {
len = ClampTop(len, a.len);
String16 result = {a.data + len, a.len - len};
return result;
}
API String16 GetPostfix(String16 a, int64_t len) {
len = ClampTop(len, a.len);
int64_t remain_len = a.len - len;
String16 result = {a.data + remain_len, len};
return result;
}
API String16 GetPrefix(String16 a, int64_t len) {
len = ClampTop(len, a.len);
String16 result = {a.data, len};
return result;
}
API String16 GetSlice(String16 arr, int64_t first_index, int64_t one_past_last_index) {
// Negative indexes work in python style, they return you the index counting from end of list
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
if (first_index == SLICE_LAST) first_index = arr.len;
if (first_index < 0) first_index = arr.len + first_index;
String16 result = {arr.data, arr.len};
if (arr.len > 0) {
if (one_past_last_index > first_index) {
first_index = ClampTop(first_index, arr.len - 1);
one_past_last_index = ClampTop(one_past_last_index, arr.len);
result.data += first_index;
result.len = one_past_last_index - first_index;
} else {
result.len = 0;
}
}
return result;
}
API bool AreEqual(String16 a, String16 b, unsigned ignore_case) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
char16_t A = a.data[i];
char16_t B = b.data[i];
if (ignore_case) {
A = ToLowerCase(A);
B = ToLowerCase(B);
}
if (A != B)
return false;
}
return true;
}
API bool EndsWith(String16 a, String16 end, unsigned ignore_case) {
String16 a_end = GetPostfix(a, end.len);
bool result = AreEqual(end, a_end, ignore_case);
return result;
}
API bool StartsWith(String16 a, String16 start, unsigned ignore_case) {
String16 a_start = GetPrefix(a, start.len);
bool result = AreEqual(start, a_start, ignore_case);
return result;
}
API String16 Trim(String16 string) {
if (string.len == 0)
return string;
int64_t whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!IsWhitespace(string.data[whitespace_begin])) {
break;
}
}
int64_t whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
if (whitespace_begin == whitespace_end) {
string.len = 0;
} else {
string = GetSlice(string, whitespace_begin, whitespace_end);
}
return string;
}
API String16 TrimEnd(String16 string) {
int64_t whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
String16 result = GetPrefix(string, whitespace_end);
return result;
}
API String16 Copy16(Allocator allocator, String16 string) {
char16_t *copy = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
memcpy(copy, string.data, string.len);
copy[string.len] = 0;
String16 result = {copy, string.len};
return result;
}
API String16 Copy16(Allocator allocator, char16_t *string) {
return Copy16(allocator, {string, (int64_t)WideLength(string)});
}
API void NormalizePathInPlace(String16 s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.data[i] == u'\\')
s.data[i] = u'/';
}
}
API String16 NormalizePath(Allocator allocator, String16 s) {
String16 copy = Copy16(allocator, s);
NormalizePathInPlace(copy);
return copy;
}
API bool Seek(String16 string, String16 find, int64_t *index_out, SeekFlag flags) {
bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false;
bool result = false;
if (flags & SeekFlag_MatchFindLast) {
for (int64_t i = string.len; i != 0; i--) {
int64_t index = i - 1;
String16 substring = GetSlice(string, index, index + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
} else {
for (int64_t i = 0; i < string.len; i++) {
String16 substring = GetSlice(string, i, i + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
API String16 CutLastSlash(String16 *s) {
String16 result = *s;
Seek(*s, u"/", &s->len, SeekFlag_MatchFindLast);
result = Skip(result, s->len);
return result;
}
API String16 ChopLastSlash(String16 s) {
String16 result = s;
Seek(s, u"/", &result.len, SeekFlag_MatchFindLast);
return result;
}
API String16 ChopLastPeriod(String16 s) {
String16 result = s;
Seek(s, u".", &result.len, SeekFlag_MatchFindLast);
return result;
}
API String16 SkipToLastSlash(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, u"/", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
API String16 SkipToLastPeriod(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, u".", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
API String16 CutPrefix(String16 *string, int64_t len) {
String16 result = GetPrefix(*string, len);
*string = Skip(*string, len);
return result;
}
API String16 CutPostfix(String16 *string, int64_t len) {
String16 result = GetPostfix(*string, len);
*string = Chop(*string, len);
return result;
}
API String16 Format16V(Allocator allocator, const char *data, va_list args1) {
Scratch scratch;
va_list args2;
va_copy(args2, args1);
int64_t len = stbsp_vsnprintf(0, 0, data, args2);
va_end(args2);
char *result = (char *)AllocSize(scratch, sizeof(char) * (len + 1));
stbsp_vsnprintf(result, (int)(len + 1), data, args1);
String res = {result, len};
String16 res16 = ToString16(allocator, res);
return res16;
}
API String16 Format16(Allocator allocator, const char *data, ...) {
Scratch scratch;
STRING_FORMAT(scratch, data, result);
String16 result16 = ToString16(allocator, result);
return result16;
}
API Array<String16> Split(Allocator allocator, String16 string, String16 delimiter) {
Array<String16> result = {allocator};
int64_t index = 0;
while (Seek(string, delimiter, &index)) {
String16 before_match = {string.data, index};
Add(&result, before_match);
string = Skip(string, index + delimiter.len);
}
Add(&result, string);
return result;
}
API String16 Merge(Allocator allocator, Array<String16> list, String16 separator) {
int64_t char_count = 0;
For(list) char_count += it.len;
if (char_count == 0) return {};
int64_t node_count = list.len;
int64_t base_size = (char_count + 1);
int64_t sep_size = (node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char16_t *buff = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (size + 1));
String16 string = {buff, 0};
For(list) {
Assert(string.len + it.len <= size);
memcpy(string.data + string.len, it.data, it.len);
string.len += it.len;
if (!IsLast(list, it)) {
memcpy(string.data + string.len, separator.data, separator.len);
string.len += separator.len;
}
}
Assert(string.len == size - 1);
string.data[size] = 0;
return string;
}
API Int GetSize(Array<String16> array) {
Int result = 0;
For (array) result += it.len;
return result;
}
API String16 SkipNumberEx(String16 *string) {
String16 col = {string->data, 0};
for (int64_t i = 0; i < string->len; i += 1) {
if (IsDigit(string->data[i])) {
col.len += 1;
} else {
break;
}
}
*string = Skip(*string, col.len);
return col;
}
API Int SkipNumber(String16 *string) {
String16 col = SkipNumberEx(string);
if (col.len == 0) return -1;
Scratch scratch;
String num_string = ToString(scratch, col);
Int result = strtoll(num_string.data, NULL, 10);
return result;
}
API String16 SkipUntil(String16 *string, String16 str) {
String16 begin = *string;
begin.len = 0;
for (; string->len; begin.len += 1) {
String16 match = GetPrefix(*string, str.len);
if (StartsWith(match, str)) break;
*string = Skip(*string, 1);
}
return begin;
}
API String16 SkipWhitespace(String16 *string) {
String16 begin = {string->data, 0};
for (Int i = 0; i < string->len; i += 1) {
if (!IsWhitespace(string->data[i])) break;
*string = Skip(*string, 1);
begin.len += 1;
}
return begin;
}
// chop this - :324
API String16 ChopNumberEx(String16 *string) {
String16 col = {};
for (int64_t i = string->len - 1; i >= 0; i -= 1) {
if (IsDigit(string->data[i])) {
col.data = string->data + i;
col.len += 1;
} else if (string->data[i] == L':') {
break;
} else {
return {};
}
}
*string = Chop(*string, col.len + 1);
return col;
}
API Int ChopNumber(String16 *string) {
Scratch scratch;
String16 col = ChopNumberEx(string);
if (col.len == 0) return -1;
String num_string = ToString(scratch, col);
Int result = strtoll(num_string.data, NULL, 10) - 1;
return result;
}

104
src/basic/basic_string16.h Normal file
View File

@@ -0,0 +1,104 @@
#pragma once
API int64_t WideLength(char16_t *string);
struct String16 {
char16_t *data;
int64_t len;
String16() = default;
String16(char16_t *s) : data(s), len(WideLength(s)) {}
String16(char16_t *s, int64_t l) : data((char16_t *)s), len(l) {}
String16(const char16_t *s) : data((char16_t *)s), len(WideLength((char16_t *)s)) {}
String16(const char16_t *s, int64_t l) : data((char16_t *)s), len(l) {}
char16_t &operator[](int64_t index) {
Assert(index < len);
return data[index];
}
char16_t *begin() { return data; }
char16_t *end() { return data + len; }
struct ReverseIter {
char16_t *data;
String16 *arr;
ReverseIter operator++(int) {
ReverseIter ret = *this;
data -= 1;
return ret;
}
ReverseIter &operator++() {
data -= 1;
return *this;
}
char16_t &operator*() { return data[0]; }
char16_t *operator->() { return data; }
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
};
ReverseIter reverse() { return {this->end() - 1, this}; }
};
API char16_t ToLowerCase(char16_t a);
API char16_t ToUpperCase(char16_t a);
API bool IsWhitespace(char16_t w);
API bool IsAlphabetic(char16_t a);
API bool IsIdent(char16_t a);
API bool IsDigit(char16_t a);
API bool IsAlphanumeric(char16_t a);
API bool IsSymbol(char16_t w);
API bool IsNonWord(char16_t w);
API bool IsWord(char16_t w);
API bool IsBrace(char16_t c);
API bool IsParen(char16_t c);
API bool EndsWith(String16 a, String16 end, unsigned ignore_case = false);
API bool StartsWith(String16 a, String16 start, unsigned ignore_case = false);
API bool AreEqual(String16 a, String16 b, unsigned ignore_case = false);
inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); }
inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); }
API String16 Trim(String16 string);
API String16 TrimEnd(String16 string);
API String16 Chop(String16 a, int64_t len);
API String16 Skip(String16 a, int64_t len);
API String16 GetPostfix(String16 a, int64_t len);
API String16 GetPrefix(String16 a, int64_t len);
API String16 GetSlice(String16 arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST);
API String16 CutLastSlash(String16 *s);
API String16 ChopLastSlash(String16 s);
API String16 ChopLastPeriod(String16 s);
API String16 SkipToLastSlash(String16 s);
API String16 SkipToLastPeriod(String16 s);
API String16 CutPrefix(String16 *string, int64_t len);
API String16 CutPostfix(String16 *string, int64_t len);
// @todo: think about this API, for parsing, maybe redesign or something
API String16 SkipNumberEx(String16 *string);
API Int SkipNumber(String16 *string);
API String16 SkipUntil(String16 *string, String16 str);
API String16 SkipWhitespace(String16 *string);
API String16 ChopNumberEx(String16 *string);
API Int ChopNumber(String16 *string);
API String16 Copy16(Allocator allocator, String16 string);
API String16 Copy16(Allocator allocator, char16_t *string, int64_t len);
API String16 Copy16(Allocator allocator, char *string);
API void NormalizePathInPlace(String16 s);
API String16 NormalizePath(Allocator allocator, String16 s);
API String16 Format16V(Allocator allocator, const char *data, va_list args1);
API String16 Format16(Allocator allocator, const char *data, ...);
API bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None);
API Array<String16> Split(Allocator allocator, String16 string, String16 delimiter = u" ");
API String16 Merge(Allocator allocator, Array<String16> list, String16 separator);
API Int GetSize(Array<String16> array);

185
src/basic/basic_table.h Normal file
View File

@@ -0,0 +1,185 @@
/*
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;
};
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) {
Assert(size > cap && "New size is smaller then original size");
Assert(IsPowerOf2(size));
if (!allocator.proc) allocator = GetSystemAllocator();
Entry *old_values = values;
size_t old_cap = cap;
values = (Entry *)AllocSize(allocator, sizeof(Entry) * size);
for (size_t i = 0; i < size; i += 1) values[i] = {};
cap = size;
Assert(!(old_values == 0 && len != 0));
if (len == 0) {
if (old_values) 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);
}
}
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 = WrapAroundPowerOf2(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 = WrapAroundPowerOf2(i + 1, cap);
if (i == index) return 0;
}
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 = WrapAroundPowerOf2(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 = WrapAroundPowerOf2(i + 1, cap);
Assert(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
}
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 *get(String s) {
uint64_t hash = HashBytes(s.data, (unsigned)s.len);
return get(hash);
}
Value get(String s, Value default_value) {
uint64_t hash = HashBytes(s.data, (unsigned)s.len);
return get(hash, default_value);
}
void put(String s, const Value &value) {
uint64_t hash = HashBytes(s.data, (unsigned)s.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() {
Dealloc(allocator, &values);
len = 0;
cap = 0;
}
};

237
src/basic/basic_unicode.cpp Normal file
View File

@@ -0,0 +1,237 @@
API UTF32Result UTF16ToUTF32(uint16_t *c, int64_t max_advance) {
UTF32Result result;
MemoryZero(&result, sizeof(result));
if (max_advance >= 1) {
result.advance = 1;
result.out_str = c[0];
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
if (max_advance >= 2) {
result.out_str = 0x10000;
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
result.advance = 2;
} else
result.error = 2;
}
} else {
result.error = 1;
}
return result;
}
API UTF8Result UTF32ToUTF8(uint32_t codepoint) {
UTF8Result result;
MemoryZero(&result, sizeof(result));
if (codepoint <= 0x7F) {
result.len = 1;
result.out_str[0] = (char)codepoint;
} else if (codepoint <= 0x7FF) {
result.len = 2;
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
result.out_str[1] = 0x80 | (0x3f & codepoint);
} else if (codepoint <= 0xFFFF) { // 16 bit word
result.len = 3;
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
} else if (codepoint <= 0x10FFFF) { // 21 bit word
result.len = 4;
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
} else {
result.error = 1;
}
return result;
}
API UTF32Result UTF8ToUTF32(uint8_t *c, int64_t max_advance) {
UTF32Result result;
MemoryZero(&result, sizeof(result));
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
if (max_advance >= 1) {
result.out_str = c[0];
result.advance = 1;
} else result.error = 1;
}
else if ((c[0] & 0xe0) == 0xc0) {
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
if (max_advance >= 2) {
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
result.advance = 2;
} else result.error = 2;
} else result.error = 2;
}
else if ((c[0] & 0xf0) == 0xe0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
if (max_advance >= 3) {
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
result.advance = 3;
} else result.error = 3;
} else result.error = 3;
}
else if ((c[0] & 0xf8) == 0xf0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
if (max_advance >= 4) {
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
result.advance = 4;
} else result.error = 4;
} else result.error = 4;
} else result.error = 4;
return result;
}
API UTF16Result UTF32ToUTF16(uint32_t codepoint) {
UTF16Result result;
MemoryZero(&result, sizeof(result));
if (codepoint < 0x10000) {
result.out_str[0] = (uint16_t)codepoint;
result.out_str[1] = 0;
result.len = 1;
} else if (codepoint <= 0x10FFFF) {
uint32_t code = (codepoint - 0x10000);
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
result.len = 2;
} else {
result.error = 1;
}
return result;
}
#define UTF__HANDLE_DECODE_ERROR(question_mark, I) \
{ \
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
i += I; \
}
API int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen && in[i];) {
UTF32Result decode = UTF16ToUTF32((uint16_t *)(in + i), (int64_t)(inlen - i));
if (!decode.error) {
i += decode.advance;
UTF8Result encode = UTF32ToUTF8(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
} else UTF__HANDLE_DECODE_ERROR('?', 0);
} else UTF__HANDLE_DECODE_ERROR('?', 1);
}
buffer[outlen] = 0;
return outlen;
}
API int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen;) {
UTF32Result decode = UTF8ToUTF32((uint8_t *)(in + i), (int64_t)(inlen - i));
if (!decode.error) {
i += decode.advance;
UTF16Result encode = UTF32ToUTF16(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
} else UTF__HANDLE_DECODE_ERROR(0x003f, 0);
} else UTF__HANDLE_DECODE_ERROR(0x003f, 1);
}
buffer[outlen] = 0;
return outlen;
}
API bool IsValid(UTF8Iter &iter) {
return iter.item;
}
API void Advance(UTF8Iter *iter) {
iter->i += iter->utf8_codepoint_byte_size;
UTF32Result r = UTF8ToUTF32((uint8_t *)(iter->data + iter->i), iter->len - iter->i);
if (r.error) {
iter->item = 0;
return;
}
iter->utf8_codepoint_byte_size = r.advance;
iter->item = r.out_str;
}
API UTF8Iter IterateUTF8Ex(char *data, int64_t len) {
UTF8Iter result;
MemoryZero(&result, sizeof(result));
result.data = data;
result.len = len;
if (len) Advance(&result);
return result;
}
API UTF8Iter IterateUTF8(char *data) {
int64_t length = 0;
while (data[length]) length += 1;
return IterateUTF8Ex(data, length);
}
API UTF8Iter IterateUTF8(String string) {
return IterateUTF8Ex(string.data, string.len);
}
API bool IsUTF8ContinuationByte(char c) {
char result = (c & 0b11000000) == 0b10000000;
return result;
}
API String16 ToString16(Allocator allocator, String string) {
char16_t *buffer = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
int64_t size = CreateWidecharFromChar(buffer, string.len + 1, string.data, string.len);
String16 result = {buffer, size};
return result;
}
API String16 ToString16(Allocator allocator, char *in_string) {
String string(in_string);
String16 result = ToString16(allocator, string);
return result;
}
API char16_t *ToWidechar(Allocator allocator, String string) {
String16 result = ToString16(allocator, string);
return result.data;
}
API String ToString(Allocator allocator, String16 string) {
Assert(sizeof(char16_t) == 2);
int64_t buffer_size = (string.len + 1) * 2;
char *buffer = (char *)AllocSize(allocator, buffer_size);
int64_t size = CreateCharFromWidechar(buffer, buffer_size, string.data, string.len);
String result = {buffer, size};
Assert(size < buffer_size);
return result;
}
API String ToString(Allocator allocator, char16_t *string, int64_t len) {
return ToString(allocator, {string, len});
}
API String ToString(Allocator allocator, char16_t *wstring) {
int64_t size = WideLength(wstring);
String result = ToString(allocator, {wstring, size});
return result;
}

60
src/basic/basic_unicode.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
struct UTF32Result {
uint32_t out_str;
int64_t advance;
int64_t error;
};
struct UTF8Result {
uint8_t out_str[4];
int64_t len;
int64_t error;
};
struct UTF16Result {
uint16_t out_str[2];
int64_t len;
int64_t error;
};
struct UTF8Iter {
char *data;
int64_t len;
int64_t utf8_codepoint_byte_size;
int64_t i;
uint32_t item;
UTF8Iter &operator++() {
void Advance(UTF8Iter * iter);
Advance(this);
return *this;
}
friend bool operator!=(const UTF8Iter &a, const UTF8Iter &b) { return a.item != b.item; }
UTF8Iter &operator*() { return *this; }
UTF8Iter begin() {
UTF8Iter IterateUTF8Ex(char *data, int64_t len);
return {IterateUTF8Ex(data, len)};
}
UTF8Iter end() { return {}; }
};
API UTF32Result UTF16ToUTF32(uint16_t *c, int64_t max_advance);
API UTF8Result UTF32ToUTF8(uint32_t codepoint);
API UTF32Result UTF8ToUTF32(uint8_t *c, int64_t max_advance);
API UTF16Result UTF32ToUTF16(uint32_t codepoint);
API int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen);
API int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
API char16_t *ToWidechar(Allocator allocator, String string);
API String16 ToString16(Allocator allocator, String string);
API String ToString(Allocator allocator, String16 string);
API String ToString(Allocator allocator, char16_t *string, int64_t len);
API String ToString(Allocator allocator, char16_t *wstring);
API bool IsValid(UTF8Iter &iter);
API void Advance(UTF8Iter *iter);
API UTF8Iter IterateUTF8Ex(char *data, int64_t len);
API UTF8Iter IterateUTF8(char *data);
API UTF8Iter IterateUTF8(String string);
API bool IsUTF8ContinuationByte(char c);

View File

@@ -1,41 +1,3 @@
struct Vec2 {
float x;
float y;
};
union Vec4 {
struct {
float x;
float y;
float z;
float w;
};
struct {
float r;
float g;
float b;
float a;
};
};
union Vec3 {
struct {
float x;
float y;
float z;
};
struct {
float r;
float g;
float b;
};
};
struct Rect2 {
Vec2 min;
Vec2 max;
};
Vec2 GetSize(Rect2 r) {
Vec2 result = {r.max.x - r.min.x, r.max.y - r.min.y};
return result;

View File

@@ -1,23 +1,3 @@
struct Vec2I {
Int x;
Int y;
};
struct Rect2I {
Vec2I min;
Vec2I max;
};
union Color {
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uint32_t value;
};
Vec2I GetSize(Rect2I r) {
Vec2I result = {r.max.x - r.min.x, r.max.y - r.min.y};
return result;

1928
src/basic/stb_sprintf.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,341 +0,0 @@
char16_t ToLowerCase(char16_t a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
char16_t ToUpperCase(char16_t a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
bool IsWhitespace(char16_t w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
bool IsSymbol(char16_t w) {
bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
return result;
}
bool IsNonWord(char16_t w) {
if (w == '_') return false;
bool result = IsSymbol(w) || IsWhitespace(w);
return result;
}
bool IsWord(char16_t w) {
bool result = !IsNonWord(w);
return result;
}
bool IsBrace(char16_t c) {
bool result = c == '{' || c == '}';
return result;
}
bool IsParen(char16_t c) {
bool result = c == '(' || c == ')';
return result;
}
bool IsAlphabetic(char16_t a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
bool IsIdent(char16_t a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
bool IsDigit(char16_t a) {
bool result = a >= '0' && a <= '9';
return result;
}
bool IsAlphanumeric(char16_t a) {
bool result = IsDigit(a) || IsAlphabetic(a);
return result;
}
bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
char16_t A = a.data[i];
char16_t B = b.data[i];
if (ignore_case) {
A = ToLowerCase(A);
B = ToLowerCase(B);
}
if (A != B)
return false;
}
return true;
}
inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); }
inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); }
char16_t GetChar(String16 string, int64_t i) {
char16_t result = 0;
if (i < string.len) result = string.data[i];
return result;
}
String16 Merge(Allocator allocator, Array<String16> list, String16 separator = " ") {
int64_t char_count = 0;
For(list) char_count += it.len;
if (char_count == 0) return {};
int64_t node_count = list.len;
int64_t base_size = (char_count + 1);
int64_t sep_size = (node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char16_t *buff = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (size + 1));
String16 string = {buff, 0};
For(list) {
Assert(string.len + it.len <= size);
memcpy(string.data + string.len, it.data, it.len * sizeof(char16_t));
string.len += it.len;
if (!IsLast(list, it)) {
memcpy(string.data + string.len, separator.data, separator.len * sizeof(char16_t));
string.len += separator.len;
}
}
Assert(string.len == size - 1);
string.data[size] = 0;
return string;
}
bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag flags = SeekFlag_None) {
bool ignore_case = flags & SeekFlag_IgnoreCase ? true : false;
bool result = false;
if (flags & SeekFlag_MatchFindLast) {
for (int64_t i = string.len; i != 0; i--) {
int64_t index = i - 1;
String16 substring = GetSlice(string, index, index + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
} else {
for (int64_t i = 0; i < string.len; i++) {
String16 substring = GetSlice(string, i, i + find.len);
if (AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
String16 CutLastSlash(String16 *s) {
String16 result = *s;
Seek(*s, u"/", &s->len, SeekFlag_MatchFindLast);
result = Skip(result, s->len);
return result;
}
String16 ChopLastSlash(String16 s) {
String16 result = s;
Seek(s, u"/", &result.len, SeekFlag_MatchFindLast);
return result;
}
String16 ChopLastPeriod(String16 s) {
String16 result = s;
Seek(s, u".", &result.len, SeekFlag_MatchFindLast);
return result;
}
String16 SkipToLastSlash(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, u"/", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
String16 SkipToLastPeriod(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, u".", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
String16 CutPrefix(String16 *string, int64_t len) {
String16 result = GetPrefix(*string, len);
*string = Skip(*string, len);
return result;
}
String16 CutPostfix(String16 *string, int64_t len) {
String16 result = GetPostfix(*string, len);
*string = Chop(*string, len);
return result;
}
String16 Trim(String16 string) {
if (string.len == 0)
return string;
int64_t whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!IsWhitespace(string.data[whitespace_begin])) {
break;
}
}
int64_t whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
if (whitespace_begin == whitespace_end) {
string.len = 0;
} else {
string = GetSlice(string, whitespace_begin, whitespace_end);
}
return string;
}
String16 TrimEnd(String16 string) {
int64_t whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!IsWhitespace(string.data[whitespace_end - 1])) {
break;
}
}
String16 result = GetPrefix(string, whitespace_end);
return result;
}
bool EndsWith(String16 a, String16 end, unsigned ignore_case = false) {
String16 a_end = GetPostfix(a, end.len);
bool result = AreEqual(end, a_end, ignore_case);
return result;
}
bool StartsWith(String16 a, String16 start, unsigned ignore_case = false) {
String16 a_start = GetPrefix(a, start.len);
bool result = AreEqual(start, a_start, ignore_case);
return result;
}
String16 Copy16(Allocator allocator, String16 string) {
char16_t *copy = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
memcpy(copy, string.data, string.len);
copy[string.len] = 0;
String16 result = {copy, string.len};
return result;
}
String16 Copy16(Allocator allocator, char16_t *string) {
String16 s = {string, (int64_t)WideLength(string)};
return Copy(allocator, s);
}
String16 Concat(Allocator allocator, String16 a, String16 b) {
char16_t *str = AllocArray(allocator, char16_t, a.len + b.len + 1);
MemoryCopy(str, a.data, a.len * 2);
MemoryCopy(str + a.len, b.data, b.len * 2);
str[a.len + b.len] = 0;
return {str, a.len + b.len};
}
void NormalizePathInPlace(String16 s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.data[i] == u'\\')
s.data[i] = u'/';
}
}
String16 NormalizePath(Allocator allocator, String16 s) {
String16 copy = Copy(allocator, s);
NormalizePathInPlace(copy);
return copy;
}
String16 SkipNumberEx(String16 *string) {
String16 col = {string->data, 0};
for (int64_t i = 0; i < string->len; i += 1) {
if (IsDigit(string->data[i])) {
col.len += 1;
} else {
break;
}
}
*string = Skip(*string, col.len);
return col;
}
Int SkipNumber(String16 *string) {
String16 col = SkipNumberEx(string);
if (col.len == 0) return -1;
Scratch scratch;
String num_string = ToString(scratch, col);
Int result = strtoll(num_string.data, NULL, 10);
return result;
}
String16 SkipUntil(String16 *string, String16 str) {
String16 begin = *string;
begin.len = 0;
for (; string->len; begin.len += 1) {
String16 match = GetPrefix(*string, str.len);
if (StartsWith(match, str)) break;
*string = Skip(*string, 1);
}
return begin;
}
String16 SkipWhitespace(String16 *string) {
String16 begin = {string->data, 0};
for (Int i = 0; i < string->len; i += 1) {
if (!IsWhitespace(string->data[i])) break;
*string = Skip(*string, 1);
begin.len += 1;
}
return begin;
}
// chop this - :324
String16 ChopNumberEx(String16 *string) {
String16 col = {};
for (int64_t i = string->len - 1; i >= 0; i -= 1) {
if (IsDigit(string->data[i])) {
col.data = string->data + i;
col.len += 1;
} else if (string->data[i] == L':') {
break;
} else {
return {};
}
}
*string = Chop(*string, col.len + 1);
return col;
}
Int ChopNumber(String16 *string) {
Scratch scratch;
String16 col = ChopNumberEx(string);
if (col.len == 0) return -1;
String num_string = ToString(scratch, col);
Int result = strtoll(num_string.data, NULL, 10) - 1;
return result;
}

View File

@@ -1,33 +0,0 @@
#define WORK_FUNCTION(name) void name(void *data)
typedef WORK_FUNCTION(WorkQueueCallback);
struct WorkQueueEntry {
WorkQueueCallback *callback;
void *data;
};
struct ThreadCtx {
int thread_index;
};
struct WorkQueue {
int32_t thread_count;
WorkQueueEntry entries[256];
int64_t volatile index_to_write;
int64_t volatile index_to_read;
int64_t volatile completion_index;
int64_t volatile completion_goal;
void *semaphore;
};
struct ThreadStartupInfo {
uint32_t thread_id;
int32_t thread_index;
WorkQueue *queue;
};
void PushWork(WorkQueue *wq, void *data, WorkQueueCallback *callback);
void InitWorkQueue(WorkQueue *queue, uint32_t thread_count, ThreadStartupInfo *info);
void WaitUntilCompletion(WorkQueue *wq);
int64_t AtomicIncrement(volatile int64_t *i);

View File

@@ -1,417 +0,0 @@
#include "filesystem.h"
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <linux/limits.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <poll.h>
void (*Error)(const char *, ...);
void *VReserve(size_t size) {
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
return result == (void *)-1 ? 0 : result;
}
bool VCommit(void *p, size_t size) {
int result = mprotect(p, size, PROT_READ | PROT_WRITE);
return result == 0;
}
bool VRelease(void *p, size_t size) {
int result = munmap(p, size);
return result == 0;
}
bool VDecommit(void *p, size_t size) {
mprotect(p, size, PROT_NONE);
madvise(p, size, MADV_DONTNEED);
return true;
}
void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc;
}
String ReadFile(Allocator al, String path) {
Scratch scratch(al);
String null_term = Copy(scratch, path);
String result = {};
FILE *f = fopen(null_term.data, "rb");
if (f) {
fseek(f, 0, SEEK_END);
result.len = ftell(f);
fseek(f, 0, SEEK_SET);
result.data = (char *)AllocSize(al, result.len + 1);
fread(result.data, result.len, 1, f);
result.data[result.len] = 0;
fclose(f);
}
return result;
}
bool WriteFile(String path, String data) {
Scratch scratch;
String null_term = Copy(scratch, path);
bool result = false;
FILE *f = fopen((const char *)null_term.data, "w");
if (f) {
size_t written = fwrite(data.data, 1, data.len, f);
if (written == data.len) {
result = true;
}
fclose(f);
}
return result;
}
bool DeleteFile(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
int result = unlink(null_term.data);
return result == 0;
}
MakeDirResult MakeDir(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
int error = mkdir(null_term.data, 0755);
MakeDirResult result = MakeDirResult_Success;
if (error != 0) {
result = MakeDirResult_ErrorOther;
if (errno == EEXIST) result = MakeDirResult_Exists;
}
return result;
}
int64_t GetFileModTime(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat attrib = {};
stat(null_term.data, &attrib);
struct timespec ts = attrib.st_mtim;
int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll;
return result;
}
String GetAbsolutePath(Allocator al, String path) {
Scratch scratch(al);
String null_term = Copy(scratch, path);
char *buffer = AllocArray(al, char, PATH_MAX);
realpath(null_term.data, buffer);
String result = buffer;
return result;
}
bool FileExists(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
bool result = false;
if (access((char *)null_term.data, F_OK) == 0) {
result = true;
}
return result;
}
bool IsDir(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat s;
if (stat(null_term.data, &s) != 0)
return false;
return S_ISDIR(s.st_mode);
}
bool IsFile(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat s;
if (stat(null_term.data, &s) != 0)
return false;
return S_ISREG(s.st_mode);
}
bool IsAbsolute(String path) {
bool result = path.len && path.data[0] == '/';
return result;
}
String GetWorkingDir(Allocator al) {
char *buffer = AllocArray(al, char, PATH_MAX);
char *cwd = getcwd(buffer, PATH_MAX);
return cwd;
}
String GetExePath(Allocator al) {
char *buffer = AllocArray(al, char, PATH_MAX);
readlink("/proc/self/exe", buffer, PATH_MAX);
return buffer;
}
String GetExeDir(Allocator al) {
Scratch scratch(al);
String exe_path = GetExePath(scratch);
String dir = ChopLastSlash(exe_path);
String result = Copy(al, dir);
return result;
}
double get_time_in_micros(void) {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000);
}
bool IsValid(const FileIter &it) {
return it.is_valid;
}
void Advance(FileIter *it) {
struct dirent *file = NULL;
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 = Copy(it->allocator, file->d_name);
const char *dir_char_ending = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = Format(it->allocator, "%.*s%s%s%s", FmtString(it->path), separator, file->d_name, dir_char_ending);
it->absolute_path = GetAbsolutePath(it->allocator, it->relative_path);
if (it->is_directory) it->absolute_path = Format(it->allocator, "%.*s/", FmtString(it->absolute_path));
it->is_valid = true;
return;
}
it->is_valid = false;
closedir((DIR *)it->dir);
}
FileIter IterateFiles(Allocator alo, String path) {
FileIter it = {};
it.allocator = alo;
it.path = path;
Scratch scratch(alo);
String null_term = Copy(scratch, path);
it.dir = (void *)opendir((char *)null_term.data);
if (it.dir) {
Advance(&it);
}
return it;
}
struct UnixProcess {
pid_t pid;
int child_stdout_read;
int stdin_write;
};
Array<char *> SplitCommand(Allocator allocator, String command_line) {
Array<char *> cmd = {allocator};
String curr = {};
for (int i = 0; i < command_line.len; i += 1) {
if (command_line.data[i] == ' ') {
if (curr.len > 0) {
Add(&cmd, Copy(allocator, curr).data);
curr = {};
}
continue;
}
if (curr.len == 0) {
curr.data = command_line.data + i;
}
curr.len += 1;
}
if (curr.len > 0) {
Add(&cmd, Copy(allocator, curr).data);
}
return cmd;
}
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
Scratch scratch;
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
bool error = false;
char *buffer = AllocArray(scratch, char, 4096);
chdir(working_dir.data);
getcwd(buffer, 4096);
defer { chdir(buffer); };
Process process = {};
UnixProcess *plat = (UnixProcess *)&process.platform;
Array<char *> args = SplitCommand(scratch, command_line);
Array<char *> env = {scratch};
For (enviroment) {
Add(&env, Copy(scratch, it).data);
}
int stdout_desc[2] = {};
int stdin_desc[2] = {};
posix_spawn_file_actions_t actions = {};
if (posix_spawn_file_actions_init(&actions) != 0) {
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
return process;
}
defer {
posix_spawn_file_actions_destroy(&actions);
};
if (pipe(stdout_desc) == -1) {
Error("Libc function failed: pipe, with error: %s", strerror(errno));
return process;
}
defer {
if (error) {
close(stdout_desc[PIPE_READ]);
close(stdout_desc[PIPE_WRITE]);
} else {
close(stdout_desc[PIPE_WRITE]);
}
};
if (pipe(stdin_desc) == -1) {
Error("Libc function failed: pipe, with error: %s", strerror(errno));
return process;
}
defer {
if (error) {
close(stdin_desc[PIPE_READ]);
close(stdin_desc[PIPE_WRITE]);
} else {
close(stdin_desc[PIPE_READ]);
}
};
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
if (error) {
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
return process;
}
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
if (error) {
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
return process;
}
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
if (error) {
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
return process;
}
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
if (error) {
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
return process;
}
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
if (error) {
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
return process;
}
pid_t process_pid = 0;
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
if (error) {
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
return process;
}
plat->child_stdout_read = stdout_desc[PIPE_READ];
plat->stdin_write = stdin_desc[PIPE_WRITE];
plat->pid = process_pid;
if (write_stdin.len) {
WriteStdin(&process, write_stdin);
CloseStdin(&process);
}
process.id = process_pid;
process.is_valid = true;
return process;
}
bool IsValid(Process *process) {
UnixProcess *plat = (UnixProcess *)&process->platform;
if (process->is_valid == false) {
return false;
}
int status = 0;
pollfd p = {};
p.fd = plat->child_stdout_read;
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
int res = poll(&p, 1, 0);
if (res > 0) {
pid_t result = waitpid(plat->pid, &status, 0);
process->exit_code = WEXITSTATUS(status);
return false;
}
return true;
}
void KillProcess(Process *process) {
Assert(process->is_valid);
UnixProcess *plat = (UnixProcess *)process->platform;
kill(plat->pid, SIGKILL);
process->exit_code = -1;
}
String PollStdout(Allocator allocator, Process *process, bool force_read) {
Assert(process->is_valid);
UnixProcess *plat = (UnixProcess *)process->platform;
String result = {};
result.data = AllocArray(allocator, char, 16 * 4096);
pollfd p = {};
p.fd = plat->child_stdout_read;
p.events = POLLIN;
int res = poll(&p, 1, 0);
if (res > 0 || force_read) {
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
}
return result;
}
void WriteStdin(Process *process, String string) {
if (string.len == 0) return;
Assert(process->is_valid);
UnixProcess *plat = (UnixProcess *)process->platform;
ssize_t size = write(plat->stdin_write, string.data, string.len);
Assert(size == string.len);
}
void CloseStdin(Process *process) {
UnixProcess *plat = (UnixProcess *)process->platform;
close(plat->stdin_write);
}

View File

@@ -1,260 +0,0 @@
#include "filesystem.h"
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <poll.h>
#include <emscripten.h>
#define PATH_MAX 1024
EM_JS(void, JS_Breakpoint, (), {
debugger;
})
void (*Error)(const char *, ...);
void *VReserve(size_t size) {
InvalidCodepath();
return NULL;
}
bool VCommit(void *p, size_t size) {
InvalidCodepath();
return false;
}
bool VRelease(void *p, size_t size) {
InvalidCodepath();
return false;
}
bool VDecommit(void *p, size_t size) {
InvalidCodepath();
return false;
}
void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc;
}
String ReadFile(Allocator al, String path) {
Scratch scratch(al);
String null_term = Copy(scratch, path);
String result = {};
FILE *f = fopen(null_term.data, "rb");
if (f) {
fseek(f, 0, SEEK_END);
result.len = ftell(f);
fseek(f, 0, SEEK_SET);
result.data = (char *)AllocSize(al, result.len + 1);
fread(result.data, result.len, 1, f);
result.data[result.len] = 0;
fclose(f);
}
return result;
}
bool WriteFile(String path, String data) {
Scratch scratch;
String null_term = Copy(scratch, path);
bool result = false;
FILE *f = fopen((const char *)null_term.data, "w");
if (f) {
size_t written = fwrite(data.data, 1, data.len, f);
if (written == data.len) {
result = true;
}
fclose(f);
}
return result;
}
bool DeleteFile(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
int result = unlink(null_term.data);
return result == 0;
}
MakeDirResult MakeDir(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
int error = mkdir(null_term.data, 0755);
MakeDirResult result = MakeDirResult_Success;
if (error != 0) {
result = MakeDirResult_ErrorOther;
if (errno == EEXIST) result = MakeDirResult_Exists;
}
return result;
}
int64_t GetFileModTime(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat attrib = {};
stat(null_term.data, &attrib);
struct timespec ts = attrib.st_mtim;
int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll;
return result;
}
String GetAbsolutePath(Allocator al, String path) {
return path;
}
bool FileExists(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
bool result = false;
if (access((char *)null_term.data, F_OK) == 0) {
result = true;
}
return result;
}
bool IsDir(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat s;
if (stat(null_term.data, &s) != 0)
return false;
return S_ISDIR(s.st_mode);
}
bool IsFile(String path) {
Scratch scratch;
String null_term = Copy(scratch, path);
struct stat s;
if (stat(null_term.data, &s) != 0)
return false;
return S_ISREG(s.st_mode);
}
bool IsAbsolute(String path) {
bool result = path.len && path.data[0] == '/';
return result;
}
String GetWorkingDir(Allocator al) {
return Copy(al, "/workingdir");
}
String GetExePath(Allocator al) {
return Copy(al, "/text_editor");
}
String GetExeDir(Allocator al) {
Scratch scratch(al);
String exe_path = GetExePath(scratch);
String dir = ChopLastSlash(exe_path);
String result = Copy(al, dir);
return result;
}
double get_time_in_micros(void) {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000);
}
bool IsValid(const FileIter &it) {
return it.is_valid;
}
void Advance(FileIter *it) {
struct dirent *file = NULL;
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 = Copy(it->allocator, file->d_name);
const char *dir_char_ending = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = Format(it->allocator, "%.*s%s%s%s", FmtString(it->path), separator, file->d_name, dir_char_ending);
it->absolute_path = GetAbsolutePath(it->allocator, it->relative_path);
if (it->is_directory) it->absolute_path = Format(it->allocator, "%.*s/", FmtString(it->absolute_path));
it->is_valid = true;
return;
}
it->is_valid = false;
closedir((DIR *)it->dir);
}
FileIter IterateFiles(Allocator alo, String path) {
FileIter it = {};
it.allocator = alo;
it.path = path;
Scratch scratch(alo);
String null_term = Copy(scratch, path);
it.dir = (void *)opendir((char *)null_term.data);
if (it.dir) {
Advance(&it);
}
return it;
}
Array<char *> SplitCommand(Allocator allocator, String command_line) {
Array<char *> cmd = {allocator};
String curr = {};
for (int i = 0; i < command_line.len; i += 1) {
if (command_line.data[i] == ' ') {
if (curr.len > 0) {
Add(&cmd, Copy(allocator, curr).data);
curr = {};
}
continue;
}
if (curr.len == 0) {
curr.data = command_line.data + i;
}
curr.len += 1;
}
if (curr.len > 0) {
Add(&cmd, Copy(allocator, curr).data);
}
return cmd;
}
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
return {};
}
bool IsValid(Process *process) {
return false;
}
void KillProcess(Process *process) {
}
String PollStdout(Allocator allocator, Process *process, bool force_read) {
return {};
}
void WriteStdin(Process *process, String string) {
return;
}
void CloseStdin(Process *process) {
return;
}

View File

@@ -1,487 +0,0 @@
#include "filesystem.h"
#include "thread_queue.h"
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <intrin.h>
#include "win32_thread.cpp"
void (*Error)(const char *, ...);
void *VReserve(size_t size) {
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return result;
}
bool VCommit(void *p, size_t size) {
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
return result ? true : false;
}
bool VRelease(void *p, size_t size) {
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
return result ? true : false;
}
bool VDecommit(void *p, size_t size) {
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
return result ? true : false;
}
void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc;
SetConsoleOutputCP(65001);
SetConsoleCP(65001);
}
String ReadFile(Allocator arena, String path) {
bool success = false;
String result = {};
Scratch scratch(arena);
String16 string16 = ToString16(scratch, path);
HANDLE handle = CreateFileW((wchar_t *)string16.data, 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.data = (char *)AllocSize(arena, result.len + 1);
DWORD read;
if (ReadFile(handle, result.data, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
if (read == result.len) {
success = true;
result.data[result.len] = 0;
}
}
}
}
CloseHandle(handle);
}
if (!success) {
Dealloc(arena, &result.data);
result = {};
}
return result;
}
typedef struct Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} Win32_FileIter;
String GetAbsolutePath(Allocator arena, String relative) {
Scratch scratch(arena);
String16 wpath = ToString16(scratch, relative);
wchar_t *wpath_abs = AllocArray(scratch, wchar_t, 4096);
DWORD written = GetFullPathNameW((wchar_t *)wpath.data, 4096, wpath_abs, 0);
if (written == 0)
return {};
String path = ToString(arena, {(char16_t *)wpath_abs, written});
NormalizePathInPlace(path);
return path;
}
bool IsValid(const FileIter &it) {
return it.is_valid;
}
void Advance(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 = ToString(it->allocator, (char16_t *)data->cFileName, WideLength((char16_t *)data->cFileName));
const char *is_dir = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = Format(it->allocator, "%.*s%s%.*s%s", FmtString(it->path), separator, FmtString(it->filename), is_dir);
it->absolute_path = GetAbsolutePath(it->allocator, it->relative_path);
it->is_valid = true;
if (it->is_directory) {
Assert(it->relative_path.data[it->relative_path.len - 1] == '/');
Assert(it->absolute_path.data[it->absolute_path.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
Assert(error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
}
FileIter IterateFiles(Allocator alo, String path) {
FileIter it = {0};
it.allocator = alo;
it.path = path;
String modified_path = Format(it.allocator, "%.*s\\*", FmtString(path));
String16 modified_path16 = ToString16(it.allocator, modified_path);
it.w32 = AllocType(it.allocator, Win32_FileIter);
it.w32->handle = FindFirstFileW((wchar_t *)modified_path16.data, &it.w32->data);
if (it.w32->handle == INVALID_HANDLE_VALUE) {
it.is_valid = false;
return it;
}
Advance(&it);
return it;
}
double get_time_in_micros(void) {
static double invfreq;
if (!invfreq) {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
bool WriteFile(String path, String data) {
bool result = false;
Scratch scratch;
String16 wpath = ToString16(scratch, path);
DWORD access = GENERIC_WRITE;
DWORD creation_disposition = CREATE_ALWAYS;
HANDLE handle = CreateFileW((wchar_t *)wpath.data, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0;
Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
BOOL error = WriteFile(handle, data.data, (DWORD)data.len, &bytes_written, NULL);
if (error == TRUE) {
if (bytes_written == data.len) {
result = true;
}
}
CloseHandle(handle);
}
return result;
}
String GetExePath(Allocator allocator) {
Scratch scratch(allocator);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetModuleFileNameW(0, wbuffer, 4096);
Assert(wsize != 0);
String path = ToString(allocator, (char16_t *)wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
String GetExeDir(Allocator allocator) {
Scratch scratch((Arena *)allocator.object);
String path = GetExePath(scratch);
path = ChopLastSlash(path);
path = Copy(allocator, path);
return path;
}
bool FileExists(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD attribs = GetFileAttributesW((wchar_t *)wbuff.data);
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
return result;
}
bool IsDir(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
bool IsFile(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data);
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
}
String GetWorkingDir(Allocator arena) {
Scratch scratch(arena);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetCurrentDirectoryW(4096, wbuffer);
Assert(wsize != 0);
wbuffer[wsize] = 0;
String path = ToString(arena, (char16_t *)wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
bool IsAbsolute(String path) {
bool result = path.len > 3 && IsAlphabetic(path.data[0]) && path.data[1] == ':' && (path.data[2] == '/' || path.data[2] == '\\');
return result;
}
bool DeleteFile(String path) {
Scratch scratch;
String16 wpath = ToString16(scratch, path);
BOOL success = DeleteFileW((wchar_t *)wpath.data);
bool result = true;
if (success == 0) result = false;
return result;
}
int64_t GetFileModTime(String file) {
Scratch scratch;
String16 string16 = ToString16(scratch, file);
WIN32_FIND_DATAW data;
HANDLE handle = FindFirstFileW((wchar_t *)string16.data, &data);
if (handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
FILETIME time = data.ftLastWriteTime;
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
return result;
} else {
return -1;
}
}
MakeDirResult MakeDir(String path) {
Scratch scratch;
MakeDirResult result = MakeDirResult_Success;
String16 string16 = ToString16(scratch, path);
BOOL success = CreateDirectoryW((wchar_t *)string16.data, NULL);
if (success == 0) {
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
result = MakeDirResult_Exists;
} else if (error == ERROR_PATH_NOT_FOUND) {
result = MakeDirResult_NotFound;
} else {
result = MakeDirResult_ErrorOther;
}
}
return result;
}
struct Win32Process {
HANDLE handle;
HANDLE child_stdout_read;
HANDLE child_stdout_write;
HANDLE child_stdin_read;
HANDLE child_stdin_write;
};
// static_assert(sizeof(Win32Process) < sizeof(Process::platform));
void Win32ReportError(String msg, String cmd) {
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
defer { LocalFree(lpMsgBuf); };
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
}
void Win32CloseProcess(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
process->is_valid = false;
}
static void Win32ProcessError(Process *process, String msg, String cmd) {
Win32ReportError(msg, cmd);
Win32CloseProcess(process);
}
void WriteStdin(Process *process, String string) {
if (string.len == 0) return;
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
DWORD written = 0;
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
Assert(write_error == false);
Assert(written == string.len);
}
void CloseStdin(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
CloseHandle(p->child_stdin_write);
p->child_stdin_write = INVALID_HANDLE_VALUE;
}
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
Process process = {};
Win32Process *p = (Win32Process *)process.platform;
Scratch scratch;
command_line = Format(scratch, "/C %.*s", FmtString(command_line));
p->handle = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->child_stdout_read = INVALID_HANDLE_VALUE;
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdin_write = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES security_atrb = {};
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
security_atrb.bInheritHandle = TRUE;
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
STARTUPINFOW startup = {};
startup.cb = sizeof(STARTUPINFO);
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startup.hStdInput = p->child_stdin_read;
startup.hStdOutput = p->child_stdout_write;
startup.hStdError = p->child_stdout_write;
startup.wShowWindow = SW_HIDE;
String16 cwd = ToString16(scratch, working_dir);
String16 cmd = ToString16(scratch, command_line);
char *env = NULL;
if (enviroment.len) {
Int size = GetSize(enviroment) + enviroment.len + 1;
env = (char *)PushSize(scratch, size);
Int i = 0;
For(enviroment) {
MemoryCopy(env + i, it.data, it.len);
i += it.len;
env[i++] = 0;
}
env[i++] = 0;
}
DWORD dwCreationFlags = 0;
BOOL bInheritHandles = TRUE;
PROCESS_INFORMATION info = {};
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
Win32ProcessError(&process, "failed to create process", command_line);
return process;
}
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(info.hThread);
CloseHandle(p->child_stdin_read);
CloseHandle(p->child_stdout_write);
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->handle = info.hProcess;
process.is_valid = true;
process.id = (int64_t)p->handle;
if (write_stdin.len) {
WriteStdin(&process, write_stdin);
CloseStdin(&process);
}
return process;
}
bool IsValid(Process *process) {
if (process->is_valid == false) return false;
Win32Process *p = (Win32Process *)process->platform;
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
return true;
}
DWORD exit_code;
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
if (get_exit_code_failed) {
exit_code = -1;
}
process->exit_code = (int)exit_code;
Win32CloseProcess(process);
return false;
}
void KillProcess(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
if (terminate_process_error) {
Assert(0);
}
Win32CloseProcess(process);
process->exit_code = -1;
}
String PollStdout(Allocator allocator, Process *process, bool force_read) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
DWORD bytes_avail = 0;
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
if (peek_error) {
return {};
} else if (bytes_avail == 0) {
return {};
}
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
char *buffer = AllocArray(allocator, char, buffer_size);
DWORD bytes_read = 0;
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
if (read_error) {
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
Dealloc(allocator, &buffer);
Win32CloseProcess(process);
return {};
}
String result = {buffer, bytes_read};
return result;
}

View File

@@ -1,84 +0,0 @@
int64_t AtomicIncrement(volatile int64_t *i) {
return InterlockedIncrement64(i);
}
int64_t AtomicCompareAndSwap(volatile int64_t *dst, int64_t exchange, int64_t comperand) {
return InterlockedCompareExchange64(dst, exchange, comperand);
}
void PushWork(WorkQueue *wq, void *data, WorkQueueCallback *callback) {
uint32_t new_index = (wq->index_to_write + 1) % Lengthof(wq->entries);
assert(new_index != wq->index_to_read);
WorkQueueEntry *entry = wq->entries + wq->index_to_write;
entry->data = data;
entry->callback = callback;
wq->completion_goal += 1;
_WriteBarrier();
wq->index_to_write = new_index;
ReleaseSemaphore(wq->semaphore, 1, 0);
}
bool TryDoingWork(WorkQueue *wq) {
bool should_sleep = false;
int64_t original_index_to_read = wq->index_to_read;
int64_t new_index_to_read = (original_index_to_read + 1) % Lengthof(wq->entries);
if (original_index_to_read != wq->index_to_write) {
int64_t index = AtomicCompareAndSwap(&wq->index_to_read, new_index_to_read, original_index_to_read);
if (index == original_index_to_read) {
WorkQueueEntry *entry = wq->entries + index;
entry->callback(entry->data);
AtomicIncrement(&wq->completion_index);
}
} else {
should_sleep = true;
}
return should_sleep;
}
DWORD WINAPI WorkQueueThreadEntry(LPVOID param) {
auto ti = (ThreadStartupInfo *)param;
ThreadCtx ctx = {};
ctx.thread_index = ti->thread_index;
InitScratch();
for (;;) {
if (TryDoingWork(ti->queue)) {
WaitForSingleObject(ti->queue->semaphore, INFINITE);
}
}
}
void InitWorkQueue(WorkQueue *queue, uint32_t thread_count, ThreadStartupInfo *info) {
queue->thread_count = thread_count;
queue->index_to_read = 0;
queue->index_to_write = 0;
queue->completion_index = 0;
queue->completion_goal = 0;
queue->semaphore = CreateSemaphoreExA(0, 0, thread_count, 0, 0, SEMAPHORE_ALL_ACCESS);
Assert(queue->semaphore != INVALID_HANDLE_VALUE);
for (uint32_t i = 0; i < thread_count; i++) {
ThreadStartupInfo *ti = info + i;
ti->thread_index = i;
ti->queue = queue;
DWORD thread_id = 0;
HANDLE thread_handle = CreateThread(0, 0, WorkQueueThreadEntry, ti, 0, &thread_id);
Assert(thread_handle != INVALID_HANDLE_VALUE);
ti->thread_id = thread_id;
CloseHandle(thread_handle);
}
}
void WaitUntilCompletion(WorkQueue *wq) {
while (wq->completion_goal != wq->completion_index) {
TryDoingWork(wq);
}
}
bool IsWorkCompleted(WorkQueue *wq) {
bool result = wq->completion_goal == wq->completion_index;
return result;
}

View File

@@ -1,18 +1,58 @@
#define BASIC_IMPL
#include "basic/basic.h"
#include "basic/filesystem.h"
#include "basic/basic.cpp"
int main() {
InitScratch();
// Basic constructors
{
String a = "thing";
String b("thing");
Assert(a == b);
String c = {};
Assert(c.len == 0 && c.data == NULL);
Assert(a != c);
}
Scratch scratch;
String data = ReadFile(scratch, "../data/init.lua");
Array<String> array = {scratch};
// Accessors
{
String a = "thing";
Assert(Skip(a, 1) == "hing");
Assert(Chop(a, 1) == "thin");
Assert(GetPrefix(a, 1) == "t");
Assert(GetPostfix(a, 1) == "g");
Assert(GetSlice(a, 0) == "thing");
Assert(GetSlice(a, 0, -1) == "thin");
Assert(GetSlice(a, 1, -1) == "hin");
Assert(GetSlice(a, -2, -1) == "n");
}
Add(&array, String{"String BaseLuaConfig = R\"==(\n"});
Add(&array, data);
Add(&array, String{"\n)==\";\n"});
String result = Merge(scratch, array, "");
WriteFile("../src/text_editor/generated_config.cpp", result);
{
Scratch scratch;
String16 a = Format16(scratch, "Memes %d", 30);
Assert(a == u"Memes 30");
}
printf("hello world\n");
}
// #define BASIC_IMPL
// #include "basic/basic.h"
// #include "basic/filesystem.h"
// int main() {
// InitScratch();
// Scratch scratch;
// String data = ReadFile(scratch, "../data/init.lua");
// Array<String> array = {scratch};
// Add(&array, String{"String BaseLuaConfig = R\"==(\n"});
// Add(&array, data);
// Add(&array, String{"\n)==\";\n"});
// String result = Merge(scratch, array, "");
// WriteFile("../src/text_editor/generated_config.cpp", result);
// }

View File

@@ -3,7 +3,7 @@
static SpallProfile spall_ctx;
static SpallBuffer spall_buffer;
double get_time_in_micros(void);
double GetTimeMicros(void);
void BeginProfiler() {
spall_ctx = spall_init_file("hello_world.spall", 1);
@@ -25,14 +25,14 @@ void _BeginProfileScope(const char *name, int len) {
spall_buffer_begin(&spall_ctx, &spall_buffer,
name, // name of your name
len, // name len minus the null terminator
get_time_in_micros() // timestamp in microseconds -- start of your timing block
GetTimeMicros() // timestamp in microseconds -- start of your timing block
);
}
#define BeginProfileScope(name) _BeginProfileScope(#name, sizeof(#name) - 1)
void EndProfileScope() {
spall_buffer_end(&spall_ctx, &spall_buffer,
get_time_in_micros() // timestamp in microseconds -- end of your timing block
GetTimeMicros() // timestamp in microseconds -- end of your timing block
);
}
#define ProfileScope(name) ProfileScope_ PROFILE_SCOPE_VAR_##name(#name, sizeof(#name) - 1)

View File

@@ -1,3 +1,41 @@
REDESIGN and DELETE CODE
- Reduce the amount of actions needed to navigate using keyboard
- Make mouse important but much less so
Needs to change:
- Make it more similar to sublime like editors
- Need Ctrl + P
- Clickable title bar may be cool or whatever but it's pretty bad
- Executing lua commands is clunky, need a real Ctrl+P and keybind actions, popups: remove a lot of the lua functionality, just for config files
- Window, View, Buffer + flags design (or is completely new kind based approach needed for Windows/Views?)
- How to make non-editable, informative, with different font size, title bar. Which might also contain tabs
- How to design clickable tree view in this way?
- How to design Command view?
- How to design popup view (input field)?
- How to design search view? or search and replace view?
- Window management, splitting, GC
- add modified stb_sprintf
Things I like:
- Basic editing
- Configurable Open
- Lua config files work pretty well
DESIGN try to make console less special, make stuff reusable etc.
DESIGN Config file versions, when loading should be checked, at the top of the file, what to do when old version?
ISSUE Ctrl+Alt+Down (DuplicateLine) doesn't work on ubuntu