956 lines
14 KiB
D
956 lines
14 KiB
D
module dlib.alloc;
|
|
|
|
import dlib.aliases;
|
|
import dlib.math;
|
|
import dlib.platform;
|
|
import dlib.util;
|
|
|
|
import core.stdc.string;
|
|
|
|
static if(NativeTarget)
|
|
{
|
|
public import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
|
|
}
|
|
|
|
static Scratch g_scratch;
|
|
static usize g_scratch_index;
|
|
|
|
const DEFAULT_ALIGNMENT = (void *).sizeof * 2;
|
|
|
|
struct Scratch
|
|
{
|
|
Arena arena;
|
|
bool init;
|
|
}
|
|
|
|
struct ArenaPool
|
|
{
|
|
u8* mem;
|
|
usize pos;
|
|
usize length;
|
|
ArenaPool* next;
|
|
}
|
|
|
|
struct Arena
|
|
{
|
|
ArenaPool* first, last;
|
|
usize def_size;
|
|
}
|
|
|
|
struct TempArena
|
|
{
|
|
Arena* arena;
|
|
usize start_pos;
|
|
ArenaPool* start_pool;
|
|
}
|
|
|
|
/*
|
|
extern(C)
|
|
{
|
|
void gc_setProxy(void* p);
|
|
void gc_clrProxy();
|
|
}
|
|
*/
|
|
|
|
@nogc:
|
|
|
|
version(WebAssembly)
|
|
{
|
|
|
|
pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
|
|
extern(C) usize grow(usize index, usize size) nothrow @nogc;
|
|
|
|
pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
|
|
extern(C) usize size(usize index) nothrow @nogc;
|
|
|
|
|
|
extern(C) u32
|
|
WasmGrow(u32 size) nothrow @nogc
|
|
{
|
|
return grow(0, size);
|
|
}
|
|
|
|
extern(C) u32
|
|
WasmGrow2(u32 index, u32 size)
|
|
{
|
|
return grow(0, size);
|
|
}
|
|
|
|
extern(C) u32
|
|
WasmSize() nothrow @nogc
|
|
{
|
|
return size(0);
|
|
}
|
|
|
|
}
|
|
|
|
T*
|
|
MAlloc(T)()
|
|
{
|
|
void* mem = MemAlloc(T.sizeof);
|
|
return cast(T*)mem;
|
|
}
|
|
|
|
T[]
|
|
MAlloc(T)(usize count)
|
|
{
|
|
void* mem = MemAlloc(T.sizeof * count);
|
|
return (cast(T*)mem)[0 .. count];
|
|
}
|
|
|
|
void
|
|
MFree(T)(T* ptr)
|
|
{
|
|
MemFree(ptr, T.sizeof);
|
|
}
|
|
|
|
void
|
|
MFree(T)(T[] slice)
|
|
{
|
|
MemFree(slice.ptr, cast(usize)slice.length * T.sizeof);
|
|
}
|
|
|
|
T*
|
|
Alloc(T)()
|
|
{
|
|
void* mem = malloc(T.sizeof);
|
|
memset(mem, 0, T.sizeof);
|
|
return (cast(T*)mem);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(usize count)
|
|
{
|
|
void* mem = malloc(T.sizeof * count);
|
|
memset(mem, 0, T.sizeof * count);
|
|
return (cast(T*)mem)[0 .. count];
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(T[] target)
|
|
{
|
|
T[] arr = Alloc!(T)(target.length);
|
|
arr[] = target[];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
Alloc(string target)
|
|
{
|
|
u8[] str = Alloc!(u8)(target.length);
|
|
str[] = cast(u8[])target[];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
string
|
|
AllocEscaped(char[] target)
|
|
{
|
|
u8[] str = Alloc!(u8)(target.length+1);
|
|
str[0 .. target.length] = str[];
|
|
str[str.length-1] = '\0';
|
|
return cast(string)str[0 .. str.length-1];
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(T[] target, usize start, usize len)
|
|
{
|
|
T[] arr = Alloc!(T)(len);
|
|
arr[0 .. $] = target[start .. start+len];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
Alloc(string target, usize start, usize len)
|
|
{
|
|
u8[] str = Alloc!(u8)(len);
|
|
str[0 .. $] = cast(u8[])target[start .. start+len];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(usize count, T set)
|
|
{
|
|
T[] arr = Alloc!(T)(count);
|
|
arr[] = set;
|
|
return arr;
|
|
}
|
|
|
|
T[]
|
|
Realloc(T)(T[] arr, usize count)
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
void* mem = realloc(arr.ptr, T.sizeof * count);
|
|
}
|
|
else
|
|
{
|
|
void* mem = alloc(T.sizeof*count);
|
|
memcpy(mem, arr.ptr, T.sizeof*arr.length);
|
|
Free(arr);
|
|
}
|
|
|
|
return (cast(T*)mem)[0 .. count];
|
|
}
|
|
|
|
void
|
|
Free(T)(T[] arr)
|
|
{
|
|
free(arr.ptr);
|
|
}
|
|
|
|
void
|
|
Free(T)(T* ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
Arena
|
|
CreateArena(usize size)
|
|
{
|
|
Arena arena = {
|
|
def_size: size,
|
|
};
|
|
|
|
AddArenaPool(&arena, size);
|
|
|
|
return arena;
|
|
}
|
|
|
|
TempArena
|
|
BeginTempArena(Arena* arena)
|
|
{
|
|
TempArena t = {
|
|
arena: arena,
|
|
};
|
|
|
|
auto n = arena.first;
|
|
for(;;)
|
|
{
|
|
if(n.next == null)
|
|
{
|
|
t.start_pool = n;
|
|
t.start_pos = n.pos;
|
|
break;
|
|
}
|
|
|
|
n = n.next;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
void
|
|
End(TempArena* t)
|
|
{
|
|
bool resetting = false;
|
|
for(auto n = t.arena.first; n != null; n = n.next)
|
|
{
|
|
if(t.start_pool == n)
|
|
{
|
|
n.pos = t.start_pos;
|
|
resetting = true;
|
|
}
|
|
else if(resetting)
|
|
{
|
|
n.pos = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(TempArena* t, T[] target)
|
|
{
|
|
return Alloc!(T)(t.arena, target);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(TempArena* t, usize count, T set)
|
|
{
|
|
return Alloc!(T)(t.arena, count, set);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(TempArena* t, usize count)
|
|
{
|
|
return Alloc!(T)(t.arena, count);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(TempArena* t, T[] target)
|
|
{
|
|
return Alloc!(T)(t.arena, target);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(TempArena* t, T[] target, usize start, usize len)
|
|
{
|
|
return Alloc!(T)(t.arena, target, start, len);
|
|
}
|
|
|
|
T*
|
|
Alloc(T)(TempArena* t)
|
|
{
|
|
return Alloc!(T)(t.arena);
|
|
}
|
|
|
|
void
|
|
AddArenaPool(Arena* arena, usize size)
|
|
{
|
|
u8* mem = Alloc!(u8)(size + ArenaPool.sizeof).ptr;
|
|
|
|
ArenaPool* node = cast(ArenaPool*)mem;
|
|
|
|
node.mem = (cast(u8*)mem) + ArenaPool.sizeof;
|
|
node.pos = 0;
|
|
node.length = size;
|
|
|
|
assert(node.mem != null, "Unable to allocate memory for arena");
|
|
|
|
SLLPushFront(arena, node, null);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(Arena* arena, usize count)
|
|
{
|
|
void* mem = AllocAlign(arena, T.sizeof * count, DEFAULT_ALIGNMENT);
|
|
memset(mem, 0, T.sizeof * count);
|
|
return (cast(T*)mem)[0 .. cast(usize)count];
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(Arena* arena, T[] target)
|
|
{
|
|
T[] arr = Alloc!(T)(arena, target.length);
|
|
arr[] = target[];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
Alloc(Arena* arena, string target)
|
|
{
|
|
u8[] str = Alloc!(u8)(arena, target.length);
|
|
str[] = cast(u8[])target[];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(Arena* arena, T[] target, usize start, usize len)
|
|
{
|
|
T[] arr = Alloc!(T)(arena, len);
|
|
arr[0 .. $] = target[start .. start+len];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
Alloc(Arena* arena, string target, usize start, usize len)
|
|
{
|
|
u8[] str = Alloc!(u8)(arena, len);
|
|
str[0 .. $] = cast(u8[])target[start .. start+len];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
T[]
|
|
Alloc(T)(Arena* arena, usize count, T set)
|
|
{
|
|
T[] arr = Alloc!(T)(arena, count);
|
|
arr[] = set;
|
|
return arr;
|
|
}
|
|
|
|
T*
|
|
Alloc(T)(Arena* arena)
|
|
{
|
|
void* mem = AllocAlign(arena, T.sizeof, DEFAULT_ALIGNMENT);
|
|
memset(mem, 0, T.sizeof);
|
|
return cast(T*)mem;
|
|
};
|
|
|
|
void*
|
|
AllocAlign(Arena* arena, usize size, usize alignment)
|
|
{
|
|
void* ptr = null;
|
|
|
|
usize pool_alloc_size = size;
|
|
if(pool_alloc_size > arena.def_size)
|
|
{
|
|
pool_alloc_size += arena.def_size;
|
|
}
|
|
|
|
usize mem_pos, current, offset;
|
|
ArenaPool* node = arena.first;
|
|
while(true)
|
|
{
|
|
if(node == null)
|
|
{
|
|
AddArenaPool(arena, Max(pool_alloc_size, arena.def_size));
|
|
node = arena.first;
|
|
}
|
|
|
|
mem_pos = cast(usize)node.mem;
|
|
current = mem_pos + node.pos;
|
|
offset = AlignPow2(current, alignment) - mem_pos;
|
|
|
|
if(offset+size <= node.length)
|
|
{
|
|
break;
|
|
}
|
|
|
|
node = node.next;
|
|
}
|
|
|
|
ptr = &node.mem[offset];
|
|
node.pos = offset+size;
|
|
|
|
return ptr;
|
|
};
|
|
|
|
void
|
|
Reset(Arena* arena)
|
|
{
|
|
ArenaPool* node = arena.first;
|
|
while(node != null)
|
|
{
|
|
node.pos = 0;
|
|
node = node.next;
|
|
}
|
|
}
|
|
|
|
void
|
|
Free(Arena* arena)
|
|
{
|
|
ArenaPool* node = arena.first;
|
|
ArenaPool* next;
|
|
while(node != null)
|
|
{
|
|
next = node.next;
|
|
Free(node);
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
void
|
|
ResetScratch(usize size)
|
|
{
|
|
if(!g_scratch.init)
|
|
{
|
|
g_scratch.arena = CreateArena(size);
|
|
g_scratch.init = true;
|
|
}
|
|
|
|
Reset(&g_scratch.arena);
|
|
}
|
|
|
|
T*
|
|
ScratchAlloc(T)()
|
|
{
|
|
return Alloc!(T)(&g_scratch.arena);
|
|
}
|
|
|
|
T[]
|
|
ScratchAlloc(T)(usize count)
|
|
{
|
|
return Alloc!(T)(&g_scratch.arena, count);
|
|
}
|
|
|
|
T[]
|
|
ScratchAlloc(T)(T[] target)
|
|
{
|
|
T[] arr = ScratchAlloc!(T)(target.length);
|
|
arr[] = target[];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
ScratchAlloc(string target)
|
|
{
|
|
u8[] str = ScratchAlloc!(u8)(target.length);
|
|
str[] = cast(u8[])target[];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
T[]
|
|
ScratchAlloc(T)(T[] target, usize start, usize len)
|
|
{
|
|
T[] arr = ScratchAlloc!(T)(len);
|
|
arr[0 .. $] = target[start .. start+len];
|
|
return arr;
|
|
}
|
|
|
|
string
|
|
ScratchAlloc(string target, usize start, usize len)
|
|
{
|
|
u8[] str = ScratchAlloc!(u8)(len);
|
|
str[0 .. $] = cast(u8[])target[start .. start+len];
|
|
return ConvToStr(str);
|
|
}
|
|
|
|
T[]
|
|
ScratchAlloc(T)(usize count, T set)
|
|
{
|
|
T[] arr = ScratchAlloc!(T)(count);
|
|
arr[] = set;
|
|
return arr;
|
|
}
|
|
|
|
version(DLIB_TEST)
|
|
{
|
|
void WriteToArray(T)(T[] arr, T factor)
|
|
{
|
|
foreach(i; 0 .. arr.length)
|
|
{
|
|
arr[i] = i*factor;
|
|
}
|
|
}
|
|
|
|
void AssertArrayValues(T)(T[] arr, T factor)
|
|
{
|
|
foreach(i; 0 .. arr.length)
|
|
{
|
|
assert(arr[i] == i*factor);
|
|
}
|
|
}
|
|
|
|
void DLibTestAlloc()
|
|
{
|
|
{
|
|
u64[5] arr0 = [1, 2, 3, 4, 5];
|
|
|
|
u64[] copy = Alloc!(u64)(arr0);
|
|
|
|
assert(arr0 == copy);
|
|
|
|
u64[] arr1 = Alloc!(u64)(64);
|
|
|
|
WriteToArray(arr1, 5);
|
|
|
|
assert(arr0 == copy);
|
|
AssertArrayValues(arr1, 5);
|
|
}
|
|
|
|
{
|
|
Arena a = CreateArena(64);
|
|
|
|
u64[] arr0 = Alloc!(u64)(&a, 128);
|
|
u64[] arr1 = Alloc!(u64)(&a, 256);
|
|
|
|
WriteToArray(arr0, 2);
|
|
WriteToArray(arr1, 3);
|
|
WriteToArray(arr0, 2);
|
|
|
|
AssertArrayValues(arr0, 2);
|
|
AssertArrayValues(arr1, 3);
|
|
|
|
Reset(&a);
|
|
|
|
arr0 = Alloc!(u64)(&a, 256);
|
|
arr1 = Alloc!(u64)(&a, 128);
|
|
|
|
WriteToArray(arr0, 4);
|
|
WriteToArray(arr1, 5);
|
|
WriteToArray(arr0, 4);
|
|
|
|
AssertArrayValues(arr0, 4);
|
|
AssertArrayValues(arr1, 5);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
DLibTestAlloc();
|
|
}
|
|
}
|
|
|
|
version(WebAssembly) nothrow @nogc:
|
|
|
|
struct MemBlock
|
|
{
|
|
void* addr;
|
|
MemBlock* next;
|
|
usize size;
|
|
}
|
|
|
|
struct Heap
|
|
{
|
|
MemBlock* free;
|
|
MemBlock* used;
|
|
MemBlock* fresh;
|
|
usize top;
|
|
}
|
|
|
|
extern extern(C) u8 __heap_base;
|
|
extern extern(C) u8 __data_end;
|
|
|
|
__gshared Heap* heap;
|
|
__gshared void* heap_limit;
|
|
__gshared usize heap_split_thresh;
|
|
__gshared usize heap_alignment;
|
|
__gshared usize heap_max_blocks;
|
|
__gshared usize current_pages;
|
|
|
|
enum bool MALLOC_COMPACT = true;
|
|
enum bool MALLOC_SPLIT = true;
|
|
|
|
usize WasmPageSize(usize x) => x*1024*64;
|
|
|
|
/**
|
|
* If compaction is enabled, inserts block
|
|
* into free list, sorted by addr.
|
|
* If disabled, add block has new head of
|
|
* the free list.
|
|
*/
|
|
void
|
|
InsertBlock(MemBlock *block)
|
|
{
|
|
static if(MALLOC_COMPACT)
|
|
{
|
|
MemBlock *ptr = heap.free;
|
|
MemBlock *prev = null;
|
|
while(ptr != null)
|
|
{
|
|
if(cast(usize)block.addr <= cast(usize)ptr.addr)
|
|
{
|
|
break;
|
|
}
|
|
prev = ptr;
|
|
ptr = ptr.next;
|
|
}
|
|
|
|
if(prev != null)
|
|
{
|
|
prev.next = block;
|
|
}
|
|
else
|
|
{
|
|
heap.free = block;
|
|
}
|
|
block.next = ptr;
|
|
}
|
|
else
|
|
{
|
|
block.next = heap.free;
|
|
heap.free = block;
|
|
}
|
|
}
|
|
|
|
void
|
|
ReleaseBlocks(MemBlock *scan, MemBlock *to)
|
|
{
|
|
MemBlock *scan_next;
|
|
while(scan != to)
|
|
{
|
|
scan_next = scan.next;
|
|
scan.next = heap.fresh;
|
|
heap.fresh = scan;
|
|
scan.addr = null;
|
|
scan.size = 0;
|
|
scan = scan_next;
|
|
}
|
|
}
|
|
|
|
void
|
|
Compact()
|
|
{
|
|
MemBlock *ptr = heap.free;
|
|
MemBlock *prev;
|
|
MemBlock *scan;
|
|
while(ptr != null)
|
|
{
|
|
prev = ptr;
|
|
scan = ptr.next;
|
|
|
|
while(scan != null && cast(usize)prev.addr + prev.size == cast(usize)scan.addr)
|
|
{
|
|
prev = scan;
|
|
scan = scan.next;
|
|
}
|
|
|
|
if(prev != ptr)
|
|
{
|
|
usize new_size = cast(usize)prev.addr - cast(usize)ptr.addr + prev.size;
|
|
ptr.size = new_size;
|
|
MemBlock *next = prev.next;
|
|
// make merged blocks available
|
|
ReleaseBlocks(ptr.next, prev.next);
|
|
// relink
|
|
ptr.next = next;
|
|
}
|
|
|
|
ptr = ptr.next;
|
|
}
|
|
}
|
|
|
|
void
|
|
MallocInit(const usize heap_blocks, const usize split_thresh, const usize alignment)
|
|
{
|
|
current_pages = WasmSize();
|
|
void* limit = cast(void *)WasmPageSize(current_pages);
|
|
|
|
heap = cast(Heap *)&__heap_base;
|
|
heap_limit = limit;
|
|
heap_split_thresh = split_thresh;
|
|
heap_alignment = alignment;
|
|
heap_max_blocks = heap_blocks;
|
|
|
|
heap.free = null;
|
|
heap.used = null;
|
|
heap.fresh = cast(MemBlock*)(heap + 1);
|
|
heap.top = cast(usize)(heap.fresh + heap_blocks);
|
|
|
|
MemBlock *block = heap.fresh;
|
|
usize i = heap_max_blocks - 1;
|
|
while(i--)
|
|
{
|
|
block.next = block + 1;
|
|
block++;
|
|
}
|
|
|
|
block.next = null;
|
|
}
|
|
|
|
void
|
|
free(void *ptr)
|
|
{
|
|
MemBlock *block = heap.used;
|
|
MemBlock *prev = null;
|
|
while(block != null)
|
|
{
|
|
if(ptr == block.addr)
|
|
{
|
|
if(prev)
|
|
{
|
|
prev.next = block.next;
|
|
}
|
|
else
|
|
{
|
|
heap.used = block.next;
|
|
}
|
|
InsertBlock(block);
|
|
|
|
static if(MALLOC_COMPACT)
|
|
{
|
|
Compact();
|
|
}
|
|
}
|
|
prev = block;
|
|
block = block.next;
|
|
}
|
|
}
|
|
|
|
usize
|
|
PtrInfo(void *ptr)
|
|
{
|
|
usize result = 0;
|
|
|
|
MemBlock *block = heap.used;
|
|
while(block != null)
|
|
{
|
|
if(ptr == block.addr)
|
|
{
|
|
result = block.size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MemBlock*
|
|
AllocBlock(usize num)
|
|
{
|
|
MemBlock *ptr = heap.free;
|
|
MemBlock *prev = null;
|
|
usize top = heap.top;
|
|
num = (num + heap_alignment - 1) & -heap_alignment;
|
|
while(ptr != null)
|
|
{
|
|
const i32 is_top = (cast(usize)ptr.addr + ptr.size >= top) && (cast(usize)ptr.addr + num <= cast(usize)heap_limit);
|
|
if(is_top || ptr.size >= num)
|
|
{
|
|
if(prev != null)
|
|
{
|
|
prev.next = ptr.next;
|
|
}
|
|
else
|
|
{
|
|
heap.free = ptr.next;
|
|
}
|
|
ptr.next = heap.used;
|
|
heap.used = ptr;
|
|
|
|
static if(!MALLOC_SPLIT)
|
|
{
|
|
if(is_top)
|
|
{
|
|
ptr.size = num;
|
|
heap.top = cast(usize)ptr.addr + num;
|
|
static if(MALLOC_COMPACT)
|
|
{
|
|
Compact();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(is_top)
|
|
{
|
|
ptr.size = num;
|
|
heap.top = cast(usize)ptr.addr + num;
|
|
}
|
|
else if(heap.fresh != null)
|
|
{
|
|
usize excess = ptr.size - num;
|
|
if(excess >= heap_split_thresh)
|
|
{
|
|
ptr.size = num;
|
|
MemBlock *split = heap.fresh;
|
|
heap.fresh = split.next;
|
|
split.addr = cast(void *)(cast(usize)ptr.addr + num);
|
|
split.size = excess;
|
|
InsertBlock(split);
|
|
static if(MALLOC_COMPACT)
|
|
{
|
|
Compact();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
prev = ptr;
|
|
ptr = ptr.next;
|
|
}
|
|
|
|
// no matching free blocks
|
|
// see if any other blocks available
|
|
usize new_top = top + num;
|
|
if(heap.fresh != null && new_top <= cast(usize)heap_limit)
|
|
{
|
|
ptr = heap.fresh;
|
|
heap.fresh = ptr.next;
|
|
ptr.addr = cast(void *)top;
|
|
ptr.next = heap.used;
|
|
ptr.size = num;
|
|
heap.used = ptr;
|
|
heap.top = new_top;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
usize
|
|
BytesToPages(usize count)
|
|
{
|
|
usize pages = count/(1024*64);
|
|
return pages > 0 ? pages : 1;
|
|
}
|
|
|
|
void*
|
|
malloc(usize num)
|
|
{
|
|
MemBlock *block = AllocBlock(num);
|
|
|
|
if(block == null)
|
|
{
|
|
WasmGrow(BytesToPages(num));
|
|
usize new_pages = WasmSize();
|
|
usize page_diff = new_pages - current_pages;
|
|
if(page_diff > 0)
|
|
{
|
|
current_pages = new_pages;
|
|
heap_limit = cast(void*)WasmPageSize(current_pages);
|
|
|
|
block = AllocBlock(num);
|
|
}
|
|
}
|
|
|
|
return block != null ? block.addr : null;
|
|
}
|
|
|
|
void
|
|
MemClear(void *ptr, usize num)
|
|
{
|
|
usize *ptrw = cast(usize*)ptr;
|
|
usize numw = (num & -usize.sizeof) / usize.sizeof;
|
|
while(numw--)
|
|
{
|
|
*ptrw++ = 0;
|
|
}
|
|
num &= (usize.sizeof - 1);
|
|
u8* ptrb = cast(u8*)ptrw;
|
|
while(num--)
|
|
{
|
|
*ptrb++ = 0;
|
|
}
|
|
}
|
|
|
|
void*
|
|
calloc(usize num, usize size)
|
|
{
|
|
num *= size;
|
|
MemBlock *block = AllocBlock(num);
|
|
|
|
{
|
|
MemClear(block.addr, num);
|
|
return block.addr;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void*
|
|
realloc(void *ptr, usize size)
|
|
{
|
|
void *new_ptr = malloc(size);
|
|
usize prev_size = PtrInfo(ptr);
|
|
|
|
memcpy(new_ptr, ptr, prev_size);
|
|
|
|
free(ptr);
|
|
|
|
return new_ptr;
|
|
}
|
|
|
|
usize
|
|
CountBlocks(MemBlock *ptr)
|
|
{
|
|
usize num = 0;
|
|
while(ptr != null)
|
|
{
|
|
num++;
|
|
ptr = ptr.next;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
usize MallocFreeCount()
|
|
{
|
|
return CountBlocks(heap.free);
|
|
}
|
|
|
|
usize MallocUsedCount()
|
|
{
|
|
return CountBlocks(heap.used);
|
|
}
|
|
|
|
usize MallocMallocFreshCount()
|
|
{
|
|
return CountBlocks(heap.fresh);
|
|
}
|
|
|
|
bool MallocCheck()
|
|
{
|
|
return heap_max_blocks == MallocFreeCount() + MallocUsedCount() + MallocMallocFreshCount();
|
|
}
|
|
|
|
unittest
|
|
{
|
|
{
|
|
string zeroed = AllocZeroed("Test");
|
|
assert(zeroed.ptr[4] == '\0');
|
|
}
|
|
}
|