BlockArena, a bit of allocator rework
This commit is contained in:
@@ -14,3 +14,5 @@ cl %flags% ../src/text_editor/text_editor.cpp -Fe:te.exe -I../src/external/SDL/i
|
|||||||
copy te.exe ..\data\te.exe
|
copy te.exe ..\data\te.exe
|
||||||
copy te.pdb ..\data\te.pdb
|
copy te.pdb ..\data\te.pdb
|
||||||
echo written ..\data\te.exe
|
echo written ..\data\te.exe
|
||||||
|
cd ..
|
||||||
|
rem /fsanitize=address
|
||||||
@@ -21,7 +21,8 @@ Things I like:
|
|||||||
- Lua config files work pretty well
|
- Lua config files work pretty well
|
||||||
|
|
||||||
Splits:
|
Splits:
|
||||||
- Buffer16.cpp / h
|
- Buffer16.cpp / h vs Buffer ?
|
||||||
|
- Move font params to MainFont, allow for more fonts
|
||||||
- Block arena and refactor alloc
|
- Block arena and refactor alloc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
@@ -20,44 +21,44 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
void *VReserve(size_t size) {
|
API void *VReserve(size_t size) {
|
||||||
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCommit(void *p, size_t size) {
|
API bool VCommit(void *p, size_t size) {
|
||||||
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
return result ? true : false;
|
return result ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VRelease(void *p, size_t size) {
|
API bool VRelease(void *p, size_t size) {
|
||||||
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
|
||||||
return result ? true : false;
|
return result ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VDecommit(void *p, size_t size) {
|
API bool VDecommit(void *p, size_t size) {
|
||||||
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
|
||||||
return result ? true : false;
|
return result ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif OS_LINUX || OS_MAC
|
#elif OS_LINUX || OS_MAC
|
||||||
|
|
||||||
void *VReserve(size_t size) {
|
API void *VReserve(size_t size) {
|
||||||
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
|
void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0);
|
||||||
return result == (void *)-1 ? 0 : result;
|
return result == (void *)-1 ? 0 : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCommit(void *p, size_t size) {
|
API bool VCommit(void *p, size_t size) {
|
||||||
int result = mprotect(p, size, PROT_READ | PROT_WRITE);
|
int result = mprotect(p, size, PROT_READ | PROT_WRITE);
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VRelease(void *p, size_t size) {
|
API bool VRelease(void *p, size_t size) {
|
||||||
int result = munmap(p, size);
|
int result = munmap(p, size);
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VDecommit(void *p, size_t size) {
|
API bool VDecommit(void *p, size_t size) {
|
||||||
mprotect(p, size, PROT_NONE);
|
mprotect(p, size, PROT_NONE);
|
||||||
madvise(p, size, MADV_DONTNEED);
|
madvise(p, size, MADV_DONTNEED);
|
||||||
return true;
|
return true;
|
||||||
@@ -65,29 +66,102 @@ bool VDecommit(void *p, size_t size) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void *VReserve(size_t size) {
|
API void *VReserve(size_t size) {
|
||||||
InvalidCodepath();
|
InvalidCodepath();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCommit(void *p, size_t size) {
|
API bool VCommit(void *p, size_t size) {
|
||||||
InvalidCodepath();
|
InvalidCodepath();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VRelease(void *p, size_t size) {
|
API bool VRelease(void *p, size_t size) {
|
||||||
InvalidCodepath();
|
InvalidCodepath();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VDecommit(void *p, size_t size) {
|
API bool VDecommit(void *p, size_t size) {
|
||||||
InvalidCodepath();
|
InvalidCodepath();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void InitArena(Arena *arena, size_t reserve) {
|
API void *SystemAllocatorProc(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);
|
||||||
|
} else {
|
||||||
|
InvalidCodepath();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
API Allocator GetSystemAllocator() {
|
||||||
|
Allocator result = {SystemAllocatorProc};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void *_AllocSize_(Allocator alo, size_t size) {
|
||||||
|
void *result = alo.proc(alo.object, AllocatorKind_Allocate, NULL, size);
|
||||||
|
memset(result, 0, size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemoryRecord {
|
||||||
|
size_t size;
|
||||||
|
void *addr;
|
||||||
|
char *file;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
thread_local Array<MemoryRecord> MemoryTrackingRecord;
|
||||||
|
|
||||||
|
|
||||||
|
API void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||||
|
void *result = NULL;
|
||||||
|
|
||||||
|
if (kind == AllocatorKind_Allocate) {
|
||||||
|
result = malloc(size);
|
||||||
|
Add(&MemoryTrackingRecord, {size, result, LocationTraceO.file, LocationTraceO.line});
|
||||||
|
Assert(result);
|
||||||
|
} else if (kind == AllocatorKind_Deallocate) {
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
For(IterateInReverse(&MemoryTrackingRecord)) {
|
||||||
|
if (it.addr == p) {
|
||||||
|
found = true;
|
||||||
|
UnorderedRemove(&MemoryTrackingRecord, it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(found);
|
||||||
|
} else {
|
||||||
|
InvalidCodepath();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
API void TrackingAllocatorCheck() {
|
||||||
|
// For (MemoryTrackingRecord) {
|
||||||
|
// ReportConsolef("%s(%d): error: memory leak");
|
||||||
|
// }
|
||||||
|
Assert(MemoryTrackingRecord.len == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
API Allocator GetTrackingAllocator() {
|
||||||
|
Allocator result = {TrackingAllocatorProc};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Virtual Arena
|
||||||
|
|
||||||
|
API void InitArena(VirtualArena *arena, size_t reserve) {
|
||||||
reserve = AlignUp(reserve, PAGE_SIZE);
|
reserve = AlignUp(reserve, PAGE_SIZE);
|
||||||
arena->align = DEFAULT_ALIGNMENT;
|
arena->align = DEFAULT_ALIGNMENT;
|
||||||
arena->data = (uint8_t *)VReserve(reserve);
|
arena->data = (uint8_t *)VReserve(reserve);
|
||||||
@@ -96,8 +170,8 @@ void InitArena(Arena *arena, size_t reserve) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena *AllocArena(Allocator allocator, size_t size) {
|
API VirtualArena *AllocArena(Allocator allocator, size_t size) {
|
||||||
Arena *result = AllocType(allocator, Arena);
|
VirtualArena *result = AllocType(allocator, VirtualArena);
|
||||||
result->data = (uint8_t *)AllocSize(allocator, size);
|
result->data = (uint8_t *)AllocSize(allocator, size);
|
||||||
result->reserve = size;
|
result->reserve = size;
|
||||||
result->commit = size;
|
result->commit = size;
|
||||||
@@ -105,8 +179,8 @@ Arena *AllocArena(Allocator allocator, size_t size) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena *AllocArena(size_t reserve) {
|
API VirtualArena *AllocArena(size_t reserve) {
|
||||||
Arena *result = NULL;
|
VirtualArena *result = NULL;
|
||||||
|
|
||||||
void *data = VReserve(reserve);
|
void *data = VReserve(reserve);
|
||||||
if (!data) return result;
|
if (!data) return result;
|
||||||
@@ -117,16 +191,16 @@ Arena *AllocArena(size_t reserve) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (Arena *)data;
|
result = (VirtualArena *)data;
|
||||||
result->data = (uint8_t *)data;
|
result->data = (uint8_t *)data;
|
||||||
result->reserve = reserve;
|
result->reserve = reserve;
|
||||||
result->commit = PAGE_SIZE;
|
result->commit = PAGE_SIZE;
|
||||||
result->len = result->base_len = sizeof(Arena);
|
result->len = result->base_len = sizeof(VirtualArena);
|
||||||
result->align = DEFAULT_ALIGNMENT;
|
result->align = DEFAULT_ALIGNMENT;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *PushSize(Arena *arena, size_t size) {
|
API void *PushSize(VirtualArena *arena, size_t size) {
|
||||||
// base_len is used for bootstraping arenas, it denotes the
|
// base_len is used for bootstraping arenas, it denotes the
|
||||||
// space occupied by the arena. If len is smaller then base_len then
|
// space occupied by the arena. If len is smaller then base_len then
|
||||||
// we start to overwrite the arena itself - pure barbarism.
|
// we start to overwrite the arena itself - pure barbarism.
|
||||||
@@ -145,7 +219,7 @@ void *PushSize(Arena *arena, size_t size) {
|
|||||||
if (to_commit_clamped > 0) {
|
if (to_commit_clamped > 0) {
|
||||||
bool success = VCommit(arena->data + arena->commit, to_commit_clamped);
|
bool success = VCommit(arena->data + arena->commit, to_commit_clamped);
|
||||||
if (success) {
|
if (success) {
|
||||||
MA_ASAN_UNPOISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped);
|
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped);
|
||||||
arena->commit += to_commit_clamped;
|
arena->commit += to_commit_clamped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,14 +233,14 @@ void *PushSize(Arena *arena, size_t size) {
|
|||||||
return (void *)result;
|
return (void *)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Release(Arena *arena) {
|
API void Release(VirtualArena *arena) {
|
||||||
if (arena == NULL || arena->data == NULL) return;
|
if (arena == NULL || arena->data == NULL) return;
|
||||||
bool zero_memory = (uint8_t *)arena != arena->data;
|
bool zero_memory = (uint8_t *)arena != arena->data;
|
||||||
VRelease(arena->data, arena->reserve);
|
VRelease(arena->data, arena->reserve);
|
||||||
if (zero_memory) MemoryZero(arena, sizeof(Arena));
|
if (zero_memory) MemoryZero(arena, sizeof(VirtualArena));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopToPos(Arena *arena, size_t pos) {
|
API void PopToPos(VirtualArena *arena, size_t pos) {
|
||||||
// base_len is used for bootstraping arenas, it denotes the
|
// base_len is used for bootstraping arenas, it denotes the
|
||||||
// space occupied by the arena. If len is smaller then base_len then
|
// space occupied by the arena. If len is smaller then base_len then
|
||||||
// we start to overwrite the arena itself - pure barbarism.
|
// we start to overwrite the arena itself - pure barbarism.
|
||||||
@@ -178,9 +252,9 @@ void PopToPos(Arena *arena, size_t pos) {
|
|||||||
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size);
|
MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
API void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||||
if (kind == AllocatorKind_Allocate) {
|
if (kind == AllocatorKind_Allocate) {
|
||||||
return PushSize((Arena *)object, size);
|
return PushSize((VirtualArena *)object, size);
|
||||||
} else if (AllocatorKind_Deallocate) {
|
} else if (AllocatorKind_Deallocate) {
|
||||||
} else {
|
} else {
|
||||||
Assert(!"invalid codepath");
|
Assert(!"invalid codepath");
|
||||||
@@ -188,106 +262,123 @@ void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local Arena *ScratchArenaPool[4];
|
///////////////////////////////
|
||||||
|
// Block Arena
|
||||||
|
|
||||||
#if OS_WASM
|
API void *PushSize(BlockArena *arena, size_t size) {
|
||||||
void InitScratch() {
|
if (size > (size_t)(arena->end - arena->start)) {
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
size_t block_size = MiB(1);
|
||||||
ScratchArenaPool[0] = AllocArena(sys_allocator, MiB(16));
|
if (size > block_size) {
|
||||||
ScratchArenaPool[1] = AllocArena(sys_allocator, MiB(8));
|
block_size = size;
|
||||||
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 (arena->allocator.proc == NULL) {
|
||||||
|
arena->allocator = GetSystemAllocator();
|
||||||
|
}
|
||||||
|
BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode));
|
||||||
|
Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0);
|
||||||
|
arena->start = new_block->start;
|
||||||
|
new_block->end = arena->end = new_block->start + block_size;
|
||||||
|
SLL_STACK_ADD(arena->blocks, new_block);
|
||||||
|
}
|
||||||
|
U8 *result = arena->start;
|
||||||
|
Assert(GetAlignOffset((size_t)result, DEFAULT_ALIGNMENT) == 0);
|
||||||
|
arena->start = (U8 *)AlignUp((size_t)(arena->start + size), DEFAULT_ALIGNMENT);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (unoccupied) {
|
API void Release(BlockArena *arena) {
|
||||||
|
for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) {
|
||||||
|
next = it->next;
|
||||||
|
Dealloc(arena->allocator, it);
|
||||||
|
}
|
||||||
|
MemoryZero(arena, sizeof(BlockArena));
|
||||||
|
}
|
||||||
|
|
||||||
|
API void Unwind(BlockArena *arena, U8 *pos) {
|
||||||
|
bool contains = false;
|
||||||
|
for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) {
|
||||||
|
next = it->next;
|
||||||
|
if ((pos >= it->start) && (pos < it->end)) {
|
||||||
|
contains = true;
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
arena->blocks = arena->blocks->next;
|
||||||
|
Dealloc(arena->allocator, it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Assert(contains || pos == NULL);
|
||||||
// Failed to get free scratch memory, this is a fatal error, this shouldnt happen
|
arena->start = pos;
|
||||||
Assert(unoccupied);
|
|
||||||
TempArena result = BeginTemp(unoccupied);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <stdlib.h>
|
API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||||
void *SystemAllocator_Alloc(void *object, int kind, void *p, size_t size) {
|
BlockArena *arena = (BlockArena *)object;
|
||||||
void *result = NULL;
|
|
||||||
if (kind == AllocatorKind_Allocate) {
|
if (kind == AllocatorKind_Allocate) {
|
||||||
result = malloc(size);
|
return PushSize(arena, size);
|
||||||
Assert(result);
|
} else if (AllocatorKind_Deallocate) {
|
||||||
} else if (kind == AllocatorKind_Deallocate) {
|
|
||||||
free(p);
|
|
||||||
} else {
|
} else {
|
||||||
InvalidCodepath();
|
Assert(!"invalid codepath");
|
||||||
}
|
}
|
||||||
return result;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator GetSystemAllocator() {
|
void TestArena() {
|
||||||
Allocator result = {SystemAllocator_Alloc};
|
Allocator memory_tracking_allocator = GetTrackingAllocator();
|
||||||
return result;
|
{
|
||||||
}
|
BlockArena arena = {};
|
||||||
|
arena.allocator = memory_tracking_allocator;
|
||||||
struct MemoryRecord {
|
for (int i = 0; i < 10000; i += 1) {
|
||||||
size_t size;
|
int *vals = (int *)PushSize(&arena, sizeof(int)*i);
|
||||||
void *addr;
|
for (int j = 0; j < i; j += 1) {
|
||||||
bool deallocated;
|
vals[j] = j;
|
||||||
};
|
|
||||||
Array<MemoryRecord> MemoryTrackingRecord;
|
|
||||||
|
|
||||||
|
|
||||||
void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) {
|
|
||||||
void *result = NULL;
|
|
||||||
|
|
||||||
if (kind == AllocatorKind_Allocate) {
|
|
||||||
result = malloc(size);
|
|
||||||
Add(&MemoryTrackingRecord, {size, result});
|
|
||||||
Assert(result);
|
|
||||||
} else if (kind == AllocatorKind_Deallocate) {
|
|
||||||
free(p);
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
For(MemoryTrackingRecord){
|
|
||||||
if (it.addr == p) {
|
|
||||||
it.deallocated = true;
|
|
||||||
found = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert(found);
|
Release(&arena);
|
||||||
} else {
|
TrackingAllocatorCheck();
|
||||||
InvalidCodepath();
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackingAllocatorCheck() {
|
{
|
||||||
For (MemoryTrackingRecord) {
|
BlockArena arena = {};
|
||||||
Assert(it.deallocated);
|
U8 *start = arena.start;
|
||||||
|
arena.allocator = memory_tracking_allocator;
|
||||||
|
int *vals = (int *)PushSize(&arena, sizeof(int) * 32);
|
||||||
|
for (int i = 0; i < 32; i += 1) vals[i] = i;
|
||||||
|
Unwind(&arena, (U8 *)vals);
|
||||||
|
Assert(arena.blocks);
|
||||||
|
Assert(arena.blocks->next == NULL);
|
||||||
|
Assert(arena.start == (U8 *)vals);
|
||||||
|
Assert(arena.blocks[0].start == (U8 *)vals);
|
||||||
|
Unwind(&arena, NULL);
|
||||||
|
Dealloc(arena.allocator, arena.blocks);
|
||||||
|
TrackingAllocatorCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BlockArena arena = {};
|
||||||
|
arena.allocator = memory_tracking_allocator;
|
||||||
|
int *vals = (int *)PushSize(&arena, sizeof(int) * 32);
|
||||||
|
for (int i = 0; i < 32; i += 1) vals[i] = i;
|
||||||
|
|
||||||
|
U8 *p = arena.start;
|
||||||
|
U8 *a = (U8 *)PushSize(&arena, KiB(32));
|
||||||
|
U8 *b = (U8 *)PushSize(&arena, KiB(1000));
|
||||||
|
Assert(arena.blocks);
|
||||||
|
Assert(arena.blocks->next);
|
||||||
|
Assert(arena.blocks->next->next == NULL);
|
||||||
|
Unwind(&arena, a);
|
||||||
|
Assert(arena.blocks);
|
||||||
|
Assert(arena.blocks->next == NULL);
|
||||||
|
Assert(arena.start == p);
|
||||||
|
|
||||||
|
Release(&arena);
|
||||||
|
TrackingAllocatorCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
BlockArena arena = {};
|
||||||
|
arena.allocator = memory_tracking_allocator;
|
||||||
|
U8 *a = (U8 *)PushSize(&arena, KiB(2000));
|
||||||
|
Assert((size_t)(arena.blocks[0].end - arena.blocks[0].start) == KiB(2000));
|
||||||
|
Release(&arena);
|
||||||
|
TrackingAllocatorCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator GetTrackingAllocator() {
|
|
||||||
Allocator result = {TrackingAllocatorProc};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
struct LocationTrace {
|
||||||
|
char *file;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
thread_local LocationTrace LocationTraceO;
|
||||||
|
#define LOCATION_TRACE (LocationTraceO.file = __FILE__, LocationTraceO.line = __LINE__)
|
||||||
|
#else
|
||||||
|
#define LOCATION_TRACE
|
||||||
|
#endif
|
||||||
|
|
||||||
const int AllocatorKind_Allocate = 1;
|
const int AllocatorKind_Allocate = 1;
|
||||||
const int AllocatorKind_Deallocate = 2;
|
const int AllocatorKind_Deallocate = 2;
|
||||||
|
|
||||||
@@ -10,29 +22,47 @@ struct Allocator {
|
|||||||
|
|
||||||
#define AllocType(alo, Type) (Type *)AllocSize(alo, sizeof(Type))
|
#define AllocType(alo, Type) (Type *)AllocSize(alo, sizeof(Type))
|
||||||
#define AllocArray(alo, Type, count) (Type *)AllocSize(alo, sizeof(Type) * (count))
|
#define AllocArray(alo, Type, count) (Type *)AllocSize(alo, sizeof(Type) * (count))
|
||||||
inline void *AllocSize(Allocator alo, size_t size) {
|
#define AllocSize(alo, size) (LOCATION_TRACE, _AllocSize_(alo, size))
|
||||||
void *result = alo.proc(alo.object, AllocatorKind_Allocate, NULL, size);
|
API void *_AllocSize_(Allocator alo, size_t size);
|
||||||
memset(result, 0, size);
|
inline void Dealloc(Allocator alo, void *p) { if (p) (alo).proc((alo).object, AllocatorKind_Deallocate, (p), 0); }
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
API Allocator GetSystemAllocator();
|
||||||
void Dealloc(Allocator alo, T **p) {
|
API void TrackingAllocatorCheck();
|
||||||
if (*p == NULL) return;
|
API Allocator GetTrackingAllocator();
|
||||||
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 MemoryZero(x, size) memset(x, 0, size)
|
||||||
#define MemoryCopy(dst, src, size) memcpy(dst, src, size)
|
#define MemoryCopy(dst, src, size) memcpy(dst, src, size)
|
||||||
#define MemoryMove(dst, src, size) memmove(dst, src, size)
|
#define MemoryMove(dst, src, size) memmove(dst, src, size)
|
||||||
|
|
||||||
const int PAGE_SIZE = 4096;
|
///////////////////
|
||||||
const int DEFAULT_ALIGNMENT = sizeof(void *);
|
// Block Arena
|
||||||
|
///////////////////
|
||||||
|
API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size);
|
||||||
|
|
||||||
struct Arena {
|
struct BlockArenaNode {
|
||||||
|
BlockArenaNode *next;
|
||||||
|
U8 *end;
|
||||||
|
U8 start[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockArena {
|
||||||
|
U8 *start;
|
||||||
|
U8 *end;
|
||||||
|
BlockArenaNode *blocks;
|
||||||
|
Allocator allocator;
|
||||||
|
|
||||||
|
operator Allocator() { return {BlockArenaAllocatorProc, this}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
API void *PushSize(BlockArena *arena, size_t size);
|
||||||
|
API void Release(BlockArena *arena);
|
||||||
|
API void Unwind(BlockArena *arena, U8 *pos);
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
// Virtual Arena
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
struct VirtualArena {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t base_len; // to prevent self deleting the arena
|
size_t base_len; // to prevent self deleting the arena
|
||||||
@@ -46,50 +76,40 @@ struct Arena {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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 SetLen(VirtualArena *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 void Pop(VirtualArena *arena, size_t size) { SetLen(arena, arena->len - size); }
|
||||||
inline TempArena BeginTemp(Arena *arena) { return {arena, arena->len}; }
|
inline void Clear(VirtualArena *arena) { SetLen(arena, 0); }
|
||||||
inline void EndTemp(TempArena temp) { SetLen(temp.arena, temp.len); }
|
|
||||||
inline void Clear(Arena *arena) { SetLen(arena, 0); }
|
|
||||||
|
|
||||||
void *VReserve(size_t size);
|
API void *VReserve(size_t size);
|
||||||
bool VCommit(void *p, size_t size);
|
API bool VCommit(void *p, size_t size);
|
||||||
bool VRelease(void *p, size_t size);
|
API bool VRelease(void *p, size_t size);
|
||||||
bool VDecommit(void *p, size_t size);
|
API bool VDecommit(void *p, size_t size);
|
||||||
|
|
||||||
void InitArena(Arena *arena, size_t reserve = MiB(256));
|
API void InitArena(VirtualArena *arena, size_t reserve = MiB(256));
|
||||||
Arena *AllocArena(size_t reserve = MiB(256));
|
API VirtualArena *AllocArena(size_t reserve = MiB(256));
|
||||||
Arena *AllocArena(Allocator allocator, size_t size);
|
API VirtualArena *AllocArena(Allocator allocator, size_t size);
|
||||||
void *PushSize(Arena *arena, size_t size);
|
API void *PushSize(VirtualArena *arena, size_t size);
|
||||||
void Release(Arena *arena);
|
API void Release(VirtualArena *arena);
|
||||||
void InitScratch();
|
|
||||||
TempArena GetScratchEx(Arena **conflicts, int conflict_count);
|
|
||||||
|
|
||||||
inline TempArena GetScratch(Arena *c1 = NULL, Arena *c2 = NULL) {
|
///////////////////////////////
|
||||||
int count = c1 ? 1 : 0;
|
// Scratch
|
||||||
count += c2 ? 1 : 0;
|
|
||||||
Arena *conflicts[] = {c1, c2};
|
|
||||||
return GetScratchEx(conflicts, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Scratch {
|
struct Scratch {
|
||||||
TempArena checkpoint;
|
BlockArena arena = {};
|
||||||
Scratch() { this->checkpoint = GetScratch(); }
|
Scratch() {}
|
||||||
|
Scratch(BlockArena *conflict) {}
|
||||||
Scratch(Arena *conflict) { this->checkpoint = GetScratch(conflict); }
|
Scratch(BlockArena *c1, BlockArena *c2) {}
|
||||||
Scratch(Arena *c1, Arena *c2) { this->checkpoint = GetScratch(c1, c2); }
|
Scratch(Allocator conflict) {}
|
||||||
Scratch(Allocator conflict) { this->checkpoint = GetScratch((Arena *)conflict.object); }
|
Scratch(Allocator c1, Allocator c2) {}
|
||||||
Scratch(Allocator c1, Allocator c2) { this->checkpoint = GetScratch((Arena *)c1.object, (Arena *)c2.object); }
|
~Scratch() { Release(&arena); }
|
||||||
~Scratch() { EndTemp(checkpoint); }
|
operator BlockArena *() { return &arena; }
|
||||||
operator Arena *() { return checkpoint.arena; }
|
operator Allocator() { return arena; }
|
||||||
operator Allocator() { return *checkpoint.arena; }
|
|
||||||
|
|
||||||
private: // @Note: Disable copy constructors, cause its error prone
|
private: // @Note: Disable copy constructors, cause its error prone
|
||||||
Scratch(Scratch &arena);
|
Scratch(Scratch &arena);
|
||||||
Scratch(Scratch &arena, Scratch &a2);
|
Scratch(Scratch &arena, Scratch &a2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int PAGE_SIZE = 4096;
|
||||||
|
const int DEFAULT_ALIGNMENT = sizeof(void *);
|
||||||
@@ -214,7 +214,7 @@ void Reserve(Array<T> *arr, int64_t size) {
|
|||||||
T *new_data = AllocArray(arr->allocator, T, size);
|
T *new_data = AllocArray(arr->allocator, T, size);
|
||||||
Assert(new_data);
|
Assert(new_data);
|
||||||
memcpy(new_data, arr->data, arr->len * sizeof(T));
|
memcpy(new_data, arr->data, arr->len * sizeof(T));
|
||||||
Dealloc(arr->allocator, &arr->data);
|
Dealloc(arr->allocator, arr->data);
|
||||||
|
|
||||||
arr->data = new_data;
|
arr->data = new_data;
|
||||||
arr->cap = size;
|
arr->cap = size;
|
||||||
@@ -393,7 +393,10 @@ T Get(Array<T> &arr, int64_t i, T default_value = {}) {
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void Dealloc(Array<T> *arr) {
|
void Dealloc(Array<T> *arr) {
|
||||||
if (arr->data) Dealloc(arr->allocator, &arr->data);
|
if (arr->data) {
|
||||||
|
Dealloc(arr->allocator, arr->data);
|
||||||
|
arr->data = NULL;
|
||||||
|
}
|
||||||
arr->len = arr->cap = 0;
|
arr->len = arr->cap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -442,7 +442,7 @@ API String ReadFile(Allocator arena, String path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Dealloc(arena, &result.data);
|
Dealloc(arena, result.data);
|
||||||
result = {};
|
result = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +566,7 @@ API String GetExePath(Allocator allocator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
API String GetExeDir(Allocator allocator) {
|
API String GetExeDir(Allocator allocator) {
|
||||||
Scratch scratch((Arena *)allocator.object);
|
Scratch scratch(allocator);
|
||||||
String path = GetExePath(scratch);
|
String path = GetExePath(scratch);
|
||||||
path = ChopLastSlash(path);
|
path = ChopLastSlash(path);
|
||||||
path = Copy(allocator, path);
|
path = Copy(allocator, path);
|
||||||
@@ -852,7 +852,7 @@ API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
|||||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||||
if (read_error) {
|
if (read_error) {
|
||||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||||
Dealloc(allocator, &buffer);
|
Dealloc(allocator, buffer);
|
||||||
Win32CloseProcess(process);
|
Win32CloseProcess(process);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ struct HashTable {
|
|||||||
|
|
||||||
Assert(!(old_values == 0 && len != 0));
|
Assert(!(old_values == 0 && len != 0));
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
if (old_values) Dealloc(allocator, &old_values);
|
if (old_values) {
|
||||||
|
Dealloc(allocator, old_values);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +64,7 @@ struct HashTable {
|
|||||||
insert(it->key, it->value);
|
insert(it->key, it->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dealloc(allocator, &old_values);
|
Dealloc(allocator, old_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry *get_table_entry(uint64_t key) {
|
Entry *get_table_entry(uint64_t key) {
|
||||||
@@ -177,7 +179,8 @@ struct HashTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dealloc() {
|
void dealloc() {
|
||||||
Dealloc(allocator, &values);
|
Dealloc(allocator, values);
|
||||||
|
values = NULL;
|
||||||
len = 0;
|
len = 0;
|
||||||
cap = 0;
|
cap = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ void Test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
InitScratch();
|
|
||||||
|
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
String data = ReadFile(scratch, "../data/init.lua");
|
String data = ReadFile(scratch, "../data/init.lua");
|
||||||
Array<String> array = {scratch};
|
Array<String> array = {scratch};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ int64_t TotalVertexCount;
|
|||||||
|
|
||||||
unsigned VBO, VAO;
|
unsigned VBO, VAO;
|
||||||
Shader Shader2D;
|
Shader Shader2D;
|
||||||
Arena RenderArena;
|
BlockArena RenderArena;
|
||||||
Rect2 CurrentScissor;
|
Rect2 CurrentScissor;
|
||||||
|
|
||||||
Font MainFont;
|
Font MainFont;
|
||||||
@@ -139,12 +139,6 @@ Shader CreateShaderES3(const char *vsrc, const char *fsrc) {
|
|||||||
|
|
||||||
// ---------- InitRender for ES3 ----------
|
// ---------- InitRender for ES3 ----------
|
||||||
void InitRender() {
|
void InitRender() {
|
||||||
#if OS_WASM
|
|
||||||
RenderArena = *AllocArena(GetSystemAllocator(), MiB(64));
|
|
||||||
#else
|
|
||||||
InitArena(&RenderArena);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !OS_WASM
|
#if !OS_WASM
|
||||||
glDebugMessageCallback(&GLDebugCallback, NULL);
|
glDebugMessageCallback(&GLDebugCallback, NULL);
|
||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||||
@@ -184,7 +178,7 @@ void InitRender() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BeginFrameRender(float wx, float wy) {
|
void BeginFrameRender(float wx, float wy) {
|
||||||
Clear(&RenderArena);
|
Release(&RenderArena);
|
||||||
TotalVertexCount = 0;
|
TotalVertexCount = 0;
|
||||||
Vertices.first = NULL;
|
Vertices.first = NULL;
|
||||||
Vertices.last = NULL;
|
Vertices.last = NULL;
|
||||||
@@ -392,8 +386,6 @@ void DrawCircle(Vec2 pos, float radius, Color color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------- ReloadFont - replace DSA texture calls ----------
|
|
||||||
void ReloadFont() {
|
void ReloadFont() {
|
||||||
Int size = StyleFontSize;
|
Int size = StyleFontSize;
|
||||||
size = ClampBottom((Int)2, size);
|
size = ClampBottom((Int)2, size);
|
||||||
|
|||||||
@@ -628,7 +628,7 @@ void RawGrow(Buffer *buffer, Int change_size) {
|
|||||||
Int new_cap = (buffer->cap + outside) * 2;
|
Int new_cap = (buffer->cap + outside) * 2;
|
||||||
U16 *new_array = AllocArray(alo, U16, new_cap);
|
U16 *new_array = AllocArray(alo, U16, new_cap);
|
||||||
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
|
MemoryCopy(new_array, buffer->data, buffer->len * sizeof(U16));
|
||||||
Dealloc(alo, &buffer->data);
|
Dealloc(alo, buffer->data);
|
||||||
buffer->cap = new_cap;
|
buffer->cap = new_cap;
|
||||||
buffer->data = new_array;
|
buffer->data = new_array;
|
||||||
}
|
}
|
||||||
@@ -884,12 +884,10 @@ API void ApplyEditsMultiCursor(Buffer *buffer, Array<Edit> edits) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We need to sort from lowest to highest based on range.min
|
// We need to sort from lowest to highest based on range.min
|
||||||
{
|
Scratch scratch(buffer->line_starts.allocator);
|
||||||
Scratch scratch((Arena *)buffer->line_starts.allocator.object);
|
Array<Edit> edits_copy = TightCopy(scratch, edits);
|
||||||
Array<Edit> edits_copy = TightCopy(scratch, edits);
|
if (edits.len > 1) MergeSort(edits.len, edits_copy.data, edits.data);
|
||||||
if (edits.len > 1) MergeSort(edits.len, edits_copy.data, edits.data);
|
edits = edits_copy;
|
||||||
edits = edits_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BUFFER_DEBUG
|
#if BUFFER_DEBUG
|
||||||
for (int64_t i = 0; i < edits.len - 1; i += 1) {
|
for (int64_t i = 0; i < edits.len - 1; i += 1) {
|
||||||
@@ -973,7 +971,7 @@ API void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
|
|||||||
*carets = entry.carets;
|
*carets = entry.carets;
|
||||||
|
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
Allocator sys_allocator = GetSystemAllocator();
|
||||||
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
For(entry.edits) Dealloc(sys_allocator, it.string.data);
|
||||||
Dealloc(&entry.edits);
|
Dealloc(&entry.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -992,14 +990,14 @@ API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
|||||||
*carets = entry.carets;
|
*carets = entry.carets;
|
||||||
|
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
Allocator sys_allocator = GetSystemAllocator();
|
||||||
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
For(entry.edits) Dealloc(sys_allocator, it.string.data);
|
||||||
Dealloc(&entry.edits);
|
Dealloc(&entry.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
||||||
For(*entries) {
|
For(*entries) {
|
||||||
Dealloc(&it.carets);
|
Dealloc(&it.carets);
|
||||||
ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data);
|
ForItem(edit, it.edits) Dealloc(it.edits.allocator, edit.string.data);
|
||||||
Dealloc(&it.edits);
|
Dealloc(&it.edits);
|
||||||
}
|
}
|
||||||
entries->len = 0;
|
entries->len = 0;
|
||||||
@@ -1175,15 +1173,23 @@ API void DeinitBuffer(Buffer *buffer) {
|
|||||||
Assert(buffer->next == NULL);
|
Assert(buffer->next == NULL);
|
||||||
Assert(buffer->prev == NULL);
|
Assert(buffer->prev == NULL);
|
||||||
Allocator allocator = buffer->line_starts.allocator;
|
Allocator allocator = buffer->line_starts.allocator;
|
||||||
Dealloc(allocator, &buffer->data);
|
Dealloc(allocator, buffer->data);
|
||||||
Dealloc(&buffer->line_starts);
|
Dealloc(&buffer->line_starts);
|
||||||
DeallocHistoryArray(&buffer->undo_stack);
|
DeallocHistoryArray(&buffer->undo_stack);
|
||||||
DeallocHistoryArray(&buffer->redo_stack);
|
DeallocHistoryArray(&buffer->redo_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
API Buffer *CreateTempBuffer(Allocator allocator, Int size = 4096) {
|
||||||
|
Buffer *result = AllocType(allocator, Buffer);
|
||||||
|
result->no_history = true;
|
||||||
|
result->no_line_starts = true;
|
||||||
|
InitBuffer(allocator, result, {}, "*temp*", size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void TestBuffer() {
|
void TestBuffer() {
|
||||||
Scratch scratch;
|
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
InitBuffer(scratch, &buffer);
|
InitBuffer(scratch, &buffer);
|
||||||
Assert(buffer.line_starts.len == 1);
|
Assert(buffer.line_starts.len == 1);
|
||||||
@@ -1259,6 +1265,7 @@ void TestBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
InitBuffer(scratch, &buffer);
|
InitBuffer(scratch, &buffer);
|
||||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||||
@@ -1272,7 +1279,9 @@ void TestBuffer() {
|
|||||||
RawValidateLineStarts(&buffer);
|
RawValidateLineStarts(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all the line starts are properly created
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
InitBuffer(scratch, &buffer);
|
InitBuffer(scratch, &buffer);
|
||||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||||
@@ -1293,7 +1302,9 @@ void TestBuffer() {
|
|||||||
RawValidateLineStarts(&buffer);
|
RawValidateLineStarts(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Basic case make sure no leaks
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
InitBuffer(GetTrackingAllocator(), &buffer);
|
InitBuffer(GetTrackingAllocator(), &buffer);
|
||||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||||
@@ -1302,8 +1313,9 @@ void TestBuffer() {
|
|||||||
TrackingAllocatorCheck();
|
TrackingAllocatorCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Testing Edit API and making sure no leaks
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
InitBuffer(GetTrackingAllocator(), &buffer);
|
InitBuffer(GetTrackingAllocator(), &buffer);
|
||||||
RawReplaceText(&buffer, {}, u"Testing\nthings");
|
RawReplaceText(&buffer, {}, u"Testing\nthings");
|
||||||
@@ -1325,6 +1337,7 @@ void TestBuffer() {
|
|||||||
|
|
||||||
// Make sure no_history and no line_starts properly makes sure of these
|
// Make sure no_history and no line_starts properly makes sure of these
|
||||||
{
|
{
|
||||||
|
Scratch scratch;
|
||||||
Buffer buffer = {};
|
Buffer buffer = {};
|
||||||
buffer.no_history = true;
|
buffer.no_history = true;
|
||||||
buffer.no_line_starts = true;
|
buffer.no_line_starts = true;
|
||||||
|
|||||||
@@ -21,14 +21,15 @@ EM_ASYNC_JS(const char *, GetClipboardText, (), {
|
|||||||
|
|
||||||
void SetClipboardText(String16 string16) {
|
void SetClipboardText(String16 string16) {
|
||||||
String string = ToString(SysAllocator, string16);
|
String string = ToString(SysAllocator, string16);
|
||||||
defer { Dealloc(SysAllocator, &string.data); };
|
defer { Dealloc(SysAllocator, string.data); };
|
||||||
_SetClipboardText(string.data);
|
_SetClipboardText(string.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeClipboardGlobals() {
|
void FreeClipboardGlobals() {
|
||||||
For(SavedClipboardCarets) Dealloc(SysAllocator, &it.data);
|
For(SavedClipboardCarets) Dealloc(SysAllocator, it.data);
|
||||||
SavedClipboardCarets.len = 0;
|
SavedClipboardCarets.len = 0;
|
||||||
Dealloc(SysAllocator, &SavedClipboardString.data);
|
Dealloc(SysAllocator, SavedClipboardString.data);
|
||||||
|
SavedClipboardString = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStringInClipboard(String16 string) {
|
void SaveStringInClipboard(String16 string) {
|
||||||
@@ -69,7 +70,7 @@ void Command_Paste(View *view) {
|
|||||||
defer { FreeClipboardText((void *)text); };
|
defer { FreeClipboardText((void *)text); };
|
||||||
|
|
||||||
String16 string = ToUnixString16(SysAllocator, text);
|
String16 string = ToUnixString16(SysAllocator, text);
|
||||||
defer { Dealloc(SysAllocator, &string.data); };
|
defer { Dealloc(SysAllocator, string.data); };
|
||||||
|
|
||||||
// Regular paste
|
// Regular paste
|
||||||
if (string != SavedClipboardString || SavedClipboardCarets.len != view->carets.len) {
|
if (string != SavedClipboardString || SavedClipboardCarets.len != view->carets.len) {
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
struct InternTable {
|
struct InternTable {
|
||||||
HashTable<String> strings;
|
HashTable<String> strings; // general allocator
|
||||||
Arena *arena;
|
BlockArena arena;
|
||||||
};
|
};
|
||||||
|
|
||||||
String Intern(InternTable *table, String string) {
|
String Intern(InternTable *table, String string) {
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
|
||||||
if (table->arena == NULL) {
|
|
||||||
table->arena = AllocArena(sys_allocator, MiB(8));
|
|
||||||
table->strings.allocator = *table->arena;
|
|
||||||
}
|
|
||||||
String *value = table->strings.get(string);
|
String *value = table->strings.get(string);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
String copy = Copy(*table->arena, string);
|
String copy = Copy(table->arena, string);
|
||||||
table->strings.put(copy, copy);
|
table->strings.put(copy, copy);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ void DeallocBuffer(Buffer *buffer) {
|
|||||||
Dealloc(&buffer->line_starts);
|
Dealloc(&buffer->line_starts);
|
||||||
DeallocHistoryArray(&buffer->undo_stack);
|
DeallocHistoryArray(&buffer->undo_stack);
|
||||||
DeallocHistoryArray(&buffer->redo_stack);
|
DeallocHistoryArray(&buffer->redo_stack);
|
||||||
DeallocEx(buffer->line_starts.allocator, buffer->data);
|
Dealloc(buffer->line_starts.allocator, buffer->data);
|
||||||
DeallocEx(buffer->line_starts.allocator, buffer);
|
Dealloc(buffer->line_starts.allocator, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
||||||
@@ -172,14 +172,6 @@ Buffer *CreateBuffer(Allocator allocator, String name, Int size) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer *CreateTempBuffer(Allocator allocator, Int size = 4096) {
|
|
||||||
Buffer *result = AllocType(allocator, Buffer);
|
|
||||||
result->no_history = true;
|
|
||||||
result->no_line_starts = true;
|
|
||||||
InitBuffer(allocator, result, "*temp*", size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window *CreateWind(bool create_command_buffer = true) {
|
Window *CreateWind(bool create_command_buffer = true) {
|
||||||
Window *w = AllocType(SysAllocator, Window);
|
Window *w = AllocType(SysAllocator, Window);
|
||||||
w->visible = true;
|
w->visible = true;
|
||||||
@@ -540,7 +532,7 @@ void GarbageCollect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Dealloc(&it->carets);
|
Dealloc(&it->carets);
|
||||||
DeallocEx(sys_allocator, it);
|
Dealloc(sys_allocator, it);
|
||||||
DLL_QUEUE_REMOVE(FirstView, LastView, it);
|
DLL_QUEUE_REMOVE(FirstView, LastView, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -376,6 +376,7 @@ void MainLoop() {
|
|||||||
SDL_GL_SwapWindow(SDLWindow);
|
SDL_GL_SwapWindow(SDLWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||||
#else
|
#else
|
||||||
@@ -386,14 +387,15 @@ int main(int argc, char **argv)
|
|||||||
#if _WIN32
|
#if _WIN32
|
||||||
int argc = __argc;
|
int argc = __argc;
|
||||||
char **argv = __argv;
|
char **argv = __argv;
|
||||||
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||||
#endif
|
#endif
|
||||||
BeginProfiler();
|
BeginProfiler();
|
||||||
InitScratch();
|
|
||||||
|
|
||||||
if (1) {
|
if (1) {
|
||||||
|
TestArena();
|
||||||
TestBuffer();
|
TestBuffer();
|
||||||
ReportErrorf("Testing DONE\n");
|
// ReportErrorf("Testing DONE\n");
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !OS_WINDOWS
|
#if !OS_WINDOWS
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ struct BSet {
|
|||||||
// Dont use it
|
// Dont use it
|
||||||
Int FrameID;
|
Int FrameID;
|
||||||
|
|
||||||
Allocator SysAllocator = {SystemAllocator_Alloc};
|
Allocator SysAllocator = {SystemAllocatorProc};
|
||||||
String ConfigDir;
|
String ConfigDir;
|
||||||
float DPIScale = 1.0f;
|
float DPIScale = 1.0f;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user