Files
text_editor/src/basic/basic.h
2025-05-05 08:37:41 +02:00

1780 lines
50 KiB
C++

#pragma once
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#define Assert(x) \
if (!(x)) { \
__debugbreak(); \
}
#define InvalidCodepath() Assert(!"invalid codepath")
#define ElseInvalidCodepath() else {InvalidCodepath()}
#if defined(__APPLE__) && defined(__MACH__)
#define OS_MAC 1
#elif defined(_WIN32)
#define OS_WINDOWS 1
#elif defined(__linux__)
#define OS_POSIX 1
#define OS_LINUX 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_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
#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]))))
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;
}
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;
};
inline void *AllocSize(Allocator alo, size_t size) {
void *result = alo.proc(alo.object, AllocatorKind_Allocate, NULL, size);
memset(result, 0, size);
return result;
}
#define AllocType(alo, Type) (Type *)AllocSize(alo, sizeof(Type))
#define AllocArray(alo, Type, count) (Type *)AllocSize(alo, sizeof(Type) * (count))
template <class T>
void Dealloc(Allocator alo, T **p) {
if (*p == NULL) return;
alo.proc(alo.object, AllocatorKind_Deallocate, *p, 0);
*p = NULL;
}
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)
/*
// 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)
constexpr int64_t SLICE_LAST = INT64_MIN;
inline int64_t StringLen(char *string) {
if (!string) return 0;
int64_t i = 0;
while (string[i]) i += 1;
return i;
}
inline int64_t WideLength(char16_t *string) {
if (!string) return 0;
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
template <class T>
struct Slice {
T *data;
int64_t len;
Slice() = default;
Slice(T *s, int64_t l) : data(s), len(l) {}
Slice(char *s) : data(s), len(StringLen(s)) {}
Slice(const char *s) : data((char *)s), len(StringLen((char *)s)) {}
Slice(const char *s, int64_t l) : data((char *)s), len(l) {}
Slice(char16_t *s) : data(s), len(WideLength(s)) {}
Slice(const char16_t *s) : data((char16_t *)s), len(WideLength((char16_t *)s)) {}
Slice(const char16_t *s, int64_t l) : data((char16_t *)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};
}
template <class T>
struct CircularArray {
Allocator allocator;
T *data;
int16_t cap;
int16_t write;
};
template <class T>
CircularArray<T> MakeCircularArray(Allocator allocator, int size) {
CircularArray<T> arr = {allocator};
arr.data = AllocArray(allocator, T, size);
arr.cap = size;
return arr;
}
template <class T>
void Add(CircularArray<T> *arr, T item) {
if (arr->cap == 0) {
arr->allocator = GetSystemAllocator();
arr->cap = 128;
arr->data = AllocArray(arr->allocator, T, arr->cap);
}
int16_t i = arr->write;
arr->write = (arr->write + 1) % arr->cap;
arr->data[i] = item;
}
static int GetCircularIndex(int cap, int idx) {
int result = idx % cap;
if (result < 0) result = cap + result;
return result;
}
template <class T>
T Get(CircularArray<T> *arr, int idx, T default_value) {
int idx = circ->write - 1 - i;
idx = GetCircularIndex(circ->size, idx);
int result = circ->data[idx];
return result;
}
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 {}; }
};
using String = Slice<char>;
using String16 = Slice<char16_t>;
bool IsValid(UTF8Iter &iter);
void Advance(UTF8Iter *iter);
UTF8Iter IterateUTF8Ex(char *data, int64_t len);
UTF8Iter IterateUTF8(char *data);
UTF8Iter IterateUTF8(String string);
bool IsAlphabetic(char a);
#define FmtString(string) (int)(string).len, (string).data
bool AreEqual(String a, String b, unsigned ignore_case = false);
int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
inline bool operator==(String a, String b) { return AreEqual(a, b); }
inline bool operator!=(String a, String b) { return !AreEqual(a, b); }
#define STRING_FORMAT(allocator, data, result) \
va_list args1; \
va_start(args1, data); \
String result = FormatV(allocator, data, args1); \
va_end(args1)
String Format(Allocator allocator, const char *data, ...);
String FormatV(Allocator allocator, const char *data, va_list args1);
String ToString(Allocator allocator, String16 string);
String ToString(Allocator allocator, char16_t *string, int64_t len);
String ToString(Allocator allocator, char16_t *wstring);
String16 ToString16(Allocator allocator, String string);
char16_t *ToWidechar(Allocator allocator, String string);
void NormalizePathInPlace(String s);
String CutLastSlash(String *s);
String ChopLastSlash(String s);
String ChopLastPeriod(String s);
String SkipToLastSlash(String s);
String SkipToLastPeriod(String s);
String Copy(Allocator allocator, String string);
Int GetSize(Array<String> array);
/*
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;
}
};
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() + [&]()
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);
bool VDecommit(void *p, size_t size);
void InitArena(Arena *arena, size_t reserve = GiB(4));
Arena *AllocArena(size_t reserve = GiB(4));
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);
};
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;
}
//
// Implementation
//
#ifdef BASIC_IMPL
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;
}
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;
}
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;
}
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; \
}
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;
}
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;
}
bool IsValid(UTF8Iter &iter) {
return iter.item;
}
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;
}
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;
}
UTF8Iter IterateUTF8(char *data) {
int64_t length = 0;
while (data[length]) length += 1;
return IterateUTF8Ex(data, length);
}
UTF8Iter IterateUTF8(String string) {
return IterateUTF8Ex(string.data, string.len);
}
bool IsUTF8ContinuationByte(char c) {
char result = (c & 0b11000000) == 0b10000000;
return result;
}
char ToLowerCase(char a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
char ToUpperCase(char a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
bool IsWhitespace(char w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
bool IsAlphabetic(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
bool IsIdent(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
bool IsDigit(char a) {
bool result = a >= '0' && a <= '9';
return result;
}
bool IsAlphanumeric(char a) {
bool result = IsDigit(a) || IsAlphabetic(a);
return result;
}
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;
}
bool EndsWith(String a, String end, unsigned ignore_case = false) {
String a_end = GetPostfix(a, end.len);
bool result = AreEqual(end, a_end, ignore_case);
return result;
}
bool StartsWith(String a, String start, unsigned ignore_case = false) {
String a_start = GetPrefix(a, start.len);
bool result = AreEqual(start, a_start, ignore_case);
return result;
}
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;
}
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;
}
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;
}
String Copy(Allocator allocator, char *string) {
return Copy(allocator, {string, (int64_t)strlen(string)});
}
char *NullTerminate(Allocator allocator, String string) {
if (string.data[string.len] != 0) return Copy(allocator, string).data;
return string.data;
}
void NormalizePathInPlace(String s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.data[i] == '\\')
s.data[i] = '/';
}
}
String NormalizePath(Allocator allocator, String s) {
String copy = Copy(allocator, s);
NormalizePathInPlace(copy);
return copy;
}
typedef int SeekFlag;
enum {
SeekFlag_None = 0,
SeekFlag_IgnoreCase = 1,
SeekFlag_MatchFindLast = 2,
};
bool Seek(String string, String 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;
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;
}
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;
}
String CutLastSlash(String *s) {
String result = *s;
Seek(*s, "/", &s->len, SeekFlag_MatchFindLast);
result = Skip(result, s->len);
return result;
}
String ChopLastSlash(String s) {
String result = s;
Seek(s, "/", &result.len, SeekFlag_MatchFindLast);
return result;
}
String ChopLastPeriod(String s) {
String result = s;
Seek(s, ".", &result.len, SeekFlag_MatchFindLast);
return result;
}
String SkipToLastSlash(String s) {
int64_t pos;
String result = s;
if (Seek(s, "/", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
String SkipToLastPeriod(String s) {
int64_t pos;
String result = s;
if (Seek(s, ".", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
}
String CutPrefix(String *string, int64_t len) {
String result = GetPrefix(*string, len);
*string = Skip(*string, len);
return result;
}
String CutPostfix(String *string, int64_t len) {
String result = GetPostfix(*string, len);
*string = Chop(*string, len);
return result;
}
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;
}
Int GetSize(Array<String> array) {
Int result = 0;
For (array) result += it.len;
return result;
}
#include <stdio.h>
String FormatV(Allocator allocator, const char *data, va_list args1) {
va_list args2;
va_copy(args2, args1);
int64_t len = vsnprintf(0, 0, data, args2);
va_end(args2);
char *result = (char *)AllocSize(allocator, sizeof(char) * (len + 1));
vsnprintf(result, (int)(len + 1), data, args1);
String res = {result, len};
return res;
}
String Format(Allocator allocator, const char *data, ...) {
STRING_FORMAT(allocator, data, result);
return result;
}
String16 ToString16(Allocator allocator, String string) {
Assert(sizeof(char16_t) == 2);
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;
}
char16_t *ToWidechar(Allocator allocator, String string) {
String16 result = ToString16(allocator, string);
return result.data;
}
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;
}
String ToString(Allocator allocator, char16_t *string, int64_t len) {
return ToString(allocator, {string, len});
}
String ToString(Allocator allocator, char16_t *wstring) {
int64_t size = WideLength(wstring);
String result = ToString(allocator, {wstring, size});
return result;
}
#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
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);
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);
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];
void InitScratch() {
for (int i = 0; i < Lengthof(ScratchArenaPool); i += 1) {
ScratchArenaPool[i] = AllocArena();
}
}
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;
}
#endif // BASIC_IMPL