1378 lines
22 KiB
D
1378 lines
22 KiB
D
module dlib.util;
|
|
|
|
import dlib.aliases;
|
|
import dlib.alloc;
|
|
|
|
import std.traits : isIntegral, hasMember, isArray;
|
|
|
|
import core.stdc.string;
|
|
|
|
//import std.traits;
|
|
|
|
static if(NativeTarget)
|
|
{
|
|
import std.format : sformat;
|
|
import std.stdio : write, writeln, writef, writefln, stderr;
|
|
import dlib.platform;
|
|
}
|
|
|
|
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]) || is(T: const(char)[]));
|
|
|
|
pragma(inline) void
|
|
Int3()
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
debug asm
|
|
{
|
|
db 0xCC;
|
|
}
|
|
}
|
|
}
|
|
|
|
pragma(inline) void
|
|
Pause()
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
asm
|
|
{
|
|
rep;
|
|
nop;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Assert(T)(T cond, string msg)
|
|
{
|
|
if(!cond)
|
|
{
|
|
assert(false, msg);
|
|
}
|
|
}
|
|
|
|
string
|
|
Str(T)(T arr) if(StringType!(T))
|
|
{
|
|
return (cast(immutable(char)*)arr.ptr)[0 .. arr.length];
|
|
}
|
|
|
|
alias ConvToStr = Str;
|
|
|
|
T[]
|
|
Arr(T)(string str)
|
|
{
|
|
T[] arr = (cast(T*)str.ptr)[0 .. str.length];
|
|
return arr;
|
|
}
|
|
|
|
alias CastStr = Arr;
|
|
|
|
T[]
|
|
CastArr(T, U)(U[] input_array)
|
|
{
|
|
static assert(T.sizeof == U.sizeof);
|
|
T[] output_array = (cast(T*)input_array.ptr)[0 .. input_array.length];
|
|
return output_array;
|
|
}
|
|
|
|
void
|
|
Logf(string prefix = "INFO ", Args...)(string fmt, Args args)
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
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__)
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
try
|
|
{
|
|
writef("FN: [%s] ", func);
|
|
Logf(fmt, args);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
assert(false, "Incompatible format type");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Warnf(Args...)(string fmt, Args args)
|
|
{
|
|
Logf!("WARN ", Args)(fmt, args);
|
|
}
|
|
|
|
void
|
|
Errf(Args...)(string fmt, Args args)
|
|
{
|
|
Logf!("ERROR", Args)(fmt, args);
|
|
}
|
|
|
|
void
|
|
Debugf(Args...)(string fmt, Args args)
|
|
{
|
|
debug Logf(fmt, args, "[DEBUG]: ");
|
|
}
|
|
|
|
string
|
|
Scratchf(Args...)(string fmt, Args args)
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
assert(g_scratch.init);
|
|
char[] buf = ScratchAlloc!(char)(fmt.length < 16 ? 32 : 128);
|
|
return Str(sformat(buf, fmt, args));
|
|
}
|
|
else static assert(false, NO_IMPL);
|
|
}
|
|
|
|
void
|
|
Log(string str)
|
|
{
|
|
static if(NativeTarget) writeln(str);
|
|
}
|
|
|
|
void
|
|
Log(char* str)
|
|
{
|
|
static if(NativeTarget) writeln(str);
|
|
}
|
|
|
|
@nogc usize
|
|
KB(usize v)
|
|
{
|
|
return v * 1024;
|
|
};
|
|
|
|
@nogc usize
|
|
MB(usize v)
|
|
{
|
|
return KB(v) * 1024;
|
|
};
|
|
|
|
@nogc usize
|
|
GB(usize v)
|
|
{
|
|
return MB(v) * 1024;
|
|
};
|
|
|
|
u32
|
|
StrCharCount(T)(T str, u8 ch) if(is(T: string) || is(T: u8[]) || is(T: char[]))
|
|
{
|
|
u32 count = 0;
|
|
for(u64 i = 0; i < str.length; i += 1)
|
|
{
|
|
count += cast(u8)str[i] == ch;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
pragma(inline) void
|
|
ConvertColor(Vec4 *dst, u32 src)
|
|
{
|
|
if(src == 0)
|
|
{
|
|
dst.rgb = 0.0;
|
|
dst.a = 1.0;
|
|
}
|
|
else
|
|
{
|
|
Convert(dst, src);
|
|
}
|
|
}
|
|
|
|
string
|
|
GetFilePath(string file_name)
|
|
{
|
|
string result = file_name;
|
|
|
|
for(usize i = file_name.length-1; i64(i) >= 0; i -= 1)
|
|
{
|
|
version(Windows)
|
|
{
|
|
char ch = '\\';
|
|
}
|
|
else
|
|
{
|
|
char ch = '/';
|
|
}
|
|
|
|
if(file_name[i] == ch)
|
|
{
|
|
result = file_name[0 .. i+1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
pragma(inline) void
|
|
Convert(Vec4* dst, u32 src)
|
|
{
|
|
dst.r = cast(f32)((src >> 0) & 0xFF) / 255.0;
|
|
dst.g = cast(f32)((src >> 8) & 0xFF) / 255.0;
|
|
dst.b = cast(f32)((src >> 16) & 0xFF) / 255.0;
|
|
dst.a = cast(f32)((src >> 24) & 0xFF) / 255.0;
|
|
}
|
|
|
|
bool
|
|
BitEq(u64 l, u64 r)
|
|
{
|
|
return (l & r) == r;
|
|
}
|
|
|
|
struct ListBox(T, bool doubly_linked)
|
|
{
|
|
alias U = ListBox!(T, doubly_linked);
|
|
|
|
U* next;
|
|
static if(doubly_linked)
|
|
{
|
|
U* prev;
|
|
}
|
|
T value;
|
|
}
|
|
|
|
struct LinkedList(T)
|
|
{
|
|
T* first, last;
|
|
}
|
|
|
|
void
|
|
ConcatInPlace(T)(T* list, T* to_concat)
|
|
{
|
|
if(to_concat.first)
|
|
{
|
|
if(list.first)
|
|
{
|
|
list.last.next = to_concat.first;
|
|
list.last = to_concat.last;
|
|
}
|
|
else
|
|
{
|
|
list.first = to_concat.first;
|
|
list.last = to_concat.last;
|
|
}
|
|
|
|
memset(to_concat, 0, T.sizeof);
|
|
}
|
|
}
|
|
|
|
U*
|
|
DLLPop(T, U)(T* list, U* nil)
|
|
{
|
|
U* node = list.first;
|
|
|
|
if (!CheckNil(nil, list.first))
|
|
{
|
|
if(list.first == list.last)
|
|
{
|
|
list.first = list.last = nil;
|
|
}
|
|
else
|
|
{
|
|
list.first = list.first.next;
|
|
list.first.prev = nil;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
void
|
|
DLLRemove(T, U)(T* list, U* node, U* nil)
|
|
{
|
|
if(list.first == list.last)
|
|
{
|
|
list.first = list.last = nil;
|
|
}
|
|
else if(list.first == node)
|
|
{
|
|
list.first = node.next;
|
|
list.first.prev = nil;
|
|
}
|
|
else if(list.last == node)
|
|
{
|
|
node.prev.next = nil;
|
|
list.last = node.prev;
|
|
}
|
|
else
|
|
{
|
|
node.next.prev = node.prev;
|
|
node.prev.next = node.next;
|
|
}
|
|
}
|
|
|
|
void
|
|
DLLPushFront(T, U)(T* list, U* node, U* nil)
|
|
{
|
|
node.prev = node.next = nil;
|
|
|
|
if(CheckNil(nil, list.first))
|
|
{
|
|
list.first = list.last = node;
|
|
}
|
|
else
|
|
{
|
|
node.next = list.first;
|
|
node.prev = nil;
|
|
list.first.prev = node;
|
|
list.first = node;
|
|
}
|
|
}
|
|
|
|
void
|
|
DLLPush(T, U)(T* list, U* node, U* nil)
|
|
{
|
|
node.prev = node.next = nil;
|
|
|
|
if(CheckNil(nil, list.first))
|
|
{
|
|
list.first = list.last = node;
|
|
}
|
|
else
|
|
{
|
|
list.last.next = node;
|
|
node.prev = list.last;
|
|
list.last = node;
|
|
node.next = nil;
|
|
}
|
|
}
|
|
|
|
void
|
|
DLLInsert(T, U)(T* list, U* node, U* prev, U* nil) if(hasMember!(T, "last") && hasMember!(U, "prev"))
|
|
{
|
|
if(CheckNil(nil, list.first) && CheckNil(nil, list.last))
|
|
{
|
|
list.first = list.last = node;
|
|
node.next = node.prev = nil;
|
|
}
|
|
else if(!CheckNil(nil, prev))
|
|
{
|
|
node.next = list.first;
|
|
list.first.prev = node;
|
|
list.first = node;
|
|
node.prev = nil;
|
|
}
|
|
else if(list.last == prev)
|
|
{
|
|
list.last.next = node;
|
|
node.prev = list.last;
|
|
list.last = node;
|
|
node.next = nil;
|
|
}
|
|
else if(!CheckNil(nil, prev) && CheckNil(nil, prev.next))
|
|
{}
|
|
else
|
|
{
|
|
node.prev = prev;
|
|
node.next = prev.next;
|
|
node.next.prev = node;
|
|
prev.next = node;
|
|
}
|
|
}
|
|
|
|
pragma(inline) bool
|
|
CheckNil(T)(T* nil, T* node)
|
|
{
|
|
return node == null || node == nil;
|
|
}
|
|
|
|
pragma(inline) U*
|
|
SLLPop(T, U)(T* list, U* nil)
|
|
{
|
|
U* node = list.first;
|
|
|
|
if(list.first == list.last)
|
|
{
|
|
list.first = list.last = nil;
|
|
}
|
|
else
|
|
{
|
|
list.first = list.first.next;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
pragma(inline) void
|
|
SLLRemove(T, U)(T* list, U* node, U* prev, U* nil)
|
|
{
|
|
if(list.first == list.last)
|
|
{
|
|
list.first = list.last = nil;
|
|
}
|
|
else if(list.first == node)
|
|
{
|
|
list.first = node.next;
|
|
}
|
|
else if(list.last == node)
|
|
{
|
|
list.last = prev;
|
|
prev.next = nil;
|
|
}
|
|
else
|
|
{
|
|
prev.next = node.next;
|
|
}
|
|
}
|
|
|
|
pragma(inline) void
|
|
SLLPushFront(T, U)(T* list, U* node, U* nil)
|
|
{
|
|
node.next = nil;
|
|
|
|
if(CheckNil(nil, list.first))
|
|
{
|
|
list.first = list.last = node;
|
|
}
|
|
else
|
|
{
|
|
node.next = list.first;
|
|
list.first = node;
|
|
}
|
|
}
|
|
|
|
pragma(inline) void
|
|
SLLPush(T, U)(T* list, U* node, U* nil)
|
|
{
|
|
node.next = nil;
|
|
|
|
if(CheckNil(nil, list.first))
|
|
{
|
|
list.first = list.last = node;
|
|
}
|
|
else
|
|
{
|
|
list.last.next = node;
|
|
list.last = node;
|
|
node.next = nil;
|
|
}
|
|
}
|
|
|
|
struct KVPair(K, V)
|
|
{
|
|
K key;
|
|
V value;
|
|
KVPair!(K, V)* next;
|
|
}
|
|
|
|
struct Result(V)
|
|
{
|
|
V value;
|
|
bool ok;
|
|
}
|
|
|
|
struct HashTable(K, V)
|
|
{
|
|
alias P = KVPair!(K, V);
|
|
|
|
LinkedList!(P) free_lists;
|
|
LinkedList!(P)[] lists;
|
|
P* nil;
|
|
Arena arena;
|
|
u64 node_count;
|
|
u64 list_count;
|
|
|
|
void opIndexAssign(V value, K key)
|
|
{
|
|
Push(&this, key, value);
|
|
}
|
|
|
|
Result!(V) opIndex(K key)
|
|
{
|
|
P* pair = Search(&this, key);
|
|
|
|
Result!(V) result = { ok: false };
|
|
if(pair != null)
|
|
{
|
|
result.value = pair.value;
|
|
result.ok = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
HashTable!(K, V)
|
|
CreateHashTable(K, V)(usize size)
|
|
{
|
|
Arena arena = CreateArena(MB(4));
|
|
auto nil = Alloc!(KVPair!(K, V))(&arena);
|
|
auto lists = Alloc!(LinkedList!(KVPair!(K, V)))(&arena, size);
|
|
|
|
HashTable!(K, V) table = {
|
|
arena: arena,
|
|
lists: lists,
|
|
list_count: size,
|
|
nil: nil,
|
|
free_lists: {
|
|
first: nil,
|
|
last: nil,
|
|
},
|
|
};
|
|
|
|
foreach(list; table.lists)
|
|
{
|
|
list.first = nil;
|
|
list.last = nil;
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
pragma(inline) void
|
|
Clear(K, V)(HashTable!(K, V)* ht)
|
|
{
|
|
table.count = 0;
|
|
foreach(i, list; ht.lists)
|
|
{
|
|
ConcatInPlace(&ht.free_lists, ht.lists.ptr + i);
|
|
}
|
|
}
|
|
|
|
pragma(inline) KVPair!(K, V)*
|
|
Push(K, V)(HashTable!(K, V)* ht, K key, V value)
|
|
{
|
|
alias P = KVPair!(K, V);
|
|
|
|
P* node = ht.nil;
|
|
|
|
if(!CheckNil(ht.nil, ht.free_lists.first))
|
|
{
|
|
node = SLLPop(&ht.free_lists, ht.nil);
|
|
}
|
|
else
|
|
{
|
|
node = Alloc!(P)(&ht.arena);
|
|
}
|
|
|
|
node.next = ht.nil;
|
|
node.key = key;
|
|
node.value = value;
|
|
|
|
SLLPush(GetList(ht, key), node, ht.nil);
|
|
|
|
ht.node_count += 1;
|
|
|
|
return node;
|
|
}
|
|
|
|
pragma(inline) KVPair!(K, V)*
|
|
Search(K, V)(HashTable!(K, V)* ht, K key)
|
|
{
|
|
KVPair!(K, V)* result = null;
|
|
|
|
auto list = GetList(ht, key);
|
|
for(auto node = list.first; !CheckNil(ht.nil, node); node = node.next)
|
|
{
|
|
if(node.key == key)
|
|
{
|
|
result = node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
pragma(inline) LinkedList!(KVPair!(K, V))*
|
|
GetList(K, V)(HashTable!(K, V)* ht, K key)
|
|
{
|
|
u64 hash = Hash(&key);
|
|
u64 index = hash % ht.list_count;
|
|
return ht.lists.ptr + index;
|
|
}
|
|
|
|
KVPair!(K, V)*[]
|
|
GetAllNodes(K, V)(Arena* arena, HashTable!(K, V)* ht)
|
|
{
|
|
KVPair!(K, V)*[] pairs = Alloc!(KVPair!(K, V)*)(arena, ht.node_count);
|
|
|
|
if(ht.node_count > 0)
|
|
{
|
|
u64 count = 0;
|
|
|
|
for(u64 i = 0; i < ht.lists.length; i += 1)
|
|
{
|
|
for(auto n = ht.lists[i].first; !CheckNil(ht.nil, n); n = n.next)
|
|
{
|
|
pairs[count] = n;
|
|
count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pairs;
|
|
}
|
|
|
|
pragma(inline) Result!(V)
|
|
Delete(K, V)(HashTable!(K, V)* ht, K key)
|
|
{
|
|
Result!(V) result = { ok: false };
|
|
|
|
auto list = GetList(ht, key);
|
|
auto prev = ht.nil;
|
|
for(auto node = list.first; !CheckNil(ht.nil, node); prev = node, node = node.next)
|
|
{
|
|
if(node.key == key)
|
|
{
|
|
SLLRemove(list, node, prev, ht.nil);
|
|
|
|
result.ok = true;
|
|
result.value = node.value;
|
|
|
|
memset(&node.value, 0, node.value.sizeof);
|
|
|
|
SLLPush(&ht.free_lists, node, ht.nil);
|
|
|
|
ht.node_count -= 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const u64 HASH_SEED = 5995;
|
|
|
|
pragma(inline) u64
|
|
Hash(T)(T[] value)
|
|
{
|
|
return XXHash64(value.ptr, (T.sizeof * value.length) / u8.sizeof, HASH_SEED);
|
|
}
|
|
|
|
pragma(inline) u64
|
|
Hash(T)(T* value)
|
|
{
|
|
return XXHash64(value, T.sizeof / u8.sizeof, HASH_SEED);
|
|
}
|
|
|
|
pragma(inline) u64
|
|
Hash(string str)
|
|
{
|
|
return XXHash64(str.ptr, str.length, HASH_SEED);
|
|
}
|
|
|
|
// TODO: probably needs improvement/testing
|
|
struct IntervalTimer
|
|
{
|
|
Timer base;
|
|
u64 interval;
|
|
|
|
alias base this;
|
|
}
|
|
|
|
IntervalTimer
|
|
CreateFPSTimer(u64 fps)
|
|
{
|
|
return CreateTimer(1000/fps);
|
|
}
|
|
|
|
IntervalTimer
|
|
CreateTimer(u64 ms)
|
|
{
|
|
IntervalTimer timer = { base: CreateTimer() };
|
|
|
|
timer.interval = timer.cpu_freq * (ms/1000);
|
|
|
|
return timer;
|
|
}
|
|
|
|
void
|
|
ResetTimer(IntervalTimer* t)
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
t.prev = RDTSC();
|
|
}
|
|
else
|
|
{
|
|
assert(false, NO_IMPL);
|
|
}
|
|
}
|
|
|
|
pragma(inline) bool
|
|
CheckTimer(IntervalTimer* t)
|
|
{
|
|
bool result = false;
|
|
|
|
static if(NativeTarget)
|
|
{
|
|
u64 time = RDTSC();
|
|
if(time - t.prev > t.interval)
|
|
{
|
|
result = true;
|
|
t.prev = time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false, NO_IMPL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
struct Timer
|
|
{
|
|
u64 cpu_freq;
|
|
u64 prev;
|
|
}
|
|
|
|
Timer
|
|
CreateTimer()
|
|
{
|
|
static if(NativeTarget)
|
|
{
|
|
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 timer = {
|
|
cpu_freq: cpu_freq,
|
|
prev: RDTSC(),
|
|
};
|
|
}
|
|
else
|
|
{
|
|
Timer timer;
|
|
|
|
assert(false, NO_IMPL);
|
|
}
|
|
|
|
return timer;
|
|
}
|
|
|
|
pragma(inline) f32
|
|
DeltaTime(Timer* t)
|
|
{
|
|
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
|
|
Replace(string target, u8 find, string replace)
|
|
{
|
|
assert(__ctfe, "Function can only be run at compile time");
|
|
|
|
u8[1024] result = '\0';
|
|
|
|
usize count;
|
|
foreach(ch; target)
|
|
{
|
|
if(ch == find)
|
|
{
|
|
for(usize i = 0; i < replace.length; i += 1)
|
|
{
|
|
result[count++] = replace[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result[count++] = ch;
|
|
}
|
|
}
|
|
|
|
return Str(result[0 .. count]);
|
|
}
|
|
|
|
static string
|
|
Str(T)(T v) if(isIntegral!(T))
|
|
{
|
|
assert(__ctfe);
|
|
|
|
if(v == 0)
|
|
{
|
|
return "0";
|
|
}
|
|
|
|
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
|
|
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;
|
|
|
|
usize remaining = length;
|
|
version(X86_64)
|
|
{
|
|
if(remaining >= 64)
|
|
{
|
|
for(u64 i = 0; i + 64 < length; i += 64)
|
|
{
|
|
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;
|
|
|
|
sub remaining, 64;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(remaining >= 32)
|
|
{
|
|
for(u64 i = length - remaining; i + 32 < length; i += 32)
|
|
{
|
|
asm
|
|
{
|
|
mov R8, src;
|
|
mov R9, dst;
|
|
|
|
add R8, i;
|
|
movdqu XMM0, [R8+00];
|
|
movdqu XMM1, [R8+16];
|
|
|
|
add R9, i;
|
|
movdqu [R9+00], XMM0;
|
|
movdqu [R9+16], XMM1;
|
|
|
|
sub remaining, 32;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(remaining > 0)
|
|
{
|
|
dst[length-remaining .. length] = src[length-remaining .. length];
|
|
}
|
|
}
|
|
|
|
void
|
|
Assign(T, Args...)(T slice, Args args) if(isArray!(T))
|
|
{
|
|
assert(slice.length == Args.length);
|
|
|
|
static foreach(i; 0 .. Args.length)
|
|
{
|
|
slice[i] = cast(typeof(slice[0]))args[i];
|
|
}
|
|
}
|
|
|
|
const u64 PRIME_1 = 11400714785074694791UL;
|
|
const u64 PRIME_2 = 14029467366897019727UL;
|
|
const u64 PRIME_3 = 1609587929392839161UL;
|
|
const u64 PRIME_4 = 9650029242287828579UL;
|
|
const u64 PRIME_5 = 2870177450012600261UL;
|
|
|
|
const u64 HASH_MAX_BUFFER_SIZE = 31+1;
|
|
|
|
struct XXHash
|
|
{
|
|
u64[4] state;
|
|
u8[HASH_MAX_BUFFER_SIZE] buffer;
|
|
usize buffer_size;
|
|
usize total_length;
|
|
}
|
|
|
|
static XXHash XXHASH;
|
|
|
|
u64
|
|
XXHash64(const(void)* input, u64 length, u64 seed)
|
|
{
|
|
InitHash(seed);
|
|
HashAdd(input, length);
|
|
return GetHash();
|
|
}
|
|
|
|
void
|
|
InitHash(u64 seed)
|
|
{
|
|
ref XXHash xxh = XXHASH;
|
|
|
|
xxh.state[0] = seed + PRIME_1 + PRIME_2;
|
|
xxh.state[1] = seed + PRIME_2;
|
|
xxh.state[2] = seed;
|
|
xxh.state[3] = seed - PRIME_1;
|
|
|
|
xxh.buffer_size = 0;
|
|
xxh.total_length = 0;
|
|
}
|
|
|
|
bool
|
|
HashAdd(const(void)* input, u64 length)
|
|
{
|
|
if(!input || length == 0) return false;
|
|
|
|
ref XXHash xxh = XXHASH;
|
|
|
|
xxh.total_length += length;
|
|
|
|
const(u8)* data = cast(const(u8)*)input;
|
|
|
|
if(xxh.buffer_size+length < HASH_MAX_BUFFER_SIZE)
|
|
{
|
|
for(;length--;)
|
|
{
|
|
xxh.buffer[xxh.buffer_size++] = *(data++);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const(u8)* stop = data+length;
|
|
const(u8)* stop_block = stop - HASH_MAX_BUFFER_SIZE;
|
|
|
|
if(xxh.buffer_size > 0)
|
|
{
|
|
for(; xxh.buffer_size < HASH_MAX_BUFFER_SIZE;)
|
|
{
|
|
xxh.buffer[xxh.buffer_size++] = *data++;
|
|
}
|
|
|
|
ProcessHash(xxh.buffer.ptr, xxh.state[0], xxh.state[1], xxh.state[2], xxh.state[3]);
|
|
}
|
|
|
|
u64 s0 = xxh.state[0];
|
|
u64 s1 = xxh.state[1];
|
|
u64 s2 = xxh.state[2];
|
|
u64 s3 = xxh.state[3];
|
|
|
|
while(data <= stop_block)
|
|
{
|
|
ProcessHash(data, s0, s1, s2, s3);
|
|
data += 32;
|
|
}
|
|
|
|
xxh.state[0] = s0;
|
|
xxh.state[1] = s1;
|
|
xxh.state[2] = s2;
|
|
xxh.state[3] = s3;
|
|
|
|
xxh.buffer_size = stop - data;
|
|
for(u32 i = 0; i < xxh.buffer_size; i += 1)
|
|
{
|
|
xxh.buffer[i] = data[i];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
u64
|
|
GetHash()
|
|
{
|
|
ref XXHash xxh = XXHASH;
|
|
|
|
u64 result;
|
|
if(xxh.total_length >= HASH_MAX_BUFFER_SIZE)
|
|
{
|
|
result = RotateLeft(xxh.state[0], 1) +
|
|
RotateLeft(xxh.state[1], 7) +
|
|
RotateLeft(xxh.state[2], 12) +
|
|
RotateLeft(xxh.state[3], 18);
|
|
|
|
result = (result ^ ProcessSingleHashValue(0, xxh.state[0])) * PRIME_1 + PRIME_4;
|
|
result = (result ^ ProcessSingleHashValue(0, xxh.state[1])) * PRIME_1 + PRIME_4;
|
|
result = (result ^ ProcessSingleHashValue(0, xxh.state[2])) * PRIME_1 + PRIME_4;
|
|
result = (result ^ ProcessSingleHashValue(0, xxh.state[3])) * PRIME_1 + PRIME_4;
|
|
}
|
|
else
|
|
{
|
|
result = xxh.state[2] + PRIME_5;
|
|
}
|
|
|
|
result += xxh.total_length;
|
|
|
|
const(u8)* data = xxh.buffer.ptr;
|
|
const(u8)* stop = data + xxh.buffer_size;
|
|
|
|
for(; data + 8 <= stop; data += 8)
|
|
{
|
|
result = RotateLeft(result ^ ProcessSingleHashValue(0, *cast(u64*)data), 27) * PRIME_1 + PRIME_4;
|
|
}
|
|
|
|
if(data+4 <= stop)
|
|
{
|
|
result = RotateLeft(result ^ (*cast(u32*)data) * PRIME_1, 23) * PRIME_2 + PRIME_3;
|
|
data += 4;
|
|
}
|
|
|
|
while(data != stop)
|
|
{
|
|
result = RotateLeft(result ^ (*data++) * PRIME_5, 11) * PRIME_1;
|
|
}
|
|
|
|
result ^= result >> 33;
|
|
result *= PRIME_2;
|
|
result ^= result >> 29;
|
|
result *= PRIME_3;
|
|
result ^= result >> 32;
|
|
|
|
return result;
|
|
}
|
|
|
|
pragma(inline) u64
|
|
RotateLeft(u64 x, u8 bits)
|
|
{
|
|
return (x << bits) | (x >> (64 - bits));
|
|
}
|
|
|
|
pragma(inline) u64
|
|
ProcessSingleHashValue(u64 previous, u64 input)
|
|
{
|
|
return RotateLeft(previous + input*PRIME_2, 31) * PRIME_1;
|
|
}
|
|
|
|
pragma(inline) void
|
|
ProcessHash(const(void)* data, ref u64 state_0, ref u64 state_1, ref u64 state_2, ref u64 state_3)
|
|
{
|
|
u64* block = cast(u64*)data;
|
|
state_0 = ProcessSingleHashValue(state_0, block[0]);
|
|
state_1 = ProcessSingleHashValue(state_1, block[1]);
|
|
state_2 = ProcessSingleHashValue(state_2, block[2]);
|
|
state_3 = ProcessSingleHashValue(state_3, block[3]);
|
|
}
|
|
|
|
version(DLIB_TEST) unittest
|
|
{
|
|
{ // Singly Linked List
|
|
alias LT = ListBox!(u32, false);
|
|
LinkedList!(LT) list;
|
|
LT[5] nodes;
|
|
foreach(u32 i, n; nodes)
|
|
{
|
|
nodes[i].value = i;
|
|
SLLPush(&list, &nodes[i], null);
|
|
}
|
|
|
|
u32 count = 0;
|
|
u32[3] res1 = [0, 2, 4];
|
|
|
|
SLLRemove(&list, &nodes[1], &nodes[0], null);
|
|
SLLRemove(&list, &nodes[3], &nodes[2], null);
|
|
|
|
LT* n = list.first;
|
|
|
|
assert(list.first != null && list.last != null);
|
|
assert(n != null);
|
|
assert(n.next != null);
|
|
|
|
void TestSLList(LinkedList!(LT)* list, u32[] result)
|
|
{
|
|
LT* n = list.first;
|
|
foreach(i, v; result)
|
|
{
|
|
assert(n != null);
|
|
assert(v == n.value);
|
|
|
|
if(i == result.length-1)
|
|
{
|
|
assert(n.next == null);
|
|
assert(n == list.last);
|
|
}
|
|
|
|
n = n.next;
|
|
}
|
|
}
|
|
|
|
TestSLList(&list, res1);
|
|
|
|
count = 0;
|
|
u32[5] res2 = [3, 0, 2, 4, 1];
|
|
|
|
SLLPushFront(&list, &nodes[3], null);
|
|
SLLPush(&list, &nodes[1], null);
|
|
|
|
TestSLList(&list, res2);
|
|
|
|
count = 0;
|
|
|
|
SLLRemove(&list, &nodes[3], null, null);
|
|
SLLRemove(&list, &nodes[1], &nodes[4], null);
|
|
|
|
TestSLList(&list, res1);
|
|
}
|
|
|
|
{ // Doubly Linked List
|
|
alias LT = ListBox!(u32, true);
|
|
|
|
void TestDLList(T)(T* list, u32[] result)
|
|
{
|
|
LT* n = list.first;
|
|
foreach(i, v; result)
|
|
{
|
|
assert(n != null);
|
|
assert(v == n.value);
|
|
|
|
if(i > 0)
|
|
{
|
|
assert(n.prev != null);
|
|
}
|
|
|
|
if(i == result.length-1)
|
|
{
|
|
assert(n.next == null);
|
|
assert(n == list.last);
|
|
}
|
|
|
|
n = n.next;
|
|
}
|
|
|
|
n = list.last;
|
|
foreach_reverse(i, v; result)
|
|
{
|
|
assert(n != null);
|
|
assert(v == n.value);
|
|
|
|
if(i == result.length-1)
|
|
{
|
|
assert(n.next == null);
|
|
}
|
|
|
|
if(i == 0)
|
|
{
|
|
assert(n.prev == null);
|
|
assert(n == list.first);
|
|
}
|
|
|
|
n = n.prev;
|
|
}
|
|
}
|
|
|
|
LinkedList!(LT) list;
|
|
LT[5] nodes;
|
|
foreach(u32 i, n; nodes)
|
|
{
|
|
nodes[i].value = i;
|
|
DLLPush(&list, &nodes[i], null);
|
|
}
|
|
|
|
assert(list.first != null && list.last != null);
|
|
|
|
u32[5] test1 = [0, 1, 2, 3, 4];
|
|
TestDLList(&list, test1);
|
|
|
|
u32 count = 0;
|
|
u32[3] res1 = [0, 2, 4];
|
|
|
|
DLLRemove(&list, &nodes[1], null);
|
|
DLLRemove(&list, &nodes[3], null);
|
|
|
|
TestDLList(&list, res1);
|
|
|
|
count = 0;
|
|
u32[5] res2 = [3, 0, 2, 4, 1];
|
|
|
|
DLLPushFront(&list, &nodes[3], null);
|
|
DLLPush(&list, &nodes[1], null);
|
|
|
|
TestDLList(&list, res2);
|
|
|
|
DLLRemove(&list, &nodes[3], null);
|
|
DLLRemove(&list, &nodes[1], null);
|
|
|
|
TestDLList(&list, res1);
|
|
|
|
DLLInsert(&list, &nodes[1], &nodes[0], null);
|
|
}
|
|
|
|
{ // MemCpy
|
|
|
|
u8[777] bytes;
|
|
u8[777] test_bytes;
|
|
|
|
bytes[0 .. 123] = 123;
|
|
bytes[123 .. 333] = 133;
|
|
bytes[333 .. 655] = 155;
|
|
bytes[655 .. $] = 199;
|
|
|
|
test_bytes[0 .. $] = bytes[0 .. $];
|
|
|
|
assert(test_bytes == bytes);
|
|
|
|
test_bytes[0 .. $] = 0;
|
|
|
|
MemCpy(test_bytes.ptr, bytes.ptr, 777);
|
|
|
|
assert(test_bytes == bytes);
|
|
|
|
test_bytes[0 .. $] = 0;
|
|
|
|
MemCpy(test_bytes.ptr+100, bytes.ptr, 32);
|
|
|
|
u32 count = 0;
|
|
foreach(i, v; test_bytes[100 .. 132])
|
|
{
|
|
if(v != bytes[count])
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
count += 1;
|
|
}
|
|
|
|
assert(test_bytes[100 .. 132] == bytes[0 .. 32]);
|
|
|
|
test_bytes[0 .. $] = 0;
|
|
|
|
MemCpy(test_bytes.ptr, bytes.ptr, 33);
|
|
|
|
assert(test_bytes[0 .. 33] == bytes[0 .. 33]);
|
|
|
|
test_bytes[0 .. $] = 0;
|
|
|
|
MemCpy(test_bytes.ptr, bytes.ptr, 65);
|
|
|
|
assert(test_bytes[0 .. 65] == bytes[0 .. 65]);
|
|
|
|
test_bytes[0 .. $] = 0;
|
|
|
|
MemCpy(test_bytes.ptr, bytes.ptr, 96);
|
|
|
|
foreach(i, v; test_bytes[0 .. 96])
|
|
{
|
|
if(v != bytes[i])
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
assert(test_bytes[0 .. 96] == bytes[0 .. 96]);
|
|
}
|
|
|
|
{ // Hash Table
|
|
auto table = CreateHashTable!(u64, u64)(10);
|
|
|
|
table[100] = 100;
|
|
}
|
|
|
|
{ // Hash
|
|
u8[10] arr_1 = 5;
|
|
u64[10] arr_2 = 555;
|
|
u64 val_1 = 555555;
|
|
u32 val_2 = 33333;
|
|
|
|
u64 v1 = Hash(arr_1);
|
|
u64 v2 = Hash(arr_2);
|
|
u64 v3 = Hash(&val_1);
|
|
|
|
assert(v1 > 0);
|
|
assert(v2 > 0);
|
|
assert(v3 > 0);
|
|
}
|
|
|
|
{ // Casts
|
|
u8[] arr = CastStr!(u8)("Test");
|
|
char[2] char_arr = ['a', 'b'];
|
|
arr = CastArr!(u8)(char_arr);
|
|
}
|
|
|
|
static if(NativeTarget)
|
|
{
|
|
ResetScratch(MB(4));
|
|
string str = Scratchf("%s testing %s", 55, "testing");
|
|
assert(str == "55 testing testing");
|
|
}
|
|
|
|
{
|
|
u64[555] slice = 53321;
|
|
|
|
MemSet(slice.ptr, 0, u64.sizeof*slice.length);
|
|
|
|
for(usize i = 0; i < slice.length; i += 1)
|
|
{
|
|
assert(slice[i] == 0);
|
|
}
|
|
|
|
MemSet(slice.ptr, 5, u64.sizeof*slice.length);
|
|
|
|
u64 cmp = cast(u64)(
|
|
cast(u64)(5) << 56 |
|
|
cast(u64)(5) << 48 |
|
|
cast(u64)(5) << 40 |
|
|
cast(u64)(5) << 32 |
|
|
cast(u64)(5) << 24 |
|
|
cast(u64)(5) << 16 |
|
|
cast(u64)(5) << 8 |
|
|
cast(u64)(5) << 0
|
|
);
|
|
|
|
for(usize i = 0; i < slice.length; i += 1)
|
|
{
|
|
assert(slice[i] == cmp);
|
|
}
|
|
}
|
|
}
|