reorganize files, port stbtt to d, remove truetype
This commit is contained in:
parent
f98e6cc07f
commit
2c81e42008
476
dlib/alloc.d
476
dlib/alloc.d
@ -7,13 +7,9 @@ import dlib.util;
|
|||||||
|
|
||||||
import core.stdc.string;
|
import core.stdc.string;
|
||||||
|
|
||||||
version(WebAssembly)
|
static if(NativeTarget)
|
||||||
{
|
{
|
||||||
import dlib.externdecl;
|
public import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Scratch g_scratch;
|
static Scratch g_scratch;
|
||||||
@ -62,14 +58,14 @@ version(WebAssembly)
|
|||||||
{
|
{
|
||||||
|
|
||||||
pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
|
pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
|
||||||
extern(C) size_t grow(size_t index, size_t size);
|
extern(C) usize grow(usize index, usize size) nothrow @nogc;
|
||||||
|
|
||||||
pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
|
pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
|
||||||
extern(C) size_t size(size_t index);
|
extern(C) usize size(usize index) nothrow @nogc;
|
||||||
|
|
||||||
|
|
||||||
extern(C) u32
|
extern(C) u32
|
||||||
WasmGrow(u32 size)
|
WasmGrow(u32 size) nothrow @nogc
|
||||||
{
|
{
|
||||||
return grow(0, size);
|
return grow(0, size);
|
||||||
}
|
}
|
||||||
@ -81,7 +77,7 @@ WasmGrow2(u32 index, u32 size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern(C) u32
|
extern(C) u32
|
||||||
WasmSize()
|
WasmSize() nothrow @nogc
|
||||||
{
|
{
|
||||||
return size(0);
|
return size(0);
|
||||||
}
|
}
|
||||||
@ -373,7 +369,7 @@ AllocAlign(Arena* arena, usize size, usize alignment)
|
|||||||
|
|
||||||
usize mem_pos, current, offset;
|
usize mem_pos, current, offset;
|
||||||
ArenaPool* node = arena.first;
|
ArenaPool* node = arena.first;
|
||||||
while (true)
|
while(true)
|
||||||
{
|
{
|
||||||
if(node == null)
|
if(node == null)
|
||||||
{
|
{
|
||||||
@ -403,7 +399,7 @@ void
|
|||||||
Reset(Arena* arena)
|
Reset(Arena* arena)
|
||||||
{
|
{
|
||||||
ArenaPool* node = arena.first;
|
ArenaPool* node = arena.first;
|
||||||
while (node != null)
|
while(node != null)
|
||||||
{
|
{
|
||||||
node.pos = 0;
|
node.pos = 0;
|
||||||
node = node.next;
|
node = node.next;
|
||||||
@ -415,7 +411,7 @@ Free(Arena* arena)
|
|||||||
{
|
{
|
||||||
ArenaPool* node = arena.first;
|
ArenaPool* node = arena.first;
|
||||||
ArenaPool* next;
|
ArenaPool* next;
|
||||||
while (node != null)
|
while(node != null)
|
||||||
{
|
{
|
||||||
next = node.next;
|
next = node.next;
|
||||||
Free(node);
|
Free(node);
|
||||||
@ -487,19 +483,457 @@ ScratchAlloc(T)(usize count, T set)
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(DLIB_TEST) unittest
|
version(DLIB_TEST)
|
||||||
{
|
{
|
||||||
|
void WriteToArray(T)(T[] arr, T factor)
|
||||||
{
|
{
|
||||||
u64[5] arr = [1, 2, 3, 4, 5];
|
foreach(i; 0 .. arr.length)
|
||||||
|
{
|
||||||
u64[] copy = Alloc!(u64)(arr);
|
arr[i] = i*factor;
|
||||||
|
}
|
||||||
assert(arr == copy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssertArrayValues(T)(T[] arr, T factor)
|
||||||
{
|
{
|
||||||
Arena a = CreateArena(64);
|
foreach(i; 0 .. arr.length)
|
||||||
|
{
|
||||||
|
assert(arr[i] == i*factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u64[] arr = Alloc!(u64)(&a, 128);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_STBI
|
#ifndef NO_STBI
|
||||||
# include "../external/stb/stb_truetype.h"
|
// # include "../external/stb/stb_truetype.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DLIB_INCLUDE_VULKAN
|
#ifdef DLIB_INCLUDE_VULKAN
|
||||||
|
|||||||
@ -2,53 +2,12 @@ module dlib.externdecl;
|
|||||||
|
|
||||||
import dlib.aliases;
|
import dlib.aliases;
|
||||||
|
|
||||||
enum stbtt_curvetype : u8
|
import stb_truetype;
|
||||||
{
|
|
||||||
none,
|
|
||||||
vmove,
|
|
||||||
vline,
|
|
||||||
vcurve,
|
|
||||||
vcubic,
|
|
||||||
}
|
|
||||||
|
|
||||||
alias stbtt_vertex_type = short;
|
|
||||||
|
|
||||||
struct stbtt__buf
|
|
||||||
{
|
|
||||||
u8* data;
|
|
||||||
i32 cursor;
|
|
||||||
i32 size;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stbtt_vertex
|
|
||||||
{
|
|
||||||
stbtt_vertex_type x, y, cx, cy, cx1, cy1;
|
|
||||||
stbtt_curvetype type;
|
|
||||||
u8 padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stbtt_fontinfo
|
|
||||||
{
|
|
||||||
void* userdata;
|
|
||||||
u8* data;
|
|
||||||
i32 fontstart;
|
|
||||||
|
|
||||||
i32 numGlyphs;
|
|
||||||
|
|
||||||
i32 loca, head, glyf, hhea, hmtx, kern, gpos, svg;
|
|
||||||
i32 index_map;
|
|
||||||
i32 indexToLocFormat;
|
|
||||||
|
|
||||||
stbtt__buf cff;
|
|
||||||
stbtt__buf charstrings;
|
|
||||||
stbtt__buf gsubrs;
|
|
||||||
stbtt__buf subrs;
|
|
||||||
stbtt__buf fontdicts;
|
|
||||||
stbtt__buf fdselect;
|
|
||||||
}
|
|
||||||
|
|
||||||
@nogc extern(C):
|
@nogc extern(C):
|
||||||
|
|
||||||
|
/*
|
||||||
|
static if(NativeTarget):
|
||||||
// stb_truetype
|
// stb_truetype
|
||||||
|
|
||||||
i32
|
i32
|
||||||
@ -78,24 +37,4 @@ stbtt_GetGlyphBox(const stbtt_fontinfo* info, i32 glyph_index, i32* x0, i32* y0,
|
|||||||
i32
|
i32
|
||||||
stbtt_GetFontOffsetForIndex(const(u8*) data, i32 index);
|
stbtt_GetFontOffsetForIndex(const(u8*) data, i32 index);
|
||||||
|
|
||||||
// tinyalloc
|
*/
|
||||||
|
|
||||||
version(WebAssembly)
|
|
||||||
{
|
|
||||||
void
|
|
||||||
ta_init(const usize heap_blocks, const usize split_thresh, const usize alignment);
|
|
||||||
|
|
||||||
void*
|
|
||||||
malloc(usize size) nothrow @nogc;
|
|
||||||
|
|
||||||
void*
|
|
||||||
calloc(usize size, usize count);
|
|
||||||
|
|
||||||
void
|
|
||||||
free(void *ptr);
|
|
||||||
|
|
||||||
void*
|
|
||||||
realloc(void *ptr, usize size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
420
dlib/fonts.d
420
dlib/fonts.d
@ -1,18 +1,19 @@
|
|||||||
module dlib.fonts;
|
module dlib.fonts;
|
||||||
|
|
||||||
static if(NativeTarget)
|
|
||||||
{
|
|
||||||
import dlibincludes : FT_Library, FT_Face, FT_Error, FT_Bitmap, FT_GlyphSlot, FT_Int32, FT_ULong, FT_Long,
|
|
||||||
FT_Set_Pixel_Sizes, FT_Load_Char, FT_Done_Face, FT_New_Memory_Face, FT_Done_FreeType, FT_Init_FreeType,
|
|
||||||
FT_LOAD_RENDER, FT_PIXEL_MODE_GRAY, FT_PIXEL_MODE_MONO, FT_PIXEL_MODE_BGRA;
|
|
||||||
}
|
|
||||||
|
|
||||||
import dlib.externdecl;
|
import dlib.externdecl;
|
||||||
import dlib.aliases;
|
import dlib.aliases;
|
||||||
import dlib.util;
|
import dlib.util;
|
||||||
import dlib.alloc;
|
import dlib.alloc;
|
||||||
import dlib.math;
|
import dlib.math;
|
||||||
|
|
||||||
|
import core.math : fabs;
|
||||||
|
|
||||||
|
import std.traits;
|
||||||
|
|
||||||
|
import stb_truetype;
|
||||||
|
|
||||||
|
alias FontFace = stbtt_fontinfo;
|
||||||
|
|
||||||
const u32 FONT_MAX_BANDS = 8;
|
const u32 FONT_MAX_BANDS = 8;
|
||||||
const u32 FONT_TEX_WIDTH = 4096;
|
const u32 FONT_TEX_WIDTH = 4096;
|
||||||
|
|
||||||
@ -141,68 +142,196 @@ struct SlugBuffer
|
|||||||
struct FontAtlas
|
struct FontAtlas
|
||||||
{
|
{
|
||||||
Glyph[128] glyphs;
|
Glyph[128] glyphs;
|
||||||
u8[] data;
|
f32 ascent = 0.0; // All unscaled
|
||||||
u32 size;
|
f32 descent = 0.0;
|
||||||
UVec2 dimensions;
|
f32 line_gap = 0.0;
|
||||||
f32 ascent;
|
f32 line_height = 0.0;
|
||||||
f32 descent;
|
f32 max_advance = 0.0;
|
||||||
f32 line_gap;
|
}
|
||||||
f32 line_height;
|
|
||||||
f32 max_advance;
|
struct GlyphBounds
|
||||||
|
{
|
||||||
|
f32 left = 0.0, bottom = 0.0, right = 0.0, top = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
dchar ch;
|
dchar ch;
|
||||||
f32 advance;
|
f32 advance;
|
||||||
f32 plane_left;
|
GlyphBounds plane;
|
||||||
f32 plane_bottom;
|
GlyphBounds atlas;
|
||||||
f32 plane_right;
|
|
||||||
f32 plane_top;
|
|
||||||
f32 atlas_left;
|
|
||||||
f32 atlas_bottom;
|
|
||||||
f32 atlas_right;
|
|
||||||
f32 atlas_top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static if(NativeTarget)
|
enum MSDFType
|
||||||
{
|
{
|
||||||
|
None,
|
||||||
|
HardMask,
|
||||||
|
SoftMask,
|
||||||
|
SDF,
|
||||||
|
PSDF,
|
||||||
|
MSDF,
|
||||||
|
MTSDF,
|
||||||
|
}
|
||||||
|
|
||||||
__gshared FT_Library FT_LIB;
|
enum MSDFYOrigin
|
||||||
alias FontFace = FT_Face;
|
|
||||||
|
|
||||||
void
|
|
||||||
CloseFreeType()
|
|
||||||
{
|
{
|
||||||
if(FT_LIB)
|
None,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MSDFInfo
|
||||||
|
{
|
||||||
|
MSDFGlyph[] glyphs;
|
||||||
|
MSDFType type;
|
||||||
|
Vec2 aem_range;
|
||||||
|
u32 distance_field_range;
|
||||||
|
u32 distance_field_range_middle;
|
||||||
|
f32 pixels_per_em;
|
||||||
|
u32 texture_width;
|
||||||
|
u32 texture_height;
|
||||||
|
MSDFYOrigin y_origin;
|
||||||
|
f32 line_height; // EM
|
||||||
|
f32 ascender; // EM
|
||||||
|
f32 descender; // EM
|
||||||
|
f32 underline_y; // EM
|
||||||
|
f32 underline_thickness; // EM
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MSDFGlyph
|
||||||
|
{
|
||||||
|
dchar glyph;
|
||||||
|
f32 advance = 0.0;
|
||||||
|
GlyphBounds plane; // EM
|
||||||
|
GlyphBounds atlas; // Pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
static MSDFInfo
|
||||||
|
GenerateMSDF(string file_path)()
|
||||||
|
{
|
||||||
|
import std.json;
|
||||||
|
|
||||||
|
string msdf_json = import(file_path);
|
||||||
|
|
||||||
|
JSONValue jsonv = parseJSON(msdf_json);
|
||||||
|
|
||||||
|
static T GetJSONValue(T)(JSONValue jsonv, string name)
|
||||||
{
|
{
|
||||||
FT_Done_FreeType(FT_LIB);
|
T result = Select!(is(T == string), null, cast(T)0);
|
||||||
|
|
||||||
|
if(const(JSONValue)* value = name in jsonv)
|
||||||
|
{
|
||||||
|
switch(value.type())
|
||||||
|
{
|
||||||
|
static if(is(T == u32) || is(T == u64) || is(T == f32))
|
||||||
|
{
|
||||||
|
case JSONType.float_: result = cast(T)value.floating; break;
|
||||||
|
case JSONType.uinteger: result = cast(T)value.uinteger; break;
|
||||||
|
case JSONType.integer: result = cast(T)value.integer; break;
|
||||||
|
}
|
||||||
|
static if(is(T == string))
|
||||||
|
{
|
||||||
|
case JSONType.string: result = cast(T)value.str; break;
|
||||||
|
}
|
||||||
|
default: assert(false, "Unable to retrieve value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MSDFInfo result = {
|
||||||
|
distance_field_range: GetJSONValue!(u32)(jsonv["atlas"], "distanceRange"),
|
||||||
|
distance_field_range_middle: GetJSONValue!(u32)(jsonv["atlas"], "distanceRangeMiddle"),
|
||||||
|
pixels_per_em: GetJSONValue!(f32)(jsonv["atlas"], "size"),
|
||||||
|
texture_width: GetJSONValue!(u32)(jsonv["atlas"], "width"),
|
||||||
|
texture_height: GetJSONValue!(u32)(jsonv["atlas"], "height"),
|
||||||
|
line_height: GetJSONValue!(f32)(jsonv["metrics"], "lineHeight"),
|
||||||
|
ascender: GetJSONValue!(f32)(jsonv["metrics"], "ascender"),
|
||||||
|
descender: GetJSONValue!(f32)(jsonv["metrics"], "descender"),
|
||||||
|
underline_y: GetJSONValue!(f32)(jsonv["metrics"], "underlineY"),
|
||||||
|
underline_thickness: GetJSONValue!(f32)(jsonv["metrics"], "underlineThickness"),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.aem_range = Vec2(
|
||||||
|
(result.distance_field_range_middle - result.distance_field_range/2) / result.pixels_per_em,
|
||||||
|
(result.distance_field_range_middle + result.distance_field_range/2) / result.pixels_per_em
|
||||||
|
);
|
||||||
|
|
||||||
|
string type_string = GetJSONValue!(string)(jsonv["atlas"], "type");
|
||||||
|
string y_origin_string = GetJSONValue!(string)(jsonv["atlas"], "yOrigin");
|
||||||
|
|
||||||
|
assert(type_string.length);
|
||||||
|
assert(y_origin_string.length);
|
||||||
|
|
||||||
|
final switch(type_string) with(MSDFType)
|
||||||
|
{
|
||||||
|
case "hardmask": result.type = HardMask; break;
|
||||||
|
case "softmask": result.type = SoftMask; break;
|
||||||
|
case "sdf": result.type = SDF; break;
|
||||||
|
case "psdf": result.type = PSDF; break;
|
||||||
|
case "msdf": result.type = MSDF; break;
|
||||||
|
case "mtsdf": result.type = MTSDF; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final switch(y_origin_string) with(MSDFYOrigin)
|
||||||
|
{
|
||||||
|
case "top": result.y_origin = Top; break;
|
||||||
|
case "left": result.y_origin = Left; break;
|
||||||
|
case "right": result.y_origin = Right; break;
|
||||||
|
case "bottom": result.y_origin = Bottom; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
MSDFGlyph[] glyphs = new MSDFGlyph[128];
|
||||||
|
|
||||||
|
JSONValue[] glyph_json = jsonv["glyphs"].array;
|
||||||
|
|
||||||
|
foreach(i; 0 .. glyph_json.length)
|
||||||
|
{
|
||||||
|
u64 ch = cast(u64)glyph_json[i]["unicode"].integer;
|
||||||
|
MSDFGlyph glyph = { glyph: cast(dchar)ch };
|
||||||
|
glyph.advance = GetJSONValue!(f32)(glyph_json[i], "advance");
|
||||||
|
if("planeBounds" in glyph_json[i])
|
||||||
|
{
|
||||||
|
glyph.plane.left = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "left");
|
||||||
|
glyph.plane.right = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "right");
|
||||||
|
glyph.plane.bottom = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "bottom");
|
||||||
|
glyph.plane.top = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "top");
|
||||||
|
|
||||||
|
glyph.atlas.left = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "left");
|
||||||
|
glyph.atlas.right = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "right");
|
||||||
|
glyph.atlas.bottom = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "bottom");
|
||||||
|
glyph.atlas.top = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "top");
|
||||||
|
}
|
||||||
|
|
||||||
|
glyphs[ch] = glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(i; 0 .. glyphs.length)
|
||||||
|
{
|
||||||
|
if(glyphs[i].glyph == 0xFFFF)
|
||||||
|
{
|
||||||
|
glyphs[i].glyph = 0;
|
||||||
|
glyphs[i].plane = GlyphBounds(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0);
|
||||||
|
glyphs[i].atlas = GlyphBounds(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.glyphs = glyphs;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FontFace
|
FontFace
|
||||||
OpenFont(u8[] data)
|
OpenFont(u8[] data)
|
||||||
{
|
{
|
||||||
FontFace font;
|
FontFace font;
|
||||||
FT_New_Memory_Face(FT_LIB, data.ptr, cast(FT_Long)data.length, 0, &font);
|
stbtt_InitFont(&font, data.ptr, stbtt_GetFontOffsetForIndex(data.ptr, 0));
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
CloseFont(FontFace font)
|
|
||||||
{
|
|
||||||
if(font != null)
|
|
||||||
{
|
|
||||||
FT_Done_Face(font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i32
|
|
||||||
Float26(f32 f)
|
|
||||||
{
|
|
||||||
return cast(i32)round(cast(f32)(1 << 6) * f);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8
|
u8
|
||||||
DeMultiply(u8 color, u8 alpha)
|
DeMultiply(u8 color, u8 alpha)
|
||||||
{
|
{
|
||||||
@ -217,180 +346,59 @@ TextSize(f32 s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FontAtlas
|
FontAtlas
|
||||||
CreateAtlas(Arena* arena, FontFace font, u32 size, UVec2 atlas_dim)
|
PopulateAtlasGlyphInfo(Arena* arena, FontFace* font, MSDFInfo* msdf_info)
|
||||||
{
|
{
|
||||||
FontAtlas atlas = {
|
FontAtlas atlas;
|
||||||
data: Alloc!(u8)(arena, atlas_dim.x * atlas_dim.y * 4),
|
|
||||||
size: size,
|
|
||||||
dimensions: atlas_dim,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper packing algorithm
|
// TODO: proper packing algorithm
|
||||||
if(font != null)
|
if(font != null)
|
||||||
{
|
{
|
||||||
FT_Set_Pixel_Sizes(font, 0, TextSize(size));
|
i32 ascent, descent, line_gap;
|
||||||
|
stbtt_GetFontVMetrics(font, &ascent, &descent, &line_gap);
|
||||||
|
|
||||||
i64 bbox_ymax = font.bbox.yMax >> 6;
|
i32 max_advance;
|
||||||
i64 bbox_ymin = font.bbox.yMin >> 6;
|
foreach(i32 ch; 0 .. 128)
|
||||||
|
|
||||||
u64 baseline = font.size.metrics.ascender >> 6;
|
|
||||||
u64 line_height = (font.size.metrics.height >> 6)+1;
|
|
||||||
|
|
||||||
atlas.line_height = line_height;
|
|
||||||
atlas.max_advance = font.size.metrics.max_advance >> 6;
|
|
||||||
|
|
||||||
u32 max_w, max_h, count;
|
|
||||||
|
|
||||||
i32 font_size = Float26(size);
|
|
||||||
|
|
||||||
const u32 PADDING = 2;
|
|
||||||
foreach(FT_ULong char_code; 0 .. 0x7F)
|
|
||||||
{
|
{
|
||||||
FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER);
|
i32 glyph_index = stbtt_FindGlyphIndex(font, ch);
|
||||||
if(res != 0)
|
if(glyph_index)
|
||||||
{
|
{
|
||||||
continue;
|
i32 advance, x0, y0, x1, y1;
|
||||||
}
|
stbtt_GetGlyphHMetrics(font, glyph_index, &advance, null);
|
||||||
|
stbtt_GetGlyphBox(font, glyph_index, &x0, &y0, &x1, &y1);
|
||||||
|
|
||||||
u32 bmp_w = font.glyph.bitmap.width;
|
max_advance = max_advance < advance ? advance : max_advance;
|
||||||
u32 bmp_h = font.glyph.bitmap.rows;
|
|
||||||
if(max_w + bmp_w > atlas_dim.x)
|
|
||||||
{
|
|
||||||
max_h += line_height+PADDING;
|
|
||||||
max_w = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(max_h > atlas_dim.y)
|
MSDFGlyph* msdfg;
|
||||||
{
|
foreach(j; 0 .. msdf_info.glyphs.length)
|
||||||
Logf("Unable to pack atlas within dimensions provided");
|
{
|
||||||
assert(false);
|
if(msdf_info.glyphs[j].glyph == ch)
|
||||||
|
{
|
||||||
|
msdfg = msdf_info.glyphs.ptr + j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Glyph* g = atlas.glyphs.ptr + ch;
|
||||||
|
|
||||||
|
g.atlas = msdfg.atlas;
|
||||||
|
|
||||||
|
g.plane.left = cast(f32)x0;
|
||||||
|
g.plane.top = cast(f32)y0;
|
||||||
|
g.plane.right = cast(f32)x1;
|
||||||
|
g.plane.bottom = cast(f32)y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
max_w += bmp_w;
|
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
max_w = max_h = count = 0;
|
atlas.ascent = cast(f32)ascent;
|
||||||
|
atlas.descent = cast(f32)descent;
|
||||||
foreach(FT_ULong char_code; 0 .. 0x7F)
|
atlas.line_height = cast(f32)(ascent - descent); // Maybe add line gap?
|
||||||
{
|
atlas.line_gap = cast(f32)line_gap;
|
||||||
FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER);
|
atlas.max_advance = cast(f32)max_advance;
|
||||||
if(res != 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FT_GlyphSlot glyph = font.glyph;
|
|
||||||
FT_Bitmap* bmp = &font.glyph.bitmap;
|
|
||||||
|
|
||||||
if(max_w + bmp.rows > atlas_dim.x)
|
|
||||||
{
|
|
||||||
max_h += line_height+PADDING;
|
|
||||||
max_w = PADDING;
|
|
||||||
}
|
|
||||||
|
|
||||||
i64 base = Max(baseline, font.glyph.bitmap_top);
|
|
||||||
|
|
||||||
switch(bmp.pixel_mode)
|
|
||||||
{
|
|
||||||
case FT_PIXEL_MODE_BGRA:
|
|
||||||
{
|
|
||||||
u64 x, y;
|
|
||||||
foreach(r; 0 .. bmp.rows)
|
|
||||||
{
|
|
||||||
y = (base - font.glyph.bitmap_top + r) + max_h;
|
|
||||||
foreach(c; 0 .. bmp.width)
|
|
||||||
{
|
|
||||||
x = max_w + c;
|
|
||||||
u64 offset = (y*atlas_dim.y + x) * 4;
|
|
||||||
|
|
||||||
u8 p_b = bmp.buffer[offset+0];
|
|
||||||
u8 p_g = bmp.buffer[offset+1];
|
|
||||||
u8 p_r = bmp.buffer[offset+2];
|
|
||||||
u8 p_a = bmp.buffer[offset+3];
|
|
||||||
|
|
||||||
atlas.data[offset .. offset+4] = [DeMultiply(p_r, p_a), DeMultiply(p_b, p_a), DeMultiply(p_g, p_a), p_a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case FT_PIXEL_MODE_MONO:
|
|
||||||
{
|
|
||||||
u32 pitch = bmp.pitch;
|
|
||||||
u8* buf = bmp.buffer;
|
|
||||||
u64 x, y;
|
|
||||||
foreach(r; 0 .. bmp.rows)
|
|
||||||
{
|
|
||||||
u8 bits = 0;
|
|
||||||
u8* buf_ptr = buf;
|
|
||||||
y = (base - font.glyph.bitmap_top + r) + max_h;
|
|
||||||
foreach(c; 0 .. bmp.width)
|
|
||||||
{
|
|
||||||
if((c & 7) == 0)
|
|
||||||
{
|
|
||||||
bits = *buf_ptr;
|
|
||||||
buf_ptr += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
x = max_w + c;
|
|
||||||
u64 offset = (y*atlas_dim.y + x) * 4;
|
|
||||||
|
|
||||||
atlas.data[offset .. offset+4] = [255, 255, 255, bits & 0x80 ? 255 : 0];
|
|
||||||
bits <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf += pitch;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case FT_PIXEL_MODE_GRAY:
|
|
||||||
{
|
|
||||||
u64 x, y;
|
|
||||||
foreach(r; 0 .. bmp.rows)
|
|
||||||
{
|
|
||||||
y = (base - font.glyph.bitmap_top + r) + max_h;
|
|
||||||
foreach(c; 0 .. bmp.width)
|
|
||||||
{
|
|
||||||
x = max_w + c;
|
|
||||||
u64 offset = (y*atlas_dim.y + x) * 4;
|
|
||||||
|
|
||||||
atlas.data[offset .. offset+4] = [255, 255, 255, bmp.buffer[r*bmp.pitch + c]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
assert(false, "Unknown pixel_mode value");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Glyph* g = atlas.glyphs.ptr + char_code;
|
|
||||||
|
|
||||||
f32 height = glyph.metrics.height >> 6;
|
|
||||||
f32 width = glyph.metrics.width >> 6;
|
|
||||||
f32 top = font.glyph.bitmap_top;
|
|
||||||
f32 left = font.glyph.bitmap_left;
|
|
||||||
|
|
||||||
g.ch = cast(dchar)char_code;
|
|
||||||
g.advance = cast(f32)(glyph.advance.x >> 6);
|
|
||||||
g.plane_left = left;
|
|
||||||
g.plane_right = g.plane_left + bmp.width;
|
|
||||||
g.plane_top = 0.0;
|
|
||||||
g.plane_bottom = line_height;
|
|
||||||
|
|
||||||
g.atlas_top = max_h;
|
|
||||||
g.atlas_left = max_w;
|
|
||||||
g.atlas_bottom = max_h + line_height;
|
|
||||||
g.atlas_right = max_w + bmp.width;
|
|
||||||
|
|
||||||
max_w += bmp.width + PADDING;
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return atlas;
|
return atlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info, u32* curve_index, stbtt_fontinfo* stb_font_info)
|
BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info, u32* curve_index, stbtt_fontinfo* stb_font_info)
|
||||||
{
|
{
|
||||||
@ -424,7 +432,7 @@ BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info
|
|||||||
f32 dx = x-cx;
|
f32 dx = x-cx;
|
||||||
f32 dy = y-cy;
|
f32 dy = y-cy;
|
||||||
|
|
||||||
if(Abs(dx) < 0.1 && Abs(dy) < 0.1)
|
if(fabs(dx) < 0.1 && fabs(dy) < 0.1)
|
||||||
{
|
{
|
||||||
cx = x;
|
cx = x;
|
||||||
cy = y;
|
cy = y;
|
||||||
|
|||||||
472
dlib/math.d
472
dlib/math.d
@ -947,9 +947,7 @@ align(16) struct Matrix(T, int D)
|
|||||||
|
|
||||||
template IsMatrixInstantiation(U)
|
template IsMatrixInstantiation(U)
|
||||||
{
|
{
|
||||||
static void IsMatrix(T, int D)(Matrix!(T, D) x) {}
|
enum bool IsMatrixInstantiation = isInstanceOf!(Matrix, U);
|
||||||
|
|
||||||
enum bool IsMatrixInstantiation = is(typeof(IsMatrix(U.init)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1556,262 +1554,270 @@ Clamp(T)(T value, T min, T max) if(IsVector!(T))
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(DLIB_TEST) unittest
|
version(DLIB_TEST)
|
||||||
{
|
{
|
||||||
enum FLOAT_MAX = f32.max;
|
void DLibTestMath()
|
||||||
enum FLOAT_MIN = -f32.max;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
void PrintMatrix(Mat4 mat)
|
|
||||||
{
|
{
|
||||||
foreach(i; 0 .. mat.N)
|
enum FLOAT_MAX = f32.max;
|
||||||
|
enum FLOAT_MIN = -f32.max;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
void PrintMatrix(Mat4 mat)
|
||||||
{
|
{
|
||||||
if(i % 4 == 0)
|
foreach(i; 0 .. mat.N)
|
||||||
{
|
{
|
||||||
printf("\n");
|
if(i % 4 == 0)
|
||||||
}
|
|
||||||
printf("%.08f ", mat.v[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
srand(cast(u32)time(null));
|
|
||||||
|
|
||||||
f32 RandomFloat()
|
|
||||||
{
|
|
||||||
return cast(f32)(rand())/cast(f32)(RAND_MAX + 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Vec2 arithmetic
|
|
||||||
Vec2 v1 = Vec2(5.0, 10.0);
|
|
||||||
Vec2 v2 = Vec2(2.5, 5.0);
|
|
||||||
|
|
||||||
Vec2 result = v1 * v2;
|
|
||||||
|
|
||||||
assert(result == Vec2(12.5, 50.0), "Vec2 mul failure");
|
|
||||||
|
|
||||||
result = v1 + v2;
|
|
||||||
|
|
||||||
assert(result == Vec2(7.5, 15.0), "Vec2 add failure");
|
|
||||||
|
|
||||||
result = v1 - v2;
|
|
||||||
|
|
||||||
assert(result == Vec2(2.5, 5.0), "Vec2 sub failure");
|
|
||||||
|
|
||||||
result = v1 / v2;
|
|
||||||
|
|
||||||
assert(result == Vec2(2.0), "Vec2 div failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Vec3 Arithmetic
|
|
||||||
Vec3 v1 = Vec3(5.0, 10.0, 15.0);
|
|
||||||
Vec3 v2 = Vec3(2.5, 5.0, 7.5);
|
|
||||||
|
|
||||||
Vec3 result = v1 * v2;
|
|
||||||
|
|
||||||
assert(result == Vec3(12.5, 50.0, 112.5), "Vec3 mul failure");
|
|
||||||
|
|
||||||
result = v1 + v2;
|
|
||||||
|
|
||||||
assert(result == Vec3(7.5, 15.0, 22.5), "Vec3 add failure");
|
|
||||||
|
|
||||||
result = v1 - v2;
|
|
||||||
|
|
||||||
assert(result == Vec3(2.5, 5.0, 7.5), "Vec3 sub failure");
|
|
||||||
|
|
||||||
result = v1 / v2;
|
|
||||||
|
|
||||||
assert(result == Vec3(2.0), "Vec3 div failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Vec3 Arithmetic
|
|
||||||
Vec4 v1 = Vec4(5.0, 10.0, 15.0, 20.0);
|
|
||||||
Vec4 v2 = Vec4(2.5, 5.0, 7.5, 10.0);
|
|
||||||
|
|
||||||
Vec4 result = v1 * v2;
|
|
||||||
|
|
||||||
assert(result == Vec4(12.5, 50.0, 112.5, 200.0), "Vec4 mul failure");
|
|
||||||
|
|
||||||
result = v1 + v2;
|
|
||||||
|
|
||||||
assert(result == Vec4(7.5, 15.0, 22.5, 30.0), "Vec4 add failure");
|
|
||||||
|
|
||||||
result = v1 - v2;
|
|
||||||
|
|
||||||
assert(result == Vec4(2.5, 5.0, 7.5, 10.0), "Vec4 sub failure");
|
|
||||||
|
|
||||||
result = v1 / v2;
|
|
||||||
|
|
||||||
assert(result == Vec4(2.0), "Vec4 div failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Mat4 Arithmetic
|
|
||||||
Mat4 m1 = RandomMat4();
|
|
||||||
Mat4 m2 = RandomMat4();
|
|
||||||
Mat4 m3 = m1 * m2;
|
|
||||||
Mat4 m4;
|
|
||||||
|
|
||||||
MatZero(&m4);
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 4; i += 1)
|
|
||||||
{
|
|
||||||
for(u32 j = 0; j < 4; j += 1)
|
|
||||||
{
|
|
||||||
for(u32 k = 0; k < 4; k += 1)
|
|
||||||
{
|
{
|
||||||
m4.rows[i][j] += m1.rows[k][j] * m2.rows[i][k];
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("%.08f ", mat.v[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
srand(cast(u32)time(null));
|
||||||
|
|
||||||
|
f32 RandomFloat()
|
||||||
|
{
|
||||||
|
return cast(f32)(rand())/cast(f32)(RAND_MAX + 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Vec2 arithmetic
|
||||||
|
Vec2 v1 = Vec2(5.0, 10.0);
|
||||||
|
Vec2 v2 = Vec2(2.5, 5.0);
|
||||||
|
|
||||||
|
Vec2 result = v1 * v2;
|
||||||
|
|
||||||
|
assert(result == Vec2(12.5, 50.0), "Vec2 mul failure");
|
||||||
|
|
||||||
|
result = v1 + v2;
|
||||||
|
|
||||||
|
assert(result == Vec2(7.5, 15.0), "Vec2 add failure");
|
||||||
|
|
||||||
|
result = v1 - v2;
|
||||||
|
|
||||||
|
assert(result == Vec2(2.5, 5.0), "Vec2 sub failure");
|
||||||
|
|
||||||
|
result = v1 / v2;
|
||||||
|
|
||||||
|
assert(result == Vec2(2.0), "Vec2 div failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Vec3 Arithmetic
|
||||||
|
Vec3 v1 = Vec3(5.0, 10.0, 15.0);
|
||||||
|
Vec3 v2 = Vec3(2.5, 5.0, 7.5);
|
||||||
|
|
||||||
|
Vec3 result = v1 * v2;
|
||||||
|
|
||||||
|
assert(result == Vec3(12.5, 50.0, 112.5), "Vec3 mul failure");
|
||||||
|
|
||||||
|
result = v1 + v2;
|
||||||
|
|
||||||
|
assert(result == Vec3(7.5, 15.0, 22.5), "Vec3 add failure");
|
||||||
|
|
||||||
|
result = v1 - v2;
|
||||||
|
|
||||||
|
assert(result == Vec3(2.5, 5.0, 7.5), "Vec3 sub failure");
|
||||||
|
|
||||||
|
result = v1 / v2;
|
||||||
|
|
||||||
|
assert(result == Vec3(2.0), "Vec3 div failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Vec3 Arithmetic
|
||||||
|
Vec4 v1 = Vec4(5.0, 10.0, 15.0, 20.0);
|
||||||
|
Vec4 v2 = Vec4(2.5, 5.0, 7.5, 10.0);
|
||||||
|
|
||||||
|
Vec4 result = v1 * v2;
|
||||||
|
|
||||||
|
assert(result == Vec4(12.5, 50.0, 112.5, 200.0), "Vec4 mul failure");
|
||||||
|
|
||||||
|
result = v1 + v2;
|
||||||
|
|
||||||
|
assert(result == Vec4(7.5, 15.0, 22.5, 30.0), "Vec4 add failure");
|
||||||
|
|
||||||
|
result = v1 - v2;
|
||||||
|
|
||||||
|
assert(result == Vec4(2.5, 5.0, 7.5, 10.0), "Vec4 sub failure");
|
||||||
|
|
||||||
|
result = v1 / v2;
|
||||||
|
|
||||||
|
assert(result == Vec4(2.0), "Vec4 div failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Mat4 Arithmetic
|
||||||
|
Mat4 m1 = RandomMat4();
|
||||||
|
Mat4 m2 = RandomMat4();
|
||||||
|
Mat4 m3 = m1 * m2;
|
||||||
|
Mat4 m4;
|
||||||
|
|
||||||
|
MatZero(&m4);
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 4; i += 1)
|
||||||
|
{
|
||||||
|
for(u32 j = 0; j < 4; j += 1)
|
||||||
|
{
|
||||||
|
for(u32 k = 0; k < 4; k += 1)
|
||||||
|
{
|
||||||
|
m4.rows[i][j] += m1.rows[k][j] * m2.rows[i][k];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(m3 == m4, "Mat4 mul failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(m3 == m4, "Mat4 mul failure");
|
{ // Translate
|
||||||
}
|
Mat4 mat = Mat4Identity();
|
||||||
|
Vec4 vec = Vec4(1.0, 2.0, 3.0, 1.0);
|
||||||
|
|
||||||
{ // Translate
|
Translate(&mat, Vec3(13.0, 11.0, 7.0));
|
||||||
Mat4 mat = Mat4Identity();
|
Vec4 result = mat * vec;
|
||||||
Vec4 vec = Vec4(1.0, 2.0, 3.0, 1.0);
|
|
||||||
|
|
||||||
Translate(&mat, Vec3(13.0, 11.0, 7.0));
|
assert(result == Vec4(14.0, 13.0, 10.0, 1.0));
|
||||||
Vec4 result = mat * vec;
|
|
||||||
|
|
||||||
assert(result == Vec4(14.0, 13.0, 10.0, 1.0));
|
mat = Mat4Identity();
|
||||||
|
Translate(&mat, Vec3(1.0, -1.0, -5.0));
|
||||||
|
result = mat * result;
|
||||||
|
|
||||||
mat = Mat4Identity();
|
assert(result == Vec4(15.0, 12.0, 5.0, 1.0));
|
||||||
Translate(&mat, Vec3(1.0, -1.0, -5.0));
|
}
|
||||||
result = mat * result;
|
|
||||||
|
|
||||||
assert(result == Vec4(15.0, 12.0, 5.0, 1.0));
|
{ // Identity
|
||||||
}
|
Mat4 identity = Mat4(
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0
|
||||||
|
);
|
||||||
|
Mat4 mat = Mat4Identity();
|
||||||
|
|
||||||
{ // Identity
|
assert(identity == mat);
|
||||||
Mat4 identity = Mat4(
|
}
|
||||||
1.0, 0.0, 0.0, 0.0,
|
|
||||||
0.0, 1.0, 0.0, 0.0,
|
|
||||||
0.0, 0.0, 1.0, 0.0,
|
|
||||||
0.0, 0.0, 0.0, 1.0
|
|
||||||
);
|
|
||||||
Mat4 mat = Mat4Identity();
|
|
||||||
|
|
||||||
assert(identity == mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{ // Inverse
|
{ // Inverse
|
||||||
foreach(i; 0 .. 1000)
|
foreach(i; 0 .. 1000)
|
||||||
|
{
|
||||||
|
Mat4 m1 = RandomMat4();
|
||||||
|
|
||||||
|
Mat4 m1_inv = Inverse(m1);
|
||||||
|
Mat4 m1_reinv = Inverse(m1_inv);
|
||||||
|
|
||||||
|
assert(m1 == m1_reinv, "Inverse test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Cross
|
||||||
|
Vec3 v1 = Vec3(2.0, -3.0, 4.0);
|
||||||
|
Vec3 v2 = Vec3(12.0, -31.0, 43.0);
|
||||||
|
|
||||||
|
Vec3 v3 = Cross(v1, v2);
|
||||||
|
|
||||||
|
Vec3 v4 = Vec3(
|
||||||
|
v1.y * v2.z - v1.z * v2.y,
|
||||||
|
v1.z * v2.x - v1.x * v2.z,
|
||||||
|
v1.x * v2.y - v1.y * v2.x
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(v3 == v4, "Vec3 Cross failure");
|
||||||
|
|
||||||
|
v3 = CrossN(v1, v2);
|
||||||
|
|
||||||
|
Normalize(&v4);
|
||||||
|
|
||||||
|
assert(v3 == v4, "Vec3 CrossN failure");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
{ // Initializers
|
||||||
|
u32[4] arr = [1, 2, 3, 4];
|
||||||
|
Vec4 vec = Vec4(arr);
|
||||||
|
assert(vec == Vec4(1.0, 2.0, 3.0, 4.0));
|
||||||
|
|
||||||
|
Mat2 mat = Mat2(1.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
f32[16] floats = 22.0;
|
||||||
|
Mat4 mat4 = Mat4(floats);
|
||||||
|
assert(mat4.v == floats);
|
||||||
|
|
||||||
|
Quat quat = Quat(1.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
Quat quat2;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Mat4 m1 = RandomMat4();
|
Vec3 vec = Vec3(1.0, 2.0, 3.0);
|
||||||
|
Mat4 mat = Mat4(
|
||||||
|
1.0, 1.0, 1.0, 1.0,
|
||||||
|
2.0, 2.0, 2.0, 2.0,
|
||||||
|
3.0, 3.0, 3.0, 3.0,
|
||||||
|
4.0, 4.0, 4.0, 4.0
|
||||||
|
);
|
||||||
|
|
||||||
Mat4 m1_inv = Inverse(m1);
|
Transform(&vec, &mat, 1.0);
|
||||||
Mat4 m1_reinv = Inverse(m1_inv);
|
|
||||||
|
assert(vec == Vec3(18.0));
|
||||||
|
|
||||||
|
Vec4 low = Vec4(-5.0);
|
||||||
|
Vec4 high = Vec4(+5.0);
|
||||||
|
Vec4 test = Vec4(-500.0);
|
||||||
|
|
||||||
|
test = Clamp(test, low, high);
|
||||||
|
|
||||||
|
assert(test == low);
|
||||||
|
|
||||||
|
test = Vec4(+500.0);
|
||||||
|
test = Clamp(test, low, high);
|
||||||
|
|
||||||
|
assert(test == high);
|
||||||
|
|
||||||
assert(m1 == m1_reinv, "Inverse test failed");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{ // Cross
|
|
||||||
Vec3 v1 = Vec3(2.0, -3.0, 4.0);
|
|
||||||
Vec3 v2 = Vec3(12.0, -31.0, 43.0);
|
|
||||||
|
|
||||||
Vec3 v3 = Cross(v1, v2);
|
|
||||||
|
|
||||||
Vec3 v4 = Vec3(
|
|
||||||
v1.y * v2.z - v1.z * v2.y,
|
|
||||||
v1.z * v2.x - v1.x * v2.z,
|
|
||||||
v1.x * v2.y - v1.y * v2.x
|
|
||||||
);
|
|
||||||
|
|
||||||
assert(v3 == v4, "Vec3 Cross failure");
|
|
||||||
|
|
||||||
v3 = CrossN(v1, v2);
|
|
||||||
|
|
||||||
Normalize(&v4);
|
|
||||||
|
|
||||||
assert(v3 == v4, "Vec3 CrossN failure");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
{ // Initializers
|
|
||||||
u32[4] arr = [1, 2, 3, 4];
|
|
||||||
Vec4 vec = Vec4(arr);
|
|
||||||
assert(vec == Vec4(1.0, 2.0, 3.0, 4.0));
|
|
||||||
|
|
||||||
Mat2 mat = Mat2(1.0, 0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
f32[16] floats = 22.0;
|
|
||||||
Mat4 mat4 = Mat4(floats);
|
|
||||||
assert(mat4.v == floats);
|
|
||||||
|
|
||||||
Quat quat = Quat(1.0, 1.0, 1.0, 1.0);
|
|
||||||
|
|
||||||
Quat quat2;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vec3 vec = Vec3(1.0, 2.0, 3.0);
|
|
||||||
Mat4 mat = Mat4(
|
|
||||||
1.0, 1.0, 1.0, 1.0,
|
|
||||||
2.0, 2.0, 2.0, 2.0,
|
|
||||||
3.0, 3.0, 3.0, 3.0,
|
|
||||||
4.0, 4.0, 4.0, 4.0
|
|
||||||
);
|
|
||||||
|
|
||||||
Transform(&vec, &mat, 1.0);
|
|
||||||
|
|
||||||
assert(vec == Vec3(18.0));
|
|
||||||
|
|
||||||
Vec4 low = Vec4(-5.0);
|
|
||||||
Vec4 high = Vec4(+5.0);
|
|
||||||
Vec4 test = Vec4(-500.0);
|
|
||||||
|
|
||||||
test = Clamp(test, low, high);
|
|
||||||
|
|
||||||
assert(test == low);
|
|
||||||
|
|
||||||
test = Vec4(+500.0);
|
|
||||||
test = Clamp(test, low, high);
|
|
||||||
|
|
||||||
assert(test == high);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vec4 m0 = Vec4(0.0);
|
|
||||||
Vec4 m1 = Vec4(10.0);
|
|
||||||
|
|
||||||
assert(Mix(m0, m1, 0.0) == m0);
|
|
||||||
assert(Mix(m0, m1, 1.0) == m1);
|
|
||||||
assert(Mix(m0, m1, 0.5) == Vec4(5.0));
|
|
||||||
assert(Mix(m0, m1, 0.75) == Vec4(7.5));
|
|
||||||
|
|
||||||
void TestModify(Vec4 v)
|
|
||||||
{
|
{
|
||||||
v.r = 55;
|
Vec4 m0 = Vec4(0.0);
|
||||||
|
Vec4 m1 = Vec4(10.0);
|
||||||
|
|
||||||
|
assert(Mix(m0, m1, 0.0) == m0);
|
||||||
|
assert(Mix(m0, m1, 1.0) == m1);
|
||||||
|
assert(Mix(m0, m1, 0.5) == Vec4(5.0));
|
||||||
|
assert(Mix(m0, m1, 0.75) == Vec4(7.5));
|
||||||
|
|
||||||
|
void TestModify(Vec4 v)
|
||||||
|
{
|
||||||
|
v.r = 55;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
u32 max = Max(50, 33, 123.0);
|
||||||
|
assert(max == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Vec2 v0 = 50U;
|
||||||
|
|
||||||
|
assert(Abs(v0.x-50.0) < 0.0009 && Abs(v0.y-50.0) < 0.0009);
|
||||||
|
|
||||||
|
U8Vec2 v1 = U8Vec2(55U);
|
||||||
|
|
||||||
|
assert(v1.x == 55 && v1.y == 55);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(Ceil(10.5) == 11.0);
|
||||||
|
assert(Floor(100.33) == 100.0);
|
||||||
|
assert(Abs(-500) == 500);
|
||||||
|
assert(Abs(-500.0) == 500.0);
|
||||||
|
|
||||||
|
assert(Clamp(55, 10, 40) == 40);
|
||||||
|
assert(Clamp(-20, -5, 55) == -5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
{
|
{
|
||||||
u32 max = Max(50, 33, 123.0);
|
DLibTestMath();
|
||||||
assert(max == 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Vec2 v0 = 50U;
|
|
||||||
|
|
||||||
assert(Abs(v0.x-50.0) < 0.0009 && Abs(v0.y-50.0) < 0.0009);
|
|
||||||
|
|
||||||
U8Vec2 v1 = U8Vec2(55U);
|
|
||||||
|
|
||||||
assert(v1.x == 55 && v1.y == 55);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
assert(Ceil(10.5) == 11.0);
|
|
||||||
assert(Floor(100.33) == 100.0);
|
|
||||||
assert(Abs(-500) == 500);
|
|
||||||
assert(Abs(-500.0) == 500.0);
|
|
||||||
|
|
||||||
assert(Clamp(55, 10, 40) == 40);
|
|
||||||
assert(Clamp(-20, -5, 55) == -5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,15 +16,3 @@ else
|
|||||||
public import dlibincludes;
|
public import dlibincludes;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared static this()
|
|
||||||
{
|
|
||||||
static if(NativeTarget)
|
|
||||||
{
|
|
||||||
FT_Init_FreeType(&FT_LIB);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ta_init(256, 16, (void*).sizeof*2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
542
dlib/util.d
542
dlib/util.d
@ -17,7 +17,8 @@ static if(NativeTarget)
|
|||||||
import dlib.platform;
|
import dlib.platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]) || is(T: const(char)[]));
|
enum bool StringType(T) = (is(T == string) || is(T == u8[]) || is(T == char[]) || is(T == const(char)[]));
|
||||||
|
enum bool CStringType(T) = (is(T == char*) || is(T == u8*) || is(T == immutable(char)*) || is(T == const(char)*));
|
||||||
|
|
||||||
pragma(inline) void
|
pragma(inline) void
|
||||||
Int3()
|
Int3()
|
||||||
@ -59,6 +60,12 @@ Str(T)(T arr) if(StringType!(T))
|
|||||||
return (cast(immutable(char)*)arr.ptr)[0 .. arr.length];
|
return (cast(immutable(char)*)arr.ptr)[0 .. arr.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
Str(T)(T ptr) if(CStringType!(T))
|
||||||
|
{
|
||||||
|
return ptr ? Str(ptr[0 .. cast(usize)strlen(ptr)]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
alias ConvToStr = Str;
|
alias ConvToStr = Str;
|
||||||
|
|
||||||
T[]
|
T[]
|
||||||
@ -1120,273 +1127,280 @@ ProcessHash(const(void)* data, ref u64 state_0, ref u64 state_1, ref u64 state_2
|
|||||||
state_3 = ProcessSingleHashValue(state_3, block[3]);
|
state_3 = ProcessSingleHashValue(state_3, block[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
version(DLIB_TEST) unittest
|
version(DLIB_TEST)
|
||||||
{
|
{
|
||||||
{ // Singly Linked List
|
void DLibTestUtil()
|
||||||
alias LT = ListBox!(u32, false);
|
|
||||||
LinkedList!(LT) list;
|
|
||||||
LT[5] nodes;
|
|
||||||
foreach(u32 i, n; nodes)
|
|
||||||
{
|
|
||||||
nodes[i].value = i;
|
|
||||||
SLLPush(&list, &nodes[i], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 count = 0;
|
|
||||||
u32[3] res1 = [0, 2, 4];
|
|
||||||
|
|
||||||
SLLRemove(&list, &nodes[1], &nodes[0], null);
|
|
||||||
SLLRemove(&list, &nodes[3], &nodes[2], null);
|
|
||||||
|
|
||||||
LT* n = list.first;
|
|
||||||
|
|
||||||
assert(list.first != null && list.last != null);
|
|
||||||
assert(n != null);
|
|
||||||
assert(n.next != null);
|
|
||||||
|
|
||||||
void TestSLList(LinkedList!(LT)* list, u32[] result)
|
|
||||||
{
|
|
||||||
LT* n = list.first;
|
|
||||||
foreach(i, v; result)
|
|
||||||
{
|
|
||||||
assert(n != null);
|
|
||||||
assert(v == n.value);
|
|
||||||
|
|
||||||
if(i == result.length-1)
|
|
||||||
{
|
|
||||||
assert(n.next == null);
|
|
||||||
assert(n == list.last);
|
|
||||||
}
|
|
||||||
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TestSLList(&list, res1);
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
u32[5] res2 = [3, 0, 2, 4, 1];
|
|
||||||
|
|
||||||
SLLPushFront(&list, &nodes[3], null);
|
|
||||||
SLLPush(&list, &nodes[1], null);
|
|
||||||
|
|
||||||
TestSLList(&list, res2);
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
SLLRemove(&list, &nodes[3], null, null);
|
|
||||||
SLLRemove(&list, &nodes[1], &nodes[4], null);
|
|
||||||
|
|
||||||
TestSLList(&list, res1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Doubly Linked List
|
|
||||||
alias LT = ListBox!(u32, true);
|
|
||||||
|
|
||||||
void TestDLList(T)(T* list, u32[] result)
|
|
||||||
{
|
|
||||||
LT* n = list.first;
|
|
||||||
foreach(i, v; result)
|
|
||||||
{
|
|
||||||
assert(n != null);
|
|
||||||
assert(v == n.value);
|
|
||||||
|
|
||||||
if(i > 0)
|
|
||||||
{
|
|
||||||
assert(n.prev != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i == result.length-1)
|
|
||||||
{
|
|
||||||
assert(n.next == null);
|
|
||||||
assert(n == list.last);
|
|
||||||
}
|
|
||||||
|
|
||||||
n = n.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = list.last;
|
|
||||||
foreach_reverse(i, v; result)
|
|
||||||
{
|
|
||||||
assert(n != null);
|
|
||||||
assert(v == n.value);
|
|
||||||
|
|
||||||
if(i == result.length-1)
|
|
||||||
{
|
|
||||||
assert(n.next == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i == 0)
|
|
||||||
{
|
|
||||||
assert(n.prev == null);
|
|
||||||
assert(n == list.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
n = n.prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedList!(LT) list;
|
|
||||||
LT[5] nodes;
|
|
||||||
foreach(u32 i, n; nodes)
|
|
||||||
{
|
|
||||||
nodes[i].value = i;
|
|
||||||
DLLPush(&list, &nodes[i], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(list.first != null && list.last != null);
|
|
||||||
|
|
||||||
u32[5] test1 = [0, 1, 2, 3, 4];
|
|
||||||
TestDLList(&list, test1);
|
|
||||||
|
|
||||||
u32 count = 0;
|
|
||||||
u32[3] res1 = [0, 2, 4];
|
|
||||||
|
|
||||||
DLLRemove(&list, &nodes[1], null);
|
|
||||||
DLLRemove(&list, &nodes[3], null);
|
|
||||||
|
|
||||||
TestDLList(&list, res1);
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
u32[5] res2 = [3, 0, 2, 4, 1];
|
|
||||||
|
|
||||||
DLLPushFront(&list, &nodes[3], null);
|
|
||||||
DLLPush(&list, &nodes[1], null);
|
|
||||||
|
|
||||||
TestDLList(&list, res2);
|
|
||||||
|
|
||||||
DLLRemove(&list, &nodes[3], null);
|
|
||||||
DLLRemove(&list, &nodes[1], null);
|
|
||||||
|
|
||||||
TestDLList(&list, res1);
|
|
||||||
|
|
||||||
DLLInsert(&list, &nodes[1], &nodes[0], null);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // MemCpy
|
|
||||||
|
|
||||||
u8[777] bytes;
|
|
||||||
u8[777] test_bytes;
|
|
||||||
|
|
||||||
bytes[0 .. 123] = 123;
|
|
||||||
bytes[123 .. 333] = 133;
|
|
||||||
bytes[333 .. 655] = 155;
|
|
||||||
bytes[655 .. $] = 199;
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = bytes[0 .. $];
|
|
||||||
|
|
||||||
assert(test_bytes == bytes);
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = 0;
|
|
||||||
|
|
||||||
MemCpy(test_bytes.ptr, bytes.ptr, 777);
|
|
||||||
|
|
||||||
assert(test_bytes == bytes);
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = 0;
|
|
||||||
|
|
||||||
MemCpy(test_bytes.ptr+100, bytes.ptr, 32);
|
|
||||||
|
|
||||||
u32 count = 0;
|
|
||||||
foreach(i, v; test_bytes[100 .. 132])
|
|
||||||
{
|
|
||||||
if(v != bytes[count])
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(test_bytes[100 .. 132] == bytes[0 .. 32]);
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = 0;
|
|
||||||
|
|
||||||
MemCpy(test_bytes.ptr, bytes.ptr, 33);
|
|
||||||
|
|
||||||
assert(test_bytes[0 .. 33] == bytes[0 .. 33]);
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = 0;
|
|
||||||
|
|
||||||
MemCpy(test_bytes.ptr, bytes.ptr, 65);
|
|
||||||
|
|
||||||
assert(test_bytes[0 .. 65] == bytes[0 .. 65]);
|
|
||||||
|
|
||||||
test_bytes[0 .. $] = 0;
|
|
||||||
|
|
||||||
MemCpy(test_bytes.ptr, bytes.ptr, 96);
|
|
||||||
|
|
||||||
foreach(i, v; test_bytes[0 .. 96])
|
|
||||||
{
|
|
||||||
if(v != bytes[i])
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(test_bytes[0 .. 96] == bytes[0 .. 96]);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Hash Table
|
|
||||||
auto table = CreateHashTable!(u64, u64)(10);
|
|
||||||
|
|
||||||
table[100] = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Hash
|
|
||||||
u8[10] arr_1 = 5;
|
|
||||||
u64[10] arr_2 = 555;
|
|
||||||
u64 val_1 = 555555;
|
|
||||||
u32 val_2 = 33333;
|
|
||||||
|
|
||||||
u64 v1 = Hash(arr_1);
|
|
||||||
u64 v2 = Hash(arr_2);
|
|
||||||
u64 v3 = Hash(&val_1);
|
|
||||||
|
|
||||||
assert(v1 > 0);
|
|
||||||
assert(v2 > 0);
|
|
||||||
assert(v3 > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Casts
|
|
||||||
u8[] arr = CastStr!(u8)("Test");
|
|
||||||
char[2] char_arr = ['a', 'b'];
|
|
||||||
arr = CastArr!(u8)(char_arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
ResetScratch(MB(4));
|
{ // Singly Linked List
|
||||||
string str = Scratchf("%s testing %s", 55, "testing");
|
alias LT = ListBox!(u32, false);
|
||||||
assert(str == "55 testing testing");
|
LinkedList!(LT) list;
|
||||||
Logf(str);
|
LT[5] nodes;
|
||||||
|
foreach(u32 i, n; nodes)
|
||||||
|
{
|
||||||
|
nodes[i].value = i;
|
||||||
|
SLLPush(&list, &nodes[i], null);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
u32[3] res1 = [0, 2, 4];
|
||||||
|
|
||||||
|
SLLRemove(&list, &nodes[1], &nodes[0], null);
|
||||||
|
SLLRemove(&list, &nodes[3], &nodes[2], null);
|
||||||
|
|
||||||
|
LT* n = list.first;
|
||||||
|
|
||||||
|
assert(list.first != null && list.last != null);
|
||||||
|
assert(n != null);
|
||||||
|
assert(n.next != null);
|
||||||
|
|
||||||
|
void TestSLList(LinkedList!(LT)* list, u32[] result)
|
||||||
|
{
|
||||||
|
LT* n = list.first;
|
||||||
|
foreach(i, v; result)
|
||||||
|
{
|
||||||
|
assert(n != null);
|
||||||
|
assert(v == n.value);
|
||||||
|
|
||||||
|
if(i == result.length-1)
|
||||||
|
{
|
||||||
|
assert(n.next == null);
|
||||||
|
assert(n == list.last);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestSLList(&list, res1);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
u32[5] res2 = [3, 0, 2, 4, 1];
|
||||||
|
|
||||||
|
SLLPushFront(&list, &nodes[3], null);
|
||||||
|
SLLPush(&list, &nodes[1], null);
|
||||||
|
|
||||||
|
TestSLList(&list, res2);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
SLLRemove(&list, &nodes[3], null, null);
|
||||||
|
SLLRemove(&list, &nodes[1], &nodes[4], null);
|
||||||
|
|
||||||
|
TestSLList(&list, res1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Doubly Linked List
|
||||||
|
alias LT = ListBox!(u32, true);
|
||||||
|
|
||||||
|
void TestDLList(T)(T* list, u32[] result)
|
||||||
|
{
|
||||||
|
LT* n = list.first;
|
||||||
|
foreach(i, v; result)
|
||||||
|
{
|
||||||
|
assert(n != null);
|
||||||
|
assert(v == n.value);
|
||||||
|
|
||||||
|
if(i > 0)
|
||||||
|
{
|
||||||
|
assert(n.prev != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == result.length-1)
|
||||||
|
{
|
||||||
|
assert(n.next == null);
|
||||||
|
assert(n == list.last);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = list.last;
|
||||||
|
foreach_reverse(i, v; result)
|
||||||
|
{
|
||||||
|
assert(n != null);
|
||||||
|
assert(v == n.value);
|
||||||
|
|
||||||
|
if(i == result.length-1)
|
||||||
|
{
|
||||||
|
assert(n.next == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == 0)
|
||||||
|
{
|
||||||
|
assert(n.prev == null);
|
||||||
|
assert(n == list.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList!(LT) list;
|
||||||
|
LT[5] nodes;
|
||||||
|
foreach(u32 i, n; nodes)
|
||||||
|
{
|
||||||
|
nodes[i].value = i;
|
||||||
|
DLLPush(&list, &nodes[i], null);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(list.first != null && list.last != null);
|
||||||
|
|
||||||
|
u32[5] test1 = [0, 1, 2, 3, 4];
|
||||||
|
TestDLList(&list, test1);
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
u32[3] res1 = [0, 2, 4];
|
||||||
|
|
||||||
|
DLLRemove(&list, &nodes[1], null);
|
||||||
|
DLLRemove(&list, &nodes[3], null);
|
||||||
|
|
||||||
|
TestDLList(&list, res1);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
u32[5] res2 = [3, 0, 2, 4, 1];
|
||||||
|
|
||||||
|
DLLPushFront(&list, &nodes[3], null);
|
||||||
|
DLLPush(&list, &nodes[1], null);
|
||||||
|
|
||||||
|
TestDLList(&list, res2);
|
||||||
|
|
||||||
|
DLLRemove(&list, &nodes[3], null);
|
||||||
|
DLLRemove(&list, &nodes[1], null);
|
||||||
|
|
||||||
|
TestDLList(&list, res1);
|
||||||
|
|
||||||
|
DLLInsert(&list, &nodes[1], &nodes[0], null);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // MemCpy
|
||||||
|
|
||||||
|
u8[777] bytes;
|
||||||
|
u8[777] test_bytes;
|
||||||
|
|
||||||
|
bytes[0 .. 123] = 123;
|
||||||
|
bytes[123 .. 333] = 133;
|
||||||
|
bytes[333 .. 655] = 155;
|
||||||
|
bytes[655 .. $] = 199;
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = bytes[0 .. $];
|
||||||
|
|
||||||
|
assert(test_bytes == bytes);
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = 0;
|
||||||
|
|
||||||
|
MemCpy(test_bytes.ptr, bytes.ptr, 777);
|
||||||
|
|
||||||
|
assert(test_bytes == bytes);
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = 0;
|
||||||
|
|
||||||
|
MemCpy(test_bytes.ptr+100, bytes.ptr, 32);
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
foreach(i, v; test_bytes[100 .. 132])
|
||||||
|
{
|
||||||
|
if(v != bytes[count])
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(test_bytes[100 .. 132] == bytes[0 .. 32]);
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = 0;
|
||||||
|
|
||||||
|
MemCpy(test_bytes.ptr, bytes.ptr, 33);
|
||||||
|
|
||||||
|
assert(test_bytes[0 .. 33] == bytes[0 .. 33]);
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = 0;
|
||||||
|
|
||||||
|
MemCpy(test_bytes.ptr, bytes.ptr, 65);
|
||||||
|
|
||||||
|
assert(test_bytes[0 .. 65] == bytes[0 .. 65]);
|
||||||
|
|
||||||
|
test_bytes[0 .. $] = 0;
|
||||||
|
|
||||||
|
MemCpy(test_bytes.ptr, bytes.ptr, 96);
|
||||||
|
|
||||||
|
foreach(i, v; test_bytes[0 .. 96])
|
||||||
|
{
|
||||||
|
if(v != bytes[i])
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(test_bytes[0 .. 96] == bytes[0 .. 96]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Hash Table
|
||||||
|
auto table = CreateHashTable!(u64, u64)(10);
|
||||||
|
|
||||||
|
table[100] = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Hash
|
||||||
|
u8[10] arr_1 = 5;
|
||||||
|
u64[10] arr_2 = 555;
|
||||||
|
u64 val_1 = 555555;
|
||||||
|
u32 val_2 = 33333;
|
||||||
|
|
||||||
|
u64 v1 = Hash(arr_1);
|
||||||
|
u64 v2 = Hash(arr_2);
|
||||||
|
u64 v3 = Hash(&val_1);
|
||||||
|
|
||||||
|
assert(v1 > 0);
|
||||||
|
assert(v2 > 0);
|
||||||
|
assert(v3 > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Casts
|
||||||
|
u8[] arr = CastStr!(u8)("Test");
|
||||||
|
char[2] char_arr = ['a', 'b'];
|
||||||
|
arr = CastArr!(u8)(char_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ResetScratch(MB(4));
|
||||||
|
string str = Scratchf("%s testing %s", 55, "testing");
|
||||||
|
assert(str == "55 testing testing");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
u64[555] slice = 53321;
|
||||||
|
|
||||||
|
MemSet(slice.ptr, 0, u64.sizeof*slice.length);
|
||||||
|
|
||||||
|
for(usize i = 0; i < slice.length; i += 1)
|
||||||
|
{
|
||||||
|
assert(slice[i] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemSet(slice.ptr, 5, u64.sizeof*slice.length);
|
||||||
|
|
||||||
|
u64 cmp = cast(u64)(
|
||||||
|
cast(u64)(5) << 56 |
|
||||||
|
cast(u64)(5) << 48 |
|
||||||
|
cast(u64)(5) << 40 |
|
||||||
|
cast(u64)(5) << 32 |
|
||||||
|
cast(u64)(5) << 24 |
|
||||||
|
cast(u64)(5) << 16 |
|
||||||
|
cast(u64)(5) << 8 |
|
||||||
|
cast(u64)(5) << 0
|
||||||
|
);
|
||||||
|
|
||||||
|
for(usize i = 0; i < slice.length; i += 1)
|
||||||
|
{
|
||||||
|
assert(slice[i] == cmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
{
|
{
|
||||||
u64[555] slice = 53321;
|
DLibTestUtil();
|
||||||
|
|
||||||
MemSet(slice.ptr, 0, u64.sizeof*slice.length);
|
|
||||||
|
|
||||||
for(usize i = 0; i < slice.length; i += 1)
|
|
||||||
{
|
|
||||||
assert(slice[i] == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
MemSet(slice.ptr, 5, u64.sizeof*slice.length);
|
|
||||||
|
|
||||||
u64 cmp = cast(u64)(
|
|
||||||
cast(u64)(5) << 56 |
|
|
||||||
cast(u64)(5) << 48 |
|
|
||||||
cast(u64)(5) << 40 |
|
|
||||||
cast(u64)(5) << 32 |
|
|
||||||
cast(u64)(5) << 24 |
|
|
||||||
cast(u64)(5) << 16 |
|
|
||||||
cast(u64)(5) << 8 |
|
|
||||||
cast(u64)(5) << 0
|
|
||||||
);
|
|
||||||
|
|
||||||
for(usize i = 0; i < slice.length; i += 1)
|
|
||||||
{
|
|
||||||
assert(slice[i] == cmp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4414
stb_truetype.d
Normal file
4414
stb_truetype.d
Normal file
File diff suppressed because it is too large
Load Diff
10
test.sh
10
test.sh
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
name="Test_Runner"
|
name="Test_Runner"
|
||||||
|
|
||||||
shared_src="dlib/package.d dlib/platform.d dlib/fonts.d dlib/aliases.d dlib/math.d dlib/util.d dlib/alloc.d dlib/assets.d dlib/externdecl.d"
|
shared_src="dlib/package.d dlib/platform.d dlib/fonts.d dlib/aliases.d dlib/math.d dlib/util.d dlib/alloc.d dlib/assets.d dlib/externdecl.d stb_truetype.d"
|
||||||
|
|
||||||
if [ "$1" == "wasm" ]; then
|
if [ "$1" == "wasm" ]; then
|
||||||
flags="-c -vgc -mtriple=wasm32-unknown-unknown-wasm -d-version=DLIB_TEST -fvisibility=public -dllimport=all --unittest --Xcc=-DBUILD_WASM -Iwasm/runtime -L--no-entry -i=core -i=std -i=. --real-precision=double --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90"
|
flags="-vgc -betterC -mtriple=wasm32-unknown-unknown-wasm -d-version=DLIB_TEST -fvisibility=hidden --unittest -Iwasm/runtime -i=core -i=std -i=. --real-precision=double --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90 --of=build/dlib.wasm -gcc=clang"
|
||||||
wasm_src="wasm/runtime/object.d"
|
wasm_src="wasm/runtime/object.d stb_truetype.d"
|
||||||
|
|
||||||
/bin/bash ./build.sh build wasm
|
#/bin/bash ./build.sh build wasm
|
||||||
|
|
||||||
ldc2 $flags $shared_src $wasm_src
|
ldc2 $flags $shared_src $wasm_src
|
||||||
|
|
||||||
wasm-ld build/dlibmain.wasm build/dlibincludes.wasm --export=RunTests --error-limit=0 --export-memory -obuild/dlib.wasm
|
#wasm-ld build/dlibmain.wasm build/dlibincludes.wasm --export=RunTests --error-limit=0 --export-memory -obuild/dlib.wasm
|
||||||
|
|
||||||
cp build/dlib.wasm wasm/dlib.wasm
|
cp build/dlib.wasm wasm/dlib.wasm
|
||||||
else
|
else
|
||||||
|
|||||||
26
test/teststbtt.d
Normal file
26
test/teststbtt.d
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import stb_truetype;
|
||||||
|
|
||||||
|
import core.stdc.stdio;
|
||||||
|
import core.stdc.stdlib;
|
||||||
|
|
||||||
|
ubyte[] FONT_DATA = cast(ubyte[])import("pc-9800.ttf");
|
||||||
|
stbtt_bakedchar[96] cdata;
|
||||||
|
|
||||||
|
void
|
||||||
|
main(string[] args)
|
||||||
|
{
|
||||||
|
char arg1 = args.length > 1 ? args[1][0] : 'a';
|
||||||
|
|
||||||
|
stbtt_fontinfo font;
|
||||||
|
ubyte* bitmap;
|
||||||
|
int w,h,i,j,c = (arg1), s = (args.length > 2 ? atoi(args[2].ptr) : 20);
|
||||||
|
|
||||||
|
stbtt_InitFont(&font, FONT_DATA.ptr, stbtt_GetFontOffsetForIndex(FONT_DATA.ptr, 0));
|
||||||
|
bitmap = stbtt_GetCodepointBitmap(&font, 0, stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, null, null);
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
for (i=0; i < w; ++i)
|
||||||
|
putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
wasm/dlib.wasm
BIN
wasm/dlib.wasm
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
module core.arsd.objectutils;
|
module core.arsd.objectutils;
|
||||||
|
|
||||||
|
/*
|
||||||
///Provides only __doPostblit and hasPostblit for making the code simpler.
|
///Provides only __doPostblit and hasPostblit for making the code simpler.
|
||||||
size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
|
size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
|
||||||
{
|
{
|
||||||
@ -11,6 +12,9 @@ size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
// strip const/immutable/shared/inout from type info
|
// strip const/immutable/shared/inout from type info
|
||||||
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
||||||
{
|
{
|
||||||
@ -32,6 +36,7 @@ inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
|||||||
}
|
}
|
||||||
return ti;
|
return ti;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
bool hasPostblit(in TypeInfo ti) nothrow pure
|
bool hasPostblit(in TypeInfo ti) nothrow pure
|
||||||
{
|
{
|
||||||
|
|||||||
@ -157,10 +157,10 @@ void* _d_cast(To, From)(From o) @trusted
|
|||||||
{
|
{
|
||||||
return _d_paint_cast!To(o);
|
return _d_paint_cast!To(o);
|
||||||
}
|
}
|
||||||
else static if (is (To : From))
|
//else static if (is (To : From))
|
||||||
{
|
//{
|
||||||
return _d_class_cast!To(o);
|
// return _d_class_cast!To(o);
|
||||||
}
|
//}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
4
wasm/runtime/core/stdc/math.d
Normal file
4
wasm/runtime/core/stdc/math.d
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module core.stdc.math;
|
||||||
|
|
||||||
|
public import wasm : pow, cos, acos;
|
||||||
|
|
||||||
@ -1,10 +1,13 @@
|
|||||||
module core.math;
|
module core.math;
|
||||||
|
|
||||||
pragma(LDC_intrinsic, "llvm.sqrt.f32")
|
pragma(LDC_intrinsic, "llvm.sqrt.f#") T
|
||||||
float llvm_sqrt(float x) pure nothrow @nogc @safe;
|
llvm_sqrt(T)(T x) pure nothrow @nogc @safe;
|
||||||
|
|
||||||
pragma(LDC_intrinsic, "llvm.sqrt.f64")
|
|
||||||
double llvm_sqrt(double x) pure nothrow @nogc @safe;
|
|
||||||
|
|
||||||
float sqrt(float x ) => x < 0.0f ? float.nan : llvm_sqrt(x);
|
float sqrt(float x ) => x < 0.0f ? float.nan : llvm_sqrt(x);
|
||||||
double sqrt(double x) => x < 0.0f ? double.nan : llvm_sqrt(x);
|
double sqrt(double x) => x < 0.0f ? double.nan : llvm_sqrt(x);
|
||||||
|
|
||||||
|
pragma(LDC_intrinsic, "llvm.fabs.f#") T
|
||||||
|
llvm_fabs(T)(T x) pure nothrow @nogc @safe;
|
||||||
|
|
||||||
|
float fabs(float x) => llvm_fabs(x);
|
||||||
|
|
||||||
|
|||||||
@ -12,10 +12,18 @@ alias AliasSeq(TList...) = TList;
|
|||||||
// import core.arsd.memory_allocation;
|
// import core.arsd.memory_allocation;
|
||||||
|
|
||||||
import core.stdc.string;
|
import core.stdc.string;
|
||||||
import dlib.externdecl;
|
import dlib.alloc;
|
||||||
|
import dlib.util;
|
||||||
|
|
||||||
import wasm;
|
import wasm;
|
||||||
|
|
||||||
|
export extern(C) void
|
||||||
|
__assert(const(char)* msg, const(char)* file, uint line)
|
||||||
|
{
|
||||||
|
|
||||||
|
Abortf("%s(%s): Assertion failure %s", Str(file), line, Str(msg));
|
||||||
|
}
|
||||||
|
|
||||||
version(CarelessAlocation)
|
version(CarelessAlocation)
|
||||||
{
|
{
|
||||||
version = inline_concat;
|
version = inline_concat;
|
||||||
@ -42,96 +50,96 @@ int __cmp(C1, C2)(C1 lhs, C2 rhs)
|
|||||||
if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) &&
|
if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) &&
|
||||||
(is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D"))))
|
(is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D"))))
|
||||||
{
|
{
|
||||||
static if (is(C1 == typeof(null)) && is(C2 == typeof(null)))
|
static if (is(C1 == typeof(null)) && is(C2 == typeof(null)))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else static if (is(C1 == typeof(null)))
|
else static if (is(C1 == typeof(null)))
|
||||||
{
|
{
|
||||||
// Regard null references as always being "less than"
|
// Regard null references as always being "less than"
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else static if (is(C2 == typeof(null)))
|
else static if (is(C2 == typeof(null)))
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (lhs is rhs)
|
if (lhs is rhs)
|
||||||
return 0;
|
return 0;
|
||||||
if (lhs is null)
|
if (lhs is null)
|
||||||
return -1;
|
return -1;
|
||||||
if (rhs is null)
|
if (rhs is null)
|
||||||
return 1;
|
return 1;
|
||||||
return lhs.opCmp(rhs);
|
return lhs.opCmp(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TypeInfo_AssociativeArray : TypeInfo
|
class TypeInfo_AssociativeArray : TypeInfo
|
||||||
{
|
{
|
||||||
override string toString() const
|
override string toString() const
|
||||||
{
|
{
|
||||||
return MakeString(value.toString(), "[", key.toString(), "]");
|
return MakeString(value.toString(), "[", key.toString(), "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
override bool opEquals(Object o)
|
override bool opEquals(Object o)
|
||||||
{
|
{
|
||||||
if (this is o)
|
if (this is o)
|
||||||
return true;
|
return true;
|
||||||
auto c = cast(const TypeInfo_AssociativeArray)o;
|
auto c = cast(const TypeInfo_AssociativeArray)o;
|
||||||
return c && this.key == c.key &&
|
return c && this.key == c.key &&
|
||||||
this.value == c.value;
|
this.value == c.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
override bool equals(in void* p1, in void* p2) @trusted const
|
override bool equals(in void* p1, in void* p2) @trusted const
|
||||||
{
|
{
|
||||||
return xopEquals(p1, p2);
|
return xopEquals(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
override hash_t getHash(scope const void* p) nothrow @trusted const
|
override hash_t getHash(scope const void* p) nothrow @trusted const
|
||||||
{
|
{
|
||||||
return xtoHash(p);
|
return xtoHash(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUG: need to add the rest of the functions
|
// BUG: need to add the rest of the functions
|
||||||
|
|
||||||
override @property size_t tsize() nothrow pure const
|
override @property size_t tsize() nothrow pure const
|
||||||
{
|
{
|
||||||
return (char[int]).sizeof;
|
return (char[int]).sizeof;
|
||||||
}
|
}
|
||||||
|
|
||||||
override const(void)[] initializer() const @trusted
|
override const(void)[] initializer() const @trusted
|
||||||
{
|
{
|
||||||
return (cast(void *)null)[0 .. (char[int]).sizeof];
|
return (cast(void *)null)[0 .. (char[int]).sizeof];
|
||||||
}
|
}
|
||||||
|
|
||||||
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
||||||
override @property uint flags() nothrow pure const { return 1; }
|
override @property uint flags() nothrow pure const { return 1; }
|
||||||
|
|
||||||
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
|
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
|
||||||
// private static import core.internal.newaa;
|
// private static import core.internal.newaa;
|
||||||
// alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
|
// alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
|
||||||
|
|
||||||
TypeInfo value;
|
TypeInfo value;
|
||||||
TypeInfo key;
|
TypeInfo key;
|
||||||
TypeInfo entry;
|
TypeInfo entry;
|
||||||
|
|
||||||
bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
|
bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
|
||||||
hash_t function(scope const void*) nothrow @safe xtoHash;
|
hash_t function(scope const void*) nothrow @safe xtoHash;
|
||||||
|
|
||||||
alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
|
alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
|
||||||
alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
|
alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
|
||||||
|
|
||||||
override @property size_t talign() nothrow pure const
|
override @property size_t talign() nothrow pure const
|
||||||
{
|
{
|
||||||
return (char[int]).alignof;
|
return (char[int]).alignof;
|
||||||
}
|
}
|
||||||
|
|
||||||
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
||||||
{
|
{
|
||||||
arg1 = typeid(void*);
|
//arg1 = typeid(void*);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template RTInfoImpl(size_t[] pointerBitmap)
|
template RTInfoImpl(size_t[] pointerBitmap)
|
||||||
@ -168,11 +176,12 @@ AllocPtr(size_t size) @trusted nothrow @nogc
|
|||||||
|
|
||||||
template _arrayOp(Args...)
|
template _arrayOp(Args...)
|
||||||
{
|
{
|
||||||
import core.internal.array.operations;
|
import core.internal.array.operations;
|
||||||
alias _arrayOp = arrayOp!Args;
|
alias _arrayOp = arrayOp!Args;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz) {
|
extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz)
|
||||||
|
{
|
||||||
auto d = cast(ubyte*) dst;
|
auto d = cast(ubyte*) dst;
|
||||||
auto s = cast(ubyte*) src;
|
auto s = cast(ubyte*) src;
|
||||||
auto len = dstlen * elemsz;
|
auto len = dstlen * elemsz;
|
||||||
@ -183,10 +192,10 @@ extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t s
|
|||||||
s++;
|
s++;
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reserve(T)(ref T[] arr, size_t length) @trusted {
|
void reserve(T)(ref T[] arr, size_t length) @trusted
|
||||||
|
{
|
||||||
arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0];
|
arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,37 +203,37 @@ void reserve(T)(ref T[] arr, size_t length) @trusted {
|
|||||||
|
|
||||||
extern(C) void _d_arraybounds(string file, size_t line)
|
extern(C) void _d_arraybounds(string file, size_t line)
|
||||||
{
|
{
|
||||||
Abort("Range Error");
|
Abortf("%s(%s): Range Error", file, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Called when an out of range slice of an array is created
|
/// Called when an out of range slice of an array is created
|
||||||
extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length)
|
extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length)
|
||||||
{
|
{
|
||||||
Abort("Range error on slice creation");
|
Abortf("%s(%s): Range error on slice creation %s is out of range on [%s .. %s]", file, line, length, lwr, upr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when an out of range array index is accessed
|
/// Called when an out of range array index is accessed
|
||||||
extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
|
extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
|
||||||
{
|
{
|
||||||
Abort("Range error on indexing array");
|
Abortf("%s(%s): Range error on indexing array %s is out of range of %s", file, line, index, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
extern(C) void _d_assert(string file, uint line) @trusted @nogc
|
extern(C) void _d_assert(string file, uint line) @trusted @nogc
|
||||||
{
|
{
|
||||||
Abort("Assertion failure");
|
Abortf("%s(%s): Assertion failure", file, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _d_assertp(immutable(char)* file, uint line)
|
void _d_assertp(immutable(char)* file, uint line)
|
||||||
{
|
{
|
||||||
Abort("Assertion failure");
|
Abortf("%s(%s): Assertion failure", Str(file), line);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc
|
extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc
|
||||||
{
|
{
|
||||||
Abort("Assertion failure with msg");
|
Abortf("%s(%s): Assertion failure - %s", file, line, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __switch_error(string file, size_t line) @trusted @nogc
|
void __switch_error(string file, size_t line) @trusted @nogc
|
||||||
@ -232,7 +241,8 @@ void __switch_error(string file, size_t line) @trusted @nogc
|
|||||||
_d_assert_msg("final switch error", file, line);
|
_d_assert_msg("final switch error", file, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) {
|
bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
|
||||||
|
{
|
||||||
if (lhs.length != rhs.length)
|
if (lhs.length != rhs.length)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -252,7 +262,8 @@ bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) {
|
|||||||
// bare basics class support {
|
// bare basics class support {
|
||||||
|
|
||||||
|
|
||||||
extern(C) Object _d_allocclass(TypeInfo_Class ti) {
|
extern(C) Object _d_allocclass(TypeInfo_Class ti)
|
||||||
|
{
|
||||||
auto ptr = (cast(byte*)malloc(ti.m_init.length))[0 .. ti.m_init.length];
|
auto ptr = (cast(byte*)malloc(ti.m_init.length))[0 .. ti.m_init.length];
|
||||||
ptr[0 .. $] = ti.m_init[0 .. $];
|
ptr[0 .. $] = ti.m_init[0 .. $];
|
||||||
return cast(Object) ptr.ptr;
|
return cast(Object) ptr.ptr;
|
||||||
@ -1962,11 +1973,13 @@ extern(C) void[] _d_newarrayiT(const TypeInfo ti, size_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
extern (C) void* _d_newitemU(scope const TypeInfo _ti)
|
extern (C) void* _d_newitemU(scope const TypeInfo _ti)
|
||||||
{
|
{
|
||||||
auto ti = unqualify(_ti);
|
auto ti = unqualify(_ti);
|
||||||
return malloc(ti.tsize);
|
return malloc(ti.tsize);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
extern (C) T* _d_newitemT(T)() @trusted
|
extern (C) T* _d_newitemT(T)() @trusted
|
||||||
@ -1979,6 +1992,7 @@ extern (C) T* _d_newitemT(T)() @trusted
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/// Same as above, for item with non-zero initializer.
|
/// Same as above, for item with non-zero initializer.
|
||||||
extern (C) void* _d_newitemiT(TypeInfo ti)
|
extern (C) void* _d_newitemiT(TypeInfo ti)
|
||||||
{
|
{
|
||||||
@ -1987,8 +2001,7 @@ extern (C) void* _d_newitemiT(TypeInfo ti)
|
|||||||
memcpy(p, initializer.ptr, initializer.length);
|
memcpy(p, initializer.ptr, initializer.length);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
|
private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
|
||||||
{
|
{
|
||||||
@ -2133,6 +2146,7 @@ extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
|
|||||||
return p[0 .. x.length + y.length];
|
return p[0 .. x.length + y.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
|
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
|
||||||
{
|
{
|
||||||
// c could encode into from 1 to 4 characters
|
// c could encode into from 1 to 4 characters
|
||||||
@ -2174,6 +2188,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
|
|||||||
//
|
//
|
||||||
return _d_arrayappendT(typeid(shared char[]), x, appendthis);
|
return _d_arrayappendT(typeid(shared char[]), x, appendthis);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -3103,26 +3118,28 @@ class Exception : Throwable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
||||||
{
|
{
|
||||||
TypeInfo ti = cast() cti;
|
TypeInfo ti = cast() cti;
|
||||||
while (ti)
|
while (ti)
|
||||||
{
|
{
|
||||||
// avoid dynamic type casts
|
// avoid dynamic type casts
|
||||||
auto tti = typeid(ti);
|
auto tti = typeid(ti);
|
||||||
if (tti is typeid(TypeInfo_Const))
|
if (tti is typeid(TypeInfo_Const))
|
||||||
ti = (cast(TypeInfo_Const)cast(void*)ti).base;
|
ti = (cast(TypeInfo_Const)cast(void*)ti).base;
|
||||||
else if (tti is typeid(TypeInfo_Invariant))
|
else if (tti is typeid(TypeInfo_Invariant))
|
||||||
ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
|
ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
|
||||||
else if (tti is typeid(TypeInfo_Shared))
|
else if (tti is typeid(TypeInfo_Shared))
|
||||||
ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
|
ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
|
||||||
else if (tti is typeid(TypeInfo_Inout))
|
else if (tti is typeid(TypeInfo_Inout))
|
||||||
ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
|
ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ti;
|
return ti;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
private void _doPostblit(T)(T[] arr)
|
private void _doPostblit(T)(T[] arr)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import std.traits;
|
|||||||
import wasm;
|
import wasm;
|
||||||
|
|
||||||
char[]
|
char[]
|
||||||
sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args args)
|
sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args args) @nogc
|
||||||
{
|
{
|
||||||
SprintfType type = SprintfType.None;
|
SprintfType type = SprintfType.None;
|
||||||
static foreach(i; 0 .. Args.length)
|
static foreach(i; 0 .. Args.length)
|
||||||
@ -52,10 +52,18 @@ sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args ar
|
|||||||
else static if(is(Args[i] == double)) type = F64;
|
else static if(is(Args[i] == double)) type = F64;
|
||||||
else static if(is(Args[i] == bool)) type = Bool;
|
else static if(is(Args[i] == bool)) type = Bool;
|
||||||
else static if(is(Args[i] == char)) type = Char;
|
else static if(is(Args[i] == char)) type = Char;
|
||||||
|
else static if(isPointer!(Args[i])) type = Pointer;
|
||||||
else static assert(false, "Type unsupported");
|
else static assert(false, "Type unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
SprintfLoadValue(&args[i], type);
|
static if(isPointer!(Args[i]))
|
||||||
|
{
|
||||||
|
SprintfLoadValue(args[i], type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SprintfLoadValue(&args[i], type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +73,7 @@ sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args ar
|
|||||||
}
|
}
|
||||||
|
|
||||||
char[]
|
char[]
|
||||||
sformat(alias fmt, Args...)(char[] buf, Args args) if(isSomeString!(typeof(fmt)))
|
sformat(alias fmt, Args...)(char[] buf, Args args) @nogc if(isSomeString!(typeof(fmt)))
|
||||||
{
|
{
|
||||||
return .sformat(buf, fmt, args);
|
return .sformat(buf, fmt, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,21 @@ module std.math.rounding;
|
|||||||
|
|
||||||
pragma(LDC_intrinsic, "llvm.round.f#")
|
pragma(LDC_intrinsic, "llvm.round.f#")
|
||||||
T llvm_round(T)(T val)
|
T llvm_round(T)(T val)
|
||||||
if (__traits(isFloating, T));
|
if(__traits(isFloating, T));
|
||||||
|
|
||||||
float round(float x) => llvm_round(x);
|
float round(float x) => llvm_round(x);
|
||||||
double round(double x) => llvm_round(x);
|
double round(double x) => llvm_round(x);
|
||||||
|
|
||||||
|
pragma(LDC_intrinsic, "llvm.floor.f#")
|
||||||
|
T llvm_floor(T)(T val)
|
||||||
|
if(__traits(isFloating, T));
|
||||||
|
|
||||||
|
float floor(float x) => llvm_floor(x);
|
||||||
|
double floor(double x) => llvm_floor(x);
|
||||||
|
|
||||||
|
pragma(LDC_intrisic, "llvm.ceil.f#")
|
||||||
|
T llvm_ceil(T)(T val)
|
||||||
|
if(__traits(isFloating, T));
|
||||||
|
|
||||||
|
float ceil(float x) => llvm_ceil(x);
|
||||||
|
double ceil(double x) => llvm_ceil(x);
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
module std.traits;
|
module std.traits;
|
||||||
|
|
||||||
|
public import core.internal.traits;
|
||||||
|
|
||||||
enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
|
enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
|
||||||
|
|
||||||
template isIntegral(T)
|
template isIntegral(T)
|
||||||
@ -82,3 +84,11 @@ template isAggregateType(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar));
|
enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar));
|
||||||
|
|
||||||
|
template Select(bool condition, T...)
|
||||||
|
if (T.length == 2)
|
||||||
|
{
|
||||||
|
import std.meta : Alias;
|
||||||
|
alias Select = Alias!(T[!condition]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,9 @@ module wasm;
|
|||||||
|
|
||||||
import ldc.attributes;
|
import ldc.attributes;
|
||||||
|
|
||||||
|
import dlib.util;
|
||||||
|
import std.format;
|
||||||
|
|
||||||
enum SprintfType : size_t
|
enum SprintfType : size_t
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@ -30,9 +33,12 @@ enum SprintfType : size_t
|
|||||||
F64Array,
|
F64Array,
|
||||||
Char,
|
Char,
|
||||||
CharArray,
|
CharArray,
|
||||||
|
Pointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern extern(C) @nogc @llvmAttr("wasm-import-module", "env"):
|
|
||||||
|
|
||||||
|
extern extern(C) @llvmAttr("wasm-import-module", "env"):
|
||||||
|
|
||||||
@llvmAttr("wasm-import-name", "Console") void
|
@llvmAttr("wasm-import-name", "Console") void
|
||||||
Console(string str, bool write_line);
|
Console(string str, bool write_line);
|
||||||
@ -41,39 +47,60 @@ Console(string str, bool write_line);
|
|||||||
Console2(size_t length, const(void)* ptr, bool write_line);
|
Console2(size_t length, const(void)* ptr, bool write_line);
|
||||||
|
|
||||||
@llvmAttr("wasm-import-name", "Abort") void
|
@llvmAttr("wasm-import-name", "Abort") void
|
||||||
Abort(string message);
|
Abort(string message) @nogc;
|
||||||
|
|
||||||
@llvmAttr("wasm-import-name", "SprintfLoadValue") void
|
@llvmAttr("wasm-import-name", "SprintfLoadValue") void
|
||||||
SprintfLoadValue(const(void)* ptr, SprintfType type);
|
SprintfLoadValue(const(void)* ptr, SprintfType type) @nogc;
|
||||||
|
|
||||||
@llvmAttr("wasm-import-name", "SprintfLoadArray") void
|
@llvmAttr("wasm-import-name", "SprintfLoadArray") void
|
||||||
SprintfLoadArray(size_t length, const(void)* ptr, SprintfType type);
|
SprintfLoadArray(size_t length, const(void)* ptr, SprintfType type) @nogc;
|
||||||
|
|
||||||
@llvmAttr("wasm-import-name", "SprintfEnd") size_t
|
@llvmAttr("wasm-import-name", "SprintfEnd") size_t
|
||||||
SprintfEnd(char[] buffer, string format);
|
SprintfEnd(char[] buffer, string format) @nogc;
|
||||||
|
|
||||||
|
@llvmAttr("wasm-import-name", "pow") double
|
||||||
|
pow(double base, double exponent) @nogc;
|
||||||
|
|
||||||
|
@llvmAttr("wasm-import-name", "cos") double
|
||||||
|
cos(double x) @nogc;
|
||||||
|
|
||||||
|
@llvmAttr("wasm-import-name", "acos") double
|
||||||
|
acos(double x) @nogc;
|
||||||
|
|
||||||
|
void
|
||||||
|
Abortf(Args...)(string fmt, Args args) @nogc
|
||||||
|
{
|
||||||
|
char[1024] buffer;
|
||||||
|
|
||||||
|
string abort_message = Str(sformat(buffer, fmt, args));
|
||||||
|
|
||||||
|
Abort(abort_message);
|
||||||
|
}
|
||||||
|
|
||||||
export void
|
export void
|
||||||
_start()
|
_start()
|
||||||
{
|
{
|
||||||
import dlib.alloc;
|
import dlib.alloc;
|
||||||
import dlib.util;
|
import dlib.util;
|
||||||
|
import std.format;
|
||||||
|
|
||||||
|
MallocInit(256, 16, (void*).sizeof*2);
|
||||||
|
|
||||||
ResetScratch(MB(2));
|
ResetScratch(MB(2));
|
||||||
|
|
||||||
|
char[100] buffer;
|
||||||
|
uint[2] arr = [1, 2];
|
||||||
|
sformat(buffer, "%s", arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
version(DLIB_TEST)
|
version(DLIB_TEST) export void
|
||||||
{
|
|
||||||
|
|
||||||
export void
|
|
||||||
RunTests()
|
RunTests()
|
||||||
{
|
{
|
||||||
Console("azZ", true);
|
import dlib;
|
||||||
static foreach(test_fn; __traits(getUnitTests, __traits(parent, _start)))
|
|
||||||
{
|
DLibTestMath();
|
||||||
Console("Running test", true);
|
DLibTestUtil();
|
||||||
test_fn();
|
DLibTestAlloc();
|
||||||
}
|
|
||||||
Console("Tests succeeded", true);
|
Console("Tests succeeded", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
448
wasm/wasm.js
448
wasm/wasm.js
@ -27,6 +27,8 @@ const TYPES = {
|
|||||||
CharArray: 25,
|
CharArray: 25,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let LoadSize = null;
|
||||||
|
|
||||||
function assert(condition, message)
|
function assert(condition, message)
|
||||||
{
|
{
|
||||||
if(!condition)
|
if(!condition)
|
||||||
@ -36,260 +38,272 @@ function assert(condition, message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WasmManager
|
const wasm_manager = {
|
||||||
|
memory: null,
|
||||||
|
data_view: null,
|
||||||
|
stdout_string: "",
|
||||||
|
sprintf_values: [],
|
||||||
|
ptr_size: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
function InitWasmManager(memory, ptr_size = 4)
|
||||||
{
|
{
|
||||||
constructor(memory, ptr_size = 4)
|
wasm_manager.ptr_size = ptr_size;
|
||||||
|
|
||||||
|
wasm_manager.sprintf_values = [];
|
||||||
|
|
||||||
|
wasm_manager.stdout_string = "";
|
||||||
|
|
||||||
|
switch(wasm_manager.ptr_size)
|
||||||
{
|
{
|
||||||
this.ptr_size = ptr_size;
|
case 8: LoadSize = LoadU64; break;
|
||||||
|
case 4:
|
||||||
|
default: LoadSize = LoadU32; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.sprintf_values = [];
|
function SetMemory(memory)
|
||||||
|
{
|
||||||
|
wasm_manager.memory = memory;
|
||||||
|
wasm_manager.data_view = new DataView(wasm_manager.memory.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
this.stdout_string = "";
|
function Memory()
|
||||||
|
{
|
||||||
|
return new DataView(wasm_manager.memory.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
switch(this.ptr_size)
|
function LoadBool(addr) { return Boolean(Memory().getUint8(addr, true)); }
|
||||||
|
function LoadF32(addr) { return Memory().getFloat32(addr, true); }
|
||||||
|
function LoadF64(addr) { return Memory().getFloat64(addr, true); }
|
||||||
|
function LoadU8 (addr) { return Memory().getUint8 (addr, true); }
|
||||||
|
function LoadI8 (addr) { return Memory().getInt8 (addr, true); }
|
||||||
|
function LoadU16(addr) { return Memory().getUint16 (addr, true); }
|
||||||
|
function LoadI16(addr) { return Memory().getInt16 (addr, true); }
|
||||||
|
function LoadU32(addr) { return Memory().getUint32 (addr, true); }
|
||||||
|
function LoadI32(addr) { return Memory().getInt32 (addr, true); }
|
||||||
|
|
||||||
|
function LoadU64(addr)
|
||||||
|
{
|
||||||
|
const lo = Memory().getUint32(addr+0, true);
|
||||||
|
const hi = Memory().getUint32(addr+4, true);
|
||||||
|
return lo + hi*4294967296;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadI64(addr)
|
||||||
|
{
|
||||||
|
const lo = Memory().getUint32(addr+0, true);
|
||||||
|
const hi = Memory().getInt32 (addr+4, true);
|
||||||
|
return lo + hi*4294967296;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadArray(array_class, length, addr)
|
||||||
|
{
|
||||||
|
return new array_class(wasm_manager.memory.buffer, addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadString(length, addr)
|
||||||
|
{
|
||||||
|
return new TextDecoder().decode(LoadArray(Uint8Array, length, addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadCString(ptr)
|
||||||
|
{
|
||||||
|
if(!ptr) return null;
|
||||||
|
|
||||||
|
let length = 0;
|
||||||
|
for(; LoadU8(ptr+length); length += 1) {}
|
||||||
|
|
||||||
|
return LoadString(ptr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StoreString(ptr, value)
|
||||||
|
{
|
||||||
|
const src = new TextEncoder().encode(value);
|
||||||
|
const dst = new Uint8Array(wasm_manager.memory.buffer, ptr, src.length);
|
||||||
|
|
||||||
|
dst.set(src);
|
||||||
|
|
||||||
|
return src.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imports = {
|
||||||
|
env: {
|
||||||
|
Abort(string_length, string_ptr)
|
||||||
{
|
{
|
||||||
case 8: this.LoadSize = this.LoadU64; break;
|
console.error(LoadString(string_length, string_ptr));
|
||||||
case 4:
|
},
|
||||||
default: this.LoadSize = this.LoadU32; break;
|
SprintfLoadValue(ptr, type)
|
||||||
}
|
{
|
||||||
}
|
let value = null;
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case TYPES.U8: value = LoadU8(ptr); break;
|
||||||
|
case TYPES.I8: value = LoadI8(ptr); break;
|
||||||
|
case TYPES.U16: value = LoadU16(ptr); break;
|
||||||
|
case TYPES.I16: value = LoadI16(ptr); break;
|
||||||
|
case TYPES.U32: value = LoadU32(ptr); break;
|
||||||
|
case TYPES.I32: value = LoadI32(ptr); break;
|
||||||
|
case TYPES.U64: value = LoadU64(ptr); break;
|
||||||
|
case TYPES.I64: value = LoadI64(ptr); break;
|
||||||
|
case TYPES.SizeT: value = LoadSize(ptr); break;
|
||||||
|
case TYPES.F32: value = LoadF32(ptr); break;
|
||||||
|
case TYPES.F64: value = LoadF64(ptr); break;
|
||||||
|
case TYPES.Bool: value = LoadBool(ptr); break;
|
||||||
|
case TYPES.Char: value = LoadString(1, ptr); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
SetMemory(memory)
|
if(value != null)
|
||||||
{
|
{
|
||||||
this.memory = memory;
|
if(type === TYPES.Bool)
|
||||||
this.data_view = new DataView(this.memory.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadBool = (addr) => Boolean(this.data_view.getUint8(addr, true));
|
|
||||||
|
|
||||||
LoadF32 = (addr) => this.data_view.getFloat32(addr, true);
|
|
||||||
LoadF64 = (addr) => this.data_view.getFloat64(addr, true);
|
|
||||||
|
|
||||||
LoadU8 = (addr) => this.data_view.getUint8 (addr, true);
|
|
||||||
LoadI8 = (addr) => this.data_view.getInt8 (addr, true);
|
|
||||||
LoadU16 = (addr) => this.data_view.getUint16 (addr, true);
|
|
||||||
LoadI16 = (addr) => this.data_view.getInt16 (addr, true);
|
|
||||||
LoadU32 = (addr) => this.data_view.getUint32 (addr, true);
|
|
||||||
LoadI32 = (addr) => this.data_view.getInt32 (addr, true);
|
|
||||||
|
|
||||||
LoadU64(addr)
|
|
||||||
{
|
|
||||||
const lo = this.data_view.getUint32(addr+0, true);
|
|
||||||
const hi = this.data_view.getUint32(addr+4, true);
|
|
||||||
return lo + hi*4294967296;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadI64(addr)
|
|
||||||
{
|
|
||||||
const lo = this.data_view.getUint32(addr+0, true);
|
|
||||||
const hi = this.data_view.getInt32 (addr+4, true);
|
|
||||||
return lo + hi*4294967296;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadArray = (array_class, length, addr) => new array_class(this.memory.buffer, addr, length);
|
|
||||||
LoadString = (length, addr) => new TextDecoder().decode(this.LoadArray(Uint8Array, length, addr));
|
|
||||||
|
|
||||||
LoadCString(ptr)
|
|
||||||
{
|
|
||||||
if(!ptr) return null;
|
|
||||||
|
|
||||||
let length = 0;
|
|
||||||
for(; this.LoadU8(ptr+length); length += 1) {}
|
|
||||||
|
|
||||||
return this.LoadString(ptr, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
StoreString(ptr, value)
|
|
||||||
{
|
|
||||||
const src = new TextEncoder().encode(value);
|
|
||||||
const dst = new Uint8Array(this.memory.buffer, ptr, src.length);
|
|
||||||
|
|
||||||
dst.set(src);
|
|
||||||
|
|
||||||
return src.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Imports()
|
|
||||||
{
|
|
||||||
const manager = this;
|
|
||||||
return {
|
|
||||||
env: {
|
|
||||||
Abort(string_length, string_ptr)
|
|
||||||
{
|
{
|
||||||
console.error(manager.LoadString(string_length, string_ptr));
|
wasm_manager.sprintf_values.push(value ? 'true' : 'false');
|
||||||
},
|
}
|
||||||
SprintfLoadValue(ptr, type)
|
else
|
||||||
{
|
{
|
||||||
let value = null;
|
wasm_manager.sprintf_values.push(''+value);
|
||||||
switch(type)
|
}
|
||||||
{
|
}
|
||||||
case TYPES.U8: value = manager.LoadU8(ptr); break;
|
},
|
||||||
case TYPES.I8: value = manager.LoadI8(ptr); break;
|
SprintfLoadArray(length, ptr, type)
|
||||||
case TYPES.U16: value = manager.LoadU16(ptr); break;
|
{
|
||||||
case TYPES.I16: value = manager.LoadI16(ptr); break;
|
let value = null;
|
||||||
case TYPES.U32: value = manager.LoadU32(ptr); break;
|
switch(type)
|
||||||
case TYPES.I32: value = manager.LoadI32(ptr); break;
|
{
|
||||||
case TYPES.U64: value = manager.LoadU64(ptr); break;
|
case TYPES.CharArray:
|
||||||
case TYPES.I64: value = manager.LoadI64(ptr); break;
|
case TYPES.String: value = LoadString(length, ptr); break;
|
||||||
case TYPES.SizeT: value = manager.LoadSize(ptr); break;
|
case TYPES.U8Array: value = LoadArray(Uint8Array, length, ptr); break;
|
||||||
case TYPES.F32: value = manager.LoadF32(ptr); break;
|
case TYPES.I8Array: value = LoadArray(Int8Array, length, ptr); break;
|
||||||
case TYPES.F64: value = manager.LoadF64(ptr); break;
|
case TYPES.U16Array: value = LoadArray(Uint16Array, length, ptr); break;
|
||||||
case TYPES.Bool: value = manager.LoadBool(ptr); break;
|
case TYPES.I16Array: value = LoadArray(Int16Array, length, ptr); break;
|
||||||
case TYPES.Char: value = manager.LoadString(1, ptr); break;
|
case TYPES.U32Array: value = LoadArray(Uint32Array, length, ptr); break;
|
||||||
default: break;
|
case TYPES.I32Array: value = LoadArray(Int32Array, length, ptr); break;
|
||||||
}
|
case TYPES.U64Array: value = LoadArray(BigUint64Array, length, ptr); break;
|
||||||
|
case TYPES.I64Array: value = LoadArray(BigInt64Array, length, ptr); break;
|
||||||
|
case TYPES.F32Array: value = LoadArray(Float32Array, length, ptr); break;
|
||||||
|
case TYPES.F64Array: value = LoadArray(Float64Array, length, ptr); break;
|
||||||
|
}
|
||||||
|
|
||||||
if(value)
|
if(value)
|
||||||
{
|
{
|
||||||
if(type === TYPES.Bool)
|
if(type !== TYPES.String && type !== TYPES.CharArray)
|
||||||
{
|
|
||||||
manager.sprintf_values.push(value ? 'true' : 'false');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
manager.sprintf_values.push(''+value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SprintfLoadArray(length, ptr, type)
|
|
||||||
{
|
{
|
||||||
let value = null;
|
value = `[${value.join(", ")}]`;
|
||||||
switch(type)
|
}
|
||||||
{
|
|
||||||
case TYPES.CharArray:
|
|
||||||
case TYPES.String: value = manager.LoadString(length, ptr); break;
|
|
||||||
case TYPES.U8Array: value = manager.LoadArray(Uint8Array, length, ptr); break;
|
|
||||||
case TYPES.I8Array: value = manager.LoadArray(Int8Array, length, ptr); break;
|
|
||||||
case TYPES.U16Array: value = manager.LoadArray(Uint16Array, length, ptr); break;
|
|
||||||
case TYPES.I16Array: value = manager.LoadArray(Int16Array, length, ptr); break;
|
|
||||||
case TYPES.U32Array: value = manager.LoadArray(Uint32Array, length, ptr); break;
|
|
||||||
case TYPES.I32Array: value = manager.LoadArray(Int32Array, length, ptr); break;
|
|
||||||
case TYPES.U64Array: value = manager.LoadArray(BigUint64Array, length, ptr); break;
|
|
||||||
case TYPES.I64Array: value = manager.LoadArray(BigInt64Array, length, ptr); break;
|
|
||||||
case TYPES.F32Array: value = manager.LoadArray(Float32Array, length, ptr); break;
|
|
||||||
case TYPES.F64Array: value = manager.LoadArray(Float64Array, length, ptr); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(value)
|
wasm_manager.sprintf_values.push(value);
|
||||||
{
|
}
|
||||||
if(type !== TYPES.String && type !== TYPES.CharArray)
|
},
|
||||||
{
|
SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr)
|
||||||
value = `[${value.join(", ")}]`;
|
{
|
||||||
}
|
let format_string = LoadString(string_length, string_ptr);
|
||||||
|
|
||||||
manager.sprintf_values.push(value);
|
let index = format_string.indexOf("%");
|
||||||
}
|
|
||||||
},
|
let result = format_string.substring(0, index);
|
||||||
SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr)
|
let format = format_string.substring(index);
|
||||||
|
|
||||||
|
function Next()
|
||||||
|
{
|
||||||
|
index = format.indexOf("%");
|
||||||
|
result += format.substring(0, index);
|
||||||
|
format = format.substring(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value_index = 0;
|
||||||
|
for(; value_index < wasm_manager.sprintf_values.length;)
|
||||||
|
{
|
||||||
|
assert(format.length);
|
||||||
|
|
||||||
|
const char = format.charAt(1);
|
||||||
|
assert(char !== " " && char.length, "Invalid format string " + format_string);
|
||||||
|
|
||||||
|
if(char === "%")
|
||||||
{
|
{
|
||||||
let format_string = manager.LoadString(string_length, string_ptr);
|
result += "%";
|
||||||
|
format = format.substring(2);
|
||||||
let index = format_string.indexOf("%");
|
Next();
|
||||||
|
continue;
|
||||||
let result = format_string.substring(0, index);
|
}
|
||||||
let format = format_string.substring(index);
|
else if(char >= '0' && char <= '9')
|
||||||
|
{
|
||||||
function Next()
|
let digit_index = 2;
|
||||||
|
for(;;)
|
||||||
{
|
{
|
||||||
index = format.indexOf("%");
|
let next_char = format.charAt(digit_index++);
|
||||||
result += format.substring(0, index);
|
|
||||||
format = format.substring(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_index = 0;
|
assert(next_char !== " " && next_char.length, "Invalid format string " + format_string);
|
||||||
for(; value_index < manager.sprintf_values.length;)
|
|
||||||
{
|
|
||||||
assert(format.length);
|
|
||||||
|
|
||||||
const char = format.charAt(1);
|
if(next_char < '0' || next_char > '9')
|
||||||
assert(char !== " " && char.length, "Invalid format string " + format_string);
|
|
||||||
|
|
||||||
if(char === "%")
|
|
||||||
{
|
{
|
||||||
result += "%";
|
|
||||||
format = format.substring(2);
|
|
||||||
Next();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if(char >= '0' && char <= '9')
|
|
||||||
{
|
|
||||||
let digit_index = 2;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
let next_char = format.charAt(digit_index++);
|
|
||||||
|
|
||||||
assert(next_char !== " " && next_char.length, "Invalid format string " + format_string);
|
result += wasm_manager.sprintf_values[value_index++];
|
||||||
|
format = format.substring(digit_index);
|
||||||
|
Next();
|
||||||
|
|
||||||
if(next_char < '0' || next_char > '9')
|
break;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += manager.sprintf_values[value_index++];
|
|
||||||
format = format.substring(digit_index);
|
|
||||||
Next();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z'))
|
|
||||||
{
|
|
||||||
result += manager.sprintf_values[value_index++];
|
|
||||||
format = format.substring(2);
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
else assert(false, "Invalid format string " + format_string);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result += format;
|
else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z'))
|
||||||
|
|
||||||
const bytes_written = manager.StoreString(buffer_ptr, result);
|
|
||||||
assert(bytes_written <= buffer_length, "Format string is longer than buffer length");
|
|
||||||
|
|
||||||
manager.sprintf_values = [];
|
|
||||||
|
|
||||||
return bytes_written;
|
|
||||||
},
|
|
||||||
Console(string_length, string_ptr, write_line)
|
|
||||||
{
|
{
|
||||||
manager.stdout_string += manager.LoadString(string_length, string_ptr);
|
result += wasm_manager.sprintf_values[value_index++];
|
||||||
const arr = manager.LoadArray(Uint8Array, string_length, string_ptr);
|
format = format.substring(2);
|
||||||
|
Next();
|
||||||
|
}
|
||||||
|
else assert(false, "Invalid format string " + format_string);
|
||||||
|
}
|
||||||
|
|
||||||
if(write_line || manager.stdout_string.includes("\n"))
|
result += format;
|
||||||
{
|
|
||||||
console.log(manager.stdout_string);
|
|
||||||
manager.stdout_string = "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Console2(string_length, string_ptr, write_line)
|
|
||||||
{
|
|
||||||
manager.stdout_string += manager.LoadString(string_length, string_ptr);
|
|
||||||
const arr = manager.LoadArray(Uint8Array, string_length, string_ptr);
|
|
||||||
|
|
||||||
if(write_line || manager.stdout_string.includes("\n"))
|
const bytes_written = StoreString(buffer_ptr, result);
|
||||||
{
|
assert(bytes_written <= buffer_length, "Format string is longer than buffer length");
|
||||||
console.log(manager.stdout_string);
|
|
||||||
manager.stdout_string = "";
|
wasm_manager.sprintf_values = [];
|
||||||
}
|
|
||||||
},
|
return bytes_written;
|
||||||
},
|
},
|
||||||
}
|
Console(string_length, string_ptr, write_line)
|
||||||
}
|
{
|
||||||
|
wasm_manager.stdout_string += LoadString(string_length, string_ptr);
|
||||||
|
const arr = LoadArray(Uint8Array, string_length, string_ptr);
|
||||||
|
|
||||||
|
if(write_line || wasm_manager.stdout_string.includes("\n"))
|
||||||
|
{
|
||||||
|
console.log(wasm_manager.stdout_string);
|
||||||
|
wasm_manager.stdout_string = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Console2(string_length, string_ptr, write_line)
|
||||||
|
{
|
||||||
|
wasm_manager.stdout_string += LoadString(string_length, string_ptr);
|
||||||
|
const arr = LoadArray(Uint8Array, string_length, string_ptr);
|
||||||
|
|
||||||
|
if(write_line || wasm_manager.stdout_string.includes("\n"))
|
||||||
|
{
|
||||||
|
console.log(wasm_manager.stdout_string);
|
||||||
|
wasm_manager.stdout_string = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pow: Math.pow,
|
||||||
|
cos: Math.cos,
|
||||||
|
acos: Math.acos,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async function StartWasm(path)
|
async function StartWasm(path)
|
||||||
{
|
{
|
||||||
const manager = new WasmManager();
|
|
||||||
|
|
||||||
const response = await fetch(path);
|
const response = await fetch(path);
|
||||||
const data = await response.arrayBuffer();
|
const data = await response.arrayBuffer();
|
||||||
const wasm = await WebAssembly.instantiate(data, manager.Imports());
|
const wasm = await WebAssembly.instantiate(data, imports);
|
||||||
|
|
||||||
const exports = wasm.instance.exports;
|
const exports = wasm.instance.exports;
|
||||||
|
|
||||||
if(exports.memory)
|
if(exports.memory)
|
||||||
{
|
{
|
||||||
manager.SetMemory(exports.memory);
|
SetMemory(exports.memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports?._start();
|
exports?._start();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user