diff --git a/.ignore b/.ignore
index 08aa99c..79fd401 100644
--- a/.ignore
+++ b/.ignore
@@ -1,2 +1,5 @@
.git
-external
+build
+test
+external/cglm
+external/xxhash
diff --git a/Test_Runner b/Test_Runner
new file mode 100755
index 0000000..12893f9
Binary files /dev/null and b/Test_Runner differ
diff --git a/aliases.d b/aliases.d
index 72ec3d2..b054ca0 100644
--- a/aliases.d
+++ b/aliases.d
@@ -1,9 +1,23 @@
module dlib.aliases;
-import core.memory;
-import std.stdint;
+// import core.memory;
+//import std.stdint;
import dlib.math;
+enum string NO_IMPL = "Not yet implemented.";
+
+public import std.traits;
+public import std.meta;
+
+version(WebAssembly)
+{
+ enum bool NativeTarget = false;
+}
+else
+{
+ enum bool NativeTarget = true;
+}
+
debug
{
const BUILD_DEBUG = true;
@@ -13,41 +27,41 @@ else
const BUILD_DEBUG = false;
}
-alias i8 = byte;
-alias i16 = short;
-alias i32 = int;
-alias i64 = long;
+alias i8 = byte;
+alias i16 = short;
+alias i32 = int;
+alias i64 = long;
-alias u8 = ubyte;
-alias u16 = ushort;
-alias u32 = uint;
-alias u64 = ulong;
+alias u8 = ubyte;
+alias u16 = ushort;
+alias u32 = uint;
+alias u64 = ulong;
-alias f32 = float;
-alias f64 = double;
+alias f32 = float;
+alias f64 = double;
-alias b32 = uint;
+alias b32 = uint;
-alias intptr = i64;
+alias intptr = i64;
alias uintptr = u64;
-alias usize = size_t;
+alias usize = size_t;
-alias Vec2 = Vector!(f32, 2);
-alias Vec3 = Vector!(f32, 3);
-alias Vec4 = Vector!(f32, 4);
+alias Vec2 = Vector!(f32, 2);
+alias Vec3 = Vector!(f32, 3);
+alias Vec4 = Vector!(f32, 4);
-alias DVec2 = Vector!(f64, 2);
-alias DVec3 = Vector!(f64, 3);
-alias DVec4 = Vector!(f64, 4);
+alias DVec2 = Vector!(f64, 2);
+alias DVec3 = Vector!(f64, 3);
+alias DVec4 = Vector!(f64, 4);
-alias IVec2 = Vector!(i32, 2);
-alias IVec3 = Vector!(i32, 3);
-alias IVec4 = Vector!(i32, 4);
+alias IVec2 = Vector!(i32, 2);
+alias IVec3 = Vector!(i32, 3);
+alias IVec4 = Vector!(i32, 4);
-alias I8Vec2 = Vector!(i8, 2);
-alias I8Vec3 = Vector!(i8, 3);
-alias I8Vec4 = Vector!(i8, 4);
+alias I8Vec2 = Vector!(i8, 2);
+alias I8Vec3 = Vector!(i8, 3);
+alias I8Vec4 = Vector!(i8, 4);
alias I16Vec2 = Vector!(i16, 2);
alias I16Vec3 = Vector!(i16, 3);
@@ -61,13 +75,13 @@ alias I64Vec2 = Vector!(i64, 2);
alias I64Vec3 = Vector!(i64, 3);
alias I64Vec4 = Vector!(i64, 4);
-alias UVec2 = Vector!(u32, 2);
-alias UVec3 = Vector!(u32, 3);
-alias UVec4 = Vector!(u32, 4);
+alias UVec2 = Vector!(u32, 2);
+alias UVec3 = Vector!(u32, 3);
+alias UVec4 = Vector!(u32, 4);
-alias U8Vec2 = Vector!(u8, 2);
-alias U8Vec3 = Vector!(u8, 3);
-alias U8Vec4 = Vector!(u8, 4);
+alias U8Vec2 = Vector!(u8, 2);
+alias U8Vec3 = Vector!(u8, 3);
+alias U8Vec4 = Vector!(u8, 4);
alias U16Vec2 = Vector!(u16, 2);
alias U16Vec3 = Vector!(u16, 3);
@@ -81,10 +95,345 @@ alias U64Vec2 = Vector!(u64, 2);
alias U64Vec3 = Vector!(u64, 3);
alias U64Vec4 = Vector!(u64, 4);
-alias Mat2 = Matrix!(f32, 2);
-alias Mat3 = Matrix!(f32, 3);
-alias Mat4 = Matrix!(f32, 4);
+alias Mat2 = Matrix!(f32, 2);
+alias Mat3 = Matrix!(f32, 3);
+alias Mat4 = Matrix!(f32, 4);
-alias DMat2 = Matrix!(f64, 2);
-alias DMat3 = Matrix!(f64, 3);
-alias DMat4 = Matrix!(f64, 4);
+alias DMat2 = Matrix!(f64, 2);
+alias DMat3 = Matrix!(f64, 3);
+alias DMat4 = Matrix!(f64, 4);
+
+/*
+
+enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T: real);
+enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) && is(T: real);
+
+template isIntegral(T)
+{
+ static if(!__traits(isIntegral, T))
+ {
+ enum isIntegral = false;
+ }
+ else static if(is(T U == enum))
+ {
+ enum isIntegral = isIntegral!U;
+ }
+ else
+ {
+ enum isIntegral = __traits(isZeroInit, T) && !is(immutable T == immutable bool) && !is(T == __vector);
+ }
+}
+
+template isNumeric(T)
+{
+ static if (!__traits(isArithmetic, T))
+ {
+ enum isNumeric = false;
+ }
+ else static if (__traits(isFloating, T))
+ {
+ enum isNumeric = is(T : real); // Not __vector, imaginary, or complex.
+ }
+ else static if (is(T U == enum))
+ {
+ enum isNumeric = isNumeric!U;
+ }
+ else
+ {
+ enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar.
+ && !is(immutable T == immutable bool) && !is(T == __vector);
+ }
+}
+
+enum RealFormat
+{
+ ieeeHalf,
+ ieeeSingle,
+ ieeeDouble,
+ ieeeExtended, // x87 80-bit real
+ ieeeExtended53, // x87 real rounded to precision of double.
+ ibmExtended, // IBM 128-bit extended
+ ieeeQuadruple,
+}
+
+template floatTraits(T)
+{
+ // EXPMASK is a ushort mask to select the exponent portion (without sign)
+ // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
+ // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
+ // EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
+ // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
+ // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
+ enum Unqual!T RECIP_EPSILON = (1/T.epsilon);
+ static if (T.mant_dig == 24)
+ {
+ // Single precision float
+ enum ushort EXPMASK = 0x7F80;
+ enum ushort EXPSHIFT = 7;
+ enum ushort EXPBIAS = 0x3F00;
+ enum uint EXPMASK_INT = 0x7F80_0000;
+ enum uint MANTISSAMASK_INT = 0x007F_FFFF;
+ enum realFormat = RealFormat.ieeeSingle;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 1;
+ enum SIGNPOS_BYTE = 3;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 53)
+ {
+ static if (T.sizeof == 8)
+ {
+ // Double precision float, or real == double
+ enum ushort EXPMASK = 0x7FF0;
+ enum ushort EXPSHIFT = 4;
+ enum ushort EXPBIAS = 0x3FE0;
+ enum uint EXPMASK_INT = 0x7FF0_0000;
+ enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only
+ enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF;
+ enum realFormat = RealFormat.ieeeDouble;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 3;
+ enum SIGNPOS_BYTE = 7;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.sizeof == 12)
+ {
+ // Intel extended real80 rounded to double
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended53;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+ }
+ else static if (T.mant_dig == 64)
+ {
+ // Intel extended real80
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 113)
+ {
+ // Quadruple precision float
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeQuadruple;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 7;
+ enum SIGNPOS_BYTE = 15;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 106)
+ {
+ // IBM Extended doubledouble
+ enum ushort EXPMASK = 0x7FF0;
+ enum ushort EXPSHIFT = 4;
+ enum realFormat = RealFormat.ibmExtended;
+
+ // For IBM doubledouble the larger magnitude double comes first.
+ // It's really a double[2] and arrays don't index differently
+ // between little and big-endian targets.
+ enum DOUBLEPAIR_MSB = 0;
+ enum DOUBLEPAIR_LSB = 1;
+
+ // The exponent/sign byte is for most significant part.
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 3;
+ enum SIGNPOS_BYTE = 7;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+}
+
+version (StdDdoc)
+{
+ template Unqual(T)
+ {
+ import core.internal.traits : CoreUnqual = Unqual;
+ alias Unqual = CoreUnqual!(T);
+ }
+}
+else
+{
+ import core.internal.traits : CoreUnqual = Unqual;
+ alias Unqual = CoreUnqual;
+}
+
+// These apply to all floating-point types
+version (LittleEndian)
+{
+ enum MANTISSA_LSB = 0;
+ enum MANTISSA_MSB = 1;
+}
+else
+{
+ enum MANTISSA_LSB = 1;
+ enum MANTISSA_MSB = 0;
+}
+
+
+bool isInfinity(X)(X x) @nogc @trusted pure nothrow
+if (isFloatingPoint!(X))
+{
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
+ == 0x7FF0_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]);
+ const ulong ps = *cast(ulong *)&x;
+
+ // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1.
+ return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const long psLsb = (cast(long *)&x)[MANTISSA_LSB];
+ const long psMsb = (cast(long *)&x)[MANTISSA_MSB];
+ return (psLsb == 0)
+ && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000;
+ }
+ else
+ {
+ return (x < -X.max) || (X.max < x);
+ }
+}
+
+bool isNaN(X)(X x) @nogc @trusted pure nothrow
+if (isFloatingPoint!(X))
+{
+ version (all)
+ {
+ return x != x;
+ }
+ else
+ {
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ const uint p = *cast(uint *)&x;
+ // Sign bit (MSB) is irrelevant so mask it out.
+ // Next 8 bits should be all set.
+ // At least one bit among the least significant 23 bits should be set.
+ return (p & 0x7FFF_FFFF) > 0x7F80_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ const ulong p = *cast(ulong *)&x;
+ // Sign bit (MSB) is irrelevant so mask it out.
+ // Next 11 bits should be all set.
+ // At least one bit among the least significant 52 bits should be set.
+ return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ const ulong ps = *cast(ulong *)&x;
+ return e == F.EXPMASK &&
+ ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB];
+ const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB];
+ return e == F.EXPMASK &&
+ (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
+ }
+ else
+ {
+ return x != x;
+ }
+ }
+}
+
+enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...);
+
+template isInstanceOf(alias S, alias T)
+{
+ enum impl(alias T : S!Args, Args...) = true;
+ enum impl(alias T) = false;
+ enum isInstanceOf = impl!T;
+}
+
+enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs);
+enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; });
+enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; });
+
+template isDynamicArray(T)
+{
+ static if (is(T == U[], U))
+ enum bool isDynamicArray = true;
+ else static if (is(T U == enum))
+ // BUG: isDynamicArray / isStaticArray considers enums
+ // with appropriate base types as dynamic/static arrays
+ // Retain old behaviour for now, see
+ // https://github.com/dlang/phobos/pull/7574
+ enum bool isDynamicArray = isDynamicArray!U;
+ else
+ enum bool isDynamicArray = false;
+}
+
+enum isStaticArray(T) = __traits(isStaticArray, T);
+
+enum isArray(T) = isStaticArray!T || isDynamicArray!T;
+
+*/
diff --git a/alloc.d b/alloc.d
index b9c8df9..7665334 100644
--- a/alloc.d
+++ b/alloc.d
@@ -5,9 +5,14 @@ import dlib.math;
import dlib.platform;
import dlib.util;
-import std.stdio;
-import core.stdc.string : memset;
-import core.memory;
+version(WebAssembly)
+{
+
+}
+else
+{
+ import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
+}
static Scratch g_scratch;
static u64 g_scratch_index;
@@ -76,16 +81,16 @@ MFree(T)(T[] slice)
T*
Alloc(T)()
{
- void* mem = pureMalloc(T.sizeof);
- memset(mem, 0, T.sizeof);
+ void* mem = malloc(T.sizeof);
+ MemSet(mem, 0, T.sizeof);
return (cast(T*)mem);
}
T[]
-Alloc(T)(u64 count)
+Alloc(T)(usize count)
{
- void* mem = pureMalloc(T.sizeof * count);
- memset(mem, 0, T.sizeof * count);
+ void* mem = malloc(T.sizeof * count);
+ MemSet(mem, 0, T.sizeof * count);
return (cast(T*)mem)[0 .. count];
}
@@ -93,7 +98,7 @@ T[]
Alloc(T)(T[] target)
{
T[] arr = Alloc!(T)(target.length);
- arr[] = target[];
+ arr[] = target[];
return arr;
}
@@ -132,20 +137,20 @@ Alloc(T)(u64 count, T set)
T[]
Realloc(T)(T[] arr, u64 count)
{
- void* mem = pureRealloc(arr.ptr, T.sizeof * count);
+ void* mem = realloc(arr.ptr, T.sizeof * count);
return (cast(T*)mem)[0 .. count];
}
void
Free(T)(T[] arr)
{
- pureFree(arr.ptr);
+ free(arr.ptr);
}
void
Free(T)(T* ptr)
{
- pureFree(ptr);
+ free(ptr);
}
Arena
@@ -257,8 +262,8 @@ T[]
Alloc(T)(Arena* arena, u64 count)
{
void* mem = AllocAlign(arena, T.sizeof * count, DEFAULT_ALIGNMENT);
- memset(mem, 0, T.sizeof * count);
- return (cast(T*)mem)[0 .. count];
+ MemSet(mem, 0, T.sizeof * count);
+ return (cast(T*)mem)[0 .. cast(usize)count];
}
T[]
@@ -305,7 +310,7 @@ T*
Alloc(T)(Arena* arena)
{
void* mem = AllocAlign(arena, T.sizeof, DEFAULT_ALIGNMENT);
- memset(mem, 0, T.sizeof);
+ MemSet(mem, 0, T.sizeof);
return cast(T*)mem;
};
@@ -413,7 +418,7 @@ ScratchAlloc(string target)
}
T[]
-ScratchAlloc(T)(T[] target, u64 start, u64 len)
+ScratchAlloc(T)(T[] target, usize start, usize len)
{
T[] arr = ScratchAlloc!(T)(len);
arr[0 .. $] = target[start .. start+len];
@@ -421,7 +426,7 @@ ScratchAlloc(T)(T[] target, u64 start, u64 len)
}
string
-ScratchAlloc(string target, u64 start, u64 len)
+ScratchAlloc(string target, usize start, usize len)
{
u8[] str = ScratchAlloc!(u8)(len);
str[0 .. $] = cast(u8[])target[start .. start+len];
diff --git a/assets.d b/assets.d
index 12c3d43..5bf7164 100644
--- a/assets.d
+++ b/assets.d
@@ -1,46 +1,50 @@
module dlib.assets;
-import dlibincludes : cgltf_result,
- cgltf_memory_options,
- cgltf_file_options,
- cgltf_size,
- cgltf_image,
- cgltf_accessor,
- cgltf_result_success,
- cgltf_result_io_error,
- cgltf_free,
- cgltf_options,
- cgltf_node,
- cgltf_load_buffers,
- cgltf_data,
- cgltf_load_buffer_base64,
- stbi_image_free,
- stbi_load_from_memory,
- cgltf_primitive_type_triangles,
- cgltf_node_transform_world,
- cgltf_float,
- cgltf_mesh,
- cgltf_parse,
- cgltf_attribute_type_position,
- cgltf_type_vec3,
- cgltf_component_type_r_32f,
- cgltf_attribute_type_normal,
- cgltf_attribute_type_tangent,
- cgltf_type_vec4,
- cgltf_attribute_type_texcoord,
- cgltf_type_vec2,
- cgltf_component_type_r_8u,
- cgltf_component_type_r_16u,
- cgltf_attribute_type_color,
- cgltf_component_type_r_32u;
import dlib.aliases;
+
+static if(NativeTarget)
+{
+
+import dlibincludes : cgltf_result,
+ cgltf_memory_options,
+ cgltf_file_options,
+ cgltf_size,
+ cgltf_image,
+ cgltf_accessor,
+ cgltf_result_success,
+ cgltf_result_io_error,
+ cgltf_free,
+ cgltf_options,
+ cgltf_node,
+ cgltf_load_buffers,
+ cgltf_data,
+ cgltf_load_buffer_base64,
+ stbi_image_free,
+ stbi_load_from_memory,
+ cgltf_primitive_type_triangles,
+ cgltf_node_transform_world,
+ cgltf_float,
+ cgltf_mesh,
+ cgltf_parse,
+ cgltf_attribute_type_position,
+ cgltf_type_vec3,
+ cgltf_component_type_r_32f,
+ cgltf_attribute_type_normal,
+ cgltf_attribute_type_tangent,
+ cgltf_type_vec4,
+ cgltf_attribute_type_texcoord,
+ cgltf_type_vec2,
+ cgltf_component_type_r_8u,
+ cgltf_component_type_r_16u,
+ cgltf_attribute_type_color,
+ cgltf_component_type_r_32u;
import dlib.util;
import dlib.alloc;
import dlib.math;
+import std.format;
import std.file;
import std.path;
-import std.format;
import std.stdio;
import core.stdc.string : strlen;
import core.stdc.stdio : File = FILE, FOpen = fopen, FSeek = fseek, FTell = ftell, FClose = fclose, FRead = fread, SeekSet = SEEK_SET, SeekEnd = SEEK_END;
@@ -144,14 +148,14 @@ struct Mesh
__gshared string g_BASE_ASSETS_DIR;
-static u32
+ static u32
MagicValue(string str)
{
assert(str.length == 4, "Magic value must 4 characters");
return cast(u32)(cast(u32)(str[0] << 24) | cast(u32)(str[1] << 16) | cast(u32)(str[2] << 8) | cast(u32)(str[3] << 0));
}
-void
+ void
SetBaseAssetsDir(string dir)
{
if(!isDir(dir))
@@ -162,7 +166,7 @@ SetBaseAssetsDir(string dir)
g_BASE_ASSETS_DIR = dir;
}
-void
+ void
U32ColToVec4(u32 col, Vec4* vec)
{
if(!col)
@@ -222,7 +226,7 @@ GLTFFreeCallback(cgltf_memory_options* memory_opts, cgltf_file_options* file_opt
Free(data);
}
-ImageData
+ ImageData
LoadImage(void* data, i32 size)
{
ImageData image;
@@ -251,7 +255,7 @@ LoadImage(void* data, i32 size)
return image;
}
-void
+ void
UnloadImage(ImageData* image)
{
version(NO_STBI) {}
@@ -261,7 +265,7 @@ UnloadImage(ImageData* image)
}
}
-ImageData
+ ImageData
LoadImage(cgltf_image* asset_image, string tex_path)
{
ImageData image;
@@ -335,7 +339,7 @@ LoadImage(cgltf_image* asset_image, string tex_path)
}
}
- PostTypeCheck:
+PostTypeCheck:
u8[] image_data;
if(accepted)
{
@@ -359,7 +363,7 @@ LoadImage(cgltf_image* asset_image, string tex_path)
return image;
}
-ModelData
+ ModelData
LoadGLTF(Arena* arena, string file_name)
{
ModelData model;
@@ -616,11 +620,11 @@ LoadGLTF(Arena* arena, string file_name)
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
- cast(f32)(buffer[k*3+0])/255.0,
- cast(f32)(buffer[k*3+1])/255.0,
- cast(f32)(buffer[k*3+2])/255.0,
- 1.0
- );
+ cast(f32)(buffer[k*3+0])/255.0,
+ cast(f32)(buffer[k*3+1])/255.0,
+ cast(f32)(buffer[k*3+2])/255.0,
+ 1.0
+ );
}
}
else if(attr.component_type == cgltf_component_type_r_16u)
@@ -629,11 +633,11 @@ LoadGLTF(Arena* arena, string file_name)
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
- cast(f32)(buffer[k*3+0])/65535.0,
- cast(f32)(buffer[k*3+1])/65535.0,
- cast(f32)(buffer[k*3+2])/65535.0,
- 1.0
- );
+ cast(f32)(buffer[k*3+0])/65535.0,
+ cast(f32)(buffer[k*3+1])/65535.0,
+ cast(f32)(buffer[k*3+2])/65535.0,
+ 1.0
+ );
}
}
else if(attr.component_type == cgltf_component_type_r_32f)
@@ -657,11 +661,11 @@ LoadGLTF(Arena* arena, string file_name)
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
- cast(f32)(buffer[k*4+0])/255.0,
- cast(f32)(buffer[k*4+1])/255.0,
- cast(f32)(buffer[k*4+2])/255.0,
- cast(f32)(buffer[k*4+3])/255.0
- );
+ cast(f32)(buffer[k*4+0])/255.0,
+ cast(f32)(buffer[k*4+1])/255.0,
+ cast(f32)(buffer[k*4+2])/255.0,
+ cast(f32)(buffer[k*4+3])/255.0
+ );
}
}
else if(attr.component_type == cgltf_component_type_r_16u)
@@ -670,11 +674,11 @@ LoadGLTF(Arena* arena, string file_name)
for(u64 k = 0; k < model.meshes[mesh_index].vtx.length; k += 1)
{
model.meshes[mesh_index].vtx[k].color = Vec4(
- cast(f32)(buffer[k*4+0])/65535.0,
- cast(f32)(buffer[k*4+1])/65535.0,
- cast(f32)(buffer[k*4+2])/65535.0,
- cast(f32)(buffer[k*4+3])/65535.0
- );
+ cast(f32)(buffer[k*4+0])/65535.0,
+ cast(f32)(buffer[k*4+1])/65535.0,
+ cast(f32)(buffer[k*4+2])/65535.0,
+ cast(f32)(buffer[k*4+3])/65535.0
+ );
}
}
else if(attr.component_type == cgltf_component_type_r_32f)
@@ -689,7 +693,7 @@ LoadGLTF(Arena* arena, string file_name)
{
Logf("Color component type [%s] not supported", attr.component_type);
}
-
+
}
}
} break;
@@ -705,7 +709,7 @@ LoadGLTF(Arena* arena, string file_name)
model.meshes[mesh_index].idx = model.idx_buf[idx_index .. idx_index+attr.count];
idx_index += attr.count;
-
+
if(attr.component_type == cgltf_component_type_r_16u)
{
u16* buffer = GetGLTFBuffer!(u16)(attr);
@@ -754,7 +758,7 @@ LoadGLTF(Arena* arena, string file_name)
return model;
}
-void
+ void
AddMeshVertices(cgltf_accessor* accessor, ModelData* model, u64 mesh_index, u64* vtx_count)
{
if(model.meshes[mesh_index].vtx == null)
@@ -766,17 +770,17 @@ AddMeshVertices(cgltf_accessor* accessor, ModelData* model, u64 mesh_index, u64*
}
}
-T*
+ T*
GetGLTFBuffer(T)(cgltf_accessor* accessor)
{
return cast(T*)(accessor.buffer_view.buffer.data) + (accessor.buffer_view.offset/T.sizeof) + (accessor.offset/T.sizeof);
}
-static ImageData
+ static ImageData
GenerateDefaultTexture(u32 x, u32 y)
{
ImageData data = {
- w: x,
+ w: x,
h: y,
ch: 4,
};
@@ -802,7 +806,7 @@ GenerateDefaultTexture(u32 x, u32 y)
return data;
}
-static Material
+ static Material
GenerateDefaultMaterial()
{
Material mat;
@@ -813,4 +817,4 @@ GenerateDefaultMaterial()
return mat;
}
-import std.exception;
+}
diff --git a/build.sh b/build.sh
index b1d3b04..3d3bc5c 100755
--- a/build.sh
+++ b/build.sh
@@ -70,3 +70,16 @@ if ! [ -f "${build}/libcgltf.a" ]; then
ar rcs $lib $obj
rm $obj
fi
+
+
+# XXHASH
+src="${script_dir}/external/xxhash/xxhash.c"
+flags="-std=c99 -Wno-everything -Iexternal/xxhash -c -static"
+obj="${build}/xxhash.o"
+lib="${build}/libxxhash.a"
+
+if ! [ -f $lib ]; then
+ $c_compiler $flags $src $out $obj
+ ar rcs $lib $obj
+ rm $obj
+fi
diff --git a/dlibincludes.c b/dlibincludes.c
index 5cbc29c..cbe9173 100644
--- a/dlibincludes.c
+++ b/dlibincludes.c
@@ -17,9 +17,17 @@
# include FT_GLYPH_H
#endif
-#ifndef NO_STBI
+#if !defined(NO_STBI) && !defined(BUILD_WASM)
# include "external/stb/stb_image.h"
# include "external/stb/stb_image_write.h"
+#endif
+
+#ifndef BUILD_WASM
+# define CGLM_FORCE_DEPTH_ZERO_TO_ONE
+# include "external/cglm/cglm.h"
+#endif
+
+#ifndef NO_STBI
# include "external/stb/stb_truetype.h"
#endif
@@ -27,8 +35,12 @@
# include "../VulkanRenderer/vulkan_includes.c"
#endif
-#define CGLM_FORCE_DEPTH_ZERO_TO_ONE
-#include "external/cglm/cglm.h"
#include "external/cgltf/cgltf.h"
+#define PRINTF_SUPPORT_FLOAT
+#include "external/printf/printf.c"
+
+#define XXH_NO_STDLIB 1
+#define XXH_STATIC_LINKING_ONLY 1
+#include "external/xxhash/xxhash.h"
diff --git a/external/arsd-webassembly/arsd/color.d b/external/arsd-webassembly/arsd/color.d
new file mode 100644
index 0000000..b453f72
--- /dev/null
+++ b/external/arsd-webassembly/arsd/color.d
@@ -0,0 +1,224 @@
+module arsd.color;
+
+char toHex(int a) {
+ if(a < 10)
+ return cast(char) (a + '0');
+ else
+ return cast(char) (a - 10 + 'a');
+}
+
+struct Color {
+ int r, g, b, a;
+ this(int r, int g, int b, int a = 255) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ void toTempString(char[] data) {
+ data[0] = '#';
+ data[1] = toHex(r >> 4);
+ data[2] = toHex(r & 0x0f);
+ data[3] = toHex(g >> 4);
+ data[4] = toHex(g & 0x0f);
+ data[5] = toHex(b >> 4);
+ data[6] = toHex(b & 0x0f);
+ }
+
+ static Color fromHsl(double h, double s, double l, double a = 255) {
+ h = h % 360;
+
+ double C = (1 - absInternal(2 * l - 1)) * s;
+
+ double hPrime = h / 60;
+
+ double X = C * (1 - absInternal(hPrime % 2 - 1));
+
+ double r, g, b;
+
+ if(h is double.nan)
+ r = g = b = 0;
+ else if (hPrime >= 0 && hPrime < 1) {
+ r = C;
+ g = X;
+ b = 0;
+ } else if (hPrime >= 1 && hPrime < 2) {
+ r = X;
+ g = C;
+ b = 0;
+ } else if (hPrime >= 2 && hPrime < 3) {
+ r = 0;
+ g = C;
+ b = X;
+ } else if (hPrime >= 3 && hPrime < 4) {
+ r = 0;
+ g = X;
+ b = C;
+ } else if (hPrime >= 4 && hPrime < 5) {
+ r = X;
+ g = 0;
+ b = C;
+ } else if (hPrime >= 5 && hPrime < 6) {
+ r = C;
+ g = 0;
+ b = X;
+ }
+
+ double m = l - C / 2;
+
+ r += m;
+ g += m;
+ b += m;
+
+ return Color(
+ cast(int)(r * 255),
+ cast(int)(g * 255),
+ cast(int)(b * 255),
+ cast(int)(a));
+ }
+
+ static immutable Color white = Color(255, 255, 255, 255);
+ static immutable Color black = Color(0, 0, 0, 255);
+ static immutable Color red = Color(255, 0, 0, 255);
+ static immutable Color blue = Color(0, 0, 255, 255);
+ static immutable Color green = Color(0, 255, 0, 255);
+ static immutable Color yellow = Color(255, 255, 0, 255);
+ static immutable Color teal = Color(0, 255, 255, 255);
+ static immutable Color purple = Color(255, 0, 255, 255);
+ static immutable Color gray = Color(127, 127, 127, 255);
+ static immutable Color transparent = Color(0, 0, 0, 0);
+}
+
+struct Point {
+ int x;
+ int y;
+
+ pure const nothrow @safe:
+
+ Point opBinary(string op)(in Point rhs) @nogc {
+ return Point(mixin("x" ~ op ~ "rhs.x"), mixin("y" ~ op ~ "rhs.y"));
+ }
+
+ Point opBinary(string op)(int rhs) @nogc {
+ return Point(mixin("x" ~ op ~ "rhs"), mixin("y" ~ op ~ "rhs"));
+ }
+
+}
+
+struct Size {
+ int width;
+ int height;
+}
+
+
+nothrow @safe @nogc pure
+double absInternal(double a) { return a < 0 ? -a : a; }
+
+struct Rectangle {
+ int left; ///
+ int top; ///
+ int right; ///
+ int bottom; ///
+
+ pure const nothrow @safe @nogc:
+
+ ///
+ this(int left, int top, int right, int bottom) {
+ this.left = left;
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ }
+
+ ///
+ this(in Point upperLeft, in Point lowerRight) {
+ this(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
+ }
+
+ ///
+ this(in Point upperLeft, in Size size) {
+ this(upperLeft.x, upperLeft.y, upperLeft.x + size.width, upperLeft.y + size.height);
+ }
+
+ ///
+ @property Point upperLeft() {
+ return Point(left, top);
+ }
+
+ ///
+ @property Point upperRight() {
+ return Point(right, top);
+ }
+
+ ///
+ @property Point lowerLeft() {
+ return Point(left, bottom);
+ }
+
+ ///
+ @property Point lowerRight() {
+ return Point(right, bottom);
+ }
+
+ ///
+ @property Point center() {
+ return Point((right + left) / 2, (bottom + top) / 2);
+ }
+
+ ///
+ @property Size size() {
+ return Size(width, height);
+ }
+
+ ///
+ @property int width() {
+ return right - left;
+ }
+
+ ///
+ @property int height() {
+ return bottom - top;
+ }
+
+ /// Returns true if this rectangle entirely contains the other
+ bool contains(in Rectangle r) {
+ return contains(r.upperLeft) && contains(r.lowerRight);
+ }
+
+ /// ditto
+ bool contains(in Point p) {
+ return (p.x >= left && p.x < right && p.y >= top && p.y < bottom);
+ }
+
+ /// Returns true of the two rectangles at any point overlap
+ bool overlaps(in Rectangle r) {
+ // the -1 in here are because right and top are exclusive
+ return !((right-1) < r.left || (r.right-1) < left || (bottom-1) < r.top || (r.bottom-1) < top);
+ }
+
+ /++
+ Returns a Rectangle representing the intersection of this and the other given one.
+
+ History:
+ Added July 1, 2021
+ +/
+ Rectangle intersectionOf(in Rectangle r) {
+ auto tmp = Rectangle(max(left, r.left), max(top, r.top), min(right, r.right), min(bottom, r.bottom));
+ if(tmp.left >= tmp.right || tmp.top >= tmp.bottom)
+ tmp = Rectangle.init;
+
+ return tmp;
+ }
+}
+
+private int max(int a, int b) @nogc nothrow pure @safe {
+ return a >= b ? a : b;
+}
+private int min(int a, int b) @nogc nothrow pure @safe {
+ return a <= b ? a : b;
+}
+
+
+enum arsd_jsvar_compatible = "arsd_jsvar_compatible";
+class MemoryImage {}
diff --git a/external/arsd-webassembly/arsd/simpleaudio.d b/external/arsd-webassembly/arsd/simpleaudio.d
new file mode 100644
index 0000000..47099cc
--- /dev/null
+++ b/external/arsd-webassembly/arsd/simpleaudio.d
@@ -0,0 +1,8 @@
+module arsd.simpleaudio;
+
+struct AudioOutputThread {
+ this(int) {}
+ void start() {}
+ void beep(int = 0) {}
+ void boop(int = 0) {}
+}
diff --git a/external/arsd-webassembly/arsd/simpledisplay.d b/external/arsd-webassembly/arsd/simpledisplay.d
new file mode 100644
index 0000000..de2a941
--- /dev/null
+++ b/external/arsd-webassembly/arsd/simpledisplay.d
@@ -0,0 +1,315 @@
+module arsd.simpledisplay;
+
+public import arsd.color;
+
+import arsd.webassembly;
+
+//shared static this() { eval("hi there"); }
+
+// the js bridge is SO EXPENSIVE we have to minimize using it.
+
+class SimpleWindow {
+ this(int width, int height, string title = "D Application") {
+ this.width = width;
+ this.height = height;
+
+ element = eval!NativeHandle(q{
+ var s = document.getElementById("screen");
+ var canvas = document.createElement("canvas");
+ canvas.addEventListener("contextmenu", function(event) { event.preventDefault(); });
+ canvas.setAttribute("width", $0);
+ canvas.setAttribute("height", $1);
+ canvas.setAttribute("title", $2);
+ s.appendChild(canvas);
+ return canvas;
+ }, width, height, title);
+
+ canvasContext = eval!NativeHandle(q{
+ return $0.getContext("2d");
+ }, element);
+ }
+
+ NativeHandle element;
+ NativeHandle canvasContext;
+ int width;
+ int height;
+
+ void close() {
+ eval(q{ clearInterval($0); }, intervalId);
+ intervalId = 0;
+ }
+
+ void delegate() onClosing;
+
+ ScreenPainter draw() {
+ return ScreenPainter(this);
+ }
+
+ int intervalId;
+
+ void eventLoop(T...)(int timeout, T t) {
+ foreach(arg; t) {
+ static if(is(typeof(arg) : void delegate())) {
+ sdpy_timer = arg;
+ } else static if(is(typeof(arg) : void delegate(KeyEvent))) {
+ sdpy_key = arg;
+ } else static if(is(typeof(arg) : void delegate(MouseEvent))) {
+ sdpy_mouse = arg;
+ } else static assert(0, typeof(arg).stringof);
+ }
+
+ if(timeout)
+ intervalId = eval!int(q{
+ return setInterval(function(a) { exports.sdpy_timer_trigger(); }, $0);
+ }, timeout);
+
+ eval(q{
+ function translate(key) {
+ var k = 0;
+ switch(key) {
+ case "[": k = 1; break;
+ case "]": k = 2; break;
+ case "Left": case "ArrowLeft": k = 3; break;
+ case "Right": case "ArrowRight": k = 4; break;
+ case "Down": case "ArrowDown": k = 5; break;
+ case "Up": case "ArrowUp": k = 6; break;
+ case " ": k = 7; break;
+ // "Enter", "Esc" / "Escape"
+ default: k = 0;
+ }
+ return k;
+ }
+ document.body.addEventListener("keydown", function(event) {
+ exports.sdpy_key_trigger(1, translate(event.key));
+ event.preventDefault();
+ }, true);
+ document.body.addEventListener("keyup", function(event) {
+ exports.sdpy_key_trigger(0, translate(event.key));
+ event.preventDefault();
+ }, true);
+ $0.addEventListener("mousedown", function(event) {
+ exports.sdpy_mouse_trigger(1, event.button, event.offsetX, event.offsetY);
+ }, true);
+ $0.addEventListener("mouseup", function(event) {
+ exports.sdpy_mouse_trigger(0, event.button);
+ }, true);
+ }, element);
+
+ }
+}
+
+void delegate() sdpy_timer;
+void delegate(KeyEvent) sdpy_key;
+void delegate(MouseEvent) sdpy_mouse;
+
+export extern(C) void sdpy_timer_trigger() {
+ sdpy_timer();
+}
+export extern(C) void sdpy_key_trigger(int pressed, int key) {
+ KeyEvent ke;
+ ke.pressed = pressed ? true : false;
+ ke.key = key;
+ if(sdpy_key)
+ sdpy_key(ke);
+}
+export extern(C) void sdpy_mouse_trigger(int pressed, int button, int x, int y) {
+ MouseEvent me;
+ me.type = pressed ? MouseEventType.buttonPressed : MouseEventType.buttonReleased;
+ switch(button) {
+ case 0:
+ me.button = MouseButton.left;
+ break;
+ case 1:
+ me.button = MouseButton.middle;
+ break;
+ case 2:
+ me.button = MouseButton.right;
+ break;
+ default:
+ }
+ me.x = x;
+ me.y = y;
+ if(sdpy_mouse)
+ sdpy_mouse(me);
+
+}
+
+
+// push arguments in reverse order then push the command
+enum canvasRender = q{
+};
+
+struct ScreenPainter {
+ this(SimpleWindow window) {
+ // no need to arc here tbh
+ this.w = window.width;
+ this.h = window.height;
+
+ this.element = NativeHandle(window.element.handle, false);
+ this.context = NativeHandle(window.canvasContext.handle, false);
+ }
+ @disable this(this); // for now...
+ NativeHandle element;
+ NativeHandle context;
+
+ private int w, h;
+
+ void clear() {
+ addCommand(1);
+ }
+
+ void outlineColor(Color c) {
+ char[7] data;
+ c.toTempString(data[]);
+ addCommand(2, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ return;
+ //context.properties.strokeStyle!string = cast(immutable)(data[]);
+ }
+ void fillColor(Color c) {
+ char[7] data;
+ c.toTempString(data[]);
+ addCommand(3, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ return;
+ //context.properties.fillStyle!string = cast(immutable)(data[]);
+ }
+
+ void drawPolygon(Point[] points) {
+ addCommand(8);
+ addCommand(cast(double) points.length);
+ foreach(point; points) {
+ push(cast(double) point.x);
+ push(cast(double) point.y);
+ }
+ }
+
+ void drawRectangle(Point p, int w, int h) {
+ addCommand(4, p.x, p.y, w, h);
+ }
+ void drawRectangle(Point p, Size s) {
+ drawRectangle(p, s.width, s.height);
+ }
+ void drawText(Point p, in char[] txt, Point lowerRight = Point(0, 0), uint alignment = 0) {
+ // FIXME use the new system
+ addCommand(5, p.x, p.y + 16, txt.length);
+ foreach(c; txt)
+ push(cast(double) c);
+ return;
+ eval(q{
+ var context = $0;
+ context.font = "18px sans-serif";
+ context.strokeText($1, $2, $3 + 16);
+ }, context, txt, p.x, p.y);
+ }
+
+ void drawCircle(Point upperLeft, int diameter) {
+ addCommand(6, upperLeft.x + diameter / 2, upperLeft.y + diameter / 2, diameter / 2);
+ }
+
+ void drawLine(Point p1, Point p2) {
+ drawLine(p1.x, p1.y, p2.x, p2.y);
+ }
+
+ void drawLine(int x1, int y1, int x2, int y2) {
+ addCommand(7, x1, y1, x2, y2);
+ }
+
+ private:
+ void addCommand(T...)(int cmd, T args) {
+ push(cmd);
+ foreach(arg; args) {
+ push(arg);
+ }
+ }
+
+ // 50ish % on ronaroids total cpu without this
+ // with it, we at like 16%
+ static __gshared double[] commandStack;
+ size_t commandStackPosition;
+
+ void push(T)(T t) {
+ if(commandStackPosition == commandStack.length) {
+ commandStack.length = commandStack.length + 1024;
+ commandStack.assumeUniqueReference();
+ }
+
+ commandStack[commandStackPosition++] = t;
+ }
+
+ ~this() {
+ executeCanvasCommands(this.context.handle, this.commandStack.ptr, commandStackPosition);
+ }
+}
+
+extern(C) void executeCanvasCommands(int handle, double* start, size_t len);
+
+struct KeyEvent {
+ int key;
+ bool pressed;
+}
+
+enum MouseEventType : int {
+ motion = 0, /// The mouse moved inside the window
+ buttonPressed = 1, /// A mouse button was pressed or the wheel was spun
+ buttonReleased = 2, /// A mouse button was released
+}
+
+struct MouseEvent {
+ MouseEventType type;
+ int x;
+ int y;
+ int dx;
+ int dy;
+
+ MouseButton button;
+ int modifierState;
+}
+
+enum MouseButton : int {
+ none = 0,
+ left = 1, ///
+ right = 2, ///
+ middle = 4, ///
+ wheelUp = 8, ///
+ wheelDown = 16, ///
+ backButton = 32, /// often found on the thumb and used for back in browsers
+ forwardButton = 64, /// often found on the thumb and used for forward in browsers
+}
+
+enum TextAlignment : uint {
+ Left = 0, ///
+ Center = 1, ///
+ Right = 2, ///
+
+ VerticalTop = 0, ///
+ VerticalCenter = 4, ///
+ VerticalBottom = 8, ///
+}
+
+enum Key {
+ LeftBracket = 1,
+ RightBracket,
+ Left,
+ Right,
+ Down,
+ Up,
+ Space
+}
+
+enum MouseCursor { cross }
+
+class OperatingSystemFont {}
+enum UsingSimpledisplayX11 = false;
+enum SimpledisplayTimerAvailable = false;
+
+
+class Sprite{}
+
+enum bool OpenGlEnabled = false;
+
+alias ScreenPainterImplementation = ScreenPainter;
+
+mixin template ExperimentalTextComponent() {
+ class TextLayout {
+
+ }
+}
diff --git a/external/arsd-webassembly/arsd/webassembly.d b/external/arsd-webassembly/arsd/webassembly.d
new file mode 100644
index 0000000..6bbfb41
--- /dev/null
+++ b/external/arsd-webassembly/arsd/webassembly.d
@@ -0,0 +1,178 @@
+/+
+ This is the D interface to my webassembly javascript bridge.
++/
+module arsd.webassembly;
+
+struct AcquireArgument {
+ int type;
+ const(void)* ptr;
+ int length;
+}
+
+// the basic bridge functions defined in webassembly-core.js {
+
+@trusted @nogc pure nothrow
+{
+ extern(C) void retain(int);
+ extern(C) void release(int);
+ extern(C) int acquire(int returnType, string callingModuleName, string code, AcquireArgument[] arguments);
+ extern(C) void abort();
+ extern(C) int monotimeNow();
+}
+
+
+// }
+
+export extern(C) int invoke_d_array_delegate(size_t ptr, size_t funcptr, ubyte[] arg) {
+ void delegate(in ubyte[] arr) dg;
+
+ dg.ptr = cast(void*) ptr;
+ dg.funcptr = cast(typeof(dg.funcptr)) funcptr;
+
+ dg(arg);
+ return 0;
+
+};
+
+/++
+ Evaluates the given code in Javascript. The arguments are available in JS as $0, $1, $2, ....
+ The `this` object in the evaluated code is set to an object representing the D module that
+ you can store some stuff in across calls without having to hit the global namespace.
+
+ Note that if you want to return a value from javascript, you MUST use the return keyword
+ in the script string.
+
+ Wrong: `eval!NativeHandle("document");`
+
+ Right: `eval!NativeHandle("return document");`
++/
+template eval(T = void) {
+ T eval(Args...)(string code, Args args, string callingModuleName = __MODULE__) @trusted @nogc pure {
+ AcquireArgument[Args.length] aa;
+ foreach(idx, ref arg; args) {
+ // FIXME: some other type for unsigned....
+ static if(is(typeof(arg) : const int)) {
+ aa[idx].type = 0;
+ aa[idx].ptr = cast(void*) arg;
+ aa[idx].length = arg.sizeof;
+ } else static if(is(immutable typeof(arg) == immutable string)) {
+ aa[idx].type = 1;
+ aa[idx].ptr = arg.ptr;
+ aa[idx].length = arg.length;
+ } else static if(is(immutable typeof(arg) == immutable NativeHandle)) {
+ aa[idx].type = 2;
+ aa[idx].ptr = cast(void*) arg.handle;
+ aa[idx].length = NativeHandle.sizeof;
+ } else static if(is(typeof(arg) : const float)) {
+ aa[idx].type = 3;
+ aa[idx].ptr = cast(void*) &arg;
+ aa[idx].length = arg.sizeof;
+ } else static if(is(immutable typeof(arg) == immutable ubyte[])) {
+ aa[idx].type = 4;
+ aa[idx].ptr = arg.ptr;
+ aa[idx].length = arg.length;
+ /*
+ } else static if(is(typeof(arg) == delegate)) {
+ aa[idx].type = 5;
+ aa[idx].ptr = cast(void*) &arg;
+ aa[idx].length = arg.sizeof;
+ */
+ } else {
+ static assert(0);
+ }
+ }
+ static if(is(T == void))
+ acquire(0, callingModuleName, code, aa[]);
+ else static if(is(T == int))
+ return acquire(1, callingModuleName, code, aa[]);
+ else static if(is(T == float))
+ return *cast(float*) cast(void*) acquire(2, callingModuleName, code, aa[]);
+ else static if(is(T == NativeHandle))
+ return NativeHandle(acquire(3, callingModuleName, code, aa[]));
+ else static if(is(T == string)) {
+ auto ptr = cast(int*) acquire(7, callingModuleName, code, aa[]);
+ auto len = *ptr;
+ ptr++;
+ return (cast(immutable(char)*) ptr)[0 .. len];
+ }
+ else static assert(0);
+ }
+}
+
+// and do some opDispatch on the native things to call their methods and it should look p cool
+
+struct NativeHandle {
+ @trusted @nogc pure:
+
+ int handle;
+ bool arc;
+ this(int handle, bool arc = true) {
+ this.handle = handle;
+ this.arc = arc;
+ }
+
+ this(this) {
+ if(arc) retain(handle);
+ }
+
+ ~this() {
+ if(arc) release(handle);
+ }
+
+ // never store these, they don't affect the refcount
+ PropertiesHelper properties() {
+ return PropertiesHelper(handle);
+ }
+
+ // never store these, they don't affect the refcount
+ MethodsHelper methods() {
+ return MethodsHelper(handle);
+
+ }
+}
+
+struct MethodsHelper {
+ @trusted @nogc pure:
+ @disable this();
+ @disable this(this);
+
+ int handle;
+ private this(int handle) { this.handle = handle; }
+
+ template opDispatch(string name) {
+ template opDispatch(T = NativeHandle)
+ {
+ T opDispatch(Args...)(Args args, string callingModuleName = __MODULE__) @trusted @nogc pure
+ {
+ return eval!T(q{
+ return $0[$1].apply($0, Array.prototype.slice.call(arguments, 2));
+ }, NativeHandle(this.handle, false), name, args, callingModuleName);
+ }
+ }
+ }
+
+}
+struct PropertiesHelper {
+ @trusted @nogc pure:
+ @disable this();
+ @disable this(this);
+
+ int handle;
+ private this(int handle) { this.handle = handle; }
+
+ template opDispatch(string name) {
+ template opDispatch(T = NativeHandle) {
+ T opDispatch() {
+ return eval!T(q{
+ return $0[$1];
+ }, NativeHandle(this.handle, false), name);
+ }
+
+ void opDispatch(T value) {
+ return eval!void(q{
+ return $0[$1] = $2;
+ }, NativeHandle(this.handle, false), name, value);
+ }
+ }
+ }
+}
diff --git a/external/arsd-webassembly/core/arsd/aa.d b/external/arsd-webassembly/core/arsd/aa.d
new file mode 100644
index 0000000..aa7cff3
--- /dev/null
+++ b/external/arsd-webassembly/core/arsd/aa.d
@@ -0,0 +1,778 @@
+/**
+ * Implementation of associative arrays.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak
+ * Source: $(DRUNTIMESRC rt/_aaA.d)
+ */
+module core.arsd.aa;
+
+/// AA version for debuggers, bump whenever changing the layout
+extern (C) immutable int _aaVersion = 1;
+
+import core.internal.hash;
+import core.arsd.memory_allocation;
+
+uint min(uint a, uint b) { return a < b ? a : b; }
+uint max(uint a, uint b) { return a > b ? a : b; }
+
+// grow threshold
+private enum GROW_NUM = 4;
+private enum GROW_DEN = 5;
+// shrink threshold
+private enum SHRINK_NUM = 1;
+private enum SHRINK_DEN = 8;
+// grow factor
+private enum GROW_FAC = 4;
+// growing the AA doubles it's size, so the shrink threshold must be
+// smaller than half the grow threshold to have a hysteresis
+static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN);
+// initial load factor (for literals), mean of both thresholds
+private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2;
+private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
+
+private enum INIT_NUM_BUCKETS = 8;
+// magic hash constants to distinguish empty, deleted, and filled buckets
+private enum HASH_EMPTY = 0;
+private enum HASH_DELETED = 0x1;
+private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
+
+// The compiler uses `void*` for its prototypes.
+// Don't wrap in a struct to maintain ABI compatibility.
+alias AA = Impl*;
+
+private bool empty(scope const AA impl) pure nothrow @nogc
+{
+ return impl is null || !impl.length;
+}
+
+private struct Impl
+{
+private:
+ this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
+ {
+ keysz = cast(uint) ti.key.size;
+ valsz = cast(uint) ti.value.size;
+ buckets = allocBuckets(sz);
+ firstUsed = cast(uint) buckets.length;
+ valoff = cast(uint) talign(keysz, ti.value.talign);
+
+ import core.arsd.objectutils : hasPostblit;
+
+ if (hasPostblit(cast()ti.key))
+ flags |= Flags.keyHasPostblit;
+ if ((ti.key.flags | ti.value.flags) & 1)
+ flags |= Flags.hasPointers;
+
+ entryTI = fakeEntryTI(this, ti.key, ti.value);
+ }
+
+ Bucket[] buckets;
+ uint used;
+ uint deleted;
+ TypeInfo_Struct entryTI;
+ uint firstUsed;
+ immutable uint keysz;
+ immutable uint valsz;
+ immutable uint valoff;
+ Flags flags;
+
+ enum Flags : ubyte
+ {
+ none = 0x0,
+ keyHasPostblit = 0x1,
+ hasPointers = 0x2,
+ }
+
+ @property size_t length() const pure nothrow @nogc
+ {
+ assert(used >= deleted);
+ return used - deleted;
+ }
+
+ @property size_t dim() const pure nothrow @nogc @safe
+ {
+ return buckets.length;
+ }
+
+ @property size_t mask() const pure nothrow @nogc
+ {
+ return dim - 1;
+ }
+
+ // find the first slot to insert a value with hash
+ inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc
+ {
+ for (size_t i = hash & mask, j = 1;; ++j)
+ {
+ if (!buckets[i].filled)
+ return &buckets[i];
+ i = (i + j) & mask;
+ }
+ }
+
+ // lookup a key
+ inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
+ {
+ for (size_t i = hash & mask, j = 1;; ++j)
+ {
+ if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry))
+ return &buckets[i];
+ else if (buckets[i].empty)
+ return null;
+ i = (i + j) & mask;
+ }
+ }
+
+ void grow(scope const TypeInfo keyti)
+ {
+ // If there are so many deleted entries, that growing would push us
+ // below the shrink threshold, we just purge deleted entries instead.
+ if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
+ resize(dim);
+ else
+ resize(GROW_FAC * dim);
+ }
+
+ void shrink(scope const TypeInfo keyti)
+ {
+ if (dim > INIT_NUM_BUCKETS)
+ resize(dim / GROW_FAC);
+ }
+
+ void resize(size_t ndim)
+ {
+ auto obuckets = buckets;
+ buckets = allocBuckets(ndim);
+
+ foreach (ref b; obuckets[firstUsed .. $])
+ if (b.filled)
+ *findSlotInsert(b.hash) = b;
+
+ firstUsed = 0;
+ used -= deleted;
+ deleted = 0;
+ free(cast(ubyte*)(obuckets.ptr)); // safe to free b/c impossible to reference
+ }
+
+ void clear() pure nothrow
+ {
+ import core.stdc.string : memset;
+ // clear all data, but don't change bucket array length
+ memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof);
+ deleted = used = 0;
+ firstUsed = cast(uint) dim;
+ }
+}
+
+//==============================================================================
+// Bucket
+//------------------------------------------------------------------------------
+
+private struct Bucket
+{
+private pure nothrow @nogc:
+ size_t hash;
+ void* entry;
+
+ @property bool empty() const
+ {
+ return hash == HASH_EMPTY;
+ }
+
+ @property bool deleted() const
+ {
+ return hash == HASH_DELETED;
+ }
+
+ @property bool filled() const @safe
+ {
+ return cast(ptrdiff_t) hash < 0;
+ }
+}
+
+Bucket[] allocBuckets(size_t dim) @trusted
+{
+ enum attr = 0b0001_0000; //enum attr = GC.BlkAttr.NO_INTERIOR;
+ immutable sz = dim * Bucket.sizeof;
+ return (cast(Bucket*) calloc(sz, attr))[0 .. dim];
+}
+
+//==============================================================================
+// Entry
+//------------------------------------------------------------------------------
+
+private void* allocEntry(scope const Impl* aa, scope const void* pkey)
+{
+ immutable akeysz = aa.valoff;
+ void* res = void;
+ if(aa.entryTI)
+ res = _d_newitemU(aa.entryTI);
+ else
+ res = malloc(akeysz + aa.valsz).ptr;
+
+ memcpy(res, pkey, aa.keysz); // copy key
+ memset(res + akeysz, 0, aa.valsz); // zero value
+
+ return res;
+}
+
+package void entryDtor(void* p, const TypeInfo_Struct sti)
+{
+ // key and value type info stored after the TypeInfo_Struct by tiEntry()
+ auto sizeti = __traits(classInstanceSize, TypeInfo_Struct);
+ auto extra = cast(const(TypeInfo)*)(cast(void*) sti + sizeti);
+ extra[0].destroy(p);
+ extra[1].destroy(p + talign(extra[0].size, extra[1].talign));
+}
+
+private bool hasDtor(const TypeInfo ti) pure nothrow
+{
+
+ if (typeid(ti) is typeid(TypeInfo_Struct))
+ if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor)
+ return true;
+ if (typeid(ti) is typeid(TypeInfo_StaticArray))
+ return hasDtor(cast()ti.next);
+
+ return false;
+}
+
+// build type info for Entry with additional key and value fields
+TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
+{
+ import core.arsd.objectutils;
+ //Same as unqualify
+ auto kti = unqualify(keyti);
+ auto vti = unqualify(valti);
+
+
+ bool entryHasDtor = hasDtor(kti) || hasDtor(vti);
+ if (!entryHasDtor)
+ return null;
+
+ // save kti and vti after type info for struct
+ enum sizeti = __traits(classInstanceSize, TypeInfo_Struct);
+ void* p = malloc(sizeti + (2) * (void*).sizeof).ptr;
+
+ memcpy(p, __traits(initSymbol, TypeInfo_Struct).ptr, sizeti);
+
+ auto ti = cast(TypeInfo_Struct) p;
+ auto extra = cast(TypeInfo*)(p + sizeti);
+ extra[0] = cast() kti;
+ extra[1] = cast() vti;
+
+ static immutable tiMangledName = "S2rt3aaA__T5EntryZ";
+ ti.name = tiMangledName;
+
+
+ // we don't expect the Entry objects to be used outside of this module, so we have control
+ // over the non-usage of the callback methods and other entries and can keep these null
+ // xtoHash, xopEquals, xopCmp, xtoString and xpostblit
+ immutable entrySize = aa.valoff + aa.valsz;
+ ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr
+
+ if (entryHasDtor)
+ {
+ // xdtor needs to be built from the dtors of key and value for the GC
+ ti.xdtorti = &entryDtor;
+ ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType;
+ }
+
+ ti.align_ = cast(uint) max(kti.talign, vti.talign);
+
+ return ti;
+}
+
+
+//==============================================================================
+// Helper functions
+//------------------------------------------------------------------------------
+
+private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
+{
+ immutable mask = algn - 1;
+ assert(!(mask & algn));
+ return (tsize + mask) & ~mask;
+}
+
+// mix hash to "fix" bad hash functions
+private size_t mix(size_t h) @safe pure nothrow @nogc
+{
+ // final mix function of MurmurHash2
+ enum m = 0x5bd1e995;
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+}
+
+private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow
+{
+ immutable hash = keyti.getHash(pkey);
+ // highest bit is set to distinguish empty/deleted from filled buckets
+ return mix(hash) | HASH_FILLED_MARK;
+}
+
+private size_t nextpow2(const size_t n) pure nothrow @nogc
+{
+ import core.bitop : bsr;
+
+ if (!n)
+ return 1;
+
+ const isPowerOf2 = !((n - 1) & n);
+ return 1 << (bsr(n) + !isPowerOf2);
+}
+
+
+//==============================================================================
+// API Implementation
+//------------------------------------------------------------------------------
+
+/** Allocate associative array data.
+ * Called for `new SomeAA` expression.
+ * Params:
+ * ti = TypeInfo for the associative array
+ * Returns:
+ * A new associative array.
+ */
+extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
+{
+ return new Impl(ti);
+}
+
+/// Determine number of entries in associative array.
+extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
+{
+ return aa ? aa.length : 0;
+}
+
+/******************************
+ * Lookup *pkey in aa.
+ * Called only from implementation of (aa[key]) expressions when value is mutable.
+ * Params:
+ * paa = associative array opaque pointer
+ * ti = TypeInfo for the associative array
+ * valsz = ignored
+ * pkey = pointer to the key value
+ * Returns:
+ * if key was in the aa, a mutable pointer to the existing value.
+ * If key was not in the aa, a mutable pointer to newly inserted value which
+ * is set to all zeros
+ */
+extern (C) void* _aaGetY(scope ubyte** paa, const TypeInfo_AssociativeArray ti,
+ const size_t valsz, scope const void* pkey)
+{
+ bool found;
+ return _aaGetX(paa, ti, valsz, pkey, found);
+}
+
+/******************************
+ * Lookup *pkey in aa.
+ * Called only from implementation of require
+ * Params:
+ * paa = associative array opaque pointer
+ * ti = TypeInfo for the associative array
+ * valsz = ignored
+ * pkey = pointer to the key value
+ * found = true if the value was found
+ * Returns:
+ * if key was in the aa, a mutable pointer to the existing value.
+ * If key was not in the aa, a mutable pointer to newly inserted value which
+ * is set to all zeros
+ */
+extern (C) void* _aaGetX(scope ubyte** paa, const TypeInfo_AssociativeArray ti,
+ const size_t valsz, scope const void* pkey, out bool found)
+{
+
+ // lazily alloc implementation
+ AA aa = *cast(AA*)paa;
+ if (aa is null)
+ {
+ aa = new Impl(ti);
+ *cast(AA*)paa = aa;
+ }
+
+ // get hash and bucket for key
+ immutable hash = calcHash(pkey, ti.key);
+
+ // found a value => return it
+ if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
+ {
+ found = true;
+ return p.entry + aa.valoff;
+ }
+
+ auto p = aa.findSlotInsert(hash);
+ if (p.deleted)
+ --aa.deleted;
+ // check load factor and possibly grow
+ else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
+ {
+ aa.grow(ti.key);
+ p = aa.findSlotInsert(hash);
+ assert(p.empty);
+ }
+
+ // update search cache and allocate entry
+ aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
+ p.hash = hash;
+ p.entry = allocEntry(aa, pkey);
+ // postblit for key
+ if (aa.flags & Impl.Flags.keyHasPostblit)
+ {
+ import core.arsd.objectutils;
+ __doPostblit(p.entry, aa.keysz, unqualify(ti.key));
+ }
+ // return pointer to value
+ return p.entry + aa.valoff;
+}
+
+/******************************
+ * Lookup *pkey in aa.
+ * Called only from implementation of (aa[key]) expressions when value is not mutable.
+ * Params:
+ * aa = associative array opaque pointer
+ * keyti = TypeInfo for the key
+ * valsz = ignored
+ * pkey = pointer to the key value
+ * Returns:
+ * pointer to value if present, null otherwise
+ */
+extern (C) inout(void)* _aaGetRvalueX(inout ubyte** aa, scope const TypeInfo keyti, const size_t valsz,
+ scope const void* pkey)
+{
+ return _aaInX(aa, keyti, pkey);
+}
+
+/******************************
+ * Lookup *pkey in aa.
+ * Called only from implementation of (key in aa) expressions.
+ * Params:
+ * aa = associative array opaque pointer
+ * keyti = TypeInfo for the key
+ * pkey = pointer to the key value
+ * Returns:
+ * pointer to value if present, null otherwise
+ */
+extern (C) inout(void)* _aaInX(inout ubyte** _aa, scope const TypeInfo keyti, scope const void* pkey)
+{
+ import std.stdio;
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return null;
+
+ immutable hash = calcHash(pkey, keyti);
+ if (auto p = aa.findSlotLookup(hash, pkey, keyti))
+ return cast(inout)(p.entry + aa.valoff);
+ return null;
+}
+
+/// Delete entry scope const AA, return true if it was present
+extern (C) bool _aaDelX(ubyte* _aa, scope const TypeInfo keyti, scope const void* pkey)
+{
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return false;
+ immutable hash = calcHash(pkey, keyti);
+ if (auto p = aa.findSlotLookup(hash, pkey, keyti))
+ {
+ // clear entry
+ p.hash = HASH_DELETED;
+ p.entry = null;
+
+ ++aa.deleted;
+ // `shrink` reallocates, and allocating from a finalizer leads to
+ // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
+ if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM) // && !GC.inFinalizer() no GC so never in finalizer
+ aa.shrink(keyti);
+
+ return true;
+ }
+ return false;
+}
+
+/// Remove all elements from AA.
+extern (C) void _aaClear(ubyte* _aa) pure nothrow
+{
+ AA aa = cast(AA)_aa;
+ if (!aa.empty)
+ {
+ aa.clear();
+ }
+}
+
+/// Rehash AA
+extern (C) void* _aaRehash(ubyte** _paa, scope const TypeInfo keyti)
+{
+ AA* paa = cast(AA*)_paa;
+ AA aa = *paa;
+ if (!aa.empty)
+ aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
+ return aa;
+}
+
+/// Return a GC allocated array of all values
+extern (C) inout(void[]) _aaValues(inout ubyte* _aa, const size_t keysz, const size_t valsz,
+ const TypeInfo tiValueArray)
+{
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return null;
+
+ auto res = _d_newarrayU(tiValueArray, aa.length).ptr;
+ auto pval = res;
+
+ immutable off = aa.valoff;
+ foreach (b; aa.buckets[aa.firstUsed .. $])
+ {
+ if (!b.filled)
+ continue;
+ pval[0 .. valsz] = b.entry[off .. valsz + off];
+ pval += valsz;
+ }
+ // postblit is done in object.values
+ return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
+}
+
+/// Return a GC allocated array of all keys
+extern (C) inout(void[]) _aaKeys(inout ubyte* _aa, const size_t keysz, const TypeInfo tiKeyArray)
+{
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return null;
+
+ auto res = _d_newarrayU(tiKeyArray, aa.length).ptr;
+ auto pkey = res;
+
+ foreach (b; aa.buckets[aa.firstUsed .. $])
+ {
+ if (!b.filled)
+ continue;
+ pkey[0 .. keysz] = b.entry[0 .. keysz];
+ pkey += keysz;
+ }
+ // postblit is done in object.keys
+ return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
+}
+
+// opApply callbacks are extern(D)
+extern (D) alias dg_t = int delegate(void*);
+extern (D) alias dg2_t = int delegate(void*, void*);
+
+/// foreach opApply over all values
+extern (C) int _aaApply(ubyte* _aa, const size_t keysz, dg_t dg)
+{
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return 0;
+
+ immutable off = aa.valoff;
+ foreach (b; aa.buckets)
+ {
+ if (!b.filled)
+ continue;
+ if (auto res = dg(b.entry + off))
+ return res;
+ }
+ return 0;
+}
+
+/// foreach opApply over all key/value pairs
+extern (C) int _aaApply2(ubyte* _aa, const size_t keysz, dg2_t dg)
+{
+ AA aa = cast(AA)_aa;
+ if (aa.empty)
+ return 0;
+
+ immutable off = aa.valoff;
+ foreach (b; aa.buckets)
+ {
+ if (!b.filled)
+ continue;
+ if (auto res = dg(b.entry, b.entry + off))
+ return res;
+ }
+ return 0;
+}
+
+/** Construct an associative array of type ti from corresponding keys and values.
+ * Called for an AA literal `[k1:v1, k2:v2]`.
+ * Params:
+ * ti = TypeInfo for the associative array
+ * keys = array of keys
+ * vals = array of values
+ * Returns:
+ * A new associative array opaque pointer, or null if `keys` is empty.
+ */
+extern (C) ubyte* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
+ void[] vals)
+{
+ assert(keys.length == vals.length);
+
+ immutable keysz = ti.key.size;
+ immutable valsz = ti.value.size;
+ immutable length = keys.length;
+
+ if (!length)
+ return null;
+
+ auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM));
+
+ void* pkey = keys.ptr;
+ void* pval = vals.ptr;
+ immutable off = aa.valoff;
+ uint actualLength = 0;
+ foreach (_; 0 .. length)
+ {
+ immutable hash = calcHash(pkey, ti.key);
+ auto p = aa.findSlotLookup(hash, pkey, ti.key);
+ if (p is null)
+ {
+ p = aa.findSlotInsert(hash);
+ p.hash = hash;
+ p.entry = allocEntry(aa, pkey); // move key, no postblit
+ aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
+ actualLength++;
+ }
+ else if (aa.entryTI && hasDtor(ti.value))
+ {
+ // destroy existing value before overwriting it
+ ti.value.destroy(p.entry + off);
+ }
+ // set hash and blit value
+ auto pdst = p.entry + off;
+ pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit
+
+ pkey += keysz;
+ pval += valsz;
+ }
+ aa.used = actualLength;
+ return cast(ubyte*)aa;
+}
+
+/// compares 2 AAs for equality
+extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const ubyte* _aa1, scope const ubyte* _aa2)
+{
+ AA aa1 = cast(AA)_aa1;
+ AA aa2 = cast(AA)_aa2;
+ if (aa1 is aa2)
+ return true;
+
+ immutable len = _aaLen(aa1);
+ if (len != _aaLen(aa2))
+ return false;
+
+ if (!len) // both empty
+ return true;
+
+ import core.arsd.objectutils;
+
+ auto uti = unqualify(tiRaw); //unqualify
+ auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
+ // compare the entries
+ immutable off = aa1.valoff;
+ foreach (b1; aa1.buckets)
+ {
+ if (!b1.filled)
+ continue;
+ auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key);
+ if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off))
+ return false;
+ }
+ return true;
+}
+
+/// compute a hash
+extern (C) size_t _aaGetHash(scope const ubyte** _paa, scope const TypeInfo tiRaw) nothrow
+{
+ AA* paa = cast(AA*)_paa;
+ const AA aa = *paa;
+
+ if (aa.empty)
+ return 0;
+
+
+ import core.arsd.objectutils;
+ auto uti = unqualify(tiRaw);
+ auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
+ immutable off = aa.valoff;
+ auto keyHash = &ti.key.getHash;
+ auto valHash = &ti.value.getHash;
+
+ size_t h;
+ foreach (b; aa.buckets)
+ {
+ // use addition here, so that hash is independent of element order
+ if (b.filled)
+ h += hashOf(valHash(b.entry + off), keyHash(b.entry));
+ }
+
+ return h;
+}
+
+/**
+ * _aaRange implements a ForwardRange
+ */
+struct Range
+{
+ ubyte* impl;
+ size_t idx;
+ alias impl this;
+}
+
+extern (C) pure nothrow @nogc @trusted
+{
+ Range _aaRange(return scope ubyte* _aa)
+ {
+ AA aa = cast(AA)_aa;
+ if (!aa)
+ return Range();
+
+ foreach (i; aa.firstUsed .. aa.dim)
+ {
+ if (aa.buckets[i].filled)
+ return Range(cast(ubyte*)aa, i);
+ }
+ return Range(cast(ubyte*)aa, aa.dim);
+ }
+
+ bool _aaRangeEmpty(Range r)
+ {
+ return r.impl is null || r.idx >= (cast(Impl*)r.impl).dim;
+ }
+
+ void* _aaRangeFrontKey(Range r)
+ {
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= (cast(Impl*)r.impl).dim)
+ return null;
+ return (cast(Impl*)r.impl).buckets[r.idx].entry;
+ }
+
+ void* _aaRangeFrontValue(Range r)
+ {
+ Impl* ri = cast(Impl*)r.impl;
+ assert(!_aaRangeEmpty(r));
+ if (r.idx >= ri.dim)
+ return null;
+
+ auto entry = ri.buckets[r.idx].entry;
+ return entry is null ?
+ null :
+ (() @trusted { return entry + ri.valoff; } ());
+ }
+
+ void _aaRangePopFront(ref Range r)
+ {
+ Impl* ri = (cast(Impl*)r.impl);
+ if (r.idx >= ri.dim) return;
+ for (++r.idx; r.idx < ri.dim; ++r.idx)
+ {
+ if (ri.buckets[r.idx].filled)
+ break;
+ }
+ }
+}
diff --git a/external/arsd-webassembly/core/arsd/memory_allocation.d b/external/arsd-webassembly/core/arsd/memory_allocation.d
new file mode 100644
index 0000000..dd6ac92
--- /dev/null
+++ b/external/arsd-webassembly/core/arsd/memory_allocation.d
@@ -0,0 +1,260 @@
+module core.arsd.memory_allocation;
+
+
+
+private __gshared ubyte* nextFree;
+private __gshared size_t memorySize; // in units of 64 KB pages
+
+// ldc defines this, used to find where wasm memory begins
+private extern extern(C) ubyte __heap_base;
+// ---unused--- -- stack grows down -- -- heap here --
+// this is less than __heap_base. memory map 0 ... __data_end ... __heap_base ... end of memory
+private extern extern(C) ubyte __data_end;
+
+// llvm intrinsics {
+ /+
+ mem must be 0 (it is index of memory thing)
+ delta is in 64 KB pages
+ return OLD size in 64 KB pages, or size_t.max if it failed.
+ +/
+ pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
+ private int llvm_wasm_memory_grow(int mem, int delta);
+
+
+ // in 64 KB pages
+ pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
+ private int llvm_wasm_memory_size(int mem);
+// }
+// debug
+void printBlockDebugInfo(AllocatedBlock* block) {
+ import std.stdio;
+ writeln(block.blockSize, " ", block.flags, " ", block.checkChecksum() ? "OK" : "X", " ");
+ if(block.checkChecksum())
+ writeln(cast(size_t)((cast(ubyte*) (block + 2)) + block.blockSize), " ", block.file, " : ", block.line);
+}
+
+
+// debug
+export extern(C) void printBlockDebugInfo(void* ptr) {
+ if(ptr is null) {
+ foreach(block; AllocatedBlock) {
+ printBlockDebugInfo(block);
+ }
+ return;
+ }
+
+ // otherwise assume it is a pointer returned from malloc
+
+ auto block = (cast(AllocatedBlock*) ptr) - 1;
+ if(ptr is null)
+ block = cast(AllocatedBlock*) &__heap_base;
+
+ printBlockDebugInfo(block);
+}
+
+
+export extern(C) ubyte* bridge_malloc(size_t sz) {
+ return malloc(sz).ptr;
+}
+
+
+
+align(16)
+struct AllocatedBlock {
+ enum Magic = 0x731a_9bec;
+ enum Flags {
+ inUse = 1,
+ unique = 2,
+ }
+
+ size_t blockSize;
+ size_t flags;
+ size_t magic;
+ size_t checksum;
+
+ size_t used; // the amount actually requested out of the block; used for assumeSafeAppend
+
+ /* debug */
+ string file;
+ size_t line;
+
+ // note this struct MUST align each alloc on an 8 byte boundary or JS is gonna throw bullshit
+
+ void populateChecksum() {
+ checksum = blockSize ^ magic;
+ }
+
+ bool checkChecksum() const @nogc {
+ return magic == Magic && checksum == (blockSize ^ magic);
+ }
+
+ ubyte[] dataSlice() return {
+ return ((cast(ubyte*) &this) + typeof(this).sizeof)[0 .. blockSize];
+ }
+
+ static int opApply(scope int delegate(AllocatedBlock*) dg) {
+ if(nextFree is null)
+ return 0;
+ ubyte* next = &__heap_base;
+ AllocatedBlock* block = cast(AllocatedBlock*) next;
+ while(block.checkChecksum()) {
+ if(auto result = dg(block))
+ return result;
+ next += AllocatedBlock.sizeof;
+ next += block.blockSize;
+ block = cast(AllocatedBlock*) next;
+ }
+
+ return 0;
+ }
+}
+
+static assert(AllocatedBlock.sizeof % 16 == 0);
+
+
+
+private bool growMemoryIfNeeded(size_t sz) @trusted {
+ if(cast(size_t) nextFree + AllocatedBlock.sizeof + sz >= memorySize * 64*1024) {
+ if(llvm_wasm_memory_grow(0, 4) == size_t.max)
+ assert(0, "Out of memory"); // out of memory
+
+ memorySize = llvm_wasm_memory_size(0);
+
+ return true;
+ }
+
+ return false;
+}
+
+void free(ubyte* ptr) @nogc @trusted {
+ auto block = (cast(AllocatedBlock*) ptr) - 1;
+ if(!block.checkChecksum())
+ assert(false, "Could not check block on free");
+
+ block.used = 0;
+ block.flags = 0;
+
+ // last one
+ if(ptr + block.blockSize == nextFree) {
+ nextFree = cast(ubyte*) block;
+ assert(cast(size_t)nextFree % 16 == 0);
+ }
+}
+
+
+ubyte[] malloc(size_t sz, string file = __FILE__, size_t line = __LINE__) @trusted {
+ // lol bumping that pointer
+ if(nextFree is null) {
+ nextFree = &__heap_base; // seems to be 75312
+ assert(cast(size_t)nextFree % 16 == 0);
+ memorySize = llvm_wasm_memory_size(0);
+ }
+
+ while(growMemoryIfNeeded(sz)) {}
+
+ auto base = cast(AllocatedBlock*) nextFree;
+
+ auto blockSize = sz;
+ if(auto val = blockSize % 16)
+ blockSize += 16 - val; // does NOT include this metadata section!
+
+ // debug list allocations
+ //import std.stdio; writeln(file, ":", line, " / ", sz, " +", blockSize);
+
+ base.blockSize = blockSize;
+ base.flags = AllocatedBlock.Flags.inUse;
+ // these are just to make it more reliable to detect this header by backtracking through the pointer from a random array.
+ // otherwise it'd prolly follow the linked list from the beginning every time or make a free list or something. idk tbh.
+ base.magic = AllocatedBlock.Magic;
+ base.populateChecksum();
+
+ base.used = sz;
+
+ // debug
+ base.file = file;
+ base.line = line;
+
+ nextFree += AllocatedBlock.sizeof;
+
+ auto ret = nextFree;
+
+ nextFree += blockSize;
+
+ //writeln(cast(size_t) nextFree);
+ //import std.stdio; writeln(cast(size_t) ret, " of ", sz, " rounded to ", blockSize);
+ //writeln(file, ":", line);
+ assert(cast(size_t) ret % 8 == 0);
+
+ return ret[0 .. sz];
+}
+
+
+ubyte[] calloc(size_t count, size_t size, string file = __FILE__, size_t line = __LINE__) @trusted
+{
+ auto ret = malloc(count*size,file,line);
+ ret[0..$] = 0;
+ return ret;
+}
+
+
+ubyte[] realloc(ubyte* ptr, size_t newSize, string file = __FILE__, size_t line = __LINE__) @trusted {
+ if(ptr is null)
+ return malloc(newSize, file, line);
+
+ auto block = (cast(AllocatedBlock*) ptr) - 1;
+ if(!block.checkChecksum())
+ assert(false, "Could not check block while realloc");
+
+ // block.populateChecksum();
+ if(newSize <= block.blockSize) {
+ block.used = newSize;
+ return ptr[0 .. newSize];
+ } else {
+ // FIXME: see if we can extend teh block into following free space before resorting to malloc
+
+ if(ptr + block.blockSize == nextFree) {
+ while(growMemoryIfNeeded(newSize)) {}
+
+ size_t blockSize = newSize;
+ if(const over = blockSize % 16)
+ blockSize+= 16 - over;
+
+ block.blockSize = blockSize;
+ block.used = newSize;
+ block.populateChecksum();
+ nextFree = ptr + block.blockSize;
+ assert(cast(size_t)nextFree % 16 == 0);
+ return ptr[0 .. newSize];
+ }
+
+ auto newThing = malloc(newSize);
+ newThing[0 .. block.used] = ptr[0 .. block.used];
+
+ if(block.flags & AllocatedBlock.Flags.unique) {
+ // if we do malloc, this means we are allowed to free the existing block
+ free(ptr);
+ }
+
+ assert(cast(size_t) newThing.ptr % 16 == 0);
+
+ return newThing;
+ }
+}
+
+/**
+* If the ptr isn't owned by the runtime, it will completely malloc the data (instead of realloc)
+* and copy its old content.
+*/
+ubyte[] realloc(ubyte[] ptr, size_t newSize, string file = __FILE__, size_t line = __LINE__) @trusted
+{
+ if(ptr is null)
+ return malloc(newSize, file, line);
+ auto block = (cast(AllocatedBlock*) ptr) - 1;
+ if(!block.checkChecksum())
+ {
+ auto ret = malloc(newSize, file, line);
+ ret[0..ptr.length] = ptr[]; //Don't clear ptr memory as it can't be clear.
+ return ret;
+ }
+ else return realloc(ptr.ptr, newSize, file, line);
+}
\ No newline at end of file
diff --git a/external/arsd-webassembly/core/arsd/objectutils.d b/external/arsd-webassembly/core/arsd/objectutils.d
new file mode 100644
index 0000000..5c41a94
--- /dev/null
+++ b/external/arsd-webassembly/core/arsd/objectutils.d
@@ -0,0 +1,68 @@
+module core.arsd.objectutils;
+
+///Provides only __doPostblit and hasPostblit for making the code simpler.
+size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
+{
+ if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
+ {
+ auto sti = cast(TypeInfo_Struct)cast(void*)ti;
+ if (sti.xdtor)
+ return size_t.sizeof;
+ }
+ return 0;
+}
+// strip const/immutable/shared/inout from type info
+inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
+{
+ TypeInfo ti = cast() cti;
+ while (ti)
+ {
+ // avoid dynamic type casts
+ auto tti = typeid(ti);
+ if (tti is typeid(TypeInfo_Const))
+ ti = (cast(TypeInfo_Const)cast(void*)ti).base;
+ else if (tti is typeid(TypeInfo_Invariant))
+ ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
+ else if (tti is typeid(TypeInfo_Shared))
+ ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
+ else if (tti is typeid(TypeInfo_Inout))
+ ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
+ else
+ break;
+ }
+ return ti;
+}
+
+bool hasPostblit(in TypeInfo ti) nothrow pure
+{
+ return (&ti.postblit).funcptr !is &TypeInfo.postblit;
+}
+
+void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
+{
+ if (!hasPostblit(ti))
+ return;
+
+ if (auto tis = cast(TypeInfo_Struct)ti)
+ {
+ // this is a struct, check the xpostblit member
+ auto pblit = tis.xpostblit;
+ if (!pblit)
+ // postblit not specified, no point in looping.
+ return;
+
+ // optimized for struct, call xpostblit directly for each element
+ immutable size = ti.size;
+ const eptr = ptr + len;
+ for (;ptr < eptr;ptr += size)
+ pblit(ptr);
+ }
+ else
+ {
+ // generic case, call the typeinfo's postblit function
+ immutable size = ti.size;
+ const eptr = ptr + len;
+ for (;ptr < eptr;ptr += size)
+ ti.postblit(ptr);
+ }
+}
diff --git a/external/arsd-webassembly/core/arsd/utf_decoding.d b/external/arsd-webassembly/core/arsd/utf_decoding.d
new file mode 100644
index 0000000..cf14a5d
--- /dev/null
+++ b/external/arsd-webassembly/core/arsd/utf_decoding.d
@@ -0,0 +1,444 @@
+module core.arsd.utf_decoding;
+
+
+import core.internal.utf : decode, toUTF8;
+
+/**********************************************/
+/* 1 argument versions */
+
+/**
+Delegate type corresponding to transformed loop body
+
+The parameter is a pointer to the current `char`, `wchar` or `dchar`
+
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg_t = int delegate(void* c);
+
+// Note: dg is extern(D), but _aApplycd() is extern(C)
+
+/**
+Loop over a string while changing the UTF encoding
+
+There are 6 combinations of conversions between `char`, `wchar`, and `dchar`,
+and 2 of each of those.
+
+The naming convention is as follows:
+
+_aApply{c,d,w}{c,d,w}{1,2}
+
+The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type.
+
+- c = `char`
+- w = `wchar`
+- d = `dchar`
+
+The `1` variant only produces the character, the `2` variant also produces a loop index.
+
+Examples:
+---
+void main()
+{
+ string str;
+ wtring wstr;
+ dstring dstr;
+
+ foreach (dchar c; str) {}
+ // _aApplycd1
+
+ foreach (wchar c; dstr) {}
+ // _aApplydw1
+
+ foreach (i, wchar c; str) {}
+ // _aApplycw2
+
+ foreach (wchar w; wstr) {}
+ // no conversion
+}
+---
+
+Params:
+ aa = input string
+ dg = foreach body transformed into a delegate, similar to `opApply`
+
+Returns:
+ non-zero when the loop was exited through a `break`
+*/
+extern (C) int _aApplycd1(in char[] aa, dg_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplycd1(), len = %d\n", len);
+ for (size_t i = 0; i < len; )
+ {
+ dchar d = aa[i];
+ if (d & 0x80)
+ d = decode(aa, i);
+ else
+ ++i;
+ result = dg(cast(void *)&d);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+
+
+/// ditto
+extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplywd1(), len = %d\n", len);
+ for (size_t i = 0; i < len; )
+ {
+ dchar d = aa[i];
+ if (d >= 0xD800)
+ d = decode(aa, i);
+ else
+ ++i;
+ result = dg(cast(void *)&d);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplycw1(in char[] aa, dg_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplycw1(), len = %d\n", len);
+ for (size_t i = 0; i < len; )
+ {
+ wchar w = aa[i];
+ if (w & 0x80)
+ {
+ dchar d = decode(aa, i);
+ if (d <= 0xFFFF)
+ w = cast(wchar) d;
+ else
+ {
+ w = cast(wchar)((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ result = dg(cast(void *)&w);
+ if (result)
+ break;
+ w = cast(wchar)(((d - 0x10000) & 0x3FF) + 0xDC00);
+ }
+ }
+ else
+ ++i;
+ result = dg(cast(void *)&w);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplywc1(), len = %d\n", len);
+ for (size_t i = 0; i < len; )
+ {
+ wchar w = aa[i];
+ if (w & ~0x7F)
+ {
+ char[4] buf = void;
+
+ dchar d = decode(aa, i);
+ auto b = toUTF8(buf, d);
+ foreach (char c2; b)
+ {
+ result = dg(cast(void *)&c2);
+ if (result)
+ return result;
+ }
+ }
+ else
+ {
+ char c = cast(char)w;
+ ++i;
+ result = dg(cast(void *)&c);
+ if (result)
+ break;
+ }
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
+{
+ int result;
+
+ debug(apply) printf("_aApplydc1(), len = %d\n", aa.length);
+ foreach (dchar d; aa)
+ {
+ if (d & ~0x7F)
+ {
+ char[4] buf = void;
+
+ auto b = toUTF8(buf, d);
+ foreach (char c2; b)
+ {
+ result = dg(cast(void *)&c2);
+ if (result)
+ return result;
+ }
+ }
+ else
+ {
+ char c = cast(char)d;
+ result = dg(cast(void *)&c);
+ if (result)
+ break;
+ }
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
+{
+ int result;
+
+ debug(apply) printf("_aApplydw1(), len = %d\n", aa.length);
+ foreach (dchar d; aa)
+ {
+ wchar w;
+
+ if (d <= 0xFFFF)
+ w = cast(wchar) d;
+ else
+ {
+ w = cast(wchar)((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ result = dg(cast(void *)&w);
+ if (result)
+ break;
+ w = cast(wchar)(((d - 0x10000) & 0x3FF) + 0xDC00);
+ }
+ result = dg(cast(void *)&w);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+
+/****************************************************************************/
+/* 2 argument versions */
+
+/**
+Delegate type corresponding to transformed loop body
+
+Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
+
+Returns: non-zero when a `break` statement is hit
+*/
+extern (D) alias dg2_t = int delegate(void* i, void* c);
+
+// Note: dg is extern(D), but _aApplycd2() is extern(C)
+
+/**
+Variants of _aApplyXXX that include a loop index.
+*/
+extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplycd2(), len = %d\n", len);
+ size_t n;
+ for (size_t i = 0; i < len; i += n)
+ {
+ dchar d = aa[i];
+ if (d & 0x80)
+ {
+ n = i;
+ d = decode(aa, n);
+ n -= i;
+ }
+ else
+ n = 1;
+ result = dg(&i, cast(void *)&d);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+/// ditto
+extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplywd2(), len = %d\n", len);
+ size_t n;
+ for (size_t i = 0; i < len; i += n)
+ {
+ dchar d = aa[i];
+ if (d & ~0x7F)
+ {
+ n = i;
+ d = decode(aa, n);
+ n -= i;
+ }
+ else
+ n = 1;
+ result = dg(&i, cast(void *)&d);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+/// ditto
+extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplycw2(), len = %d\n", len);
+ size_t n;
+ for (size_t i = 0; i < len; i += n)
+ {
+ wchar w = aa[i];
+ if (w & 0x80)
+ {
+ n = i;
+ dchar d = decode(aa, n);
+ n -= i;
+ if (d <= 0xFFFF)
+ w = cast(wchar) d;
+ else
+ {
+ w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ result = dg(&i, cast(void *)&w);
+ if (result)
+ break;
+ w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
+ }
+ }
+ else
+ n = 1;
+ result = dg(&i, cast(void *)&w);
+ if (result)
+ break;
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplywc2(), len = %d\n", len);
+ size_t n;
+ for (size_t i = 0; i < len; i += n)
+ {
+ wchar w = aa[i];
+ if (w & ~0x7F)
+ {
+ char[4] buf = void;
+
+ n = i;
+ dchar d = decode(aa, n);
+ n -= i;
+ auto b = toUTF8(buf, d);
+ foreach (char c2; b)
+ {
+ result = dg(&i, cast(void *)&c2);
+ if (result)
+ return result;
+ }
+ }
+ else
+ {
+ char c = cast(char)w;
+ n = 1;
+ result = dg(&i, cast(void *)&c);
+ if (result)
+ break;
+ }
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
+{
+ int result;
+ size_t len = aa.length;
+
+ debug(apply) printf("_aApplydc2(), len = %d\n", len);
+ for (size_t i = 0; i < len; i++)
+ {
+ dchar d = aa[i];
+ if (d & ~0x7F)
+ {
+ char[4] buf = void;
+
+ auto b = toUTF8(buf, d);
+ foreach (char c2; b)
+ {
+ result = dg(&i, cast(void *)&c2);
+ if (result)
+ return result;
+ }
+ }
+ else
+ {
+ char c = cast(char)d;
+ result = dg(&i, cast(void *)&c);
+ if (result)
+ break;
+ }
+ }
+ return result;
+}
+
+
+/// ditto
+extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
+{ int result;
+
+ debug(apply) printf("_aApplydw2(), len = %d\n", aa.length);
+ foreach (size_t i, dchar d; aa)
+ {
+ wchar w;
+ auto j = i;
+
+ if (d <= 0xFFFF)
+ w = cast(wchar) d;
+ else
+ {
+ w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ result = dg(&j, cast(void *)&w);
+ if (result)
+ break;
+ w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
+ }
+ result = dg(&j, cast(void *)&w);
+ if (result)
+ break;
+ }
+ return result;
+}
diff --git a/external/arsd-webassembly/core/internal/utf.d b/external/arsd-webassembly/core/internal/utf.d
new file mode 100644
index 0000000..43aab77
--- /dev/null
+++ b/external/arsd-webassembly/core/internal/utf.d
@@ -0,0 +1,892 @@
+/********************************************
+ * Encode and decode UTF-8, UTF-16 and UTF-32 strings.
+ *
+ * For Win32 systems, the C wchar_t type is UTF-16 and corresponds to the D
+ * wchar type.
+ * For Posix systems, the C wchar_t type is UTF-32 and corresponds to
+ * the D utf.dchar type.
+ *
+ * UTF character support is restricted to (\u0000 <= character <= \U0010FFFF).
+ *
+ * See_Also:
+ * $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)
+ * $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)
+ * $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
+ *
+ * Copyright: Copyright Digital Mars 2003 - 2016.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC core/internal/_utf.d)
+ */
+
+module core.internal.utf;
+
+extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @trusted
+{
+ _d_assert_msg("onUnicodeError: "~msg, file, line);
+}
+
+/*******************************
+ * Test if c is a valid UTF-32 character.
+ *
+ * \uFFFE and \uFFFF are considered valid by this function,
+ * as they are permitted for internal use by an application,
+ * but they are not allowed for interchange by the Unicode standard.
+ *
+ * Returns: true if it is, false if not.
+ */
+
+@safe @nogc pure nothrow
+bool isValidDchar(dchar c)
+{
+ /* Note: FFFE and FFFF are specifically permitted by the
+ * Unicode standard for application internal use, but are not
+ * allowed for interchange.
+ * (thanks to Arcane Jill)
+ */
+
+ return c < 0xD800 ||
+ (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/);
+}
+
+unittest
+{
+ debug(utf) printf("utf.isValidDchar.unittest\n");
+ assert(isValidDchar(cast(dchar)'a') == true);
+ assert(isValidDchar(cast(dchar)0x1FFFFF) == false);
+}
+
+
+
+static immutable UTF8stride =
+[
+ cast(ubyte)
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF,
+];
+
+/**
+ * stride() returns the length of a UTF-8 sequence starting at index i
+ * in string s.
+ * Returns:
+ * The number of bytes in the UTF-8 sequence or
+ * 0xFF meaning s[i] is not the start of of UTF-8 sequence.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope char[] s, size_t i)
+{
+ return UTF8stride[s[i]];
+}
+
+/**
+ * stride() returns the length of a UTF-16 sequence starting at index i
+ * in string s.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope wchar[] s, size_t i)
+{ uint u = s[i];
+ return 1 + (u >= 0xD800 && u <= 0xDBFF);
+}
+
+/**
+ * stride() returns the length of a UTF-32 sequence starting at index i
+ * in string s.
+ * Returns: The return value will always be 1.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope dchar[] s, size_t i)
+{
+ return 1;
+}
+
+/*******************************************
+ * Given an index i into an array of characters s[],
+ * and assuming that index i is at the start of a UTF character,
+ * determine the number of UCS characters up to that index i.
+ */
+@safe
+size_t toUCSindex(const scope char[] s, size_t i)
+{
+ size_t n;
+ size_t j;
+
+ for (j = 0; j < i; )
+ {
+ j += stride(s, j);
+ n++;
+ }
+ if (j > i)
+ {
+ onUnicodeError("invalid UTF-8 sequence", j);
+ }
+ return n;
+}
+
+/** ditto */
+@safe
+size_t toUCSindex(const scope wchar[] s, size_t i)
+{
+ size_t n;
+ size_t j;
+
+ for (j = 0; j < i; )
+ {
+ j += stride(s, j);
+ n++;
+ }
+ if (j > i)
+ {
+ onUnicodeError("invalid UTF-16 sequence", j);
+ }
+ return n;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUCSindex(const scope dchar[] s, size_t i)
+{
+ return i;
+}
+
+/******************************************
+ * Given a UCS index n into an array of characters s[], return the UTF index.
+ */
+@safe
+size_t toUTFindex(const scope char[] s, size_t n)
+{
+ size_t i;
+
+ while (n--)
+ {
+ uint j = UTF8stride[s[i]];
+ if (j == 0xFF)
+ onUnicodeError("invalid UTF-8 sequence", i);
+ i += j;
+ }
+ return i;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUTFindex(const scope wchar[] s, size_t n)
+{
+ size_t i;
+
+ while (n--)
+ { wchar u = s[i];
+
+ i += 1 + (u >= 0xD800 && u <= 0xDBFF);
+ }
+ return i;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUTFindex(const scope dchar[] s, size_t n)
+{
+ return n;
+}
+
+/* =================== Decode ======================= */
+
+/***************
+ * Decodes and returns character starting at s[idx]. idx is advanced past the
+ * decoded character. If the character is not well formed, a UtfException is
+ * thrown and idx remains unchanged.
+ */
+@safe
+dchar decode(const scope char[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ out (result)
+ {
+ assert(isValidDchar(result));
+ }
+ do
+ {
+ size_t len = s.length;
+ dchar V;
+ size_t i = idx;
+ char u = s[i];
+
+ if (u & 0x80)
+ { uint n;
+ char u2;
+
+ /* The following encodings are valid, except for the 5 and 6 byte
+ * combinations:
+ * 0xxxxxxx
+ * 110xxxxx 10xxxxxx
+ * 1110xxxx 10xxxxxx 10xxxxxx
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ */
+ for (n = 1; ; n++)
+ {
+ if (n > 4)
+ goto Lerr; // only do the first 4 of 6 encodings
+ if (((u << n) & 0x80) == 0)
+ {
+ if (n == 1)
+ goto Lerr;
+ break;
+ }
+ }
+
+ // Pick off (7 - n) significant bits of B from first byte of octet
+ V = cast(dchar)(u & ((1 << (7 - n)) - 1));
+
+ if (i + (n - 1) >= len)
+ goto Lerr; // off end of string
+
+ /* The following combinations are overlong, and illegal:
+ * 1100000x (10xxxxxx)
+ * 11100000 100xxxxx (10xxxxxx)
+ * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
+ * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
+ * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ */
+ u2 = s[i + 1];
+ if ((u & 0xFE) == 0xC0 ||
+ (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
+ (u == 0xF0 && (u2 & 0xF0) == 0x80) ||
+ (u == 0xF8 && (u2 & 0xF8) == 0x80) ||
+ (u == 0xFC && (u2 & 0xFC) == 0x80))
+ goto Lerr; // overlong combination
+
+ for (uint j = 1; j != n; j++)
+ {
+ u = s[i + j];
+ if ((u & 0xC0) != 0x80)
+ goto Lerr; // trailing bytes are 10xxxxxx
+ V = (V << 6) | (u & 0x3F);
+ }
+ if (!isValidDchar(V))
+ goto Lerr;
+ i += n;
+ }
+ else
+ {
+ V = cast(dchar) u;
+ i++;
+ }
+
+ idx = i;
+ return V;
+
+ Lerr:
+ onUnicodeError("invalid UTF-8 sequence", i);
+ return V; // dummy return
+}
+
+
+/** ditto */
+@safe
+dchar decode(const scope wchar[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ out (result)
+ {
+ assert(isValidDchar(result));
+ }
+ do
+ {
+ string msg;
+ dchar V;
+ size_t i = idx;
+ uint u = s[i];
+
+ if (u & ~0x7F)
+ { if (u >= 0xD800 && u <= 0xDBFF)
+ { uint u2;
+
+ if (i + 1 == s.length)
+ { msg = "surrogate UTF-16 high value past end of string";
+ goto Lerr;
+ }
+ u2 = s[i + 1];
+ if (u2 < 0xDC00 || u2 > 0xDFFF)
+ { msg = "surrogate UTF-16 low value out of range";
+ goto Lerr;
+ }
+ u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
+ i += 2;
+ }
+ else if (u >= 0xDC00 && u <= 0xDFFF)
+ { msg = "unpaired surrogate UTF-16 value";
+ goto Lerr;
+ }
+ else if (u == 0xFFFE || u == 0xFFFF)
+ { msg = "illegal UTF-16 value";
+ goto Lerr;
+ }
+ else
+ i++;
+ }
+ else
+ {
+ i++;
+ }
+
+ idx = i;
+ return cast(dchar)u;
+
+ Lerr:
+ onUnicodeError(msg, i);
+ return cast(dchar)u; // dummy return
+ }
+
+/** ditto */
+@safe
+dchar decode(const scope dchar[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ do
+ {
+ size_t i = idx;
+ dchar c = s[i];
+
+ if (!isValidDchar(c))
+ goto Lerr;
+ idx = i + 1;
+ return c;
+
+ Lerr:
+ onUnicodeError("invalid UTF-32 value", i);
+ return c; // dummy return
+ }
+
+
+/* =================== Encode ======================= */
+
+/*******************************
+ * Encodes character c and appends it to array s[].
+ */
+@safe pure nothrow
+void encode(ref char[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ char[] r = s;
+
+ if (c <= 0x7F)
+ {
+ r ~= cast(char) c;
+ }
+ else
+ {
+ char[4] buf = void;
+ uint L;
+
+ if (c <= 0x7FF)
+ {
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ L = 2;
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[0] = cast(char)(0xE0 | (c >> 12));
+ buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[2] = cast(char)(0x80 | (c & 0x3F));
+ L = 3;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[0] = cast(char)(0xF0 | (c >> 18));
+ buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[3] = cast(char)(0x80 | (c & 0x3F));
+ L = 4;
+ }
+ else
+ {
+ assert(0);
+ }
+ r ~= buf[0 .. L];
+ }
+ s = r;
+ }
+
+unittest
+{
+ debug(utf) printf("utf.encode.unittest\n");
+
+ char[] s = "abcd".dup;
+ encode(s, cast(dchar)'a');
+ assert(s.length == 5);
+ assert(s == "abcda");
+
+ encode(s, cast(dchar)'\u00A9');
+ assert(s.length == 7);
+ assert(s == "abcda\xC2\xA9");
+ //assert(s == "abcda\u00A9"); // BUG: fix compiler
+
+ encode(s, cast(dchar)'\u2260');
+ assert(s.length == 10);
+ assert(s == "abcda\xC2\xA9\xE2\x89\xA0");
+}
+
+/** ditto */
+@safe pure nothrow
+void encode(ref wchar[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ wchar[] r = s;
+
+ if (c <= 0xFFFF)
+ {
+ r ~= cast(wchar) c;
+ }
+ else
+ {
+ wchar[2] buf = void;
+
+ buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
+ r ~= buf;
+ }
+ s = r;
+ }
+
+/** ditto */
+@safe pure nothrow
+void encode(ref dchar[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ s ~= c;
+ }
+
+/**
+Returns the code length of $(D c) in the encoding using $(D C) as a
+code point. The code is returned in character count, not in bytes.
+ */
+@safe pure nothrow @nogc
+ubyte codeLength(C)(dchar c)
+{
+ static if (C.sizeof == 1)
+ {
+ if (c <= 0x7F) return 1;
+ if (c <= 0x7FF) return 2;
+ if (c <= 0xFFFF) return 3;
+ if (c <= 0x10FFFF) return 4;
+ assert(false);
+ }
+ else static if (C.sizeof == 2)
+ {
+ return c <= 0xFFFF ? 1 : 2;
+ }
+ else
+ {
+ static assert(C.sizeof == 4);
+ return 1;
+ }
+}
+
+/* =================== Validation ======================= */
+
+/***********************************
+Checks to see if string is well formed or not. $(D S) can be an array
+ of $(D char), $(D wchar), or $(D dchar). Returns $(D false) if it is not.
+ Use to check all untrusted input for correctness.
+ */
+@safe
+bool isValidString(S)(const scope S s)
+{
+ auto len = s.length;
+ for (size_t i = 0; i < len; )
+ {
+ decode(s, i);
+ }
+
+ return true;
+}
+
+/* =================== Conversion to UTF8 ======================= */
+
+@safe nothrow @nogc
+char[] toUTF8(return scope char[] buf, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ if (c <= 0x7F)
+ {
+ buf[0] = cast(char) c;
+ return buf[0 .. 1];
+ }
+ else if (c <= 0x7FF)
+ {
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 2];
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[0] = cast(char)(0xE0 | (c >> 12));
+ buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[2] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 3];
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[0] = cast(char)(0xF0 | (c >> 18));
+ buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[3] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 4];
+ }
+ assert(0);
+ }
+
+/*******************
+ * Encodes string s into UTF-8 and returns the encoded string.
+ */
+@safe
+string toUTF8(return scope string s)
+ in
+ {
+ assert(isValidString(s));
+ }
+ do
+ {
+ return s;
+ }
+
+/** ditto */
+@trusted
+string toUTF8(const scope wchar[] s)
+{
+ char[] r;
+ size_t i;
+ size_t slen = s.length;
+
+ r.length = slen;
+
+ for (i = 0; i < slen; i++)
+ { wchar c = s[i];
+
+ if (c <= 0x7F)
+ r[i] = cast(char)c; // fast path for ascii
+ else
+ {
+ r.length = i;
+ foreach (dchar ch; s[i .. slen])
+ {
+ encode(r, ch);
+ }
+ break;
+ }
+ }
+ return cast(string)r;
+}
+
+/** ditto */
+@trusted
+string toUTF8(const scope dchar[] s)
+{
+ char[] r;
+ size_t i;
+ size_t slen = s.length;
+
+ r.length = slen;
+
+ for (i = 0; i < slen; i++)
+ { dchar c = s[i];
+
+ if (c <= 0x7F)
+ r[i] = cast(char)c; // fast path for ascii
+ else
+ {
+ r.length = i;
+ foreach (dchar d; s[i .. slen])
+ {
+ encode(r, d);
+ }
+ break;
+ }
+ }
+ return cast(string)r;
+}
+
+/* =================== Conversion to UTF16 ======================= */
+
+@safe pure nothrow @nogc
+wchar[] toUTF16(return scope wchar[] buf, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ if (c <= 0xFFFF)
+ {
+ buf[0] = cast(wchar) c;
+ return buf[0 .. 1];
+ }
+ else
+ {
+ buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
+ return buf[0 .. 2];
+ }
+ }
+
+/****************
+ * Encodes string s into UTF-16 and returns the encoded string.
+ * toUTF16z() is suitable for calling the 'W' functions in the Win32 API that take
+ * an LPWSTR or LPCWSTR argument.
+ */
+@trusted
+wstring toUTF16(const scope char[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return ""w;
+ r.reserve(slen);
+ }
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c <= 0x7F)
+ {
+ i++;
+ r ~= cast(wchar)c;
+ }
+ else
+ {
+ c = decode(s, i);
+ encode(r, c);
+ }
+ }
+ return cast(wstring)r;
+}
+
+alias const(wchar)* wptr;
+/** ditto */
+@trusted
+wptr toUTF16z(const scope char[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return &"\0"w[0];
+ r.reserve(slen + 1);
+ }
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c <= 0x7F)
+ {
+ i++;
+ r ~= cast(wchar)c;
+ }
+ else
+ {
+ c = decode(s, i);
+ encode(r, c);
+ }
+ }
+ r ~= '\000';
+ return &r[0];
+}
+
+/** ditto */
+@safe
+wstring toUTF16(return scope wstring s)
+ in
+ {
+ assert(isValidString(s));
+ }
+ do
+ {
+ return s;
+ }
+
+/** ditto */
+@trusted
+wstring toUTF16(const scope dchar[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return ""w;
+ r.reserve(slen);
+ }
+ for (size_t i = 0; i < slen; i++)
+ {
+ encode(r, s[i]);
+ }
+ return cast(wstring)r;
+}
+
+/* =================== Conversion to UTF32 ======================= */
+
+/*****
+ * Encodes string s into UTF-32 and returns the encoded string.
+ */
+@trusted
+dstring toUTF32(const scope char[] s)
+{
+ dchar[] r;
+ size_t slen = s.length;
+ size_t j = 0;
+
+ r.length = slen; // r[] will never be longer than s[]
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c >= 0x80)
+ c = decode(s, i);
+ else
+ i++; // c is ascii, no need for decode
+ r[j++] = c;
+ }
+ return cast(dstring)r[0 .. j];
+}
+
+/** ditto */
+@trusted
+dstring toUTF32(const scope wchar[] s)
+{
+ dchar[] r;
+ size_t slen = s.length;
+ size_t j = 0;
+
+ r.length = slen; // r[] will never be longer than s[]
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c >= 0x80)
+ c = decode(s, i);
+ else
+ i++; // c is ascii, no need for decode
+ r[j++] = c;
+ }
+ return cast(dstring)r[0 .. j];
+}
+
+/** ditto */
+@safe
+dstring toUTF32(return scope dstring s)
+ in
+ {
+ assert(isValidString(s));
+ }
+ do
+ {
+ return s;
+ }
+
+/* ================================ tests ================================== */
+
+unittest
+{
+ debug(utf) printf("utf.toUTF.unittest\n");
+
+ auto c = "hello"c[];
+ auto w = toUTF16(c);
+ assert(w == "hello");
+ auto d = toUTF32(c);
+ assert(d == "hello");
+
+ c = toUTF8(w);
+ assert(c == "hello");
+ d = toUTF32(w);
+ assert(d == "hello");
+
+ c = toUTF8(d);
+ assert(c == "hello");
+ w = toUTF16(d);
+ assert(w == "hello");
+
+
+ c = "hel\u1234o";
+ w = toUTF16(c);
+ assert(w == "hel\u1234o");
+ d = toUTF32(c);
+ assert(d == "hel\u1234o");
+
+ c = toUTF8(w);
+ assert(c == "hel\u1234o");
+ d = toUTF32(w);
+ assert(d == "hel\u1234o");
+
+ c = toUTF8(d);
+ assert(c == "hel\u1234o");
+ w = toUTF16(d);
+ assert(w == "hel\u1234o");
+
+
+ c = "he\U000BAAAAllo";
+ w = toUTF16(c);
+ //foreach (wchar c; w) printf("c = x%x\n", c);
+ //foreach (wchar c; cast(wstring)"he\U000BAAAAllo") printf("c = x%x\n", c);
+ assert(w == "he\U000BAAAAllo");
+ d = toUTF32(c);
+ assert(d == "he\U000BAAAAllo");
+
+ c = toUTF8(w);
+ assert(c == "he\U000BAAAAllo");
+ d = toUTF32(w);
+ assert(d == "he\U000BAAAAllo");
+
+ c = toUTF8(d);
+ assert(c == "he\U000BAAAAllo");
+ w = toUTF16(d);
+ assert(w == "he\U000BAAAAllo");
+
+ wchar[2] buf;
+ auto ret = toUTF16(buf, '\U000BAAAA');
+ assert(ret == "\U000BAAAA");
+}
diff --git a/external/arsd-webassembly/object.d b/external/arsd-webassembly/object.d
new file mode 100644
index 0000000..51e1739
--- /dev/null
+++ b/external/arsd-webassembly/object.d
@@ -0,0 +1,2031 @@
+// Minimal druntime for webassembly. Assumes your program has a main function.
+module object;
+
+static import arsd.webassembly;
+
+version(CarelessAlocation)
+{
+ version = inline_concat;
+}
+
+import core.arsd.memory_allocation;
+
+alias noreturn = typeof(*null);
+alias string = immutable(char)[];
+alias wstring = immutable(wchar)[];
+alias dstring = immutable(dchar)[];
+alias size_t = uint;
+alias ptrdiff_t = int;
+
+
+// then the entry point just for convenience so main works.
+extern(C) int _Dmain(string[] args);
+export extern(C) void _start() { _Dmain(null); }
+
+extern(C) bool _xopEquals(in void*, in void*) { return false; } // assert(0);
+
+// basic array support {
+
+template _arrayOp(Args...)
+{
+ import core.internal.array.operations;
+ alias _arrayOp = arrayOp!Args;
+}
+
+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 s = cast(ubyte*) src;
+ auto len = dstlen * elemsz;
+
+ while(len) {
+ *d = *s;
+ d++;
+ s++;
+ len--;
+ }
+
+}
+
+void reserve(T)(ref T[] arr, size_t length) @trusted {
+ arr = (cast(T*) (malloc(length * T.sizeof).ptr))[0 .. 0];
+}
+
+
+extern(C) void _d_arraybounds(string file, size_t line) {
+ arsd.webassembly.eval(
+ q{ console.error("Range error: " + $0 + ":" + $1 )},
+ file, line);
+ arsd.webassembly.abort();
+}
+
+
+/// 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)
+{
+ arsd.webassembly.eval(
+ q{ console.error("Range error: " + $0 + ":" + $1 + " [" + $2 + ".." + $3 + "] <> " + $4)},
+ file, line, lwr, upr, length);
+ arsd.webassembly.abort();
+}
+
+/// 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)
+{
+ arsd.webassembly.eval(
+ q{ console.error("Array index " + $0 + " out of bounds '[0.."+$1+"]' " + $2 + ":" + $3)},
+ index, length, file, line);
+ arsd.webassembly.abort();
+}
+
+
+extern(C) void* memset(void* s, int c, size_t n) @nogc nothrow pure
+{
+ auto d = cast(ubyte*) s;
+ while(n) {
+ *d = cast(ubyte) c;
+ d++;
+ n--;
+ }
+ return s;
+}
+
+pragma(LDC_intrinsic, "llvm.memcpy.p0i8.p0i8.i#")
+ void llvm_memcpy(T)(void* dst, const(void)* src, T len, bool volatile_ = false);
+
+extern(C) void *memcpy(void* dest, const(void)* src, size_t n) pure @nogc nothrow
+{
+ ubyte *d = cast(ubyte*) dest;
+ const (ubyte) *s = cast(const(ubyte)*)src;
+ for (; n; n--) *d++ = *s++;
+ return dest;
+}
+
+extern(C) int memcmp(const(void)* s1, const(void*) s2, size_t n) pure @nogc nothrow @trusted
+{
+ auto b = cast(ubyte*) s1;
+ auto b2 = cast(ubyte*) s2;
+ foreach(i; 0 .. n) {
+ if(auto diff = *b - *b2)
+ return diff;
+ b++;
+ b2++;
+ }
+ return 0;
+}
+
+public import core.arsd.utf_decoding;
+
+// }
+
+extern(C) void _d_assert(string file, uint line) @trusted @nogc pure
+{
+ arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line);//, lwr, upr, length);
+ arsd.webassembly.abort();
+}
+void _d_assertp(immutable(char)* file, uint line)
+{
+ // import core.stdc.string : strlen;
+ size_t sz = 0;
+ while(file[sz] != '\0') sz++;
+ arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file[0 .. sz], line);//, lwr, upr, length);
+ arsd.webassembly.abort();
+}
+
+
+extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc pure
+{
+ arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line, msg);//, lwr, upr, length);
+ arsd.webassembly.abort();
+}
+
+void __switch_error(string file, size_t line) @trusted @nogc pure
+{
+ _d_assert_msg("final switch error",file, line);
+}
+
+bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) {
+ if (lhs.length != rhs.length) {
+ return false;
+ }
+ foreach(i; 0..lhs.length) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// bare basics class support {
+
+
+extern(C) Object _d_allocclass(TypeInfo_Class ti) {
+ auto ptr = malloc(ti.m_init.length);
+ ptr[] = ti.m_init[];
+ return cast(Object) ptr.ptr;
+}
+
+extern(C) void* _d_dynamic_cast(Object o, TypeInfo_Class c) {
+ void* res = null;
+ size_t offset = 0;
+ if (o && _d_isbaseof2(typeid(o), c, offset))
+ {
+ res = cast(void*) o + offset;
+ }
+ return res;
+}
+
+/*************************************
+ * Attempts to cast Object o to class c.
+ * Returns o if successful, null if not.
+ */
+extern(C) void* _d_interface_cast(void* p, TypeInfo_Class c)
+{
+ if (!p)
+ return null;
+
+ Interface* pi = **cast(Interface***) p;
+ return _d_dynamic_cast(cast(Object)(p - pi.offset), c);
+}
+
+
+extern(C)
+int _d_isbaseof2(scope TypeInfo_Class oc, scope const TypeInfo_Class c, scope ref size_t offset) @safe
+
+{
+ if (oc is c)
+ return true;
+
+ do
+ {
+ if (oc.base is c)
+ return true;
+
+ // Bugzilla 2013: Use depth-first search to calculate offset
+ // from the derived (oc) to the base (c).
+ foreach (iface; oc.interfaces)
+ {
+ if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
+ {
+ offset += iface.offset;
+ return true;
+ }
+ }
+
+ oc = oc.base;
+ } while (oc);
+
+ return false;
+}
+
+int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted pure @nogc nothrow
+ if (__traits(isScalar, T))
+{
+ // Compute U as the implementation type for T
+ static if (is(T == ubyte) || is(T == void) || is(T == bool))
+ alias U = char;
+ else static if (is(T == wchar))
+ alias U = ushort;
+ else static if (is(T == dchar))
+ alias U = uint;
+ else static if (is(T == ifloat))
+ alias U = float;
+ else static if (is(T == idouble))
+ alias U = double;
+ else static if (is(T == ireal))
+ alias U = real;
+ else
+ alias U = T;
+
+ static if (is(U == char))
+ {
+ int dstrcmp(scope const char[] s1, scope const char[] s2 ) @trusted pure @nogc nothrow
+ {
+ immutable len = s1.length <= s2.length ? s1.length : s2.length;
+ if (__ctfe)
+ {
+ foreach (const u; 0 .. len)
+ {
+ if (s1[u] != s2[u])
+ return s1[u] > s2[u] ? 1 : -1;
+ }
+ }
+ else
+ {
+ const ret = memcmp( s1.ptr, s2.ptr, len );
+ if ( ret )
+ return ret;
+ }
+ return (s1.length > s2.length) - (s1.length < s2.length);
+ }
+ return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
+ }
+ else static if (!is(U == T))
+ {
+ // Reuse another implementation
+ return __cmp(cast(U[]) lhs, cast(U[]) rhs);
+ }
+ else
+ {
+ version (BigEndian)
+ static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P))
+ {
+ if (!__ctfe)
+ {
+ import core.stdc.string : memcmp;
+ int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof);
+ if (c)
+ return c;
+ static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2)
+ return cast(int) lhs.length - cast(int) rhs.length;
+ else
+ return int(lhs.length > rhs.length) - int(lhs.length < rhs.length);
+ }
+ }
+
+ immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
+ foreach (const u; 0 .. len)
+ {
+ auto a = lhs.ptr[u], b = rhs.ptr[u];
+ static if (is(T : creal))
+ {
+ // Use rt.cmath2._Ccmp instead ?
+ // Also: if NaN is present, numbers will appear equal.
+ auto r = (a.re > b.re) - (a.re < b.re);
+ if (!r) r = (a.im > b.im) - (a.im < b.im);
+ }
+ else
+ {
+ // This pattern for three-way comparison is better than conditional operators
+ // See e.g. https://godbolt.org/z/3j4vh1
+ const r = (a > b) - (a < b);
+ }
+ if (r) return r;
+ }
+ return (lhs.length > rhs.length) - (lhs.length < rhs.length);
+ }
+}
+
+// This function is called by the compiler when dealing with array
+// comparisons in the semantic analysis phase of CmpExp. The ordering
+// comparison is lowered to a call to this template.
+int __cmp(T1, T2)(T1[] s1, T2[] s2)
+if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
+{
+ import core.internal.traits : Unqual;
+ alias U1 = Unqual!T1;
+ alias U2 = Unqual!T2;
+
+ static if (is(U1 == void) && is(U2 == void))
+ static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
+ else
+ static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
+
+ // All unsigned byte-wide types = > dstrcmp
+ immutable len = s1.length <= s2.length ? s1.length : s2.length;
+
+ foreach (const u; 0 .. len)
+ {
+ static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
+ {
+ auto c = __cmp(at(s1, u), at(s2, u));
+ if (c != 0)
+ return c;
+ }
+ else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
+ {
+ auto c = at(s1, u).opCmp(at(s2, u));
+ if (c != 0)
+ return c;
+ }
+ else static if (__traits(compiles, at(s1, u) < at(s2, u)))
+ {
+ if (int result = (at(s1, u) > at(s2, u)) - (at(s1, u) < at(s2, u)))
+ return result;
+ }
+ else
+ {
+ // TODO: fix this legacy bad behavior, see
+ // https://issues.dlang.org/show_bug.cgi?id=17244
+ static assert(is(U1 == U2), "Internal error.");
+ import core.stdc.string : memcmp;
+ auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
+ if (c != 0)
+ return c;
+ }
+ }
+ return (s1.length > s2.length) - (s1.length < s2.length);
+}
+
+
+
+/**
+Support for switch statements switching on strings.
+Params:
+ caseLabels = sorted array of strings generated by compiler. Note the
+ strings are sorted by length first, and then lexicographically.
+ condition = string to look up in table
+Returns:
+ index of match in caseLabels, a negative integer if not found
+*/
+int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc
+{
+ // This closes recursion for other cases.
+ static if (caseLabels.length == 0)
+ {
+ return int.min;
+ }
+ else static if (caseLabels.length == 1)
+ {
+ return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
+ }
+ // To be adjusted after measurements
+ // Compile-time inlined binary search.
+ else static if (caseLabels.length < 7)
+ {
+ int r = void;
+ enum mid = cast(int)caseLabels.length / 2;
+ if (condition.length == caseLabels[mid].length)
+ {
+ r = __cmp(condition, caseLabels[mid]);
+ if (r == 0) return mid;
+ }
+ else
+ {
+ // Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1
+ r = ((condition.length > caseLabels[mid].length) << 1) - 1;
+ }
+
+ if (r < 0)
+ {
+ // Search the left side
+ return __switch!(T, caseLabels[0 .. mid])(condition);
+ }
+ else
+ {
+ // Search the right side
+ return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1;
+ }
+ }
+ else
+ {
+ // Need immutable array to be accessible in pure code, but case labels are
+ // currently coerced to the switch condition type (e.g. const(char)[]).
+ pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items)
+ {
+ assert(__ctfe); // only @safe for CTFE
+ immutable T[][caseLabels.length] result = cast(immutable)(items[]);
+ return result;
+ }
+ static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]);
+
+ // Run-time binary search in a static array of labels.
+ return __switchSearch!T(cases[], condition);
+ }
+}
+
+// binary search in sorted string cases, also see `__switch`.
+private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc
+{
+ size_t low = 0;
+ size_t high = cases.length;
+
+ do
+ {
+ auto mid = (low + high) / 2;
+ int r = void;
+ if (condition.length == cases[mid].length)
+ {
+ r = __cmp(condition, cases[mid]);
+ if (r == 0) return cast(int) mid;
+ }
+ else
+ {
+ // Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc
+ r = ((condition.length > cases[mid].length) << 1) - 1;
+ }
+
+ if (r > 0) low = mid + 1;
+ else high = mid;
+ }
+ while (low < high);
+
+ // Not found
+ return -1;
+}
+
+//TODO: Support someday?
+ extern(C) void _d_throw_exception(Throwable o)
+ {
+ assert(false, "Exception throw");
+ }
+
+
+// for closures
+extern(C) void* _d_allocmemory(size_t sz) {
+ return malloc(sz).ptr;
+}
+
+///For POD structures
+extern (C) void* _d_allocmemoryT(TypeInfo ti)
+{
+ return malloc(ti.size).ptr;
+}
+
+
+class Object
+{
+ /// Convert Object to human readable string
+ string toString() { return "Object"; }
+ /// Compute hash function for Object
+ size_t toHash() @trusted nothrow
+ {
+ auto addr = cast(size_t)cast(void*)this;
+ return addr ^ (addr >>> 4);
+ }
+
+ /// Compare against another object. NOT IMPLEMENTED!
+ int opCmp(Object o) { assert(false, "not implemented"); }
+ /// Check equivalence againt another object
+ bool opEquals(Object o) { return this is o; }
+}
+
+/// Compare to objects
+bool opEquals(Object lhs, Object rhs)
+{
+ // If aliased to the same object or both null => equal
+ if (lhs is rhs) return true;
+
+ // If either is null => non-equal
+ if (lhs is null || rhs is null) return false;
+
+ if (!lhs.opEquals(rhs)) return false;
+
+ // If same exact type => one call to method opEquals
+ if (typeid(lhs) is typeid(rhs) ||
+ !__ctfe && typeid(lhs).opEquals(typeid(rhs)))
+ /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't
+ (issue 7147). But CTFE also guarantees that equal TypeInfos are
+ always identical. So, no opEquals needed during CTFE. */
+ {
+ return true;
+ }
+
+ // General case => symmetric calls to method opEquals
+ return rhs.opEquals(lhs);
+}
+/************************
+* Returns true if lhs and rhs are equal.
+*/
+bool opEquals(const Object lhs, const Object rhs)
+{
+ // A hack for the moment.
+ return opEquals(cast()lhs, cast()rhs);
+}
+
+class TypeInfo
+{
+ override string toString() const @safe nothrow
+ {
+ return typeid(this).name;
+ }
+
+ const(TypeInfo) next()nothrow pure inout @nogc { return null; }
+ size_t size() nothrow pure const @safe @nogc { return 0; }
+
+ bool equals(in void* p1, in void* p2) const { return p1 == p2; }
+
+ override size_t toHash() @trusted const nothrow
+ {
+ return hashOf(this.toString());
+ }
+
+
+ size_t getHash(scope const void* p) @trusted nothrow const
+ {
+ return hashOf(p);
+ }
+
+ /**
+ * Return default initializer. If the type should be initialized to all
+ * zeros, an array with a null ptr and a length equal to the type size will
+ * be returned. For static arrays, this returns the default initializer for
+ * a single element of the array, use `tsize` to get the correct size.
+ */
+ const(void)[] initializer() const @trusted nothrow pure
+ {
+ return (cast(const(void)*) null)[0 .. typeof(null).sizeof];
+ }
+
+ @property uint flags() nothrow pure const @safe @nogc { return 0; }
+ /// Run the destructor on the object and all its sub-objects
+ void destroy(void* p) const {}
+ /// Run the postblit on the object and all its sub-objects
+ void postblit(void* p) const {}
+
+ @property size_t talign() nothrow pure const { return size; }
+}
+
+class TypeInfo_Class : TypeInfo
+{
+ ubyte[] m_init; /// class static initializer (length gives class size)
+ string name; /// name of class
+ void*[] vtbl; // virtual function pointer table
+ Interface[] interfaces;
+ TypeInfo_Class base;
+ void* destructor;
+ void function(Object) classInvariant;
+ uint flags;
+ void* deallocator;
+ void*[] offTi;
+ void function(Object) defaultConstructor;
+ immutable(void)* rtInfo;
+
+ override @property size_t size() nothrow pure const
+ { return Object.sizeof; }
+
+ override size_t getHash(scope const void* p) @trusted const
+ {
+ auto o = *cast(Object*)p;
+ return o ? o.toHash() : 0;
+ }
+
+ override bool equals(in void* p1, in void* p2) const
+ {
+ Object o1 = *cast(Object*)p1;
+ Object o2 = *cast(Object*)p2;
+
+ return (o1 is o2) || (o1 && o1.opEquals(o2));
+ }
+
+ override const(void)[] initializer() nothrow pure const @safe
+ {
+ return m_init;
+ }
+}
+
+void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
+{
+ import core.internal.destruction : destructRecurse;
+
+ destructRecurse(obj);
+
+ static if (initialize)
+ {
+ import core.internal.lifetime : emplaceInitializer;
+ emplaceInitializer(obj); // emplace T.init
+ }
+}
+
+private extern (D) nothrow alias void function (Object) fp_t;
+private extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow
+{
+ auto ppv = cast(void**) p;
+ if (!p || !*ppv)
+ return;
+
+ auto pc = cast(TypeInfo_Class*) *ppv;
+ if (det)
+ {
+ auto c = *pc;
+ do
+ {
+ if (c.destructor)
+ (cast(fp_t) c.destructor)(cast(Object) p); // call destructor
+ }
+ while ((c = c.base) !is null);
+ }
+
+ if (resetMemory)
+ {
+ auto w = (*pc).initializer;
+ p[0 .. w.length] = w[];
+ }
+ *ppv = null; // zero vptr even if `resetMemory` is false
+}
+extern(C) void _d_callfinalizer(void* p)
+{
+ rt_finalize2(p);
+}
+
+void destroy(bool initialize = true, T)(T obj) if (is(T == class))
+{
+ static if (__traits(getLinkage, T) == "C++")
+ {
+ static if (__traits(hasMember, T, "__xdtor"))
+ obj.__xdtor();
+
+ static if (initialize)
+ {
+ const initializer = __traits(initSymbol, T);
+ (cast(void*)obj)[0 .. initializer.length] = initializer[];
+ }
+ }
+ else
+ {
+ // Bypass overloaded opCast
+ auto ptr = (() @trusted => *cast(void**) &obj)();
+ rt_finalize2(ptr, true, initialize);
+ }
+}
+void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
+{
+ static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
+
+ destroy!initialize(cast(Object)obj);
+}
+void destroy(bool initialize = true, T)(ref T obj)
+ if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
+{
+ static if (initialize)
+ obj = T.init;
+}
+
+
+class TypeInfo_Pointer : TypeInfo
+{
+ TypeInfo m_next;
+
+ override bool equals(in void* p1, in void* p2) const { return *cast(void**)p1 == *cast(void**)p2; }
+ override size_t getHash(scope const void* p) @trusted const
+ {
+ size_t addr = cast(size_t) *cast(const void**)p;
+ return addr ^ (addr >> 4);
+ }
+ override @property size_t size() nothrow pure const { return (void*).sizeof; }
+
+ override const(void)[] initializer() const @trusted { return (cast(void *)null)[0 .. (void*).sizeof]; }
+
+ override const (TypeInfo) next() const { return m_next; }
+}
+
+class TypeInfo_Array : TypeInfo {
+ TypeInfo value;
+ override size_t size() const { return (void[]).sizeof; }
+ override const(TypeInfo) next() const { return value; }
+
+ override bool equals(in void* p1, in void* p2) const
+ {
+ void[] a1 = *cast(void[]*)p1;
+ void[] a2 = *cast(void[]*)p2;
+ if (a1.length != a2.length)
+ return false;
+ size_t sz = value.size;
+ for (size_t i = 0; i < a1.length; i++)
+ {
+ if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz))
+ return false;
+ }
+ return true;
+ }
+ override @property size_t talign() nothrow pure const
+ {
+ return (void[]).alignof;
+ }
+ override const(void)[] initializer() const @trusted { return (cast(void *)null)[0 .. (void[]).sizeof]; }
+}
+
+class TypeInfo_StaticArray : TypeInfo {
+ TypeInfo value;
+ size_t len;
+ override size_t size() const { return value.size * len; }
+ override const(TypeInfo) next() const { return value; }
+
+ override bool equals(in void* p1, in void* p2) const {
+ size_t sz = value.size;
+
+ for (size_t u = 0; u < len; u++)
+ {
+ if (!value.equals(p1 + u * sz, p2 + u * sz))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ override @property size_t talign() nothrow pure const
+ {
+ return value.talign;
+ }
+
+}
+
+import core.arsd.aa;
+alias AARange = core.arsd.aa.Range;
+extern (C)
+{
+ // from druntime/src/rt/aaA.d
+ /* The real type is (non-importable) `rt.aaA.Impl*`;
+ * the compiler uses `void*` for its prototypes.
+ */
+ private alias AA = void*;
+
+ // size_t _aaLen(in AA aa) pure nothrow @nogc;
+ private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
+ private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) ;
+ // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
+ inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) ;
+ inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) ;
+ void* _aaRehash(AA* paa, const scope TypeInfo keyti) ;
+ void _aaClear(AA aa) ;
+
+ // alias _dg_t = extern(D) int delegate(void*);
+ // int _aaApply(AA aa, size_t keysize, _dg_t dg);
+
+ // alias _dg2_t = extern(D) int delegate(void*, void*);
+ // int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
+
+ AARange _aaRange(AA aa) pure nothrow @nogc @safe;
+ bool _aaRangeEmpty(AARange r) pure @safe @nogc nothrow;
+ void* _aaRangeFrontKey(AARange r);
+ void* _aaRangeFrontValue(AARange r) pure @nogc nothrow;
+ void _aaRangePopFront(ref AARange r) pure @nogc nothrow @safe;
+
+ int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
+ size_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
+
+ /*
+ _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
+ This is a typesystem hole, however this is existing hole.
+ Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus
+ copiler allowed to create AA literal with keys, which have impure unsafe toHash methods.
+ */
+ void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values);
+}
+
+private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
+{
+ // ensure we are dealing with a genuine AA.
+ static if (is(const(V[K]) == const(T)))
+ alias realAA = aa;
+ else
+ const(V[K]) realAA = aa;
+ return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
+}
+
+auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
+{
+ import core.internal.traits : substInout;
+
+ static struct Result
+ {
+ AARange r;
+
+ pure nothrow @nogc:
+ @property bool empty() @safe { return _aaRangeEmpty(r); }
+ @property ref front() @trusted
+ {
+ return *cast(substInout!K*) _aaRangeFrontKey(r);
+ }
+ void popFront() @safe { _aaRangePopFront(r); }
+ @property Result save() { return this; }
+ }
+
+ return Result(_aaToRange(aa));
+}
+
+/** ditto */
+auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
+{
+ return (*aa).byKey();
+}
+
+
+
+auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
+{
+ import core.internal.traits : substInout;
+
+ static struct Result
+ {
+ AARange r;
+
+ pure nothrow @nogc:
+ @property bool empty() @safe { return _aaRangeEmpty(r); }
+ @property ref front() @trusted
+ {
+ return *cast(substInout!V*) _aaRangeFrontValue(r);
+ }
+ void popFront() @safe { _aaRangePopFront(r); }
+ @property Result save() { return this; }
+ }
+
+ return Result(_aaToRange(aa));
+}
+
+/** ditto */
+auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
+{
+ return (*aa).byValue();
+}
+
+Key[] keys(T : Value[Key], Value, Key)(T aa) @property
+{
+ // ensure we are dealing with a genuine AA.
+ static if (is(const(Value[Key]) == const(T)))
+ alias realAA = aa;
+ else
+ const(Value[Key]) realAA = aa;
+ auto res = () @trusted {
+ auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
+ return *cast(Key[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Key))
+ _doPostblit(res);
+ return res;
+}
+
+/** ditto */
+Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
+{
+ return (*aa).keys;
+}
+
+/***********************************
+ * Returns a newly allocated dynamic array containing a copy of the values from
+ * the associative array.
+ * Params:
+ * aa = The associative array.
+ * Returns:
+ * A dynamic array containing a copy of the values.
+ */
+Value[] values(T : Value[Key], Value, Key)(T aa) @property
+{
+ // ensure we are dealing with a genuine AA.
+ static if (is(const(Value[Key]) == const(T)))
+ alias realAA = aa;
+ else
+ const(Value[Key]) realAA = aa;
+ auto res = () @trusted {
+ auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
+ return *cast(Value[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Value))
+ _doPostblit(res);
+ return res;
+}
+
+/** ditto */
+Value[] values(T : Value[Key], Value, Key)(T *aa) @property
+{
+ return (*aa).values;
+}
+inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)
+{
+ auto p = key in aa;
+ return p ? *p : defaultValue;
+}
+
+/** ditto */
+inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
+{
+ return (*aa).get(key, defaultValue);
+}
+// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
+private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
+
+/***********************************
+ * Looks up key; if it exists applies the update callable else evaluates the
+ * create callable and adds it to the associative array
+ * Params:
+ * aa = The associative array.
+ * key = The key.
+ * create = The callable to apply on create.
+ * update = The callable to apply on update.
+ */
+void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
+if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
+{
+ bool found;
+ // if key is @safe-ly copyable, `update` may infer @safe
+ static if (isSafeCopyable!K)
+ {
+ auto p = () @trusted
+ {
+ return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
+ } ();
+ }
+ else
+ {
+ auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
+ }
+ if (!found)
+ *p = create();
+ else
+ {
+ static if (is(typeof(update(*p)) == void))
+ update(*p);
+ else
+ *p = update(*p);
+ }
+}
+
+ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
+{
+ bool found;
+ // if key is @safe-ly copyable, `require` can infer @safe
+ static if (isSafeCopyable!K)
+ {
+ auto p = () @trusted
+ {
+ return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
+ } ();
+ }
+ else
+ {
+ auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
+ }
+ if (found)
+ return *p;
+ else
+ {
+ *p = value; // Not `return (*p = value)` since if `=` is overloaded
+ return *p; // this might not return a ref to the left-hand side.
+ }
+}
+
+
+
+/***********************************
+ * Removes all remaining keys and values from an associative array.
+ * Params:
+ * aa = The associative array.
+ */
+void clear(Value, Key)(Value[Key] aa)
+{
+ _aaClear(*cast(AA *) &aa);
+}
+
+/** ditto */
+void clear(Value, Key)(Value[Key]* aa)
+{
+ _aaClear(*cast(AA *) aa);
+}
+void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
+{
+ return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
+}
+
+alias AssociativeArray(Key, Value) = Value[Key];
+
+class TypeInfo_AssociativeArray : TypeInfo
+{
+ override string toString() const
+ {
+ return value.toString() ~ "[" ~ key.toString() ~ "]";
+ }
+
+ override bool opEquals(Object o)
+ {
+ if (this is o)
+ return true;
+ auto c = cast(const TypeInfo_AssociativeArray)o;
+ return c && this.key == c.key &&
+ this.value == c.value;
+ }
+
+ override bool equals(in void* p1, in void* p2) @trusted const
+ {
+ return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2);
+ }
+
+ override size_t getHash(scope const void* p) nothrow @trusted const
+ {
+ return _aaGetHash(cast(AA*)p, this);
+ }
+
+ // BUG: need to add the rest of the functions
+
+ override @property size_t size() nothrow pure const
+ {
+ return (char[int]).sizeof;
+ }
+
+ override const(void)[] initializer() const @trusted
+ {
+ return (cast(void *)null)[0 .. (char[int]).sizeof];
+ }
+
+ override @property inout(TypeInfo) next() nothrow pure inout { return value; }
+ override @property uint flags() nothrow pure const { return 1; }
+
+
+ TypeInfo value;
+ TypeInfo key;
+
+ override @property size_t talign() nothrow pure const
+ {
+ return (char[int]).alignof;
+ }
+
+ version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
+ {
+ arg1 = typeid(void*);
+ return 0;
+ }
+}
+
+
+
+class TypeInfo_Enum : TypeInfo {
+ TypeInfo base;
+ string name;
+ void[] m_init;
+
+ override size_t size() const { return base.size; }
+ override const(TypeInfo) next() const { return base.next; }
+ override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
+ override @property size_t talign() const { return base.talign; }
+ override void destroy(void* p) const { return base.destroy(p); }
+ override void postblit(void* p) const { return base.postblit(p); }
+
+ override const(void)[] initializer() const
+ {
+ return m_init.length ? m_init : base.initializer();
+ }
+}
+
+extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length)
+{
+ return malloc(length * ti.next.size);
+}
+
+extern(C) void[] _d_newarrayT(const TypeInfo ti, size_t length)
+{
+ auto arr = _d_newarrayU(ti, length);
+ (cast(byte[])arr)[] = 0;
+ return arr;
+}
+
+extern(C) void[] _d_newarrayiT(const TypeInfo ti, size_t length)
+{
+ auto result = _d_newarrayU(ti, length);
+ auto tinext = ti.next;
+ auto size = tinext.size;
+ auto init = tinext.initializer();
+ switch (init.length)
+ {
+ foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
+ {
+ case T.sizeof:
+ if (tinext.talign % T.alignof == 0)
+ {
+ (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
+ return result;
+ }
+ goto default;
+ }
+
+ default:
+ {
+ immutable sz = init.length;
+ for (size_t u = 0; u < size * length; u += sz)
+ {
+ memcpy(result.ptr + u, init.ptr, sz);
+ }
+ return result;
+ }
+ }
+}
+
+extern (C) void* _d_newitemU(scope const TypeInfo _ti)
+{
+ import core.arsd.objectutils;
+ auto ti = cast()_ti;
+ immutable tiSize = structTypeInfoSize(ti);
+ immutable itemSize = ti.size;
+ immutable size = itemSize + tiSize;
+ auto p = malloc(size);
+
+ return p.ptr;
+}
+
+/// ditto
+extern (C) void* _d_newitemT(in TypeInfo _ti)
+{
+ auto p = _d_newitemU(_ti);
+ memset(p, 0, _ti.size);
+ return p;
+}
+
+/// Same as above, for item with non-zero initializer.
+extern (C) void* _d_newitemiT(in TypeInfo _ti)
+{
+ auto p = _d_newitemU(_ti);
+ auto init = _ti.initializer();
+ assert(init.length <= _ti.size);
+ memcpy(p, init.ptr, init.length);
+ return p;
+}
+
+
+
+private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
+{
+ if (dimensions.length == 0)
+ return null;
+
+ void[] foo(const TypeInfo ti, size_t[] dimensions)
+ {
+ size_t count = dimensions[0];
+
+ if (dimensions.length == 1)
+ {
+ auto r = op(ti, count);
+ return (*cast(void[]*)(&r))[0..count];
+ }
+ void[] p = malloc((void[]).sizeof * count);
+
+ foreach (i; 0..count)
+ {
+ (cast(void[]*)p.ptr)[i] = foo(ti.next, dimensions[1..$]);
+ }
+ return p[0..count];
+ }
+
+ return foo(ti, dimensions);
+}
+
+
+extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims)
+{
+ if (dims.length == 0)
+ return null;
+ else
+ return _d_newarrayOpT!(_d_newarrayT)(ti, dims);
+}
+
+/// ditto
+extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
+{
+ if (dims.length == 0)
+ return null;
+ else
+ return _d_newarrayOpT!(_d_newarrayiT)(ti, dims);
+}
+
+
+
+
+AllocatedBlock* getAllocatedBlock(void* ptr) {
+ auto block = (cast(AllocatedBlock*) ptr) - 1;
+ if(!block.checkChecksum())
+ return null;
+ return block;
+}
+
+/++
+ Marks the memory block as OK to append in-place if possible.
++/
+void assumeSafeAppend(T)(T[] arr) {
+ auto block = getAllocatedBlock(arr.ptr);
+ if(block is null) assert(0);
+
+ block.used = arr.length;
+}
+
+/++
+ Marks the memory block associated with this array as unique, meaning
+ the runtime is allowed to free the old block immediately instead of
+ keeping it around for other lingering slices.
+
+ In real D, the GC would take care of this but here I have to hack it.
+
+ arsd.webasm extension
++/
+void assumeUniqueReference(T)(T[] arr) {
+ auto block = getAllocatedBlock(arr.ptr);
+ if(block is null) assert(0);
+
+ block.flags |= AllocatedBlock.Flags.unique;
+}
+
+template _d_arraysetlengthTImpl(Tarr : T[], T) {
+ size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted {
+ auto orig = arr;
+
+ if(newlength <= arr.length) {
+ arr = arr[0 ..newlength];
+ } else {
+ auto ptr = cast(T*) realloc(cast(ubyte[])arr, newlength * T.sizeof);
+ arr = ptr[0 .. newlength];
+ if(orig !is null) {
+ arr[0 .. orig.length] = orig[];
+ }
+ }
+
+ return newlength;
+ }
+}
+
+extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) @trusted {
+ auto elemSize = ti.next.size;
+ auto newLength = n + px.length;
+ auto newSize = newLength * elemSize;
+ //import std.stdio; writeln(newSize, " ", newLength);
+ ubyte* ptr;
+ if(px.ptr is null)
+ ptr = malloc(newSize).ptr;
+ else // FIXME: anti-stomping by checking length == used
+ ptr = realloc(cast(ubyte[])px, newSize).ptr;
+ auto ns = ptr[0 .. newSize];
+ auto op = px.ptr;
+ auto ol = px.length * elemSize;
+
+ foreach(i, b; op[0 .. ol])
+ ns[i] = b;
+
+ (cast(size_t *)(&px))[0] = newLength;
+ (cast(void **)(&px))[1] = ns.ptr;
+ return px;
+}
+
+
+version(inline_concat)
+extern(C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @trusted
+{
+ auto elemSize = ti.next.size;
+ size_t length;
+ foreach (b; arrs)
+ length += b.length;
+ if(!length)
+ return null;
+ ubyte* ptr = cast(ubyte*)malloc(length * elemSize);
+
+ //Copy data
+ {
+ ubyte* nPtr = ptr;
+ foreach(b; arrs)
+ {
+ byte* bPtr = b.ptr;
+ size_t copySize = b.length*elemSize;
+ nPtr[0..copySize] = cast(ubyte[])bPtr[0..copySize];
+ nPtr+= copySize;
+ }
+ }
+ return cast(void[])ptr[0..length];
+}
+
+version(inline_concat)
+extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
+{
+ import core.arsd.objectutils;
+ auto sizeelem = ti.next.size; // array element size
+ size_t xlen = x.length * sizeelem;
+ size_t ylen = y.length * sizeelem;
+ size_t len = xlen + ylen;
+
+ if (!len)
+ return null;
+
+ byte[] p = cast(byte[])malloc(len);
+ memcpy(p.ptr, x.ptr, xlen);
+ memcpy(p.ptr + xlen, y.ptr, ylen);
+ // do postblit processing
+ __doPostblit(p.ptr, xlen + ylen, ti.next);
+ return p[0 .. x.length + y.length];
+}
+
+extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
+{
+ // c could encode into from 1 to 4 characters
+ char[4] buf = void;
+ byte[] appendthis; // passed to appendT
+ if (c <= 0x7F)
+ {
+ buf.ptr[0] = cast(char)c;
+ appendthis = (cast(byte *)buf.ptr)[0..1];
+ }
+ else if (c <= 0x7FF)
+ {
+ buf.ptr[0] = cast(char)(0xC0 | (c >> 6));
+ buf.ptr[1] = cast(char)(0x80 | (c & 0x3F));
+ appendthis = (cast(byte *)buf.ptr)[0..2];
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf.ptr[0] = cast(char)(0xE0 | (c >> 12));
+ buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf.ptr[2] = cast(char)(0x80 | (c & 0x3F));
+ appendthis = (cast(byte *)buf.ptr)[0..3];
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf.ptr[0] = cast(char)(0xF0 | (c >> 18));
+ buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf.ptr[3] = cast(char)(0x80 | (c & 0x3F));
+ appendthis = (cast(byte *)buf.ptr)[0..4];
+ }
+ else
+ assert(false, "Could not append dchar"); // invalid utf character - should we throw an exception instead?
+
+ //
+ // TODO: This always assumes the array type is shared, because we do not
+ // get a typeinfo from the compiler. Assuming shared is the safest option.
+ // Once the compiler is fixed, the proper typeinfo should be forwarded.
+ //
+ return _d_arrayappendT(typeid(shared char[]), x, appendthis);
+}
+
+
+
+
+alias AliasSeq(T...) = T;
+static foreach(type; AliasSeq!(byte, char, dchar, double, float, int, long, short, ubyte, uint, ulong, ushort, void, wchar)) {
+ mixin(q{
+ class TypeInfo_}~type.mangleof~q{ : TypeInfo {
+ override string toString() const pure nothrow @safe { return type.stringof; }
+ override size_t size() const { return type.sizeof; }
+ override @property size_t talign() const pure nothrow
+ {
+ return type.alignof;
+ }
+
+ override bool equals(in void* a, in void* b) const {
+ static if(is(type == void))
+ return false;
+ else
+ return (*(cast(type*) a) == (*(cast(type*) b)));
+ }
+ static if(!is(type == void))
+ override size_t getHash(scope const void* p) @trusted const nothrow
+ {
+ return hashOf(*cast(const type *)p);
+ }
+ override const(void)[] initializer() pure nothrow @trusted const
+ {
+ static if(__traits(isZeroInit, type))
+ return (cast(void*)null)[0 .. type.sizeof];
+ else
+ {
+ static immutable type[1] c;
+ return c;
+ }
+ }
+ }
+ class TypeInfo_A}~type.mangleof~q{ : TypeInfo_Array {
+ override string toString() const { return (type[]).stringof; }
+ override const(TypeInfo) next() const { return cast(inout)typeid(type); }
+ override size_t getHash(scope const void* p) @trusted const nothrow
+ {
+ return hashOf(*cast(const type[]*) p);
+ }
+
+ override bool equals(in void* av, in void* bv) const {
+ type[] a = *(cast(type[]*) av);
+ type[] b = *(cast(type[]*) bv);
+
+ static if(is(type == void))
+ return false;
+ else {
+ foreach(idx, item; a)
+ if(item != b[idx])
+ return false;
+ return true;
+ }
+ }
+ }
+ });
+}
+// typeof(null)
+class TypeInfo_n : TypeInfo
+{
+ const: pure: @nogc: nothrow: @safe:
+ override string toString() { return "typeof(null)"; }
+ override size_t getHash(scope const void*) { return 0; }
+ override bool equals(in void*, in void*) { return true; }
+ override @property size_t size() { return typeof(null).sizeof; }
+ override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
+}
+
+struct Interface {
+ TypeInfo_Class classinfo;
+ void*[] vtbl;
+ size_t offset;
+}
+
+/**
+ * Array of pairs giving the offset and type information for each
+ * member in an aggregate.
+ */
+struct OffsetTypeInfo
+{
+ size_t offset; /// Offset of member from start of object
+ TypeInfo ti; /// TypeInfo for this member
+}
+
+class TypeInfo_Axa : TypeInfo_Aa {
+
+}
+class TypeInfo_Aya : TypeInfo_Aa {
+
+}
+
+class TypeInfo_Function : TypeInfo
+{
+ override string toString() const pure @trusted{return deco;}
+ override bool opEquals(Object o)
+ {
+ if (this is o)
+ return true;
+ auto c = cast(const TypeInfo_Function)o;
+ return c && this.deco == c.deco;
+ }
+
+ // BUG: need to add the rest of the functions
+
+ override @property size_t size() nothrow pure const
+ {
+ return 0; // no size for functions
+ }
+ override const(void)[] initializer() const @safe{return null;}
+ TypeInfo _next;
+ override const(TypeInfo) next()nothrow pure inout @nogc { return _next; }
+
+ /**
+ * Mangled function type string
+ */
+ string deco;
+}
+
+
+class TypeInfo_Delegate : TypeInfo {
+ TypeInfo next;
+ string deco;
+ override @property size_t size() nothrow pure const
+ {
+ alias dg = int delegate();
+ return dg.sizeof;
+ }
+ override bool equals(in void* p1, in void* p2) const
+ {
+ auto dg1 = *cast(void delegate()*)p1;
+ auto dg2 = *cast(void delegate()*)p2;
+ return dg1 == dg2;
+ }
+ override const(void)[] initializer() const @trusted
+ {
+ return (cast(void *)null)[0 .. (int delegate()).sizeof];
+ }
+ override size_t getHash(scope const void* p) @trusted const
+ {
+ return hashOf(*cast(const void delegate() *)p);
+ }
+
+ override @property size_t talign() nothrow pure const
+ {
+ alias dg = int delegate();
+ return dg.alignof;
+ }
+}
+
+
+//Directly copied from LWDR source.
+class TypeInfo_Interface : TypeInfo
+{
+ TypeInfo_Class info;
+
+ override bool equals(in void* p1, in void* p2) const
+ {
+ Interface* pi = **cast(Interface ***)*cast(void**)p1;
+ Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
+ pi = **cast(Interface ***)*cast(void**)p2;
+ Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
+
+ return o1 == o2 || (o1 && o1.opCmp(o2) == 0);
+ }
+ override size_t getHash(scope const void* p) @trusted const
+ {
+ if (!*cast(void**)p)
+ {
+ return 0;
+ }
+ Interface* pi = **cast(Interface ***)*cast(void**)p;
+ Object o = cast(Object)(*cast(void**)p - pi.offset);
+ assert(o);
+ return o.toHash();
+ }
+
+ override const(void)[] initializer() const @trusted
+ {
+ return (cast(void *)null)[0 .. Object.sizeof];
+ }
+
+ override @property size_t size() nothrow pure const
+ {
+ return Object.sizeof;
+ }
+}
+
+class TypeInfo_Const : TypeInfo {
+ override size_t getHash(scope const(void*) p) @trusted const nothrow { return base.getHash(p); }
+ TypeInfo base;
+ override size_t size() const { return base.size; }
+ override const(TypeInfo) next() const { return base.next; }
+ override const(void)[] initializer() nothrow pure const{return base.initializer();}
+ override @property size_t talign() nothrow pure const { return base.talign; }
+ override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
+}
+
+
+///For some reason, getHash for interfaces wanted that
+pragma(mangle, "_D9invariant12_d_invariantFC6ObjectZv")
+extern(D) void _d_invariant(Object o)
+{
+ TypeInfo_Class c;
+
+ //printf("__d_invariant(%p)\n", o);
+
+ // BUG: needs to be filename/line of caller, not library routine
+ assert(o !is null); // just do null check, not invariant check
+
+ c = typeid(o);
+ do
+ {
+ if (c.classInvariant)
+ {
+ (*c.classInvariant)(o);
+ }
+ c = c.base;
+ } while (c);
+}
+
+/+
+class TypeInfo_Immutable : TypeInfo {
+ size_t getHash(in void*) nothrow { return 0; }
+ TypeInfo base;
+}
++/
+class TypeInfo_Invariant : TypeInfo {
+ TypeInfo base;
+ override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
+ override size_t size() const { return base.size; }
+ override const(TypeInfo) next() const { return base; }
+}
+class TypeInfo_Shared : TypeInfo {
+ override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
+ TypeInfo base;
+ override size_t size() const { return base.size; }
+ override const(TypeInfo) next() const { return base; }
+}
+class TypeInfo_Inout : TypeInfo {
+ override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
+ TypeInfo base;
+ override size_t size() const { return base.size; }
+ override const(TypeInfo) next() const { return base; }
+}
+
+class TypeInfo_Struct : TypeInfo {
+ string name;
+ void[] m_init;
+ @safe pure nothrow
+ {
+ size_t function(in void*) xtoHash;
+ bool function(in void*, in void*) xopEquals;
+ int function(in void*, in void*) xopCmp;
+ string function(in void*) xtoString;
+ }
+ uint m_flags;
+ union {
+ void function(void*) xdtor;
+ void function(void*, const TypeInfo_Struct) xdtorti;
+ }
+ void function(void*) xpostblit;
+ uint align_;
+ immutable(void)* rtinfo;
+ // private struct _memberFunc //? Is it necessary
+ // {
+ // union
+ // {
+ // struct // delegate
+ // {
+ // const void* ptr;
+ // const void* funcptr;
+ // }
+ // @safe pure nothrow
+ // {
+ // bool delegate(in void*) xopEquals;
+ // int delegate(in void*) xopCmp;
+ // }
+ // }
+ // }
+
+ enum StructFlags : uint
+ {
+ hasPointers = 0x1,
+ isDynamicType = 0x2, // built at runtime, needs type info in xdtor
+ }
+ override size_t size() const { return m_init.length; }
+ override @property uint flags() nothrow pure const @safe @nogc { return m_flags; }
+
+ override size_t toHash() const
+ {
+ return hashOf(this.name);
+ }
+ override bool opEquals(Object o)
+ {
+ if (this is o)
+ return true;
+ auto s = cast(const TypeInfo_Struct)o;
+ return s && this.name == s.name;
+ }
+ override size_t getHash(scope const void* p) @trusted pure nothrow const
+ {
+ assert(p);
+ if (xtoHash)
+ {
+ return (*xtoHash)(p);
+ }
+ else
+ {
+ return hashOf(p[0 .. initializer().length]);
+ }
+ }
+
+
+ override bool equals(in void* p1, in void* p2) @trusted const
+ {
+ if (!p1 || !p2)
+ return false;
+ else if (xopEquals)
+ return (*xopEquals)(p1, p2);
+ else if (p1 == p2)
+ return true;
+ else
+ // BUG: relies on the GC not moving objects
+ return memcmp(p1, p2, m_init.length) == 0;
+ }
+ override @property size_t talign() nothrow pure const { return align_; }
+ final override void destroy(void* p) const
+ {
+ if (xdtor)
+ {
+ if (m_flags & StructFlags.isDynamicType)
+ (*xdtorti)(p, this);
+ else
+ (*xdtor)(p);
+ }
+ }
+
+ override void postblit(void* p) const
+ {
+ if (xpostblit)
+ (*xpostblit)(p);
+ }
+
+ override const(void)[] initializer() nothrow pure const @safe
+ {
+ return m_init;
+ }
+
+}
+
+extern(C) bool _xopCmp(in void*, in void*) { return false; }
+
+// }
+
+void __ArrayDtor(T)(scope T[] a)
+{
+ foreach_reverse (ref T e; a)
+ e.__xdtor();
+}
+
+TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) nothrow
+{
+ const fromSize = from.length * TFrom.sizeof;
+ const toLength = fromSize / TTo.sizeof;
+
+ if ((fromSize % TTo.sizeof) != 0)
+ {
+ //onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
+ import arsd.webassembly;
+ abort();
+ }
+
+ struct Array
+ {
+ size_t length;
+ void* ptr;
+ }
+ auto a = cast(Array*)&from;
+ a.length = toLength; // jam new length
+ return *cast(TTo[]*)a;
+}
+
+extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y)
+{
+ auto length = x.length;
+ auto tinext = ti.next;
+ auto sizeelem = tinext./*t*/size; // array element size
+ _d_arrayappendcTX(ti, x, y.length);
+ memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem);
+
+ // do postblit
+ //__doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext);
+ return x;
+}
+
+extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
+{
+ debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2. length);
+ if (a1.length != a2.length)
+ return 0; // not equal
+ if (!ti.equals(&a1, &a2))
+ return 0;
+ return 1;
+}
+
+V[K] dup(T : V[K], K, V)(T aa)
+{
+ //pragma(msg, "K = ", K, ", V = ", V);
+
+ // Bug10720 - check whether V is copyable
+ static assert(is(typeof({ V v = aa[K.init]; })),
+ "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
+
+ V[K] result;
+
+ //foreach (k, ref v; aa)
+ // result[k] = v; // Bug13701 - won't work if V is not mutable
+
+ ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
+ {
+ void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
+ memcpy(pv, &v, V.sizeof);
+ return *cast(V*)pv;
+ }
+
+ foreach (k, ref v; aa)
+ {
+ static if (!__traits(hasPostblit, V))
+ duplicateElem(k, v);
+ else static if (__traits(isStaticArray, V))
+ _doPostblit(duplicateElem(k, v)[]);
+ else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
+ (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
+ else
+ duplicateElem(k, v).__xpostblit();
+ }
+
+ return result;
+}
+
+/** ditto */
+V[K] dup(T : V[K], K, V)(T* aa)
+{
+ return (*aa).dup;
+}
+
+T[] dup(T)(scope T[] array) pure nothrow @trusted if (__traits(isPOD, T) && !is(const(T) : T))
+{
+ T[] result;
+ foreach(ref e; array) {
+ result ~= e;
+ }
+ return result;
+}
+
+
+
+T[] dup(T)(scope const(T)[] array) pure nothrow @trusted if (__traits(isPOD, T))
+{
+ T[] result;
+ foreach(ref e; array) {
+ result ~= e;
+ }
+ return result;
+}
+
+immutable(T)[] idup(T)(scope const(T)[] array) pure nothrow @trusted
+{
+ immutable(T)[] result;
+ foreach(ref e; array) {
+ result ~= e;
+ }
+ return result;
+}
+
+class Error { this(string msg) {} }
+class Throwable : Object
+{
+ interface TraceInfo
+ {
+ int opApply(scope int delegate(ref const(char[]))) const;
+ int opApply(scope int delegate(ref size_t, ref const(char[]))) const;
+ string toString() const;
+ }
+
+ string msg; /// A message describing the error.
+
+ /**
+ * The _file name of the D source code corresponding with
+ * where the error was thrown from.
+ */
+ string file;
+ /**
+ * The _line number of the D source code corresponding with
+ * where the error was thrown from.
+ */
+ size_t line;
+
+ /**
+ * The stack trace of where the error happened. This is an opaque object
+ * that can either be converted to $(D string), or iterated over with $(D
+ * foreach) to extract the items in the stack trace (as strings).
+ */
+ TraceInfo info;
+
+ /**
+ * A reference to the _next error in the list. This is used when a new
+ * $(D Throwable) is thrown from inside a $(D catch) block. The originally
+ * caught $(D Exception) will be chained to the new $(D Throwable) via this
+ * field.
+ */
+ private Throwable nextInChain;
+
+ private uint _refcount; // 0 : allocated by GC
+ // 1 : allocated by _d_newThrowable()
+ // 2.. : reference count + 1
+
+ /**
+ * Returns:
+ * A reference to the _next error in the list. This is used when a new
+ * $(D Throwable) is thrown from inside a $(D catch) block. The originally
+ * caught $(D Exception) will be chained to the new $(D Throwable) via this
+ * field.
+ */
+ @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
+
+ /**
+ * Replace next in chain with `tail`.
+ * Use `chainTogether` instead if at all possible.
+ */
+ @property void next(Throwable tail) @safe scope pure nothrow @nogc{}
+
+ /**
+ * Returns:
+ * mutable reference to the reference count, which is
+ * 0 - allocated by the GC, 1 - allocated by _d_newThrowable(),
+ * and >=2 which is the reference count + 1
+ * Note:
+ * Marked as `@system` to discourage casual use of it.
+ */
+ @system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
+
+ /**
+ * Loop over the chain of Throwables.
+ */
+ int opApply(scope int delegate(Throwable) dg)
+ {
+ int result = 0;
+ for (Throwable t = this; t; t = t.nextInChain)
+ {
+ result = dg(t);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Append `e2` to chain of exceptions that starts with `e1`.
+ * Params:
+ * e1 = start of chain (can be null)
+ * e2 = second part of chain (can be null)
+ * Returns:
+ * Throwable that is at the start of the chain; null if both `e1` and `e2` are null
+ */
+ static @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2)
+ {
+ if (!e1)
+ return e2;
+ if (!e2)
+ return e1;
+ if (e2.refcount())
+ ++e2.refcount();
+
+ for (auto e = e1; 1; e = e.nextInChain)
+ {
+ if (!e.nextInChain)
+ {
+ e.nextInChain = e2;
+ break;
+ }
+ }
+ return e1;
+ }
+
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
+ {
+ this.msg = msg;
+ this.nextInChain = nextInChain;
+ if (nextInChain && nextInChain._refcount)
+ ++nextInChain._refcount;
+ //this.info = _d_traceContext();
+ }
+
+ @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
+ {
+ this(msg, nextInChain);
+ this.file = file;
+ this.line = line;
+ //this.info = _d_traceContext();
+ }
+
+ @trusted nothrow ~this(){}
+
+ /**
+ * Overrides $(D Object.toString) and returns the error message.
+ * Internally this forwards to the $(D toString) overload that
+ * takes a $(D_PARAM sink) delegate.
+ */
+ override string toString()
+ {
+ string s;
+ toString((in buf) { s ~= buf; });
+ return s;
+ }
+
+ /**
+ * The Throwable hierarchy uses a toString overload that takes a
+ * $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be
+ * performed in certain error situations. Override this $(D
+ * toString) method to customize the error message.
+ */
+ void toString(scope void delegate(in char[]) sink) const{}
+
+ /**
+ * Get the message describing the error.
+ * Base behavior is to return the `Throwable.msg` field.
+ * Override to return some other error message.
+ *
+ * Returns:
+ * Error message
+ */
+ const(char)[] message() const
+ {
+ return this.msg;
+ }
+}
+class Exception : Throwable
+{
+
+ /**
+ * Creates a new instance of Exception. The nextInChain parameter is used
+ * internally and should always be $(D null) when passed by user code.
+ * This constructor does not automatically throw the newly-created
+ * Exception; the $(D throw) statement should be used for that purpose.
+ */
+ @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
+ {
+ super(msg, file, line, nextInChain);
+ }
+
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
+ {
+ super(msg, file, line, nextInChain);
+ }
+}
+
+
+import core.internal.hash;
diff --git a/external/arsd-webassembly/std/random.d b/external/arsd-webassembly/std/random.d
new file mode 100644
index 0000000..aa56fd9
--- /dev/null
+++ b/external/arsd-webassembly/std/random.d
@@ -0,0 +1,8 @@
+module std.random;
+
+import arsd.webassembly;
+
+int uniform(int low, int high) {
+ int max = high - low;
+ return low + eval!int(q{ return Math.floor(Math.random() * $0); }, max);
+}
diff --git a/external/arsd-webassembly/std/stdio.d b/external/arsd-webassembly/std/stdio.d
new file mode 100644
index 0000000..5a9275b
--- /dev/null
+++ b/external/arsd-webassembly/std/stdio.d
@@ -0,0 +1,17 @@
+module std.stdio;
+
+import arsd.webassembly;
+
+void writeln(T...)(T t) {
+ eval(q{
+ var str = "";
+ for(var i = 0; i < arguments.length; i++)
+ str += arguments[i];
+
+ str += "\n";
+
+ var txt = document.createTextNode(str);
+ var fd = document.getElementById("stdout");
+ fd.appendChild(txt);
+ }, t);
+}
\ No newline at end of file
diff --git a/external/printf/printf.c b/external/printf/printf.c
new file mode 100644
index 0000000..6398e6f
--- /dev/null
+++ b/external/printf/printf.c
@@ -0,0 +1,840 @@
+///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources. These routines are thread
+// safe and reentrant!
+// Use this instead of the bloated standard/newlib printf cause these use
+// malloc for printf (and may not be thread safe).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include "printf.h"
+
+
+// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
+// printf_config.h header file
+// default: undefined
+#ifdef PRINTF_INCLUDE_CONFIG_H
+#include "printf_config.h"
+#endif
+
+
+// 'ntoa' conversion buffer size, this must be big enough to hold one converted
+// numeric number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_NTOA_BUFFER_SIZE
+#define PRINTF_NTOA_BUFFER_SIZE 32U
+#endif
+
+// 'ftoa' conversion buffer size, this must be big enough to hold one converted
+// float number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_FTOA_BUFFER_SIZE
+#define PRINTF_FTOA_BUFFER_SIZE 32U
+#endif
+
+// support for the floating point type (%f)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
+#define PRINTF_SUPPORT_FLOAT
+#endif
+
+// support for exponential floating point notation (%e/%g)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
+#define PRINTF_SUPPORT_EXPONENTIAL
+#endif
+
+// define the default floating point precision
+// default: 6 digits
+#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
+#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
+#endif
+
+// define the largest float suitable to print with %f
+// default: 1e9
+#ifndef PRINTF_MAX_FLOAT
+#define PRINTF_MAX_FLOAT 1e9
+#endif
+
+// support for the long long types (%llu or %p)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
+#define PRINTF_SUPPORT_LONG_LONG
+#endif
+
+// support for the ptrdiff_t type (%t)
+// ptrdiff_t is normally defined in as long or long long type
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
+#define PRINTF_SUPPORT_PTRDIFF_T
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// internal flag definitions
+#define FLAGS_ZEROPAD (1U << 0U)
+#define FLAGS_LEFT (1U << 1U)
+#define FLAGS_PLUS (1U << 2U)
+#define FLAGS_SPACE (1U << 3U)
+#define FLAGS_HASH (1U << 4U)
+#define FLAGS_UPPERCASE (1U << 5U)
+#define FLAGS_CHAR (1U << 6U)
+#define FLAGS_SHORT (1U << 7U)
+#define FLAGS_LONG (1U << 8U)
+#define FLAGS_LONG_LONG (1U << 9U)
+#define FLAGS_PRECISION (1U << 10U)
+#define FLAGS_ADAPT_EXP (1U << 11U)
+
+
+// import float.h for DBL_MAX
+#if defined(PRINTF_SUPPORT_FLOAT)
+#include
+#endif
+
+
+// output function type
+typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
+
+// internal buffer output
+static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ if (idx < maxlen) {
+ ((char*)buffer)[idx] = character;
+ }
+}
+
+// internal null output
+static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ (void)character; (void)buffer; (void)idx; (void)maxlen;
+}
+
+// internal secure strlen
+// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
+static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
+{
+ const char* s;
+ for (s = str; *s && maxsize--; ++s);
+ return (unsigned int)(s - str);
+}
+
+
+// internal test if char is a digit (0-9)
+// \return true if char is a digit
+static inline bool _is_digit(char ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+
+// internal ASCII string to unsigned int conversion
+static unsigned int _atoi(const char** str)
+{
+ unsigned int i = 0U;
+ while (_is_digit(**str)) {
+ i = i * 10U + (unsigned int)(*((*str)++) - '0');
+ }
+ return i;
+}
+
+
+// output the specified string in reverse, taking care of any zero-padding
+static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
+{
+ const size_t start_idx = idx;
+
+ // pad spaces up to given width
+ if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+ for (size_t i = len; i < width; i++) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ // reverse string
+ while (len) {
+ out(buf[--len], buffer, idx++, maxlen);
+ }
+
+ // append pad spaces up to given width
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ return idx;
+}
+
+
+// internal itoa format
+static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ // pad leading zeros
+ if (!(flags & FLAGS_LEFT)) {
+ if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ // handle hash
+ if (flags & FLAGS_HASH) {
+ if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
+ len--;
+ if (len && (base == 16U)) {
+ len--;
+ }
+ }
+ if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'x';
+ }
+ else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'X';
+ }
+ else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'b';
+ }
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ }
+ else if (flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+// internal itoa for 'long' type
+static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+
+
+// internal itoa for 'long long' type
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+#endif // PRINTF_SUPPORT_LONG_LONG
+
+
+#if defined(PRINTF_SUPPORT_FLOAT)
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
+static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
+#endif
+
+
+// internal ftoa for fixed decimal floating point
+static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_FTOA_BUFFER_SIZE];
+ size_t len = 0U;
+ double diff = 0.0;
+
+ // powers of 10
+ static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+
+ // test for special values
+ if (value != value)
+ return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
+ if (value < -DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
+ if (value > DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
+
+ // test for very large values
+ // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
+ if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
+#else
+ return 0U;
+#endif
+ }
+
+ // test for negative
+ bool negative = false;
+ if (value < 0) {
+ negative = true;
+ value = 0 - value;
+ }
+
+ // set default precision, if not set explicitly
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+ // limit precision to 9, cause a prec >= 10 can lead to overflow errors
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
+ buf[len++] = '0';
+ prec--;
+ }
+
+ int whole = (int)value;
+ double tmp = (value - whole) * pow10[prec];
+ unsigned long frac = (unsigned long)tmp;
+ diff = tmp - frac;
+
+ if (diff > 0.5) {
+ ++frac;
+ // handle rollover, e.g. case 0.99 with prec 1 is 1.0
+ if (frac >= pow10[prec]) {
+ frac = 0;
+ ++whole;
+ }
+ }
+ else if (diff < 0.5) {
+ }
+ else if ((frac == 0U) || (frac & 1U)) {
+ // if halfway, round up if odd OR if last digit is 0
+ ++frac;
+ }
+
+ if (prec == 0U) {
+ diff = value - (double)whole;
+ if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
+ // exactly 0.5 and ODD, then round up
+ // 1.5 -> 2, but 2.5 -> 2
+ ++whole;
+ }
+ }
+ else {
+ unsigned int count = prec;
+ // now do fractional part, as an unsigned number
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ --count;
+ buf[len++] = (char)(48U + (frac % 10U));
+ if (!(frac /= 10U)) {
+ break;
+ }
+ }
+ // add extra 0s
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
+ buf[len++] = '0';
+ }
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ // add decimal
+ buf[len++] = '.';
+ }
+ }
+
+ // do whole part, number is reversed
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ buf[len++] = (char)(48 + (whole % 10));
+ if (!(whole /= 10)) {
+ break;
+ }
+ }
+
+ // pad leading zeros
+ if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+ if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ }
+ else if (flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse
+static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ // check for NaN and special values
+ if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
+ return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
+ }
+
+ // determine the sign
+ const bool negative = value < 0;
+ if (negative) {
+ value = -value;
+ }
+
+ // default precision
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+
+ // determine the decimal exponent
+ // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
+ union {
+ uint64_t U;
+ double F;
+ } conv;
+
+ conv.F = value;
+ int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
+ conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
+ // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
+ int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
+ // now we want to compute 10^expval but we want to be sure it won't overflow
+ exp2 = (int)(expval * 3.321928094887362 + 0.5);
+ const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
+ const double z2 = z * z;
+ conv.U = (uint64_t)(exp2 + 1023) << 52U;
+ // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+ conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+ // correct for rounding errors
+ if (value < conv.F) {
+ expval--;
+ conv.F /= 10;
+ }
+
+ // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
+ unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
+
+ // in "%g" mode, "prec" is the number of *significant figures* not decimals
+ if (flags & FLAGS_ADAPT_EXP) {
+ // do we want to fall-back to "%f" mode?
+ if ((value >= 1e-4) && (value < 1e6)) {
+ if ((int)prec > expval) {
+ prec = (unsigned)((int)prec - expval - 1);
+ }
+ else {
+ prec = 0;
+ }
+ flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
+ // no characters in exponent
+ minwidth = 0U;
+ expval = 0;
+ }
+ else {
+ // we use one sigfig for the whole part
+ if ((prec > 0) && (flags & FLAGS_PRECISION)) {
+ --prec;
+ }
+ }
+ }
+
+ // will everything fit?
+ unsigned int fwidth = width;
+ if (width > minwidth) {
+ // we didn't fall-back so subtract the characters required for the exponent
+ fwidth -= minwidth;
+ } else {
+ // not enough characters, so go back to default sizing
+ fwidth = 0U;
+ }
+ if ((flags & FLAGS_LEFT) && minwidth) {
+ // if we're padding on the right, DON'T pad the floating part
+ fwidth = 0U;
+ }
+
+ // rescale the float value
+ if (expval) {
+ value /= conv.F;
+ }
+
+ // output the floating part
+ const size_t start_idx = idx;
+ idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
+
+ // output the exponent part
+ if (minwidth) {
+ // output the exponential symbol
+ out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
+ // output the exponent value
+ idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
+ // might need to right-pad spaces
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
+ }
+ }
+ return idx;
+}
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+
+
+// internal vsnprintf
+static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
+{
+ unsigned int flags, width, precision, n;
+ size_t idx = 0U;
+
+ if (!buffer) {
+ // use null output function
+ out = _out_null;
+ }
+
+ while (*format)
+ {
+ // format specifier? %[flags][width][.precision][length]
+ if (*format != '%') {
+ // no
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ continue;
+ }
+ else {
+ // yes, evaluate it
+ format++;
+ }
+
+ // evaluate flags
+ flags = 0U;
+ do {
+ switch (*format) {
+ case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
+ case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
+ case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
+ case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
+ case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
+ default : n = 0U; break;
+ }
+ } while (n);
+
+ // evaluate width field
+ width = 0U;
+ if (_is_digit(*format)) {
+ width = _atoi(&format);
+ }
+ else if (*format == '*') {
+ const int w = va_arg(va, int);
+ if (w < 0) {
+ flags |= FLAGS_LEFT; // reverse padding
+ width = (unsigned int)-w;
+ }
+ else {
+ width = (unsigned int)w;
+ }
+ format++;
+ }
+
+ // evaluate precision field
+ precision = 0U;
+ if (*format == '.') {
+ flags |= FLAGS_PRECISION;
+ format++;
+ if (_is_digit(*format)) {
+ precision = _atoi(&format);
+ }
+ else if (*format == '*') {
+ const int prec = (int)va_arg(va, int);
+ precision = prec > 0 ? (unsigned int)prec : 0U;
+ format++;
+ }
+ }
+
+ // evaluate length field
+ switch (*format) {
+ case 'l' :
+ flags |= FLAGS_LONG;
+ format++;
+ if (*format == 'l') {
+ flags |= FLAGS_LONG_LONG;
+ format++;
+ }
+ break;
+ case 'h' :
+ flags |= FLAGS_SHORT;
+ format++;
+ if (*format == 'h') {
+ flags |= FLAGS_CHAR;
+ format++;
+ }
+ break;
+#if defined(PRINTF_SUPPORT_PTRDIFF_T)
+ case 't' :
+ flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+#endif
+ case 'j' :
+ flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ case 'z' :
+ flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ default :
+ break;
+ }
+
+ // evaluate specifier
+ switch (*format) {
+ case 'd' :
+ case 'i' :
+ case 'u' :
+ case 'x' :
+ case 'X' :
+ case 'o' :
+ case 'b' : {
+ // set the base
+ unsigned int base;
+ if (*format == 'x' || *format == 'X') {
+ base = 16U;
+ }
+ else if (*format == 'o') {
+ base = 8U;
+ }
+ else if (*format == 'b') {
+ base = 2U;
+ }
+ else {
+ base = 10U;
+ flags &= ~FLAGS_HASH; // no hash for dec format
+ }
+ // uppercase
+ if (*format == 'X') {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ // no plus or space flag for u, x, X, o, b
+ if ((*format != 'i') && (*format != 'd')) {
+ flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+ }
+
+ // ignore '0' flag when precision is given
+ if (flags & FLAGS_PRECISION) {
+ flags &= ~FLAGS_ZEROPAD;
+ }
+
+ // convert the integer
+ if ((*format == 'i') || (*format == 'd')) {
+ // signed
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const long long value = va_arg(va, long long);
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+#endif
+ }
+ else if (flags & FLAGS_LONG) {
+ const long value = va_arg(va, long);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ }
+ else {
+ const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ }
+ }
+ else {
+ // unsigned
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
+#endif
+ }
+ else if (flags & FLAGS_LONG) {
+ idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
+ }
+ else {
+ const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
+ }
+ }
+ format++;
+ break;
+ }
+#if defined(PRINTF_SUPPORT_FLOAT)
+ case 'f' :
+ case 'F' :
+ if (*format == 'F') flags |= FLAGS_UPPERCASE;
+ idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
+ if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
+ idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+ case 'c' : {
+ unsigned int l = 1U;
+ // pre padding
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // char output
+ out((char)va_arg(va, int), buffer, idx++, maxlen);
+ // post padding
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 's' : {
+ const char* p = va_arg(va, char*);
+ unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
+ // pre padding
+ if (flags & FLAGS_PRECISION) {
+ l = (l < precision ? l : precision);
+ }
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // string output
+ while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
+ out(*(p++), buffer, idx++, maxlen);
+ }
+ // post padding
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 'p' : {
+ width = sizeof(void*) * 2U;
+ flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
+ if (is_ll) {
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
+ }
+ else {
+#endif
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ }
+#endif
+ format++;
+ break;
+ }
+
+ case '%' :
+ out('%', buffer, idx++, maxlen);
+ format++;
+ break;
+
+ default :
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ break;
+ }
+ }
+
+ // termination
+ out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
+
+ // return written chars without terminating \0
+ return (int)idx;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+int sprintf_(char* buffer, const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
+ va_end(va);
+ return ret;
+}
+
diff --git a/external/printf/printf.h b/external/printf/printf.h
new file mode 100644
index 0000000..2c17739
--- /dev/null
+++ b/external/printf/printf.h
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources.
+// Use this instead of bloated standard/newlib printf.
+// These routines are thread safe and reentrant.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _PRINTF_H_
+#define _PRINTF_H_
+
+#include
+#include
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Tiny sprintf implementation
+ * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
+ * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define sprintf sprintf_
+int sprintf_(char* buffer, const char* format, ...);
+
+
+/**
+ * Tiny snprintf/vsnprintf implementation
+ * \param buffer A pointer to the buffer where to store the formatted string
+ * \param count The maximum number of characters to store in the buffer, including a terminating null character
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that COULD have been written into the buffer, not counting the terminating
+ * null character. A value equal or larger than count indicates truncation. Only when the returned value
+ * is non-negative and less than count, the string has been completely written.
+ */
+#define snprintf snprintf_
+#define vsnprintf vsnprintf_
+int snprintf_(char* buffer, size_t count, const char* format, ...);
+int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
+
+
+/**
+ * Tiny vprintf implementation
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define vprintf vprintf_
+int vprintf_(const char* format, va_list va);
+
+
+/**
+ * printf with output function
+ * You may use this as dynamic alternative to printf() with its fixed _putchar() output
+ * \param out An output function which takes one character and an argument pointer
+ * \param arg An argument pointer for user data passed to output function
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are sent to the output function, not counting the terminating null character
+ */
+int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // _PRINTF_H_
diff --git a/external/tinyalloc/LICENCE b/external/tinyalloc/LICENCE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/external/tinyalloc/LICENCE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/external/tinyalloc/tinyalloc.c b/external/tinyalloc/tinyalloc.c
new file mode 100644
index 0000000..eb9fa3f
--- /dev/null
+++ b/external/tinyalloc/tinyalloc.c
@@ -0,0 +1,270 @@
+#include "tinyalloc.h"
+#include
+
+#ifdef TA_DEBUG
+extern void print_s(char *);
+extern void print_i(size_t);
+#else
+#define print_s(X)
+#define print_i(X)
+#endif
+
+typedef struct Block Block;
+
+struct Block {
+ void *addr;
+ Block *next;
+ size_t size;
+};
+
+typedef struct {
+ Block *free; // first free block
+ Block *used; // first used block
+ Block *fresh; // first available blank block
+ size_t top; // top free addr
+} Heap;
+
+static Heap *heap = NULL;
+static const void *heap_limit = NULL;
+static size_t heap_split_thresh;
+static size_t heap_alignment;
+static size_t heap_max_blocks;
+
+/**
+ * If compaction is enabled, inserts block
+ * into free list, sorted by addr.
+ * If disabled, add block has new head of
+ * the free list.
+ */
+static void insert_block(Block *block) {
+#ifndef TA_DISABLE_COMPACT
+ Block *ptr = heap->free;
+ Block *prev = NULL;
+ while (ptr != NULL) {
+ if ((size_t)block->addr <= (size_t)ptr->addr) {
+ print_s("insert");
+ print_i((size_t)ptr);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ if (prev != NULL) {
+ if (ptr == NULL) {
+ print_s("new tail");
+ }
+ prev->next = block;
+ } else {
+ print_s("new head");
+ heap->free = block;
+ }
+ block->next = ptr;
+#else
+ block->next = heap->free;
+ heap->free = block;
+#endif
+}
+
+#ifndef TA_DISABLE_COMPACT
+static void release_blocks(Block *scan, Block *to) {
+ Block *scan_next;
+ while (scan != to) {
+ print_s("release");
+ print_i((size_t)scan);
+ scan_next = scan->next;
+ scan->next = heap->fresh;
+ heap->fresh = scan;
+ scan->addr = 0;
+ scan->size = 0;
+ scan = scan_next;
+ }
+}
+
+static void compact() {
+ Block *ptr = heap->free;
+ Block *prev;
+ Block *scan;
+ while (ptr != NULL) {
+ prev = ptr;
+ scan = ptr->next;
+ while (scan != NULL &&
+ (size_t)prev->addr + prev->size == (size_t)scan->addr) {
+ print_s("merge");
+ print_i((size_t)scan);
+ prev = scan;
+ scan = scan->next;
+ }
+ if (prev != ptr) {
+ size_t new_size =
+ (size_t)prev->addr - (size_t)ptr->addr + prev->size;
+ print_s("new size");
+ print_i(new_size);
+ ptr->size = new_size;
+ Block *next = prev->next;
+ // make merged blocks available
+ release_blocks(ptr->next, prev->next);
+ // relink
+ ptr->next = next;
+ }
+ ptr = ptr->next;
+ }
+}
+#endif
+
+bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment) {
+ 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 = (Block *)(heap + 1);
+ heap->top = (size_t)(heap->fresh + heap_blocks);
+
+ Block *block = heap->fresh;
+ size_t i = heap_max_blocks - 1;
+ while (i--) {
+ block->next = block + 1;
+ block++;
+ }
+ block->next = NULL;
+ return true;
+}
+
+bool ta_free(void *free) {
+ Block *block = heap->used;
+ Block *prev = NULL;
+ while (block != NULL) {
+ if (free == block->addr) {
+ if (prev) {
+ prev->next = block->next;
+ } else {
+ heap->used = block->next;
+ }
+ insert_block(block);
+#ifndef TA_DISABLE_COMPACT
+ compact();
+#endif
+ return true;
+ }
+ prev = block;
+ block = block->next;
+ }
+ return false;
+}
+
+static Block *alloc_block(size_t num) {
+ Block *ptr = heap->free;
+ Block *prev = NULL;
+ size_t top = heap->top;
+ num = (num + heap_alignment - 1) & -heap_alignment;
+ while (ptr != NULL) {
+ const int is_top = ((size_t)ptr->addr + ptr->size >= top) && ((size_t)ptr->addr + num <= (size_t)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;
+ if (is_top) {
+ print_s("resize top block");
+ ptr->size = num;
+ heap->top = (size_t)ptr->addr + num;
+#ifndef TA_DISABLE_SPLIT
+ } else if (heap->fresh != NULL) {
+ size_t excess = ptr->size - num;
+ if (excess >= heap_split_thresh) {
+ ptr->size = num;
+ Block *split = heap->fresh;
+ heap->fresh = split->next;
+ split->addr = (void *)((size_t)ptr->addr + num);
+ print_s("split");
+ print_i((size_t)split->addr);
+ split->size = excess;
+ insert_block(split);
+#ifndef TA_DISABLE_COMPACT
+ compact();
+#endif
+ }
+#endif
+ }
+ return ptr;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ // no matching free blocks
+ // see if any other blocks available
+ size_t new_top = top + num;
+ if (heap->fresh != NULL && new_top <= (size_t)heap_limit) {
+ ptr = heap->fresh;
+ heap->fresh = ptr->next;
+ ptr->addr = (void *)top;
+ ptr->next = heap->used;
+ ptr->size = num;
+ heap->used = ptr;
+ heap->top = new_top;
+ return ptr;
+ }
+ return NULL;
+}
+
+void *ta_alloc(size_t num) {
+ Block *block = alloc_block(num);
+ if (block != NULL) {
+ return block->addr;
+ }
+ return NULL;
+}
+
+static void memclear(void *ptr, size_t num) {
+ size_t *ptrw = (size_t *)ptr;
+ size_t numw = (num & -sizeof(size_t)) / sizeof(size_t);
+ while (numw--) {
+ *ptrw++ = 0;
+ }
+ num &= (sizeof(size_t) - 1);
+ uint8_t *ptrb = (uint8_t *)ptrw;
+ while (num--) {
+ *ptrb++ = 0;
+ }
+}
+
+void *ta_calloc(size_t num, size_t size) {
+ num *= size;
+ Block *block = alloc_block(num);
+ if (block != NULL) {
+ memclear(block->addr, num);
+ return block->addr;
+ }
+ return NULL;
+}
+
+static size_t count_blocks(Block *ptr) {
+ size_t num = 0;
+ while (ptr != NULL) {
+ num++;
+ ptr = ptr->next;
+ }
+ return num;
+}
+
+size_t ta_num_free() {
+ return count_blocks(heap->free);
+}
+
+size_t ta_num_used() {
+ return count_blocks(heap->used);
+}
+
+size_t ta_num_fresh() {
+ return count_blocks(heap->fresh);
+}
+
+bool ta_check() {
+ return heap_max_blocks == ta_num_free() + ta_num_used() + ta_num_fresh();
+}
diff --git a/external/tinyalloc/tinyalloc.h b/external/tinyalloc/tinyalloc.h
new file mode 100644
index 0000000..0d00596
--- /dev/null
+++ b/external/tinyalloc/tinyalloc.h
@@ -0,0 +1,23 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+#include
+
+uint32_t grow(uint32_t pages);
+
+bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment);
+void *ta_alloc(size_t num);
+void *ta_calloc(size_t num, size_t size);
+bool ta_free(void *ptr);
+
+size_t ta_num_free();
+size_t ta_num_used();
+size_t ta_num_fresh();
+bool ta_check();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/external/xxhash/xxhash.c b/external/xxhash/xxhash.c
index e60cc37..a23b7d6 100644
--- a/external/xxhash/xxhash.c
+++ b/external/xxhash/xxhash.c
@@ -38,5 +38,6 @@
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
#define XXH_IMPLEMENTATION /* access definitions */
+#define XXH_NO_STDLIB 1
#include "xxhash.h"
diff --git a/external/xxhash/xxhash.h b/external/xxhash/xxhash.h
index c4cb3ba..2ee7597 100644
--- a/external/xxhash/xxhash.h
+++ b/external/xxhash/xxhash.h
@@ -2394,35 +2394,57 @@ static void XXH_free(void* p) { free(p); }
#endif /* XXH_NO_STDLIB */
-#ifndef XXH_memcpy
-/*!
- * @internal
- * @brief XXH_memcpy() macro can be redirected at compile time
- */
-# include
-# define XXH_memcpy memcpy
-#endif
+void *
+memcpy(void *dest, const void *src, size_t n)
+{
+ if(!dest || n == 0) return dest;
-#ifndef XXH_memset
-/*!
- * @internal
- * @brief XXH_memset() macro can be redirected at compile time
- */
-# include
-# define XXH_memset memset
-#endif
+ uint8_t *dp = (uint8_t *)dest;
+ uint8_t *sp = (uint8_t *)src;
-#ifndef XXH_memcmp
-/*!
- * @internal
- * @brief XXH_memcmp() macro can be redirected at compile time
- * Note: only needed by XXH128.
- */
-# include
-# define XXH_memcmp memcmp
-#endif
+ for(size_t i = 0; i < n; i += 1)
+ {
+ dp[i] = sp[i];
+ }
+ return dest;
+}
+void *
+memset(void *dest, int ch, size_t n)
+{
+ if(!dest || n == 0) return dest;
+
+ uint8_t *p = (uint8_t *)dest;
+ uint8_t v = (uint8_t )(ch & 0xFF);
+
+ for(size_t i = 0; i < n; i += 1)
+ {
+ p[i] = v;
+ }
+
+ return dest;
+}
+
+int
+memcmp(const void *s1, const void *s2, size_t n)
+{
+ int result = 0;
+
+ uint8_t *p1 = (uint8_t *)s1;
+ uint8_t *p2 = (uint8_t *)s2;
+
+ for(size_t i = 0; i < n; i += 1)
+ {
+ result += p1[i] - p2[i];
+ }
+
+ return result;
+}
+
+#define XXH_memcpy memcpy
+#define XXH_memset memset
+#define XXH_memcmp memcmp
#include /* ULLONG_MAX */
diff --git a/fonts.d b/fonts.d
index 64f6d5e..2f253f4 100644
--- a/fonts.d
+++ b/fonts.d
@@ -5,10 +5,6 @@ import dlib.aliases;
import dlib.util;
import dlib.alloc;
import dlib.math;
-import std.math.traits : isNaN;
-import std.math : Floor = floor;
-import std.algorithm.comparison : clamp;
-import std.algorithm.sorting;
const u64 FONT_MAX_BANDS = 8;
const u64 FONT_TEX_WIDTH = 4096;
@@ -162,6 +158,9 @@ struct Glyph
f32 atlas_top;
}
+static if(NativeTarget)
+{
+
__gshared FT_Library FT_LIB;
alias FontFace = FT_Face;
@@ -388,6 +387,8 @@ CreateAtlas(Arena* arena, FontFace font, u32 size, UVec2 atlas_dim)
return atlas;
}
+}
+
void
BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info, u32* curve_index, stbtt_fontinfo* stb_font_info)
{
@@ -421,14 +422,14 @@ BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info
f32 dx = x-cx;
f32 dy = y-cy;
- if(fabs(dx) < 0.1 && fabs(dy) < 0.1)
+ if(Abs(dx) < 0.1 && Abs(dy) < 0.1)
{
cx = x;
cy = y;
break;
}
- f32 length = sqrt(dx*dx + dy*dy);
+ f32 length = Sqrt(dx*dx + dy*dy);
f32 nx = -dy/length*0.05;
f32 ny = -dx/length*0.05;
@@ -554,7 +555,7 @@ LoadFontCurves(Arena* arena, SlugFontInfo* font_info, u8[] font_file_data)
curve_texel_count += font_info.glyphs[i].curve_count*2;
}
- u32 curve_texel_height = clamp(cast(u32)((curve_texel_count+FONT_TEX_WIDTH-1) / FONT_TEX_WIDTH), 1, u32.max);
+ u32 curve_texel_height = Clamp(cast(u32)((curve_texel_count+FONT_TEX_WIDTH-1) / FONT_TEX_WIDTH), 1, u32.max);
u32[] curve_start_indices = Alloc!(u32)(arena, font_info.glyphs.length);
SlugTexel!(f32) curve_texel = {
@@ -578,14 +579,14 @@ LoadFontCurves(Arena* arena, SlugFontInfo* font_info, u8[] font_file_data)
UVec2 t0 = UVec2(curve_index%FONT_TEX_WIDTH, curve_index/FONT_TEX_WIDTH);
u32 offset = cast(u32)((t0.y*FONT_TEX_WIDTH + t0.x) * 4);
- curve_texel.data[offset .. offset+4] = [curve.p1.x, curve.p1.y, curve.p2.x, curve.p2.y];
+ Assign(curve_texel.data[offset .. offset+4], curve.p1.x, curve.p1.y, curve.p2.x, curve.p2.y);
curve_index += 1;
UVec2 t1 = UVec2(curve_index%FONT_TEX_WIDTH, curve_index/FONT_TEX_WIDTH);
offset = cast(u32)((t1.y*FONT_TEX_WIDTH + t1.x) * 4);
- curve_texel.data[offset .. offset+4] = [curve.p3.x, curve.p3.y, 0.0, 0.0];
+ Assign(curve_texel.data[offset .. offset+4], curve.p3.x, curve.p3.y, 0.0, 0.0);
curve_index += 1;
}
@@ -617,7 +618,7 @@ LoadFontCurves(Arena* arena, SlugFontInfo* font_info, u8[] font_file_data)
}
}
- u32 band_texel_height = clamp(cast(u32)((band_total_count+FONT_TEX_WIDTH-1) / FONT_TEX_WIDTH), 1U, u32.max);
+ u32 band_texel_height = Clamp(cast(u32)((band_total_count+FONT_TEX_WIDTH-1) / FONT_TEX_WIDTH), 1U, u32.max);
SlugTexel!(u32) band_texel = {
data: Alloc!(u32)(arena, FONT_TEX_WIDTH*band_texel_height*4),
width: FONT_TEX_WIDTH,
@@ -683,7 +684,7 @@ LoadFontCurves(Arena* arena, SlugFontInfo* font_info, u8[] font_file_data)
count = band_data.bands[1][index].count;
}
- band_texel.data[di .. di+2] = [count, offsets[j]];
+ Assign(band_texel.data[di .. di+2], count, offsets[j]);
}
foreach(j; 0 .. band_total)
@@ -700,7 +701,7 @@ LoadFontCurves(Arena* arena, SlugFontInfo* font_info, u8[] font_file_data)
u32 tl = ls+k;
u32 di = cast(u32)((tl/FONT_TEX_WIDTH*FONT_TEX_WIDTH + tl%FONT_TEX_WIDTH) * 4);
- band_texel.data[di .. di+2] = [ct%FONT_TEX_WIDTH, ct/FONT_TEX_WIDTH];
+ Assign(band_texel.data[di .. di+2], ct%FONT_TEX_WIDTH, ct/FONT_TEX_WIDTH);
}
}
@@ -729,7 +730,8 @@ SlugSortBand(Arena* arena, SlugBandEntry* entry, SlugCurve[] curves, u32 axis)
refs[i] = SlugCurveRef(entry.indices[i], mx);
}
- sort!("a.sort_key < b.sort_key")(refs);
+ assert(false, "implement sorting!");
+ // sort!("a.sort_key < b.sort_key")(refs);
foreach(i; 0 .. entry.count)
{
@@ -802,8 +804,8 @@ SlugBuildBands(Arena* arena, u32 index, SlugFontInfo* font_info, u32 band_count)
if(size.y > 0.0)
{
- u32 b0 = cast(u32)clamp(floor(curve_min.y * glyph.band_scale.y + glyph.band_offset.y), 0.0, cast(f32)(band_count-1));
- u32 b1 = cast(u32)clamp(floor(curve_max.y * glyph.band_scale.y + glyph.band_offset.y), 0.0, cast(f32)(band_count-1));
+ u32 b0 = cast(u32)Clamp(Floor(curve_min.y * glyph.band_scale.y + glyph.band_offset.y), 0.0, cast(f32)(band_count-1));
+ u32 b1 = cast(u32)Clamp(Floor(curve_max.y * glyph.band_scale.y + glyph.band_offset.y), 0.0, cast(f32)(band_count-1));
for(u32 j = b0; j <= b1; j += 1)
{
@@ -814,8 +816,8 @@ SlugBuildBands(Arena* arena, u32 index, SlugFontInfo* font_info, u32 band_count)
if(size.x > 0.0)
{
- u32 b0 = cast(u32)clamp(Floor(curve_min.x * glyph.band_scale.x + glyph.band_offset.x), 0.0, cast(f32)(band_count-1));
- u32 b1 = cast(u32)clamp(Floor(curve_max.x * glyph.band_scale.x + glyph.band_offset.x), 0.0, cast(f32)(band_count-1));
+ u32 b0 = cast(u32)Clamp(Floor(curve_min.x * glyph.band_scale.x + glyph.band_offset.x), 0.0, cast(f32)(band_count-1));
+ u32 b1 = cast(u32)Clamp(Floor(curve_max.x * glyph.band_scale.x + glyph.band_offset.x), 0.0, cast(f32)(band_count-1));
for(u32 j = b0; j <= b1; j += 1)
{
@@ -883,7 +885,7 @@ SlugLoadFontVertex(u8 ch, IVec2 pos, SlugFont* font, SlugBuffer* buffer)
vertices[3].em_space_pos = Vec2(glyph.rect.max.x, glyph.rect.min.y);
u32 vidx = vertex_start;
- indices[0 .. 6] = [vidx+0, vidx+2, vidx+3, vidx+3, vidx+1, vidx+0];
+ Assign(indices[0 .. 6], vidx+0, vidx+2, vidx+3, vidx+3, vidx+1, vidx+0);
buffer.quad_offset += 1;
diff --git a/main.wasm b/main.wasm
new file mode 100755
index 0000000..4b590d2
Binary files /dev/null and b/main.wasm differ
diff --git a/math.d b/math.d
index bbf9825..f121f51 100644
--- a/math.d
+++ b/math.d
@@ -5,15 +5,334 @@ import dlib.util;
import dlibincludes;
-import core.stdc.math : tanf, cosf, sinf, sqrtf, fabsf;
-import std.algorithm.comparison : clamp;
-
-import std.math;
-import std.math.algebraic;
import std.traits;
+import std.math.traits;
import std.meta;
-import std.format;
-import std.stdio;
+
+static if(NativeTarget)
+{
+}
+
+enum PI = 3.14159L;
+
+const u16[128] _RSQRT_TABLE = [
+ 0xb451, 0xb2f0, 0xb196, 0xb044, 0xaef9, 0xadb6, 0xac79, 0xab43,
+ 0xaa14, 0xa8eb, 0xa7c8, 0xa6aa, 0xa592, 0xa480, 0xa373, 0xa26b,
+ 0xa168, 0xa06a, 0x9f70, 0x9e7b, 0x9d8a, 0x9c9d, 0x9bb5, 0x9ad1,
+ 0x99f0, 0x9913, 0x983a, 0x9765, 0x9693, 0x95c4, 0x94f8, 0x9430,
+ 0x936b, 0x92a9, 0x91ea, 0x912e, 0x9075, 0x8fbe, 0x8f0a, 0x8e59,
+ 0x8daa, 0x8cfe, 0x8c54, 0x8bac, 0x8b07, 0x8a64, 0x89c4, 0x8925,
+ 0x8889, 0x87ee, 0x8756, 0x86c0, 0x862b, 0x8599, 0x8508, 0x8479,
+ 0x83ec, 0x8361, 0x82d8, 0x8250, 0x81c9, 0x8145, 0x80c2, 0x8040,
+ 0xff02, 0xfd0e, 0xfb25, 0xf947, 0xf773, 0xf5aa, 0xf3ea, 0xf234,
+ 0xf087, 0xeee3, 0xed47, 0xebb3, 0xea27, 0xe8a3, 0xe727, 0xe5b2,
+ 0xe443, 0xe2dc, 0xe17a, 0xe020, 0xdecb, 0xdd7d, 0xdc34, 0xdaf1,
+ 0xd9b3, 0xd87b, 0xd748, 0xd61a, 0xd4f1, 0xd3cd, 0xd2ad, 0xd192,
+ 0xd07b, 0xcf69, 0xce5b, 0xcd51, 0xcc4a, 0xcb48, 0xca4a, 0xc94f,
+ 0xc858, 0xc764, 0xc674, 0xc587, 0xc49d, 0xc3b7, 0xc2d4, 0xc1f4,
+ 0xc116, 0xc03c, 0xbf65, 0xbe90, 0xbdbe, 0xbcef, 0xbc23, 0xbb59,
+ 0xba91, 0xb9cc, 0xb90a, 0xb84a, 0xb78c, 0xb6d0, 0xb617, 0xb560,
+];
+
+union Union64
+{
+ f64 f;
+ u64 u;
+}
+
+union Union32
+{
+ f32 f;
+ u32 u;
+}
+
+auto
+AsUint(T)(T v) if(isFloatingPoint!(T))
+{
+ static if(is(T == f32))
+ {
+ Union32 u = { f: v };
+ }
+ else
+ {
+ Union64 u = { f: v };
+ }
+
+ return u.u;
+}
+
+auto
+AsFloat(T)(T v) if(isIntegral!(T))
+{
+ static if(T.sizeof == 4)
+ {
+ Union32 u = { u: v };
+ }
+ else
+ {
+ Union64 u = { u: v };
+ }
+
+ return u.f;
+}
+
+T
+_MathInvalid(T)(T v) if(isFloatingPoint!(T))
+{
+ return (v - v) / (v - v);
+}
+
+u32
+Mul32(T)(T a, T b) if(isIntegral!(T))
+{
+ return cast(u32)(cast(u64)(a*b) >> 32);
+}
+
+u64
+Mul64(u64 a, u64 b)
+{
+ u64 ahi = a>>32;
+ u64 alo = a&0xffffffff;
+ u64 bhi = b>>32;
+ u64 blo = b&0xffffffff;
+ return ahi*bhi + (ahi*blo >> 32) + (alo*bhi >> 32);
+}
+
+T
+Sqrt(T)(T x) if(isFloatingPoint!(T))
+{
+ u64 ix, top, m;
+
+ ix = AsUint(x);
+ top = ix >> 52;
+ if (top - 0x001 >= 0x7ff - 0x001) {
+ if (ix * 2 == 0)
+ {
+ return x;
+ }
+ if (ix == 0x7ff0000000000000)
+ {
+ return x;
+ }
+ if (ix > 0x7ff0000000000000)
+ {
+ return _MathInvalid(x);
+ }
+ ix = AsUint(x * 0x1p52);
+ top = ix >> 52;
+ top -= 52;
+ }
+
+ i32 even = top & 1;
+ m = (ix << 11) | 0x8000000000000000;
+ if (even)
+ {
+ m >>= 1;
+ }
+
+ top = (top + 0x3ff) >> 1;
+
+ static const u64 three = 0xc0000000;
+ u64 r, s, d, u, i;
+
+ i = (ix >> 46) % 128;
+ r = cast(u32)(_RSQRT_TABLE[cast(usize)i] << 16);
+ s = Mul32(m>>32, r);
+ d = Mul32(s, r);
+ u = three - d;
+ r = Mul32(r, u) << 1;
+ s = Mul32(s, u) << 1;
+ d = Mul32(s, r);
+ u = three - d;
+ r = Mul32(r, u) << 1;
+ r = r << 32;
+ s = Mul64(m, r);
+ d = Mul64(s, r);
+ u = (three<<32) - d;
+ s = Mul64(s, u);
+ s = (s - 2) >> 9;
+
+ u64 d0, d1, d2;
+ f64 y, t;
+ d0 = (m << 42) - s*s;
+ d1 = s - d0;
+ d2 = d1 + s + 1;
+ s += d1 >> 63;
+ s &= 0x000fffffffffffff;
+ s |= top << 52;
+ y = AsFloat(s);
+
+ static if(true)
+ {
+ u64 tiny = d2==0 ? 0 : 0x0010000000000000;
+ tiny |= (d1^d2) & 0x8000000000000000;
+ t = AsFloat(tiny);
+ y = cast(f64)(y + t);
+ }
+
+ return y;
+}
+
+T
+Abs(T)(T x) if(isSigned!(T))
+{
+ static if(isFloatingPoint!(T))
+ {
+ static if(is(T == f32))
+ {
+ Union32 f = { f: x };
+ }
+ else
+ {
+ Union64 f = { f: x };
+ }
+
+ f.u &= cast(typeof(f.u))-1 / 2;
+
+ x = f.f;
+ }
+ else
+ {
+ x = x >= 0 ? x : -x;
+ }
+
+ return x;
+}
+
+T
+Ceil(T)(T x) if(is(T == f32) || is(T == f64))
+{
+ if(!isNaN(x) && !isInfinity(x) && x != 0.0)
+ {
+ x = _Floor(x+1.0);
+ }
+
+ return x;
+}
+
+T
+Floor(T)(T x) if(is(T == f32) || is(T == f64))
+{
+ if(!isNaN(x) && !isInfinity(x) && x != 0.0)
+ {
+ x = _Floor(x);
+ }
+
+ return x;
+}
+
+T
+_Floor(T)(const T x) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(T);
+ // Take care not to trigger library calls from the compiler,
+ // while ensuring that we don't get defeated by some optimizers.
+ union floatBits
+ {
+ T rv;
+ ushort[T.sizeof/2] vu;
+
+ // Other kinds of extractors for real formats.
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ uint vi;
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ ulong vi;
+ }
+
+ floatBits y = void;
+ y.rv = x;
+
+ // Find the exponent (power of 2)
+ // Do this by shifting the raw value so that the exponent lies in the low bits,
+ // then mask out the sign bit, and subtract the bias.
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f;
+ enum mantissa_mask = F.MANTISSAMASK_INT;
+ enum sign_shift = 31;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ long exp = ((y.vi >> (T.mant_dig - 1)) & 0x7ff) - 0x3ff;
+ enum mantissa_mask = F.MANTISSAMASK_LONG;
+ enum sign_shift = 63;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 4;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 7;
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ if (exp < 0)
+ {
+ if (x < 0.0)
+ return -1.0;
+ else
+ return 0.0;
+ }
+
+ static if (F.realFormat == RealFormat.ieeeSingle ||
+ F.realFormat == RealFormat.ieeeDouble)
+ {
+ if (exp < (T.mant_dig - 1))
+ {
+ // Clear all bits representing the fraction part.
+ // Note: the fraction mask represents the floating point number 0.999999...
+ // i.e: `2.0 ^^ (exp - T.mant_dig + 1) * (fraction_mask + 1) == 1.0`
+ const fraction_mask = mantissa_mask >> exp;
+
+ if ((y.vi & fraction_mask) != 0)
+ {
+ // If 'x' is negative, then first substract (1.0 - T.epsilon) from the value.
+ if (y.vi >> sign_shift)
+ y.vi += fraction_mask;
+ y.vi &= ~fraction_mask;
+ }
+ }
+ }
+ else
+ {
+ static if (F.realFormat == RealFormat.ieeeExtended53)
+ exp = (T.mant_dig + 11 - 1) - exp; // mant_dig is really 64
+ else
+ exp = (T.mant_dig - 1) - exp;
+
+ // Zero 16 bits at a time.
+ while (exp >= 16)
+ {
+ version (LittleEndian)
+ y.vu[pos++] = 0;
+ else
+ y.vu[pos--] = 0;
+ exp -= 16;
+ }
+
+ // Clear the remaining bits.
+ if (exp > 0)
+ y.vu[pos] &= 0xffff ^ ((1 << exp) - 1);
+
+ if ((x < 0.0) && (x != y.rv))
+ y.rv -= 1.0;
+ }
+
+ return y.rv;
+}
T
_MinOrMaxInner(T, bool max, Args...)(T first, Args args)
@@ -169,7 +488,7 @@ struct Vector(T, int N)
foreach(i; 0 .. N)
{
- if(fabsf(v[i] - other.v[i]) > 0.0000009)
+ if(Abs(v[i] - other.v[i]) > 0.0000009)
{
result = false;
break;
@@ -192,13 +511,19 @@ struct Vector(T, int N)
Vector opUnary(string op)() if(op == "+" || op == "-" || op == "~" || op == "!")
{
Vector res;
- mixin(GenerateLoop!("res.v[@] = " ~ op ~ " v[@];", N)());
+ static foreach(i; 0 .. N)
+ {
+ mixin("res.v[", i, "] = " ~ op ~ "v[", i, "];\n");
+ }
return res;
}
ref Vector opOpAssign(string op, U)(U value) if(is(U: Vector))
{
- mixin(GenerateLoop!("v[@] " ~ op ~ "= value.v[@];", N)());
+ static foreach(i; 0 .. N)
+ {
+ mixin("v[", i, "] " ~op~ "= value.v[", i, "];");
+ }
return this;
}
@@ -233,7 +558,10 @@ struct Vector(T, int N)
opBinaryRight(string op, U)(U operand) if(IsConvertible!(U) && (op == "*" || op == "+" || op == "-" || op == "/"))
{
Vector res;
- mixin(GenerateLoop!("res.v[@] = v[@] "~op~" operand;", N));
+ static foreach(i; 0 .. N)
+ {
+ mixin("res.v[", i, "] = v[", i, "] " ~op~ " operand;");
+ }
return res;
}
@@ -242,25 +570,36 @@ struct Vector(T, int N)
Vector opBinary(string op, U)(U operand) if((is(U: Vector!(f32, 4)) && is(T: f32)) && (op == "*" || op == "+" || op == "-" || op == "/"))
{
Vector result;
- f32* l = &x;
- f32* r = &operand.x;
- f32* res = &result.x;
-
- asm
+
+ version(X86_64)
{
- mov R8, l;
- mov R9, r;
- mov R10, res;
- movups XMM0, x.offsetof[R8];
- movups XMM1, operand.x.offsetof[R9];
+ f32* l = &x;
+ f32* r = &operand.x;
+ f32* res = &result.x;
+
+ asm
+ {
+ mov R8, l;
+ mov R9, r;
+ mov R10, res;
+ movups XMM0, x.offsetof[R8];
+ movups XMM1, operand.x.offsetof[R9];
+ }
+ static if(op == "*") asm { mulps XMM0, XMM1; }
+ else static if(op == "-") asm { subps XMM0, XMM1; }
+ else static if(op == "+") asm { addps XMM0, XMM1; }
+ else static if(op == "/") asm { divps XMM0, XMM1; }
+ asm
+ {
+ movups result.x.offsetof[R10], XMM0;
+ }
}
- static if(op == "*") asm { mulps XMM0, XMM1; }
- else static if(op == "-") asm { subps XMM0, XMM1; }
- else static if(op == "+") asm { addps XMM0, XMM1; }
- else static if(op == "/") asm { divps XMM0, XMM1; }
- asm
+ else
{
- movups result.x.offsetof[R10], XMM0;
+ static foreach(i; 0 .. N)
+ {
+ result.v[i] = mixin("this.v[", i, "] " ~op~ " operand.v[", i, "]");
+ }
}
return result;
@@ -269,26 +608,34 @@ struct Vector(T, int N)
Vector opBinary(string op, U)(U operand) if(IsConvertible!(U) && (op == "*" || op == "+" || op == "-" || op == "/"))
{
Vector result;
- Vector other = operand;
- f32* l = &x;
- f32* r = &other.x;
- f32* res = &result.x;
-
- asm
+
+ version(X86_64)
{
- mov R8, l;
- mov R9, r;
- mov R10, res;
- movups XMM0, x.offsetof[R8];
- movups XMM1, other.x.offsetof[R9];
+ Vector other = operand;
+ f32* l = &x;
+ f32* r = &other.x;
+ f32* res = &result.x;
+
+ asm
+ {
+ mov R8, l;
+ mov R9, r;
+ mov R10, res;
+ movups XMM0, x.offsetof[R8];
+ movups XMM1, other.x.offsetof[R9];
+ }
+ static if(op == "*") asm { mulps XMM0, XMM1; }
+ else static if(op == "-") asm { subps XMM0, XMM1; }
+ else static if(op == "+") asm { addps XMM0, XMM1; }
+ else static if(op == "/") asm { divps XMM0, XMM1; }
+ asm
+ {
+ movups result.x.offsetof[R10], XMM0;
+ }
}
- static if(op == "*") asm { mulps XMM0, XMM1; }
- else static if(op == "-") asm { subps XMM0, XMM1; }
- else static if(op == "+") asm { addps XMM0, XMM1; }
- else static if(op == "/") asm { divps XMM0, XMM1; }
- asm
+ else
{
- movups result.x.offsetof[R10], XMM0;
+ static assert(false, "Currently not supported for platform");
}
return result;
@@ -299,7 +646,12 @@ struct Vector(T, int N)
Vector opBinary(string op, U)(U operand) if(is(U: Vector) && U._N == N && (op == "*" || op == "+" || op == "-" || op == "/"))
{
Vector res;
- mixin(GenerateLoop!("res.v[@] = v[@] " ~ op ~ " operand.v[@];", N)());
+
+ static foreach(i; 0 .. N)
+ {
+ mixin("res.v[", i, "] = v[", i, "] " ~ op ~ " operand.v[", i, "];");
+ }
+
return res;
}
@@ -307,7 +659,12 @@ struct Vector(T, int N)
{
Vector res;
Vector other = operand;
- mixin(GenerateLoop!("res.v[@] = v[@] " ~ op ~ " other.v[@];", N)());
+
+ static foreach(i; 0 .. N)
+ {
+ mixin("res.v[", i, "] = v[", i, "] " ~ op ~ " other.v[", i, "];");
+ }
+
return res;
}
}
@@ -327,13 +684,18 @@ struct Vector(T, int N)
U opCast(U)() if(IsVector!(U) && (U._N == _N))
{
U result;
- mixin(GenerateLoop!("res.v[@] = cast(U._T)v[@];", N)());
+
+ static foreach(i; 0 .. N)
+ {
+ rev.v[i] = cast(U._T)v[i];
+ }
+
return result;
}
- template IsConvertible(T)
+ template IsConvertible(U)
{
- enum bool IsConvertible = (!is(T : Vector)) && is(typeof({ T x; Vector v = x; }()));
+ enum bool IsConvertible = !is(U == Vector) && __traits(compiles, { U u; T t = cast(T)u; } );
}
template TypesAreNumeric(U, V)
@@ -434,10 +796,10 @@ align(16) struct Matrix(T, int D)
static assert(D > 0 && D <= 4);
alias Vector!(T, D) MatrixVec;
- alias T _T;
- alias T[4] Row;
+ alias T _T;
+ alias T[4] Row;
- enum N = D*D;
+ enum N = D*D;
enum _D = D;
union
@@ -445,9 +807,12 @@ align(16) struct Matrix(T, int D)
T[N] v = 0;
Row[D] rows;
MatrixVec[D] vec;
- static if(D == 4) mat4 glm_mat;
- static if(D == 3) mat3 glm_mat;
- static if(D == 2) mat2 glm_mat;
+ static if(NativeTarget)
+ {
+ static if(D == 4) mat4 glm_mat;
+ static if(D == 3) mat3 glm_mat;
+ static if(D == 2) mat2 glm_mat;
+ }
}
// TODO: setup
@@ -532,7 +897,7 @@ align(16) struct Matrix(T, int D)
static foreach(i; 0 .. N)
{
- if(fabsf(this.v[i] - other.v[i]) > 0.0000009)
+ if(Abs(this.v[i] - other.v[i]) > 0.0000009)
{
result = false;
}
@@ -558,16 +923,22 @@ align(16) struct Matrix(T, int D)
Vec4 opBinary(string op, U)(U x) if(is(U: Vec4) && is(T: f32) && (op == "*"))
{
Vec4 result = 0.0;
- glm_mat4_mulv(glm_mat.ptr, x.v.ptr, result.v.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_mat4_mulv(glm_mat.ptr, x.v.ptr, result.v.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return result;
}
Matrix opBinary(string op, U)(U x) if(is(U: Matrix!(T, D)) && is(T: f32) && D == 4 && (op == "*"))
{
Matrix result;
- MatZero(&result);
- Mat4MulASM(&this, &x, &result);
+ MatZero(&result);
+ Mat4Mul(&this, &x, &result);
return result;
}
@@ -612,7 +983,13 @@ struct Quat
U opCast(U)() if(is(U: Mat4))
{
Mat4 result;
- glm_quat_mat4(vec.ptr, result.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_quat_mat4(vec.ptr, result.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return result;
}
@@ -630,106 +1007,109 @@ struct Quat
}
void
-Mat4MulASM(Mat4* l, Mat4* r, Mat4* result)
+Mat4Mul(Mat4* l, Mat4* r, Mat4* result)
{
- asm @trusted
+ version(X86_64)
{
- mov R8, l;
- mov R9, r;
- mov R10, result;
+ asm @trusted
+ {
+ mov R8, l;
+ mov R9, r;
+ mov R10, result;
- movups XMM0, [R8];
- movups XMM1, [R9+00];
- movups XMM2, [R9+16];
- movups XMM3, [R9+32];
- movups XMM4, [R9+48];
+ movups XMM0, [R8];
+ movups XMM1, [R9+00];
+ movups XMM2, [R9+16];
+ movups XMM3, [R9+32];
+ movups XMM4, [R9+48];
- movups XMM6, XMM1;
- shufps XMM6, XMM6, 0; // XMM5 = vec.xxxx;
- mulps XMM6, XMM0; // XMM6 = col1;
+ movups XMM6, XMM1;
+ shufps XMM6, XMM6, 0; // XMM5 = vec.xxxx;
+ mulps XMM6, XMM0; // XMM6 = col1;
- movups XMM7, XMM2;
- shufps XMM7, XMM7, 0;
- mulps XMM7, XMM0; // XMM7 = col2;
+ movups XMM7, XMM2;
+ shufps XMM7, XMM7, 0;
+ mulps XMM7, XMM0; // XMM7 = col2;
- movups XMM8, XMM3;
- shufps XMM8, XMM8, 0;
- mulps XMM8, XMM0; // XMM8 = col3;
+ movups XMM8, XMM3;
+ shufps XMM8, XMM8, 0;
+ mulps XMM8, XMM0; // XMM8 = col3;
- movups XMM9, XMM4;
- shufps XMM9, XMM9, 0;
- mulps XMM9, XMM0; // XMM9 = col4;
+ movups XMM9, XMM4;
+ shufps XMM9, XMM9, 0;
+ mulps XMM9, XMM0; // XMM9 = col4;
- movups XMM0, [R8+16];
+ movups XMM0, [R8+16];
- movups XMM5, XMM1;
- shufps XMM5, XMM5, 85; // XMM5 = vec.yyyy;
- mulps XMM5, XMM0;
- addps XMM6, XMM5;
+ movups XMM5, XMM1;
+ shufps XMM5, XMM5, 85; // XMM5 = vec.yyyy;
+ mulps XMM5, XMM0;
+ addps XMM6, XMM5;
- movups XMM5, XMM2;
- shufps XMM5, XMM5, 85;
- mulps XMM5, XMM0;
- addps XMM7, XMM5;
+ movups XMM5, XMM2;
+ shufps XMM5, XMM5, 85;
+ mulps XMM5, XMM0;
+ addps XMM7, XMM5;
- movups XMM5, XMM3;
- shufps XMM5, XMM5, 85;
- mulps XMM5, XMM0;
- addps XMM8, XMM5;
+ movups XMM5, XMM3;
+ shufps XMM5, XMM5, 85;
+ mulps XMM5, XMM0;
+ addps XMM8, XMM5;
- movups XMM5, XMM4;
- shufps XMM5, XMM5, 85;
- mulps XMM5, XMM0;
- addps XMM9, XMM5;
+ movups XMM5, XMM4;
+ shufps XMM5, XMM5, 85;
+ mulps XMM5, XMM0;
+ addps XMM9, XMM5;
- movups XMM0, [R8+32];
+ movups XMM0, [R8+32];
- movups XMM5, XMM1;
- shufps XMM5, XMM5, 170; // XMM5 = vec.zzzz;
- mulps XMM5, XMM0;
- addps XMM6, XMM5;
+ movups XMM5, XMM1;
+ shufps XMM5, XMM5, 170; // XMM5 = vec.zzzz;
+ mulps XMM5, XMM0;
+ addps XMM6, XMM5;
- movups XMM5, XMM2;
- shufps XMM5, XMM5, 170;
- mulps XMM5, XMM0;
- addps XMM7, XMM5;
+ movups XMM5, XMM2;
+ shufps XMM5, XMM5, 170;
+ mulps XMM5, XMM0;
+ addps XMM7, XMM5;
- movups XMM5, XMM3;
- shufps XMM5, XMM5, 170;
- mulps XMM5, XMM0;
- addps XMM8, XMM5;
-
- movups XMM5, XMM4;
- shufps XMM5, XMM5, 170;
- mulps XMM5, XMM0;
- addps XMM9, XMM5;
+ movups XMM5, XMM3;
+ shufps XMM5, XMM5, 170;
+ mulps XMM5, XMM0;
+ addps XMM8, XMM5;
- movups XMM0, [R8+48];
+ movups XMM5, XMM4;
+ shufps XMM5, XMM5, 170;
+ mulps XMM5, XMM0;
+ addps XMM9, XMM5;
- movups XMM5, XMM1;
- shufps XMM5, XMM5, 255; // XMM5 = vec.wwww;
- mulps XMM5, XMM0;
- addps XMM6, XMM5;
+ movups XMM0, [R8+48];
- movups XMM5, XMM2;
- shufps XMM5, XMM5, 255;
- mulps XMM5, XMM0;
- addps XMM7, XMM5;
+ movups XMM5, XMM1;
+ shufps XMM5, XMM5, 255; // XMM5 = vec.wwww;
+ mulps XMM5, XMM0;
+ addps XMM6, XMM5;
- movups XMM5, XMM3;
- shufps XMM5, XMM5, 255;
- mulps XMM5, XMM0;
- addps XMM8, XMM5;
+ movups XMM5, XMM2;
+ shufps XMM5, XMM5, 255;
+ mulps XMM5, XMM0;
+ addps XMM7, XMM5;
- movups XMM5, XMM4;
- shufps XMM5, XMM5, 255;
- mulps XMM5, XMM0;
- addps XMM9, XMM5;
+ movups XMM5, XMM3;
+ shufps XMM5, XMM5, 255;
+ mulps XMM5, XMM0;
+ addps XMM8, XMM5;
- movups [R10+00], XMM6;
- movups [R10+16], XMM7;
- movups [R10+32], XMM8;
- movups [R10+48], XMM9;
+ movups XMM5, XMM4;
+ shufps XMM5, XMM5, 255;
+ mulps XMM5, XMM0;
+ addps XMM9, XMM5;
+
+ movups [R10+00], XMM6;
+ movups [R10+16], XMM7;
+ movups [R10+32], XMM8;
+ movups [R10+48], XMM9;
+ }
}
}
@@ -778,7 +1158,13 @@ pragma(inline) Quat
QuatFromAxis(f32 angle, Vec3 axis)
{
Quat q;
- glm_quatv(q.vec.ptr, angle, axis.v.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_quatv(q.vec.ptr, angle, axis.v.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return q;
}
@@ -804,14 +1190,14 @@ Dot(Vec4* l, Vec4* r)
pragma(inline) f32
Norm(Vec3* v)
{
- return sqrtf(Dot(v, v));
+ return Sqrt(Dot(v, v));
}
pragma(inline) f32
Norm(Vec4* v)
{
// TODO: SIMD this
- return sqrtf(Dot(v, v));
+ return Sqrt(Dot(v, v));
}
pragma(inline) void
@@ -821,11 +1207,17 @@ Normalize(T)(T* vec) if(is(T: Vec2) || is(T: Vec3) || is(T: Vec4))
if(length < f32.epsilon)
{
- mixin(GenerateLoop!("vec.v[@] = 0.0;", vec._N)());
+ static foreach(i; 0 .. vec._N)
+ {
+ vec.v[i] = 0.0;
+ }
}
else
{
- mixin(GenerateLoop!("vec.v[@] *= (1.0 / length);", vec._N)());
+ static foreach(i; 0 .. vec._N)
+ {
+ vec.v[i] *= (1.0 / length);
+ }
}
}
@@ -846,7 +1238,7 @@ Normalize(Quat q)
q = Quat(1.0, 0.0, 0.0, 0.0);
}
- q.vec *= 1.0 / sqrtf(dot);
+ q.vec *= 1.0 / Sqrt(dot);
return q;
}
@@ -856,7 +1248,13 @@ Perspective(f32 fov, f32 aspect, f32 near, f32 far)
{
Mat4 res;
MatZero(&res);
- glm_perspective(fov, aspect, near, far, res.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_perspective(fov, aspect, near, far, res.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
res[1, 1] *= -1.0;
return res;
}
@@ -865,7 +1263,12 @@ void
Ortho(Mat4* mat, f32 left, f32 bottom, f32 right, f32 top, f32 near, f32 far)
{
MatZero(mat);
- glm_ortho(left, right, bottom, top, near, far, mat.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_ortho(left, right, bottom, top, near, far, mat.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
}
Mat4
@@ -873,7 +1276,13 @@ Ortho(f32 left, f32 bottom, f32 right, f32 top, f32 near, f32 far)
{
Mat4 mat;
MatZero(&mat);
- glm_ortho(left, right, bottom, top, near, far, mat.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_ortho(left, right, bottom, top, near, far, mat.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return mat;
}
@@ -906,7 +1315,13 @@ LookAt(Vec3 eye, Vec3 center, Vec3 up)
{
Mat4 result;
MatZero(&result);
- glm_lookat(eye.v.ptr, center.v.ptr, up.v.ptr, result.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_lookat(eye.v.ptr, center.v.ptr, up.v.ptr, result.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return result;
}
@@ -937,34 +1352,38 @@ pragma(inline) void
CrossN(Vec3 a, Vec3 b, Vec3* dst)
{
Cross(a, b, dst);
- glm_vec3_normalize(dst.v.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_vec3_normalize(dst.v.ptr);
+ }
+ else assert(false, "Not implemented for platform");
}
pragma(inline) void
Cross(Vec3 a, Vec3 b, Vec3* dst)
{
- glm_vec3_cross(a.v.ptr, b.v.ptr, dst.v.ptr);
+ static if(NativeTarget)
+ {
+ glm_vec3_cross(a.v.ptr, b.v.ptr, dst.v.ptr);
+ }
+ else assert(false, "Not implemented for platform");
}
pragma(inline) void
MatZero(Mat4* mat)
{
- auto v = &mat.vec;
- asm
- {
- mov R8, v;
- xorps XMM0, XMM0;
- movups mat.vec.offsetof[R8]+00, XMM0;
- movups mat.vec.offsetof[R8]+16, XMM0;
- movups mat.vec.offsetof[R8]+32, XMM0;
- movups mat.vec.offsetof[R8]+48, XMM0;
- }
+ mat.v[] = 0.0;
}
pragma(inline) void
Translate(Mat4* mat, Vec3 vec)
{
- glm_translate(mat.glm_mat.ptr, vec.v.ptr);
+ static if(NativeTarget)
+ {
+ glm_translate(mat.glm_mat.ptr, vec.v.ptr);
+ }
+ else assert(false, "Not implemented for platform");
}
pragma(inline) Mat4
@@ -972,7 +1391,13 @@ Inverse(Mat4 mat)
{
Mat4 res;
MatZero(&res);
- glm_mat4_inv(mat.glm_mat.ptr, res.glm_mat.ptr);
+
+ static if(NativeTarget)
+ {
+ glm_mat4_inv(mat.glm_mat.ptr, res.glm_mat.ptr);
+ }
+ else assert(false, "Not implemented for platform");
+
return res;
}
@@ -1006,14 +1431,26 @@ Remap(T)(T v, T in_min, T in_max, T out_min, T out_max)
template
IsVector(T)
{
- import std.traits : isInstanceOf;
+// import std.traits : isInstanceOf;
enum bool IsVector = isInstanceOf!(Vector, T);
}
T
-Clamp(T)(T value, T low, T high) if(IsVector!(T))
+Clamp(T)(T value, T min, T max) if(isNumeric!(T))
{
- mixin(GenerateLoop!("value.v[@] = clamp(value.v[@], low.v[@], high.v[@]);", T.v.length));
+ assert(min <= max, "Clamp assertion failed: min is higher than max");
+ T x = value < min ? min : value;
+ return x > max ? max : x;
+}
+
+T
+Clamp(T)(T value, T min, T max) if(IsVector!(T))
+{
+ static foreach(i; 0 .. value._N)
+ {
+ value.v[i] = Clamp(value.v[i], min.v[i], max.v[i]);
+ }
+
return value;
}
@@ -1023,11 +1460,6 @@ version(DLIB_TEST) unittest
enum FLOAT_MIN = -f32.max;
/*
- import core.stdc.stdio;
- import core.stdc.stdlib;
- import core.stdc.time;
- import std.range : take;
- import std.algorithm.iteration : sum;
void PrintMatrix(Mat4 mat)
{
@@ -1264,10 +1696,20 @@ version(DLIB_TEST) unittest
{
Vec2 v0 = 50U;
- assert(fabsf(v0.x-50.0) < 0.0009 && fabsf(v0.y-50.0) < 0.0009);
+ 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);
+ }
}
diff --git a/package.d b/package.d
index a3144c6..0c15127 100644
--- a/package.d
+++ b/package.d
@@ -9,3 +9,11 @@ public import dlib.platform;
public import dlib.aliases;
public import dlib.fonts;
public import dlib.assets;
+
+version(DLIB_TEST)
+{
+ version(WebAssembly)
+ {
+ void _start() { main() }
+ }
+}
diff --git a/platform.d b/platform.d
index 79bce1f..e314c37 100644
--- a/platform.d
+++ b/platform.d
@@ -1,6 +1,10 @@
module dlib.platform;
import dlib.aliases;
+
+static if(NativeTarget)
+{
+
import dlib.alloc : Reset;
import dlib.alloc;
import dlib.util;
@@ -23,117 +27,121 @@ const WINDOW_EDGE_BUFFER = 50;
enum Input : u32
{
None,
- Backspace = 0x08,
- Tab = 0x09,
- Enter = 0x0A,
- Escape = 0x1B,
- Space = 0x20,
+ Backspace = 0x08,
+ Tab = 0x09,
+ Enter = 0x0A,
+ Escape = 0x1B,
+ Space = 0x20,
Exclamation = 0x21,
DoubleQuote = 0x22,
- Hash = 0x23,
- Dollar = 0x24,
- Percent = 0x25,
- Ampersand = 0x26,
+ Hash = 0x23,
+ Dollar = 0x24,
+ Percent = 0x25,
+ Ampersand = 0x26,
SingleQuote = 0x27,
- LeftParen = 0x28,
+ LeftParen = 0x28,
RightParent = 0x29,
- Asterisk = 0x2A,
- Plus = 0x2B,
- Comma = 0x2C,
- Minus = 0x2D,
- Period = 0x2E,
- Slash = 0x2F,
- Zero = 0x30,
- One = 0x31,
- Two = 0x32,
- Three = 0x33,
- Four = 0x34,
- Five = 0x35,
- Six = 0x36,
- Seven = 0x37,
- Eight = 0x38,
- Nine = 0x39,
- Colon = 0x3A,
- Semicolon = 0x3B,
- LessThan = 0x3C,
- Equals = 0x3D,
+ Asterisk = 0x2A,
+ Plus = 0x2B,
+ Comma = 0x2C,
+ Minus = 0x2D,
+ Period = 0x2E,
+ Slash = 0x2F,
+ Zero = 0x30,
+ One = 0x31,
+ Two = 0x32,
+ Three = 0x33,
+ Four = 0x34,
+ Five = 0x35,
+ Six = 0x36,
+ Seven = 0x37,
+ Eight = 0x38,
+ Nine = 0x39,
+ Colon = 0x3A,
+ Semicolon = 0x3B,
+ LessThan = 0x3C,
+ Equals = 0x3D,
GreaterThan = 0x3E,
- Question = 0x3F,
- At = 0x40,
+ Question = 0x3F,
+ At = 0x40,
A = 0x41, B = 0x42, C = 0x43, D = 0x44, E = 0x45, F = 0x46, G = 0x47, H = 0x48, I = 0x49, J = 0x4A, K = 0x4B, L = 0x4C, M = 0x4D,
N = 0x4E, O = 0x4F, P = 0x50, Q = 0x51, R = 0x52, S = 0x53, T = 0x54, U = 0x55, V = 0x56, W = 0x57, X = 0x58, Y = 0x59, Z = 0x5A,
LeftBracket = 0x5B,
- BackSlash = 0x5C,
+ BackSlash = 0x5C,
RightBracket = 0x5D,
- Caret = 0x5E,
- Underscore = 0x5F,
- Grave = 0x60,
+ Caret = 0x5E,
+ Underscore = 0x5F,
+ Grave = 0x60,
a = I.A+32, b = I.B+32, c = I.C+32, d = I.D+32, e = I.E+32, f = I.F+32, g = I.G+32, h = I.H+32, i = I.I+32, j = I.J+32, k = I.K+32, l = I.L+32, m = I.M+32,
n = I.N+32, o = I.O+32, p = I.P+32, q = I.Q+32, r = I.R+32, s = I.S+32, t = I.T+32, u = I.U+32, v = I.V+32, w = I.W+32, x = I.X+32, y = I.Y+32, z = I.Z+32,
- LeftBrace = 0x7B,
+ LeftBrace = 0x7B,
VerticalBar = 0x7C,
- RightBrace = 0x7D,
- Tilde = 0x7E,
- Delete = 0x7F,
+ RightBrace = 0x7D,
+ Tilde = 0x7E,
+ Delete = 0x7F,
- F1 = 0x150,
- F2 = 0x151,
- F3 = 0x152,
- F4 = 0x153,
- F5 = 0x154,
- F6 = 0x155,
- F7 = 0x156,
- F8 = 0x157,
- F9 = 0x158,
+ F1 = 0x150,
+ F2 = 0x151,
+ F3 = 0x152,
+ F4 = 0x153,
+ F5 = 0x154,
+ F6 = 0x155,
+ F7 = 0x156,
+ F8 = 0x157,
+ F9 = 0x158,
F10 = 0x159,
F11 = 0x15A,
F12 = 0x15B,
+
PrintScreen = 0x10C,
- ScrollLock = 0x10D,
- Pause = 0x10E,
- Insert = 0x10F,
- Home = 0x110,
- End = 0x111,
- PageUp = 0x112,
- PageDown = 0x113,
- NumLock = 0x114,
+ ScrollLock = 0x10D,
+ Pause = 0x10E,
+ Insert = 0x10F,
+ Home = 0x110,
+ End = 0x111,
+ PageUp = 0x112,
+ PageDown = 0x113,
- NumEnter = 0x10A,
+ NumLock = 0x114,
+ NumEnter = 0x10A,
NumAsterisk = 0x12A,
- NumPlus = 0x12B,
- NumMinus = 0x12D,
- NumPeriod = 0x12E,
- NumSlash = 0x12F,
- NumZero = 0x130,
- NumOne = 0x131,
- NumTwo = 0x132,
- NumThree = 0x133,
- NumFour = 0x134,
- NumFive = 0x135,
- NumSix = 0x136,
- NumSeven = 0x137,
- NumEight = 0x138,
- NumNine = 0x139,
+ NumPlus = 0x12B,
+ NumMinus = 0x12D,
+ NumPeriod = 0x12E,
+ NumSlash = 0x12F,
+ NumZero = 0x130,
+ NumOne = 0x131,
+ NumTwo = 0x132,
+ NumThree = 0x133,
+ NumFour = 0x134,
+ NumFive = 0x135,
+ NumSix = 0x136,
+ NumSeven = 0x137,
+ NumEight = 0x138,
+ NumNine = 0x139,
- LeftCtrl = 0x13A,
- RightCtrl = 0x13B,
- LeftShift = 0x13C,
+ LeftCtrl = 0x13A,
+ RightCtrl = 0x13B,
+ LeftShift = 0x13C,
RightShift = 0x13D,
- LeftSuper = 0x13E,
+ LeftSuper = 0x13E,
RightSuper = 0x13F,
- LeftAlt = 0x140,
- RightAlt = 0x141,
- CapsLock = 0x142,
+ LeftAlt = 0x140,
+ RightAlt = 0x141,
+ CapsLock = 0x142,
- Left = 0x143,
+ Left = 0x143,
Right = 0x144,
- Up = 0x145,
- Down = 0x146,
+ Up = 0x145,
+ Down = 0x146,
// Mouse
MouseMotion = 0x147,
-
- LeftClick = 0x148, MiddleClick = 0x149, RightClick = 0x14A, ScrollUp = 0x14B, ScrollDown = 0x14C,
+ LeftClick = 0x148,
+ MiddleClick = 0x149,
+ RightClick = 0x14A,
+ ScrollUp = 0x14B,
+ ScrollDown = 0x14C,
};
alias I = Input;
@@ -204,7 +212,7 @@ GetEvents(PlatformWindow* window)
{
Lock(&window.input_mutex);
- Inputs* inputs = &window.inputs[window.input_idx];
+ Inputs* inputs = &window.inputs[window.input_idx];
window.input_idx = (window.input_idx + 1) % 2;
ResetInputs(&window.inputs[window.input_idx]);
@@ -381,17 +389,17 @@ import core.sys.linux.sys.inotify;
import core.sys.linux.fcntl;
import core.sys.posix.sys.time : timespec, timeval, gettimeofday;
import core.sys.posix.unistd;
-import core.sys.posix.pthread : PThread = pthread_t,
- PThreadCond = pthread_cond_t,
- PThreadMutex = pthread_mutex_t,
- PThreadMutexInit = pthread_mutex_init,
- PThreadCondInit = pthread_cond_init,
- PThreadCreate = pthread_create,
- PThreadMutexLock = pthread_mutex_lock,
- PThreadCondWait = pthread_cond_wait,
- PThreadMutexUnlock = pthread_mutex_unlock,
- PThreadCondSignal = pthread_cond_signal,
- PThreadExit = pthread_exit,
+import core.sys.posix.pthread : PThread = pthread_t,
+ PThreadCond = pthread_cond_t,
+ PThreadMutex = pthread_mutex_t,
+ PThreadMutexInit = pthread_mutex_init,
+ PThreadCondInit = pthread_cond_init,
+ PThreadCreate = pthread_create,
+ PThreadMutexLock = pthread_mutex_lock,
+ PThreadCondWait = pthread_cond_wait,
+ PThreadMutexUnlock = pthread_mutex_unlock,
+ PThreadCondSignal = pthread_cond_signal,
+ PThreadExit = pthread_exit,
PThreadCondTimedWait = pthread_cond_timedwait;
import core.stdc.string : strlen;
@@ -584,32 +592,29 @@ PushMotion(Inputs* inputs, i32 rel_x, i32 rel_y, i32 x, i32 y)
struct PlatformWindow
{
- Atom[Atoms.max] atoms;
- Display* display;
- Window window;
- Window root_window;
- i32 screen_id;
- // xcb_connection_t* conn;
- // xcb_screen_t* screen;
- // xcb_window_t window;
- u32 w;
- u32 h;
- i32 mouse_prev_x;
- i32 mouse_prev_y;
- bool locked_cursor;
- bool close;
- Modifier modifier;
+ Atom[Atoms.max] atoms;
+ Display* display;
+ Window window;
+ Window root_window;
+ i32 screen_id;
+ u32 w;
+ u32 h;
+ i32 mouse_prev_x;
+ i32 mouse_prev_y;
+ bool locked_cursor;
+ bool close;
+ Modifier modifier;
- SysThread thread;
- MessageQueue msg_queue;
- u32 input_idx;
- Inputs[2] inputs;
- TicketMut input_mutex;
+ SysThread thread;
+ MessageQueue msg_queue;
+ u32 input_idx;
+ Inputs[2] inputs;
+ TicketMut input_mutex;
- Mut cb_msg_mut;
- TicketMut cb_mut;
- Selection[CBM.max] selections;
- version(linux) u32 cb_transfer_size;
+ Mut cb_msg_mut;
+ TicketMut cb_mut;
+ Selection[CBM.max] selections;
+ version(linux) u32 cb_transfer_size;
};
struct Library
@@ -1977,3 +1982,62 @@ version(DLIB_TEST) unittest
}
+pragma(inline) u64
+RDTSC()
+{
+ union u64_split
+ {
+ u64 full;
+ struct
+ {
+ u32 lower;
+ u32 upper;
+ };
+ };
+
+ u64_split val;
+
+ u64_split* valp = &val;
+ asm
+ {
+ cpuid;
+ rdtsc;
+ mov R8, valp;
+ mov valp.upper.offsetof[R8], EDX;
+ mov valp.lower.offsetof[R8], EAX;
+ }
+
+ return val.full;
+}
+
+pragma(inline) u64
+OSTimeFreq()
+{
+ u64 freq;
+
+ version (linux)
+ {
+ u64 freq = 1000000;
+ }
+ else static assert(false, NO_IMPL);
+
+ return freq;
+}
+
+pragma(inline) u64
+OSTime()
+{
+ version(linux)
+ {
+ import core.sys.linux.sys.time;
+ timeval value;
+ gettimeofday(&value, null);
+
+ u64 time = OSTimeFreq() * cast(u64)(value.tv_sec) + cast(u64)(value.tv_usec);
+ }
+ else static assert(false, NO_IMPL);
+
+ return time;
+}
+
+}
diff --git a/test.sh b/test.sh
index a8e7d53..f0bc1fc 100755
--- a/test.sh
+++ b/test.sh
@@ -2,11 +2,16 @@
name="Test_Runner"
+flags="-P-I/usr/include/freetype2 -L-lfreetype"
+if [ "$1" == "wasm" ]; then
+ flags="-mtriple=wasm32-unknown-unknown-wasm --Xcc=-DBUILD_WASM -Iexternal/arsd-webassembly"
+fi
+
/bin/bash ./build.sh build
-ldc2 platform.d fonts.d aliases.d math.d util.d alloc.d assets.d external/xxhash/xxhash.d build/libcglm.a -d-version=DLIB_TEST -Xcc=-mno-sse -P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name -verrors=8
+ldc2 platform.d fonts.d aliases.d math.d util.d alloc.d assets.d build/libxxhash.a build/libcglm.a build/libprintf.a $flags -d-version=DLIB_TEST --main --unittest -g --of=$name -verrors=50
rm $name.o
./$name
-rm $name
+#rm $name
diff --git a/util.d b/util.d
index d93cfd5..40a5acb 100644
--- a/util.d
+++ b/util.d
@@ -3,35 +3,44 @@ module dlib.util;
import dlib.aliases;
import dlib.alloc;
-import xxhash3;
import dlibincludes;
-import std.stdio : write, writeln, writef, writefln, stderr;
-import std.conv;
-import std.string;
-import std.traits;
+//import std.traits;
-import core.stdc.string : memset;
-import core.simd;
+static if(NativeTarget)
+{
+ import std.format : sformat;
+ import std.stdio : write, writeln, writef, writefln, stderr;
+}
+else
+{
-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)[]));
pragma(inline) void
Int3()
{
- debug asm
+ static if(NativeTarget)
{
- db 0xCC;
+ debug asm
+ {
+ db 0xCC;
+ }
}
}
pragma(inline) void
Pause()
{
- asm
+ static if(NativeTarget)
{
- rep;
- nop;
+ asm
+ {
+ rep;
+ nop;
+ }
}
}
@@ -69,37 +78,37 @@ CastArr(T, U)(U[] input_array)
return output_array;
}
-void
-Debugf(Args...)(string fmt, Args args)
-{
- debug Logf(fmt, args, "[DEBUG]: ");
-}
-
void
Logf(string prefix = "INFO ", Args...)(string fmt, Args args)
{
- try
+ static if(NativeTarget)
{
- writef("[%s]: ", prefix);
- writefln(fmt, args);
- }
- catch (Exception e)
- {
- assert(false, "Incompatible format type");
+ try
+ {
+ writef("[%s]: ", prefix);
+ writefln(fmt, args);
+ }
+ catch (Exception e)
+ {
+ assert(false, "Incompatible format type");
+ }
}
}
void
Logif(Args...)(string fmt, Args args, string func = __FUNCTION__)
{
- try
+ static if(NativeTarget)
{
- writef("FN: [%s] ", func);
- Logf(fmt, args);
- }
- catch (Exception e)
- {
- assert(false, "Incompatible format type");
+ try
+ {
+ writef("FN: [%s] ", func);
+ Logf(fmt, args);
+ }
+ catch (Exception e)
+ {
+ assert(false, "Incompatible format type");
+ }
}
}
@@ -115,24 +124,10 @@ Errf(Args...)(string fmt, Args args)
Logf!("ERROR", Args)(fmt, args);
}
-static string
-AssignStr(T, alias p)() if(StringType!(T))
+void
+Debugf(Args...)(string fmt, Args args)
{
- import std.format : format;
-
- enum string id = __traits(identifier, p);
- string result = "";
- static if(is(T == string))
- {
- result = id;
- }
- else static if(StringType!(T))
- {
- result = format("Str(%s)", id);
- }
- else static assert(false, "Unknown type for AssignItem");
-
- return result;
+ debug Logf(fmt, args, "[DEBUG]: ");
}
string
@@ -146,13 +141,13 @@ Scratchf(Args...)(string fmt, Args args)
void
Log(string str)
{
- writeln(str);
+ static if(NativeTarget) writeln(str);
}
void
Log(char* str)
{
- writeln(str);
+ static if(NativeTarget) writeln(str);
}
@nogc u64
@@ -203,7 +198,7 @@ GetFilePath(string file_name)
{
string result = file_name;
- for(u64 i = file_name.length-1; i64(i) >= 0; i -= 1)
+ for(usize i = file_name.length-1; i64(i) >= 0; i -= 1)
{
version(Windows)
{
@@ -272,7 +267,7 @@ ConcatInPlace(T)(T* list, T* to_concat)
list.last = to_concat.last;
}
- memset(to_concat, 0, T.sizeof);
+ MemSet(to_concat, 0, T.sizeof);
}
}
@@ -477,7 +472,7 @@ struct KVPair(K, V)
struct Result(V)
{
- V value;
+ V value;
bool ok;
}
@@ -487,10 +482,10 @@ struct HashTable(K, V)
LinkedList!(P) free_lists;
LinkedList!(P)[] lists;
- P* nil;
+ P* nil;
Arena arena;
- u64 node_count;
- u64 list_count;
+ u64 node_count;
+ u64 list_count;
void opIndexAssign(V value, K key)
{
@@ -640,7 +635,7 @@ Delete(K, V)(HashTable!(K, V)* ht, K key)
result.ok = true;
result.value = node.value;
- memset(&node.value, 0, node.value.sizeof);
+ MemSet(&node.value, 0, node.value.sizeof);
SLLPush(&ht.free_lists, node, ht.nil);
@@ -658,110 +653,28 @@ const u64 HASH_SEED = 5995;
pragma(inline) u64
Hash(T)(T[] value)
{
- return xxh3_64bits_withSeed(value.ptr, (T.sizeof * value.length) / u8.sizeof, HASH_SEED);
+ return XXH3_64bits_withSeed(value.ptr, (T.sizeof * value.length) / u8.sizeof, HASH_SEED);
}
pragma(inline) u64
Hash(T)(T* value)
{
- return xxh3_64bits_withSeed(value, T.sizeof / u8.sizeof, HASH_SEED);
+ return XXH3_64bits_withSeed(value, T.sizeof / u8.sizeof, HASH_SEED);
}
pragma(inline) u64
Hash(string str)
{
- return xxh3_64bits_withSeed(str.ptr, str.length, HASH_SEED);
-}
-
-pragma(inline) u64
-Hash(void* ptr_1, u64 len_1, void* ptr_2, u64 len_2)
-{
- XXH3_state_t xxh;
- XXH3_INITSTATE(&xxh);
- xxh3_64bits_reset_withSeed(&xxh, HASH_SEED);
- xxh3_64bits_update(&xxh, ptr_1, len_1);
- xxh3_64bits_update(&xxh, ptr_2, len_2);
-
- return xxh3_64bits_digest(&xxh);
-}
-
-pragma(inline) u64
-Hash(T, U)(T value_1, U value_2) if(isArray!(T) && isArray!(U))
-{
- return Hash(value_1.ptr, value_1.length*T.sizeof, value_2.ptr, value_2.length*U.sizeof);
-}
-
-pragma(inline) u64
-Hash(T, U)(T value_1, U value_2) if(isArray!(T) && !isArray!(U))
-{
- return Hash(value_1.ptr, value_1.length*value_1[0].sizeof, &value_2, U.sizeof);
-}
-
-pragma(inline) u64
-Hash(T, U)(T value_1, U value_2) if(!isArray!(T) && !isArray!(U))
-{
- return Hash(&value_1, T.sizeof, &value_2, U.sizeof);
-}
-
-pragma(inline) u64
-RDTSC()
-{
- union u64_split
- {
- u64 full;
- struct
- {
- u32 lower;
- u32 upper;
- };
- };
-
- u64_split val;
- u64_split* valp = &val;
- asm
- {
- cpuid;
- rdtsc;
- mov R8, valp;
- mov valp.upper.offsetof[R8], EDX;
- mov valp.lower.offsetof[R8], EAX;
- }
-
- return val.full;
-}
-
-pragma(inline) u64
-OSTimeFreq()
-{
- version (linux)
- {
- u64 freq = 1000000;
- }
-
- return freq;
-}
-
-pragma(inline) u64
-OSTime()
-{
- version(linux)
- {
- import core.sys.linux.sys.time;
- timeval value;
- gettimeofday(&value, null);
-
- u64 time = OSTimeFreq() * cast(u64)(value.tv_sec) + cast(u64)(value.tv_usec);
- }
-
- return time;
+ return XXH3_64bits_withSeed(str.ptr, str.length, HASH_SEED);
}
// TODO: probably needs improvement/testing
struct IntervalTimer
{
- u64 cpu_freq;
- u64 interval;
- u64 prev;
+ Timer base;
+ u64 interval;
+
+ alias base this;
}
IntervalTimer
@@ -773,35 +686,9 @@ CreateFPSTimer(u64 fps)
IntervalTimer
CreateTimer(u64 ms)
{
- IntervalTimer timer;
+ IntervalTimer timer = { base: CreateTimer() };
- u64 ms_to_wait = 50;
-
- u64 os_freq = OSTimeFreq();
- u64 cpu_start = RDTSC();
- u64 os_start = OSTime();
- u64 os_end = 0;
- u64 os_elapsed = 0;
-
- u64 os_wait_time = os_freq * ms_to_wait / 1000;
-
- while (os_elapsed < os_wait_time)
- {
- os_end = OSTime();
- os_elapsed = os_end - os_start;
- }
-
- u64 cpu_end = RDTSC();
- u64 cpu_elapsed = cpu_end - cpu_start;
- u64 cpu_freq = 0;
- if(os_elapsed)
- {
- cpu_freq = os_freq * cpu_elapsed / os_elapsed;
- }
-
- timer.cpu_freq = cpu_freq;
- timer.interval = cpu_freq*(ms/1000);
- timer.prev = RDTSC();
+ timer.interval = timer.cpu_freq * (ms/1000);
return timer;
}
@@ -809,18 +696,33 @@ CreateTimer(u64 ms)
void
ResetTimer(IntervalTimer* t)
{
- t.prev = RDTSC();
+ static if(NativeTarget)
+ {
+ t.prev = RDTSC();
+ }
+ else
+ {
+ assert(false, NO_IMPL);
+ }
}
pragma(inline) bool
CheckTimer(IntervalTimer* t)
{
bool result = false;
- u64 time = RDTSC();
- if(time - t.prev > t.interval)
+
+ static if(NativeTarget)
{
- result = true;
- t.prev = time;
+ u64 time = RDTSC();
+ if(time - t.prev > t.interval)
+ {
+ result = true;
+ t.prev = time;
+ }
+ }
+ else
+ {
+ assert(false, NO_IMPL);
}
return result;
@@ -835,34 +737,43 @@ struct Timer
Timer
CreateTimer()
{
- u64 ms_to_wait = 50;
-
- u64 os_freq = OSTimeFreq();
- u64 cpu_start = RDTSC();
- u64 os_start = OSTime();
- u64 os_end = 0;
- u64 os_elapsed = 0;
-
- u64 os_wait_time = os_freq * ms_to_wait / 1000;
-
- while (os_elapsed < os_wait_time)
+ static if(NativeTarget)
{
- os_end = OSTime();
- os_elapsed = os_end - os_start;
- }
+ u64 ms_to_wait = 50;
- u64 cpu_end = RDTSC();
- u64 cpu_elapsed = cpu_end - cpu_start;
- u64 cpu_freq = 0;
- if(os_elapsed)
+ u64 os_freq = OSTimeFreq();
+ u64 cpu_start = RDTSC();
+ u64 os_start = OSTime();
+ u64 os_end = 0;
+ u64 os_elapsed = 0;
+
+ u64 os_wait_time = os_freq * ms_to_wait / 1000;
+
+ while (os_elapsed < os_wait_time)
+ {
+ os_end = OSTime();
+ os_elapsed = os_end - os_start;
+ }
+
+ u64 cpu_end = RDTSC();
+ u64 cpu_elapsed = cpu_end - cpu_start;
+ u64 cpu_freq = 0;
+ if(os_elapsed)
+ {
+ cpu_freq = os_freq * cpu_elapsed / os_elapsed;
+ }
+
+ Timer timer = {
+ cpu_freq: cpu_freq,
+ prev: RDTSC(),
+ };
+ }
+ else
{
- cpu_freq = os_freq * cpu_elapsed / os_elapsed;
- }
+ Timer timer;
- Timer timer = {
- cpu_freq: cpu_freq,
- prev: RDTSC(),
- };
+ assert(false, NO_IMPL);
+ }
return timer;
}
@@ -870,92 +781,132 @@ CreateTimer()
pragma(inline) f32
DeltaTime(Timer* t)
{
- u64 time = RDTSC();
- u64 step = time - t.prev;
- t.prev = time;
+ u64 time, step;
+
+ static if(NativeTarget)
+ {
+ time = RDTSC();
+ step = time - t.prev;
+ t.prev = time;
+ }
+ else
+ {
+ assert(false, NO_IMPL);
+ }
+
return cast(f32)(step) / cast(f32)(t.cpu_freq);
}
static string
-IntToStr(int n) nothrow pure @safe
+Replace(string target, u8 find, string replace)
{
- string result;
+ assert(__ctfe, "Function can only be run at compile time");
- static immutable string[] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
- if(n < table.length)
+ u8[1024] result = '\0';
+
+ usize count;
+ foreach(ch; target)
{
- result = table[n];
- }
- else
- {
- result = to!string(n);
+ if(ch == find)
+ {
+ for(usize i = 0; i < replace.length; i += 1)
+ {
+ result[count++] = replace[i];
+ }
+ }
+ else
+ {
+ result[count++] = ch;
+ }
}
- return result;
+ return Str(result[0 .. count]);
}
static string
-GenerateLoop(string format_string, int N)() nothrow pure @safe
+Str(T)(T v) if(isIntegral!(T))
{
- string result;
- for (int i = 0; i < N; i++)
+ assert(__ctfe);
+
+ if(v == 0)
{
- result ~= format_string.replace("@", IntToStr(i));
+ return "0";
}
- return result;
+
+ u8[] slice = new u8[32];
+
+ enum base = 10;
+ i32 i = 30;
+
+ for(; v && i; i--, v /= base)
+ {
+ slice[i] = r"0123456789"[v%base];
+ }
+
+ return Str(slice[i+1 .. $]);
}
void
-MemCpy(void* dst_p, void* src_p, u64 length)
+MemSet(void* dst_p, i32 ch, u64 count) @nogc nothrow
+{
+ u8[] slice = (cast(u8*)dst_p)[0 .. cast(usize)count];
+ slice[] = cast(u8)(ch&0xFF);
+}
+
+void
+MemCpy(void* dst_p, void* src_p, usize length)
{
u8* dst = cast(u8*)dst_p;
u8* src = cast(u8*)src_p;
- u64 remaining = length;
- if(remaining >= 64)
+ usize remaining = length;
+ version(X86_64)
{
- for(u64 i = 0; i + 64 < length; i += 64)
+ if(remaining >= 64)
{
- asm
+ for(u64 i = 0; i + 64 < length; i += 64)
{
- mov R8, src;
- mov R9, dst;
+ asm
+ {
+ mov R8, src;
+ mov R9, dst;
- add R8, i;
- movdqu XMM0, [R8+00];
- movdqu XMM1, [R8+16];
- movdqu XMM2, [R8+32];
- movdqu XMM3, [R8+48];
-
- add R9, i;
- movups [R9+00], XMM0;
- movups [R9+16], XMM1;
- movups [R9+32], XMM2;
- movups [R9+48], XMM3;
+ add R8, i;
+ movdqu XMM0, [R8+00];
+ movdqu XMM1, [R8+16];
+ movdqu XMM2, [R8+32];
+ movdqu XMM3, [R8+48];
- sub remaining, 64;
+ add R9, i;
+ movups [R9+00], XMM0;
+ movups [R9+16], XMM1;
+ movups [R9+32], XMM2;
+ movups [R9+48], XMM3;
+
+ sub remaining, 64;
+ }
}
}
- }
- if(remaining >= 32)
- {
- for(u64 i = length - remaining; i + 32 < length; i += 32)
+ if(remaining >= 32)
{
- asm
+ for(u64 i = length - remaining; i + 32 < length; i += 32)
{
- mov R8, src;
- mov R9, dst;
+ asm
+ {
+ mov R8, src;
+ mov R9, dst;
- add R8, i;
- movdqu XMM0, [R8+00];
- movdqu XMM1, [R8+16];
+ add R8, i;
+ movdqu XMM0, [R8+00];
+ movdqu XMM1, [R8+16];
- add R9, i;
- movdqu [R9+00], XMM0;
- movdqu [R9+16], XMM1;
+ add R9, i;
+ movdqu [R9+00], XMM0;
+ movdqu [R9+16], XMM1;
- sub remaining, 32;
+ sub remaining, 32;
+ }
}
}
}
@@ -966,11 +917,15 @@ MemCpy(void* dst_p, void* src_p, u64 length)
}
}
-u8[]
-Embed(string file_name)
+void
+Assign(T, Args...)(T slice, Args args) if(isArray!(T))
{
- import std.file;
- return cast(u8[])read(file_name);
+ assert(slice.length == Args.length);
+
+ static foreach(i; 0 .. Args.length)
+ {
+ slice[i] = cast(typeof(slice[0]))args[i];
+ }
}
version(DLIB_TEST) unittest
@@ -1089,7 +1044,8 @@ version(DLIB_TEST) unittest
assert(list.first != null && list.last != null);
- TestDLList(&list, [0, 1, 2, 3, 4]);
+ u32[5] test1 = [0, 1, 2, 3, 4];
+ TestDLList(&list, test1);
u32 count = 0;
u32[3] res1 = [0, 2, 4];
@@ -1116,7 +1072,6 @@ version(DLIB_TEST) unittest
}
{ // MemCpy
- import std.conv;
u8[777] bytes;
u8[777] test_bytes;
@@ -1145,7 +1100,6 @@ version(DLIB_TEST) unittest
{
if(v != bytes[count])
{
- Logf("Failed %d %d %d", i, v, bytes[count]);
assert(false);
}
@@ -1193,9 +1147,9 @@ version(DLIB_TEST) unittest
u64 val_1 = 555555;
u32 val_2 = 33333;
- u64 v1 = Hash(arr_1, arr_2);
- u64 v2 = Hash(arr_1, val_1);
- u64 v3 = Hash(val_1, val_2);
+ u64 v1 = Hash(arr_1);
+ u64 v2 = Hash(arr_2);
+ u64 v3 = Hash(&val_1);
assert(v1 > 0);
assert(v2 > 0);
@@ -1204,7 +1158,7 @@ version(DLIB_TEST) unittest
{ // Casts
u8[] arr = CastStr!(u8)("Test");
- char[] char_arr = ['a', 'b'];
+ char[2] char_arr = ['a', 'b'];
arr = CastArr!(u8)(char_arr);
}
@@ -1213,4 +1167,33 @@ version(DLIB_TEST) unittest
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(u64 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(u64 i = 0; i < slice.length; i += 1)
+ {
+ assert(slice[i] == cmp);
+ }
+ }
}
diff --git a/wasm/wasm.js b/wasm/wasm.js
new file mode 100644
index 0000000..e69de29