wip setting everything up for wasm

This commit is contained in:
Matthew 2026-04-25 17:39:06 +10:00
parent 6ae59dab5b
commit 899a28a17e
34 changed files with 8300 additions and 733 deletions

View File

@ -1,2 +1,5 @@
.git
external
build
test
external/cglm
external/xxhash

BIN
Test_Runner Executable file

Binary file not shown.

427
aliases.d
View File

@ -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;
*/

39
alloc.d
View File

@ -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];

144
assets.d
View File

@ -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;
}

View File

@ -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

View File

@ -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"

224
external/arsd-webassembly/arsd/color.d vendored Normal file
View File

@ -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 {}

View File

@ -0,0 +1,8 @@
module arsd.simpleaudio;
struct AudioOutputThread {
this(int) {}
void start() {}
void beep(int = 0) {}
void boop(int = 0) {}
}

View File

@ -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 {
}
}

View File

@ -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);
}
}
}
}

778
external/arsd-webassembly/core/arsd/aa.d vendored Normal file
View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 &lt;= character &lt;= \U0010FFFF).
*
* See_Also:
* $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br>
* $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br>
* $(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");
}

2031
external/arsd-webassembly/object.d vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

17
external/arsd-webassembly/std/stdio.d vendored Normal file
View File

@ -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);
}

840
external/printf/printf.c vendored Normal file
View File

@ -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 <stdbool.h>
#include <stdint.h>
#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 <stddef.h> 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 <float.h>
#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 <m.jasperse@gmail.com>
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;
}

97
external/printf/printf.h vendored Normal file
View File

@ -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 <stdarg.h>
#include <stddef.h>
#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_

201
external/tinyalloc/LICENCE vendored Normal file
View File

@ -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.

270
external/tinyalloc/tinyalloc.c vendored Normal file
View File

@ -0,0 +1,270 @@
#include "tinyalloc.h"
#include <stdint.h>
#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();
}

23
external/tinyalloc/tinyalloc.h vendored Normal file
View File

@ -0,0 +1,23 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
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

View File

@ -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"

View File

@ -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 <string.h>
# 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 <string.h>
# 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 <string.h>
# 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 <limits.h> /* ULLONG_MAX */

38
fonts.d
View File

@ -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;

BIN
main.wasm Executable file

Binary file not shown.

784
math.d

File diff suppressed because it is too large Load Diff

View File

@ -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() }
}
}

View File

@ -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;
}
}

View File

@ -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

517
util.d
View File

@ -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);
}
}
}

0
wasm/wasm.js Normal file
View File