diff --git a/Makefile b/Makefile index 924a71e..e74677b 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,7 @@ DEPFILE := $(OBJDIR)/$(TARGETNAME).d DFLAGS := $(DFLAGS) -preview=bitfields -preview=rvaluerefparam -preview=nosharedaccess -preview=in -ifeq ($(OS),windows) -SOURCES := $(shell dir /s /b $(SRCDIR)\\*.d) -else SOURCES := $(shell find "$(SRCDIR)" -type f -name '*.d') -endif # Set target file based on build type and OS ifeq ($(BUILD_TYPE),exe) @@ -93,30 +89,19 @@ else endif ifeq ($(CONFIG),unittest) - DFLAGS := $(DFLAGS) -unittest + DFLAGS := $(DFLAGS) -unittest -main endif -include $(DEPFILE) $(TARGET): -ifeq ($(OS),windows) - @if not exist "obj" mkdir "obj" > nul 2>&1 - @if not exist "$(subst /,\,$(OBJDIR))" mkdir "$(subst /,\,$(OBJDIR))" > nul 2>&1 - @if not exist "bin" mkdir "bin" > nul 2>&1 - @if not exist "$(subst /,\,$(TARGETDIR))" mkdir "$(subst /,\,$(TARGETDIR))" > nul 2>&1 -else mkdir -p $(OBJDIR) $(TARGETDIR) -endif ifeq ($(D_COMPILER),ldc) "$(DC)" $(DFLAGS) $(BUILD_CMD_FLAGS) -of$(TARGET) -od$(OBJDIR) -deps=$(DEPFILE) $(SOURCES) else ifeq ($(D_COMPILER),dmd) ifeq ($(BUILD_TYPE),lib) - "$(DC)" $(DFLAGS) $(BUILD_CMD_FLAGS) -of$(notdir $(TARGET)) -od$(OBJDIR) -makedeps $(SOURCES) > $(DEPFILE) -ifeq ($(OS),windows) - move "$(subst /,\,$(OBJDIR))\\$(notdir $(TARGET))" "$(subst /,\,$(TARGETDIR))" > nul -else + "$(DC)" $(DFLAGS) $(BUILD_CMD_FLAGS) -of$(OBJDIR)/$(notdir $(TARGET)) -od$(OBJDIR) -makedeps $(SOURCES) > $(DEPFILE) mv "$(OBJDIR)/$(notdir $(TARGET))" "$(TARGETDIR)" -endif else # exe "$(DC)" $(DFLAGS) $(BUILD_CMD_FLAGS) -of$(TARGET) -od$(OBJDIR) -makedeps $(SOURCES) > $(DEPFILE) endif diff --git a/src/object.d b/src/object.d new file mode 100644 index 0000000..e739fa9 --- /dev/null +++ b/src/object.d @@ -0,0 +1,1755 @@ +// Minimal object.d — replaces druntime's implicit root module. +// +// This file is the auditing frontier: every symbol added here is a +// symbol the compiler or linker demanded. Keep it as small as possible. +module object; + +static assert(__VERSION__ >= 2112, + "uRT requires DMD frontend 2.112+ (DMD ≥2.112, LDC ≥1.42). " ~ + "Older frontends use TypeInfo-based AA hooks incompatible with uRT's template-based AAs."); + +// ────────────────────────────────────────────────────────────────────── +// Platform-dependent ABI flags (must match druntime's detection) +// ────────────────────────────────────────────────────────────────────── + +version (X86_64) +{ + version (DigitalMars) version = WithArgTypes; + else version (Windows) {} // Win64 ABI doesn't need argTypes + else version = WithArgTypes; +} +else version (AArch64) +{ + version (OSX) {} + else version (iOS) {} + else version (TVOS) {} + else version (WatchOS) {} + else version = WithArgTypes; +} + + +// ────────────────────────────────────────────────────────────────────── +// Fundamental type aliases (compiler hardcodes references to these) +// ────────────────────────────────────────────────────────────────────── + +alias size_t = typeof(int.sizeof); +alias ptrdiff_t = typeof(cast(void*) 0 - cast(void*) 0); +alias nullptr_t = typeof(null); +alias noreturn = typeof(*null); + +version (Windows) + alias wchar wchar_t; +else version (Posix) + alias dchar wchar_t; +else version (WASI) + alias dchar wchar_t; + +alias string = immutable(char)[]; +alias wstring = immutable(wchar)[]; +alias dstring = immutable(dchar)[]; + +alias hash_t = size_t; + +// Required by __importc_builtins.di for lazy module references in ImportC. +template imported(string name) +{ + mixin("import imported = " ~ name ~ ";"); +} + +// ────────────────────────────────────────────────────────────────────── +// Primitive tools +// ────────────────────────────────────────────────────────────────────── + +// TODO: move the functions here so object doesn't import these modules... +public import urt.lifetime : move, forward; +public import urt.meta : Alias, AliasSeq; +public import urt.util : min, max, swap; + +//alias Alias(alias a) = a; +//alias Alias(T) = T; +// +//alias AliasSeq(TList...) = TList; +// +//T min(T)(T a, T b) pure nothrow @nogc @safe +// => b < a ? b : a; +// +//T max(T)(T a, T b) pure nothrow @nogc @safe +// => b > a ? b : a; +// +//ref T swap(T)(ref T a, return ref T b) +//{ +// import urt.lifetime : move, moveEmplace; +// +// T t = a.move; +// b.move(a); +// t.move(b); +// return b; +//} +// +//T swap(T)(ref T a, T b) +//{ +// import urt.lifetime : move, moveEmplace; +// +// auto t = a.move; +// b.move(a); +// return t.move; +//} + + +// ────────────────────────────────────────────────────────────────────── +// Object — root of the class hierarchy +// ────────────────────────────────────────────────────────────────────── + +class Object +{ +@nogc: + size_t toHash() @trusted nothrow + { + size_t addr = cast(size_t) cast(void*) this; + return addr ^ (addr >>> 4); + } + + bool opEquals(Object rhs) + => this is rhs; + + int opCmp(Object rhs) + => 0; + + ptrdiff_t toString(char[] buffer) const nothrow + => try_copy_string(buffer, "Object"); +} + +// Free-function opEquals for class types — the compiler lowers `a == b` +// on class objects to a call to this function. +bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) + if ((is(LHS : const Object) || is(LHS : const shared Object)) && + (is(RHS : const Object) || is(RHS : const shared Object))) +{ + static if (__traits(compiles, lhs.opEquals(rhs)) && __traits(compiles, rhs.opEquals(lhs))) + { + if (lhs is rhs) + return true; + if (lhs is null || rhs is null) + return false; + if (!lhs.opEquals(rhs)) + return false; + if (typeid(lhs) is typeid(rhs) || !__ctfe && typeid(lhs).opEquals(typeid(rhs))) + return true; + return rhs.opEquals(lhs); + } + else + return .opEquals!(Object, Object)(*cast(Object*) &lhs, *cast(Object*) &rhs); +} + +// ────────────────────────────────────────────────────────────────────── +// TypeInfo — compiler generates references for typeid, AAs, etc. +// ────────────────────────────────────────────────────────────────────── + +class TypeInfo +{ +@nogc: + size_t getHash(scope const void* p) @trusted nothrow const + => 0; + + bool equals(scope const void* p1, scope const void* p2) @trusted const + => p1 == p2; + + int compare(scope const void* p1, scope const void* p2) @trusted const + => 0; + + @property size_t tsize() nothrow pure const @safe @nogc + => 0; + + const(TypeInfo) next() nothrow pure const @nogc + => null; + + size_t[] offTi() nothrow const + => null; + + @property uint flags() nothrow pure const @safe @nogc + => 0; + + @property size_t talign() nothrow pure const @safe @nogc + => tsize; + + const(void)[] initializer() nothrow pure const @safe @nogc + => null; + + @property immutable(void)* rtInfo() nothrow pure const @safe @nogc + => null; + + version (WithArgTypes) + { + int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow + { + arg1 = null; + arg2 = null; + return 0; + } + } + + override size_t toHash() @trusted nothrow const + => 0; + + override bool opEquals(Object rhs) + => false; + + override int opCmp(Object rhs) + => 0; + + override ptrdiff_t toString(char[] buffer) const nothrow + => try_copy_string(buffer, "TypeInfo"); +} + +struct Interface +{ + TypeInfo_Class classinfo; + void*[] vtbl; + size_t offset; +} + +struct OffsetTypeInfo +{ + size_t offset; + TypeInfo ti; +} + +class TypeInfo_Class : TypeInfo +{ +@nogc: + byte[] m_init; + string name; + void*[] vtbl; + Interface[] interfaces; + TypeInfo_Class base; + void* destructor; + void function(Object) nothrow @nogc classInvariant; + + enum ClassFlags : ushort + { + isCOMclass = 0x1, + noPointers = 0x2, + hasOffTi = 0x4, + hasCtor = 0x8, + hasGetMembers = 0x10, + hasTypeInfo = 0x20, + isAbstract = 0x40, + isCPPclass = 0x80, + hasDtor = 0x100, + hasNameSig = 0x200, + } + ClassFlags m_flags; + ushort depth; + void* deallocator; + OffsetTypeInfo[] m_offTi; + void function(Object) @nogc defaultConstructor; + + immutable(void)* m_RTInfo; + override @property immutable(void)* rtInfo() nothrow pure const @safe { return m_RTInfo; } + + uint[4] nameSig; + + override @property size_t tsize() nothrow pure const @safe + { + return (void*).sizeof; + } +} + +// TypeInfo_Struct — compiler generates static instances for every struct type. +// Field layout must exactly match what the compiler emits. +class TypeInfo_Struct : TypeInfo +{ +@nogc: + override ptrdiff_t toString(char[] buffer) const nothrow + => try_copy_string(buffer, name); + + override size_t toHash() @trusted nothrow const + => hashOf(mangledName); + + override bool opEquals(Object o) + { + if (this is o) return true; + auto s = cast(const TypeInfo_Struct) o; + return s && this.mangledName == s.mangledName; + } + + override size_t getHash(scope const void* p) @trusted pure nothrow const + { + if (xtoHash) + return (*xtoHash)(p); + return 0; + } + + override bool equals(scope const void* p1, scope const void* p2) @trusted pure nothrow const + { + if (!p1 || !p2) return false; + if (xopEquals) + { + const dg = _member_func(p1, xopEquals); + return dg.xopEquals(p2); + } + return p1 == p2; + } + + override int compare(scope const void* p1, scope const void* p2) @trusted pure nothrow const + { + if (p1 == p2) return 0; + if (!p1) return -1; + if (!p2) return 1; + if (xopCmp) + { + const dg = _member_func(p1, xopCmp); + return dg.xopCmp(p2); + } + return 0; + } + + override @property size_t tsize() nothrow pure const + => initializer().length; + + override const(void)[] initializer() nothrow pure const @safe @nogc + => m_init; + + override @property uint flags() nothrow pure const + => m_flags; + + override @property size_t talign() nothrow pure const + => m_align; + + string mangledName; + + @property string name() nothrow const @trusted + => mangledName; // no demangling — avoids pulling in core.demangle + + void[] m_init; + + @safe pure nothrow + { + size_t function(in void*) @nogc xtoHash; + bool function(in void*, in void*) @nogc xopEquals; + int function(in void*, in void*) @nogc xopCmp; + ptrdiff_t function(in void*, const(char)[]) @nogc xtoString; + + enum StructFlags : uint + { + hasPointers = 0x1, + isDynamicType = 0x2, + } + StructFlags m_flags; + } + union + { + void function(void*) @nogc xdtor; + void function(void*, const TypeInfo_Struct) @nogc xdtorti; + } + void function(void*) xpostblit; + + uint m_align; + + version (WithArgTypes) + { + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + { + arg1 = m_arg1; + arg2 = m_arg2; + return 0; + } + + TypeInfo m_arg1; + TypeInfo m_arg2; + } + + override @property immutable(void)* rtInfo() nothrow pure const @safe @nogc + => m_RTInfo; + + immutable(void)* m_RTInfo; +} + +class TypeInfo_Enum : TypeInfo +{ +@nogc: + override size_t getHash(scope const void* p) const { return base.getHash(p); } + override bool equals(scope const void* p1, scope const void* p2) const { return base.equals(p1, p2); } + override int compare(scope const void* p1, scope const void* p2) const { return base.compare(p1, p2); } + override @property size_t tsize() nothrow pure const { return base.tsize; } + override const(TypeInfo) next() nothrow pure const @nogc { return base.next; } + override @property uint flags() nothrow pure const { return base.flags; } + override @property size_t talign() nothrow pure const { return base.talign; } + override @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return base.rtInfo; } + + override const(void)[] initializer() nothrow pure const @nogc + => m_init.length ? m_init : base.initializer(); + + version (WithArgTypes) + { + override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + => base.argTypes(arg1, arg2); + } + + TypeInfo base; + string name; + void[] m_init; +} + +class TypeInfo_Pointer : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => (void*).sizeof; + + override const(TypeInfo) next() nothrow pure const @nogc + => m_next; + + TypeInfo m_next; +} + +class TypeInfo_Array : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => (void[]).sizeof; + + override const(TypeInfo) next() nothrow pure const @nogc + => value; + + TypeInfo value; +} + +// Built-in array TypeInfo subclasses — the compiler generates references to +// these for common array types. Empty subclasses are sufficient; the +// compiler fills in the `value` field. +class TypeInfo_Ah : TypeInfo_Array {} // ubyte[] +class TypeInfo_Ag : TypeInfo_Array {} // byte[] +class TypeInfo_Aa : TypeInfo_Array {} // char[] +class TypeInfo_Aaya : TypeInfo_Array {} // string[] +class TypeInfo_At : TypeInfo_Array {} // ushort[] +class TypeInfo_As : TypeInfo_Array {} // short[] +class TypeInfo_Au : TypeInfo_Array {} // wchar[] +class TypeInfo_Ak : TypeInfo_Array {} // uint[] +class TypeInfo_Ai : TypeInfo_Array {} // int[] +class TypeInfo_Aw : TypeInfo_Array {} // dchar[] +class TypeInfo_Am : TypeInfo_Array {} // ulong[] +class TypeInfo_Al : TypeInfo_Array {} // long[] +class TypeInfo_Af : TypeInfo_Array {} // float[] +class TypeInfo_Ad : TypeInfo_Array {} // double[] +class TypeInfo_Av : TypeInfo_Array {} // void[] + +class TypeInfo_StaticArray : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => len * value.tsize; + + override const(TypeInfo) next() nothrow pure const @nogc + => value; + + TypeInfo value; + size_t len; +} + +class TypeInfo_Vector : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => base.tsize; + + TypeInfo base; +} + +class TypeInfo_Function : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => 0; + + TypeInfo next; + string deco; +} + +class TypeInfo_Delegate : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + { + alias dg = int delegate(); + return dg.sizeof; + } + + TypeInfo next; + string deco; +} + +class TypeInfo_Interface : TypeInfo +{ +@nogc: + override @property size_t tsize() nothrow pure const + => (void*).sizeof; + + TypeInfo_Class info; +} + +class TypeInfo_Tuple : TypeInfo +{ + TypeInfo[] elements; +} + +class TypeInfo_AssociativeArray : TypeInfo +{ +@nogc: + override ptrdiff_t toString(char[] buffer) const nothrow + { + if (!buffer.ptr) + return value.toString(null) + key.toString(null) + 2; + ptrdiff_t l = value.toString(buffer); + if (l < 0) + return l; + if (buffer.length < l + 2) + return -1; + buffer[l] = '['; + ptrdiff_t k = key.toString(buffer[l + 1 .. $]); + if (k < 0) + return k; + l += k + 2; + if (buffer.length < l) + return -1; + buffer[l - 1] = ']'; + return l; + } + + override bool opEquals(Object o) + { + if (this is o) return true; + auto c = cast(const TypeInfo_AssociativeArray) o; + return c && this.key == c.key && this.value == c.value; + } + + override bool equals(scope const void* p1, scope const void* p2) @trusted const + => xopEquals(p1, p2); + + override hash_t getHash(scope const void* p) nothrow @trusted const + => xtoHash(p); + + override @property size_t tsize() nothrow pure const + => (char[int]).sizeof; + + override const(void)[] initializer() const @trusted + => (cast(void*)null)[0 .. (char[int]).sizeof]; + + override const(TypeInfo) next() nothrow pure const @nogc { return value; } + override @property uint flags() nothrow pure const { return 1; } + + private static import urt.internal.aa; + alias Entry(K, V) = urt.internal.aa.Entry!(K, V); + + TypeInfo value; + TypeInfo key; + TypeInfo entry; + + bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals; + hash_t function(scope const void*) nothrow @safe xtoHash; + + alias aaOpEqual(K, V) = urt.internal.aa._aaOpEqual!(K, V); + alias aaGetHash(K, V) = urt.internal.aa._aaGetHash!(K, V); + + override @property size_t talign() nothrow pure const + => (char[int]).alignof; +} + +class TypeInfo_Const : TypeInfo +{ +@nogc: + override size_t getHash(scope const void* p) const { return base.getHash(p); } + override bool equals(scope const void* p1, scope const void* p2) const { return base.equals(p1, p2); } + override int compare(scope const void* p1, scope const void* p2) const { return base.compare(p1, p2); } + override @property size_t tsize() nothrow pure const { return base.tsize; } + override const(TypeInfo) next() nothrow pure const @nogc { return base.next; } + override @property uint flags() nothrow pure const { return base.flags; } + override const(void)[] initializer() nothrow pure const @nogc { return base.initializer(); } + override @property size_t talign() nothrow pure const { return base.talign; } + + TypeInfo base; +} + +class TypeInfo_Invariant : TypeInfo_Const +{ +} + +class TypeInfo_Shared : TypeInfo_Const +{ +} + +class TypeInfo_Inout : TypeInfo_Const +{ +} + +// ────────────────────────────────────────────────────────────────────── +// TypeInfo for built-in types — compiler references these by mangled name +// (e.g. TypeInfo_k for uint). Single-character suffixes follow D's type +// encoding: a=char, b=bool, d=double, f=float, g=byte, h=ubyte, +// i=int, k=uint, l=long, m=ulong, o=dchar, s=short, t=wchar, +// u=ushort, v=void, x=const, y=immutable. +// ────────────────────────────────────────────────────────────────────── + +class TypeInfo_a : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return char.sizeof; } } +class TypeInfo_b : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return bool.sizeof; } } +class TypeInfo_d : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return double.sizeof; } } +class TypeInfo_f : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return float.sizeof; } } +class TypeInfo_g : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return byte.sizeof; } } +class TypeInfo_h : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return ubyte.sizeof; } } +class TypeInfo_i : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return int.sizeof; } } +class TypeInfo_k : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return uint.sizeof; } } +class TypeInfo_l : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return long.sizeof; } } +class TypeInfo_m : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return ulong.sizeof; } } +class TypeInfo_o : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return dchar.sizeof; } } +class TypeInfo_s : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return short.sizeof; } } +class TypeInfo_t : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return wchar.sizeof; } } +class TypeInfo_u : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return ushort.sizeof; } } +class TypeInfo_v : TypeInfo { override @property size_t tsize() nothrow pure const @safe @nogc { return 0; } } + +// Helper for TypeInfo_Struct delegate dispatch. +// Uses a union to overlay raw delegate ABI (ptr + funcptr) with typed delegate fields. +private struct _member_func +{ + union + { + struct + { + const void* ptr; + const void* funcptr; + } + + @safe pure nothrow + { + bool delegate(in void*) @nogc xopEquals; + int delegate(in void*) @nogc xopCmp; + } + } +} + +// ────────────────────────────────────────────────────────────────────── +// __ArrayDtor — compiler lowers dynamic array destruction to this +// ────────────────────────────────────────────────────────────────────── + +void __ArrayDtor(T)(scope T[] a) +{ + foreach_reverse (ref T e; a) + e.__xdtor(); +} + +// ────────────────────────────────────────────────────────────────────── +// Compiler hook templates — array & AA literal lowering +// These are only called at runtime; CTFE evaluates literals directly. +// ────────────────────────────────────────────────────────────────────── + +void* _d_arrayliteralTX(T)(size_t length) @trusted pure nothrow +{ + assert(false, "Array literals require druntime"); +} + +alias AssociativeArray(Key, Value) = Value[Key]; + +public import urt.internal.aa : _d_aaIn, _d_aaDel, _d_aaNew, _d_aaEqual, _d_assocarrayliteralTX; +public import urt.internal.aa : _d_aaLen, _d_aaGetY, _d_aaGetRvalueX, _d_aaApply, _d_aaApply2; + +private import urt.internal.aa : makeAA; + +// Lower an AA to a newaa struct for static initialization. +auto _aaAsStruct(K, V)(V[K] aa) @safe +{ + assert(__ctfe); + return makeAA!(K, V)(aa); +} + +Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted +{ + assert(false, "Array concatenation requires druntime"); +} + + +T _d_newclassT(T)() @trusted + if (is(T == class)) +{ + assert(false, "new class requires druntime"); +/+ + if (__ctfe) + assert(false, "new class not supported at CTFE without druntime"); + + import urt.mem : malloc; + import urt.mem : memcpy; + + enum sz = __traits(classInstanceSize, T); + auto p = malloc(sz); + if (p is null) + assert(false, "out of memory in _d_newclassT"); + + auto initSym = __traits(initSymbol, T); + memcpy(p, initSym.ptr, sz); + return cast(T)cast(void*)p; ++/ +} + +ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @trusted +{ + assert(false, "Array append requires druntime"); +} + +ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @trusted +{ + assert(false, "Array append requires druntime"); +} + +// ────────────────────────────────────────────────────────────────────── +// Compiler hook templates — array operations, construction, etc. +// These are lowered by the compiler for various language constructs. +// ────────────────────────────────────────────────────────────────────── + +T[] _d_newarrayT(T)(size_t length, bool isShared = false) @trusted +{ + assert(false, "new array requires druntime"); +} + +Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool isShared = false) @trusted +{ + assert(false, "new multi-dim array requires druntime"); +} + +T* _d_newitemT(T)() @trusted +{ + assert(false, "new item requires druntime"); +} + +T _d_newThrowable(T)() @trusted + if (is(T : Throwable)) +{ + assert(false, "new throwable requires druntime"); +} + +int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted +{ + immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; + foreach (i; 0 .. len) + { + if (lhs.ptr[i] < rhs.ptr[i]) + return -1; + if (lhs.ptr[i] > rhs.ptr[i]) + return 1; + } + return (lhs.length > rhs.length) - (lhs.length < rhs.length); +} + +bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) +nothrow @nogc pure @trusted +if (__traits(isScalar, T1) && __traits(isScalar, T2)) +{ + if (lhs.length != rhs.length) + return false; + static if (T1.sizeof == T2.sizeof + && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2)) + && !__traits(isFloating, T1) && !__traits(isFloating, T2)) + { + if (__ctfe) + { + foreach (i; 0 .. lhs.length) + if (lhs.ptr[i] != rhs.ptr[i]) return false; + return true; + } + else + { + import urt.mem : memcmp; + return !lhs.length || 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, lhs.length * T1.sizeof); + } + } + else + { + foreach (i; 0 .. lhs.length) + if (lhs.ptr[i] != rhs.ptr[i]) return false; + return true; + } +} + +bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) +if (!__traits(isScalar, T1) || !__traits(isScalar, T2)) +{ + if (lhs.length != rhs.length) + return false; + foreach (i; 0 .. lhs.length) + if (lhs[i] != rhs[i]) return false; + return true; +} + +TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted +{ + const fromSize = from.length * TFrom.sizeof; + if (fromSize % TTo.sizeof != 0) + assert(false, "Array cast misalignment"); + return (cast(TTo*) from.ptr)[0 .. fromSize / TTo.sizeof]; +} + +Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +{ + assert(false, "Array ctor requires druntime"); +} + +void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted +{ + assert(false, "Array set-ctor requires druntime"); +} + +Tarr _d_arrayassign_l(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +{ + assert(false, "Array assign requires druntime"); +} + +Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted +{ + assert(false, "Array assign requires druntime"); +} + +void _d_arraysetassign(Tarr : T[], T)(return scope Tarr to, scope ref T value) @trusted +{ + assert(false, "Array set-assign requires druntime"); +} + +size_t _d_arraysetlengthT(Tarr : T[], T)(return ref scope Tarr arr, size_t newlength) @trusted +{ + assert(false, "Array setlength requires druntime"); +} + +// LDC wraps the above in a template namespace +template _d_arraysetlengthTImpl(Tarr : T[], T) +{ + size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted pure nothrow + { + assert(false, "Array setlength requires druntime"); + } +} + +string _d_assert_fail(A...)(const scope string comp, auto ref const scope A a) +{ + return "assertion failure"; +} + +// ────────────────────────────────────────────────────────────────────── +// Runtime hooks: assertions, array bounds checks +// These are referenced by compiler-generated code even in debug builds. +// ────────────────────────────────────────────────────────────────────── + +extern (C) void _d_assert_msg(string msg, string file, uint line) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, msg); +} + +extern (C) void _d_assertp(immutable(char)* file, uint line) nothrow @nogc @trusted +{ + import urt.mem : strlen; + import urt.exception : assert_handler; + auto f = file[0 .. file ? strlen(file) : 0]; + assert_handler()(f, line, null); +} + +extern (C) void _d_assert(string file, uint line) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, null); +} + +extern (C) void _d_arraybounds_indexp(string file, uint line, size_t index, size_t length) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, "array index out of bounds"); +} + +extern (C) void _d_arraybounds_slicep(string file, uint line, size_t lower, size_t upper, size_t length) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, "array slice out of bounds"); +} + +extern (C) void _d_arrayboundsp(string file, uint line) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, "array index out of bounds"); +} + +extern (C) void _d_arraybounds(string file, uint line) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, "array index out of bounds"); +} + +// Unittest assert hooks — the compiler generates these for assert() inside +// unittest blocks instead of the regular _d_assertp/_d_assert_msg. +extern (C) void _d_unittestp(immutable(char)* file, uint line) nothrow @nogc @trusted +{ + import urt.mem : strlen; + import urt.exception : assert_handler; + auto f = file[0 .. file ? strlen(file) : 0]; + assert_handler()(f, line, "unittest assertion failure"); +} + +extern (C) void _d_unittest_msg(string msg, string file, uint line) nothrow @nogc @trusted +{ + import urt.exception : assert_handler; + assert_handler()(file, line, msg); +} + +extern (C) void _d_unittest(string file, uint line) nothrow @nogc +{ + import urt.exception : assert_handler; + assert_handler()(file, line, "unittest assertion failure"); +} + +// GC allocation hook — compiler lowers `new` to this. In our @nogc world +// it should never be called from production code; provided so unittest +// blocks that accidentally use `new` can at least link. +extern (C) void* _d_allocmemory(size_t sz) nothrow @nogc @trusted +{ + import urt.mem : malloc; + return malloc(sz); +} + +// ────────────────────────────────────────────────────────────────────── +// LDC extern(C) runtime hooks +// LDC's codegen emits calls to old-style extern(C) functions for many +// operations where DMD uses template-based hooks. These stubs satisfy +// the linker. Implementations are provided where feasible; others +// assert(false) and must be fleshed out if the code path is hit. +// ────────────────────────────────────────────────────────────────────── + +version (LDC) +{ + +// --- Bounds checks (LDC variants without 'p' suffix) ------------------- + +extern (C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length) nothrow @nogc +{ + import urt.exception : assert_handler; + if (auto handler = assert_handler) + handler(file, line, "array index out of bounds"); + else + _halt(); +} + +extern (C) void _d_arraybounds_slice(string file, uint line, size_t lower, size_t upper, size_t length) nothrow @nogc +{ + import urt.exception : assert_handler; + if (auto handler = assert_handler) + handler(file, line, "array slice out of bounds"); + else + _halt(); +} + +// --- Array slice copy (LDC emits this for non-elaborate arr[] = other[]) - +// Bounds-checked memcpy: verifies dstlen == srclen, then copies raw bytes. + +extern (C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsize) nothrow @nogc @trusted +{ + assert(dstlen == srclen, "array slice lengths don't match for copy"); + import urt.mem : memcpy; + memcpy(dst, src, dstlen * elemsize); +} + +// --- Class allocation and casting (old-style extern C) ----------------- + +extern (C) void* _d_allocclass(TypeInfo_Class ci) nothrow @nogc @trusted +{ + import urt.mem : malloc, memcpy; + auto init = ci.initializer; + auto p = malloc(init.length); + if (p !is null) + memcpy(p, init.ptr, init.length); + return p; +} + +extern (C) Object _d_dynamic_cast(Object o, TypeInfo_Class c) nothrow @nogc @trusted +{ + // Traverse classinfo chain to check if o is-a c + if (o is null) return null; + auto oc = typeid(o); + while (oc !is null) + { + if (oc is c) + return o; + oc = oc.base; + } + return null; +} + +extern (C) void* _d_interface_cast(void* p, TypeInfo_Class c) nothrow @nogc @trusted +{ + // TODO: full interface casting requires traversing the Interface[] table + // in the target object's classinfo to find the right vtable offset. + // For now, return null (cast fails). + return null; +} + +// --- Struct array equality (old-style) --------------------------------- + +extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) nothrow @nogc @trusted +{ + if (a1.length != a2.length) return 0; + import urt.mem : memcmp; + return memcmp(a1.ptr, a2.ptr, a1.length) == 0 ? 1 : 0; +} + +} // version (LDC) + +// --- Old-style array allocation (extern C) ------------------------------ + +extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) nothrow @nogc @trusted +{ + import urt.mem : calloc; + auto elemsize = ti.next ? ti.next.tsize : 1; + auto p = calloc(length, elemsize); + return p[0 .. length * elemsize]; +} + +extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) nothrow @nogc @trusted +{ + import urt.mem : calloc; + auto elemsize = ti.next ? ti.next.tsize : 1; + auto p = calloc(length, elemsize); + return p[0 .. length * elemsize]; +} + +extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) nothrow @nogc @trusted +{ + import urt.mem : malloc; + auto elemsize = ti.next ? ti.next.tsize : 1; + auto sz = length * elemsize; + auto p = malloc(sz); + if (p is null) return null; + return p[0 .. sz]; +} + +// Unconditional halt — avoids circular dependency with assert. +private void _halt() nothrow @nogc @trusted +{ + version (D_InlineAsm_X86_64) + asm nothrow @nogc { hlt; } + else version (D_InlineAsm_X86) + asm nothrow @nogc { hlt; } + else + *(cast(int*) null) = 0; // fallback: null deref +} + +void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow + if (is(S == struct)) +{ + static if (__traits(hasMember, S, "__xpostblit")) + newLocation.__xpostblit(); +} + +void __ArrayPostblit(T)(T[] a) +{ + foreach (ref e; a) + static if (__traits(hasMember, T, "__xpostblit")) + e.__xpostblit(); +} + +int __switch(T, caseLabels...)(const scope T[] condition) pure nothrow @safe @nogc +{ + foreach (i, s; caseLabels) + { + static if (is(typeof(s) : typeof(condition))) + { + if (condition == s) + return cast(int) i; + } + } + return -1; +} + +void __switch_error()(string file = __FILE__, size_t line = __LINE__) +{ + assert(false, "Final switch error"); +} + +template _d_delstructImpl(T) +{ + void _d_delstruct(ref T p) @trusted + { + p = null; + } +} + +nothrow @nogc @trusted pure extern (C) void _d_delThrowable(scope Throwable) {} + +// ────────────────────────────────────────────────────────────────────── +// _arrayOp — compiler hook for vectorized array slice operations. +// DMD lowers `dest[] = a[] ^ b[]` to `_arrayOp!(T[], T[], T[], "^", "=")(dest, a, b)`. +// Args are in Reverse Polish Notation (RPN). +// ────────────────────────────────────────────────────────────────────── + +template _arrayOp(Args...) +{ + static if (is(Args[0] == E[], E)) + { + alias T = E; + + T[] _arrayOp(T[] res, _Filter!(_is_type, Args[1 .. $]) args) nothrow @nogc @trusted + { + foreach (pos; 0 .. res.length) + mixin(_scalar_exp!(Args[1 .. $]) ~ ";"); + return res; + } + } +} + +// ---- _arrayOp helpers (all private, CTFE-only) ---- + +private template _Filter(alias pred, args...) +{ + static if (args.length == 0) + alias _Filter = AliasSeq!(); + else static if (pred!(args[0])) + alias _Filter = AliasSeq!(args[0], _Filter!(pred, args[1 .. $])); + else + alias _Filter = _Filter!(pred, args[1 .. $]); +} + +private enum _is_type(T) = true; +private enum _is_type(alias a) = false; + +private bool _is_unary_op(string op) pure nothrow @safe @nogc + => op.length > 0 && op[0] == 'u'; + +private bool _is_binary_op(string op) pure nothrow @safe @nogc +{ + if (op == "^^") return true; + if (op.length != 1) return false; + switch (op[0]) + { + case '+', '-', '*', '/', '%', '|', '&', '^': + return true; + default: + return false; + } +} + +private bool _is_binary_assign_op(string op) + => op.length >= 2 && op[$ - 1] == '=' && _is_binary_op(op[0 .. $ - 1]); + +// Convert size_t to string at CTFE. +private string _size_to_str(size_t num) pure @safe +{ + enum digits = "0123456789"; + if (num < 10) return digits[num .. num + 1]; + return _size_to_str(num / 10) ~ digits[num % 10 .. num % 10 + 1]; +} + +// Generate element-wise mixin expression from RPN args (CTFE-evaluated enum). +// Uses a fixed-size stack with depth counter to avoid dynamic array operations +// (which would require _d_arraysetlengthT at semantic analysis time). +private enum _scalar_exp(Args...) = () { + string[Args.length] stack; + size_t depth; + size_t args_idx; + + static if (is(Args[0] == U[], U)) + alias Type = U; + else + alias Type = Args[0]; + + foreach (i, arg; Args) + { + static if (is(arg == E[], E)) + { + stack[depth] = "args[" ~ _size_to_str(args_idx++) ~ "][pos]"; + ++depth; + } + else static if (is(arg)) + { + stack[depth] = "args[" ~ _size_to_str(args_idx++) ~ "]"; + ++depth; + } + else static if (_is_unary_op(arg)) + { + auto op = arg[0] == 'u' ? arg[1 .. $] : arg; + static if (is(Type : int)) + stack[depth - 1] = "cast(typeof(" ~ stack[depth - 1] ~ "))" ~ op ~ "cast(int)(" ~ stack[depth - 1] ~ ")"; + else + stack[depth - 1] = op ~ stack[depth - 1]; + } + else static if (arg == "=") + { + stack[depth - 1] = "res[pos] = cast(T)(" ~ stack[depth - 1] ~ ")"; + } + else static if (_is_binary_assign_op(arg)) + { + stack[depth - 1] = "res[pos] " ~ arg ~ " cast(T)(" ~ stack[depth - 1] ~ ")"; + } + else static if (_is_binary_op(arg)) + { + stack[depth - 2] = "(" ~ stack[depth - 2] ~ " " ~ arg ~ " " ~ stack[depth - 1] ~ ")"; + --depth; + } + } + return stack[0]; +}(); + +// ────────────────────────────────────────────────────────────────────── +// _d_cast — dynamic class cast, walks TypeInfo_Class.base chain +// ────────────────────────────────────────────────────────────────────── + +void* _d_cast(To, From)(From o) @trusted + if (is(From == class) && is(To == class)) +{ + if (o is null) return null; + auto ci = typeid(o); + auto target = typeid(To); + do + { + if (ci is target) return cast(void*) o; + ci = ci.base; + } + while (ci !is null); + return null; +} + +void* _d_cast(To, From)(From o) @trusted + if (is(From == class) && is(To == interface)) +{ + if (o is null) return null; + // Walk interface list — not implemented yet, fall back to null + assert(false, "Interface cast not yet implemented"); +} + +// ────────────────────────────────────────────────────────────────────── +// Throwable / Exception / Error — exception hierarchy +// ────────────────────────────────────────────────────────────────────── + +class Throwable : Object +{ + string msg; + Throwable next; + string file; + size_t line; + + private uint _refcount; + + @nogc @safe pure nothrow this(string msg, Throwable next = null) + { + this.msg = msg; + this.next = next; + } + + @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null) + { + this.msg = msg; + this.file = file; + this.line = line; + this.next = next; + } + + const(char)[] message() const @nogc @safe pure nothrow + => msg; + + @system @nogc final pure nothrow ref uint refcount() return + => _refcount; + + static Throwable chainTogether(return scope Throwable e1, return scope Throwable e2) nothrow @nogc + { + if (!e1) + return e2; + if (!e2) + return e1; + for (auto e = e1; ; e = e.next) + { + if (!e.next) + { + e.next = e2; + break; + } + } + return e1; + } +} + +class Exception : Throwable +{ + @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + { + super(msg, file, line, next); + } +} + +class Error : Throwable +{ + Throwable bypassedException; + + @nogc @safe pure nothrow this(string msg, Throwable next = null) + { + super(msg, next); + } + + @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null) + { + super(msg, file, line, next); + } +} + +// ────────────────────────────────────────────────────────────────────── +// destroy — compiler generates calls for scope guards, etc. +// ────────────────────────────────────────────────────────────────────── + +void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct)) +{ + destruct_recurse(obj); + static if (initialize) + { + static if (__traits(isZeroInit, T)) + (cast(ubyte*)&obj)[0 .. T.sizeof] = 0; + else + { + auto init = __traits(initSymbol, T); + (cast(ubyte*)&obj)[0 .. T.sizeof] = (cast(const ubyte*)init.ptr)[0 .. T.sizeof]; + } + } +} + +// Destruct a struct by calling its __xdtor, but only if it truly belongs +// to this type (Bugzilla 14746). +void destruct_recurse(S)(ref S s) if (is(S == struct)) +{ + static if (__traits(hasMember, S, "__xdtor") && __traits(isSame, S, __traits(parent, s.__xdtor))) + s.__xdtor(); +} + +void destruct_recurse(E, size_t n)(ref E[n] arr) +{ + foreach_reverse (ref elem; arr) + destruct_recurse(elem); +} + +void destroy(bool initialize = true, T)(T obj) if (is(T == class)) +{ + static if (__traits(hasMember, T, "__xdtor")) + obj.__xdtor(); +} + +void destroy(bool initialize = true, T)(ref T obj) if (!is(T == struct) && !is(T == class)) +{ + static if (initialize) + obj = T.init; +} + +// ────────────────────────────────────────────────────────────────────── +// .dup / .idup — array duplication properties +// ────────────────────────────────────────────────────────────────────── + +@property immutable(T)[] idup(T)(T[] a) @trusted +{ + // CTFE-compatible: the interpreter handles ~= natively. + // At runtime this would assert via _d_arrayappendcTX. + immutable(T)[] r; + foreach (ref e; a) + r ~= cast(immutable(T)) e; + return r; +} + +@property T[] dup(T)(const(T)[] a) @trusted +{ + T[] r; + foreach (ref e; a) + r ~= cast(T) e; + return r; +} + +// ────────────────────────────────────────────────────────────────────── +// Compiler-generated struct equality/comparison fallbacks +// ────────────────────────────────────────────────────────────────────── + +bool _xopEquals(in void*, in void*) + => false; + +bool _xopCmp(in void*, in void*) + => false; + +// ────────────────────────────────────────────────────────────────────── +// hashOf — used by AAs and anywhere .toHash is needed +// ────────────────────────────────────────────────────────────────────── + +size_t hashOf(T)(auto ref T val, size_t seed = 0) @trusted pure nothrow @nogc +{ + static if (is(T : const(char)[])) + { + // FNV-1a for strings + size_t h = seed == 0 ? 2166136261 : seed; + foreach (c; cast(const(ubyte)[]) val) + { + h ^= c; + h *= 16777619; + } + return h; + } + else static if (is(T V : V*)) + { + // Pointers — CTFE compatible + if (__ctfe) + { + if (val is null) return seed; + assert(0, "Unable to hash non-null pointer at compile time"); + } + size_t v = cast(size_t) val; + return _fnv(v ^ (v >> 4), seed); + } + else static if (__traits(isIntegral, T)) + { + // Integers — CTFE compatible, no reinterpreting cast + static if (T.sizeof <= size_t.sizeof) + return _fnv(cast(size_t) val, seed); + else + return _fnv(cast(size_t)(val ^ (val >>> (size_t.sizeof * 8))), seed); + } + else static if (__traits(isFloating, T)) + { + // At CTFE we cannot reinterpret float bits; use lossy integer cast + if (__ctfe) + return _fnv(cast(size_t) cast(long) val, seed); + // Runtime: walk the bytes + auto p = cast(const ubyte*)&val; + size_t h = seed == 0 ? 2166136261 : seed; + foreach (i; 0 .. T.sizeof) + { + h ^= p[i]; + h *= 16777619; + } + return h; + } + else static if (is(T == struct)) + { + // Structs — hash each field (CTFE compatible) + size_t h = seed; + foreach (ref field; val.tupleof) + h = hashOf(field, h); + return h; + } + else static if (is(T == enum)) + { + import urt.internal.traits : Unconst; + static if (is(T EType == enum)) + return hashOf(cast(EType) val, seed); + else + return _fnv(0, seed); + } + else + { + return seed; + } +} + +private size_t _fnv(size_t val, size_t seed) nothrow @nogc pure @safe +{ + size_t h = seed == 0 ? 2166136261 : seed; + foreach (i; 0 .. size_t.sizeof) + { + h ^= val & 0xFF; + h *= 16777619; + val >>= 8; + } + return h; +} + +// ────────────────────────────────────────────────────────────────────── +// ModuleInfo — compiler emits one per module with ctor/dtor/unittest info. +// Variable-sized: fields are packed after the header based on flag bits. +// ────────────────────────────────────────────────────────────────────── + +enum +{ + MIctorstart = 0x1, + MIctordone = 0x2, + MIstandalone = 0x4, + MItlsctor = 0x8, + MItlsdtor = 0x10, + MIctor = 0x20, + MIdtor = 0x40, + MIxgetMembers = 0x80, + MIictor = 0x100, + MIunitTest = 0x200, + MIimportedModules = 0x400, + MIlocalClasses = 0x800, + MIname = 0x1000, +} + +struct ModuleInfo +{ + uint _flags; + uint _index; + +const: + private void* addr_of(int flag) return nothrow pure @nogc @trusted + { + void* p = cast(void*)&this + ModuleInfo.sizeof; + + if (flags & MItlsctor) + { + if (flag == MItlsctor) + return p; + p += (void function()).sizeof; + } + if (flags & MItlsdtor) + { + if (flag == MItlsdtor) + return p; + p += (void function()).sizeof; + } + if (flags & MIctor) + { + if (flag == MIctor) + return p; + p += (void function()).sizeof; + } + if (flags & MIdtor) + { + if (flag == MIdtor) + return p; + p += (void function()).sizeof; + } + if (flags & MIxgetMembers) + { + if (flag == MIxgetMembers) + return p; + p += (void*).sizeof; + } + if (flags & MIictor) + { + if (flag == MIictor) + return p; + p += (void function()).sizeof; + } + if (flags & MIunitTest) + { + if (flag == MIunitTest) + return p; + p += (void function()).sizeof; + } + if (flags & MIimportedModules) + { + if (flag == MIimportedModules) + return p; + p += size_t.sizeof + *cast(size_t*)p * (immutable(ModuleInfo)*).sizeof; + } + if (flags & MIlocalClasses) + { + if (flag == MIlocalClasses) + return p; + p += size_t.sizeof + *cast(size_t*)p * (TypeInfo_Class).sizeof; + } + if (true || flags & MIname) + { + if (flag == MIname) + return p; + } + assert(0); + } + + @property uint flags() nothrow pure @nogc + => _flags; + + @property void function() tlsctor() nothrow pure @nogc @trusted + => flags & MItlsctor ? *cast(typeof(return)*)addr_of(MItlsctor) : null; + + @property void function() tlsdtor() nothrow pure @nogc @trusted + => flags & MItlsdtor ? *cast(typeof(return)*)addr_of(MItlsdtor) : null; + + @property void function() ctor() nothrow pure @nogc @trusted + => flags & MIctor ? *cast(typeof(return)*)addr_of(MIctor) : null; + + @property void function() dtor() nothrow pure @nogc @trusted + => flags & MIdtor ? *cast(typeof(return)*)addr_of(MIdtor) : null; + + @property void function() ictor() nothrow pure @nogc @trusted + => flags & MIictor ? *cast(typeof(return)*)addr_of(MIictor) : null; + + @property void function() unitTest() nothrow pure @nogc @trusted + => flags & MIunitTest ? *cast(typeof(return)*)addr_of(MIunitTest) : null; + + @property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc @trusted + { + if (flags & MIimportedModules) + { + auto p = cast(size_t*)addr_of(MIimportedModules); + return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; + } + return null; + } + + @property string name() nothrow @nogc @trusted + { + if (flags & MIname) + { + auto p = cast(immutable char*)addr_of(MIname); + size_t len = 0; + while (p[len] != 0) ++len; + return p[0 .. len]; + } + return null; + } +} + +// ────────────────────────────────────────────────────────────────────── +// _d_dso_registry — ELF shared-object module registry +// +// On ELF targets the compiler generates .init_array/.fini_array entries +// that call _d_dso_registry with pointers to the module-info section. +// We stash the module range here; uRT's C main() handles constructor +// execution and unittest running via get_module_infos(). +// ────────────────────────────────────────────────────────────────────── + +version (linux) +{ + struct CompilerDSOData + { + size_t _version; + void** _slot; + immutable(ModuleInfo*)* _minfo_beg, _minfo_end; + } + + extern(C) __gshared immutable(ModuleInfo*)* _elf_minfo_beg; + extern(C) __gshared immutable(ModuleInfo*)* _elf_minfo_end; + + extern(C) void _d_dso_registry(CompilerDSOData* data) nothrow @nogc + { + if (data._version < 1) + return; + + if (*data._slot is null) + { + *data._slot = cast(void*)data; + _elf_minfo_beg = data._minfo_beg; + _elf_minfo_end = data._minfo_end; + } + else + { + *data._slot = null; + _elf_minfo_beg = null; + _elf_minfo_end = null; + } + } +} + +// ────────────────────────────────────────────────────────────────────── +// TypeInfo for const/immutable char[] — compiler references by name +// ────────────────────────────────────────────────────────────────────── + +class TypeInfo_Axa : TypeInfo_Array {} // const(char)[] +class TypeInfo_Aya : TypeInfo_Array {} // immutable(char)[] = string + +// ────────────────────────────────────────────────────────────────────── +// _d_invariant — contract invariant hook (matches rt.invariant_ mangling) +// ────────────────────────────────────────────────────────────────────── + +pragma(mangle, "_D2rt10invariant_12_d_invariantFC6ObjectZv") +void _d_invariant_impl(Object o) nothrow @nogc +{ + assert(o !is null); + auto c = typeid(o); + do + { + if (c.classInvariant) + c.classInvariant(o); + c = c.base; + } + while (c); +} + +// ────────────────────────────────────────────────────────────────────── +// Compiler-generated memset intrinsics (struct initialization) +// ────────────────────────────────────────────────────────────────────── + +private struct Bits128 { ulong[2] v; } +private struct Bits80 { ubyte[real.sizeof] v; } +private struct Bits160 { ubyte[2 * real.sizeof] v; } + +extern (C) nothrow @nogc @trusted +{ + short* _memset16(short* p, short value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + int* _memset32(int* p, int value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + long* _memset64(long* p, long value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + Bits80* _memset80(Bits80* p, Bits80 value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + Bits128* _memset128(Bits128* p, Bits128 value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + void[]* _memset128ii(void[]* p, void[] value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + Bits160* _memset160(Bits160* p, Bits160 value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + float* _memsetFloat(float* p, float value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + double* _memsetDouble(double* p, double value, size_t count) + { + foreach (i; 0 .. count) + p[i] = value; + return p; + } + + void* _memsetn(void* p, void* value, int count, size_t sizelem) + { + auto dst = cast(ubyte*) p; + auto src = cast(ubyte*) value; + foreach (_; 0 .. count) + { + foreach (j; 0 .. sizelem) + dst[j] = src[j]; + dst += sizelem; + } + return p; + } +} + +private ptrdiff_t try_copy_string(char[] buffer, const(char)[] src) pure nothrow @nogc +{ + if (buffer.ptr) + { + if (buffer.length < src.length) + return -1; + buffer[0 .. src.length] = src[]; + } + return src.length; +} diff --git a/src/std/math.d b/src/std/math.d new file mode 100644 index 0000000..e6a1a4a --- /dev/null +++ b/src/std/math.d @@ -0,0 +1,8 @@ +/// Minimal std.math stub — provides just the `pow` function that DMD's +/// `^^` operator lowering requires. Nothing else from Phobos is pulled in. +module std.math; + +// DMD lowers `a ^^ b` to `std.math.pow(a, b)`. +public import urt.math : pow; + +// TODO: do we need sqrt for `^^ 0.5`? diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 14ecc1f..d44fec3 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -1,6 +1,7 @@ module urt.algorithm; -import urt.traits : lvalueOf; +import urt.meta : AliasSeq; +import urt.traits : is_some_function, lvalue_of, Parameters, ReturnType, Unqual; import urt.util : swap; version = SmallSize; @@ -10,36 +11,44 @@ nothrow @nogc: auto compare(T, U)(auto ref T a, auto ref U b) { - static if (__traits(compiles, lvalueOf!T.opCmp(lvalueOf!U))) + static if (__traits(compiles, a.opCmp(b))) return a.opCmp(b); - else static if (__traits(compiles, lvalueOf!U.opCmp(lvalueOf!T))) + else static if (__traits(compiles, b.opCmp(a))) return -b.opCmp(a); - else static if (is(T : A[], A)) + else static if (is(T : A[], A) || is(U : B[], B)) { - import urt.traits : isPrimitive; + import urt.traits : is_primitive; + + static assert(is(T : A[], A) && is(U : B[], B), "TODO: compare an array with a not-array?"); auto ai = a.ptr; auto bi = b.ptr; - size_t len = a.length < b.length ? a.length : b.length; - static if (isPrimitive!A) + + // first compere the pointers... + if (ai !is bi) { - // compare strings - foreach (i; 0 .. len) + size_t len = a.length < b.length ? a.length : b.length; + static if (is_primitive!A) { - if (ai[i] != bi[i]) - return ai[i] < bi[i] ? -1 : 1; + // compare strings + foreach (i; 0 .. len) + { + if (ai[i] != bi[i]) + return ai[i] < bi[i] ? -1 : 1; + } } - } - else - { - // compare arrays - foreach (i; 0 .. len) + else { - auto cmp = compare(ai[i], bi[i]); - if (cmp != 0) - return cmp; + // compare arrays + foreach (i; 0 .. len) + { + if (auto cmp = compare(ai[i], bi[i])) + return cmp; + } } } + + // finally, compare the lengths if (a.length == b.length) return 0; return a.length < b.length ? -1 : 1; @@ -48,8 +57,39 @@ auto compare(T, U)(auto ref T a, auto ref U b) return a < b ? -1 : (a > b ? 1 : 0); } -size_t binarySearch(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs) +size_t binary_search(Pred)(const void[] arr, size_t stride, const void* value, auto ref Pred pred) + if (is_some_function!Pred && is(ReturnType!Pred == int) && is(Parameters!Pred == AliasSeq!(const void*, const void*))) { + debug assert(arr.length % stride == 0, "array length must be a multiple of stride"); + const count = arr.length / stride; + + const void* p = arr.ptr; + size_t low = 0; + size_t high = count; + while (low < high) + { + size_t mid = low + (high - low) / 2; + + // should we chase the first in a sequence of same values? + int cmp = pred(p + mid*stride, value); + if (cmp < 0) + low = mid + 1; + else + high = mid; + } + if (low == count) + return count; + if (pred(p + low*stride, value) == 0) + return low; + return count; +} + +size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_args) + if (!is(Unqual!T == void)) +{ + static if (is(pred == void)) + static assert (cmp_args.length == 1, "binary_search without a predicate requires exactly one comparison argument"); + T* p = arr.ptr; size_t low = 0; size_t high = arr.length; @@ -59,7 +99,7 @@ size_t binarySearch(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs) static if (is(pred == void)) { // should we chase the first in a sequence of same values? - if (p[mid] < cmpArgs[0]) + if (p[mid] < cmp_args[0]) low = mid + 1; else high = mid; @@ -67,82 +107,97 @@ size_t binarySearch(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs) else { // should we chase the first in a sequence of same values? - int cmp = pred(p[mid], cmpArgs); + int cmp = pred(p[mid], cmp_args); if (cmp < 0) low = mid + 1; else high = mid; } } + if (low == arr.length) + return arr.length; static if (is(pred == void)) { - if (p[low] == cmpArgs[0]) + if (p[low] == cmp_args[0]) return low; } else { - if (pred(p[low], cmpArgs) == 0) + if (pred(p[low], cmp_args) == 0) return low; } return arr.length; } -void qsort(alias pred = void, T)(T[] arr) +void qsort(alias pred = void, T)(T[] arr) pure { + if (arr.length <= 1) + return; + version (SmallSize) + enum use_small_size_impl = true; + else + enum use_small_size_impl = false; + + if (!__ctfe && use_small_size_impl) { static if (is(pred == void)) - static if (__traits(compiles, lvalueOf!T.opCmp(lvalueOf!T))) - static int compare(const void* a, const void* b) nothrow @nogc + static if (__traits(compiles, lvalue_of!T.opCmp(lvalue_of!T))) + static int compare(const void* a, const void* b) pure nothrow @nogc => (*cast(const T*)a).opCmp(*cast(const T*)b); else - static int compare(const void* a, const void* b) nothrow @nogc + static int compare(const void* a, const void* b) pure nothrow @nogc => *cast(const T*)a < *cast(const T*)b ? -1 : *cast(const T*)a > *cast(const T*)b ? 1 : 0; else - static int compare(const void* a, const void* b) nothrow @nogc + static int compare(const void* a, const void* b) pure nothrow @nogc => pred(*cast(T*)a, *cast(T*)b); - qsort(arr[], T.sizeof, &compare, (void* a, void* b) nothrow @nogc { + qsort(arr[], T.sizeof, &compare, (void* a, void* b) pure nothrow @nogc { swap(*cast(T*)a, *cast(T*)b); }); } else { T* p = arr.ptr; - if (arr.length > 1) - { - size_t pivotIndex = arr.length / 2; - T* pivot = &p[pivotIndex]; + const n = cast(ptrdiff_t)arr.length; + + size_t pivotIndex = n / 2; + T* pivot = p + pivotIndex; - size_t i = 0; - size_t j = arr.length - 1; + size_t i = 0; + ptrdiff_t j = n - 1; - while (i <= j) + while (i <= j) + { + static if (is(pred == void)) { - static if (is(pred == void)) - { - while (p[i] < *pivot) i++; - while (p[j] > *pivot) j--; - } - else - { - while (pred(*cast(T*)&p[i], *cast(T*)pivot) < 0) i++; - while (pred(*cast(T*)&p[j], *cast(T*)pivot) > 0) j--; - } - if (i <= j) - { - swap(p[i], p[j]); - i++; - j--; - } + while (p[i] < *pivot) ++i; + while (p[j] > *pivot) --j; } + else + { + while (pred(p[i], *pivot) < 0) ++i; + while (pred(p[j], *pivot) > 0) --j; + } + if (i <= j) + { + // track pivot value across swaps + if (p + i == pivot) + pivot = p + j; + else if (p + j == pivot) + pivot = p + i; - if (j > 0) - qsort(p[0 .. j + 1]); - if (i < arr.length) - qsort(p[i .. arr.length]); + swap(p[i], p[j]); + ++i; + --j; + } } + + if (j >= 0) + qsort!pred(p[0 .. j + 1]); + if (i < n) + qsort!pred(p[i .. n]); } } @@ -151,7 +206,7 @@ unittest struct S { int x; - int opCmp(ref const S rh) const nothrow @nogc + int opCmp(ref const S rh) pure const nothrow @nogc => x < rh.x ? -1 : x > rh.x ? 1 : 0; } @@ -159,18 +214,18 @@ unittest qsort(arr); assert(arr == [-1, 3, 17, 30, 100]); - S[5] arr2 = [ S(3), S(100), S(-1), S(17), S(30)]; + S[5] arr2 = [ S(3), S(100), S(-1), S(17), S(30) ]; qsort(arr2); foreach (i, ref s; arr2) assert(s.x == arr[i]); // test binary search, not that they're sorted... - assert(binarySearch(arr, -1) == 0); - assert(binarySearch!(s => s.x < 30 ? -1 : s.x > 30 ? 1 : 0)(arr2) == 3); - assert(binarySearch(arr, 0) == arr.length); + assert(binary_search(arr, -1) == 0); + assert(binary_search!(s => s.x < 30 ? -1 : s.x > 30 ? 1 : 0)(arr2) == 3); + assert(binary_search(arr, 0) == arr.length); int[10] rep = [1, 10, 10, 10, 10, 10, 10, 10, 10, 100]; - assert(binarySearch(rep, 10) == 1); + assert(binary_search(rep, 10) == 1); } @@ -181,34 +236,36 @@ version (SmallSize) // just one generic implementation to minimise the code... // kinda slow though... look at all those multiplies! // maybe there's some way to make this faster :/ - void qsort(void[] arr, size_t elementSize, int function(const void* a, const void* b) nothrow @nogc compare, void function(void* a, void* b) nothrow @nogc swap) + void qsort(void[] arr, size_t element_size, int function(const void* a, const void* b) pure nothrow @nogc compare, void function(void* a, void* b) pure nothrow @nogc swap) pure { void* p = arr.ptr; - size_t length = arr.length / elementSize; - if (length > 1) - { - size_t pivotIndex = length / 2; - void* pivot = p + pivotIndex*elementSize; + size_t length = arr.length / element_size; + if (length <= 1) + return; + + size_t pivot_index = length / 2; + size_t last = length - 1; + swap(p + pivot_index*element_size, p + last*element_size); - size_t i = 0; - size_t j = length - 1; + void* pivot = p + last*element_size; - while (i <= j) + size_t partition = 0; + for (size_t k = 0; k < last; ++k) + { + void* elem = p + k*element_size; + if (compare(elem, pivot) < 0) { - while (compare(p + i*elementSize, pivot) < 0) i++; - while (compare(p + j*elementSize, pivot) > 0) j--; - if (i <= j) - { - swap(p + i*elementSize, p + j*elementSize); - i++; - j--; - } + if (k != partition) + swap(elem, p + partition*element_size); + ++partition; } - - if (j > 0) - qsort(p[0 .. (j + 1)*elementSize], elementSize, compare, swap); - if (i < length) - qsort(p[i*elementSize .. length*elementSize], elementSize, compare, swap); } + + swap(p + partition*element_size, p + last*element_size); + + if (partition > 1) + qsort(p[0 .. partition*element_size], element_size, compare, swap); + if (partition + 1 < length) + qsort(p[(partition + 1)*element_size .. length*element_size], element_size, compare, swap); } } diff --git a/src/urt/array.d b/src/urt/array.d index 8d9858c..51360f6 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -1,7 +1,7 @@ module urt.array; import urt.mem; - +import urt.traits : is_some_char, is_primitive, is_trivial; nothrow @nogc: @@ -79,22 +79,9 @@ ref inout(T)[N] takeBack(size_t N, T)(ref inout(T)[] arr) pure return t[0..N]; } -bool exists(T)(const(T)[] arr, auto ref const T el, size_t *pIndex = null) -{ - foreach (i, ref e; arr) - { - if (e.elCmp(el)) - { - if (pIndex) - *pIndex = i; - return true; - } - } - return false; -} - // TODO: I'd like it if these only had one arg (T) somehow... size_t findFirst(T, U)(const(T)[] arr, auto ref const U el) + if (!is_some_char!T) { size_t i = 0; while (i < arr.length && !arr[i].elCmp(el)) @@ -104,15 +91,17 @@ size_t findFirst(T, U)(const(T)[] arr, auto ref const U el) // TODO: I'd like it if these only had one arg (T) somehow... size_t findLast(T, U)(const(T)[] arr, auto ref const U el) + if (!is_some_char!T) { - ptrdiff_t last = length-1; + ptrdiff_t last = arr.length-1; while (last >= 0 && !arr[last].elCmp(el)) --last; - return last < 0 ? length : last; + return last < 0 ? arr.length : last; } // TODO: I'd like it if these only had one arg (T) somehow... size_t findFirst(T, U)(const(T)[] arr, U[] seq) + if (!is_some_char!T) { if (seq.length == 0) return 0; @@ -129,6 +118,7 @@ size_t findFirst(T, U)(const(T)[] arr, U[] seq) // TODO: I'd like it if these only had one arg (T) somehow... size_t findLast(T, U)(const(T)[] arr, U[] seq) + if (!is_some_char!T) { if (seq.length == 0) return arr.length; @@ -143,7 +133,8 @@ size_t findLast(T, U)(const(T)[] arr, U[] seq) return arr.length; } -size_t findFirst(T)(const(T)[] arr, bool delegate(auto ref const T) nothrow @nogc pred) +size_t findFirst(T)(const(T)[] arr, bool delegate(ref const T) nothrow @nogc pred) + if (!is_some_char!T) { size_t i = 0; while (i < arr.length && !pred(arr[i])) @@ -151,6 +142,39 @@ size_t findFirst(T)(const(T)[] arr, bool delegate(auto ref const T) nothrow @nog return i; } +bool contains(T, U)(const(T)[] arr, auto ref const U el, size_t *index = null) + if (!is_some_char!T) +{ + size_t i = findFirst(arr, el); + if (i == arr.length) + return false; + if (index) + *index = i; + return true; +} + +bool contains(T, U)(const(T)[] arr, U[] seq, size_t *index = null) + if (!is_some_char!T) +{ + size_t i = findFirst(arr, seq); + if (i == arr.length) + return false; + if (index) + *index = i; + return true; +} + +bool contains(T)(const(T)[] arr, bool delegate(ref const T) nothrow @nogc pred, size_t *index = null) + if (!is_some_char!T) +{ + size_t i = findFirst(arr, pred); + if (i == arr.length) + return false; + if (index) + *index = i; + return true; +} + ptrdiff_t indexOfElement(T, U)(const(T)[] arr, const(U)* el) { if (el < arr.ptr || el >= arr.ptr + arr.length) @@ -189,6 +213,10 @@ struct Array(T, size_t EmbedCount = 0) { static assert(EmbedCount == 0, "Not without move semantics!"); + alias This = typeof(this); + + T* ptr; + // constructors // TODO: DELETE POSTBLIT! @@ -201,7 +229,7 @@ struct Array(T, size_t EmbedCount = 0) this = t[]; } - this(ref typeof(this) val) + this(ref This val) { this(val[]); } @@ -273,6 +301,8 @@ nothrow @nogc: void opAssign(U)(U[] arr) if (is(U : T)) { + // TODO: WHAT IF arr IS A SLICE OF THIS?!!? + debug assert(arr.length <= uint.max); clear(); reserve(arr.length); @@ -288,7 +318,121 @@ nothrow @nogc: } // manipulation - ref Array!(T, EmbedCount) concat(Things...)(auto ref Things things); + static if (!is_some_char!T) + { + alias concat = append; // TODO: REMOVE THIS ALIAS, we phase out the old name... + + ref Array!(T, EmbedCount) append(Things...)(auto ref Things things) + { + size_t ext_len = 0; + static foreach (i; 0 .. things.length) + { + static if (is(Things[i] == U[], U) && is(U : T)) + ext_len += things[i].length; + else static if (is(Things[i] == U[N], U, size_t N) && is(U : T)) + ext_len += N; + else static if (is(Things[i] : T)) + ext_len += 1; + else + static assert(false, "Invalid type for concat"); + } + reserve(_length + ext_len); + static foreach (i; 0 .. things.length) + { + static if (is(Things[i] == V[], V) && is(V : T)) + { + static if (copy_elements) + { + foreach (ref el; things[i]) + ptr[_length++] = el; + } + else + { + foreach (ref el; things[i]) + emplace!T(&ptr[_length++], el); + } + } + else static if (is(Things[i] == V[M], V, size_t M) && is(V : T)) + { + static if (copy_elements) + { + foreach (ref el; things[i]) + ptr[_length++] = el; + } + else + { + foreach (ref el; things[i]) + emplace!T(&ptr[_length++], el); + } + } + else static if (is(Things[i] : T)) + { + static if (copy_elements) + ptr[_length++] = things[i]; + else + emplace!T(&ptr[_length++], forward!(things[i])); + } + } + return this; + } + } + else + { + // char arrays are really just a string buffer, and so we'll expand the capability of concat to match what `MutableString` accepts... + static assert(is(T == char), "TODO: wchar and dchar"); // needs buffer length counting helpers + + // TODO: string's have this function `concat` which clears the string first, and that's different than Array + // we need to tighten this up! + ref Array!(T, EmbedCount) concat(Things...)(auto ref Things things) + { + clear(); + append(forward!things); + return this; + } + + ref Array!(T, EmbedCount) append(Things...)(auto ref Things things) + { + import urt.string.format : _concat = concat; + + size_t ext_len = _concat(null, things).length; + reserve(_length + ext_len); + _concat(ptr[_length .. _length + ext_len], forward!things); + _length += ext_len; + return this; + } + + ref Array!(T, EmbedCount) append_format(Things...)(const(char)[] format, auto ref Things args) + { + import urt.string.format : _format = format; + + size_t ext_len = _format(null, format, args).length; + reserve(_length + ext_len); + _format(ptr[_length .. _length + ext_len], format, forward!args); + _length += ext_len; + return this; + } + } + + T[] extend(bool do_init = true)(size_t length) + { + assert(_length + length <= uint.max); + + size_t old_len = _length; + reserve(_length + length); + static if (do_init) + { + foreach (i; _length .. _length + length) + { + // TODO: replace with palcement new... + static if (copy_elements) + ptr[i] = T.init; + else + emplace!T(&ptr[i]); + } + } + _length += cast(uint)length; + return ptr[old_len .. _length]; + } bool empty() const => _length == 0; @@ -318,26 +462,20 @@ nothrow @nogc: { static if (is(T == class) || is(T == interface)) { - uint len = _length; - reserve(len + 1); - _length = len + 1; - for (uint i = len; i > 0; --i) + reserve(_length + 1); + for (uint i = _length++; i > 0; --i) ptr[i] = ptr[i-1]; - ptr[0] = item; - return ptr[0]; + return (ptr[0] = item); } else { - uint len = _length; - reserve(len + 1); - _length = len + 1; - for (uint i = len; i > 0; --i) + reserve(_length + 1); + for (uint i = _length++; i > 0; --i) { moveEmplace!T(ptr[i-1], ptr[i]); destroy!false(ptr[i-1]); } - emplace!T(&ptr[0], forward!item); - return ptr[0]; + return *emplace!T(&ptr[0], forward!item); } } @@ -351,45 +489,30 @@ nothrow @nogc: ref T pushBack(U)(auto ref U item) if (is(U : T)) { + reserve(_length + 1); static if (is(T == class) || is(T == interface)) - { - uint len = _length; - reserve(len + 1); - _length = len + 1; - ptr[len] = item; - return ptr[len]; - } + return (ptr[_length++] = item); else - { - uint len = _length; - reserve(len + 1); - _length = len + 1; - emplace!T(&ptr[len], forward!item); - return ptr[len]; - } + return *emplace!T(&ptr[_length++], forward!item); } ref T emplaceFront(Args...)(auto ref Args args) + if (!is(T == class) && !is(T == interface)) { - uint len = _length; - reserve(len + 1); - _length = len + 1; - for (uint i = len; i > 0; --i) + reserve(_length + 1); + for (uint i = _length++; i > 0; --i) { moveEmplace(ptr[i-1], ptr[i]); destroy!false(ptr[i-1]); } - emplace!T(&ptr[0], forward!args); - return ptr[0]; + retirn *emplace!T(&ptr[0], forward!args); } ref T emplaceBack(Args...)(auto ref Args args) + if (!is(T == class) && !is(T == interface)) { - uint len = _length; - reserve(len + 1); - _length = len + 1; - emplace!T(&ptr[len], forward!args); - return ptr[len]; + reserve(_length + 1); + return *emplace!T(&ptr[_length++], forward!args); } T popFront() @@ -414,7 +537,7 @@ nothrow @nogc: moveEmplace(ptr[i], ptr[i-1]); } destroy!false(ptr[--_length]); - return copy.move; + return copy; } } @@ -424,19 +547,15 @@ nothrow @nogc: static if (is(T == class) || is(T == interface)) { - uint last = _length-1; - T copy = ptr[last]; - ptr[last] = null; - _length = last; + T copy = ptr[--_length]; + ptr[_length] = null; return copy; } else { - uint last = _length-1; - T copy = ptr[last].move; - destroy!false(ptr[last]); - _length = last; - return copy.move; + T copy = ptr[--_length].move; + destroy!false(ptr[_length]); + return copy; } } @@ -466,8 +585,7 @@ nothrow @nogc: static if (is(T == class) || is(T == interface)) { - for (size_t j = i + 1; j < _length; ++j) - ptr[j-1] = ptr[j]; + ptr[i .. _length - 1] = ptr[i + 1 .. _length]; ptr[--_length] = null; } else @@ -475,13 +593,37 @@ nothrow @nogc: destroy!false(ptr[i]); for (size_t j = i + 1; j < _length; ++j) { - emplace!T(&ptr[j-1], ptr[j].move); + moveEmplace(ptr[j], ptr[j-1]); destroy!false(ptr[j]); } --_length; } } + void remove(size_t i, size_t count) + { + debug assert(i + count <= _length); + + static if (is(T == class) || is(T == interface)) + { + ptr[i .. _length - count] = ptr[i + count .. _length]; + ptr[_length - count .. _length] = null; + _length -= cast(uint)count; + } + else + { + for (size_t j = i; j < i + count; ++j) + destroy!false(ptr[j]); + for (size_t j = i + count; j < _length; ++j) + { + moveEmplace(ptr[j], ptr[j - count]); + destroy!false(ptr[j]); + } + } + _length -= cast(uint)count; + } + + void remove(const(T)* pItem) { remove(ptr[0 .. _length].indexOfElement(pItem)); } void removeFirst(U)(ref const U item) { remove(ptr[0 .. _length].findFirst(item)); } @@ -518,45 +660,48 @@ nothrow @nogc: return ptr ? ptr[0 .. allocCount()] : null; } - bool opCast(T : bool)() const + bool opCast(T : bool)() const pure => _length != 0; - size_t opDollar() const + size_t opDollar() const pure => _length; // full slice: arr[] - inout(T)[] opIndex() inout + inout(T)[] opIndex() inout pure => ptr[0 .. _length]; + void opIndexAssign(U)(U[] rh) + { + debug assert(rh.length == _length, "Range error"); + ptr[0 .. _length] = rh[]; + } + // array indexing: arr[i] - ref inout(T) opIndex(size_t i) inout + ref inout(T) opIndex(size_t i) inout pure { debug assert(i < _length, "Range error"); return ptr[i]; } // array slicing: arr[x .. y] - inout(T)[] opIndex(uint[2] i) inout + inout(T)[] opIndex(size_t[2] i) inout pure => ptr[i[0] .. i[1]]; - uint[2] opSlice(size_t dim : 0)(size_t x, size_t y) + void opIndexAssign(U)(U[] rh, size_t[2] i) { - debug assert(y <= _length, "Range error"); - return [cast(uint)x, cast(uint)y]; + debug assert(i[1] <= _length && i[1] - i[0] == rh.length, "Range error"); + ptr[i[0] .. i[1]] = rh[]; } - void opOpAssign(string op : "~", U)(auto ref U el) - if (is(U : T)) + size_t[2] opSlice(size_t dim : 0)(size_t x, size_t y) const pure { - pushBack(forward!el); + debug assert(y <= _length, "Range error"); + return [x, y]; } - void opOpAssign(string op : "~", U)(U[] arr) - if (is(U : T)) + void opOpAssign(string op : "~", U)(auto ref U rh) { - reserve(_length + arr.length); - foreach (ref e; arr) - pushBack(e); + append(forward!rh); } void reserve(size_t count) @@ -638,21 +783,36 @@ nothrow @nogc: _length = 0; } + import urt.string.format : FormatArg, formatValue; + ptrdiff_t toString()(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const + { + static if (is(T == class) || is(T == interface)) + assert(false, "TODO: class toString is not @nogc!"); + else + return formatValue(ptr[0 .. _length], buffer, format, formatArgs); + } + + ptrdiff_t fromString()(const(char)[] s) + { + assert(false, "TODO"); + } + private: - T* ptr; + enum copy_elements = is(T == class) || is(T == interface) || is_primitive!T || is_trivial!T; + uint _length; static if (EmbedCount > 0) T[EmbedCount] embed = void; - bool hasAllocation() const + bool hasAllocation() const pure { static if (EmbedCount > 0) return ptr && ptr != embed.ptr; else return ptr !is null; } - uint allocCount() const + uint allocCount() const pure => hasAllocation() ? (cast(uint*)ptr)[-1] : EmbedCount; T* allocate(uint count) @@ -673,7 +833,7 @@ private: } pragma(inline, true) - static uint numToAlloc(uint i) + static uint numToAlloc(uint i) pure { // TODO: i'm sure we can imagine a better heuristic... return i > 16 ? i * 2 : 16; @@ -721,11 +881,6 @@ struct SharedArray(T) nothrow @nogc: - void opAssign(typeof(null)) - { - clear(); - } - void opAssign(ref typeof(this) val) { clear(); diff --git a/src/urt/async.d b/src/urt/async.d index fee575f..557f24a 100644 --- a/src/urt/async.d +++ b/src/urt/async.d @@ -20,7 +20,7 @@ Promise!(ReturnType!Fun)* async(alias Fun, size_t stackSize = DefaultStackSize, // TODO: nice to rework this; maybe make stackSize a not-template-arg, and receive a function call/closure object which stores the args Promise!(ReturnType!Fun)* async(size_t stackSize = DefaultStackSize, Fun, Args...)(Fun fun, auto ref Args args) - if (isSomeFunction!Fun) + if (is_some_function!Fun) { alias Result = ReturnType!Fun; Promise!Result* r = cast(Promise!Result*)defaultAllocator().alloc(Promise!Result.sizeof, Promise!Result.alignof); @@ -60,7 +60,7 @@ Promise!(ReturnType!Fun)* async(size_t stackSize = DefaultStackSize, Fun, Args.. void freePromise(T)(ref Promise!T* promise) { - assert(promise.state() != Promise!T.State.Pending, "Promise still pending!"); + assert(promise.state() != PromiseState.Pending, "Promise still pending!"); defaultAllocator().freeT(promise); promise = null; } @@ -79,20 +79,20 @@ void asyncUpdate() if (!t.event.ready()) continue; } - t.resume(); + t.call.fibre.resume(); } } -struct Promise(Result) +enum PromiseState { - enum State - { - Pending, - Ready, - Failed, - } + Pending, + Ready, + Failed +} +struct Promise(Result) +{ // construct using `async()` functions... this() @disable; this(ref typeof(this)) @disable; // disable copy constructor @@ -117,29 +117,29 @@ struct Promise(Result) } } - State state() const + PromiseState state() const { if (async.fibre.wasAborted()) - return State.Failed; + return PromiseState.Failed; else if (async.fibre.isFinished()) - return State.Ready; + return PromiseState.Ready; else - return State.Pending; + return PromiseState.Pending; } bool finished() const - => state() != State.Pending; + => state() != PromiseState.Pending; ref Result result() { - assert(state() == State.Ready, "Promise not fulfilled!"); + assert(state() == PromiseState.Ready, "Promise not fulfilled!"); static if (!is(Result == void)) return value; } void abort() { - assert(state() == State.Pending, "Promise already fulfilled!"); + assert(state() == PromiseState.Pending, "Promise already fulfilled!"); async.fibre.abort(); } @@ -180,11 +180,11 @@ unittest } auto p = async!fun(1, 2); - assert(p.state() == p.State.Ready); + assert(p.state() == PromiseState.Ready); assert(p.result() == 3); freePromise(p); p = async!fun(10, 20); - assert(p.state() == p.State.Ready); + assert(p.state() == PromiseState.Ready); assert(p.result() == 30); freePromise(p); @@ -201,13 +201,13 @@ unittest } auto p_yield = async(&fun_yield); - assert(p_yield.state() == p_yield.State.Pending); + assert(p_yield.state() == PromiseState.Pending); assert(val == 1); asyncUpdate(); - assert(p_yield.state() == p_yield.State.Pending); + assert(p_yield.state() == PromiseState.Pending); assert(val == 2); asyncUpdate(); - assert(p_yield.state() == p_yield.State.Ready); + assert(p_yield.state() == PromiseState.Ready); assert(val == 3); assert(p_yield.result() == 4); freePromise(p_yield); diff --git a/src/urt/atomic.d b/src/urt/atomic.d new file mode 100644 index 0000000..a93cc45 --- /dev/null +++ b/src/urt/atomic.d @@ -0,0 +1,107 @@ +module urt.atomic; + +// TODO: these are all just stubs, but we can flesh it out as we need it... + +nothrow @nogc @safe: + +enum MemoryOrder +{ + relaxed = 0, + consume = 1, + acquire = 2, + release = 3, + acq_rel = 4, + seq_cst = 5, + seq = 5, +} + + +// DMD lowers to these names... +alias atomicLoad = atomic_load; +alias atomicStore = atomic_store; +alias atomicOp = atomic_op; +alias atomicExchange = atomic_exchange; +alias atomicFetchAdd = atomic_fetch_add; +alias atomicFetchSub = atomic_fetch_sub; + + +T atomic_load(MemoryOrder ms = MemoryOrder.seq, T)(ref const T val) pure @trusted +{ + return val; +} + +// Overload for shared +TailShared!T atomic_load(MemoryOrder ms = MemoryOrder.seq, T)(auto ref shared const T val) pure @trusted +{ + return *cast(TailShared!T*)&val; +} + +void atomic_store(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, V newval) pure @trusted + if (__traits(compiles, { *cast(T*)&val = newval; })) +{ + *cast(T*)&val = newval; +} + +TailShared!T atomic_op(string op, T, V1)(ref shared T val, V1 mod) pure @trusted + if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod"))) +{ + auto ptr = cast(T*)&val; + mixin("*ptr " ~ op ~ " mod;"); + return *cast(TailShared!T*)ptr; +} + +bool cas(T, V1, V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure @trusted +{ + auto ptr = cast(T*)here; + if (*ptr == ifThis) + { + *ptr = writeThis; + return true; + } + return false; +} + +T atomic_exchange(MemoryOrder ms = MemoryOrder.seq, T, V)(shared(T)* here, V exchangeWith) pure @trusted +{ + auto ptr = cast(T*)here; + T old = *ptr; + *ptr = exchangeWith; + return old; +} + +T atomic_fetch_add(MemoryOrder ms = MemoryOrder.seq, T)(ref shared T val, T mod) pure @trusted + if (__traits(isIntegral, T)) +{ + auto ptr = cast(T*)&val; + T old = *ptr; + *ptr += mod; + return old; +} + +T atomic_fetch_sub(MemoryOrder ms = MemoryOrder.seq, T)(ref shared T val, T mod) pure @trusted + if (__traits(isIntegral, T)) +{ + auto ptr = cast(T*)&val; + T old = *ptr; + *ptr -= mod; + return old; +} + +/// Simplified TailShared — strips shared qualifier for noruntime builds. +template TailShared(U) if (!is(U == shared)) +{ + alias TailShared = .TailShared!(shared U); +} + +template TailShared(S) if (is(S == shared)) +{ + static if (is(S U == shared U)) + { + static if (is(S : U)) + alias TailShared = U; + else + alias TailShared = S; + } + else + static assert(false); +} diff --git a/src/urt/conv.d b/src/urt/conv.d index 91f39ab..55755a7 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -7,48 +7,53 @@ public import urt.string.format : toString; nothrow @nogc: -// on error or not-a-number cases, bytesTaken will contain 0 -long parseInt(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure -{ - size_t i = 0; - bool neg = false; +// on error or not-a-number cases, bytes_taken will contain 0 - if (str.length > 0) - { - char c = str.ptr[0]; - neg = c == '-'; - if (neg || c == '+') - i++; - } - - ulong value = str.ptr[i .. str.length].parseUint(bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += i; - return neg ? -cast(long)value : cast(long)value; +long parse_int(const(char)[] str, size_t* bytes_taken = null, uint base = 10) pure +{ + const(char)* s = str.ptr, e = s + str.length, p = s; + uint neg = parse_sign(p, e); + ulong value = p[0 .. e - p].parse_uint(bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += p - s; + return neg ? -long(value) : long(value); } -long parseIntWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +long parse_int_with_base(const(char)[] str, size_t* bytes_taken = null) pure { - size_t i = 0; - bool neg = false; + const(char)* s = str.ptr, e = s + str.length, p = s; + uint neg = parse_sign(p, e); + uint base = parse_base_prefix(p, e); + ulong i = p[0 .. e - p].parse_uint(bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += p - s; + return neg ? -long(i) : long(i); +} - if (str.length > 0) - { - char c = str.ptr[0]; - neg = c == '-'; - if (neg || c == '+') - i++; - } +long parse_int_with_exponent(const(char)[] str, out int exponent, size_t* bytes_taken = null, uint base = 10) pure +{ + const(char)* s = str.ptr, e = s + str.length, p = s; + uint neg = parse_sign(p, e); + ulong value = p[0 .. e - p].parse_uint_with_exponent(exponent, bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += p - s; + return neg ? -long(value) : long(value); +} - ulong value = str[i .. str.length].parseUintWithDecimal(fixedPointDivisor, bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += i; - return neg ? -cast(long)value : cast(long)value; +long parse_int_with_exponent_and_base(const(char)[] str, out int exponent, out uint base, size_t* bytes_taken = null) pure +{ + const(char)* s = str.ptr, e = s + str.length, p = s; + uint neg = parse_sign(p, e); + base = parse_base_prefix(p, e); + ulong value = p[0 .. e - p].parse_uint_with_exponent(exponent, bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += p - s; + return neg ? -long(value) : long(value); } -ulong parseUint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint(const(char)[] str, size_t* bytes_taken = null, uint base = 10) pure { - assert(base > 1 && base <= 36, "Invalid base"); + debug assert(base > 1 && base <= 36, "Invalid base"); ulong value = 0; @@ -60,7 +65,7 @@ ulong parseUint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pur for (; s < e; ++s) { uint digit = *s - '0'; - if (digit > 9) + if (digit >= base) break; value = value*base + digit; } @@ -69,99 +74,192 @@ ulong parseUint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pur { for (; s < e; ++s) { - uint digit = getDigit(*s); + uint digit = get_digit(*s); if (digit >= base) break; value = value*base + digit; } } - if (bytesTaken) - *bytesTaken = s - str.ptr; + if (bytes_taken) + *bytes_taken = s - str.ptr; return value; } -ulong parseUintWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint_with_base(const(char)[] str, size_t* bytes_taken = null) pure { - assert(base > 1 && base <= 36, "Invalid base"); + const(char)* s = str.ptr, e = s + str.length, p = s; + uint base = parse_base_prefix(p, e); + ulong i = p[0 .. e - p].parse_uint(bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += p - s; + return i; +} - ulong value = 0; - ulong divisor = 1; +ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* bytes_taken = null, uint base = 10) pure +{ + debug assert(base > 1 && base <= 36, "Invalid base"); const(char)* s = str.ptr; const(char)* e = s + str.length; - // TODO: we could optimise the common base <= 10 case... + ulong value = 0; + int exp = 0; + uint digits = 0; + uint zero_seq = 0; + char c = void; for (; s < e; ++s) { - char c = *s; + c = *s; if (c == '.') { + if (s == str.ptr) + goto done; ++s; + exp = zero_seq; goto parse_decimal; } + else if (c == '0') + { + ++zero_seq; + continue; + } - uint digit = getDigit(c); + uint digit = get_digit(c); if (digit >= base) break; - value = value*base + digit; + + if (digits) + { + for (uint i = 0; i <= zero_seq; ++i) + value = value * base; + digits += zero_seq; + } + value += digit; + digits += 1; + zero_seq = 0; } - goto done; + + // number has no decimal point, tail zeroes are positive exp + if (!digits) + goto nothing; + + exp = zero_seq; + goto check_exp; parse_decimal: for (; s < e; ++s) { - uint digit = getDigit(*s); - if (digit >= base) + c = *s; + + if (c == '0') { - // if i == 1, then the first char was a '.' and the next was not numeric, so this is not a number! - if (s == str.ptr + 1) - s = str.ptr; + ++zero_seq; + continue; + } + + uint digit = get_digit(c); + if (digit >= base) break; + + if (digits) + { + for (uint i = 0; i <= zero_seq; ++i) + value = value * base; + digits += zero_seq; + } + value += digit; + digits += 1; + exp -= 1 + zero_seq; + zero_seq = 0; + } + if (!digits) + goto nothing; + +check_exp: + // check for exponent part + if (s + 1 < e && ((*s | 0x20) == 'e')) + { + c = s[1]; + bool exp_neg = c == '-'; + if (exp_neg || c == '+') + { + if (s + 2 >= e || !s[2].is_numeric) + goto done; + s += 2; } - value = value*base + digit; - divisor *= base; + else + { + if (!c.is_numeric) + goto done; + ++s; + } + + int exp_value = 0; + for (; s < e; ++s) + { + uint digit = *s - '0'; + if (digit > 9) + break; + exp_value = exp_value * 10 + digit; + } + exp += exp_neg ? -exp_value : exp_value; } done: - fixedPointDivisor = divisor; - if (bytesTaken) - *bytesTaken = s - str.ptr; + exponent = exp; + if (bytes_taken) + *bytes_taken = s - str.ptr; return value; + +nothing: + exp = 0; + goto done; } -ulong parseUintWithBase(const(char)[] str, size_t* bytesTaken = null) pure +ulong parse_uint_with_exponent_and_base(const(char)[] str, out int exponent, out uint base, size_t* bytes_taken = null) pure { - const(char)* p = str.ptr; - int base = parseBasePrefix(str); - ulong i = parseUint(str, bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += str.ptr - p; - return i; + const(char)* s = str.ptr, e = s + str.length, p = s; + base = parse_base_prefix(p, e); + ulong value = p[0 .. e - p].parse_uint_with_exponent(exponent, bytes_taken, base); + if (value && *bytes_taken != 0) + *bytes_taken += p - s; + return value; } - unittest { size_t taken; - ulong divisor; - assert(parseUint("123") == 123); - assert(parseInt("+123.456") == 123); - assert(parseInt("-123.456", null, 10) == -123); - assert(parseUintWithDecimal("123.456", divisor, null, 10) == 123456 && divisor == 1000); - assert(parseIntWithDecimal("123.456.789", divisor, &taken, 16) == 1193046 && taken == 7 && divisor == 4096); - assert(parseInt("11001", null, 2) == 25); - assert(parseIntWithDecimal("-AbCdE.f", divisor, null, 16) == -11259375 && divisor == 16); - assert(parseInt("123abc", &taken, 10) == 123 && taken == 3); - assert(parseInt("!!!", &taken, 10) == 0 && taken == 0); - assert(parseInt("-!!!", &taken, 10) == 0 && taken == 0); - assert(parseInt("Wow", &taken, 36) == 42368 && taken == 3); - assert(parseUintWithBase("0x100", &taken) == 0x100 && taken == 5); + assert(parse_uint("123") == 123); + assert(parse_int("+123.456") == 123); + assert(parse_int("-123.456", null, 10) == -123); + assert(parse_int("11001", null, 2) == 25); + assert(parse_int("123abc", &taken, 10) == 123 && taken == 3); + assert(parse_int("!!!", &taken, 10) == 0 && taken == 0); + assert(parse_int("-!!!", &taken, 10) == 0 && taken == 0); + assert(parse_int("Wow", &taken, 36) == 42368 && taken == 3); + assert(parse_uint_with_base("0x100", &taken) == 0x100 && taken == 5); + assert(parse_int_with_base("-0x100", &taken) == -0x100 && taken == 6); + + int e; + assert("0001023000".parse_uint_with_exponent(e, &taken, 10) == 1023 && e == 3 && taken == 10); + assert("0.0012003000".parse_uint_with_exponent(e, &taken, 10) == 12003 && e == -7 && taken == 12); + assert("00010.23000".parse_uint_with_exponent(e, &taken, 10) == 1023 && e == -2 && taken == 11); + assert("00012300.0".parse_uint_with_exponent(e, &taken, 10) == 123 && e == 2 && taken == 10); + assert("00100.00230".parse_uint_with_exponent(e, &taken, 10) == 1000023 && e == -4 && taken == 11); + assert("0.0".parse_uint_with_exponent(e, &taken, 10) == 0 && e == 0 && taken == 3); + assert(".01".parse_uint_with_exponent(e, &taken, 10) == 0 && e == 0 && taken == 0); + assert("10e2".parse_uint_with_exponent(e, &taken, 10) == 1 && e == 3 && taken == 4); + assert("0.01E+2".parse_uint_with_exponent(e, &taken, 10) == 1 && e == 0 && taken == 7); + assert("0.01E".parse_uint_with_exponent(e, &taken, 10) == 1 && e == -2 && taken == 4); + assert("0.01Ex".parse_uint_with_exponent(e, &taken, 10) == 1 && e == -2 && taken == 4); + assert("0.01E-".parse_uint_with_exponent(e, &taken, 10) == 1 && e == -2 && taken == 4); + assert("0.01E-x".parse_uint_with_exponent(e, &taken, 10) == 1 && e == -2 && taken == 4); } -int parseIntFast(ref const(char)[] text, out bool success) pure +int parse_int_fast(ref const(char)[] text, out bool success) pure { if (!text.length) return 0; @@ -210,30 +308,38 @@ unittest { bool success; const(char)[] text = "123"; - assert(parseIntFast(text, success) == 123 && success == true && text.empty); + assert(parse_int_fast(text, success) == 123 && success == true && text.empty); text = "-2147483648abc"; - assert(parseIntFast(text, success) == -2147483648 && success == true && text.length == 3); + assert(parse_int_fast(text, success) == -2147483648 && success == true && text.length == 3); text = "2147483648"; - assert(parseIntFast(text, success) == 0 && success == false); + assert(parse_int_fast(text, success) == 0 && success == false); text = "-2147483649"; - assert(parseIntFast(text, success) == 0 && success == false); + assert(parse_int_fast(text, success) == 0 && success == false); text = "2147483650"; - assert(parseIntFast(text, success) == 0 && success == false); + assert(parse_int_fast(text, success) == 0 && success == false); } -// on error or not-a-number, result will be nan and bytesTaken will contain 0 -double parseFloat(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure +// on error or not-a-number, result will be nan and bytes_taken will contain 0 +double parse_float(const(char)[] str, size_t* bytes_taken = null, uint base = 10) pure { - // TODO: E-notation... - size_t taken = void; - ulong div = void; - long value = str.parseIntWithDecimal(div, &taken, base); - if (bytesTaken) - *bytesTaken = taken; + import urt.math : pow; + + int e; + size_t taken; + long mantissa = str.parse_int_with_exponent(e, &taken, base); + if (bytes_taken) + *bytes_taken = taken; if (taken == 0) return double.nan; - return cast(double)value / div; + + // TODO: the real work needs to happen here! + // we want all the bits of precision! + + if (__ctfe) + return mantissa * double(base)^^e; + else + return mantissa * pow(double(base), e); } unittest @@ -245,26 +351,115 @@ unittest } size_t taken; - assert(fcmp(parseFloat("123.456"), 123.456)); - assert(fcmp(parseFloat("+123.456"), 123.456)); - assert(fcmp(parseFloat("-123.456.789"), -123.456)); - assert(fcmp(parseFloat("1101.11", &taken, 2), 13.75) && taken == 7); - assert(parseFloat("xyz", &taken) is double.nan && taken == 0); + assert(fcmp(parse_float("123.456"), 123.456)); + assert(fcmp(parse_float("+123.456"), 123.456)); + assert(fcmp(parse_float("-123.456.789"), -123.456)); + assert(fcmp(parse_float("-123.456e10"), -1.23456e+12)); + assert(fcmp(parse_float("1101.11", &taken, 2), 13.75) && taken == 7); + assert(parse_float("xyz", &taken) is double.nan && taken == 0); +} + + +ptrdiff_t parse(T)(const char[] text, out T result) +{ + import urt.array : beginsWith; + import urt.traits; + + alias UT = Unqual!T; + + static if (is(UT == bool)) + { + if (text.beginsWith("true")) + { + result = true; + return 4; + } + result = false; + if (text.beginsWith("false")) + return 5; + return -1; + } + else static if (is_some_int!T) + { + size_t taken; + static if (is_signed_int!T) + long r = text.parse_int(&taken); + else + ulong r = text.parse_uint(&taken); + if (!taken) + return -1; + if (r >= T.min && r <= T.max) + { + result = cast(T)r; + return taken; + } + return -2; + } + else static if (is_some_float!T) + { + size_t taken; + double f = text.parse_float(&taken); + if (!taken) + return -1; + result = cast(T)f; + return taken; + } + else static if (is_enum!T) + { + static assert(false, "TODO: do we want to parse from enum keys?"); + // case-sensitive? + } + else static if (is(T == struct) && __traits(compiles, { result.fromString(text); })) + { + return result.fromString(text); + } + else + static assert(false, "Cannot parse " ~ T.stringof ~ " from string"); +} + +unittest +{ + { + bool r; + assert("true".parse(r) == 4 && r == true); + assert("false".parse(r) == 5 && r == false); + assert("wow".parse(r) == -1); + } + { + int r; + assert("-10".parse(r) == 3 && r == -10); + } + { + ubyte r; + assert("10".parse(r) == 2 && r == 10); + assert("-10".parse(r) == -1); + assert("257".parse(r) == -2); + } + { + float r; + assert("10".parse(r) == 2 && r == 10.0f); + assert("-2.5".parse(r) == 4 && r == -2.5f); + } + { + import urt.inet; + IPAddr r; + assert("10.0.0.1".parse(r) == 8 && r == IPAddr(10,0,0,1)); + } } -ptrdiff_t formatInt(long value, char[] buffer, uint base = 10, uint width = 0, char fill = ' ', bool showSign = false) pure +ptrdiff_t format_int(long value, char[] buffer, uint base = 10, uint width = 0, char fill = ' ', bool show_sign = false) pure { const bool neg = value < 0; - showSign |= neg; + show_sign |= neg; - if (buffer.ptr && buffer.length < showSign) + if (buffer.ptr && buffer.length < show_sign) return -1; ulong i = neg ? -value : value; - ptrdiff_t r = formatUint(i, buffer.ptr ? buffer.ptr[(width == 0 ? showSign : 0) .. buffer.length] : null, base, width, fill); - if (r < 0 || !showSign) + ptrdiff_t r = format_uint(i, buffer.ptr ? buffer.ptr[(width == 0 ? show_sign : 0) .. buffer.length] : null, base, width, fill); + if (r < 0 || !show_sign) return r; if (buffer.ptr) @@ -286,10 +481,10 @@ ptrdiff_t formatInt(long value, char[] buffer, uint base = 10, uint width = 0, c if (buffer.ptr[0] == fill) { // we don't need to shift it left... - size_t sgnOffset = 0; - while (buffer.ptr[sgnOffset + 1] == fill) - ++sgnOffset; - buffer.ptr[sgnOffset] = sgn; + size_t sgn_offset = 0; + while (buffer.ptr[sgn_offset + 1] == fill) + ++sgn_offset; + buffer.ptr[sgn_offset] = sgn; return r; } @@ -309,20 +504,20 @@ ptrdiff_t formatInt(long value, char[] buffer, uint base = 10, uint width = 0, c return r + 1; } -ptrdiff_t formatUint(ulong value, char[] buffer, uint base = 10, uint width = 0, char fill = ' ') pure +ptrdiff_t format_uint(ulong value, char[] buffer, uint base = 10, uint width = 0, char fill = ' ') pure { import urt.util : max; assert(base >= 2 && base <= 36, "Invalid base"); ulong i = value; - uint numLen = 0; + uint num_len = 0; char[64] t = void; if (i == 0) { if (buffer.length > 0) t.ptr[0] = '0'; - numLen = 1; + num_len = 1; } else { @@ -334,14 +529,14 @@ ptrdiff_t formatUint(ulong value, char[] buffer, uint base = 10, uint width = 0, if (buffer.ptr) { int d = cast(int)(i % base); - t.ptr[numLen] = cast(char)((d < 10 ? '0' : 'A' - 10) + d); + t.ptr[num_len] = cast(char)((d < 10 ? '0' : 'A' - 10) + d); } - ++numLen; + ++num_len; } } - uint len = max(numLen, width); - uint padding = width > numLen ? width - numLen : 0; + uint len = max(num_len, width); + uint padding = width > num_len ? width - num_len : 0; if (buffer.ptr) { @@ -351,7 +546,7 @@ ptrdiff_t formatUint(ulong value, char[] buffer, uint base = 10, uint width = 0, size_t offset = 0; while (padding--) buffer.ptr[offset++] = fill; - for (uint j = numLen; j > 0; ) + for (uint j = num_len; j > 0; ) buffer.ptr[offset++] = t[--j]; } return len; @@ -360,41 +555,41 @@ ptrdiff_t formatUint(ulong value, char[] buffer, uint base = 10, uint width = 0, unittest { char[64] buffer; - assert(formatInt(0, null) == 1); - assert(formatInt(14, null) == 2); - assert(formatInt(14, null, 16) == 1); - assert(formatInt(-14, null) == 3); - assert(formatInt(-14, null, 16) == 2); - assert(formatInt(-14, null, 16, 3, '0') == 3); - assert(formatInt(-123, null, 10, 6) == 6); - assert(formatInt(-123, null, 10, 3) == 4); - assert(formatInt(-123, null, 10, 2) == 4); - - size_t len = formatInt(0, buffer); + assert(format_int(0, null) == 1); + assert(format_int(14, null) == 2); + assert(format_int(14, null, 16) == 1); + assert(format_int(-14, null) == 3); + assert(format_int(-14, null, 16) == 2); + assert(format_int(-14, null, 16, 3, '0') == 3); + assert(format_int(-123, null, 10, 6) == 6); + assert(format_int(-123, null, 10, 3) == 4); + assert(format_int(-123, null, 10, 2) == 4); + + size_t len = format_int(0, buffer); assert(buffer[0 .. len] == "0"); - len = formatInt(14, buffer); + len = format_int(14, buffer); assert(buffer[0 .. len] == "14"); - len = formatInt(14, buffer, 2); + len = format_int(14, buffer, 2); assert(buffer[0 .. len] == "1110"); - len = formatInt(14, buffer, 8, 3); + len = format_int(14, buffer, 8, 3); assert(buffer[0 .. len] == " 16"); - len = formatInt(14, buffer, 16, 4, '0'); + len = format_int(14, buffer, 16, 4, '0'); assert(buffer[0 .. len] == "000E"); - len = formatInt(-14, buffer, 16, 3, '0'); + len = format_int(-14, buffer, 16, 3, '0'); assert(buffer[0 .. len] == "-0E"); - len = formatInt(12345, buffer, 10, 3); + len = format_int(12345, buffer, 10, 3); assert(buffer[0 .. len] == "12345"); - len = formatInt(-123, buffer, 10, 6); + len = format_int(-123, buffer, 10, 6); assert(buffer[0 .. len] == " -123"); } -ptrdiff_t formatFloat(double value, char[] buffer, const(char)[] format = null) // pure +ptrdiff_t format_float(double value, char[] buffer, const(char)[] format = null) // pure { // TODO: this function should be oblitereated and implemented natively... // CRT call can't CTFE, which is a shame - import core.stdc.stdio; + import urt.internal.stdc; import urt.string.format : concat; char[16] fmt = void; @@ -424,9 +619,9 @@ template to(T) { long to(const(char)[] str) { - int base = parseBasePrefix(str); + uint base = parse_base_prefix(str); size_t taken; - long r = parseInt(str, &taken, base); + long r = parse_int(str, &taken, base); assert(taken == str.length, "String is not numeric"); return r; } @@ -435,19 +630,19 @@ template to(T) { double to(const(char)[] str) { - int base = parseBasePrefix(str); + uint base = parse_base_prefix(str); size_t taken; - double r = parseFloat(str, &taken, base); + double r = parse_float(str, &taken, base); assert(taken == str.length, "String is not numeric"); return r; } } - else static if (isSomeInt!T) // call-through for other int types; reduce instantiation bloat + else static if (is_some_int!T) // call-through for other int types; reduce instantiation bloat { T to(const(char)[] str) => cast(T)to!long(str); } - else static if (isSomeFloat!T) // call-through for other float types; reduce instantiation bloat + else static if (is_some_float!T) // call-through for other float types; reduce instantiation bloat { T to(const(char)[] str) => cast(T)to!double(str); @@ -479,38 +674,46 @@ template to(T) private: -uint getDigit(char c) pure +// valid result is 0 .. 35; result is garbage outside that bound +uint get_digit(char c) pure { - uint zeroBase = c - '0'; - if (zeroBase < 10) - return zeroBase; - uint ABase = c - 'A'; - if (ABase < 26) - return ABase + 10; - uint aBase = c - 'a'; - if (aBase < 26) - return aBase + 10; - return -1; + uint zero_base = c - '0'; + if (zero_base < 10) + return zero_base; + uint a_base = (c | 0x20) - 'a'; + return 10 + (a_base & 0xFF); } -int parseBasePrefix(ref const(char)[] str) pure +uint parse_base_prefix(ref const(char)* str, const(char)* end) pure { - int base = 10; - if (str.length >= 2) + uint base = 10; + if (str + 2 <= end && str[0] == '0') { - if (str[0..2] == "0x") - base = 16, str = str[2..$]; - else if (str[0..2] == "0b") - base = 2, str = str[2..$]; - else if (str[0..2] == "0o") - base = 8, str = str[2..$]; + if (str[1] == 'x') + base = 16, str += 2; + else if (str[1] == 'b') + base = 2, str += 2; + else if (str[1] == 'o') + base = 8, str += 2; } return base; } +uint parse_sign(ref const(char)* str, const(char)* end) pure +{ + if (str == end) + return 0; + // NOTE: ascii is '+' = 43, '-' = 45 + uint neg = *str - '+'; + if (neg > 2 || neg == 1) + return 0; + ++str; + return neg; // neg is 0 (+) or 2 (-) +} + /+ -size_t formatStruct(T)(ref T value, char[] buffer) nothrow @nogc +size_t format_struct(T)(ref T value, char[] buffer) nothrow @nogc { import urt.string.format; @@ -518,7 +721,7 @@ size_t formatStruct(T)(ref T value, char[] buffer) nothrow @nogc alias args = value.tupleof; // alias args = AliasSeq!(value.tupleof); -// alias args = InterleaveSeparator!(", ", value.tupleof); +// alias args = INTERLEAVE_SEPARATOR!(", ", value.tupleof); // pragma(msg, args); return concat(buffer, args).length; } @@ -530,7 +733,7 @@ unittest Packet p; char[1024] buffer; - size_t len = formatStruct(p, buffer); + size_t len = format_struct(p, buffer); assert(buffer[0 .. len] == "Packet()"); } diff --git a/src/urt/crc.d b/src/urt/crc.d index 5010e3f..b26b6c9 100644 --- a/src/urt/crc.d +++ b/src/urt/crc.d @@ -1,51 +1,51 @@ module urt.crc; -import urt.meta : intForWidth; -import urt.traits : isUnsignedInt; +import urt.meta : IntForWidth; +import urt.traits : is_unsigned_int; nothrow @nogc: enum Algorithm : ubyte { - CRC16_USB, - CRC16_MODBUS, - CRC16_KERMIT, - CRC16_XMODEM, - CRC16_CCITT_FALSE, - CRC16_ISO_HDLC, - CRC16_DNP, - CRC32_ISO_HDLC, - CRC32_CASTAGNOLI, - - CRC16_Default_ShortPacket = CRC16_KERMIT, // good default choice for small packets - CRC32_Default = CRC32_CASTAGNOLI, // has SSE4.2 hardware implementation - - // aliases - CRC16_BLUETOOTH = CRC16_KERMIT, - CRC16_CCITT_TRUE = CRC16_KERMIT, - CRC16_CCITT = CRC16_KERMIT, - CRC16_EZSP = CRC16_CCITT_FALSE, - CRC16_IBM_SDLC = CRC16_ISO_HDLC, - CRC32_NVME = CRC32_CASTAGNOLI, + crc16_usb, + crc16_modbus, + crc16_kermit, + crc16_xmodem, + crc16_ccitt_false, + crc16_iso_hdlc, + crc16_dnp, + crc32_iso_hdlc, + crc32_castagnoli, + + crc16_default_short_packet = crc16_kermit, // good default choice for small packets + crc32_default = crc32_castagnoli, // has SSE4.2 hardware implementation + + // aliases + crc16_bluetooth = crc16_kermit, + crc16_ccitt_true = crc16_kermit, + crc16_ccitt = crc16_kermit, + crc16_ezsp = crc16_ccitt_false, + crc16_ibm_sdlc = crc16_iso_hdlc, + crc32_nvme = crc32_castagnoli, } -struct CRCParams +struct crc_params { ubyte width; bool reflect; uint poly; uint initial; - uint finalXor; + uint final_xor; uint check; } -alias CRCTable(Algorithm algo) = CRCTable!(paramTable[algo].width, paramTable[algo].poly, paramTable[algo].reflect); -alias CRCType(Algorithm algo) = intForWidth!(paramTable[algo].width); +alias CRCType(Algorithm algo) = IntForWidth!(param_table[algo].width); +alias crc_table(Algorithm algo) = crc_table!(param_table[algo].width, param_table[algo].poly, param_table[algo].reflect); // compute a CRC with runtime parameters -T calculateCRC(T = uint)(const void[] data, ref const CRCParams params, ref const T[256] table) pure - if (isUnsignedInt!T) +T calculate_crc(T = uint)(const void[] data, ref const crc_params params, ref const T[256] table) pure + if (is_unsigned_int!T) { assert(params.width <= T.sizeof*8, "T is too small for the CRC width"); @@ -64,22 +64,22 @@ T calculateCRC(T = uint)(const void[] data, ref const CRCParams params, ref cons crc = cast(T)((crc << 8) ^ table[(crc >> 8) ^ b]); } - return crc ^ cast(T)params.finalXor; + return crc ^ cast(T)params.final_xor; } // compute a CRC with hard-coded parameters -T calculateCRC(Algorithm algo, T = CRCType!algo)(const void[] data, T initial = cast(T)paramTable[algo].initial^paramTable[algo].finalXor) pure - if (isUnsignedInt!T) +T calculate_crc(Algorithm algo, T = CRCType!algo)(const void[] data, T initial = cast(T)param_table[algo].initial^param_table[algo].final_xor) pure + if (is_unsigned_int!T) { - enum CRCParams params = paramTable[algo]; + enum crc_params params = param_table[algo]; static assert(params.width <= T.sizeof*8, "T is too small for the CRC width"); - alias table = CRCTable!algo; + alias table = crc_table!algo; const ubyte[] bytes = cast(ubyte[])data; - static if (params.finalXor) - T crc = initial ^ params.finalXor; + static if (params.final_xor) + T crc = initial ^ params.final_xor; else T crc = initial; @@ -91,32 +91,32 @@ T calculateCRC(Algorithm algo, T = CRCType!algo)(const void[] data, T initial = crc = cast(T)((crc << 8) ^ table[(crc >> 8) ^ b]); } - static if (params.finalXor) - return T(crc ^ params.finalXor); + static if (params.final_xor) + return T(crc ^ params.final_xor); else return crc; } // computes 2 CRC's for 2 points in the data stream... -T calculateCRC_2(Algorithm algo, T = intForWidth!(paramTable[algo].width*2))(const void[] data, uint earlyOffset) pure - if (isUnsignedInt!T) +T calculate_crc_2(Algorithm algo, T = IntForWidth!(param_table[algo].width*2))(const void[] data, uint early_offset) pure + if (is_unsigned_int!T) { - enum CRCParams params = paramTable[algo]; + enum crc_params params = param_table[algo]; static assert(params.width * 2 <= T.sizeof*8, "T is too small for the CRC width"); - alias table = CRCTable!algo; + alias table = crc_table!algo; const ubyte[] bytes = cast(ubyte[])data; - T highCRC = 0; + T high_crc = 0; T crc = cast(T)params.initial; size_t i = 0; for (; i < bytes.length; ++i) { - if (i == earlyOffset) + if (i == early_offset) { - highCRC = crc; + high_crc = crc; goto fast_loop; // skips a redundant loop entry check } static if (params.reflect) @@ -136,27 +136,27 @@ T calculateCRC_2(Algorithm algo, T = intForWidth!(paramTable[algo].width*2))(con } done: - static if (params.finalXor) + static if (params.final_xor) { - crc ^= cast(T)params.finalXor; - highCRC ^= cast(T)params.finalXor; + crc ^= cast(T)params.final_xor; + high_crc ^= cast(T)params.final_xor; } static if (params.width <= 8) - return ushort(crc | highCRC << 8); + return ushort(crc | high_crc << 8); else static if (params.width <= 16) - return uint(crc | highCRC << 16); + return uint(crc | high_crc << 16); else if (params.width <= 32) - return ulong(crc | highCRC << 32); + return ulong(crc | high_crc << 32); } -T[256] generateCRCTable(T)(ref const CRCParams params) pure - if (isUnsignedInt!T) +T[256] generate_crc_table(T)(ref const crc_params params) pure + if (is_unsigned_int!T) { - enum typeWidth = T.sizeof * 8; - assert(params.width <= typeWidth && params.width > typeWidth/2, "CRC width must match the size of the type"); - T topBit = cast(T)(1 << (params.width - 1)); + enum type_width = T.sizeof * 8; + assert(params.width <= type_width && params.width > type_width/2, "CRC width must match the size of the type"); + T top_bit = cast(T)(1 << (params.width - 1)); T[256] table = void; @@ -169,7 +169,7 @@ T[256] generateCRCTable(T)(ref const CRCParams params) pure crc <<= (params.width - 8); // Shift to align with the polynomial width foreach (_; 0..8) { - if ((crc & topBit) != 0) + if ((crc & top_bit) != 0) crc = cast(T)((crc << 1) ^ params.poly); else crc <<= 1; @@ -189,38 +189,38 @@ unittest { immutable ubyte[9] checkData = ['1','2','3','4','5','6','7','8','9']; - assert(calculateCRC!(Algorithm.CRC16_MODBUS)(checkData[]) == paramTable[Algorithm.CRC16_MODBUS].check); - assert(calculateCRC!(Algorithm.CRC16_EZSP)(checkData[]) == paramTable[Algorithm.CRC16_EZSP].check); - assert(calculateCRC!(Algorithm.CRC16_KERMIT)(checkData[]) == paramTable[Algorithm.CRC16_KERMIT].check); - assert(calculateCRC!(Algorithm.CRC16_USB)(checkData[]) == paramTable[Algorithm.CRC16_USB].check); - assert(calculateCRC!(Algorithm.CRC16_XMODEM)(checkData[]) == paramTable[Algorithm.CRC16_XMODEM].check); - assert(calculateCRC!(Algorithm.CRC16_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC16_ISO_HDLC].check); - assert(calculateCRC!(Algorithm.CRC16_DNP)(checkData[]) == paramTable[Algorithm.CRC16_DNP].check); - assert(calculateCRC!(Algorithm.CRC32_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC32_ISO_HDLC].check); - assert(calculateCRC!(Algorithm.CRC32_CASTAGNOLI)(checkData[]) == paramTable[Algorithm.CRC32_CASTAGNOLI].check); + assert(calculate_crc!(Algorithm.crc16_modbus)(checkData[]) == param_table[Algorithm.crc16_modbus].check); + assert(calculate_crc!(Algorithm.crc16_ezsp)(checkData[]) == param_table[Algorithm.crc16_ezsp].check); + assert(calculate_crc!(Algorithm.crc16_kermit)(checkData[]) == param_table[Algorithm.crc16_kermit].check); + assert(calculate_crc!(Algorithm.crc16_usb)(checkData[]) == param_table[Algorithm.crc16_usb].check); + assert(calculate_crc!(Algorithm.crc16_xmodem)(checkData[]) == param_table[Algorithm.crc16_xmodem].check); + assert(calculate_crc!(Algorithm.crc16_iso_hdlc)(checkData[]) == param_table[Algorithm.crc16_iso_hdlc].check); + assert(calculate_crc!(Algorithm.crc16_dnp)(checkData[]) == param_table[Algorithm.crc16_dnp].check); + assert(calculate_crc!(Algorithm.crc32_iso_hdlc)(checkData[]) == param_table[Algorithm.crc32_iso_hdlc].check); + assert(calculate_crc!(Algorithm.crc32_castagnoli)(checkData[]) == param_table[Algorithm.crc32_castagnoli].check); // check that rolling CRC works... - ushort crc = calculateCRC!(Algorithm.CRC16_MODBUS)(checkData[0 .. 5]); - assert(calculateCRC!(Algorithm.CRC16_MODBUS)(checkData[5 .. 9], crc) == paramTable[Algorithm.CRC16_MODBUS].check); - crc = calculateCRC!(Algorithm.CRC16_ISO_HDLC)(checkData[0 .. 5]); - assert(calculateCRC!(Algorithm.CRC16_ISO_HDLC)(checkData[5 .. 9], crc) == paramTable[Algorithm.CRC16_ISO_HDLC].check); - uint crc32 = calculateCRC!(Algorithm.CRC32_ISO_HDLC)(checkData[0 .. 5]); - assert(calculateCRC!(Algorithm.CRC32_ISO_HDLC)(checkData[5 .. 9], crc32) == paramTable[Algorithm.CRC32_ISO_HDLC].check); + ushort crc = calculate_crc!(Algorithm.crc16_modbus)(checkData[0 .. 5]); + assert(calculate_crc!(Algorithm.crc16_modbus)(checkData[5 .. 9], crc) == param_table[Algorithm.crc16_modbus].check); + crc = calculate_crc!(Algorithm.crc16_iso_hdlc)(checkData[0 .. 5]); + assert(calculate_crc!(Algorithm.crc16_iso_hdlc)(checkData[5 .. 9], crc) == param_table[Algorithm.crc16_iso_hdlc].check); + uint crc32 = calculate_crc!(Algorithm.crc32_iso_hdlc)(checkData[0 .. 5]); + assert(calculate_crc!(Algorithm.crc32_iso_hdlc)(checkData[5 .. 9], crc32) == param_table[Algorithm.crc32_iso_hdlc].check); } private: -enum CRCParams[] paramTable = [ - CRCParams(16, true, 0x8005, 0xFFFF, 0xFFFF, 0xB4C8), // CRC16_USB - CRCParams(16, true, 0x8005, 0xFFFF, 0x0000, 0x4B37), // CRC16_MODBUS - CRCParams(16, true, 0x1021, 0x0000, 0x0000, 0x2189), // CRC16_KERMIT - CRCParams(16, false, 0x1021, 0x0000, 0x0000, 0x31C3), // CRC16_XMODEM - CRCParams(16, false, 0x1021, 0xFFFF, 0x0000, 0x29B1), // CRC16_CCITT_FALSE - CRCParams(16, true, 0x1021, 0xFFFF, 0xFFFF, 0x906E), // CRC16_ISO_HDLC - CRCParams(16, true, 0x3D65, 0x0000, 0xFFFF, 0xEA82), // CRC16_DNP - CRCParams(32, true, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 0xCBF43926), // CRC32_ISO_HDLC - CRCParams(32, true, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, 0xE3069283), // CRC32_CASTAGNOLI +enum crc_params[] param_table = [ + crc_params(16, true, 0x8005, 0xFFFF, 0xFFFF, 0xB4C8), // crc16_usb + crc_params(16, true, 0x8005, 0xFFFF, 0x0000, 0x4B37), // crc16_modbus + crc_params(16, true, 0x1021, 0x0000, 0x0000, 0x2189), // crc16_kermit + crc_params(16, false, 0x1021, 0x0000, 0x0000, 0x31C3), // crc16_xmodem + crc_params(16, false, 0x1021, 0xFFFF, 0x0000, 0x29B1), // crc16_ccitt_false + crc_params(16, true, 0x1021, 0xFFFF, 0xFFFF, 0x906E), // crc16_iso_hdlc + crc_params(16, true, 0x3D65, 0x0000, 0xFFFF, 0xEA82), // crc16_dnp + crc_params(32, true, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 0xCBF43926), // crc32_iso_hdlc + crc_params(32, true, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, 0xE3069283), // crc32_castagnoli ]; // helper function to reflect bits (reverse bit order) @@ -236,7 +236,7 @@ T reflect(T)(T value, ubyte bits) } // this minimises the number of table instantiations -template CRCTable(uint width, uint poly, bool reflect) +template crc_table(uint width, uint poly, bool reflect) { - __gshared immutable CRCTable = generateCRCTable!(intForWidth!width)(CRCParams(width, reflect, poly, 0, 0, 0)); + __gshared immutable crc_table = generate_crc_table!(IntForWidth!width)(crc_params(width, reflect, poly, 0, 0, 0)); } diff --git a/src/urt/crypto/chacha.d b/src/urt/crypto/chacha.d new file mode 100644 index 0000000..2f263db --- /dev/null +++ b/src/urt/crypto/chacha.d @@ -0,0 +1,299 @@ +module urt.crypto.chacha; + +import urt.endian : littleEndianToNative, nativeToLittleEndian; +import urt.mem; + +nothrow @nogc: + + +struct ChaChaContext +{ + uint[16] state; + union { + uint[16] keystream32; + ubyte[64] keystream8; + } + uint nonceLow; + ushort position; + ushort rounds; +} + + +void chacha_init_context(ref ChaChaContext ctx, ref const ubyte[16] key, ulong nonce, ulong counter = 0, uint rounds = 20) pure +{ + chacha_init_context(ctx, key, expandNonce(nonce), counter, rounds); +} + +void chacha_init_context(ref ChaChaContext ctx, ref const ubyte[32] key, ulong nonce, ulong counter = 0, uint rounds = 20) pure +{ + chacha_init_context(ctx, key, expandNonce(nonce), counter, rounds); +} + +void chacha_init_context(ref ChaChaContext ctx, ref const ubyte[16] key, ref const ubyte[12] nonce, ulong counter = 0, uint rounds = 20) pure +{ + ubyte[32] key256 = void; + key256[0 .. 16] = key[]; + key256[16 .. 32] = key[]; + chacha_init_context(ctx, key256, nonce, counter, rounds); +} + +void chacha_init_context(ref ChaChaContext ctx, ref const ubyte[32] key, ref const ubyte[12] nonce, ulong counter = 0, uint rounds = 20) pure +{ + // the number of rounds must be 8, 12 or 20 + assert (rounds == 8 || rounds == 12 || rounds == 20); + ctx.rounds = cast(ushort)rounds / 2; + + ctx.nonceLow = nonce[0..4].littleEndianToNative!uint; + + // the string "expand 32-byte k" in little-endian + enum uint[4] magic_constant = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 ]; + + ctx.state[0] = magic_constant[0]; + ctx.state[1] = magic_constant[1]; + ctx.state[2] = magic_constant[2]; + ctx.state[3] = magic_constant[3]; + ctx.state[4] = key[0..4].littleEndianToNative!uint; + ctx.state[5] = key[4..8].littleEndianToNative!uint; + ctx.state[6] = key[8..12].littleEndianToNative!uint; + ctx.state[7] = key[12..16].littleEndianToNative!uint; + ctx.state[8] = key[16..20].littleEndianToNative!uint; + ctx.state[9] = key[20..24].littleEndianToNative!uint; + ctx.state[10] = key[24..28].littleEndianToNative!uint; + ctx.state[11] = key[28..32].littleEndianToNative!uint; + ctx.state[12] = cast(uint)counter; + ctx.state[13] = ctx.nonceLow | (counter >> 32); + ctx.state[14] = nonce[4..8].littleEndianToNative!uint; + ctx.state[15] = nonce[8..12].littleEndianToNative!uint; + + ctx.keystream32[] = 0; + + // set starting position to the end of the key buffer; there are no bytes to consume yet + ctx.position = 64; +} + +static void chacha_set_counter(ref ChaChaContext ctx, ulong counter) pure +{ + ctx.state[12] = cast(uint)counter; + ctx.state[13] = ctx.nonceLow | (counter >> 32); +} + +void chacha_free_context(ref ChaChaContext ctx) pure +{ + ctx.state[] = 0; + ctx.keystream32[] = 0; + ctx.position = 0; + ctx.nonceLow = 0; + ctx.rounds = 0; +} + +size_t chacha_update(ref ChaChaContext ctx, const ubyte[] input, ubyte[] output) pure +{ + import urt.util : min; + + size_t size = input.length; + assert(output.length >= size); + + size_t offset = 0; + if (ctx.position < 64) + { + size_t startBytes = min(size, 64 - ctx.position); + output[0 .. startBytes] = input[0 .. startBytes] ^ ctx.keystream8[ctx.position .. 64]; + ctx.position += startBytes; + size -= startBytes; + offset = startBytes; + } + while (size >= 64) + { + chacha_block_next(ctx); + output[offset .. offset + 64] = input[offset .. offset + 64] ^ ctx.keystream8[]; + offset += 64; + size -= 64; + } + if (size > 0) + { + chacha_block_next(ctx); + output[offset .. offset + size] = input[offset .. offset + size] ^ ctx.keystream8[0 .. size]; + ctx.position = cast(ushort)size; + } + + return input.length; +} + + +size_t chacha_crypt(const ubyte[] input, ubyte[] output, ref const ubyte[32] key, ulong nonce, ulong counter = 0) pure +{ + return chacha_crypt(input, output, key, expandNonce(nonce), counter); +} + +size_t chacha_crypt(const ubyte[] input, ubyte[] output, ref const ubyte[32] key, ref const ubyte[12] nonce, ulong counter = 0) pure +{ + assert(output.length >= input.length); + + ChaChaContext ctx; + ctx.chacha_init_context(key, nonce, counter); + size_t r = ctx.chacha_update(input, output); + ctx.chacha_free_context(); + return r; +} + + +private: + +ubyte[12] expandNonce(ulong nonce) pure +{ + ubyte[12] nonceBytes = void; + nonceBytes[0 .. 4] = 0; + nonceBytes[4 .. 12] = nativeToLittleEndian(nonce); + return nonceBytes; +} + +pragma(inline, true) +uint rotl32(int n)(uint x) pure + => (x << n) | (x >> (32 - n)); + +pragma(inline, true) +void chacha_quarter_round(uint a, uint b, uint c, uint d)(ref uint[16] state) pure +{ + state[a] += state[b]; state[d] = rotl32!16(state[d] ^ state[a]); + state[c] += state[d]; state[b] = rotl32!12(state[b] ^ state[c]); + state[a] += state[b]; state[d] = rotl32!8(state[d] ^ state[a]); + state[c] += state[d]; state[b] = rotl32!7(state[b] ^ state[c]); +} + +void chacha_block_next(ref ChaChaContext ctx) pure +{ + // this is where the crazy voodoo magic happens. + // mix the bytes a lot and hope that nobody finds out how to undo it. + ctx.keystream32 = ctx.state; + + // TODO: we might like to unroll this...? + foreach (i; 0 .. ctx.rounds) + { + chacha_quarter_round!(0, 4, 8, 12)(ctx.keystream32); + chacha_quarter_round!(1, 5, 9, 13)(ctx.keystream32); + chacha_quarter_round!(2, 6, 10, 14)(ctx.keystream32); + chacha_quarter_round!(3, 7, 11, 15)(ctx.keystream32); + + chacha_quarter_round!(0, 5, 10, 15)(ctx.keystream32); + chacha_quarter_round!(1, 6, 11, 12)(ctx.keystream32); + chacha_quarter_round!(2, 7, 8, 13)(ctx.keystream32); + chacha_quarter_round!(3, 4, 9, 14)(ctx.keystream32); + } + + ctx.keystream32[] += ctx.state[]; + + // increment counter + uint* counter = &ctx.state[12]; + counter[0]++; + if (counter[0] == 0) + { + // wrap around occured, increment higher 32 bits of counter + counter[1]++; + // limited to 2^64 blocks of 64 bytes each. + // if you want to process more than 1180591620717411303424 bytes, you have other problems. + // we could keep counting with counter[2] and counter[3] (nonce), but then we risk reusing the nonce which is very bad! + assert(counter[1] != 0); + } +} + + +unittest +{ + import urt.encoding; + + immutable ubyte[32] test1_key = HexDecode!"0000000000000000000000000000000000000000000000000000000000000000"; + immutable ubyte[12] test1_nonce = HexDecode!"000000000000000000000000"; + immutable ubyte[64] test1_input = 0; + immutable ubyte[64] test1_output = [ + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86 + ]; + + immutable ubyte[32] test2_key = HexDecode!"0000000000000000000000000000000000000000000000000000000000000001"; + immutable ubyte[12] test2_nonce = HexDecode!"000000000000000000000002"; + immutable ubyte[375] test2_input = [ + 0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d,0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45,0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66,0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61,0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72, + 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66,0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20, + 0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61,0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74,0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49,0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20,0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, + 0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49,0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e,0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20,0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45,0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20,0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20,0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63,0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61,0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, + 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f,0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61,0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f + ]; + immutable ubyte[375] test2_output = [ + 0xa3, 0xfb, 0xf0, 0x7d, 0xf3, 0xfa, 0x2f, 0xde,0x4f, 0x37, 0x6c, 0xa2, 0x3e, 0x82, 0x73, 0x70, + 0x41, 0x60, 0x5d, 0x9f, 0x4f, 0x4f, 0x57, 0xbd,0x8c, 0xff, 0x2c, 0x1d, 0x4b, 0x79, 0x55, 0xec, + 0x2a, 0x97, 0x94, 0x8b, 0xd3, 0x72, 0x29, 0x15,0xc8, 0xf3, 0xd3, 0x37, 0xf7, 0xd3, 0x70, 0x05, + 0x0e, 0x9e, 0x96, 0xd6, 0x47, 0xb7, 0xc3, 0x9f,0x56, 0xe0, 0x31, 0xca, 0x5e, 0xb6, 0x25, 0x0d, + 0x40, 0x42, 0xe0, 0x27, 0x85, 0xec, 0xec, 0xfa,0x4b, 0x4b, 0xb5, 0xe8, 0xea, 0xd0, 0x44, 0x0e, + 0x20, 0xb6, 0xe8, 0xdb, 0x09, 0xd8, 0x81, 0xa7,0xc6, 0x13, 0x2f, 0x42, 0x0e, 0x52, 0x79, 0x50, + 0x42, 0xbd, 0xfa, 0x77, 0x73, 0xd8, 0xa9, 0x05,0x14, 0x47, 0xb3, 0x29, 0x1c, 0xe1, 0x41, 0x1c, + 0x68, 0x04, 0x65, 0x55, 0x2a, 0xa6, 0xc4, 0x05,0xb7, 0x76, 0x4d, 0x5e, 0x87, 0xbe, 0xa8, 0x5a, + 0xd0, 0x0f, 0x84, 0x49, 0xed, 0x8f, 0x72, 0xd0,0xd6, 0x62, 0xab, 0x05, 0x26, 0x91, 0xca, 0x66, + 0x42, 0x4b, 0xc8, 0x6d, 0x2d, 0xf8, 0x0e, 0xa4,0x1f, 0x43, 0xab, 0xf9, 0x37, 0xd3, 0x25, 0x9d, + 0xc4, 0xb2, 0xd0, 0xdf, 0xb4, 0x8a, 0x6c, 0x91,0x39, 0xdd, 0xd7, 0xf7, 0x69, 0x66, 0xe9, 0x28, + 0xe6, 0x35, 0x55, 0x3b, 0xa7, 0x6c, 0x5c, 0x87,0x9d, 0x7b, 0x35, 0xd4, 0x9e, 0xb2, 0xe6, 0x2b, + 0x08, 0x71, 0xcd, 0xac, 0x63, 0x89, 0x39, 0xe2,0x5e, 0x8a, 0x1e, 0x0e, 0xf9, 0xd5, 0x28, 0x0f, + 0xa8, 0xca, 0x32, 0x8b, 0x35, 0x1c, 0x3c, 0x76,0x59, 0x89, 0xcb, 0xcf, 0x3d, 0xaa, 0x8b, 0x6c, + 0xcc, 0x3a, 0xaf, 0x9f, 0x39, 0x79, 0xc9, 0x2b,0x37, 0x20, 0xfc, 0x88, 0xdc, 0x95, 0xed, 0x84, + 0xa1, 0xbe, 0x05, 0x9c, 0x64, 0x99, 0xb9, 0xfd,0xa2, 0x36, 0xe7, 0xe8, 0x18, 0xb0, 0x4b, 0x0b, + 0xc3, 0x9c, 0x1e, 0x87, 0x6b, 0x19, 0x3b, 0xfe,0x55, 0x69, 0x75, 0x3f, 0x88, 0x12, 0x8c, 0xc0, + 0x8a, 0xaa, 0x9b, 0x63, 0xd1, 0xa1, 0x6f, 0x80,0xef, 0x25, 0x54, 0xd7, 0x18, 0x9c, 0x41, 0x1f, + 0x58, 0x69, 0xca, 0x52, 0xc5, 0xb8, 0x3f, 0xa3,0x6f, 0xf2, 0x16, 0xb9, 0xc1, 0xd3, 0x00, 0x62, + 0xbe, 0xbc, 0xfd, 0x2d, 0xc5, 0xbc, 0xe0, 0x91,0x19, 0x34, 0xfd, 0xa7, 0x9a, 0x86, 0xf6, 0xe6, + 0x98, 0xce, 0xd7, 0x59, 0xc3, 0xff, 0x9b, 0x64,0x77, 0x33, 0x8f, 0x3d, 0xa4, 0xf9, 0xcd, 0x85, + 0x14, 0xea, 0x99, 0x82, 0xcc, 0xaf, 0xb3, 0x41,0xb2, 0x38, 0x4d, 0xd9, 0x02, 0xf3, 0xd1, 0xab, + 0x7a, 0xc6, 0x1d, 0xd2, 0x9c, 0x6f, 0x21, 0xba,0x5b, 0x86, 0x2f, 0x37, 0x30, 0xe3, 0x7c, 0xfd, + 0xc4, 0xfd, 0x80, 0x6c, 0x22, 0xf2, 0x21 + ]; + + immutable ubyte[32] test3_key = HexDecode!"c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d"; + enum ulong test3_nonce = 0x218268cfd531da1a; + immutable ubyte[127] test3_input = 0; + immutable ubyte[127] test3_output = [ + 0xf6, 0x3a, 0x89, 0xb7, 0x5c, 0x22, 0x71, 0xf9,0x36, 0x88, 0x16, 0x54, 0x2b, 0xa5, 0x2f, 0x06, + 0xed, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2b, 0x00,0xb5, 0xe8, 0xf8, 0x0a, 0xe9, 0xa4, 0x73, 0xaf, + 0xc2, 0x5b, 0x21, 0x8f, 0x51, 0x9a, 0xf0, 0xfd,0xd4, 0x06, 0x36, 0x2e, 0x8d, 0x69, 0xde, 0x7f, + 0x54, 0xc6, 0x04, 0xa6, 0xe0, 0x0f, 0x35, 0x3f,0x11, 0x0f, 0x77, 0x1b, 0xdc, 0xa8, 0xab, 0x92, + 0xe5, 0xfb, 0xc3, 0x4e, 0x60, 0xa1, 0xd9, 0xa9,0xdb, 0x17, 0x34, 0x5b, 0x0a, 0x40, 0x27, 0x36, + 0x85, 0x3b, 0xf9, 0x10, 0xb0, 0x60, 0xbd, 0xf1,0xf8, 0x97, 0xb6, 0x29, 0x0f, 0x01, 0xd1, 0x38, + 0xae, 0x2c, 0x4c, 0x90, 0x22, 0x5b, 0xa9, 0xea,0x14, 0xd5, 0x18, 0xf5, 0x59, 0x29, 0xde, 0xa0, + 0x98, 0xca, 0x7a, 0x6c, 0xcf, 0xe6, 0x12, 0x27,0x05, 0x3c, 0x84, 0xe4, 0x9a, 0x4a, 0x33 + ]; + + ubyte[375] output = void; + + size_t ret = test1_input.chacha_crypt(output, test1_key, test1_nonce); + assert(ret == test1_input.length); + assert(output[0 .. test1_output.length] == test1_output[]); + + ChaChaContext ctx; + ctx.chacha_init_context(test2_key, test2_nonce, 1); + ret = ctx.chacha_update(test2_input[0 .. 17], output[0 .. 17]); + ret += ctx.chacha_update(test2_input[17 .. $], output[17 .. $]); + assert(ret == test2_input.length); + assert(output[0 .. test2_output.length] == test2_output[]); + + ret = test3_input.chacha_crypt(output, test3_key, test3_nonce); + assert(ret == test3_input.length); + assert(output[0 .. test3_output.length] == test3_output[]); +} diff --git a/src/urt/dbg.d b/src/urt/dbg.d index 8cc13d1..1b0465e 100644 --- a/src/urt/dbg.d +++ b/src/urt/dbg.d @@ -57,46 +57,3 @@ else version (ARM) } else static assert(0, "TODO: Unsupported architecture"); - - -private: - -package(urt) void setupAssertHandler() -{ - import core.exception : assertHandler; - assertHandler = &urt_assert; -} - -void urt_assert(string file, size_t line, string msg) nothrow @nogc -{ - import core.stdc.stdlib : exit; - - debug - { - import core.stdc.stdio; - - if (msg.length == 0) - msg = "Assertion failed"; - - version (Windows) - { - import core.sys.windows.winbase; - char[1024] buffer; - _snprintf(buffer.ptr, buffer.length, "%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); - OutputDebugStringA(buffer.ptr); - - // Windows can have it at stdout aswell? - printf("%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); - } - else - { - // TODO: write to stderr would be better... - printf("%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); - } - - breakpoint(); -// exit(-1); // TODO: what if some systems don't support a software breakpoint? - } - else - exit(-1); -} diff --git a/src/urt/digest/md5.d b/src/urt/digest/md5.d index ef75d98..e0a59d6 100644 --- a/src/urt/digest/md5.d +++ b/src/urt/digest/md5.d @@ -14,13 +14,13 @@ struct MD5Context enum uint[4] initState = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 ]; } -void md5Init(ref MD5Context ctx) +void md5_init(ref MD5Context ctx) { ctx.size = 0; ctx.buffer = MD5Context.initState; } -void md5Update(ref MD5Context ctx, const void[] input) +void md5_update(ref MD5Context ctx, const void[] input) { size_t offset = ctx.size % 64; ctx.size += input.length; @@ -41,7 +41,7 @@ void md5Update(ref MD5Context ctx, const void[] input) uint[16] tmp = void; foreach (uint j; 0 .. 16) tmp[j] = loadLittleEndian(cast(uint*)ctx.input.ptr + j); - md5Step(ctx.buffer, tmp); + md5_step(ctx.buffer, tmp); size_t tail = input.length - i; if (tail < 64) @@ -54,7 +54,7 @@ void md5Update(ref MD5Context ctx, const void[] input) } } -ubyte[16] md5Finalise(ref MD5Context ctx) +ubyte[16] md5_finalise(ref MD5Context ctx) { uint[16] tmp = void; uint offset = ctx.size % 64; @@ -67,7 +67,7 @@ ubyte[16] md5Finalise(ref MD5Context ctx) PADDING[1 .. padding_length] = 0; // Fill in the padding and undo the changes to size that resulted from the update - md5Update(ctx, PADDING[0 .. padding_length]); + md5_update(ctx, PADDING[0 .. padding_length]); ctx.size -= cast(ulong)padding_length; // Do a final update (internal to this function) @@ -78,7 +78,7 @@ ubyte[16] md5Finalise(ref MD5Context ctx) tmp[14] = cast(uint)(ctx.size*8); tmp[15] = (ctx.size*8) >> 32; - md5Step(ctx.buffer, tmp); + md5_step(ctx.buffer, tmp); uint[4] digest = void; foreach (uint k; 0 .. 4) @@ -91,14 +91,14 @@ unittest import urt.encoding; MD5Context ctx; - md5Init(ctx); - auto digest = md5Finalise(ctx); - assert(digest == Hex!"d41d8cd98f00b204e9800998ecf8427e"); - - md5Init(ctx); - md5Update(ctx, "Hello, World!"); - digest = md5Finalise(ctx); - assert(digest == Hex!"65a8e27d8879283831b664bd8b7f0ad4"); + md5_init(ctx); + auto digest = md5_finalise(ctx); + assert(digest == HexDecode!"d41d8cd98f00b204e9800998ecf8427e"); + + md5_init(ctx); + md5_update(ctx, "Hello, World!"); + digest = md5_finalise(ctx); + assert(digest == HexDecode!"65a8e27d8879283831b664bd8b7f0ad4"); } @@ -131,11 +131,11 @@ __gshared immutable uint[] K = [ ]; // rotates a 32-bit word left by n bits -uint rotateLeft(uint x, uint n) +uint rotate_left(uint x, uint n) => (x << n) | (x >> (32 - n)); // step on 512 bits of input with the main MD5 algorithm -void md5Step(ref uint[4] buffer, ref const uint[16] input) +void md5_step(ref uint[4] buffer, ref const uint[16] input) { uint AA = buffer[0]; uint BB = buffer[1]; @@ -171,7 +171,7 @@ void md5Step(ref uint[4] buffer, ref const uint[16] input) uint temp = DD; DD = CC; CC = BB; - BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]); + BB = BB + rotate_left(AA + E + K[i] + input[j], S[i]); AA = temp; } diff --git a/src/urt/digest/sha.d b/src/urt/digest/sha.d index 196c835..671a6fa 100644 --- a/src/urt/digest/sha.d +++ b/src/urt/digest/sha.d @@ -17,7 +17,7 @@ struct SHA1Context uint datalen; uint[DigestElements] state; - alias transform = sha1Transform; + alias transform = sha1_transform; enum uint[DigestElements] initState = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]; @@ -36,7 +36,7 @@ struct SHA256Context uint datalen; uint[DigestElements] state; - alias transform = sha256Transform; + alias transform = sha256_transform; enum uint[DigestElements] initState = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]; @@ -54,14 +54,14 @@ struct SHA256Context } -void shaInit(Context)(ref Context ctx) +void sha_init(Context)(ref Context ctx) { ctx.datalen = 0; ctx.bitlen = 0; ctx.state = Context.initState; } -void shaUpdate(Context)(ref Context ctx, const void[] input) +void sha_update(Context)(ref Context ctx, const void[] input) { const(ubyte)[] data = cast(ubyte[])input; @@ -82,7 +82,7 @@ void shaUpdate(Context)(ref Context ctx, const void[] input) } } -ubyte[Context.DigestLen] shaFinalise(Context)(ref Context ctx) +ubyte[Context.DigestLen] sha_finalise(Context)(ref Context ctx) { uint i = ctx.datalen; @@ -105,7 +105,7 @@ ubyte[Context.DigestLen] shaFinalise(Context)(ref Context ctx) // reverse all the bytes when copying the final state to the output hash. uint[Context.DigestElements] digest = void; foreach (uint j; 0 .. Context.DigestElements) - digest[j] = byteReverse(ctx.state[j]); + digest[j] = byte_reverse(ctx.state[j]); return cast(ubyte[Context.DigestLen])digest; } @@ -115,24 +115,24 @@ unittest import urt.encoding; SHA1Context ctx; - shaInit(ctx); - auto digest = shaFinalise(ctx); - assert(digest == Hex!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + sha_init(ctx); + auto digest = sha_finalise(ctx); + assert(digest == HexDecode!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - shaInit(ctx); - shaUpdate(ctx, "Hello, World!"); - digest = shaFinalise(ctx); - assert(digest == Hex!"0a0a9f2a6772942557ab5355d76af442f8f65e01"); + sha_init(ctx); + sha_update(ctx, "Hello, World!"); + digest = sha_finalise(ctx); + assert(digest == HexDecode!"0a0a9f2a6772942557ab5355d76af442f8f65e01"); SHA256Context ctx2; - shaInit(ctx2); - auto digest2 = shaFinalise(ctx2); - assert(digest2 == Hex!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - - shaInit(ctx2); - shaUpdate(ctx2, "Hello, World!"); - digest2 = shaFinalise(ctx2); - assert(digest2 == Hex!"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"); + sha_init(ctx2); + auto digest2 = sha_finalise(ctx2); + assert(digest2 == HexDecode!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + sha_init(ctx2); + sha_update(ctx2, "Hello, World!"); + digest2 = sha_finalise(ctx2); + assert(digest2 == HexDecode!"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"); } @@ -141,7 +141,7 @@ private: uint ROTLEFT(uint a, uint b) => (a << b) | (a >> (32 - b)); uint ROTRIGHT(uint a, uint b) => (a >> b) | (a << (32 - b)); -void sha1Transform(ref SHA1Context ctx, const ubyte[] data) +void sha1_transform(ref SHA1Context ctx, const ubyte[] data) { uint a, b, c, d, e, i, j, t; uint[80] m = void; @@ -204,7 +204,7 @@ void sha1Transform(ref SHA1Context ctx, const ubyte[] data) ctx.state[4] += e; } -void sha256Transform(ref SHA256Context ctx, const ubyte[] data) +void sha256_transform(ref SHA256Context ctx, const ubyte[] data) { static uint CH(uint x, uint y, uint z) => (x & y) ^ (~x & z); static uint MAJ(uint x, uint y, uint z) => (x & y) ^ (x & z) ^ (y & z); diff --git a/src/urt/encoding.d b/src/urt/encoding.d index 36ced1b..9f76aa0 100644 --- a/src/urt/encoding.d +++ b/src/urt/encoding.d @@ -3,12 +3,16 @@ module urt.encoding; nothrow @nogc: -enum Hex(const char[] s) = (){ ubyte[s.length / 2] r; ptrdiff_t len = hex_decode(s, r); assert(len == r.sizeof, "Not a hex string!"); return r; }(); -enum Base64(const char[] s) = (){ ubyte[base64_decode_length(s)] r; ptrdiff_t len = base64_decode(s, r); assert(len == r.sizeof, "Not a base64 string!"); return r; }(); +enum Base64Decode(string str) = () { ubyte[base64_decode_length(str.length)] r; size_t len = base64_decode(str, r[]); assert(len == r.sizeof, "Not a base64 string: " ~ str); return r; }(); +enum HexDecode(string str) = () { ubyte[hex_decode_length(str.length)] r; size_t len = hex_decode(str, r[]); assert(len == r.sizeof, "Not a hex string: " ~ str); return r; }(); +enum URLDecode(string str) = () { char[url_decode_length(str)] r; size_t len = url_decode(str, r[]); assert(len == r.sizeof, "Not a URL encoded string: " ~ str); return r; }(); -ptrdiff_t base64_encode_length(size_t sourceLength) pure - => (sourceLength + 2) / 3 * 4; +size_t base64_encode_length(size_t source_length) pure + => (source_length + 2) / 3 * 4; + +size_t base64_encode_length(const void[] data) pure + => base64_encode_length(data.length); ptrdiff_t base64_encode(const void[] data, char[] result) pure { @@ -54,8 +58,11 @@ ptrdiff_t base64_encode(const void[] data, char[] result) pure return out_len; } -ptrdiff_t base64_decode_length(size_t sourceLength) pure -=> sourceLength / 4 * 3; +size_t base64_decode_length(size_t source_length) pure + => source_length / 4 * 3; + +size_t base64_decode_length(const char[] data) pure + => base64_decode_length(data.length); ptrdiff_t base64_decode(const char[] data, void[] result) pure { @@ -74,6 +81,9 @@ ptrdiff_t base64_decode(const char[] data, void[] result) pure size_t j = 0; while (i < len) { + if (i > len - 4) + return -1; + // TODO: this could be faster by using more memory, store a full 256-byte table and no comparisons... uint b0 = data[i++] - 43; uint b1 = data[i++] - 43; @@ -88,10 +98,10 @@ ptrdiff_t base64_decode(const char[] data, void[] result) pure if (b3 >= 80) return -1; - b0 = base64_map.ptr[b0]; - b1 = base64_map.ptr[b1]; - b2 = base64_map.ptr[b2]; - b3 = base64_map.ptr[b3]; + b0 = base64_map[b0]; + b1 = base64_map[b1]; + b2 = base64_map[b2]; + b3 = base64_map[b3]; dest[j++] = cast(ubyte)((b0 << 2) | (b1 >> 4)); if (b2 != 64) @@ -109,6 +119,8 @@ unittest char[16] encoded = void; ubyte[12] decoded = void; + static assert(Base64Decode!"AQIDBAUGBwgJCgsM" == data[]); + size_t len = base64_encode(data, encoded); assert(len == 16); assert(encoded == "AQIDBAUGBwgJCgsM"); @@ -129,10 +141,13 @@ unittest len = base64_decode(encoded, decoded); assert(len == 10); assert(data[0..10] == decoded[0..10]); - -// static assert(Base64!"012345" == [0x01, 0x23, 0x45]); } +size_t hex_encode_length(size_t sourceLength) pure + => sourceLength * 2; + +size_t hex_encode_length(const void[] data) pure + => data.length * 2; ptrdiff_t hex_encode(const void[] data, char[] result) pure { @@ -142,9 +157,15 @@ ptrdiff_t hex_encode(const void[] data, char[] result) pure return toHexString(data, result).length; } +size_t hex_decode_length(size_t sourceLength) pure + => sourceLength / 2; + +size_t hex_decode_length(const char[] data) pure + => data.length / 2; + ptrdiff_t hex_decode(const char[] data, void[] result) pure { - import urt.string.ascii : isHex; + import urt.string.ascii : is_hex; if (data.length & 1) return -1; @@ -157,7 +178,7 @@ ptrdiff_t hex_decode(const char[] data, void[] result) pure { ubyte c0 = data[i]; ubyte c1 = data[i + 1]; - if (!c0.isHex || !c1.isHex) + if (!c0.is_hex || !c1.is_hex) return -1; if ((c0 | 0x20) >= 'a') @@ -180,25 +201,25 @@ unittest char[24] encoded = void; ubyte[12] decoded = void; + static assert(HexDecode!"0102030405060708090A0B0C" == data); + size_t len = hex_encode(data, encoded); assert(len == 24); assert(encoded == "0102030405060708090A0B0C"); len = hex_decode(encoded, decoded); assert(len == 12); assert(data == decoded); - - static assert(Hex!"012345" == [0x01, 0x23, 0x45]); } -ptrdiff_t url_encode_length(const char[] data) pure +size_t url_encode_length(const char[] data) pure { - import urt.string.ascii : isURL; + import urt.string.ascii : is_url; size_t len = 0; foreach (c; data) { - if (c.isURL || c == ' ') + if (c.is_url || c == ' ') ++len; else len += 3; @@ -208,14 +229,14 @@ ptrdiff_t url_encode_length(const char[] data) pure ptrdiff_t url_encode(const char[] data, char[] result) pure { - import urt.string.ascii : isURL, hexDigits; + import urt.string.ascii : is_url, hex_digits; size_t j = 0; for (size_t i = 0; i < data.length; ++i) { char c = data[i]; - if (c.isURL || c == ' ') + if (c.is_url || c == ' ') { if (j == result.length) return -1; @@ -226,15 +247,15 @@ ptrdiff_t url_encode(const char[] data, char[] result) pure if (j + 2 == result.length) return -1; result[j++] = '%'; - result[j++] = hexDigits[c >> 4]; - result[j++] = hexDigits[c & 0xF]; + result[j++] = hex_digits[c >> 4]; + result[j++] = hex_digits[c & 0xF]; } } return j; } -ptrdiff_t url_decode_length(const char[] data) pure +size_t url_decode_length(const char[] data) pure { size_t len = 0; for (size_t i = 0; i < data.length;) @@ -250,7 +271,7 @@ ptrdiff_t url_decode_length(const char[] data) pure ptrdiff_t url_decode(const char[] data, char[] result) pure { - import urt.string.ascii : isHex; + import urt.string.ascii : is_hex; size_t j = 0; for (size_t i = 0; i < data.length; ++i) @@ -268,7 +289,7 @@ ptrdiff_t url_decode(const char[] data, char[] result) pure ubyte c0 = data[i + 1]; ubyte c1 = data[i + 2]; - if (!c0.isHex || !c1.isHex) + if (!c0.is_hex || !c1.is_hex) return -1; i += 2; @@ -290,6 +311,8 @@ ptrdiff_t url_decode(const char[] data, char[] result) pure unittest { + static assert(URLDecode!"Hello%2C+World%21" == "Hello, World!"); + char[13] data = "Hello, World!"; char[17] encoded = void; char[13] decoded = void; diff --git a/src/urt/endian.d b/src/urt/endian.d index e76f671..2ee3da5 100644 --- a/src/urt/endian.d +++ b/src/urt/endian.d @@ -4,20 +4,20 @@ import urt.processor; import urt.traits; public import urt.processor : LittleEndian; -public import urt.util : byteReverse; +public import urt.util : byte_reverse; pure nothrow @nogc: // load from byte arrays pragma(inline, true) T endianToNative(T, bool little)(ref const ubyte[1] bytes) - if (T.sizeof == 1 && isIntegral!T) + if (T.sizeof == 1 && is_integral!T) { return cast(T)bytes[0]; } ushort endianToNative(T, bool little)(ref const ubyte[2] bytes) - if (T.sizeof == 2 && isIntegral!T) + if (T.sizeof == 2 && is_integral!T) { if (__ctfe || !SupportUnalignedLoadStore) { @@ -33,12 +33,12 @@ ushort endianToNative(T, bool little)(ref const ubyte[2] bytes) static if (LittleEndian == little) return *cast(ushort*)bytes.ptr; else - return byteReverse(*cast(ushort*)bytes.ptr); + return byte_reverse(*cast(ushort*)bytes.ptr); } } uint endianToNative(T, bool little)(ref const ubyte[4] bytes) - if (T.sizeof == 4 && isIntegral!T) + if (T.sizeof == 4 && is_integral!T) { if (__ctfe || !SupportUnalignedLoadStore) { @@ -54,12 +54,12 @@ uint endianToNative(T, bool little)(ref const ubyte[4] bytes) static if (LittleEndian == little) return *cast(uint*)bytes.ptr; else - return byteReverse(*cast(uint*)bytes.ptr); + return byte_reverse(*cast(uint*)bytes.ptr); } } ulong endianToNative(T, bool little)(ref const ubyte[8] bytes) - if (T.sizeof == 8 && isIntegral!T) + if (T.sizeof == 8 && is_integral!T) { if (__ctfe || !SupportUnalignedLoadStore) { @@ -75,15 +75,15 @@ ulong endianToNative(T, bool little)(ref const ubyte[8] bytes) static if (LittleEndian == little) return *cast(ulong*)bytes.ptr; else - return byteReverse(*cast(ulong*)bytes.ptr); + return byte_reverse(*cast(ulong*)bytes.ptr); } } pragma(inline, true) T endianToNative(T, bool little)(ref const ubyte[T.sizeof] bytes) - if (!isIntegral!T && !is(T == struct) && !is(T == U[N], U, size_t N)) + if (!is_integral!T && !is(T == struct) && !is(T == U[N], U, size_t N)) { - import urt.meta : intForWidth; - alias U = intForWidth!(T.sizeof*8); + import urt.meta : IntForWidth; + alias U = IntForWidth!(T.sizeof*8); U u = endianToNative!(U, little)(bytes); return *cast(T*)&u; } @@ -96,12 +96,12 @@ T endianToNative(T, bool little)(ref const ubyte[T.sizeof] bytes) static assert(!is(U == class) && !is(U == interface) && !is(U == V*, V), T.stringof ~ " is not POD"); static if (U.sizeof == 1) - r = *cast(T*)&bytes; + return *cast(T*)&bytes; else { T r; - for (size_t i = 0, j = 0; i < N; ++i, j += T.sizeof) - r[i] = endianToNative!(U, little)(bytes.ptr[j .. j + T.sizeof][0 .. T.sizeof]); + for (size_t i = 0, j = 0; i < N; ++i, j += U.sizeof) + r[i] = endianToNative!(U, little)(bytes.ptr[j .. j + U.sizeof][0 .. U.sizeof]); return r; } } @@ -156,7 +156,7 @@ ubyte[2] nativeToEndian(bool little)(ushort u) static if (SupportUnalignedLoadStore) { static if (LittleEndian != little) - u = byteReverse(u); + u = byte_reverse(u); else pragma(inline, true); return *cast(ubyte[2]*)&u; @@ -176,7 +176,7 @@ ubyte[4] nativeToEndian(bool little)(uint u) static if (SupportUnalignedLoadStore) { static if (LittleEndian != little) - u = byteReverse(u); + u = byte_reverse(u); else pragma(inline, true); return *cast(ubyte[4]*)&u; @@ -196,7 +196,7 @@ ubyte[8] nativeToEndian(bool little)(ulong u) static if (SupportUnalignedLoadStore) { static if (LittleEndian != little) - u = byteReverse(u); + u = byte_reverse(u); else pragma(inline, true); return *cast(ubyte[8]*)&u; @@ -204,12 +204,11 @@ ubyte[8] nativeToEndian(bool little)(ulong u) } pragma(inline, true) auto nativeToEndian(bool little, T)(T val) - if (!isIntegral!T && !is(T == struct) && !is(T == U[N], U, size_t N)) + if (!is_integral!T && !is(T == struct) && !is(T == U[N], U, size_t N)) { - import urt.meta : intForWidth; - alias U = intForWidth!(T.sizeof*8); - U r = nativeToEndian!little(*cast(U*)&val); - return *cast(T*)&r; + import urt.meta : IntForWidth; + alias U = IntForWidth!(T.sizeof*8); + return nativeToEndian!little(*cast(U*)&val); } ubyte[T.sizeof] nativeToEndian(bool little, T)(auto ref const T data) @@ -261,36 +260,36 @@ ubyte[T.sizeof] nativeToLittleEndian(T)(auto ref const T data) // load/store from/to memory void storeBigEndian(T)(T* target, const T val) - if (isSomeInt!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (BigEndian) *target = val; else - *target = byteReverse(val); + *target = byte_reverse(val); } void storeLittleEndian(T)(T* target, const T val) - if (isSomeInt!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (LittleEndian) *target = val; else - *target = byteReverse(val); + *target = byte_reverse(val); } T loadBigEndian(T)(const(T)* src) - if (isSomeInt!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (BigEndian) return *src; else - return byteReverse(*src); + return byte_reverse(*src); } T loadLittleEndian(T)(const(T)* src) - if (isSomeInt!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (LittleEndian) return *src; else - return byteReverse(*src); + return byte_reverse(*src); } diff --git a/src/urt/exception.d b/src/urt/exception.d new file mode 100644 index 0000000..84decb4 --- /dev/null +++ b/src/urt/exception.d @@ -0,0 +1,58 @@ +/// Assert handler registration for uRT. +module urt.exception; + +nothrow @nogc: + + +alias AssertHandler = void function(string file, size_t line, string msg) nothrow @nogc; + +AssertHandler assert_handler() @property nothrow @nogc @trusted + => _assert_handler; + +void assert_handler(AssertHandler handler) @property nothrow @nogc @trusted +{ + if (handler is null) + _assert_handler = &urt_assert; + else + _assert_handler = handler; +} + + +private: + +__gshared AssertHandler _assert_handler = &urt_assert; + +void urt_assert(string file, size_t line, string msg) nothrow @nogc +{ + import urt.internal.stdc : exit; + + debug + { + import urt.internal.stdc; + import urt.dbg; + + if (msg.length == 0) + msg = "Assertion failed"; + + version (Windows) + { + import urt.internal.sys.windows.winbase; + char[1024] buffer; + _snprintf(buffer.ptr, buffer.length, "%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); + OutputDebugStringA(buffer.ptr); + + // Windows can have it at stdout aswell? + printf("%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); + } + else + { + // TODO: write to stderr would be better... + printf("%.*s(%d): %.*s\n", cast(int)file.length, file.ptr, cast(int)line, cast(int)msg.length, msg.ptr); + } + + breakpoint(); +// exit(-1); // TODO: what if some systems don't support a software breakpoint? + } + else + exit(-1); +} diff --git a/src/urt/fibre.d b/src/urt/fibre.d index 574e551..66253fb 100644 --- a/src/urt/fibre.d +++ b/src/urt/fibre.d @@ -2,7 +2,7 @@ module urt.fibre; import urt.mem; import urt.time; -import urt.util : isAligned, max; +import urt.util : is_aligned, max; version (Windows) version = UseWindowsFibreAPI; @@ -49,7 +49,7 @@ struct Fibre mainFibre = co_active(); // TODO: i think it's a bug that this stuff isn't initialised! - isDelegate = false; + is_delegate = false; abortRequested = false; finished = true; // init in a state ready to be recycled... aborted = false; @@ -62,7 +62,7 @@ struct Fibre while (true) { try { - if (thisFibre.isDelegate) + if (thisFibre.is_delegate) { FibreEntryDelegate dg; dg.ptr = thisFibre.userData; @@ -84,6 +84,8 @@ struct Fibre } catch (Throwable e) { + import urt.log; + writeDebugf("fibre abort: {0}:{1} - {2}", e.file, e.line, e.msg); abort(); } @@ -104,7 +106,7 @@ struct Fibre { this(cast(FibreEntryFunc)fibreEntry.funcptr, yieldHandler, fibreEntry.ptr, stackSize); - isDelegate = true; + is_delegate = true; } this(FibreEntryFunc fibreEntry, YieldHandler yieldHandler, void* userData = null, size_t stackSize = DefaultStackSize) nothrow @@ -151,7 +153,7 @@ struct Fibre this.fibreEntry = cast(FibreEntryFunc)fibreEntry.funcptr; userData = fibreEntry.ptr; - isDelegate = true; + is_delegate = true; abortRequested = false; finished = false; aborted = false; @@ -163,7 +165,7 @@ struct Fibre this.fibreEntry = fibreEntry; this.userData = userData; - isDelegate = false; + is_delegate = false; abortRequested = false; finished = false; aborted = false; @@ -198,7 +200,7 @@ private: YieldHandler yieldHandler; cothread_t fibre; - bool isDelegate; + bool is_delegate; bool abortRequested; bool finished; bool aborted; @@ -342,7 +344,7 @@ alias coentry_t = void function() @nogc; version (UseWindowsFibreAPI) { - import core.sys.windows.winbase; + import urt.internal.sys.windows.winbase; version (X86_64) { @@ -570,7 +572,7 @@ else void co_init_stack(void* base, void* top, coentry_t entry) { - assert(isAligned!16(base) && isAligned!16(top), "Stack must be aligned to 16 bytes"); + assert(is_aligned!16(base) && is_aligned!16(top), "Stack must be aligned to 16 bytes"); void** sp = cast(void**)top; // seek to top of stack *--sp = &crash; // crash if entrypoint returns @@ -741,7 +743,7 @@ else void co_init_stack(void* base, void* top, coentry_t entry) { - assert(isAligned!16(base) && isAligned!16(top), "Stack must be aligned to 16 bytes"); + assert(is_aligned!16(base) && is_aligned!16(top), "Stack must be aligned to 16 bytes"); void** p = cast(void**)base; p[8] = cast(void*)top; // starting sp @@ -804,7 +806,7 @@ else void co_init_stack(void* base, void* top, coentry_t entry) { - assert(isAligned!16(base) && isAligned!16(top), "Stack must be aligned to 16 bytes"); + assert(is_aligned!16(base) && is_aligned!16(top), "Stack must be aligned to 16 bytes"); void** p = cast(void**)base; p[0] = cast(void*)top; // x16 (stack pointer) diff --git a/src/urt/file.d b/src/urt/file.d index e7ec4d3..a401c37 100644 --- a/src/urt/file.d +++ b/src/urt/file.d @@ -12,10 +12,10 @@ alias SystemTime = void; version(Windows) { - import core.sys.windows.winbase; - import core.sys.windows.windows; - import core.sys.windows.windef : MAX_PATH; - import core.sys.windows.winnt; + import urt.internal.sys.windows.winbase; + import urt.internal.sys.windows; + import urt.internal.sys.windows.windef : MAX_PATH; + import urt.internal.sys.windows.winnt; import urt.string : twstringz; // TODO: remove this when LDC/GDC are up to date... @@ -26,7 +26,7 @@ version(Windows) } else version (Posix) { - import core.stdc.errno; + import urt.internal.stdc; import core.sys.posix.dirent; import core.sys.posix.fcntl; import core.sys.posix.stdlib; @@ -133,17 +133,17 @@ Result delete_file(const(char)[] path) version (Windows) { if (!DeleteFileW(path.twstringz)) - return Win32Result(GetLastError()); + return getlasterror_result(); } else version (Posix) { if (unlink(path.tstringz) == -1) - return PosixResult(errno); + return errno_result(); } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result rename_file(const(char)[] oldPath, const(char)[] newPath) @@ -151,18 +151,18 @@ Result rename_file(const(char)[] oldPath, const(char)[] newPath) version (Windows) { if (!MoveFileW(oldPath.twstringz, newPath.twstringz)) - return Win32Result(GetLastError()); + return getlasterror_result(); } else version (Posix) { import core.sys.posix.stdio; if (int result = rename(oldPath.tstringz, newPath.tstringz)!= 0) - return PosixResult(result); + return posix_result(result); } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result copy_file(const(char)[] oldPath, const(char)[] newPath, bool overwriteExisting = false) @@ -170,7 +170,7 @@ Result copy_file(const(char)[] oldPath, const(char)[] newPath, bool overwriteExi version (Windows) { if (!CopyFileW(oldPath.twstringz, newPath.twstringz, !overwriteExisting)) - return Win32Result(GetLastError()); + return getlasterror_result(); } else version (Posix) { @@ -180,7 +180,7 @@ Result copy_file(const(char)[] oldPath, const(char)[] newPath, bool overwriteExi else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result get_path(ref const File file, ref char[] buffer) @@ -193,11 +193,11 @@ Result get_path(ref const File file, ref char[] buffer) DWORD dwPathLen = tmp.length - 1; DWORD result = GetFinalPathNameByHandleW(cast(HANDLE)file.handle, tmp.ptr, dwPathLen, FILE_NAME_OPENED); if (result == 0 || result > dwPathLen) - return Win32Result(GetLastError()); + return getlasterror_result(); - size_t pathLen = tmp[0..result].uniConvert(buffer); + size_t pathLen = tmp[0..result].uni_convert(buffer); if (!pathLen) - return InternalResult(InternalCode.BufferTooSmall); + return InternalResult.buffer_too_small; if (buffer.length >= 4 && buffer[0..4] == `\\?\`) buffer = buffer[4..pathLen]; else @@ -210,10 +210,10 @@ Result get_path(ref const File file, ref char[] buffer) char[PATH_MAX] src = void; int r = fcntl(file.fd, F_GETPATH, src.ptr); if (r == -1) - return PosixResult(errno); + return errno_result(); size_t l = strlen(src.ptr); if (l > buffer.length) - return InternalResult(InternalCode.BufferTooSmall); + return InternalResult.buffer_too_small; buffer[0..l] = src[0..l]; buffer = buffer[0..l]; } @@ -221,18 +221,18 @@ Result get_path(ref const File file, ref char[] buffer) { ptrdiff_t r = readlink(tconcat("/proc/self/fd/", file.fd, '\0').ptr, buffer.ptr, buffer.length); if (r == -1) - return PosixResult(errno); + return errno_result(); if (r == buffer.length) { // TODO: if r == buffer.length, truncation MAY have occurred, but also maybe not... // is there any way to fix this? for now, we'll just assume it did and return an error - return InternalResult(InternalCode.BufferTooSmall); + return InternalResult.buffer_too_small; } buffer = buffer[0..r]; } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result set_file_times(ref File file, const SystemTime* createTime, const SystemTime* accessTime, const SystemTime* writeTime); @@ -243,7 +243,7 @@ Result get_file_attributes(const(char)[] path, out FileAttributes outAttributes) { WIN32_FILE_ATTRIBUTE_DATA attrData = void; if (!GetFileAttributesExW(path.twstringz, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &attrData)) - return Win32Result(GetLastError()); + return getlasterror_result(); outAttributes.attributes = FileAttributeFlag.None; if ((attrData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN) @@ -270,7 +270,7 @@ Result get_file_attributes(const(char)[] path, out FileAttributes outAttributes) else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result get_attributes(ref const File file, out FileAttributes outAttributes) @@ -282,9 +282,9 @@ Result get_attributes(ref const File file, out FileAttributes outAttributes) FILE_BASIC_INFO basicInfo = void; FILE_STANDARD_INFO standardInfo = void; if (!GetFileInformationByHandleEx(cast(HANDLE)file.handle, FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo, &basicInfo, FILE_BASIC_INFO.sizeof)) - return Win32Result(GetLastError()); + return getlasterror_result(); if (!GetFileInformationByHandleEx(cast(HANDLE)file.handle, FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, &standardInfo, FILE_STANDARD_INFO.sizeof)) - return Win32Result(GetLastError()); + return getlasterror_result(); outAttributes.attributes = FileAttributeFlag.None; if ((basicInfo.FileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN) @@ -303,7 +303,7 @@ Result get_attributes(ref const File file, out FileAttributes outAttributes) outAttributes.accessTime = SysTime(basicInfo.LastAccessTime.QuadPart); outAttributes.writeTime = SysTime(basicInfo.LastWriteTime.QuadPart); - return Result.Success; + return Result.success; +/ } else version (Posix) @@ -314,14 +314,14 @@ Result get_attributes(ref const File file, out FileAttributes outAttributes) else static assert(0, "Not implemented"); - return InternalResult(InternalCode.Unsupported); + return InternalResult.unsupported; } void[] load_file(const(char)[] path, NoGCAllocator allocator = defaultAllocator()) { File f; Result r = f.open(path, FileOpenMode.ReadExisting); - if (!r && r.get_FileResult == FileResult.NotFound) + if (!r && r.file_result == FileResult.NotFound) return null; assert(r, "TODO: handle error"); ulong size = f.get_size(); @@ -379,7 +379,7 @@ Result open(ref File file, const(char)[] path, FileOpenMode mode, FileOpenFlags dwCreationDisposition = OPEN_ALWAYS; break; default: - return InternalResult(InternalCode.InvalidParameter); + return InternalResult.invalid_parameter; } uint dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; @@ -392,7 +392,7 @@ Result open(ref File file, const(char)[] path, FileOpenMode mode, FileOpenFlags file.handle = CreateFileW(path.twstringz, dwDesiredAccess, dwShareMode, null, dwCreationDisposition, dwFlagsAndAttributes, null); if (file.handle == INVALID_HANDLE_VALUE) - return Win32Result(GetLastError()); + return getlasterror_result(); if (mode == FileOpenMode.WriteAppend || mode == FileOpenMode.ReadWriteAppend) SetFilePointer(file.handle, 0, null, FILE_END); @@ -429,7 +429,7 @@ Result open(ref File file, const(char)[] path, FileOpenMode mode, FileOpenFlags flags = O_RDWR | O_APPEND | O_CREAT; break; default: - return InternalResult(InternalCode.InvalidParameter); + return InternalResult.invalid_parameter; } flags |= O_CLOEXEC; @@ -441,7 +441,7 @@ Result open(ref File file, const(char)[] path, FileOpenMode mode, FileOpenFlags int fd = core.sys.posix.fcntl.open(path.tstringz, flags, 0b110_110_110); if (fd < 0) - return PosixResult(errno); + return errno_result(); file.fd = fd; version (Darwin) { @@ -467,7 +467,7 @@ Result open(ref File file, const(char)[] path, FileOpenMode mode, FileOpenFlags else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } bool is_open(ref const File file) @@ -532,7 +532,7 @@ Result set_size(ref File file, ulong size) if (size > curFileSize) { if (!file.set_pos(curFileSize)) - return Win32Result(GetLastError()); + return getlasterror_result(); // zero-fill char[4096] buf = void; @@ -555,19 +555,19 @@ Result set_size(ref File file, ulong size) else { if (!file.set_pos(size)) - return Win32Result(GetLastError()); + return getlasterror_result(); if (!SetEndOfFile(file.handle)) - return Win32Result(GetLastError()); + return getlasterror_result(); } } else version (Posix) { if (ftruncate(file.fd, size)) - return PosixResult(errno); + return errno_result(); } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } ulong get_pos(ref const File file) @@ -593,17 +593,17 @@ Result set_pos(ref File file, ulong offset) LARGE_INTEGER liDistanceToMove = void; liDistanceToMove.QuadPart = offset; if (!SetFilePointerEx(file.handle, liDistanceToMove, null, FILE_BEGIN)) - return Win32Result(GetLastError()); + return getlasterror_result(); } else version (Posix) { off_t rc = lseek(file.fd, offset, SEEK_SET); if (rc < 0) - return PosixResult(errno); + return errno_result(); } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result read(ref File file, void[] buffer, out size_t bytesRead) @@ -616,7 +616,7 @@ Result read(ref File file, void[] buffer, out size_t bytesRead) if (!ReadFile(file.handle, buffer.ptr, cast(uint)min(buffer.length, uint.max), &dwBytesRead, null)) { DWORD lastError = GetLastError(); - return (lastError == ERROR_BROKEN_PIPE) ? Result.Success : Win32Result(lastError); + return (lastError == ERROR_BROKEN_PIPE) ? Result.success : win32_result(lastError); } bytesRead = dwBytesRead; } @@ -624,12 +624,12 @@ Result read(ref File file, void[] buffer, out size_t bytesRead) { ptrdiff_t n = core.sys.posix.unistd.read(file.fd, buffer.ptr, buffer.length); if (n < 0) - return PosixResult(errno); + return errno_result(); bytesRead = n; } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result read_at(ref File file, void[] buffer, ulong offset, out size_t bytesRead) @@ -637,7 +637,7 @@ Result read_at(ref File file, void[] buffer, ulong offset, out size_t bytesRead) version (Windows) { if (buffer.length > DWORD.max) - return InternalResult(InternalCode.InvalidParameter); + return InternalResult.invalid_parameter; OVERLAPPED o; o.Offset = cast(DWORD)offset; @@ -646,8 +646,9 @@ Result read_at(ref File file, void[] buffer, ulong offset, out size_t bytesRead) DWORD dwBytesRead; if (!ReadFile(file.handle, buffer.ptr, cast(DWORD)buffer.length, &dwBytesRead, &o)) { - if (GetLastError() != ERROR_HANDLE_EOF) - return Win32Result(GetLastError()); + Result error = getlasterror_result(); + if (error.systemCode != ERROR_HANDLE_EOF) + return error; } bytesRead = dwBytesRead; } @@ -655,12 +656,12 @@ Result read_at(ref File file, void[] buffer, ulong offset, out size_t bytesRead) { ssize_t n = pread(file.fd, buffer.ptr, buffer.length, offset); if (n < 0) - return PosixResult(errno); + return errno_result(); bytesRead = n; } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result write(ref File file, const(void)[] data, out size_t bytesWritten) @@ -669,19 +670,19 @@ Result write(ref File file, const(void)[] data, out size_t bytesWritten) { DWORD dwBytesWritten; if (!WriteFile(file.handle, data.ptr, cast(uint)data.length, &dwBytesWritten, null)) - return Win32Result(GetLastError()); + return getlasterror_result(); bytesWritten = dwBytesWritten; } else version (Posix) { ptrdiff_t n = core.sys.posix.unistd.write(file.fd, data.ptr, data.length); if (n < 0) - return PosixResult(errno); + return errno_result(); bytesWritten = n; } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result write_at(ref File file, const(void)[] data, ulong offset, out size_t bytesWritten) @@ -689,7 +690,7 @@ Result write_at(ref File file, const(void)[] data, ulong offset, out size_t byte version (Windows) { if (data.length > DWORD.max) - return InternalResult(InternalCode.InvalidParameter); + return InternalResult.invalid_parameter; OVERLAPPED o; o.Offset = cast(DWORD)offset; @@ -697,19 +698,19 @@ Result write_at(ref File file, const(void)[] data, ulong offset, out size_t byte DWORD dwBytesWritten; if (!WriteFile(file.handle, data.ptr, cast(DWORD)data.length, &dwBytesWritten, &o)) - return Win32Result(GetLastError()); + return getlasterror_result(); bytesWritten = dwBytesWritten; } else version (Posix) { ptrdiff_t n = pwrite(file.fd, data.ptr, data.length, offset); if (n < 0) - return PosixResult(errno); + return errno_result(); bytesWritten = n; } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } Result flush(ref File file) @@ -717,19 +718,19 @@ Result flush(ref File file) version (Windows) { if (!FlushFileBuffers(file.handle)) - return Win32Result(GetLastError()); + return getlasterror_result(); } else version (Posix) { if (fsync(file.fd)) - return PosixResult(errno); + return errno_result(); } else static assert(0, "Not implemented"); - return Result.Success; + return Result.success; } -FileResult get_FileResult(Result result) +FileResult file_result(Result result) { version (Windows) { @@ -771,42 +772,31 @@ Result get_temp_filename(ref char[] buffer, const(char)[] dstDir, const(char)[] wchar[MAX_PATH] tmp = void; if (!GetTempFileNameW(dstDir.twstringz, prefix.twstringz, 0, tmp.ptr)) - return Win32Result(GetLastError()); + return getlasterror_result(); size_t resLen = wcslen(tmp.ptr); - resLen = tmp[((dstDir.length == 0 && tmp[0] == '\\') ? 1 : 0)..resLen].uniConvert(buffer); + resLen = tmp[((dstDir.length == 0 && tmp[0] == '\\') ? 1 : 0)..resLen].uni_convert(buffer); if (resLen == 0) { DeleteFileW(tmp.ptr); - return InternalResult(InternalCode.BufferTooSmall); + return InternalResult.buffer_too_small; } buffer = buffer[0 .. resLen]; } else version (Posix) { // Construct a format string which will be the supplied dir with prefix and 8 generated random characters - char[] fn = tconcat(dstDir, (dstDir.length && dstDir[$-1] != '/') ? "/" : "", prefix, "XXXXXX\0"); + char[] fn = cast(char[])tconcat(dstDir, (dstDir.length && dstDir[$-1] != '/') ? "/" : "", prefix, "XXXXXX\0"); File file; file.fd = mkstemp(fn.ptr); if (file.fd == -1) - return PosixResult(errno); + return errno_result(); Result r = get_path(file, buffer); core.sys.posix.unistd.close(file.fd); return r; } else static assert(0, "Not implemented"); - return Result.Success; -} - -version (Windows) -{ - Result Win32Result(uint err) - => Result(err); -} -else version (Posix) -{ - Result PosixResult(int err) - => Result(err); + return Result.success; } diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 544fbdf..5cfd62c 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -5,6 +5,7 @@ import urt.conv; import urt.lifetime; import urt.kvp; import urt.mem.allocator; +import urt.si.unit; import urt.string; import urt.string.format; @@ -13,38 +14,19 @@ public import urt.variant; nothrow @nogc: -Variant parseJson(const(char)[] text) +Variant parse_json(const(char)[] text) { - return parseNode(text); + return parse_node(text); } -ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, uint level = 0, uint indent = 2) +ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, uint level = 0, uint indent = 2) { final switch (val.type) { case Variant.Type.Null: - if (!buffer.ptr) - return 4; - if (buffer.length < 4) - return -1; - buffer[0 .. 4] = "null"; - return 4; - - case Variant.Type.False: - if (!buffer.ptr) - return 5; - if (buffer.length < 5) - return -1; - buffer[0 .. 5] = "false"; - return 5; - case Variant.Type.True: - if (!buffer.ptr) - return 4; - if (buffer.length < 4) - return -1; - buffer[0 .. 4] = "true"; - return 4; + case Variant.Type.False: + return val.toString(buffer, null, null); case Variant.Type.Map: case Variant.Type.Array: @@ -76,9 +58,9 @@ ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, ui int inc = val.type == Variant.Type.Map ? 2 : 1; for (uint i = 0; i < val.count; i += inc) { - len += writeJson(val.value.n[i], null, dense, level + indent, indent); + len += write_json(val.value.n[i], null, dense, level + indent, indent); if (val.type == Variant.Type.Map) - len += writeJson(val.value.n[i + 1], null, dense, level + indent, indent); + len += write_json(val.value.n[i + 1], null, dense, level + indent, indent); } return len; } @@ -99,7 +81,7 @@ ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, ui if (!buffer.newline(written, level + indent)) return -1; } - ptrdiff_t len = writeJson(val.value.n[i], buffer[written .. $], dense, level + indent, indent); + ptrdiff_t len = write_json(val.value.n[i], buffer[written .. $], dense, level + indent, indent); if (len < 0) return -1; written += len; @@ -107,7 +89,7 @@ ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, ui { if (!buffer.append(written, ':') || (!dense && !buffer.append(written, ' '))) return -1; - len = writeJson(val.value.n[i + 1], buffer[written .. $], dense, level + indent, indent); + len = write_json(val.value.n[i + 1], buffer[written .. $], dense, level + indent, indent); if (len < 0) return -1; written += len; @@ -119,40 +101,159 @@ ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, ui return -1; return written; - case Variant.Type.String: + case Variant.Type.Buffer: + if (!val.isString) + { + import urt.encoding; + + // emit raw buffer as base64 + const data = val.asBuffer(); + size_t enc_len = base64_encode_length(data.length); + if (buffer.ptr) + { + if (buffer.length < 2 + enc_len) + return -1; + buffer[0] = '"'; + ptrdiff_t r = data.base64_encode(buffer[1 .. 1 + enc_len]); + if (r != enc_len) + return -2; + buffer[1 + enc_len] = '"'; + } + return 2 + enc_len; + } + const char[] s = val.asString(); - if (buffer.ptr) + + if (!buffer.ptr) { - if (buffer.length < s.length + 2) + size_t len = 0; + foreach (c; s) + { + if (c < 0x20) + { + if (c == '\n' || c == '\r' || c == '\t' || c == '\b' || c == '\f') + len += 2; + else + len += 6; + } + else if (c == '"' || c == '\\') + len += 2; + else + len += 1; + } + return len + 2; + } + + if (buffer.length < s.length + 2) + return -1; + + buffer[0] = '"'; + // escape strings + size_t offset = 1; + foreach (c; s) + { + char sub = void; + if (c < 0x20) + { + if (c == '\n') + sub = 'n'; + else if (c == '\r') + sub = 'r'; + else if (c == '\t') + sub = 't'; + else if (c == '\b') + sub = 'b'; + else if (c == '\f') + sub = 'f'; + else + { + if (buffer.length < offset + 7) + return -1; + buffer[offset .. offset + 4] = "\\u00"; + offset += 4; + buffer[offset++] = hex_digits[c >> 4]; + buffer[offset++] = hex_digits[c & 0xF]; + continue; + } + } + else if (c == '"' || c == '\\') + sub = c; + else + { + if (buffer.length < offset + 2) + return -1; + buffer[offset++] = c; + continue; + } + + // write escape sequence + if (buffer.length < offset + 3) return -1; - buffer[0] = '"'; - buffer[1 .. 1 + s.length] = s[]; - buffer[1 + s.length] = '"'; + buffer[offset++] = '\\'; + buffer[offset++] = sub; } - return s.length + 2; + buffer[offset++] = '"'; + return offset; case Variant.Type.Number: import urt.conv; - if (val.isQuantity()) - assert(false, "TODO: implement quantity formatting for JSON"); + char[] number_buffer = buffer; + bool is_q = val.isQuantity(); + size_t u_len = 0; + float pre_scale = void; + if (is_q) + { + if (buffer.ptr) + { + if (buffer.length < 15) // {"q":x,"u":"x"} + return -1; + number_buffer = buffer[5..$]; + } + u_len = val.get_unit.format_unit(null, pre_scale, true); // TODO: should we give false (force pre-scale) here? + if (u_len <= 0) + return u_len; + + // TODO: apply the prescale... (if we change the above bool to false) + } + size_t len = 0; if (val.isDouble()) - return val.asDouble().formatFloat(buffer); + len += val.asDouble().format_float(number_buffer); + else if (val.isUlong()) + len += val.asUlong().format_uint(number_buffer); + else + len += val.asLong().format_int(number_buffer); + if (len <= 0 || !is_q) + return len; - // TODO: parse args? - //format + size_t result_len = 5 + len + 6 + u_len + 2; // {"q":x,"u":"x"} + if (!buffer.ptr) + return result_len; + if (buffer.length < result_len) + return -1; - if (val.isUlong()) - return val.asUlong().formatUint(buffer); - return val.asLong().formatInt(buffer); + size_t offset = 5 + len; + buffer[0..5] = "{\"q\":"; + buffer[offset..offset + 6] = ",\"u\":\""; + val.get_unit.format_unit(buffer[offset + 6..$], pre_scale, true); // TODO: should we give false (force pre-scale) here? + buffer[result_len-2..result_len] = "\"}"; + return result_len; case Variant.Type.User: - // in order to text-ify a user type, we probably need a hash table of text-ify functions, which - // we can lookup by the typeId... - // the tricky bit is, we need to init the table based on instantiations of constructors for each T... - // maybe an object with static constructor for each instantiation, which will hook it into the table? - assert(false, "TODO..."); + // for custom types, we'll use the type's regular string format into a json string + if (!buffer.ptr) + return val.toString(null, null, null) + 2; + if (buffer.length < 1) + return -1; + buffer[0] = '\"'; + ptrdiff_t len = val.toString(buffer[1 .. $], null, null); + if (len < 0) + return len; + if (buffer.length < len + 2) + return -1; + buffer[1 + len] = '\"'; + return len + 2; } } @@ -179,7 +280,7 @@ unittest ] }`; - Variant root = parseJson(doc); + Variant root = parse_json(doc); // check the data was parsed correctly... assert(root["nothing"].isNull); @@ -197,18 +298,18 @@ unittest char[1024] buffer = void; // check the dense writer... - assert(root["children"].writeJson(null, true) == 61); - assert(root["children"].writeJson(buffer, true) == 61); + assert(root["children"].write_json(null, true) == 61); + assert(root["children"].write_json(buffer, true) == 61); assert(buffer[0 .. 61] == `[{"name":"Jane Doe", "age":12}, {"name":"Jack Doe", "age":8}]`); // check the expanded writer - assert(root["children"].writeJson(null, false, 0, 1) == 83); - assert(root["children"].writeJson(buffer, false, 0, 1) == 83); + assert(root["children"].write_json(null, false, 0, 1) == 83); + assert(root["children"].write_json(buffer, false, 0, 1) == 83); assert(buffer[0 .. 83] == "[\n {\n \"name\": \"Jane Doe\",\n \"age\": 12\n },\n {\n \"name\": \"Jack Doe\",\n \"age\": 8\n }\n]"); // check indentation works properly - assert(root["children"].writeJson(null, false, 0, 2) == 95); - assert(root["children"].writeJson(buffer, false, 0, 2) == 95); + assert(root["children"].write_json(null, false, 0, 2) == 95); + assert(root["children"].write_json(buffer, false, 0, 2) == 95); assert(buffer[0 .. 95] == "[\n {\n \"name\": \"Jane Doe\",\n \"age\": 12\n },\n {\n \"name\": \"Jack Doe\",\n \"age\": 8\n }\n]"); // fabricate a JSON object @@ -220,7 +321,7 @@ unittest assert(write[0].asInt == 42); assert(write[1]["wow"].isTrue); assert(write[1]["bogus"].asBool == false); - assert(write.writeJson(buffer, true) == 33); + assert(write.write_json(buffer, true) == 33); assert(buffer[0 .. 33] == "[42, {\"wow\":true, \"bogus\":false}]"); } @@ -244,7 +345,7 @@ ptrdiff_t newline(char[] buffer, ref ptrdiff_t offset, int level) return true; } -Variant parseNode(ref const(char)[] text) +Variant parse_node(ref const(char)[] text) { text = text.trimFront(); @@ -269,20 +370,56 @@ Variant parseNode(ref const(char)[] text) { assert(text.length > 1); size_t i = 1; + Array!(char, 0) tmp; // TODO: needs a generous stack buffer! while (i < text.length && text[i] != '"') { - // TODO: we need to collapse the escape sequence, so we need to copy the string somewhere >_< - // ...overwrite the source buffer? if (text[i] == '\\') { - assert(i + 1 < text.length); - i += 2; + if (tmp.empty) + { + tmp.reserve(256); + tmp = text[1 .. i]; + } + if (++i == text.length) + break; + if (text[i] == 'u') + { + import urt.conv : parse_uint; + if (++i + 4 >= text.length) + break; + size_t taken; + ulong code = text[i .. i + 4].parse_uint(&taken, 16); + if (taken != 4) + break; + i += 4; + dchar c = cast(dchar)code; + if ((c >> 11) == 0x1B) + { + if (code >= 0xDC00) + break; // low surrogate without preceding high surrogate + if (i + 6 >= text.length || text[i] != '\\' || text[i+1] != 'u') + break; + code = text[i + 2 .. i + 6].parse_uint(&taken, 16); + if (taken != 4 || (code >> 10) != 0x37) + break; + c = 0x10000 + ((c & 0x3FF) << 10 | (cast(uint)code & 0x3FF)); + i += 6; + } + tmp ~= c; + } + else + goto do_concat; + } + else if (!tmp.empty) + { + do_concat: + tmp ~= text[i++]; } else - i++; + ++i; } assert(i < text.length); - Variant node = Variant(text[1 .. i]); + Variant node = Variant(tmp.empty ? text[1 .. i] : tmp[]); text = text[i + 1 .. $]; return node; } @@ -307,7 +444,7 @@ Variant parseNode(ref const(char)[] text) else expectComma = true; - tmp ~= parseNode(text); + tmp ~= parse_node(text); if (!isArray) { assert(tmp.back().isString()); @@ -315,7 +452,7 @@ Variant parseNode(ref const(char)[] text) text = text.trimFront; assert(text.length > 0 && text[0] == ':'); text = text[1 .. $].trimFront; - tmp ~= parseNode(text); + tmp ~= parse_node(text); } } assert(text.length > 0); @@ -326,33 +463,30 @@ Variant parseNode(ref const(char)[] text) r.flags = Variant.Flags.Map; return r; } - else if (text[0].isNumeric || (text[0] == '-' && text.length > 1 && text[1].isNumeric)) + else if (text[0].is_numeric || (text[0] == '-' && text.length > 1 && text[1].is_numeric)) { - bool neg = text[0] == '-'; size_t taken = void; - ulong div = void; - ulong value = text[neg .. $].parseUintWithDecimal(div, &taken, 10); + int e = void; + long value = text.parse_int_with_exponent(e, &taken, 10); assert(taken > 0); - text = text[taken + neg .. $]; + text = text[taken .. $]; - if (div > 1) - { - double d = cast(double)value; - if (neg) - d = -d; - d /= div; - return Variant(d); - } - else + // let's work out if value*10^^e is an integer? + bool is_integer = e >= 0; + for (; e > 0; --e) { - if (neg) + if (value < 0 ? (value < long.min / 10) : (value > long.max / 10)) { - assert(value <= long.max + 1); - return Variant(-cast(long)value); + is_integer = false; + break; } - else - return Variant(value); + value *= 10; } + + if (is_integer) + return Variant(value); + else + return Variant(value * 10.0^^e); } else assert(false, "Invalid JSON!"); diff --git a/src/urt/hash.d b/src/urt/hash.d index 7c068e6..0068baa 100644 --- a/src/urt/hash.d +++ b/src/urt/hash.d @@ -6,10 +6,10 @@ version = BranchIsFasterThanMod; nothrow @nogc: -alias fnv1aHash = fnv1Hash!(uint, true); -alias fnv1aHash64 = fnv1Hash!(ulong, true); +alias fnv1a = fnv1!(uint, true); +alias fnv1a64 = fnv1!(ulong, true); -T fnv1Hash(T, bool alternate)(const ubyte[] s) pure nothrow @nogc +T fnv1(T, bool alternate)(const ubyte[] s) pure nothrow @nogc if (is(T == ushort) || is(T == uint) || is(T == ulong)) { static if (is(T == ushort)) @@ -47,7 +47,7 @@ T fnv1Hash(T, bool alternate)(const ubyte[] s) pure nothrow @nogc unittest { - enum hash = fnv1aHash(cast(ubyte[])"hello world"); + enum hash = fnv1a(cast(ubyte[])"hello world"); static assert(hash == 0xD58B3FA7); } diff --git a/src/urt/inet.d b/src/urt/inet.d index 969def7..be1bddc 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -11,11 +11,11 @@ nothrow @nogc: enum AddressFamily : byte { - Unknown = -1, - Unspecified = 0, - Unix, - IPv4, - IPv6, + unknown = -1, + unspecified = 0, + unix, + ipv4, + ipv6, } enum WellKnownPort : ushort @@ -34,8 +34,8 @@ enum WellKnownPort : ushort MDNS = 5353, } -enum IPAddr IPAddrLit(string addr) = () { IPAddr a; size_t taken = a.fromString(addr); assert(taken == addr.length, "Not an IPv4 address"); return a; }(); -//enum IPv6Addr IPv6AddrLit(string addr) = () { IPv6Addr a; size_t taken = a.fromString(addr); assert(taken == addr.length, "Not an IPv6 address"); return a; }(); +enum IPAddr IPAddrLit(string addr) = () { IPAddr a; size_t taken = a.fromString(addr); assert(taken == addr.length, "Not an ipv4 address"); return a; }(); +enum IPv6Addr IPv6AddrLit(string addr) = () { IPv6Addr a; size_t taken = a.fromString(addr); assert(taken == addr.length, "Not an ipv6 address"); return a; }(); struct IPAddr { @@ -56,13 +56,13 @@ nothrow @nogc: this.b = b; } - bool isMulticast() const pure + bool is_multicast() const pure => (b[0] & 0xF0) == 224; - bool isLoopback() const pure + bool is_loopback() const pure => b[0] == 127; - bool isLinkLocal() const pure + bool is_link_local() const pure => (b[0] == 169 && b[1] == 254); - bool isPrivate() const pure + bool is_private() const pure => (b[0] == 192 && b[1] == 168) || b[0] == 10 || (b[0] == 172 && (b[1] & 0xF) == 16); bool opCast(T : bool)() const pure @@ -74,6 +74,16 @@ nothrow @nogc: bool opEquals(const(ubyte)[4] bytes) const pure => b == bytes; + int opCmp(ref const IPAddr rhs) const pure + { + uint a = loadBigEndian(&address), b = loadBigEndian(&rhs.address); + if (a < b) + return -1; + else if (a > b) + return 1; + return 0; + } + IPAddr opUnary(string op : "~")() const pure { IPAddr r; @@ -97,26 +107,26 @@ nothrow @nogc: size_t toHash() const pure { - import urt.hash : fnv1aHash, fnv1aHash64; + import urt.hash : fnv1a, fnv1a64; static if (size_t.sizeof > 4) - return fnv1aHash64(b[]); + return fnv1a64(b[]); else - return fnv1aHash(b[]); + return fnv1a(b[]); } - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const pure + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure { - char[15] stackBuffer = void; - char[] tmp = buffer.length < stackBuffer.sizeof ? stackBuffer : buffer; + char[15] stack_buffer = void; + char[] tmp = buffer.length < stack_buffer.sizeof ? stack_buffer : buffer; size_t offset = 0; for (int i = 0; i < 4; i++) { if (i > 0) tmp[offset++] = '.'; - offset += b[i].formatInt(tmp[offset..$]); + offset += b[i].format_uint(tmp[offset..$]); } - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -129,22 +139,22 @@ nothrow @nogc: { ubyte[4] t; size_t offset = 0, len; - ulong i = s[offset..$].parseInt(&len); + ulong i = s[offset..$].parse_uint(&len); offset += len; if (len == 0 || i > 255 || s.length < offset + 1 || s[offset++] != '.') return -1; t[0] = cast(ubyte)i; - i = s[offset..$].parseInt(&len); + i = s[offset..$].parse_uint(&len); offset += len; if (len == 0 || i > 255 || s.length < offset + 1 || s[offset++] != '.') return -1; t[1] = cast(ubyte)i; - i = s[offset..$].parseInt(&len); + i = s[offset..$].parse_uint(&len); offset += len; if (len == 0 || i > 255 || s.length < offset + 1 || s[offset++] != '.') return -1; t[2] = cast(ubyte)i; - i = s[offset..$].parseInt(&len); + i = s[offset..$].parse_uint(&len); offset += len; if (len == 0 || i > 255) return -1; @@ -180,13 +190,13 @@ nothrow @nogc: this.s = s; } - bool isGlobal() const pure + bool is_global() const pure => (s[0] & 0xE000) == 0x2000; - bool isLinkLocal() const pure + bool is_link_local() const pure => (s[0] & 0xFFC0) == 0xFE80; - bool isMulticast() const pure + bool is_multicast() const pure => (s[0] & 0xFF00) == 0xFF00; - bool isUniqueLocal() const pure + bool is_unique_local() const pure => (s[0] & 0xFE00) == 0xFC00; bool opCast(T : bool)() const pure @@ -198,6 +208,18 @@ nothrow @nogc: bool opEquals(const(ushort)[8] words) const pure => s == words; + int opCmp(ref const IPv6Addr rhs) const pure + { + for (int i = 0; i < 8; i++) + { + if (s[i] < rhs.s[i]) + return -1; + else if (s[i] > rhs.s[i]) + return 1; + } + return 0; + } + IPv6Addr opUnary(string op : "~")() const pure { IPv6Addr r; @@ -206,7 +228,7 @@ nothrow @nogc: return r; } - IPv6Addr opBinary(string op)(const IPv6Addr rhs) pure + IPv6Addr opBinary(string op)(const IPv6Addr rhs) const pure if (op == "&" || op == "|" || op == "^") { IPv6Addr t; @@ -224,19 +246,19 @@ nothrow @nogc: size_t toHash() const pure { - import urt.hash : fnv1aHash, fnv1aHash64; + import urt.hash : fnv1a, fnv1a64; static if (size_t.sizeof > 4) - return fnv1aHash64(cast(ubyte[])s[]); + return fnv1a64(cast(ubyte[])s[]); else - return fnv1aHash(cast(ubyte[])s[]); + return fnv1a(cast(ubyte[])s[]); } - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const pure + ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] format_args) const pure { import urt.string.ascii; // find consecutive zeroes... - int skipFrom = 0; + int skip_from = 0; int[8] z; for (int i = 0; i < 8; i++) { @@ -247,15 +269,15 @@ nothrow @nogc: if (z[j] != 0) { ++z[j]; - if (z[j] > z[skipFrom]) - skipFrom = j; + if (z[j] > z[skip_from]) + skip_from = j; } else break; } z[i] = 1; - if (z[i] > z[skipFrom]) - skipFrom = i; + if (z[i] > z[skip_from]) + skip_from = i; } } @@ -266,16 +288,16 @@ nothrow @nogc: { if (i > 0) tmp[offset++] = ':'; - if (z[skipFrom] > 1 && i == skipFrom) + if (z[skip_from] > 1 && i == skip_from) { if (i == 0) tmp[offset++] = ':'; - i += z[skipFrom]; + i += z[skip_from]; if (i == 8) tmp[offset++] = ':'; continue; } - offset += s[i].formatInt(tmp[offset..$], 16); + offset += s[i].format_uint(tmp[offset..$], 16); ++i; } @@ -284,16 +306,52 @@ nothrow @nogc: if (buffer.length < offset) return -1; foreach (i, c; tmp[0 .. offset]) - buffer[i] = c.toLower; + buffer[i] = c.to_lower; } return offset; } ptrdiff_t fromString(const(char)[] str) { - ushort[8] t; - size_t offset = 0; - assert(false); + ushort[8][2] t = void; + ubyte[2] count; + int part = 0; + + size_t offset = 0, len; + while (offset < str.length) + { + if (offset < str.length - 1 && str[offset] == ':' && str[offset + 1] == ':') + { + if (part != 0) + return -1; + part = 1; + offset += 2; + if (offset == str.length) + break; + } + else if (count[part] > 0) + { + if (str[offset] != ':') + break; + if (++offset == str.length) + return -1; + } + if (str[offset] == ':') + return -1; + ulong i = str[offset..$].parse_uint(&len, 16); + if (len == 0) + break; + if (i > ushort.max || count[0] + count[1] == 8) + return -1; + t[part][count[part]++] = cast(ushort)i; + offset += len; + } + if (part == 0 && count[0] != 8) + return -1; + + s[0 .. count[0]] = t[0][0 .. count[0]]; + s[count[0] .. 8 - count[1]] = 0; + s[8 - count[1] .. 8] = t[1][0 .. count[1]]; return offset; } @@ -307,25 +365,25 @@ nothrow @nogc: auto __debugExpanded() => s[]; } -struct IPSubnet +struct IPNetworkAddress { nothrow @nogc: - enum multicast = IPSubnet(IPAddr(224, 0, 0, 0), 4); - enum loopback = IPSubnet(IPAddr(127, 0, 0, 0), 8); - enum linkLocal = IPSubnet(IPAddr(169, 254, 0, 0), 16); - enum privateA = IPSubnet(IPAddr(10, 0, 0, 0), 8); - enum privateB = IPSubnet(IPAddr(172, 16, 0, 0), 12); - enum privateC = IPSubnet(IPAddr(192, 168, 0, 0), 16); + enum multicast = IPNetworkAddress(IPAddr(224, 0, 0, 0), 4); + enum loopback = IPNetworkAddress(IPAddr(127, 0, 0, 0), 8); + enum linklocal = IPNetworkAddress(IPAddr(169, 254, 0, 0), 16); + enum private_a = IPNetworkAddress(IPAddr(10, 0, 0, 0), 8); + enum private_b = IPNetworkAddress(IPAddr(172, 16, 0, 0), 12); + enum private_c = IPNetworkAddress(IPAddr(192, 168, 0, 0), 16); // TODO: ya know, this is gonna align to 4-bytes anyway... // we could store the actual mask in the native endian, and then clz to recover the prefix len in one opcode IPAddr addr; IPAddr mask; - ubyte prefixLen() @property const pure + ubyte prefix_len() @property const pure => clz(~loadBigEndian(&mask.address)); - void prefixLen(ubyte len) @property pure + void prefix_len(ubyte len) @property pure { if (len == 0) mask.address = 0; @@ -333,36 +391,36 @@ nothrow @nogc: storeBigEndian(&mask.address, 0xFFFFFFFF << (32 - len)); } - this(IPAddr addr, ubyte prefixLen) + this(IPAddr addr, ubyte prefix_len) { this.addr = addr; - this.prefixLen = prefixLen; + this.prefix_len = prefix_len; } - IPAddr netMask() const pure + IPAddr net_mask() const pure => mask; bool contains(IPAddr ip) const pure - => (ip & netMask()) == addr; + => (ip & mask) == get_network(); - IPAddr getNetwork(IPAddr ip) const pure - => ip & mask; - IPAddr getLocal(IPAddr ip) const pure - => ip & ~mask; + IPAddr get_network() const pure + => addr & mask; + IPAddr get_local() const pure + => addr & ~mask; size_t toHash() const pure - => addr.toHash() ^ prefixLen; + => addr.toHash() ^ prefix_len; - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const pure + ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] format_args) const pure { - char[18] stackBuffer = void; - char[] tmp = buffer.length < stackBuffer.sizeof ? stackBuffer : buffer; + char[18] stack_buffer = void; + char[] tmp = buffer.length < stack_buffer.sizeof ? stack_buffer : buffer; size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefixLen.formatInt(tmp[offset..$]); + offset += prefix_len.format_uint(tmp[offset..$]); - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -378,11 +436,11 @@ nothrow @nogc: if (taken < 0 || s.length <= taken + 1 || s[taken++] != '/') return -1; size_t t; - ulong plen = s[taken..$].parseInt(&t); + ulong plen = s[taken..$].parse_uint(&t); if (t == 0 || plen > 32) return -1; addr = a; - prefixLen = cast(ubyte)plen; + prefix_len = cast(ubyte)plen; return taken + t; } @@ -395,63 +453,71 @@ nothrow @nogc: } } -struct IPv6Subnet +struct IPv6NetworkAddress { nothrow @nogc: - enum global = IPv6Subnet(IPv6Addr(0x2000, 0, 0, 0, 0, 0, 0, 0), 3); - enum linkLocal = IPv6Subnet(IPv6Addr(0xFE80, 0, 0, 0, 0, 0, 0, 0), 10); - enum multicast = IPv6Subnet(IPv6Addr(0xFF00, 0, 0, 0, 0, 0, 0, 0), 8); - enum uniqueLocal = IPv6Subnet(IPv6Addr(0xFC00, 0, 0, 0, 0, 0, 0, 0), 7); + enum global = IPv6NetworkAddress(IPv6Addr(0x2000, 0, 0, 0, 0, 0, 0, 0), 3); + enum linklocal = IPv6NetworkAddress(IPv6Addr(0xFE80, 0, 0, 0, 0, 0, 0, 0), 10); + enum multicast = IPv6NetworkAddress(IPv6Addr(0xFF00, 0, 0, 0, 0, 0, 0, 0), 8); + enum uniquelocal = IPv6NetworkAddress(IPv6Addr(0xFC00, 0, 0, 0, 0, 0, 0, 0), 7); IPv6Addr addr; - ubyte prefixLen; + ubyte prefix_len; - this(IPv6Addr addr, ubyte prefixLen) + this(IPv6Addr addr, ubyte prefix_len) { this.addr = addr; - this.prefixLen = prefixLen; + this.prefix_len = prefix_len; } - IPv6Addr netMask() const pure + IPv6Addr net_mask() const pure { IPv6Addr r; - int i, j = prefixLen / 16; + int i, j = prefix_len / 16; while (i < j) r.s[i++] = 0xFFFF; - r.s[i++] = cast(ushort)(0xFFFF << (16 - (prefixLen % 16))); - while (i < 8) r.s[i++] = 0; + if (j < 8) + { + r.s[i++] = cast(ushort)(0xFFFF << (16 - (prefix_len % 16))); + while (i < 8) r.s[i++] = 0; + } return r; } bool contains(IPv6Addr ip) const pure { - uint n = prefixLen / 16; + uint n = prefix_len / 16; uint i = 0; for (; i < n; ++i) if (ip.s[i] != addr.s[i]) return false; - if (prefixLen % 16) + if (prefix_len % 16) { - uint s = 16 - (prefixLen % 16); + uint s = 16 - (prefix_len % 16); if (ip.s[i] >> s != addr.s[i] >> s) return false; } return true; } + IPv6Addr get_network() const pure + => addr & net_mask(); + IPv6Addr get_local() const pure + => addr & ~net_mask(); + size_t toHash() const pure - => addr.toHash() ^ prefixLen; + => addr.toHash() ^ prefix_len; - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const pure + ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] format_args) const pure { - char[42] stackBuffer = void; - char[] tmp = buffer.length < stackBuffer.sizeof ? stackBuffer : buffer; + char[42] stack_buffer = void; + char[] tmp = buffer.length < stack_buffer.sizeof ? stack_buffer : buffer; size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefixLen.formatInt(tmp[offset..$]); + offset += prefix_len.format_uint(tmp[offset..$]); - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -467,11 +533,11 @@ nothrow @nogc: if (taken < 0 || s.length <= taken + 1 || s[taken++] != '/') return -1; size_t t; - ulong plen = s[taken..$].parseInt(&t); - if (t == 0 || plen > 32) + ulong plen = s[taken..$].parse_uint(&t); + if (t == 0 || plen > 128) return -1; addr = a; - prefixLen = cast(ubyte)plen; + prefix_len = cast(ubyte)plen; return taken + t; } @@ -503,7 +569,7 @@ nothrow @nogc: { IPv6Addr addr; ushort port; - uint flowInfo; + uint flow_info; uint scopeId; } struct Addr @@ -517,37 +583,89 @@ nothrow @nogc: this(IPAddr addr, ushort port) { - family = AddressFamily.IPv4; + family = AddressFamily.ipv4; this._a.ipv4.addr = addr; this._a.ipv4.port = port; } - this(IPv6Addr addr, ushort port) + this(IPv6Addr addr, ushort port, int flow_info = 0, uint scopeId = 0) { - family = AddressFamily.IPv6; + family = AddressFamily.ipv6; this._a.ipv6.addr = addr; this._a.ipv6.port = port; + this._a.ipv6.flow_info = flow_info; + this._a.ipv6.scopeId = scopeId; + } + + inout(IPv4)* as_ipv4() inout pure + => family == AddressFamily.ipv4 ? &_a.ipv4 : null; + + inout(IPv6)* as_ipv6() inout pure + => family == AddressFamily.ipv6 ? &_a.ipv6 : null; + + bool opCast(T : bool)() const pure + => family > AddressFamily.Unspecified; + + bool opEquals(ref const InetAddress rhs) const pure + { + if (family != rhs.family) + return false; + switch (family) + { + case AddressFamily.ipv4: + return _a.ipv4 == rhs._a.ipv4; + case AddressFamily.ipv6: + return _a.ipv6 == rhs._a.ipv6; + default: + return true; + } + } + + int opCmp(ref const InetAddress rhs) const pure + { + if (family != rhs.family) + return family < rhs.family ? -1 : 1; + switch (family) + { + case AddressFamily.ipv4: + int c = _a.ipv4.addr.opCmp(rhs._a.ipv4.addr); + return c != 0 ? c : _a.ipv4.port - rhs._a.ipv4.port; + case AddressFamily.ipv6: + int c = _a.ipv6.addr.opCmp(rhs._a.ipv6.addr); + if (c != 0) + return c; + if (_a.ipv6.port == rhs._a.ipv6.port) + { + if (_a.ipv6.flow_info == rhs._a.ipv6.flow_info) + return _a.ipv6.scopeId - rhs._a.ipv6.scopeId; + return _a.ipv6.flow_info - rhs._a.ipv6.flow_info; + } + return _a.ipv6.port - rhs._a.ipv6.port; + default: + return 0; + } + return 0; } size_t toHash() const pure { - if (family == AddressFamily.IPv4) + if (family == AddressFamily.ipv4) return _a.ipv4.addr.toHash() ^ _a.ipv4.port; else return _a.ipv6.addr.toHash() ^ _a.ipv6.port; } - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const pure + ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] format_args) const pure { - char[47] stackBuffer = void; - char[] tmp = buffer.length < stackBuffer.sizeof ? stackBuffer : buffer; + char[47] stack_buffer = void; + char[] tmp = buffer.length < stack_buffer.sizeof ? stack_buffer : buffer; size_t offset = void; - if (family == AddressFamily.IPv4) + if (family == AddressFamily.ipv4) { offset = _a.ipv4.addr.toString(tmp, null, null); tmp[offset++] = ':'; - offset += _a.ipv4.port.formatInt(tmp[offset..$]); + offset += _a.ipv4.port.format_uint(tmp[offset..$]); } else { @@ -555,10 +673,10 @@ nothrow @nogc: offset = 1 + _a.ipv6.addr.toString(tmp[1 .. $], null, null); tmp[offset++] = ']'; tmp[offset++] = ':'; - offset += _a.ipv6.port.formatInt(tmp[offset..$]); + offset += _a.ipv6.port.format_uint(tmp[offset..$]); } - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -577,10 +695,10 @@ nothrow @nogc: // take address if (s.length >= 4 && (s[1] == '.' || s[2] == '.' || s[3] == '.')) - af = AddressFamily.IPv4; + af = AddressFamily.ipv4; else - af = AddressFamily.IPv6; - if (af == AddressFamily.IPv4) + af = AddressFamily.ipv6; + if (af == AddressFamily.ipv4) { taken = a4.fromString(s); if (taken < 0) @@ -602,8 +720,8 @@ nothrow @nogc: if (s.length > taken && s[taken] == ':') { size_t t; - ulong p = s[++taken..$].parseInt(&t); - if (t == 0 || p > 0xFFFF) + ulong p = s[++taken..$].parse_uint(&t); + if (t == 0 || p > ushort.max) return -1; taken += t; port = cast(ushort)p; @@ -611,7 +729,7 @@ nothrow @nogc: // success! store results.. family = af; - if (af == AddressFamily.IPv4) + if (af == AddressFamily.ipv4) { _a.ipv4.addr = a4; _a.ipv4.port = port; @@ -620,7 +738,7 @@ nothrow @nogc: { _a.ipv6.addr = a6; _a.ipv6.port = port; - _a.ipv6.flowInfo = 0; + _a.ipv6.flow_info = 0; _a.ipv6.scopeId = 0; } return taken; @@ -641,8 +759,12 @@ unittest char[64] tmp; assert(~IPAddr(255, 255, 248, 0) == IPAddr(0, 0, 7, 255)); + assert((IPAddr(255, 255, 248, 0) & IPAddr(255, 0, 255, 255)) == IPAddr(255, 0, 248, 0)); + assert((IPAddr(255, 255, 248, 0) | IPAddr(255, 0, 255, 255)) == IPAddr(255, 255, 255, 255)); assert((IPAddr(255, 255, 248, 0) ^ IPAddr(255, 0, 255, 255)) == IPAddr(0, 255, 7, 255)); - assert(IPSubnet(IPAddr(), 21).netMask() == IPAddr(0xFF, 0xFF, 0xF8, 0)); + assert(IPNetworkAddress(IPAddr(), 21).net_mask() == IPAddr(0xFF, 0xFF, 0xF8, 0)); + assert(IPNetworkAddress(IPAddr(192, 168, 0, 10), 24).get_network() == IPAddr(192, 168, 0, 0)); + assert(IPNetworkAddress(IPAddr(192, 168, 0, 10), 24).get_local() == IPAddr(0, 0, 0, 10)); assert(tmp[0 .. IPAddr(192, 168, 0, 1).toString(tmp, null, null)] == "192.168.0.1"); assert(tmp[0 .. IPAddr(0, 0, 0, 0).toString(tmp, null, null)] == "0.0.0.0"); @@ -653,16 +775,24 @@ unittest addr |= IPAddr(1, 2, 3, 4); assert(addr == IPAddr(1, 2, 3, 4)); - assert(tmp[0 .. IPSubnet(IPAddr(192, 168, 0, 0), 24).toString(tmp, null, null)] == "192.168.0.0/24"); - assert(tmp[0 .. IPSubnet(IPAddr(0, 0, 0, 0), 0).toString(tmp, null, null)] == "0.0.0.0/0"); + assert(tmp[0 .. IPNetworkAddress(IPAddr(192, 168, 0, 0), 24).toString(tmp, null, null)] == "192.168.0.0/24"); + assert(tmp[0 .. IPNetworkAddress(IPAddr(0, 0, 0, 0), 0).toString(tmp, null, null)] == "0.0.0.0/0"); - IPSubnet subnet; - assert(subnet.fromString("192.168.0.0/24") == 14 && subnet == IPSubnet(IPAddr(192, 168, 0, 0), 24)); - assert(subnet.fromString("0.0.0.0/0") == 9 && subnet == IPSubnet(IPAddr(0, 0, 0, 0), 0)); + IPNetworkAddress subnet; + assert(subnet.fromString("192.168.0.0/24") == 14 && subnet == IPNetworkAddress(IPAddr(192, 168, 0, 0), 24)); + assert(subnet.fromString("0.0.0.0/0") == 9 && subnet == IPNetworkAddress(IPAddr(0, 0, 0, 0), 0)); + assert(subnet.fromString("1.2.3.4") == -1); + assert(subnet.fromString("1.2.3.4/33") == -1); + assert(subnet.fromString("1.2.3.4/a") == -1); assert(~IPv6Addr(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFF0, 0, 0, 0) == IPv6Addr(0, 0, 0, 0, 0xF, 0xFFFF, 0xFFFF, 0xFFFF)); + assert((IPv6Addr(0xFFFF, 0, 1, 2, 3, 4, 5, 6) & IPv6Addr(0xFF00, 0, 3, 0, 0, 0, 0, 2)) == IPv6Addr(0xFF00, 0, 1, 0, 0, 0, 0, 2)); + assert((IPv6Addr(0xFFFF, 0, 1, 2, 3, 4, 5, 6) | IPv6Addr(0xFF00, 0, 3, 0, 0, 0, 0, 2)) == IPv6Addr(0xFFFF, 0, 3, 2, 3, 4, 5, 6)); assert((IPv6Addr(0xFFFF, 0, 1, 2, 3, 4, 5, 6) ^ IPv6Addr(0xFF00, 0, 3, 0, 0, 0, 0, 2)) == IPv6Addr(0xFF, 0, 2, 2, 3, 4, 5, 4)); - assert(IPv6Subnet(IPv6Addr(), 21).netMask() == IPv6Addr(0xFFFF, 0xF800, 0, 0, 0, 0, 0, 0)); + assert(IPv6NetworkAddress(IPv6Addr(), 21).net_mask() == IPv6Addr(0xFFFF, 0xF800, 0, 0, 0, 0, 0, 0)); + assert(IPv6NetworkAddress(IPv6Addr.loopback, 64).get_network() == IPv6Addr.any); + assert(IPv6NetworkAddress(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1), 32).get_network() == IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); + assert(IPv6NetworkAddress(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1), 32).get_local() == IPv6Addr(0, 0, 0, 1, 0, 0, 0, 1)); assert(tmp[0 .. IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1).toString(tmp, null, null)] == "2001:db8:0:1::1"); assert(tmp[0 .. IPv6Addr(0x2001, 0xdb8, 0, 0, 1, 0, 0, 1).toString(tmp, null, null)] == "2001:db8::1:0:0:1"); @@ -670,17 +800,40 @@ unittest assert(tmp[0 .. IPv6Addr(0, 0, 0, 0, 0, 0, 0, 1).toString(tmp, null, null)] == "::1"); assert(tmp[0 .. IPv6Addr(0, 0, 0, 0, 0, 0, 0, 0).toString(tmp, null, null)] == "::"); -// IPv6Addr addr6; -// assert(addr6.fromString("::2") == 3 && addr6 == IPv6Addr(0, 0, 0, 0, 0, 0, 0, 2)); -// assert(addr6.fromString("1::2") == 3 && addr6 == IPv6Addr(1, 0, 0, 0, 0, 0, 0, 2)); -// assert(addr6.fromString("2001:db8::1/24") == 14 && addr6 == IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); - - assert(tmp[0 .. IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 24).toString(tmp, null, null)] == "2001:db8::1/24"); - assert(tmp[0 .. IPv6Subnet(IPv6Addr(), 0).toString(tmp, null, null)] == "::/0"); - -// IPv6Subnet subnet6; -// assert(subnet6.fromString("2001:db8::1/24") == 14 && subnet6 == IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 24)); -// assert(subnet6.fromString("::/0") == 4 && subnet6 == IPv6Subnet(IPv6Addr(), 0)); + IPv6Addr addr6; + assert(addr6.fromString("::") == 2 && addr6.s[] == [0,0,0,0,0,0,0,0]); + assert(addr6.fromString("1::") == 3 && addr6.s[] == [1,0,0,0,0,0,0,0]); + assert(addr6.fromString("::2") == 3 && addr6.s[] == [0,0,0,0,0,0,0,2]); + assert(addr6.fromString("1::2") == 4 && addr6.s[] == [1,0,0,0,0,0,0,2]); + assert(addr6.fromString("1:FFFF::2") == 9 && addr6.s[] == [1,0xFFFF,0,0,0,0,0,2]); + assert(addr6.fromString("1:2:3:4:5:6:7:8") == 15 && addr6.s[] == [1,2,3,4,5,6,7,8]); + assert(addr6.fromString("1:2:3:4") == -1); + assert(addr6.fromString("1:2:3:4:5:6:7:8:9") == -1); + assert(addr6.fromString("1:2:3:4:5:6:7:8:") == -1); + assert(addr6.fromString("10000::2") == -1); + assert(addr6.fromString(":2") == -1); + assert(addr6.fromString("2:") == -1); + assert(addr6.fromString(":2:") == -1); + assert(addr6.fromString(":2::") == -1); + assert(addr6.fromString(":2::1") == -1); + assert(addr6.fromString("::2:") == -1); + assert(addr6.fromString("1::2:") == -1); + assert(addr6.fromString("1:::2") == -1); + assert(addr6.fromString("::G") == 2 && addr6 == IPv6Addr(0, 0, 0, 0, 0, 0, 0, 0)); + assert(addr6.fromString("1::2.3") == 4 && addr6 == IPv6Addr(1, 0, 0, 0, 0, 0, 0, 2)); + assert(addr6.fromString("1::2 4") == 4 && addr6 == IPv6Addr(1, 0, 0, 0, 0, 0, 0, 2)); + assert(addr6.fromString("2001:db8::1/24") == 11 && addr6 == IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); + + + assert(tmp[0 .. IPv6NetworkAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 24).toString(tmp, null, null)] == "2001:db8::1/24"); + assert(tmp[0 .. IPv6NetworkAddress(IPv6Addr(), 0).toString(tmp, null, null)] == "::/0"); + + IPv6NetworkAddress subnet6; + assert(subnet6.fromString("2001:db8::1/24") == 14 && subnet6 == IPv6NetworkAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 24)); + assert(subnet6.fromString("::/0") == 4 && subnet6 == IPv6NetworkAddress(IPv6Addr(), 0)); + assert(subnet6.fromString("1::2") == -1); + assert(subnet6.fromString("1::2/129") == -1); + assert(subnet6.fromString("1::2/a") == -1); assert(tmp[0 .. InetAddress(IPAddr(192, 168, 0, 1), 12345).toString(tmp, null, null)] == "192.168.0.1:12345"); assert(tmp[0 .. InetAddress(IPAddr(10, 0, 0, 0), 21).toString(tmp, null, null)] == "10.0.0.0:21"); @@ -692,6 +845,82 @@ unittest assert(address.fromString("192.168.0.1:21") == 14 && address == InetAddress(IPAddr(192, 168, 0, 1), 21)); assert(address.fromString("10.0.0.1:12345") == 14 && address == InetAddress(IPAddr(10, 0, 0, 1), 12345)); -// assert(address.fromString("[2001:db8:0:1::1]:12345") == 14 && address == InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1), 12345)); -// assert(address.fromString("[::]:21") == 14 && address == InetAddress(IPv6Addr(), 21)); + assert(address.fromString("[2001:db8:0:1::1]:12345") == 23 && address == InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1), 12345)); + assert(address.fromString("[::]:21") == 7 && address == InetAddress(IPv6Addr(), 21)); + assert(address.fromString("[::]:a") == -1); + assert(address.fromString("[::]:65536") == -1); + + // IPAddr sorting tests + { + IPAddr[8] expected = [ + IPAddr(0, 0, 0, 0), + IPAddr(1, 2, 3, 4), + IPAddr(1, 2, 3, 5), + IPAddr(1, 2, 4, 4), + IPAddr(10, 0, 0, 1), + IPAddr(127, 0, 0, 1), + IPAddr(192, 168, 1, 1), + IPAddr(255, 255, 255, 255), + ]; + + for (size_t i = 0; i < expected.length - 1; ++i) + { + assert(expected[i].opCmp(expected[i]) == 0, "IPAddr self-comparison failed"); + assert(expected[i].opCmp(expected[i+1]) < 0, "IPAddr sorting is incorrect"); + assert(expected[i+1].opCmp(expected[i]) > 0, "IPAddr sorting is incorrect"); + } + } + + // IPv6Addr sorting tests + { + IPv6Addr[14] expected = [ + IPv6Addr(0, 0, 0, 0, 0, 0, 0, 0), // :: + IPv6Addr(0, 0, 0, 0, 0, 0, 0, 1), // ::1 + IPv6Addr(0, 0, 0, 0, 0, 0, 0, 2), // ::2 + IPv6Addr(0, 0, 0, 0, 0, 0, 9, 0), // ::9:0 + IPv6Addr(0, 0, 0, 0, 0, 8, 0, 0), // ::8:0:0 + IPv6Addr(0, 0, 0, 0, 7, 0, 0, 0), // ::7:0:0:0 + IPv6Addr(0, 0, 0, 6, 0, 0, 0, 0), // 0:0:0:6:: + IPv6Addr(0, 0, 5, 0, 0, 0, 0, 0), // 0:0:5:: + IPv6Addr(0, 4, 0, 0, 0, 0, 0, 0), // 0:4:: + IPv6Addr(1, 0, 0, 0, 0, 0, 0, 0), // 1:: + IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), + IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2), + IPv6Addr(0xfe80, 0, 0, 0, 0, 0, 0, 1), + IPv6Addr(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), + ]; + + for (size_t i = 0; i < expected.length - 1; ++i) + { + assert(expected[i].opCmp(expected[i]) == 0, "IPv6Addr self-comparison failed"); + assert(expected[i].opCmp(expected[i+1]) < 0, "IPv6Addr sorting is incorrect"); + assert(expected[i+1].opCmp(expected[i]) > 0, "IPv6Addr sorting is incorrect"); + } + } + + // InetAddress sorting tests + { + InetAddress[10] expected = [ + // ipv4 sorted first + InetAddress(IPAddr(10, 0, 0, 1), 80), + InetAddress(IPAddr(127, 0, 0, 1), 8080), + InetAddress(IPAddr(192, 168, 1, 1), 80), + InetAddress(IPAddr(192, 168, 1, 1), 443), + + // ipv6 sorted next + InetAddress(IPv6Addr(1, 0, 0, 0, 0, 0, 0, 0), 1024), + InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 80, 0, 0), + InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 433, 1, 1), + InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0), // flow=0, scope=0 + InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 1), // flow=0, scope=1 + InetAddress(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 1, 0), // flow=1, scope=0 + ]; + + for (size_t i = 0; i < expected.length - 1; ++i) + { + assert(expected[i].opCmp(expected[i]) == 0, "InetAddress self-comparison failed"); + assert(expected[i].opCmp(expected[i+1]) < 0, "InetAddress sorting is incorrect"); + assert(expected[i+1].opCmp(expected[i]) > 0, "InetAddress sorting is incorrect"); + } + } } diff --git a/src/urt/internal/aa.d b/src/urt/internal/aa.d new file mode 100644 index 0000000..e7ae5d6 --- /dev/null +++ b/src/urt/internal/aa.d @@ -0,0 +1,1048 @@ +/** + * template implementation of associative arrays. + * + * Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak, Steven Schveighoffer, Rainer Schuetze + * + * Source: $(DRUNTIMESRC core/internal/_newaa.d) + * + * derived from rt/aaA.d + */ +module urt.internal.aa; + +/// AA version for debuggers, bump whenever changing the layout +immutable int _aaVersion = 1; + +import urt.internal.traits : substInout; + +// 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; + +/// AA wrapper +struct AA(K, V) +{ + Impl!(K,V)* impl; + alias impl this; + + @property bool empty() const pure nothrow @nogc @safe + { + pragma(inline, true); + return impl is null || !impl.length; + } + @property size_t length() const pure nothrow @nogc @safe + { + pragma(inline, true); + return impl is null ? 0 : impl.length; + } +} + +/// like urt.internal.traits.Unconst, but stripping inout, too +private template Unconstify(T : const U, U) +{ + static if (is(U == inout V, V)) + alias Unconstify = V; + else + alias Unconstify = U; +} + +ref _refAA(K, V)(ref V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(substInout!K, substInout!V)*)&aa); +} + +auto _toAA(K, V)(const V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(const(AA!(K, V))*)&aa); +} + +auto _toAA(K, V)(inout V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(inout(AA!(K, V))*)&aa); +} + +// for backward compatibility, but should be deprecated +auto _toAA(K, V)(shared const V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +// for backward compatibility, but should be deprecated +auto _toAA(K, V)(shared V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +// resolve ambiguity for immutable converting to const and shared const +auto _toAA(K, V)(immutable V[K] aa) @trusted +{ + pragma(inline, true); + return *(cast(AA!(K, V)*)&aa); +} + +static struct Entry(K, V) +{ + K key; + V value; +} + +// backward compatibility conversions +private ref compat_key(K, K2)(ref K2 key) +{ + pragma(inline, true); + static if (is(K2 == const(char)[]) && is(K == string)) + return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key); + else + return key; +} + +private void _aaMove(V)(ref V src, ref V dst) @trusted +{ + import urt.mem : memcpy, memset; + // move without postblit!? + memcpy(&dst, &src, V.sizeof); + static if (__traits(isZeroInit, V)) + memset(&src, 0, V.sizeof); + else + memcpy(&src, &V.init, V.sizeof); +} + +// mimick behaviour of rt.aaA for initialization +Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value) +{ + static if (__traits(compiles, new Entry!(K, V)(key, value))) + { + auto entry = new Entry!(K, V)(key, value); + } + else static if (__traits(compiles, { K k; new Entry!(K, V)(k); })) + { + auto entry = new Entry!(K, V)(key); + _aaMove(value, entry.value); + } + else + { + auto entry = new Entry!(K, V); + _aaMove(key, entry.key); + _aaMove(value, entry.value); + } + return entry; +} + +// mimick behaviour of rt.aaA for initialization +Entry!(K, V)* _newEntry(K, V, K2)(ref K2 key) +{ + static if (__traits(compiles, new Entry!(K, V)(key)) && + !(is(V == struct) && __traits(isNested, V))) // not detected by "compiles" + { + auto entry = new Entry!(K, V)(key); + } + else static if (__traits(compiles, { K2 k; new Entry!(K, V)(k, V.init); })) + { + // with disabled ctor for V + auto entry = new Entry!(K, V)(key, V.init); + } + else + { + // with disabled ctor for K and V + auto entry = new Entry!(K, V); + entry.key = key; + } + static if (!__traits(isZeroInit, V)) + { + () @trusted { (cast(ubyte*)&entry.value)[0..V.sizeof] = 0; }(); + } + return entry; +} + +template pure_hashOf(K) +{ + static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); })) + { + // avoid wrapper call in debug builds if pure nothrow @nogc is inferred + pragma(inline, true) + hash_t pure_hashOf(scope const ref K key) @trusted { return hashOf(cast()key); } + } + else + { + // for backward compatibility, do not require const in hashOf() + hash_t wrap_hashOf(K)(scope const ref K key) @trusted { return hashOf(cast()key); } + enum pure_hashOf = cast(hash_t function(scope ref const K key) pure nothrow @nogc @safe) &wrap_hashOf!K; + } +} + +// for backward compatibilty pretend the comparison is @safe, pure, etc +// this also breaks cyclic inference on recursive data types +template pure_keyEqual(K1, K2 = K1) +{ + static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; })) + { + // avoid wrapper call in debug builds if pure nothrow @nogc is inferred + pragma(inline, true) + bool pure_keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; } + } + else + { + bool keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; } + enum pure_keyEqual = cast(bool function(ref const K1, ref const K2) pure nothrow @nogc @safe) &keyEqual; + } +} + +private struct Impl(K, V) +{ +private: + alias Bucket = .Bucket!(K, V); + + this(size_t sz /* = INIT_NUM_BUCKETS */) nothrow + { + buckets = alloc_buckets(sz); + first_used = cast(uint) buckets.length; + + // only for binary compatibility + entry_ti = typeid(Entry!(K, V)); + hash_fn = delegate size_t (scope ref const K key) nothrow pure @nogc @safe { + return pure_hashOf!K(key); + }; + + key_sz = cast(uint) K.sizeof; + val_sz = cast(uint) V.sizeof; + val_off = cast(uint) talign(key_sz, V.alignof); + + enum ctflags = () { + import urt.internal.traits; + Impl.Flags flags; + static if (__traits(hasPostblit, K)) + flags |= flags.key_has_postblit; + static if (hasIndirections!K || hasIndirections!V) + flags |= flags.has_pointers; + return flags; + } (); + flags = ctflags; + } + + Bucket[] buckets; + uint used; + uint deleted; + const(TypeInfo) entry_ti; // only for binary compatibility + uint first_used; + immutable uint key_sz; // only for binary compatibility + immutable uint val_sz; // only for binary compatibility + immutable uint val_off; // only for binary compatibility + Flags flags; // only for binary compatibility + size_t delegate(scope ref const K) nothrow pure @nogc @safe hash_fn; + + enum Flags : ubyte + { + none = 0x0, + key_has_postblit = 0x1, + has_pointers = 0x2, + } + + @property size_t length() const pure nothrow @nogc @safe + { + pragma(inline, true); + assert(used >= deleted); + return used - deleted; + } + + @property size_t dim() const pure nothrow @nogc @safe + { + pragma(inline, true); + return buckets.length; + } + + @property size_t mask() const pure nothrow @nogc @safe + { + pragma(inline, true); + return dim - 1; + } + + // find the first slot to insert a value with hash + size_t find_slot_insert(size_t hash) const pure nothrow @nogc @safe + { + for (size_t i = hash & mask, j = 1;; ++j) + { + if (!buckets[i].filled) + return i; + i = (i + j) & mask; + } + } + + // lookup a key + inout(Bucket)* find_slot_lookup(K2)(size_t hash, scope ref const K2 key) inout pure @safe nothrow + { + for (size_t i = hash & mask, j = 1;; ++j) + { + auto b = &buckets[i]; // avoid multiple bounds checks + if (b.hash == hash && b.entry) + if (pure_keyEqual!(K2, K)(key, b.entry.key)) + return b; + if (b.empty) + return null; + i = (i + j) & mask; + } + } + + void grow() pure nothrow @safe + { + // 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() pure nothrow @safe + { + if (dim > INIT_NUM_BUCKETS) + resize(dim / GROW_FAC); + } + + void resize(size_t ndim) pure nothrow @safe + { + auto obuckets = buckets; + buckets = alloc_buckets(ndim); + + foreach (ref b; obuckets[first_used .. $]) + if (b.filled) + buckets[find_slot_insert(b.hash)] = b; + + first_used = 0; + used -= deleted; + deleted = 0; + obuckets.length = 0; // safe to free b/c impossible to reference, but doesn't really free + } + + void clear() pure nothrow + { + // clear all data, but don't change bucket array length + buckets[first_used .. $] = Bucket.init; + deleted = used = 0; + first_used = cast(uint) dim; + } + + size_t calc_hash(K2)(ref K2 key) const nothrow pure @nogc @safe + { + static if(is(K2* : K*)) // ref compatible? + hash_t hash = pure_hashOf!K(key); + else + hash_t hash = pure_hashOf!K2(key); + // highest bit is set to distinguish empty/deleted from filled buckets + return mix(hash) | HASH_FILLED_MARK; + } + + static Bucket[] alloc_buckets(size_t dim) pure nothrow @safe + { + // could allocate with BlkAttr.NO_INTERIOR, but that does not combine + // well with arrays and type info for precise scanning + return new Bucket[dim]; + } +} + +//============================================================================== +// Bucket +//------------------------------------------------------------------------------ + +private struct Bucket(K, V) +{ +private pure nothrow @nogc: + size_t hash; + Entry!(K, V)* entry; + + @property bool empty() const + { + pragma(inline, true); + return hash == HASH_EMPTY; + } + + @property bool deleted() const + { + pragma(inline, true); + return hash == HASH_DELETED; + } + + @property bool filled() const @safe + { + pragma(inline, true); + return cast(ptrdiff_t) hash < 0; + } +} + +//============================================================================== +// 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 next_pow2(const size_t n) pure nothrow @nogc @safe +{ + import urt.internal.bitop : bsr; + + if (!n) + return 1; + + const is_power_of_2 = !((n - 1) & n); + return 1 << (bsr(n) + !is_power_of_2); +} + +pure nothrow @nogc unittest +{ + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16]) + assert(next_pow2(n) == pow2); +} + +//============================================================================== +// API Implementation +//------------------------------------------------------------------------------ + +/** Allocate associative array data. + * Called for `new SomeAA` expression. + * Returns: + * A new associative array. + * Note: + * not supported in CTFE + */ +V[K] _d_aaNew(K, V)() +{ + AA!(K, V) aa; + aa.impl = new Impl!(K,V)(INIT_NUM_BUCKETS); + return *cast(V[K]*)&aa; +} + +/// Determine number of entries in associative array. +/// Note: +/// emulated by the compiler during CTFE +size_t _d_aaLen(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + return aa ? aa.length : 0; +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (aa[key]) expressions when value is mutable. + * Params: + * aa = associative array + * key = reference to the key value + * found = returns whether the key was found or a new entry was added + * 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 zero + */ +V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found) +{ + ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T + return _aaGetX!(K, V)(aax, key, found); +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of require, update and _d_aaGetY + * Params: + * a = associative array + * key = reference 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 V.init + */ +V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found) +{ + ref aa = _refAA!(K, V)(a); + + // lazily alloc implementation + if (aa is null) + { + aa.impl = new Impl!(K, V)(INIT_NUM_BUCKETS); + } + + ref key2 = compat_key!(K)(key); + + // get hash and bucket for key + immutable hash = aa.calc_hash(key2); + + // found a value => return it + if (auto p = aa.find_slot_lookup(hash, key2)) + { + found = true; + return &p.entry.value; + } + + auto pi = aa.find_slot_insert(hash); + if (aa.buckets[pi].deleted) + --aa.deleted; + // check load factor and possibly grow + else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM) + { + aa.grow(); + pi = aa.find_slot_insert(hash); + assert(aa.buckets[pi].empty); + } + + // update search cache and allocate entry + aa.first_used = min(aa.first_used, cast(uint)pi); + ref p = aa.buckets[pi]; + p.hash = hash; + p.entry = _newEntry!(K, V)(key2); + return &p.entry.value; +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (aa[key]) expressions when value is not mutable. + * Params: + * aa = associative array + * key = key value + * Returns: + * pointer to value if present, null otherwise + */ +auto _d_aaGetRvalueX(K, V, K2)(inout V[K] aa, auto ref scope K2 key) +{ + return _d_aaIn(aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(shared(V[K]) aa, auto ref scope K2 key) +{ + // accept shared for backward compatibility, should be deprecated + return cast(shared(V)*)_d_aaIn(cast(V[K]) aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(shared const(V[K]) aa, auto ref scope K2 key) +{ + // accept shared for backward compatibility, should be deprecated + return cast(const shared(V)*)_d_aaIn(cast(V[K]) aa, key); +} + +/// ditto +auto _d_aaGetRvalueX(K, V, K2)(immutable(V[K]) aa, auto ref scope K2 key) +{ + // resolve ambiguity for immutable converting to const and shared const + return _d_aaIn((() @trusted => cast(V[K]) aa) (), key); +} + +/*********************************** + * Creates a new associative array of the same size and copies the contents of + * the associative array into it. + * Params: + * a = The associative array. + */ +auto _aaDup(T : V[K], K, V)(T a) +{ + auto aa = _toAA!(K, V)(a); + immutable len = aa.length; + if (len == 0) + return null; + + auto impl = new Impl!(K, V)(aa.dim); + // copy the entries + bool same_hash = aa.hash_fn == impl.hash_fn; // can be different if coming from template/rt + foreach (b; aa.buckets[aa.first_used .. $]) + { + if (!b.filled) + continue; + hash_t hash = same_hash ? b.hash : impl.calc_hash(b.entry.key); + auto pi = impl.find_slot_insert(hash); + auto p = &impl.buckets[pi]; + p.hash = hash; + p.entry = new Entry!(K, V)(b.entry.key, b.entry.value); + impl.first_used = min(impl.first_used, cast(uint)pi); + } + impl.used = cast(uint) len; + return () @trusted { return *cast(Unconstify!V[K]*)&impl; }(); +} + +/****************************** + * Lookup key in aa. + * Called only from implementation of (key in aa) expressions. + * Params: + * a = associative array opaque pointer + * key = reference to the key value + * Returns: + * pointer to value if present, null otherwise + */ +auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + ref key2 = compat_key!(K)(key); + + immutable hash = aa.calc_hash(key2); + if (auto p = aa.find_slot_lookup(hash, key2)) + return &p.entry.value; + return null; +} + +// fake purity for backward compatibility with runtime hooks +private extern(C) bool gc_inFinalizer() pure nothrow @safe; + +/// Delete entry scope const AA, return true if it was present +auto _d_aaDel(T : V[K], K, V, K2)(T a, auto ref K2 key) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return false; + + ref key2 = compat_key!(K)(key); + + immutable hash = aa.calc_hash(key2); + if (auto p = aa.find_slot_lookup(hash, key2)) + { + // 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 && !__ctfe && !gc_inFinalizer()) + aa.shrink(); + + return true; + } + return false; +} + +/// Remove all elements from AA. +void _aaClear(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa.empty) + { + aa.clear(); + } +} + +/// Rehash AA +V[K] _aaRehash(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa.empty) + aa.resize(next_pow2(INIT_DEN * aa.length / INIT_NUM)); + return a; +} + +/// Return a GC allocated array of all values +auto _aaValues(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + static if (__traits(compiles, { V val = aa.buckets[0].entry.value; } )) + V[] res; // if value has no const indirections + else + typeof([aa.buckets[0].entry.value]) res; // as mutable as it can get + res = new typeof(res[0])[aa.length]; + + if (false) // never execute, but infer function attributes from this operation + res ~= aa.buckets[0].entry.value; + + size_t i = 0; + foreach (b; aa.buckets[aa.first_used .. $]) + { + if (!b.filled) + continue; + import core.lifetime; + () @trusted { copyEmplace(b.entry.value, res[i++]); }(); + } + return res; +} + +/// Return a GC allocated array of all keys +auto _aaKeys(K, V)(inout V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (aa.empty) + return null; + + static if (__traits(compiles, { K key = aa.buckets[0].entry.key; } )) + K[] res; // if key has no const indirections + else + typeof([aa.buckets[0].entry.key]) res; // as mutable as it can get + res = new typeof(res[0])[aa.length]; + + if (false) // never execute, but infer function attributes from this operation + res ~= aa.buckets[0].entry.key; + + size_t i = 0; + foreach (b; aa.buckets[aa.first_used .. $]) + { + if (!b.filled) + continue; + // res ~= b.entry.key; + import core.lifetime; + () @trusted { copyEmplace(b.entry.key, res[i++]); }(); + } + return res; +} + +/// foreach opApply over all values +/// Note: +/// emulated by the compiler during CTFE +int _d_aaApply(K, V, DG)(inout V[K] a, DG dg) +{ + auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }(); + if (aa.empty) + return 0; + + foreach (b; aa.buckets) + { + if (!b.filled) + continue; + if (auto res = dg(b.entry.value)) + return res; + } + return 0; +} + +int _d_aaApply(K, V, DG)(shared V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(V[K]) a, dg); +} + +int _d_aaApply(K, V, DG)(shared const V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg); +} + +int _d_aaApply(K, V, DG)(immutable V[K] a, DG dg) +{ + return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg); +} + +/// foreach opApply over all key/value pairs +/// Note: +/// emulated by the compiler during CTFE +int _d_aaApply2(K, V, DG)(inout V[K] a, DG dg) +{ + auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }(); + if (aa.empty) + return 0; + + foreach (b; aa.buckets) + { + if (!b.filled) + continue; + if (auto res = dg(b.entry.key, b.entry.value)) + return res; + } + return 0; +} + +int _d_aaApply2(K, V, DG)(shared V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(V[K]) a, dg); +} + +int _d_aaApply2(K, V, DG)(shared const V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg); +} + +int _d_aaApply2(K, V, DG)(immutable V[K] a, DG dg) +{ + return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg); +} + +/** Construct an associative array of type ti from corresponding keys and values. + * Called for an AA literal `[k1:v1, k2:v2]`. + * Params: + * keys = array of keys + * vals = array of values + * Returns: + * A new associative array opaque pointer, or null if `keys` is empty. + */ +Impl!(K, V)* _d_assocarrayliteralTX(K, V)(K[] keys, V[] vals) +{ + assert(keys.length == vals.length); + + immutable length = keys.length; + + if (!length) + return null; + + auto aa = new Impl!(K, V)(next_pow2(INIT_DEN * length / INIT_NUM)); + size_t duplicates = 0; + foreach (i; 0 .. length) + { + immutable hash = aa.calc_hash(keys[i]); + + auto p = aa.find_slot_lookup!K(hash, keys[i]); + if (p) + { + static if (__traits(compiles, p.entry.value = vals[i])) // immutable? + p.entry.value = vals[i]; + else + p.entry = _newEntry!(K, V)(keys[i], vals[i]); + duplicates++; + continue; + } + auto pi = aa.find_slot_insert(hash); + p = &aa.buckets[pi]; + p.hash = hash; + p.entry = _newEntry!(K, V)(keys[i], vals[i]); // todo: move key and value? + aa.first_used = min(aa.first_used, cast(uint)pi); + } + aa.used = cast(uint) (length - duplicates); + return aa; +} + +/// compares 2 AAs for equality +bool _aaEqual(T : AA!(K, V), K, V)(scope T aa1, scope T aa2) +{ + if (aa1 is aa2) + return true; + + immutable len = aa1.length; + if (len != aa2.length) + return false; + + if (!len) // both empty + return true; + + bool same_hash = aa1.hash_fn == aa2.hash_fn; // can be different if coming from template/rt + // compare the entries + foreach (b1; aa1.buckets[aa1.first_used .. $]) + { + if (!b1.filled) + continue; + hash_t hash = same_hash ? b1.hash : aa2.calc_hash(b1.entry.key); + auto pb2 = aa2.find_slot_lookup!K(hash, b1.entry.key); + if (pb2 is null || !pure_keyEqual!(V, V)(b1.entry.value, pb2.entry.value)) // rarely, inference on opEqual breaks builds here + return false; + } + return true; +} + +/// compares 2 AAs for equality (compiler hook) +bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2) +{ + scope aa1 = _toAA!(K, V)(a1); + scope aa2 = _toAA!(K, V)(a2); + return _aaEqual(aa1, aa2); +} + +/// callback from TypeInfo_AssociativeArray.equals (ignore const for now) +bool _aaOpEqual(K, V)(scope /* const */ AA!(K, V)* aa1, scope /* const */ AA!(K, V)* aa2) +{ + return _aaEqual(*aa1, *aa2); +} + +/// compute a hash callback from TypeInfo_AssociativeArray.xtoHash (ignore scope const for now) +hash_t _aaGetHash(K, V)(/* scope const */ AA!(K, V)* paa) +{ + const aa = *paa; + + if (aa.empty) + return 0; + + size_t h; + foreach (b; aa.buckets) + { + // use addition here, so that hash is independent of element order + if (b.filled) + h += hashOf(pure_hashOf!V(b.entry.value), pure_hashOf!K(b.entry.key)); + } + + return h; +} + +/** + * _aaRange implements a ForwardRange + */ +struct AARange(K, V) +{ + alias Key = substInout!K; + alias Value = substInout!V; + + Impl!(Key, Value)* impl; + size_t idx; + alias impl this; +} + +AARange!(K, V) _aaRange(K, V)(V[K] a) +{ + auto aa = _toAA!(K, V)(a); + if (!aa) + return AARange!(K, V)(); + + foreach (i; aa.first_used .. aa.dim) + { + if (aa.buckets[i].filled) + return AARange!(K, V)(aa, i); + } + return AARange!(K, V)(aa, aa.dim); +} + +bool _aaRangeEmpty(K, V)(AARange!(K, V) r) +{ + return r.impl is null || r.idx >= r.dim; +} + +K* _aaRangeFrontKey(K, V)(AARange!(K, V) r) +{ + assert(!_aaRangeEmpty(r)); + if (r.idx >= r.dim) + return null; + auto entry = r.buckets[r.idx].entry; + return entry is null ? null : &r.buckets[r.idx].entry.key; +} + +V* _aaRangeFrontValue(K, V)(AARange!(K, V) r) +{ + assert(!_aaRangeEmpty(r)); + if (r.idx >= r.dim) + return null; + + auto entry = r.buckets[r.idx].entry; + return entry is null ? null : &r.buckets[r.idx].entry.value; +} + +void _aaRangePopFront(K, V)(ref AARange!(K, V) r) +{ + if (r.idx >= r.dim) return; + for (++r.idx; r.idx < r.dim; ++r.idx) + { + if (r.buckets[r.idx].filled) + break; + } +} + +// test postblit for AA literals +// Disabled: uses core.memory/GC and AA syntax that can't resolve inside +// this module when compiled as source (circular object ↔ newaa import). +version (none) unittest +{ + import core.memory; + + static struct T + { + ubyte field; + static size_t postblit, dtor; + this(this) + { + ++postblit; + } + + ~this() + { + ++dtor; + } + } + + T t; + auto aa1 = [0 : t, 1 : t]; + assert(T.dtor == 2 && T.postblit == 4); + aa1[0] = t; + assert(T.dtor == 3 && T.postblit == 5); + + T.dtor = 0; + T.postblit = 0; + + auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten + assert(T.dtor == 4 && T.postblit == 6); + + T.dtor = 0; + T.postblit = 0; + + auto aa3 = [t : 0]; + assert(T.dtor == 1 && T.postblit == 2); + aa3[t] = 1; + assert(T.dtor == 1 && T.postblit == 2); + aa3.remove(t); + assert(T.dtor == 1 && T.postblit == 2); + aa3[t] = 2; + assert(T.dtor == 1 && T.postblit == 3); + + // dtor will be called by GC finalizers + aa1 = null; + aa2 = null; + aa3 = null; + auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor; + GC.runFinalizers((cast(char*)dtor1)[0 .. 1]); + auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor; + GC.runFinalizers((cast(char*)dtor2)[0 .. 1]); + assert(T.dtor == 7 && T.postblit == 3); +} + +// create a binary-compatible AA structure that can be used directly as an +// associative array. +// NOTE: this must only be called during CTFE +AA!(K, V) makeAA(K, V)(V[K] src) @trusted +{ + assert(__ctfe, "makeAA Must only be called at compile time"); + // Iterate the built-in AA directly — .keys/.values are UFCS properties + // that require druntime hooks we don't provide. + K[] keys; + V[] values; + foreach (k, v; src) + { + keys ~= k; + values ~= v; + } + auto impl = _d_assocarrayliteralTX!(K, V)(keys, values); + return AA!(K, V)(impl); +} + +// Disabled: AA-indexing lowering inside this module creates a circular +// import (object → core.internal.newaa → object) that prevents template +// resolution when compiled as source rather than a pre-compiled library. +version (none) unittest +{ + static struct Foo + { + ubyte x; + double d; + } + static int[Foo] utaa = [Foo(1, 2.0) : 5]; + auto k = Foo(1, 2.0); + // verify that getHash doesn't match hashOf for Foo + assert(typeid(Foo).getHash(&k) != hashOf(k)); + assert(utaa[Foo(1, 2.0)] == 5); +} diff --git a/src/urt/internal/bitop.d b/src/urt/internal/bitop.d new file mode 100644 index 0000000..cb68fe7 --- /dev/null +++ b/src/urt/internal/bitop.d @@ -0,0 +1,283 @@ +// TODO: DISSOLVE THIS FILE... +module urt.internal.bitop; + +nothrow @nogc @safe: + +version (D_InlineAsm_X86_64) + version = AsmX86; +else version (D_InlineAsm_X86) + version = AsmX86; + +version (X86_64) + version = AnyX86; +else version (X86) + version = AnyX86; + +// Use to implement 64-bit bitops on 32-bit arch. +private union Split64 +{ + ulong u64; + struct + { + version (LittleEndian) + { + uint lo; + uint hi; + } + else + { + uint hi; + uint lo; + } + } + + pragma(inline, true) + this(ulong u64) @safe pure nothrow @nogc + { + if (__ctfe) + { + lo = cast(uint) u64; + hi = cast(uint) (u64 >>> 32); + } + else + this.u64 = u64; + } +} + +/** + * Scans the bits in v starting with bit 0, looking + * for the first set bit. + * Returns: + * The bit number of the first bit set. + * The return value is undefined if v is zero. + */ +int bsf(uint v) pure +{ + pragma(inline, false); // so intrinsic detection will work + return softBsf!uint(v); +} + +/// ditto +int bsf(ulong v) pure +{ + static if (size_t.sizeof == ulong.sizeof) // 64 bit code gen + { + pragma(inline, false); // so intrinsic detection will work + return softBsf!ulong(v); + } + else + { + const sv = Split64(v); + return (sv.lo == 0)? + bsf(sv.hi) + 32 : + bsf(sv.lo); + } +} + +/** + * Scans the bits in v from the most significant bit + * to the least significant bit, looking + * for the first set bit. + * Returns: + * The bit number of the first bit set. + * The return value is undefined if v is zero. + */ +int bsr(uint v) pure +{ + pragma(inline, false); // so intrinsic detection will work + return softBsr!uint(v); +} + +/// ditto +int bsr(ulong v) pure +{ + static if (size_t.sizeof == ulong.sizeof) // 64 bit code gen + { + pragma(inline, false); // so intrinsic detection will work + return softBsr!ulong(v); + } + else + { + const sv = Split64(v); + return (sv.hi == 0)? + bsr(sv.lo) : + bsr(sv.hi) + 32; + } +} + +private alias softBsf(N) = softScan!(N, true); +private alias softBsr(N) = softScan!(N, false); + +private int softScan(N, bool forward)(N v) pure + if (is(N == uint) || is(N == ulong)) +{ + if (!v) + return -1; + + enum mask(ulong lo) = forward ? cast(N) lo : cast(N)~lo; + enum inc(int up) = forward ? up : -up; + + N x; + int ret; + static if (is(N == ulong)) + { + x = v & mask!0x0000_0000_FFFF_FFFFL; + if (x) + { + v = x; + ret = forward ? 0 : 63; + } + else + ret = forward ? 32 : 31; + + x = v & mask!0x0000_FFFF_0000_FFFFL; + if (x) + v = x; + else + ret += inc!16; + } + else static if (is(N == uint)) + { + x = v & mask!0x0000_FFFF; + if (x) + { + v = x; + ret = forward ? 0 : 31; + } + else + ret = forward ? 16 : 15; + } + else + static assert(false); + + x = v & mask!0x00FF_00FF_00FF_00FFL; + if (x) + v = x; + else + ret += inc!8; + + x = v & mask!0x0F0F_0F0F_0F0F_0F0FL; + if (x) + v = x; + else + ret += inc!4; + + x = v & mask!0x3333_3333_3333_3333L; + if (x) + v = x; + else + ret += inc!2; + + x = v & mask!0x5555_5555_5555_5555L; + if (!x) + ret += inc!1; + + return ret; +} + +/** + * Tests the bit. + */ +int bt(const scope size_t* p, size_t bitnum) pure @system +{ + static if (size_t.sizeof == 8) + return ((p[bitnum >> 6] & (1L << (bitnum & 63)))) != 0; + else static if (size_t.sizeof == 4) + return ((p[bitnum >> 5] & (1 << (bitnum & 31)))) != 0; + else + static assert(0); +} + +/** + * Tests and complements the bit. + */ +int btc(size_t* p, size_t bitnum) pure @system; + +/** + * Tests and resets (sets to 0) the bit. + */ +int btr(size_t* p, size_t bitnum) pure @system; + +/** + * Tests and sets the bit. + */ +int bts(size_t* p, size_t bitnum) pure @system; + +/** + * Swaps bytes in a 2 byte ushort. + */ +pragma(inline, false) +ushort byteswap(ushort x) pure +{ + return cast(ushort) (((x >> 8) & 0xFF) | ((x << 8) & 0xFF00u)); +} + +/** + * Swaps bytes in a 4 byte uint end-to-end. + */ +uint bswap(uint v) pure; + +/** + * Swaps bytes in an 8 byte ulong end-to-end. + */ +ulong bswap(ulong v) pure; + +version (DigitalMars) version (AnyX86) @system // not pure +{ + ubyte inp(uint port_address); + ushort inpw(uint port_address); + uint inpl(uint port_address); + ubyte outp(uint port_address, ubyte value); + ushort outpw(uint port_address, ushort value); + uint outpl(uint port_address, uint value); +} + +/** + * Calculates the number of set bits in an integer. + */ +int popcnt(uint x) pure +{ + return softPopcnt!uint(x); +} + +/// ditto +int popcnt(ulong x) pure +{ + static if (size_t.sizeof == uint.sizeof) + { + const sx = Split64(x); + return softPopcnt!uint(sx.lo) + softPopcnt!uint(sx.hi); + } + else static if (size_t.sizeof == ulong.sizeof) + { + return softPopcnt!ulong(x); + } + else + static assert(false); +} + +version (DigitalMars) version (AnyX86) +{ + ushort _popcnt( ushort x ) pure; + int _popcnt( uint x ) pure; + version (X86_64) + { + int _popcnt( ulong x ) pure; + } +} + +private int softPopcnt(N)(N x) pure + if (is(N == uint) || is(N == ulong)) +{ + enum mask1 = cast(N) 0x5555_5555_5555_5555L; + x = x - ((x>>1) & mask1); + enum mask2a = cast(N) 0xCCCC_CCCC_CCCC_CCCCL; + enum mask2b = cast(N) 0x3333_3333_3333_3333L; + x = ((x & mask2a)>>2) + (x & mask2b); + enum mask4 = cast(N) 0x0F0F_0F0F_0F0F_0F0FL; + x = (x + (x >> 4)) & mask4; + enum shiftbits = is(N == uint)? 24 : 56; + enum maskMul = cast(N) 0x0101_0101_0101_0101L; + x = (x * maskMul) >> shiftbits; + return cast(int) x; +} diff --git a/src/urt/internal/exception.d b/src/urt/internal/exception.d new file mode 100644 index 0000000..b6ed498 --- /dev/null +++ b/src/urt/internal/exception.d @@ -0,0 +1,3600 @@ +/** + * D exception handling — throw, catch, finally. + * + * Ported from druntime for use in uRT. + * Two implementations: + * - Win32 (x86): SEH-based, ported from rt/deh_win32.d + * - Win64/POSIX (x86_64): RBP-chain walking, ported from rt/deh_win64_posix.d + * + * Copyright: Digital Mars 1999-2013 (original), uRT authors (port). + * License: Boost Software License 1.0 + */ +module urt.internal.exception; + +// No top-level gate — individual version blocks guard platform-specific code. + +// ══════════════════════════════════════════════════════════════════════ +// Shared declarations +// ══════════════════════════════════════════════════════════════════════ + +alias ClassInfo = TypeInfo_Class; + +extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) nothrow @nogc pure @trusted +{ + if (oc is c) + return true; + + do + { + if (oc.base is c) + return true; + + foreach (iface; oc.interfaces) + { + if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c)) + return true; + } + + oc = oc.base; + } + while (oc); + + return false; +} + +// Thread-local trace buffer. _d_createTrace captures here silently; +// terminate() and _d_printLastTrace() read it back for display. +private struct StackTraceData +{ + void*[32] addrs; + ubyte length; +} + +private StackTraceData _tls_trace; // static = TLS in D + +extern(C) void _d_createTrace(Throwable t, void*) nothrow @nogc @trusted +{ + // Capture return addresses into the TLS buffer. No output — + // the trace is only printed on unhandled exceptions (terminate) + // or when explicitly requested via _d_printLastTrace(). + debug + { + _tls_trace.length = 0; + + version (Windows) + { + auto n = rtlCaptureStackBackTrace(2, 32, _tls_trace.addrs.ptr, null); + if (n == 0) + n = cast(ushort) stack_walk64_capture(_tls_trace.addrs); + _tls_trace.length = cast(ubyte) n; + } + else version (D_InlineAsm_X86_64) + { + size_t bp; + asm nothrow @nogc { mov bp, RBP; } + + ubyte n = 0; + foreach (_; 0 .. 32) + { + if (!bp) + break; + auto next_bp = *cast(size_t*) bp; + if (!next_bp || next_bp <= bp) + break; + auto retaddr = *cast(void**)(bp + size_t.sizeof); + if (!retaddr) + break; + _tls_trace.addrs[n++] = retaddr; + bp = next_bp; + } + _tls_trace.length = n; + } + else version (D_InlineAsm_X86) + { + size_t bp; + asm nothrow @nogc { mov bp, EBP; } + + ubyte n = 0; + foreach (_; 0 .. 32) + { + if (!bp) + break; + auto next_bp = *cast(size_t*) bp; + if (!next_bp || next_bp <= bp) + break; + auto retaddr = *cast(void**)(bp + size_t.sizeof); + if (!retaddr) + break; + _tls_trace.addrs[n++] = retaddr; + bp = next_bp; + } + _tls_trace.length = n; + } + else + { + // ARM, AArch64, RISC-V, etc. — use _Unwind_Backtrace + unwind_backtrace(_tls_trace); + } + } +} + +/// Print the stack trace from the most recent throw on this thread. +/// Safe to call from catch blocks, assert handlers, or anywhere else. +extern(C) void _d_printLastTrace(Throwable t) nothrow @nogc @trusted +{ + debug + { + import urt.io : writeln_err, writef_to, WriteTarget; + + if (_tls_trace.length == 0) + return; + + if (t !is null) + { + auto msg = t.msg; + writef_to!(WriteTarget.stderr, true)("Exception: {0}", msg); + } + + writeln_err(" stack trace:"); + + version (Windows) + dbghelp_print_trace(_tls_trace.addrs[0 .. _tls_trace.length]); + else + posix_print_trace(_tls_trace.addrs[0 .. _tls_trace.length]); + } +} + +// ── Stack trace support (Windows, debug only) ──────────────────────── +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// TODO: DbgHelp is NOT thread-safe. All calls to SymFromAddr, +// SymGetLineFromAddr64, SymInitialize, and StackWalk64 must be +// serialized with a CRITICAL_SECTION (or equivalent) once this +// program uses threads. Without this, concurrent exceptions from +// multiple threads WILL crash or corrupt DbgHelp's internal state. +// Both DMD and LDC druntime protect all DbgHelp access with a mutex. +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +version (Windows) debug +{ + // --- DbgHelp types --------------------------------------------------- + + private struct SYMBOL_INFOA + { + uint SizeOfStruct; + uint TypeIndex; + ulong[2] Reserved; + uint Index; + uint Size; + ulong ModBase; + uint Flags; + ulong Value; + ulong Address; + uint Register; + uint Scope; + uint Tag; + uint NameLen; + uint MaxNameLen; + char[1] Name; // variable-length + } + + private struct IMAGEHLP_LINEA64 + { + uint SizeOfStruct; + void* Key; + uint LineNumber; + const(char)* FileName; + ulong Address; + } + + // --- StackWalk64 types ----------------------------------------------- + + private alias HANDLE = void*; + private enum AddrModeFlat = 3; + + private struct ADDRESS64 + { + ulong Offset; + ushort Segment; + uint Mode; + } + + private struct STACKFRAME64 + { + ADDRESS64 AddrPC; + ADDRESS64 AddrReturn; + ADDRESS64 AddrFrame; + ADDRESS64 AddrStack; + ADDRESS64 AddrBStore; + void* FuncTableEntry; + ulong[4] Params; + int Far; + int Virtual; + ulong[3] Reserved; + ubyte[96] KdHelp; // KDHELP64, opaque + } + + // CONTEXT — opaque aligned buffer, accessed via offset constants. + version (Win64) + { + private enum CONTEXT_SIZE = 1232; + private enum CTX_FLAGS_OFF = 48; + private enum CTX_IP_OFF = 248; // Rip + private enum CTX_SP_OFF = 152; // Rsp + private enum CTX_FP_OFF = 160; // Rbp + private enum CTX_FULL = 0x10000B; + private enum MACHINE_TYPE = 0x8664; // IMAGE_FILE_MACHINE_AMD64 + } + else + { + private enum CONTEXT_SIZE = 716; + private enum CTX_FLAGS_OFF = 0; + private enum CTX_IP_OFF = 184; // Eip + private enum CTX_SP_OFF = 196; // Esp + private enum CTX_FP_OFF = 180; // Ebp + private enum CTX_FULL = 0x10007; + private enum MACHINE_TYPE = 0x014C; // IMAGE_FILE_MACHINE_I386 + } + + // --- Function pointer types ------------------------------------------ + + extern (Windows) nothrow @nogc + { + private alias SymInitializeFn = int function(HANDLE, const(char)*, int); + private alias SymSetOptionsFn = uint function(uint); + private alias SymFromAddrFn = int function(HANDLE, ulong, ulong*, SYMBOL_INFOA*); + private alias SymGetLineFromAddr64Fn = int function(HANDLE, ulong, uint*, IMAGEHLP_LINEA64*); + private alias FuncTableAccessFn = void* function(HANDLE, ulong); + private alias GetModuleBaseFn = ulong function(HANDLE, ulong); + private alias StackWalk64Fn = int function( + uint machineType, HANDLE hProcess, HANDLE hThread, + STACKFRAME64* stackFrame, void* contextRecord, + void* readMemory, FuncTableAccessFn funcTableAccess, + GetModuleBaseFn getModuleBase, void* translateAddress); + } + + // --- Globals --------------------------------------------------------- + + private __gshared bool _dbg_inited; + private __gshared bool _dbg_available; + private __gshared HANDLE _dbg_process; + private __gshared SymFromAddrFn _sym_from_addr; + private __gshared SymGetLineFromAddr64Fn _sym_get_line; + private __gshared StackWalk64Fn _stack_walk64; + private __gshared FuncTableAccessFn _func_table_access64; + private __gshared GetModuleBaseFn _get_module_base64; + + // --- Initialization -------------------------------------------------- + + private void dbghelp_init() nothrow @nogc @trusted + { + if (_dbg_inited) + return; + _dbg_inited = true; + + auto hDbg = loadLibraryA("dbghelp.dll"); + if (hDbg is null) + return; + + auto sym_init = cast(SymInitializeFn) getProcAddress(hDbg, "SymInitialize"); + auto sym_set_opt = cast(SymSetOptionsFn) getProcAddress(hDbg, "SymSetOptions"); + _sym_from_addr = cast(SymFromAddrFn) getProcAddress(hDbg, "SymFromAddr"); + _sym_get_line = cast(SymGetLineFromAddr64Fn) getProcAddress(hDbg, "SymGetLineFromAddr64"); + _stack_walk64 = cast(StackWalk64Fn) getProcAddress(hDbg, "StackWalk64"); + _func_table_access64 = cast(FuncTableAccessFn) getProcAddress(hDbg, "SymFunctionTableAccess64"); + _get_module_base64 = cast(GetModuleBaseFn) getProcAddress(hDbg, "SymGetModuleBase64"); + + if (sym_init is null || sym_set_opt is null || _sym_from_addr is null) + return; + + // SYMOPT_DEFERRED_LOAD | SYMOPT_LOAD_LINES + sym_set_opt(0x00000004 | 0x00000010); + + _dbg_process = cast(HANDLE) cast(size_t)-1; // GetCurrentProcess() pseudo-handle + if (!sym_init(_dbg_process, null, 1)) // fInvadeProcess = TRUE + return; + + _dbg_available = true; + } + + // --- Kernel32/ntdll imports ------------------------------------------ + + pragma(mangle, "LoadLibraryA") + extern (Windows) private void* loadLibraryA(const(char)*) nothrow @nogc; + + pragma(mangle, "GetProcAddress") + extern (Windows) private void* getProcAddress(void*, const(char)*) nothrow @nogc; + + pragma(mangle, "RtlCaptureStackBackTrace") + extern (Windows) private ushort rtlCaptureStackBackTrace( + uint FramesToSkip, uint FramesToCapture, void** BackTrace, uint* BackTraceHash) nothrow @nogc; + + pragma(mangle, "RtlCaptureContext") + extern (Windows) private void rtlCaptureContext(void* contextRecord) nothrow @nogc; + + pragma(mangle, "GetCurrentThread") + extern (Windows) private HANDLE getCurrentThread() nothrow @nogc; + + // --- StackWalk64 fallback -------------------------------------------- + + private size_t stack_walk64_capture(ref void*[32] addrs) nothrow @nogc @trusted + { + dbghelp_init(); + + if (_stack_walk64 is null || _func_table_access64 is null || _get_module_base64 is null) + return 0; + + align(16) ubyte[CONTEXT_SIZE] ctx = 0; + *cast(uint*)(ctx.ptr + CTX_FLAGS_OFF) = CTX_FULL; + rtlCaptureContext(ctx.ptr); + + STACKFRAME64 sf; + version (Win64) + { + sf.AddrPC.Offset = *cast(ulong*)(ctx.ptr + CTX_IP_OFF); + sf.AddrFrame.Offset = *cast(ulong*)(ctx.ptr + CTX_FP_OFF); + sf.AddrStack.Offset = *cast(ulong*)(ctx.ptr + CTX_SP_OFF); + } + else + { + sf.AddrPC.Offset = *cast(uint*)(ctx.ptr + CTX_IP_OFF); + sf.AddrFrame.Offset = *cast(uint*)(ctx.ptr + CTX_FP_OFF); + sf.AddrStack.Offset = *cast(uint*)(ctx.ptr + CTX_SP_OFF); + } + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrFrame.Mode = AddrModeFlat; + sf.AddrStack.Mode = AddrModeFlat; + + auto hThread = getCurrentThread(); + size_t n = 0; + + // Skip internal frames (_d_createTrace + stack_walk64_capture + RtlCaptureContext) + uint skip = 3; + + while (n < 32) + { + if (!_stack_walk64(MACHINE_TYPE, _dbg_process, hThread, + &sf, ctx.ptr, null, _func_table_access64, _get_module_base64, null)) + break; + if (sf.AddrPC.Offset == 0) + break; + if (skip > 0) + { + --skip; + continue; + } + addrs[n++] = cast(void*) cast(size_t) sf.AddrPC.Offset; + } + return n; + } + + // --- Symbol resolution & printing ------------------------------------ + + private void dbghelp_print_trace(void*[] addrs) nothrow @nogc @trusted + { + import urt.io : writef_to, writeln_err, WriteTarget; + import urt.mem : strlen; + import urt.string : endsWith; + + dbghelp_init(); + + // Skip frames up through _d_throw_exception (internal machinery). + // LDC druntime does the same — clears accumulated frames when it + // hits _d_throw_exception, so the trace starts at the throw site. + size_t start = 0; + if (_dbg_available) + { + foreach (i, addr; addrs) + { + align(8) ubyte[SYMBOL_INFOA.sizeof + 256] sym_buf = 0; + auto p_sym = cast(SYMBOL_INFOA*) sym_buf.ptr; + p_sym.SizeOfStruct = SYMBOL_INFOA.sizeof; + p_sym.MaxNameLen = 256; + + ulong disp; + if (_sym_from_addr(_dbg_process, cast(ulong) addr, &disp, p_sym)) + { + auto name = p_sym.Name.ptr[0 .. strlen(p_sym.Name.ptr)]; + if (name.endsWith("_d_throw_exception")) + start = i + 1; + } + } + } + + enum addr_fmt = size_t.sizeof == 4 ? "08x" : "016x"; + + foreach (addr; addrs[start .. $]) + { + if (_dbg_available) + { + align(8) ubyte[SYMBOL_INFOA.sizeof + 256] sym_buf = 0; + auto p_sym = cast(SYMBOL_INFOA*) sym_buf.ptr; + p_sym.SizeOfStruct = SYMBOL_INFOA.sizeof; + p_sym.MaxNameLen = 256; + + ulong displacement64; + auto a = cast(ulong) addr; + if (_sym_from_addr(_dbg_process, a, &displacement64, p_sym)) + { + auto name = p_sym.Name.ptr[0 .. strlen(p_sym.Name.ptr)]; + + uint displacement32; + IMAGEHLP_LINEA64 line_info; + line_info.SizeOfStruct = IMAGEHLP_LINEA64.sizeof; + + if (_sym_get_line !is null && + _sym_get_line(_dbg_process, a, &displacement32, &line_info)) + { + auto fname = line_info.FileName[0 .. strlen(line_info.FileName)]; + writef_to!(WriteTarget.stderr, true)(" {0}+0x{1:x} [{2}:{3}]", + name, displacement64, fname, line_info.LineNumber); + } + else + { + writef_to!(WriteTarget.stderr, true)(" {0}+0x{1:x}", name, displacement64); + } + continue; + } + } + + // Fallback: raw address + writef_to!(WriteTarget.stderr, true)(" 0x{0:" ~ addr_fmt ~ "}", cast(size_t)addr); + } + } +} + +// ── Stack trace support (POSIX, debug only) ────────────────────────── +// +// Uses dladdr for symbol names and DWARF .debug_line for file:line info. +// Ported from druntime's core.internal.backtrace.{dwarf,elf} and +// core.internal.elf.{io,dl}. +// +version (Windows) {} else debug +{ + import urt.io : write_err, writeln_err, writef_to, WriteTarget; + import urt.mem : strlen, memcpy; + + import core.sys.posix.dlfcn : dladdr, Dl_info; + import core.sys.posix.fcntl : open, O_RDONLY; + import core.sys.posix.unistd : close, lseek, readlink, sysconf, _SC_PAGE_SIZE; + import core.sys.posix.sys.mman : mmap, munmap, PROT_READ, MAP_PRIVATE, MAP_FAILED; + import core.sys.posix.sys.types : off_t; + + private enum SEEK_END = 2; + + // ── _Unwind_Backtrace capture (ARM, AArch64, RISC-V) ──────────── + + version (D_InlineAsm_X86_64) {} else version (D_InlineAsm_X86) {} else + { + private alias _Unwind_Trace_Fn = extern(C) int function(void* ctx, void* data) nothrow @nogc; + + extern(C) private int _Unwind_Backtrace(_Unwind_Trace_Fn, void*) nothrow @nogc; + extern(C) private size_t _Unwind_GetIP(void*) nothrow @nogc; + + private struct UnwindState { StackTraceData* trace; ubyte skip; } + + extern(C) private int unwind_trace_callback(void* ctx, void* data) nothrow @nogc + { + auto s = cast(UnwindState*) data; + if (s.skip > 0) { s.skip--; return 0; } + if (s.trace.length >= 32) return 1; + auto ip = _Unwind_GetIP(ctx); + if (!ip) return 1; + s.trace.addrs[s.trace.length++] = cast(void*) ip; + return 0; + } + + private void unwind_backtrace(ref StackTraceData trace) nothrow @nogc @trusted + { + UnwindState state = UnwindState(&trace, 2); + _Unwind_Backtrace(&unwind_trace_callback, &state); + } + } + + // ── Minimal ELF types ──────────────────────────────────────────── + + version (D_LP64) + { + private struct Elf_Ehdr + { + ubyte[16] e_ident; + ushort e_type; + ushort e_machine; + uint e_version; + ulong e_entry; + ulong e_phoff; + ulong e_shoff; + uint e_flags; + ushort e_ehsize; + ushort e_phentsize; + ushort e_phnum; + ushort e_shentsize; + ushort e_shnum; + ushort e_shstrndx; + } + + private struct Elf_Shdr + { + uint sh_name; + uint sh_type; + ulong sh_flags; + ulong sh_addr; + ulong sh_offset; + ulong sh_size; + uint sh_link; + uint sh_info; + ulong sh_addralign; + ulong sh_entsize; + } + + private struct Elf_Phdr + { + uint p_type; + uint p_flags; + ulong p_offset; + ulong p_vaddr; + ulong p_paddr; + ulong p_filesz; + ulong p_memsz; + ulong p_align; + } + } + else + { + private struct Elf_Ehdr + { + ubyte[16] e_ident; + ushort e_type; + ushort e_machine; + uint e_version; + uint e_entry; + uint e_phoff; + uint e_shoff; + uint e_flags; + ushort e_ehsize; + ushort e_phentsize; + ushort e_phnum; + ushort e_shentsize; + ushort e_shnum; + ushort e_shstrndx; + } + + private struct Elf_Shdr + { + uint sh_name; + uint sh_type; + uint sh_flags; + uint sh_addr; + uint sh_offset; + uint sh_size; + uint sh_link; + uint sh_info; + uint sh_addralign; + uint sh_entsize; + } + + private struct Elf_Phdr + { + uint p_type; + uint p_offset; + uint p_vaddr; + uint p_paddr; + uint p_filesz; + uint p_memsz; + uint p_flags; + uint p_align; + } + } + + private enum EI_MAG0 = 0; + private enum EI_CLASS = 4; + private enum EI_DATA = 5; + private enum ELFMAG = "\x7fELF"; + private enum ET_DYN = 3; + private enum SHF_COMPRESSED = 0x800; + + version (D_LP64) + private enum ELFCLASS_NATIVE = 2; // ELFCLASS64 + else + private enum ELFCLASS_NATIVE = 1; // ELFCLASS32 + + version (LittleEndian) + private enum ELFDATA_NATIVE = 1; // ELFDATA2LSB + else + private enum ELFDATA_NATIVE = 2; // ELFDATA2MSB + + // ── dl_iterate_phdr for base address ───────────────────────────── + + private struct dl_phdr_info + { + size_t dlpi_addr; + const(char)* dlpi_name; + const(Elf_Phdr)* dlpi_phdr; + ushort dlpi_phnum; + } + + private alias dl_iterate_phdr_callback_t = extern(C) int function(dl_phdr_info*, size_t, void*) nothrow @nogc; + + extern(C) private int dl_iterate_phdr(dl_iterate_phdr_callback_t callback, void* data) nothrow @nogc; + + private size_t get_executable_base_address() nothrow @nogc @trusted + { + size_t result = 0; + + extern(C) static int callback(dl_phdr_info* info, size_t, void* data) nothrow @nogc + { + // First entry is the executable itself + *cast(size_t*) data = info.dlpi_addr; + return 1; // stop iteration + } + + dl_iterate_phdr(&callback, &result); + return result; + } + + // ── Memory-mapped file region ──────────────────────────────────── + + private struct MappedRegion + { + const(ubyte)* data; + size_t mapped_size; + + nothrow @nogc @trusted: + + static MappedRegion map(int fd, size_t offset, size_t length) + { + if (fd == -1 || length == 0) + return MappedRegion.init; + + auto pgsz = cast(size_t) sysconf(_SC_PAGE_SIZE); + if (cast(int) pgsz <= 0) + pgsz = 4096; + + const page_off = offset / pgsz; + const diff = offset - page_off * pgsz; + const needed = length + diff; + const pages = (needed + pgsz - 1) / pgsz; + const msize = pages * pgsz; + + auto p = mmap(null, msize, PROT_READ, MAP_PRIVATE, fd, cast(off_t)(page_off * pgsz)); + if (p is MAP_FAILED) + return MappedRegion.init; + + return MappedRegion(cast(const(ubyte)*) p + diff, msize); + } + + void unmap() + { + if (data !is null) + { + auto pgsz = cast(size_t) sysconf(_SC_PAGE_SIZE); + if (cast(int) pgsz <= 0) + pgsz = 4096; + // Align back to page boundary + auto base = cast(void*)(cast(size_t) data & ~(pgsz - 1)); + munmap(base, mapped_size); + data = null; + } + } + } + + // ── ELF self-reader ────────────────────────────────────────────── + + private struct ElfSelf + { + int fd = -1; + MappedRegion ehdr_region; + const(Elf_Ehdr)* ehdr; + + nothrow @nogc @trusted: + + static ElfSelf open() + { + ElfSelf self; + + // Read /proc/self/exe path + char[512] pathbuf = void; + auto n = readlink("/proc/self/exe", pathbuf.ptr, pathbuf.length - 1); + if (n <= 0) + return self; + pathbuf[n] = 0; + + self.fd = .open(pathbuf.ptr, O_RDONLY); + if (self.fd == -1) + return self; + + // Map the ELF header + self.ehdr_region = MappedRegion.map(self.fd, 0, Elf_Ehdr.sizeof); + if (self.ehdr_region.data is null) + { + .close(self.fd); + self.fd = -1; + return self; + } + + self.ehdr = cast(const(Elf_Ehdr)*) self.ehdr_region.data; + + // Validate ELF magic, class, and byte order + if (self.ehdr.e_ident[0..4] != cast(const(ubyte)[4]) ELFMAG + || self.ehdr.e_ident[EI_CLASS] != ELFCLASS_NATIVE + || self.ehdr.e_ident[EI_DATA] != ELFDATA_NATIVE) + { + self.close(); + return ElfSelf.init; + } + + return self; + } + + void close() + { + ehdr_region.unmap(); + ehdr = null; + if (fd != -1) { .close(fd); fd = -1; } + } + + bool valid() const { return fd != -1 && ehdr !is null; } + + /// Find a section by name, return its offset and size. + bool find_section(const(char)[] name, out size_t offset, out size_t size) + { + if (!valid()) + return false; + + // Map section headers + auto shdr_total = cast(size_t) ehdr.e_shnum * Elf_Shdr.sizeof; + auto shdr_region = MappedRegion.map(fd, cast(size_t) ehdr.e_shoff, shdr_total); + if (shdr_region.data is null) + return false; + scope(exit) shdr_region.unmap(); + + auto shdrs = (cast(const(Elf_Shdr)*) shdr_region.data)[0 .. ehdr.e_shnum]; + + // Map string table section + if (ehdr.e_shstrndx >= ehdr.e_shnum) + return false; + auto strtab_shdr = &shdrs[ehdr.e_shstrndx]; + auto strtab_region = MappedRegion.map(fd, + cast(size_t) strtab_shdr.sh_offset, + cast(size_t) strtab_shdr.sh_size); + if (strtab_region.data is null) + return false; + scope(exit) strtab_region.unmap(); + + auto strtab = cast(const(char)*) strtab_region.data; + + // Search for the named section + foreach (ref shdr; shdrs) + { + if (shdr.sh_name >= strtab_shdr.sh_size) + continue; + auto sec_name = strtab + shdr.sh_name; + auto sec_name_len = strlen(sec_name); + if (sec_name_len == name.length && sec_name[0 .. sec_name_len] == name) + { + if (shdr.sh_flags & SHF_COMPRESSED) + return false; // compressed debug sections not supported + offset = cast(size_t) shdr.sh_offset; + size = cast(size_t) shdr.sh_size; + return true; + } + } + return false; + } + } + + // ── DWARF .debug_line types and constants ──────────────────────── + + private struct LocationInfo + { + int file = -1; + int line = -1; + } + + private struct SourceFile + { + const(char)[] file; + size_t dir_index; // 1-based + } + + private struct LineNumberProgram + { + ulong unit_length; + ushort dwarf_version; + ubyte address_size; + ubyte segment_selector_size; + ulong header_length; + ubyte minimum_instruction_length; + ubyte maximum_operations_per_instruction; + bool default_is_statement; + byte line_base; + ubyte line_range; + ubyte opcode_base; + const(ubyte)[] standard_opcode_lengths; + // Directory and file tables stored as slices into scratch buffers. + // The caller owns the scratch; the LineNumberProgram just borrows. + const(char)[][] include_directories; + size_t num_dirs; + SourceFile[] source_files; + size_t num_files; + const(ubyte)[] program; + } + + private struct StateMachine + { + const(void)* address; + uint operation_index = 0; + uint file_index = 1; + int line = 1; + uint column = 0; + bool is_statement; + bool is_end_sequence = false; + } + + private enum StandardOpcode : ubyte + { + extended_op = 0, + copy = 1, + advance_pc = 2, + advance_line = 3, + set_file = 4, + set_column = 5, + negate_statement = 6, + set_basic_block = 7, + const_add_pc = 8, + fixed_advance_pc = 9, + set_prologue_end = 10, + set_epilogue_begin = 11, + set_isa = 12, + } + + private enum ExtendedOpcode : ubyte + { + end_sequence = 1, + set_address = 2, + define_file = 3, + set_discriminator = 4, + } + + // ── LEB128 and DWARF helpers ───────────────────────────────────── + + private T dw_read(T)(ref const(ubyte)[] buf) nothrow @nogc @trusted + { + if (buf.length < T.sizeof) + return T.init; + version (X86_64) + T result = *cast(const(T)*) buf.ptr; + else version (X86) + T result = *cast(const(T)*) buf.ptr; + else + { + T result = void; + memcpy(&result, buf.ptr, T.sizeof); + } + buf = buf[T.sizeof .. $]; + return result; + } + + private const(char)[] dw_read_stringz(ref const(ubyte)[] buf) nothrow @nogc @trusted + { + auto p = cast(const(char)*) buf.ptr; + auto len = strlen(p); + buf = buf[len + 1 .. $]; + return p[0 .. len]; + } + + private ulong dw_read_uleb128(ref const(ubyte)[] buf) nothrow @nogc + { + ulong val = 0; + uint shift = 0; + while (buf.length > 0) + { + ubyte b = buf[0]; buf = buf[1 .. $]; + val |= cast(ulong)(b & 0x7f) << shift; + if ((b & 0x80) == 0) break; + shift += 7; + } + return val; + } + + private long dw_read_sleb128(ref const(ubyte)[] buf) nothrow @nogc + { + long val = 0; + uint shift = 0; + ubyte b; + while (buf.length > 0) + { + b = buf[0]; buf = buf[1 .. $]; + val |= cast(long)(b & 0x7f) << shift; + shift += 7; + if ((b & 0x80) == 0) break; + } + if (shift < 64 && (b & 0x40) != 0) + val |= -(cast(long) 1 << shift); + return val; + } + + // ── DWARF v5 entry format ──────────────────────────────────────── + + private enum DW_LNCT : ushort + { + path = 1, + directory_index = 2, + } + + private enum DW_FORM : ubyte + { + data1 = 11, + data2 = 5, + data4 = 6, + data8 = 7, + data16 = 30, + string_ = 8, + strp = 14, + line_strp = 31, + udata = 15, + block = 9, + strx = 26, + strx1 = 37, + strx2 = 38, + strx3 = 39, + strx4 = 40, + sec_offset = 23, + sdata = 13, + flag = 12, + flag_present = 25, + } + + private struct EntryFormatPair + { + DW_LNCT type; + DW_FORM form; + } + + /// Skip a DWARF form value we don't care about. + private void dw_skip_form(ref const(ubyte)[] data, DW_FORM form, bool is64bit) nothrow @nogc + { + with (DW_FORM) switch (form) + { + case strp, line_strp, sec_offset: + data = data[is64bit ? 8 : 4 .. $]; break; + case data1, strx1, flag, flag_present: + data = data[1 .. $]; break; + case data2, strx2: + data = data[2 .. $]; break; + case strx3: + data = data[3 .. $]; break; + case data4, strx4: + data = data[4 .. $]; break; + case data8: + data = data[8 .. $]; break; + case data16: + data = data[16 .. $]; break; + case udata, strx, sdata: + dw_read_uleb128(data); break; + case block: + auto length = cast(size_t) dw_read_uleb128(data); + data = data[length .. $]; break; + default: + break; + } + } + + // ── Read DWARF line number program header ──────────────────────── + + // Scratch buffers allocated on the stack for directory/file tables. + // 256 entries each should be more than enough for any compilation unit. + private enum MAX_DIRS = 256; + private enum MAX_FILES = 512; + + private LineNumberProgram dw_read_line_number_program(ref const(ubyte)[] data) nothrow @nogc @trusted + { + const original_data = data; + LineNumberProgram lp; + + bool is_64bit_dwarf = false; + lp.unit_length = dw_read!uint(data); + if (lp.unit_length == uint.max) + { + is_64bit_dwarf = true; + lp.unit_length = dw_read!ulong(data); + } + + const version_field_offset = cast(size_t)(data.ptr - original_data.ptr); + lp.dwarf_version = dw_read!ushort(data); + + if (lp.dwarf_version >= 5) + { + lp.address_size = dw_read!ubyte(data); + lp.segment_selector_size = dw_read!ubyte(data); + } + + lp.header_length = is_64bit_dwarf ? dw_read!ulong(data) : dw_read!uint(data); + + const min_insn_field_offset = cast(size_t)(data.ptr - original_data.ptr); + lp.minimum_instruction_length = dw_read!ubyte(data); + lp.maximum_operations_per_instruction = (lp.dwarf_version >= 4) ? dw_read!ubyte(data) : 1; + lp.default_is_statement = (dw_read!ubyte(data) != 0); + lp.line_base = dw_read!byte(data); + lp.line_range = dw_read!ubyte(data); + lp.opcode_base = dw_read!ubyte(data); + + lp.standard_opcode_lengths = data[0 .. lp.opcode_base - 1]; + data = data[lp.opcode_base - 1 .. $]; + + if (lp.dwarf_version >= 5) + { + // DWARF v5: directory format + entries + auto num_pairs = dw_read!ubyte(data); + EntryFormatPair[8] dir_fmt = void; + foreach (i; 0 .. num_pairs) + { + if (i < 8) + { + dir_fmt[i].type = cast(DW_LNCT) dw_read_uleb128(data); + dir_fmt[i].form = cast(DW_FORM) dw_read_uleb128(data); + } + } + + lp.num_dirs = cast(size_t) dw_read_uleb128(data); + // Caller must provide scratch buffers; we use __gshared static for simplicity. + foreach (d; 0 .. lp.num_dirs) + { + foreach (p; 0 .. num_pairs) + { + if (p < 8 && dir_fmt[p].type == DW_LNCT.path && dir_fmt[p].form == DW_FORM.string_) + { + if (d < MAX_DIRS) + _dir_scratch[d] = dw_read_stringz(data); + else + dw_read_stringz(data); + } + else if (p < 8) + dw_skip_form(data, dir_fmt[p].form, is_64bit_dwarf); + } + } + if (lp.num_dirs > MAX_DIRS) lp.num_dirs = MAX_DIRS; + lp.include_directories = _dir_scratch[0 .. lp.num_dirs]; + + // File format + entries + num_pairs = dw_read!ubyte(data); + EntryFormatPair[8] file_fmt = void; + foreach (i; 0 .. num_pairs) + { + if (i < 8) + { + file_fmt[i].type = cast(DW_LNCT) dw_read_uleb128(data); + file_fmt[i].form = cast(DW_FORM) dw_read_uleb128(data); + } + } + + lp.num_files = cast(size_t) dw_read_uleb128(data); + foreach (f; 0 .. lp.num_files) + { + SourceFile sf; + sf.file = ""; + foreach (p; 0 .. num_pairs) + { + if (p < 8 && file_fmt[p].type == DW_LNCT.path && file_fmt[p].form == DW_FORM.string_) + sf.file = dw_read_stringz(data); + else if (p < 8 && file_fmt[p].type == DW_LNCT.directory_index) + { + if (file_fmt[p].form == DW_FORM.data1) + sf.dir_index = dw_read!ubyte(data); + else if (file_fmt[p].form == DW_FORM.data2) + sf.dir_index = dw_read!ushort(data); + else if (file_fmt[p].form == DW_FORM.udata) + sf.dir_index = cast(size_t) dw_read_uleb128(data); + else + dw_skip_form(data, file_fmt[p].form, is_64bit_dwarf); + sf.dir_index++; // DWARF v5 indices are 0-based, normalize to 1-based + } + else if (p < 8) + dw_skip_form(data, file_fmt[p].form, is_64bit_dwarf); + } + if (f < MAX_FILES) + _file_scratch[f] = sf; + } + if (lp.num_files > MAX_FILES) lp.num_files = MAX_FILES; + lp.source_files = _file_scratch[0 .. lp.num_files]; + } + else + { + // DWARF v3/v4: NUL-terminated sequences + lp.num_dirs = 0; + while (data.length > 0 && data[0] != 0) + { + auto dir = dw_read_stringz(data); + if (lp.num_dirs < MAX_DIRS) + _dir_scratch[lp.num_dirs++] = dir; + } + if (data.length > 0) data = data[1 .. $]; // skip NUL terminator + lp.include_directories = _dir_scratch[0 .. lp.num_dirs]; + + lp.num_files = 0; + while (data.length > 0 && data[0] != 0) + { + SourceFile sf; + sf.file = dw_read_stringz(data); + sf.dir_index = cast(size_t) dw_read_uleb128(data); + dw_read_uleb128(data); // last modification time + dw_read_uleb128(data); // file length + if (lp.num_files < MAX_FILES) + _file_scratch[lp.num_files++] = sf; + } + if (data.length > 0) data = data[1 .. $]; // skip NUL terminator + lp.source_files = _file_scratch[0 .. lp.num_files]; + } + + const program_start = cast(size_t)(min_insn_field_offset + lp.header_length); + const program_end = cast(size_t)(version_field_offset + lp.unit_length); + if (program_start <= original_data.length && program_end <= original_data.length) + lp.program = original_data[program_start .. program_end]; + + data = (program_end <= original_data.length) ? original_data[program_end .. $] : null; + + return lp; + } + + // Static scratch buffers for DWARF parsing (debug-only, no allocator needed). + private __gshared const(char)[][MAX_DIRS] _dir_scratch; + private __gshared SourceFile[MAX_FILES] _file_scratch; + + // ── DWARF state machine — resolve addresses to file:line ───────── + + private struct ResolvedLocation + { + const(char)[] file; + const(char)[] dir; + int line = -1; + } + + /// Resolve an array of addresses to file:line using .debug_line data. + private void dw_resolve_addresses( + const(ubyte)[] debug_line_data, + void*[] addresses, + ResolvedLocation[] results, + size_t base_address) nothrow @nogc @trusted + { + size_t found = 0; + const num_addrs = addresses.length; + + while (debug_line_data.length > 0 && found < num_addrs) + { + auto lp = dw_read_line_number_program(debug_line_data); + if (lp.program.length == 0) + break; + + StateMachine machine; + machine.is_statement = lp.default_is_statement; + + LocationInfo last_loc = LocationInfo(-1, -1); + const(void)* last_address; + + const(ubyte)[] prog = lp.program; + while (prog.length > 0) + { + size_t advance_addr(size_t op_advance) + { + const inc = lp.minimum_instruction_length * + ((machine.operation_index + op_advance) / lp.maximum_operations_per_instruction); + machine.address += inc; + machine.operation_index = + (machine.operation_index + op_advance) % lp.maximum_operations_per_instruction; + return inc; + } + + void emit_row(bool is_end) + { + auto addr = machine.address + base_address; + + foreach (idx; 0 .. num_addrs) + { + if (results[idx].line != -1) + continue; + auto target = addresses[idx]; + + void apply_loc(LocationInfo loc) + { + auto file_idx = loc.file - (lp.dwarf_version < 5 ? 1 : 0); + if (file_idx >= 0 && file_idx < lp.num_files) + { + results[idx].file = lp.source_files[file_idx].file; + auto di = lp.source_files[file_idx].dir_index; + if (di > 0 && di <= lp.num_dirs) + results[idx].dir = lp.include_directories[di - 1]; + } + results[idx].line = loc.line; + found++; + } + + if (target == addr) + apply_loc(LocationInfo(machine.file_index, machine.line)); + else if (last_address !is null && target > last_address && target < addr) + apply_loc(last_loc); + } + + if (is_end) + last_address = null; + else + { + last_address = addr; + last_loc = LocationInfo(machine.file_index, machine.line); + } + } + + ubyte opcode = prog[0]; prog = prog[1 .. $]; + + if (opcode >= lp.opcode_base) + { + // Special opcode + opcode -= lp.opcode_base; + advance_addr(opcode / lp.line_range); + machine.line += lp.line_base + (opcode % lp.line_range); + emit_row(false); + } + else if (opcode == 0) + { + // Extended opcode + auto len = cast(size_t) dw_read_uleb128(prog); + if (prog.length == 0) break; + ubyte eopcode = prog[0]; prog = prog[1 .. $]; + + switch (eopcode) + { + case ExtendedOpcode.end_sequence: + machine.is_end_sequence = true; + emit_row(true); + machine = StateMachine.init; + machine.is_statement = lp.default_is_statement; + break; + case ExtendedOpcode.set_address: + machine.address = dw_read!(const(void)*)(prog); + machine.operation_index = 0; + break; + case ExtendedOpcode.set_discriminator: + dw_read_uleb128(prog); + break; + default: + if (len > 1) + prog = prog[len - 1 .. $]; + break; + } + } + else switch (opcode) with (StandardOpcode) + { + case copy: + emit_row(false); + break; + case advance_pc: + advance_addr(cast(size_t) dw_read_uleb128(prog)); + break; + case advance_line: + machine.line += cast(int) dw_read_sleb128(prog); + break; + case set_file: + machine.file_index = cast(uint) dw_read_uleb128(prog); + break; + case set_column: + machine.column = cast(uint) dw_read_uleb128(prog); + break; + case negate_statement: + machine.is_statement = !machine.is_statement; + break; + case set_basic_block: + break; + case const_add_pc: + advance_addr((255 - lp.opcode_base) / lp.line_range); + break; + case fixed_advance_pc: + machine.address += dw_read!ushort(prog); + machine.operation_index = 0; + break; + case set_prologue_end: + case set_epilogue_begin: + break; + case set_isa: + dw_read_uleb128(prog); + break; + default: + // Unknown standard opcode: skip according to standard_opcode_lengths + if (opcode > 0 && opcode <= lp.standard_opcode_lengths.length) + { + foreach (_; 0 .. lp.standard_opcode_lengths[opcode - 1]) + dw_read_uleb128(prog); + } + break; + } + } + } + } + + // ── Minimal D symbol demangler ───────────────────────────────────── + // + // Extracts the dot-separated qualified name from a D mangled symbol. + // Does not decode type signatures — just returns e.g. "module.Class.func". + + private const(char)[] demangle_symbol( + const(char)[] mangled, return ref char[512] buf) nothrow @nogc @trusted + { + import urt.array : beginsWith; + import urt.conv : parse_uint; + + if (mangled.length < 3 || !mangled.beginsWith("_D")) + return mangled; + + auto src = mangled[2 .. $]; + size_t pos = 0; + bool first = true; + + while (src.length > 0) + { + auto ch = src[0]; + + if (ch >= '1' && ch <= '9') + { + // LName: decimal length followed by that many characters + size_t taken; + size_t len = cast(size_t)parse_uint(src, &taken); + src = src[taken .. $]; + if (len > src.length || pos + len + 1 > buf.length) + break; + + if (!first) + buf[pos++] = '.'; + first = false; + + buf[pos .. pos + len] = src[0 .. len]; + pos += len; + src = src[len .. $]; + } + else if (ch == 'Q') + { + // Back reference: base-26 offset pointing to an earlier LName. + auto q_pos = cast(size_t)(src.ptr - mangled.ptr); + src = src[1 .. $]; + + size_t ref_val = 0; + while (src.length > 0 && src[0] >= 'A' && src[0] <= 'Z') + { + ref_val = ref_val * 26 + (src[0] - 'A'); + src = src[1 .. $]; + } + if (src.length > 0 && src[0] >= 'a' && src[0] <= 'z') + { + ref_val = ref_val * 26 + (src[0] - 'a'); + src = src[1 .. $]; + } + else + break; // malformed + + if (ref_val >= q_pos) + break; + auto target = mangled[q_pos - ref_val .. $]; + if (target.length == 0 || target[0] < '1' || target[0] > '9') + break; + + // Parse LName at target + size_t taken; + size_t len = cast(size_t)parse_uint(target, &taken); + target = target[taken .. $]; + if (len > target.length || pos + len + 1 > buf.length) + break; + + if (!first) + buf[pos++] = '.'; + first = false; + + buf[pos .. pos + len] = target[0 .. len]; + pos += len; + } + else if (ch == '_' && src.length >= 3 && src[1] == '_' + && (src[2] == 'T' || src[2] == 'U')) + { + // Template instance __T/__U: extract name, skip args until Z + src = src[3 .. $]; + + if (src.length > 0 && src[0] >= '1' && src[0] <= '9') + { + size_t taken; + size_t len = cast(size_t)parse_uint(src, &taken); + src = src[taken .. $]; + if (len <= src.length && pos + len + 1 <= buf.length) + { + if (!first) + buf[pos++] = '.'; + first = false; + + buf[pos .. pos + len] = src[0 .. len]; + pos += len; + src = src[len .. $]; + } + } + + // Skip template args until matching Z + int depth = 1; + while (src.length > 0 && depth > 0) + { + if (src[0] == 'Z') + --depth; + else if (src.length >= 3 && src[0] == '_' && src[1] == '_' + && (src[2] == 'T' || src[2] == 'U')) + { + ++depth; + src = src[2 .. $]; + } + src = src[1 .. $]; + } + } + else if (ch == '0') + src = src[1 .. $]; // anonymous — skip + else + break; // type signature — done + } + + if (pos == 0) + return mangled; + + // Append $TypeSignature if there's anything left + if (src.length > 0 && pos + 1 + src.length <= buf.length) + { + buf[pos++] = '$'; + buf[pos .. pos + src.length] = src[]; + pos += src.length; + } + + return buf[0 .. pos]; + } + + // ── Print formatted trace ──────────────────────────────────────── + + private void posix_print_trace(void*[] addrs) nothrow @nogc @trusted + { + if (addrs.length == 0) + return; + + // Skip internal frames (find _d_throw_exception / _d_throwdwarf) + import urt.string : endsWith; + size_t start = 0; + foreach (i, addr; addrs) + { + Dl_info info = void; + if (dladdr(addr, &info) && info.dli_sname !is null) + { + auto sym = info.dli_sname[0 .. strlen(info.dli_sname)]; + if (sym.endsWith("_d_throw_exception") || sym.endsWith("_d_throwdwarf")) + start = i + 1; + } + } + + // Try DWARF .debug_line resolution + ResolvedLocation[32] locations; + foreach (ref loc; locations) + loc = ResolvedLocation.init; + + auto elf = ElfSelf.open(); + scope(exit) elf.close(); + + if (elf.valid()) + { + size_t dbg_offset, dbg_size; + if (elf.find_section(".debug_line", dbg_offset, dbg_size)) + { + auto dbg_region = MappedRegion.map(elf.fd, dbg_offset, dbg_size); + if (dbg_region.data !is null) + { + scope(exit) dbg_region.unmap(); + + auto base_addr = (elf.ehdr.e_type == ET_DYN) + ? get_executable_base_address() : cast(size_t) 0; + + auto dbg_data = dbg_region.data[0 .. dbg_size]; + dw_resolve_addresses(dbg_data, + addrs[0 .. addrs.length], + locations[0 .. addrs.length], + base_addr); + } + } + } + + // Print each frame + foreach (i; start .. addrs.length) + { + auto addr = addrs[i]; + ref loc = locations[i]; + + // Symbol name via dladdr + Dl_info info = void; + const(char)* symname = null; + size_t sym_offset = 0; + if (dladdr(addr, &info) && info.dli_sname !is null) + { + symname = info.dli_sname; + sym_offset = cast(size_t) addr - cast(size_t) info.dli_saddr; + } + + // Format: file:line symbol+0xoffset [0xaddress] + // or: ??:? symbol+0xoffset [0xaddress] + // or: ??:? 0xaddress + + if (loc.line >= 0) + { + // Have file:line + if (loc.dir.length > 0 && loc.dir[$ - 1] != '/') + writef_to!(WriteTarget.stderr, false)(" {0}/{1}:{2}", loc.dir, loc.file, loc.line); + else if (loc.dir.length > 0) + writef_to!(WriteTarget.stderr, false)(" {0}{1}:{2}", loc.dir, loc.file, loc.line); + else + writef_to!(WriteTarget.stderr, false)(" {0}:{1}", loc.file, loc.line); + } + else + write_err(" ??:?"); + + if (symname !is null) + { + auto sym = symname[0 .. strlen(symname)]; + char[512] dbuf = void; + auto demangled = demangle_symbol(sym, dbuf); + writef_to!(WriteTarget.stderr, false)(" {0}+0x{1:x}", demangled, sym_offset); + } + + enum addr_fmt = size_t.sizeof == 4 ? "08x" : "016x"; + writef_to!(WriteTarget.stderr, true)(" [0x{0:" ~ addr_fmt ~ "}]", cast(size_t)addr); + + // Stop at _Dmain + if (symname !is null) + { + auto sym = symname[0 .. strlen(symname)]; + if (sym == "_Dmain") + break; + } + } + } +} // version (!Windows) debug + +private void terminate() nothrow @nogc @trusted +{ + import urt.io : writeln_err; + writeln_err("Unhandled exception -- no catch handler found, terminating."); + + debug + { + if (_tls_trace.length > 0) + { + writeln_err(" stack trace:"); + version (Windows) + dbghelp_print_trace(_tls_trace.addrs[0 .. _tls_trace.length]); + else + posix_print_trace(_tls_trace.addrs[0 .. _tls_trace.length]); + } + } + + version (D_InlineAsm_X86_64) + asm nothrow @nogc { hlt; } + else version (D_InlineAsm_X86) + asm nothrow @nogc { hlt; } + else + { + import urt.internal.stdc : abort; + abort(); + } +} + +// ══════════════════════════════════════════════════════════════════════ +// Platform-specific implementations +// ══════════════════════════════════════════════════════════════════════ + +version (GDC) +{ + static assert(false, "!!"); +} +else version (LDC) +{ + +// ────────────────────────────────────────────────────────────────────── +// LDC MSVC SEH exception handling +// +// LDC on Windows uses the MSVC C++ exception infrastructure. +// _d_throw_exception builds MSVC-compatible type metadata and calls +// RaiseException(). The MSVC CRT's table-based SEH unwinds the stack +// and delivers exceptions to catch landing pads, which call +// _d_eh_enter_catch to extract the D Throwable from the SEH record. +// +// Ported from ldc/eh_msvc.d in LDC's druntime. +// ────────────────────────────────────────────────────────────────────── + +version (Windows) +{ + +// --- Windows ABI types --------------------------------------------------- + +private alias DWORD = uint; +private alias ULONG_PTR = size_t; + +extern (Windows) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, + DWORD nNumberOfArguments, ULONG_PTR* lpArguments) nothrow; + +// --- MSVC SEH structures ------------------------------------------------- + +// On Win64, type info pointers are 32-bit offsets from a heap base. +version (Win64) + private struct ImgPtr(T) { uint offset; } +else + private alias ImgPtr(T) = T*; + +private alias PMFN = ImgPtr!(void function(void*)); + +private struct TypeDescriptor +{ + uint hash; + void* spare; + char[1] name; // variable-size, zero-terminated +} + +private struct PMD +{ + int mdisp; + int pdisp; + int vdisp; +} + +private struct CatchableType +{ + uint properties; + ImgPtr!TypeDescriptor pType; + PMD thisDisplacement; + int sizeOrOffset; + PMFN copyFunction; +} + +private enum CT_IsSimpleType = 0x00000001; + +private struct CatchableTypeArray +{ + int nCatchableTypes; + ImgPtr!CatchableType[1] arrayOfCatchableTypes; // variable size +} + +private struct _ThrowInfo +{ + uint attributes; + PMFN pmfnUnwind; + PMFN pForwardCompat; + ImgPtr!CatchableTypeArray pCatchableTypeArray; +} + +private struct CxxExceptionInfo +{ + size_t Magic; + Throwable* pThrowable; + _ThrowInfo* ThrowInfo; + version (Win64) void* ImgBase; +} + +private enum int STATUS_MSC_EXCEPTION = 0xe0000000 | ('m' << 16) | ('s' << 8) | ('c' << 0); +private enum EXCEPTION_NONCONTINUABLE = 0x01; +private enum EH_MAGIC_NUMBER1 = 0x19930520; + + +// --- EH Heap (image-relative pointer support) ---------------------------- + +version (Win64) +{ + private struct EHHeap + { + void* base; + size_t capacity; + size_t length; + + void initialize(size_t initial_capacity) nothrow @nogc + { + import urt.mem : malloc; + base = malloc(initial_capacity); + capacity = initial_capacity; + length = size_t.sizeof; // offset 0 reserved (null sentinel) + } + + size_t alloc(size_t size) nothrow @nogc + { + import urt.mem : malloc, memcpy; + auto offset = length; + enum align_mask = size_t.sizeof - 1; + auto new_length = (length + size + align_mask) & ~align_mask; + auto new_capacity = capacity; + while (new_length > new_capacity) + new_capacity *= 2; + if (new_capacity != capacity) + { + auto new_base = malloc(new_capacity); + memcpy(new_base, base, length); + // Old base leaks — may be referenced by in-flight exceptions. + base = new_base; + capacity = new_capacity; + } + length = new_length; + return offset; + } + } + + private __gshared EHHeap _eh_heap; + + private ImgPtr!T eh_malloc(T)(size_t size = T.sizeof) nothrow @nogc + { + return ImgPtr!T(cast(uint) _eh_heap.alloc(size)); + } + + private T* to_pointer(T)(ImgPtr!T img_ptr) nothrow @nogc + { + return cast(T*)(cast(ubyte*) _eh_heap.base + img_ptr.offset); + } +} +else // Win32 +{ + private ImgPtr!T eh_malloc(T)(size_t size = T.sizeof) nothrow @nogc + { + import urt.mem : malloc; + return cast(T*) malloc(size); + } + + private T* to_pointer(T)(T* img_ptr) nothrow @nogc + { + return img_ptr; + } +} + + +// --- ThrowInfo cache (simple linear-scan arrays) ------------------------- +// No mutex — OpenWatt's exception paths are single-threaded. + +private enum EH_CACHE_SIZE = 64; + +private __gshared ClassInfo[EH_CACHE_SIZE] _throw_info_keys; +private __gshared ImgPtr!_ThrowInfo[EH_CACHE_SIZE] _throw_info_vals; +private __gshared size_t _throw_info_len; + +private __gshared ClassInfo[EH_CACHE_SIZE] _catchable_keys; +private __gshared ImgPtr!CatchableType[EH_CACHE_SIZE] _catchable_vals; +private __gshared size_t _catchable_len; + +private __gshared bool _eh_initialized; + +private void ensure_eh_init() nothrow @nogc +{ + if (_eh_initialized) + return; + version (Win64) + _eh_heap.initialize(0x10000); + _eh_initialized = true; +} + + +// --- ThrowInfo generation ------------------------------------------------ + +private ImgPtr!CatchableType get_catchable_type(ClassInfo ti) nothrow @nogc +{ + import urt.mem : memcpy; + + foreach (i; 0 .. _catchable_len) + if (_catchable_keys[i] is ti) + return _catchable_vals[i]; + + const sz = TypeDescriptor.sizeof + ti.name.length + 1; + auto td = eh_malloc!TypeDescriptor(sz); + auto ptd = td.to_pointer; + + ptd.hash = 0; + ptd.spare = null; + ptd.name.ptr[0] = 'D'; + memcpy(ptd.name.ptr + 1, ti.name.ptr, ti.name.length); + ptd.name.ptr[ti.name.length + 1] = 0; + + auto ct = eh_malloc!CatchableType(); + ct.to_pointer[0] = CatchableType( + CT_IsSimpleType, td, PMD(0, -1, 0), + cast(int) size_t.sizeof, PMFN.init); + + if (_catchable_len < EH_CACHE_SIZE) + { + _catchable_keys[_catchable_len] = ti; + _catchable_vals[_catchable_len] = ct; + ++_catchable_len; + } + return ct; +} + +private ImgPtr!_ThrowInfo get_throw_info(ClassInfo ti) nothrow @nogc +{ + foreach (i; 0 .. _throw_info_len) + if (_throw_info_keys[i] is ti) + return _throw_info_vals[i]; + + int classes = 0; + for (ClassInfo tic = ti; tic !is null; tic = tic.base) + ++classes; + + const arr_size = int.sizeof + classes * ImgPtr!(CatchableType).sizeof; + ImgPtr!CatchableTypeArray cta = eh_malloc!CatchableTypeArray(arr_size); + to_pointer(cta).nCatchableTypes = classes; + + size_t c = 0; + for (ClassInfo tic = ti; tic !is null; tic = tic.base) + cta.to_pointer.arrayOfCatchableTypes.ptr[c++] = get_catchable_type(tic); + + auto tinf = eh_malloc!_ThrowInfo(); + *(tinf.to_pointer) = _ThrowInfo(0, PMFN.init, PMFN.init, cta); + + if (_throw_info_len < EH_CACHE_SIZE) + { + _throw_info_keys[_throw_info_len] = ti; + _throw_info_vals[_throw_info_len] = tinf; + ++_throw_info_len; + } + return tinf; +} + + +// --- Exception stack (thread-local) -------------------------------------- + +private struct ExceptionStack +{ +nothrow @nogc: + + void push(Throwable e) + { + if (_length == _cap) + grow(); + _p[_length++] = e; + } + + Throwable pop() + { + return _p[--_length]; + } + + void shrink(size_t sz) + { + while (_length > sz) + _p[--_length] = null; + } + + ref inout(Throwable) opIndex(size_t idx) inout + { + return _p[idx]; + } + + size_t find(Throwable e) + { + for (size_t i = _length; i > 0;) + if (_p[--i] is e) + return i; + return ~cast(size_t) 0; + } + + @property size_t length() const { return _length; } + +private: + + void grow() + { + import urt.mem : malloc, free, memcpy; + immutable ncap = _cap ? 2 * _cap : 16; + auto p = cast(Throwable*) malloc(ncap * size_t.sizeof); + if (_length > 0) + memcpy(p, _p, _length * size_t.sizeof); + if (_p !is null) + free(_p); + _p = p; + _cap = ncap; + } + + size_t _length; + Throwable* _p; + size_t _cap; +} + +private ExceptionStack _exception_stack; + + +// --- Exception chaining -------------------------------------------------- + +private Throwable chain_exceptions(Throwable e, Throwable t) nothrow @nogc +{ + if (!cast(Error) e) + if (auto err = cast(Error) t) + { + err.bypassedException = e; + return err; + } + return Throwable.chainTogether(e, t); +} + + +// --- Core SEH API -------------------------------------------------------- + +extern (C) void _d_throw_exception(Throwable throwable) +{ + if (throwable is null || typeid(throwable) is null) + { + terminate(); + assert(0); + } + + auto refcount = throwable.refcount(); + if (refcount) + throwable.refcount() = refcount + 1; + + ensure_eh_init(); + + _exception_stack.push(throwable); + _d_createTrace(throwable, null); + + CxxExceptionInfo info; + info.Magic = EH_MAGIC_NUMBER1; + info.pThrowable = &throwable; + info.ThrowInfo = get_throw_info(typeid(throwable)).to_pointer; + version (Win64) + info.ImgBase = _eh_heap.base; + + RaiseException(STATUS_MSC_EXCEPTION, EXCEPTION_NONCONTINUABLE, + info.sizeof / size_t.sizeof, cast(ULONG_PTR*) &info); +} + +extern (C) Throwable _d_eh_enter_catch(void* ptr, ClassInfo catch_type) +{ + if (ptr is null) + return null; + + auto e = *(cast(Throwable*) ptr); + size_t pos = _exception_stack.find(e); + if (pos >= _exception_stack.length()) + return null; // not a D exception + + auto caught = e; + + // Chain inner unhandled exceptions as collateral + for (size_t p = pos + 1; p < _exception_stack.length(); ++p) + e = chain_exceptions(e, _exception_stack[p]); + _exception_stack.shrink(pos); + + if (e !is caught) + { + if (_d_isbaseof(typeid(e), catch_type)) + *cast(Throwable*) ptr = e; + else + _d_throw_exception(e); // rethrow collateral + } + return e; +} + +extern (C) bool _d_enter_cleanup(void* ptr) nothrow @nogc @trusted +{ + // Prevents LLVM from optimizing away cleanup (finally) blocks. + return true; +} + +extern (C) void _d_leave_cleanup(void* ptr) nothrow @nogc @trusted +{ +} + +} // version (Windows) +else +{ +// Non-Windows LDC: DWARF EH is in the shared block below. +} // else (non-Windows LDC) + +} +else version (Win32) // DMD Win32 +{ + +// ────────────────────────────────────────────────────────────────────── +// Win32 SEH-based exception handling +// ────────────────────────────────────────────────────────────────────── + +// Windows types (inlined to avoid core.sys.windows dependency) +alias DWORD = uint; +alias BYTE = ubyte; +alias PVOID = void*; +alias ULONG_PTR = size_t; + +enum size_t EXCEPTION_MAXIMUM_PARAMETERS = 15; +enum DWORD EXCEPTION_NONCONTINUABLE = 1; +enum EXCEPTION_UNWIND = 6; +enum EXCEPTION_COLLATERAL = 0x100; + +enum DWORD STATUS_DIGITAL_MARS_D_EXCEPTION = + (3 << 30) | (1 << 29) | (0 << 28) | ('D' << 16) | 1; + +struct EXCEPTION_RECORD +{ + DWORD ExceptionCode; + DWORD ExceptionFlags; + EXCEPTION_RECORD* ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR[EXCEPTION_MAXIMUM_PARAMETERS] ExceptionInformation; +} + +enum MAXIMUM_SUPPORTED_EXTENSION = 512; + +struct FLOATING_SAVE_AREA +{ + DWORD ControlWord, StatusWord, TagWord; + DWORD ErrorOffset, ErrorSelector; + DWORD DataOffset, DataSelector; + BYTE[80] RegisterArea; + DWORD Cr0NpxState; +} + +struct CONTEXT +{ + DWORD ContextFlags; + DWORD Dr0, Dr1, Dr2, Dr3, Dr6, Dr7; + FLOATING_SAVE_AREA FloatSave; + DWORD SegGs, SegFs, SegEs, SegDs; + DWORD Edi, Esi, Ebx, Edx, Ecx, Eax; + DWORD Ebp, Eip, SegCs, EFlags, Esp, SegSs; + BYTE[MAXIMUM_SUPPORTED_EXTENSION] ExtendedRegisters; +} + +struct EXCEPTION_POINTERS +{ + EXCEPTION_RECORD* ExceptionRecord; + CONTEXT* ContextRecord; +} + +enum EXCEPTION_DISPOSITION +{ + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind, +} + +alias LanguageSpecificHandler = extern(C) + EXCEPTION_DISPOSITION function( + EXCEPTION_RECORD* exceptionRecord, + DEstablisherFrame* frame, + CONTEXT* context, + void* dispatcherContext); + +extern (Windows) void RaiseException(DWORD, DWORD, DWORD, void*); +extern (Windows) void RtlUnwind(void* targetFrame, void* targetIp, + EXCEPTION_RECORD* pExceptRec, void* valueForEAX); + +extern(C) extern __gshared DWORD _except_list; // FS:[0] + +// Data structures — compiler-generated exception handler tables (Win32) + +struct DEstablisherFrame +{ + DEstablisherFrame* prev; + LanguageSpecificHandler handler; + DWORD table_index; + DWORD ebp; +} + +struct DHandlerInfo +{ + int prev_index; + uint cioffset; // offset to DCatchInfo data from start of table + void* finally_code; // pointer to finally code (!=null if try-finally) +} + +struct DHandlerTable +{ + void* fptr; // pointer to start of function + uint espoffset; + uint retoffset; + DHandlerInfo[1] handler_info; +} + +struct DCatchBlock +{ + ClassInfo type; + uint bpoffset; // EBP offset of catch var + void* code; // catch handler code pointer +} + +struct DCatchInfo +{ + uint ncatches; + DCatchBlock[1] catch_block; +} + +// InFlight exception list (per-stack, swapped on fiber switches) + +EXCEPTION_RECORD* inflight_exception_list = null; + +extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc +{ + auto old = inflight_exception_list; + inflight_exception_list = cast(EXCEPTION_RECORD*) newContext; + return old; +} + +private EXCEPTION_RECORD* skip_collateral_exceptions(EXCEPTION_RECORD* n) nothrow @nogc @trusted +{ + while (n.ExceptionRecord && n.ExceptionFlags & EXCEPTION_COLLATERAL) + n = n.ExceptionRecord; + return n; +} + +// SEH to D exception translation + +private Throwable _d_translate_se_to_d_exception( + EXCEPTION_RECORD* exceptionRecord, CONTEXT*) nothrow @nogc @trusted +{ + if (exceptionRecord.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) + return cast(Throwable) cast(void*)(exceptionRecord.ExceptionInformation[0]); + + // Non-D (hardware) exceptions: cannot create Error objects without GC. + terminate(); + assert(0); +} + +// _d_throwc — throw a D exception via Windows SEH + +private void throw_impl(Throwable h) @trusted +{ + auto refcount = h.refcount(); + if (refcount) + h.refcount() = refcount + 1; + + _d_createTrace(h, null); + RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, + EXCEPTION_NONCONTINUABLE, 1, cast(void*)&h); +} + +extern(C) void _d_throwc(Throwable h) @trusted +{ + version (D_InlineAsm_X86) + asm + { + naked; + enter 0, 0; + mov EAX, [EBP + 8]; + call throw_impl; + leave; + ret; + } +} + +// _d_framehandler — SEH frame handler called by OS for each frame. +// The handler table address is passed in EAX by compiler-generated thunks. + +extern(C) EXCEPTION_DISPOSITION _d_framehandler( + EXCEPTION_RECORD* exceptionRecord, + DEstablisherFrame* frame, + CONTEXT* context, + void* dispatcherContext) @trusted +{ + DHandlerTable* handler_table; + asm { mov handler_table, EAX; } + + if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) + { + // Unwind phase: call all finally blocks in this frame + _d_local_unwind(handler_table, frame, -1, &unwindCollisionExceptionHandler); + } + else + { + // Search phase: look for a matching catch handler + + EXCEPTION_RECORD* master = null; + ClassInfo master_class_info = null; + + int prev_ndx; + for (auto ndx = frame.table_index; ndx != -1; ndx = prev_ndx) + { + auto phi = &handler_table.handler_info.ptr[ndx]; + prev_ndx = phi.prev_index; + + if (phi.cioffset) + { + auto pci = cast(DCatchInfo*)(cast(ubyte*) handler_table + phi.cioffset); + auto ncatches = pci.ncatches; + + foreach (i; 0 .. ncatches) + { + auto pcb = &pci.catch_block.ptr[i]; + + // Walk the collateral exception chain to find the master + EXCEPTION_RECORD* er = exceptionRecord; + master = null; + master_class_info = null; + + for (;;) + { + if (er.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) + { + ClassInfo ci = (**(cast(ClassInfo**)(er.ExceptionInformation[0]))); + if (!master && !(er.ExceptionFlags & EXCEPTION_COLLATERAL)) + { + master = er; + master_class_info = ci; + break; + } + if (_d_isbaseof(ci, typeid(Error))) + { + master = er; + master_class_info = ci; + } + } + else + { + // Non-D exception — cannot translate without GC + terminate(); + } + + if (!(er.ExceptionFlags & EXCEPTION_COLLATERAL)) + break; + + if (er.ExceptionRecord) + er = er.ExceptionRecord; + else + er = inflight_exception_list; + } + + if (_d_isbaseof(master_class_info, pcb.type)) + { + // Found matching catch handler + + auto original_exception = skip_collateral_exceptions(exceptionRecord); + if (original_exception.ExceptionRecord is null + && !(exceptionRecord is inflight_exception_list)) + original_exception.ExceptionRecord = inflight_exception_list; + inflight_exception_list = exceptionRecord; + + // Global unwind: call finally blocks in intervening frames + _d_global_unwind(frame, exceptionRecord); + + // Local unwind: call finally blocks skipped in this frame + _d_local_unwind(handler_table, frame, ndx, + &searchCollisionExceptionHandler); + + frame.table_index = prev_ndx; + + // Build D exception chain from SEH records + EXCEPTION_RECORD* z = exceptionRecord; + Throwable prev = null; + Error master_error = null; + + for (;;) + { + Throwable w = _d_translate_se_to_d_exception(z, context); + if (z == master && (z.ExceptionFlags & EXCEPTION_COLLATERAL)) + master_error = cast(Error) w; + prev = Throwable.chainTogether(w, prev); + if (!(z.ExceptionFlags & EXCEPTION_COLLATERAL)) + break; + z = z.ExceptionRecord; + } + + Throwable pti; + if (master_error) + { + master_error.bypassedException = prev; + pti = master_error; + } + else + pti = prev; + + inflight_exception_list = z.ExceptionRecord; + + // Initialize catch variable and jump to handler + int regebp = cast(int)&frame.ebp; + *cast(Object*)(regebp + pcb.bpoffset) = pti; + + { + uint catch_esp; + alias fp_t = void function(); + fp_t catch_addr = cast(fp_t) pcb.code; + catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; + asm + { + mov EAX, catch_esp; + mov ECX, catch_addr; + mov [EAX], ECX; + mov EBP, regebp; + mov ESP, EAX; + ret; + } + } + } + } + } + } + } + return EXCEPTION_DISPOSITION.ExceptionContinueSearch; +} + +// Exception filter for __try/__except around Dmain + +extern(C) int _d_exception_filter(EXCEPTION_POINTERS* eptrs, + int retval, Object* exception_object) @trusted +{ + *exception_object = _d_translate_se_to_d_exception( + eptrs.ExceptionRecord, eptrs.ContextRecord); + return retval; +} + +// Collision exception handlers + +extern(C) EXCEPTION_DISPOSITION searchCollisionExceptionHandler( + EXCEPTION_RECORD* exceptionRecord, + DEstablisherFrame*, + CONTEXT*, + void* dispatcherContext) @trusted +{ + if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) + { + auto n = skip_collateral_exceptions(exceptionRecord); + n.ExceptionFlags |= EXCEPTION_COLLATERAL; + return EXCEPTION_DISPOSITION.ExceptionContinueSearch; + } + // Collision during SEARCH phase — restart from 'frame' + *(cast(void**) dispatcherContext) = dispatcherContext; // frame + return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; +} + +extern(C) EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( + EXCEPTION_RECORD* exceptionRecord, + DEstablisherFrame* frame, + CONTEXT*, + void* dispatcherContext) @trusted +{ + if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) + { + auto n = skip_collateral_exceptions(exceptionRecord); + n.ExceptionFlags |= EXCEPTION_COLLATERAL; + return EXCEPTION_DISPOSITION.ExceptionContinueSearch; + } + // Collision during UNWIND phase — restart from 'frame.prev' + *(cast(void**) dispatcherContext) = frame.prev; + return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; +} + +// Local unwind — run finally blocks in the current frame + +extern(C) void _d_local_unwind(DHandlerTable* handler_table, + DEstablisherFrame* frame, int stop_index, + LanguageSpecificHandler collision_handler) @trusted +{ + // Install collision handler on SEH chain + asm + { + push dword ptr -1; + push dword ptr 0; + push collision_handler; + push dword ptr FS:_except_list; + mov FS:_except_list, ESP; + } + + for (auto i = frame.table_index; i != -1 && i != stop_index;) + { + auto phi = &handler_table.handler_info.ptr[i]; + i = phi.prev_index; + + if (phi.finally_code) + { + auto catch_ebp = &frame.ebp; + auto blockaddr = phi.finally_code; + asm + { + push EBX; + mov EBX, blockaddr; + push EBP; + mov EBP, catch_ebp; + call EBX; + pop EBP; + pop EBX; + } + } + } + + // Remove collision handler from SEH chain + asm + { + pop FS:_except_list; + add ESP, 12; + } +} + +// Global unwind — thin wrapper around RtlUnwind + +extern(C) int _d_global_unwind(DEstablisherFrame* pFrame, + EXCEPTION_RECORD* eRecord) @trusted +{ + asm + { + naked; + push EBP; + mov EBP, ESP; + push ECX; + push EBX; + push ESI; + push EDI; + push EBP; + push 0; + push dword ptr 12[EBP]; // eRecord + call __system_unwind; + jmp __unwind_exit; + __system_unwind: + push dword ptr 8[EBP]; // pFrame + call RtlUnwind; + __unwind_exit: + pop EBP; + pop EDI; + pop ESI; + pop EBX; + pop ECX; + mov ESP, EBP; + pop EBP; + ret; + } +} + +// Local unwind for goto/return across finally blocks + +extern(C) void _d_local_unwind2() @trusted +{ + asm + { + naked; + jmp _d_localUnwindForGoto; + } +} + +extern(C) void _d_localUnwindForGoto(DHandlerTable* handler_table, + DEstablisherFrame* frame, int stop_index) @trusted +{ + _d_local_unwind(handler_table, frame, stop_index, + &searchCollisionExceptionHandler); +} + +// Monitor handler stubs (for synchronized blocks) + +extern(C) EXCEPTION_DISPOSITION _d_monitor_handler( + EXCEPTION_RECORD* exceptionRecord, + DEstablisherFrame*, + CONTEXT*, + void*) @trusted +{ + if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) + { + // TODO: _d_monitorexit(cast(Object)cast(void*)frame.table_index); + } + return EXCEPTION_DISPOSITION.ExceptionContinueSearch; +} + +extern(C) void _d_monitor_prolog(void*, void*, Object) @trusted +{ + // TODO: _d_monitorenter(h); +} + +extern(C) void _d_monitor_epilog(void*, void*, Object) @trusted +{ + // TODO: _d_monitorexit(h); +} + + +} +else version (Win64) +{ + +// ────────────────────────────────────────────────────────────────────── +// Win64 — RBP-chain walking with ._deh section tables +// ────────────────────────────────────────────────────────────────────── + +// Data structures — compiler-generated exception handler tables + +struct DHandlerInfo +{ + uint offset; // offset from function address to start of guarded section + uint endoffset; // offset of end of guarded section + int prev_index; // previous table index + uint cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch) + size_t finally_offset; // offset to finally code to execute (!=0 if try-finally) +} + +struct DHandlerTable +{ + uint espoffset; // offset of ESP from EBP + uint retoffset; // offset from start of function to return code + size_t nhandlers; // dimension of handler_info[] + DHandlerInfo[1] handler_info; +} + +struct DCatchBlock +{ + ClassInfo type; // catch type + size_t bpoffset; // EBP offset of catch var + size_t codeoffset; // catch handler offset +} + +struct DCatchInfo +{ + size_t ncatches; // number of catch blocks + DCatchBlock[1] catch_block; +} + +struct FuncTable +{ + void* fptr; // pointer to start of function + DHandlerTable* handlertable; + uint fsize; // size of function in bytes +} + +// InFlight exception tracking (per-stack, swapped on fiber switches) + +private struct InFlight +{ + InFlight* next; + void* addr; + Throwable t; +} + +private __gshared InFlight* __inflight = null; + +extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc +{ + auto old = __inflight; + __inflight = cast(InFlight*) newContext; + return old; +} + +// DEH section scanning — find exception handler tables in the binary + +version (Windows) + extern(C) extern __gshared ubyte __ImageBase; + +private __gshared immutable(FuncTable)* _deh_start; +private __gshared immutable(FuncTable)* _deh_end; + +private void ensure_deh_loaded() nothrow @nogc @trusted +{ + if (_deh_start !is null) + return; + + version (Windows) + { + auto section = find_pe_section(cast(void*) &__ImageBase, "._deh\0\0\0"); + if (section.length) + { + _deh_start = cast(immutable(FuncTable)*) section.ptr; + _deh_end = cast(immutable(FuncTable)*)(section.ptr + section.length); + } + } + else version (linux) + { + // TODO: ELF section scanning for .deh + } +} + +/// PE section lookup — duplicated from urt.package to avoid import cycle. +private void[] find_pe_section(void* imageBase, string name) nothrow @nogc @trusted +{ + if (name.length > 8) return null; + + auto base = cast(ubyte*) imageBase; + if (base[0] != 0x4D || base[1] != 0x5A) + return null; + + auto lfanew = *cast(int*)(base + 0x3C); + auto pe = base + lfanew; + if (pe[0] != 'P' || pe[1] != 'E' || pe[2] != 0 || pe[3] != 0) + return null; + + auto fileHeader = pe + 4; + ushort numSections = *cast(ushort*)(fileHeader + 2); + ushort optHeaderSize = *cast(ushort*)(fileHeader + 16); + auto sections = fileHeader + 20 + optHeaderSize; + + foreach (i; 0 .. numSections) + { + auto sec = sections + i * 40; + auto secName = (cast(char*) sec)[0 .. 8]; + bool match = true; + foreach (j; 0 .. 8) + { + if (secName[j] != name[j]) + { + match = false; + break; + } + } + if (match) + { + auto virtualSize = *cast(uint*)(sec + 8); + auto virtualAddress = *cast(uint*)(sec + 12); + return (base + virtualAddress)[0 .. virtualSize]; + } + } + return null; +} + +// Handler table lookup + +immutable(FuncTable)* __eh_finddata(void* address) nothrow @nogc @trusted +{ + ensure_deh_loaded(); + if (_deh_start is null) + return null; + return __eh_finddata_range(address, _deh_start, _deh_end); +} + +immutable(FuncTable)* __eh_finddata_range(void* address, + immutable(FuncTable)* pstart, immutable(FuncTable)* pend) nothrow @nogc @trusted +{ + for (auto ft = pstart; ; ft++) + { + Lagain: + if (ft >= pend) + break; + + version (Win64) + { + // MS Linker sometimes inserts zero padding between .obj sections + if (ft.fptr == null) + { + ft = cast(immutable(FuncTable)*)(cast(void**) ft + 1); + goto Lagain; + } + } + + immutable(void)* fptr = ft.fptr; + version (Win64) + { + // Follow JMP indirection from /DEBUG linker + if ((cast(ubyte*) fptr)[0] == 0xE9) + fptr = fptr + 5 + *cast(int*)(fptr + 1); + } + + if (fptr <= address && address < cast(void*)(cast(char*) fptr + ft.fsize)) + return ft; + } + return null; +} + +// Stack frame walking + +size_t __eh_find_caller(size_t regbp, size_t* pretaddr) nothrow @nogc @trusted +{ + size_t bp = *cast(size_t*) regbp; + + if (bp) + { + // Stack grows downward — new BP must be above old + if (bp <= regbp) + terminate(); + + *pretaddr = *cast(size_t*)(regbp + size_t.sizeof); + } + return bp; +} + +// _d_throwc — the core throw implementation (RBP-chain walking) + +alias fp_t = int function(); + +extern(C) void _d_throwc(Throwable h) @trusted +{ + size_t regebp; + + version (D_InlineAsm_X86) + asm { mov regebp, EBP; } + else version (D_InlineAsm_X86_64) + asm { mov regebp, RBP; } + + // Increment reference count if refcounted + auto refcount = h.refcount(); + if (refcount) + h.refcount() = refcount + 1; + + _d_createTrace(h, null); + + while (1) + { + size_t retaddr; + + regebp = __eh_find_caller(regebp, &retaddr); + if (!regebp) + break; + + auto func_table = __eh_finddata(cast(void*) retaddr); + auto handler_table = func_table ? func_table.handlertable : null; + if (!handler_table) + continue; + + auto funcoffset = cast(size_t) func_table.fptr; + version (Win64) + { + // Follow JMP indirection from /DEBUG linker + if ((cast(ubyte*) funcoffset)[0] == 0xE9) + funcoffset = funcoffset + 5 + *cast(int*)(funcoffset + 1); + } + + // Find start index for retaddr in handler table + auto dim = handler_table.nhandlers; + auto index = -1; + for (uint i = 0; i < dim; i++) + { + auto phi = &handler_table.handler_info.ptr[i]; + if (retaddr > funcoffset + phi.offset && + retaddr <= funcoffset + phi.endoffset) + index = i; + } + + // Handle inflight exception chaining + if (dim) + { + auto phi = &handler_table.handler_info.ptr[index + 1]; + auto prev = cast(InFlight*) &__inflight; + auto curr = prev.next; + + if (curr !is null && curr.addr == cast(void*)(funcoffset + phi.finally_offset)) + { + auto e = cast(Error) h; + if (e !is null && (cast(Error) curr.t) is null) + { + e.bypassedException = curr.t; + prev.next = curr.next; + } + else + { + h = Throwable.chainTogether(curr.t, h); + prev.next = curr.next; + } + } + } + + // Walk handler table entries + int prev_ndx; + for (auto ndx = index; ndx != -1; ndx = prev_ndx) + { + auto phi = &handler_table.handler_info.ptr[ndx]; + prev_ndx = phi.prev_index; + + if (phi.cioffset) + { + // Catch handler + auto pci = cast(DCatchInfo*)(cast(char*) handler_table + phi.cioffset); + auto ncatches = pci.ncatches; + + for (uint i = 0; i < ncatches; i++) + { + auto ci = **cast(ClassInfo**) h; + auto pcb = &pci.catch_block.ptr[i]; + + if (_d_isbaseof(ci, pcb.type)) + { + // Initialize catch variable + *cast(void**)(regebp + pcb.bpoffset) = cast(void*) h; + + // Jump to catch block — does not return + { + size_t catch_esp; + fp_t catch_addr; + + catch_addr = cast(fp_t)(funcoffset + pcb.codeoffset); + catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; + + version (D_InlineAsm_X86) + asm + { + mov EAX, catch_esp; + mov ECX, catch_addr; + mov [EAX], ECX; + mov EBP, regebp; + mov ESP, EAX; + ret; + } + else version (D_InlineAsm_X86_64) + asm + { + mov RAX, catch_esp; + mov RCX, catch_esp; + mov RCX, catch_addr; + mov [RAX], RCX; + mov RBP, regebp; + mov RSP, RAX; + ret; + } + } + } + } + } + else if (phi.finally_offset) + { + // Finally block + auto blockaddr = cast(void*)(funcoffset + phi.finally_offset); + InFlight inflight; + + inflight.addr = blockaddr; + inflight.next = __inflight; + inflight.t = h; + __inflight = &inflight; + + version (D_InlineAsm_X86) + asm + { + push EBX; + mov EBX, blockaddr; + push EBP; + mov EBP, regebp; + call EBX; + pop EBP; + pop EBX; + } + else version (D_InlineAsm_X86_64) + asm + { + sub RSP, 8; + push RBX; + mov RBX, blockaddr; + push RBP; + mov RBP, regebp; + call RBX; + pop RBP; + pop RBX; + add RSP, 8; + } + + if (__inflight is &inflight) + __inflight = __inflight.next; + } + } + } + terminate(); +} + +} // version (Win64) +// Shared DWARF block — not part of the version chain above. +// Compiles on all non-Windows platforms (DMD Linux, LDC everywhere). +version (Windows) {} else +{ + +// ────────────────────────────────────────────────────────────────────── +// DWARF exception handling (shared by DMD and LDC) +// +// Uses the GCC/DWARF unwinder (libgcc_s / libunwind). +// pragma(mangle) selects the compiler-specific symbol names: +// DMD: _d_throwdwarf, __dmd_begin_catch, __dmd_personality_v0 +// LDC: _d_throw_exception, _d_eh_enter_catch, _d_eh_personality +// +// Ported from druntime rt/dwarfeh.d. +// Copyright: Digital Mars 2015-2016 (original), uRT authors (port). +// License: Boost Software License 1.0 +// ────────────────────────────────────────────────────────────────────── + +// ── libunwind / libgcc_s bindings ─────────────────────────────────── + +private: + +alias _Unwind_Ptr = size_t; +alias _Unwind_Word = size_t; +alias _Unwind_Exception_Class = ulong; +alias _uleb128_t = size_t; +alias _sleb128_t = ptrdiff_t; + +alias _Unwind_Reason_Code = int; +enum +{ + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, +} + +alias _Unwind_Action = int; +enum : _Unwind_Action +{ + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, +} + +alias _Unwind_Exception_Cleanup_Fn = extern(C) void function( + _Unwind_Reason_Code, _Unwind_Exception*); + +version (X86_64) +{ + align(16) struct _Unwind_Exception + { + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + _Unwind_Word private_1; + _Unwind_Word private_2; + } +} +else +{ + align(8) struct _Unwind_Exception + { + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + _Unwind_Word private_1; + _Unwind_Word private_2; + } +} + +struct _Unwind_Context; + +extern(C) nothrow @nogc +{ + _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*); + void _Unwind_DeleteException(_Unwind_Exception*); + _Unwind_Word _Unwind_GetGR(_Unwind_Context*, int); + void _Unwind_SetGR(_Unwind_Context*, int, _Unwind_Word); + _Unwind_Ptr _Unwind_GetIP(_Unwind_Context*); + _Unwind_Ptr _Unwind_GetIPInfo(_Unwind_Context*, int*); + void _Unwind_SetIP(_Unwind_Context*, _Unwind_Ptr); + void* _Unwind_GetLanguageSpecificData(_Unwind_Context*); + _Unwind_Ptr _Unwind_GetRegionStart(_Unwind_Context*); +} + + +// ── ARM EABI unwinder types ──────────────────────────────────────── + +version (ARM) +{ + alias _Unwind_State = int; + enum : _Unwind_State + { + _US_VIRTUAL_UNWIND_FRAME = 0, + _US_UNWIND_FRAME_STARTING = 1, + _US_UNWIND_FRAME_RESUME = 2, + _US_ACTION_MASK = 3, + _US_FORCE_UNWIND = 8, + } + + extern(C) void _Unwind_Complete(_Unwind_Exception*) nothrow @nogc; +} + +// ── EH register numbers (architecture-specific) ──────────────────── + +version (X86_64) +{ + enum eh_exception_regno = 0; + enum eh_selector_regno = 1; +} +else version (X86) +{ + enum eh_exception_regno = 0; + enum eh_selector_regno = 2; +} +else version (AArch64) +{ + enum eh_exception_regno = 0; + enum eh_selector_regno = 1; +} +else version (ARM) +{ + enum eh_exception_regno = 0; + enum eh_selector_regno = 1; +} +else version (RISCV64) +{ + enum eh_exception_regno = 10; + enum eh_selector_regno = 11; +} +else version (RISCV32) +{ + enum eh_exception_regno = 10; + enum eh_selector_regno = 11; +} +else + static assert(0, "Unknown EH register numbers for this architecture"); + +// ── DWARF encoding constants ─────────────────────────────────────── + +enum +{ + DW_EH_PE_FORMAT_MASK = 0x0F, + DW_EH_PE_APPL_MASK = 0x70, + DW_EH_PE_indirect = 0x80, + + DW_EH_PE_omit = 0xFF, + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, +} + +// ── DMD exception class identifier ───────────────────────────────── + +enum _Unwind_Exception_Class dmd_exception_class = + (cast(_Unwind_Exception_Class)'D' << 56) | + (cast(_Unwind_Exception_Class)'M' << 48) | + (cast(_Unwind_Exception_Class)'D' << 40) | + (cast(_Unwind_Exception_Class)'D' << 24); + +// ── Compiler-specific symbol names ───────────────────────────────── + +version (LDC) +{ + private enum throw_mangle = "_d_throw_exception"; + private enum catch_mangle = "_d_eh_enter_catch"; + private enum personality_mangle = "_d_eh_personality"; +} +else +{ + private enum throw_mangle = "_d_throwdwarf"; + private enum catch_mangle = "__dmd_begin_catch"; + private enum personality_mangle = "__dmd_personality_v0"; +} + +// ── ExceptionHeader ──────────────────────────────────────────────── + +struct ExceptionHeader +{ + Throwable object; + _Unwind_Exception exception_object; + + int handler; + const(ubyte)* language_specific_data; + _Unwind_Ptr landing_pad; + + ExceptionHeader* next; + + static ExceptionHeader* stack; // thread-local chain + static ExceptionHeader ehstorage; // pre-allocated (one per thread) + + static ExceptionHeader* create(Throwable o) nothrow @nogc + { + import urt.mem : calloc; + auto eh = &ehstorage; + if (eh.object) + { + eh = cast(ExceptionHeader*) calloc(1, ExceptionHeader.sizeof); + if (!eh) + dwarf_terminate(__LINE__); + } + eh.object = o; + eh.exception_object.exception_class = dmd_exception_class; + return eh; + } + + static void release(ExceptionHeader* eh) nothrow @nogc + { + import urt.mem : free; + *eh = ExceptionHeader.init; + if (eh != &ehstorage) + free(eh); + } + + void push() nothrow @nogc + { + next = stack; + stack = &this; + } + + static ExceptionHeader* pop() nothrow @nogc + { + auto eh = stack; + stack = eh.next; + return eh; + } + + static ExceptionHeader* to_exception_header(_Unwind_Exception* eo) nothrow @nogc + { + return cast(ExceptionHeader*)(cast(void*) eo - ExceptionHeader.exception_object.offsetof); + } +} + +// ── Helpers ──────────────────────────────────────────────────────── + +_Unwind_Ptr read_unaligned(T, bool consume)(ref const(ubyte)* p) nothrow @nogc @trusted +{ + import urt.processor : SupportUnalignedLoadStore; + static if (SupportUnalignedLoadStore) + T value = *cast(T*) p; + else + { + import urt.mem : memcpy; + T value = void; + memcpy(&value, p, T.sizeof); + } + + static if (consume) + p += T.sizeof; + return cast(_Unwind_Ptr) value; +} + +_uleb128_t u_leb128(const(ubyte)** p) nothrow @nogc +{ + auto q = *p; + _uleb128_t result = 0; + uint shift = 0; + while (1) + { + ubyte b = *q++; + result |= cast(_uleb128_t)(b & 0x7F) << shift; + if ((b & 0x80) == 0) + break; + shift += 7; + } + *p = q; + return result; +} + +_sleb128_t s_leb128(const(ubyte)** p) nothrow @nogc +{ + auto q = *p; + ubyte b; + _sleb128_t result = 0; + uint shift = 0; + while (1) + { + b = *q++; + result |= cast(_sleb128_t)(b & 0x7F) << shift; + shift += 7; + if ((b & 0x80) == 0) + break; + } + if (shift < result.sizeof * 8 && (b & 0x40)) + result |= -(cast(_sleb128_t)1 << shift); + *p = q; + return result; +} + +void dwarf_terminate(uint line) nothrow @nogc +{ + import urt.io : writef_to, WriteTarget; + import urt.internal.stdc : abort; + writef_to!(WriteTarget.stderr, true)("dwarfeh({0}) fatal error", line); + abort(); +} + +// ── LSDA scanning ────────────────────────────────────────────────── + +enum LsdaResult +{ + not_found, + foreign, + corrupt, + no_action, + cleanup, + handler, +} + +ClassInfo get_class_info(_Unwind_Exception* exception_object, const(ubyte)* current_lsd) nothrow @nogc +{ + ExceptionHeader* eh = ExceptionHeader.to_exception_header(exception_object); + Throwable ehobject = eh.object; + for (ExceptionHeader* ehn = eh.next; ehn; ehn = ehn.next) + { + if (current_lsd != ehn.language_specific_data) + break; + Error e = cast(Error) ehobject; + if (e is null || (cast(Error) ehn.object) !is null) + ehobject = ehn.object; + } + return typeid(ehobject); +} + +int action_table_lookup(_Unwind_Exception* exception_object, uint action_record_ptr, const(ubyte)* p_action_table, const(ubyte)* tt, + ubyte ttype, _Unwind_Exception_Class exception_class, const(ubyte)* lsda) nothrow @nogc +{ + ClassInfo thrown_type; + if (exception_class == dmd_exception_class) + thrown_type = get_class_info(exception_object, lsda); + + for (auto ap = p_action_table + action_record_ptr - 1; 1; ) + { + auto type_filter = s_leb128(&ap); + auto apn = ap; + auto next_record_ptr = s_leb128(&ap); + + if (type_filter <= 0) + return -1; + + _Unwind_Ptr entry; + const(ubyte)* tt2; + switch (ttype & DW_EH_PE_FORMAT_MASK) + { + case DW_EH_PE_sdata2: entry = read_unaligned!(short, false)(tt2 = tt - type_filter * 2); break; + case DW_EH_PE_udata2: entry = read_unaligned!(ushort, false)(tt2 = tt - type_filter * 2); break; + case DW_EH_PE_sdata4: entry = read_unaligned!(int, false)(tt2 = tt - type_filter * 4); break; + case DW_EH_PE_udata4: entry = read_unaligned!(uint, false)(tt2 = tt - type_filter * 4); break; + case DW_EH_PE_sdata8: entry = read_unaligned!(long, false)(tt2 = tt - type_filter * 8); break; + case DW_EH_PE_udata8: entry = read_unaligned!(ulong, false)(tt2 = tt - type_filter * 8); break; + case DW_EH_PE_ptr: if (size_t.sizeof == 8) + goto case DW_EH_PE_udata8; + else + goto case DW_EH_PE_udata4; + default: + return -1; + } + if (!entry) + return -1; + + switch (ttype & DW_EH_PE_APPL_MASK) + { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + entry += cast(_Unwind_Ptr) tt2; + break; + default: + return -1; + } + if (ttype & DW_EH_PE_indirect) + entry = *cast(_Unwind_Ptr*) entry; + + ClassInfo ci = cast(ClassInfo) cast(void*) entry; + + // D exception — check class hierarchy + if (exception_class == dmd_exception_class && _d_isbaseof(thrown_type, ci)) + return cast(int) type_filter; + + if (!next_record_ptr) + return 0; + + ap = apn + next_record_ptr; + } + assert(0); // unreachable — all paths return inside the loop +} + +LsdaResult scan_lsda(const(ubyte)* lsda, _Unwind_Ptr ip, _Unwind_Exception_Class exception_class, bool cleanups_only, bool prefer_handler, + _Unwind_Exception* exception_object, out _Unwind_Ptr landing_pad, out int handler) nothrow @nogc +{ + auto p = lsda; + if (!p) + return LsdaResult.no_action; + + _Unwind_Ptr dw_pe_value(ubyte pe) + { + switch (pe) + { + case DW_EH_PE_sdata2: return read_unaligned!(short, true)(p); + case DW_EH_PE_udata2: return read_unaligned!(ushort, true)(p); + case DW_EH_PE_sdata4: return read_unaligned!(int, true)(p); + case DW_EH_PE_udata4: return read_unaligned!(uint, true)(p); + case DW_EH_PE_sdata8: return read_unaligned!(long, true)(p); + case DW_EH_PE_udata8: return read_unaligned!(ulong, true)(p); + case DW_EH_PE_sleb128: return cast(_Unwind_Ptr) s_leb128(&p); + case DW_EH_PE_uleb128: return cast(_Unwind_Ptr) u_leb128(&p); + case DW_EH_PE_ptr: if (size_t.sizeof == 8) + goto case DW_EH_PE_udata8; + else + goto case DW_EH_PE_udata4; + default: + dwarf_terminate(__LINE__); + return 0; + } + } + + ubyte lp_start = *p++; + + _Unwind_Ptr lp_base = 0; + if (lp_start != DW_EH_PE_omit) + lp_base = dw_pe_value(lp_start); + + ubyte ttype = *p++; + _Unwind_Ptr tt_base = 0; + _Unwind_Ptr tt_offset = 0; + if (ttype != DW_EH_PE_omit) + { + tt_base = u_leb128(&p); + tt_offset = (p - lsda) + tt_base; + } + + ubyte call_site_format = *p++; + _Unwind_Ptr call_site_table_size = dw_pe_value(DW_EH_PE_uleb128); + + _Unwind_Ptr ip_offset = ip - lp_base; + bool no_action = false; + auto tt = lsda + tt_offset; + const(ubyte)* p_action_table = p + call_site_table_size; + + while (1) + { + if (p >= p_action_table) + { + if (p == p_action_table) + break; + return LsdaResult.corrupt; + } + + _Unwind_Ptr call_site_start = dw_pe_value(call_site_format); + _Unwind_Ptr call_site_range = dw_pe_value(call_site_format); + _Unwind_Ptr call_site_lp = dw_pe_value(call_site_format); + _uleb128_t action_record_ptr_val = u_leb128(&p); + + if (ip_offset < call_site_start) + break; + + if (ip_offset < call_site_start + call_site_range) + { + if (action_record_ptr_val) + { + if (cleanups_only) + continue; + + auto h = action_table_lookup(exception_object, cast(uint) action_record_ptr_val, + p_action_table, tt, ttype, exception_class, lsda); + if (h < 0) + return LsdaResult.corrupt; + if (h == 0) + continue; + + no_action = false; + landing_pad = call_site_lp; + handler = h; + } + else if (call_site_lp) + { + if (prefer_handler && handler) + continue; + no_action = false; + landing_pad = call_site_lp; + handler = 0; + } + else + no_action = true; + } + } + + if (no_action) + return LsdaResult.no_action; + + if (landing_pad) + return handler ? LsdaResult.handler : LsdaResult.cleanup; + + return LsdaResult.not_found; +} + +// ── Public API ────────────────────────────────────────────────────── + +pragma(mangle, catch_mangle) +extern(C) Throwable dwarfeh_begin_catch(_Unwind_Exception* exception_object) nothrow @nogc +{ + version (ARM) version (LDC) + _Unwind_Complete(exception_object); + + ExceptionHeader* eh = ExceptionHeader.to_exception_header(exception_object); + auto o = eh.object; + eh.object = null; + + if (eh != ExceptionHeader.pop()) + dwarf_terminate(__LINE__); + + _Unwind_DeleteException(&eh.exception_object); + return o; +} + +extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc +{ + auto old = ExceptionHeader.stack; + ExceptionHeader.stack = cast(ExceptionHeader*) newContext; + return old; +} + +pragma(mangle, throw_mangle) +extern(C) void dwarfeh_throw(Throwable o) +{ + import urt.io : writeln_err, writef_to, WriteTarget; + import urt.internal.stdc : abort; + + ExceptionHeader* eh = ExceptionHeader.create(o); + eh.push(); + + auto refcount = o.refcount(); + if (refcount) + o.refcount() = refcount + 1; + + extern(C) static void exception_cleanup(_Unwind_Reason_Code reason, + _Unwind_Exception* eo) nothrow @nogc + { + switch (reason) + { + case _URC_FOREIGN_EXCEPTION_CAUGHT: + case _URC_NO_REASON: + ExceptionHeader.release(ExceptionHeader.to_exception_header(eo)); + break; + default: + dwarf_terminate(__LINE__); + } + } + + eh.exception_object.exception_cleanup = &exception_cleanup; + _d_createTrace(o, null); + + auto r = _Unwind_RaiseException(&eh.exception_object); + + // Should not return — if it did, the exception was not caught. + dwarfeh_begin_catch(&eh.exception_object); + writeln_err("uncaught exception reached top of stack"); + auto msg = o.msg; + if (msg.length) + writef_to!(WriteTarget.stderr, true)(" {0}", msg); + abort(); +} + +// Common personality implementation. +_Unwind_Reason_Code dwarfeh_personality_common(_Unwind_Action actions, _Unwind_Exception_Class exception_class, _Unwind_Exception* exception_object, _Unwind_Context* context) nothrow @nogc +{ + const(ubyte)* language_specific_data; + int handler; + _Unwind_Ptr landing_pad; + + language_specific_data = cast(const(ubyte)*) _Unwind_GetLanguageSpecificData(context); + auto Start = _Unwind_GetRegionStart(context); + + // Get IP; use _Unwind_GetIPInfo to handle signal frames correctly. + int ip_before_insn; + auto ip = _Unwind_GetIPInfo(context, &ip_before_insn); + if (!ip_before_insn) + --ip; + + auto result = scan_lsda(language_specific_data, ip - Start, exception_class, + (actions & _UA_FORCE_UNWIND) != 0, + (actions & _UA_SEARCH_PHASE) != 0, + exception_object, + landing_pad, + handler); + landing_pad += Start; + + final switch (result) + { + case LsdaResult.not_found: + dwarf_terminate(__LINE__); + assert(0); + + case LsdaResult.foreign: + dwarf_terminate(__LINE__); + assert(0); + + case LsdaResult.corrupt: + dwarf_terminate(__LINE__); + assert(0); + + case LsdaResult.no_action: + return _URC_CONTINUE_UNWIND; + + case LsdaResult.cleanup: + if (actions & _UA_SEARCH_PHASE) + return _URC_CONTINUE_UNWIND; + break; + + case LsdaResult.handler: + if (actions & _UA_SEARCH_PHASE) + { + if (exception_class == dmd_exception_class) + { + auto eh = ExceptionHeader.to_exception_header(exception_object); + eh.handler = handler; + eh.language_specific_data = language_specific_data; + eh.landing_pad = landing_pad; + } + return _URC_HANDLER_FOUND; + } + break; + } + + // Multiple exceptions in flight — chain them + if (exception_class == dmd_exception_class) + { + auto eh = ExceptionHeader.to_exception_header(exception_object); + auto current_lsd = language_specific_data; + bool bypassed = false; + + while (eh.next) + { + ExceptionHeader* ehn = eh.next; + + Error e = cast(Error) eh.object; + if (e !is null && !cast(Error) ehn.object) + { + current_lsd = ehn.language_specific_data; + eh = ehn; + bypassed = true; + continue; + } + + if (current_lsd != ehn.language_specific_data) + break; + + eh.object = Throwable.chainTogether(ehn.object, eh.object); + + if (ehn.handler != handler && !bypassed) + { + handler = ehn.handler; + eh.handler = handler; + eh.language_specific_data = language_specific_data; + eh.landing_pad = landing_pad; + } + + eh.next = ehn.next; + _Unwind_DeleteException(&ehn.exception_object); + } + + if (bypassed) + { + eh = ExceptionHeader.to_exception_header(exception_object); + Error e = cast(Error) eh.object; + auto ehn = eh.next; + e.bypassedException = ehn.object; + eh.next = ehn.next; + _Unwind_DeleteException(&ehn.exception_object); + } + } + + _Unwind_SetGR(context, eh_exception_regno, cast(_Unwind_Word) exception_object); + _Unwind_SetGR(context, eh_selector_regno, handler); + _Unwind_SetIP(context, landing_pad); + + return _URC_INSTALL_CONTEXT; +} + +// Personality function entry points. +// ARM EABI uses a different calling convention than the standard Itanium ABI. +version (ARM) +{ + version (LDC) + { + extern(C) _Unwind_Reason_Code _d_eh_personality(_Unwind_State state, _Unwind_Exception* exception_object, _Unwind_Context* context) nothrow @nogc + { + _Unwind_Action actions; + switch (state & _US_ACTION_MASK) + { + case _US_VIRTUAL_UNWIND_FRAME: + actions = _UA_SEARCH_PHASE; + break; + case _US_UNWIND_FRAME_STARTING: + actions = _UA_CLEANUP_PHASE; + break; + case _US_UNWIND_FRAME_RESUME: + return _URC_CONTINUE_UNWIND; + default: + dwarf_terminate(__LINE__); + return _URC_FATAL_PHASE1_ERROR; + } + if (state & _US_FORCE_UNWIND) + actions |= _UA_FORCE_UNWIND; + + return dwarfeh_personality_common(actions, exception_object.exception_class, + exception_object, context); + } + } +} +else +{ + pragma(mangle, personality_mangle) + extern(C) _Unwind_Reason_Code dwarfeh_personality(int ver, _Unwind_Action actions, _Unwind_Exception_Class exception_class, _Unwind_Exception* exception_object, _Unwind_Context* context) nothrow @nogc + { + if (ver != 1) + return _URC_FATAL_PHASE1_ERROR; + + return dwarfeh_personality_common(actions, exception_class, + exception_object, context); + } +} + +// LDC-only: trivial cleanup hooks (DWARF handles cleanup via personality). +version (LDC) +{ + extern(C) bool _d_enter_cleanup(void* ptr) nothrow @nogc @trusted => true; + extern(C) void _d_leave_cleanup(void* ptr) nothrow @nogc @trusted {} +} + +} // version (!Windows) — shared DWARF EH diff --git a/src/urt/internal/lifetime.d b/src/urt/internal/lifetime.d new file mode 100644 index 0000000..1f9c3a9 --- /dev/null +++ b/src/urt/internal/lifetime.d @@ -0,0 +1,121 @@ +module urt.internal.lifetime; + +import urt.lifetime : forward; + +/+ +emplaceRef is a package function for druntime internal use. It works like +emplace, but takes its argument by ref (as opposed to "by pointer"). +This makes it easier to use, easier to be safe, and faster in a non-inline +build. +Furthermore, emplaceRef optionally takes a type parameter, which specifies +the type we want to build. This helps to build qualified objects on mutable +buffer, without breaking the type system with unsafe casts. ++/ +void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) +{ + static if (args.length == 0) + { + static assert(is(typeof({static T i;})), + "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~ + ".this() is annotated with @disable."); + static if (is(T == class)) static assert(!__traits(isAbstractClass, T), + T.stringof ~ " is abstract and it can't be emplaced"); + emplaceInitializer(chunk); + } + else static if ( + !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ + || + Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */ + || + is(typeof(T(forward!args))) /* general constructors */) + { + static struct S + { + T payload; + this()(auto ref Args args) + { + static if (__traits(compiles, payload = forward!args)) + payload = forward!args; + else + payload = T(forward!args); + } + } + if (__ctfe) + { + static if (__traits(compiles, chunk = T(forward!args))) + chunk = T(forward!args); + else static if (args.length == 1 && __traits(compiles, chunk = forward!(args[0]))) + chunk = forward!(args[0]); + else assert(0, "CTFE emplace doesn't support " + ~ T.stringof ~ " from " ~ Args.stringof); + } + else + { + S* p = () @trusted { return cast(S*) &chunk; }(); + static if (UT.sizeof > 0) + emplaceInitializer(*p); + p.__ctor(forward!args); + } + } + else static if (is(typeof(chunk.__ctor(forward!args)))) + { + // This catches the rare case of local types that keep a frame pointer + emplaceInitializer(chunk); + chunk.__ctor(forward!args); + } + else + { + //We can't emplace. Try to diagnose a disabled postblit. + static assert(!(Args.length == 1 && is(Args[0] : T)), + "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~ + ".this(this) is annotated with @disable."); + + //We can't emplace. + static assert(false, + T.stringof ~ " cannot be emplaced from " ~ Args[].stringof ~ "."); + } +} + +// ditto +static import urt.traits; +void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) + if (is(UT == urt.traits.Unqual!UT)) +{ + emplaceRef!(UT, UT)(chunk, forward!args); +} + +/+ +Emplaces T.init. +In contrast to `emplaceRef(chunk)`, there are no checks for disabled default +constructors etc. ++/ +void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted + if (!is(T == const) && !is(T == immutable) && !is(T == inout)) +{ + import urt.internal.traits : hasElaborateAssign; + + static if (__traits(isZeroInit, T)) + { + import urt.mem : memset; + memset(cast(void*) &chunk, 0, T.sizeof); + } + else static if (__traits(isScalar, T) || + T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, (){ T chunk; chunk = T.init; })) + { + chunk = T.init; + } + else static if (__traits(isStaticArray, T)) + { + // For static arrays there is no initializer symbol created. Instead, we emplace elements one-by-one. + foreach (i; 0 .. T.length) + { + emplaceInitializer(chunk[i]); + } + } + else + { + import urt.mem : memcpy; + const initializer = __traits(initSymbol, T); + memcpy(cast(void*)&chunk, initializer.ptr, initializer.length); + } +} diff --git a/src/urt/internal/os.c b/src/urt/internal/os.c index d6b9c02..075965f 100644 --- a/src/urt/internal/os.c +++ b/src/urt/internal/os.c @@ -2,10 +2,18 @@ #if defined(__linux) # define _DEFAULT_SOURCE +# include # include # include +# include # include # include # include # include +# include + +// EWOULDBLOCK is #define EWOULDBLOCK EAGAIN on Linux — ImportC cannot resolve +// chained macros, so re-define as a plain integer. +# undef EWOULDBLOCK +# define EWOULDBLOCK 11 /* same as EAGAIN on Linux */ #endif diff --git a/src/urt/internal/stdc.c b/src/urt/internal/stdc.c new file mode 100644 index 0000000..26fecdb --- /dev/null +++ b/src/urt/internal/stdc.c @@ -0,0 +1,19 @@ +#pragma attribute(push, nothrow, nogc) + +#include +#include +#include +#include +#include + +// The errno macro expands to (*__errno_location()) on Linux. ImportC would +// generate a function symbol `errno` that clashes with libc's TLS errno. +#undef errno + +// Some errno values are defined as chained macros (e.g. ENOTSUP → EOPNOTSUPP, +// EWOULDBLOCK → EAGAIN) that ImportC cannot resolve to integers. +// Re-define as plain integer literals so ImportC can export them. +#undef ENOTSUP +#define ENOTSUP 95 /* == EOPNOTSUPP on Linux */ +#undef EWOULDBLOCK +#define EWOULDBLOCK 11 /* == EAGAIN on Linux */ diff --git a/src/urt/internal/sys/windows/basetsd.d b/src/urt/internal/sys/windows/basetsd.d new file mode 100644 index 0000000..b881b5a --- /dev/null +++ b/src/urt/internal/sys/windows/basetsd.d @@ -0,0 +1,141 @@ +/** + * Windows API header module + * + * Translated from MinGW API for MS-Windows 3.12 + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_basetsd.d) + */ +module urt.internal.sys.windows.basetsd; +version (Windows): + +// [SnakE 2009-02-23] Moved HANDLE definition here from winnt.d to avoid +// 'forwatd template reference' to CPtr from winnt.d caused by a circular +// import. +alias HANDLE = void*; + +alias HANDLE* PHANDLE, LPHANDLE; + +// helper for aligned structs +// alignVal 0 means the default align. +// _alignSpec as parameter does not pollute namespace. +package mixin template AlignedStr(int alignVal, string name, string memberlist, + string _alignSpec = !alignVal ? "align" : "align("~alignVal.stringof~")" ) +{ + mixin( _alignSpec ~ " struct " ~ name ~" { " ~ _alignSpec ~":"~ memberlist~" }" ); +} + +version (CoreUnittest) { + private mixin AlignedStr!(16, "_Test_Aligned_Str", q{char a; char b;}); + private mixin AlignedStr!(0, "_Test_NoAligned_Str", q{char a; char b;}); +} + +version (Win64) { + alias long __int3264; +enum ulong ADDRESS_TAG_BIT = 0x40000000000; + + alias long INT_PTR, LONG_PTR; + alias long* PINT_PTR, PLONG_PTR; + alias ulong UINT_PTR, ULONG_PTR, HANDLE_PTR; + alias ulong* PUINT_PTR, PULONG_PTR; + alias int HALF_PTR; + alias int* PHALF_PTR; + alias uint UHALF_PTR; + alias uint* PUHALF_PTR; + + uint HandleToULong()(void* h) { return(cast(uint) cast(ULONG_PTR) h); } + int HandleToLong()(void* h) { return(cast(int) cast(LONG_PTR) h); } + void* ULongToHandle()(uint h) { return(cast(void*) cast(UINT_PTR) h); } + void* LongToHandle()(int h) { return(cast(void*) cast(INT_PTR) h); } + uint PtrToUlong()(void* p) { return(cast(uint) cast(ULONG_PTR) p); } + uint PtrToUint()(void* p) { return(cast(uint) cast(UINT_PTR) p); } + ushort PtrToUshort()(void* p) { return(cast(ushort) cast(uint) cast(ULONG_PTR) p); } + int PtrToLong()(void* p) { return(cast(int) cast(LONG_PTR) p); } + int PtrToInt()(void* p) { return(cast(int) cast(INT_PTR) p); } + short PtrToShort()(void* p) { return(cast(short) cast(int) cast(LONG_PTR) p); } + void* IntToPtr()(int i) { return(cast(void*) cast(INT_PTR) i); } + void* UIntToPtr()(uint ui) { return(cast(void*) cast(UINT_PTR) ui); } + void* LongToPtr()(int l) { return(cast(void*) cast(LONG_PTR) l); } + void* ULongToPtr()(uint ul) { return(cast(void*) cast(ULONG_PTR) ul); } + +} else { + alias int __int3264; +enum uint ADDRESS_TAG_BIT = 0x80000000; + + alias int INT_PTR, LONG_PTR; + alias int* PINT_PTR, PLONG_PTR; + alias uint UINT_PTR, ULONG_PTR, HANDLE_PTR; + alias uint* PUINT_PTR, PULONG_PTR; + alias short HALF_PTR; + alias short* PHALF_PTR; + alias ushort UHALF_PTR; + alias ushort* PUHALF_PTR; + + uint HandleToUlong()(HANDLE h) { return cast(uint) h; } + int HandleToLong()(HANDLE h) { return cast(int) h; } + HANDLE LongToHandle()(LONG_PTR h) { return cast(HANDLE)h; } + uint PtrToUlong(const(void)* p) { return cast(uint) p; } + uint PtrToUint(const(void)* p) { return cast(uint) p; } + int PtrToInt(const(void)* p) { return cast(int) p; } + ushort PtrToUshort(const(void)* p) { return cast(ushort) p; } + short PtrToShort(const(void)* p) { return cast(short) p; } + void* IntToPtr()(int i) { return cast(void*) i; } + void* UIntToPtr()(uint ui) { return cast(void*) ui; } + alias IntToPtr LongToPtr; + alias UIntToPtr ULongToPtr; +} + +alias UIntToPtr UintToPtr, UlongToPtr; + +enum : UINT_PTR { + MAXUINT_PTR = UINT_PTR.max +} + +enum : INT_PTR { + MAXINT_PTR = INT_PTR.max, + MININT_PTR = INT_PTR.min +} + +enum : ULONG_PTR { + MAXULONG_PTR = ULONG_PTR.max +} + +enum : LONG_PTR { + MAXLONG_PTR = LONG_PTR.max, + MINLONG_PTR = LONG_PTR.min +} + +enum : UHALF_PTR { + MAXUHALF_PTR = UHALF_PTR.max +} + +enum : HALF_PTR { + MAXHALF_PTR = HALF_PTR.max, + MINHALF_PTR = HALF_PTR.min +} + +alias byte INT8; +alias byte* PINT8; +alias ubyte UINT8; +alias ubyte* PUINT8; + +alias short INT16; +alias short* PINT16; +alias ushort UINT16; +alias ushort* PUINT16; + +alias int LONG32, INT32; +alias int* PLONG32, PINT32; +alias uint ULONG32, DWORD32, UINT32; +alias uint* PULONG32, PDWORD32, PUINT32; + +alias ULONG_PTR SIZE_T, DWORD_PTR; +alias ULONG_PTR* PSIZE_T, PDWORD_PTR; +alias LONG_PTR SSIZE_T; +alias LONG_PTR* PSSIZE_T; + +alias long LONG64, INT64; +alias long* PLONG64, PINT64; +alias ulong ULONG64, DWORD64, UINT64; +alias ulong* PULONG64, PDWORD64, PUINT64; diff --git a/src/urt/internal/sys/windows/basetyps.d b/src/urt/internal/sys/windows/basetyps.d new file mode 100644 index 0000000..d6cee67 --- /dev/null +++ b/src/urt/internal/sys/windows/basetyps.d @@ -0,0 +1,27 @@ +/** + * Windows API header module + * + * Translated from MinGW API for MS-Windows 3.10 + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_basetyps.d) + */ +module urt.internal.sys.windows.basetyps; +version (Windows): + +import urt.internal.sys.windows.windef, urt.internal.sys.windows.basetsd; + +align(1) struct GUID { // size is 16 + align(1): + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE[8] Data4; +} +alias GUID UUID, /*IID, CLSID, */FMTID, uuid_t; +alias IID = const(GUID); +alias CLSID = const(GUID); + +alias GUID* LPGUID, LPCLSID, LPIID; +alias const(GUID)* LPCGUID, REFGUID, REFIID, REFCLSID, REFFMTID; +alias uint error_status_t, PROPID; diff --git a/src/urt/internal/sys/windows/ntdef.d b/src/urt/internal/sys/windows/ntdef.d new file mode 100644 index 0000000..a80762d --- /dev/null +++ b/src/urt/internal/sys/windows/ntdef.d @@ -0,0 +1,85 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_ntdef.d) + */ +module urt.internal.sys.windows.ntdef; +version (Windows): + +import urt.internal.sys.windows.basetsd, urt.internal.sys.windows.subauth, urt.internal.sys.windows.windef, urt.internal.sys.windows.winnt; + +enum uint + OBJ_INHERIT = 0x0002, + OBJ_PERMANENT = 0x0010, + OBJ_EXCLUSIVE = 0x0020, + OBJ_CASE_INSENSITIVE = 0x0040, + OBJ_OPENIF = 0x0080, + OBJ_OPENLINK = 0x0100, + OBJ_VALID_ATTRIBUTES = 0x01F2; + +void InitializeObjectAttributes(OBJECT_ATTRIBUTES* p, UNICODE_STRING* n, + uint a, HANDLE r, void* s) { + with (*p) { + Length = OBJECT_ATTRIBUTES.sizeof; + RootDirectory = r; + Attributes = a; + ObjectName = n; + SecurityDescriptor = s; + SecurityQualityOfService = null; + } +} + +pragma(inline, true) @safe pure nothrow @nogc { + bool NT_SUCCESS()(NTSTATUS Status) { return Status >= 0; } + bool NT_INFORMATION()(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 1; } + bool NT_WARNING()(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 2; } + bool NT_ERROR()(NTSTATUS Status) { return ((cast(ULONG) Status) >> 30) == 3; } +} + +/* In MinGW, NTSTATUS, UNICODE_STRING, STRING and their associated pointer + * type aliases are defined in ntdef.h, ntsecapi.h and subauth.h, each of + * which checks that none of the others is already included. + */ +alias int NTSTATUS; +alias int* PNTSTATUS; + +struct UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} +alias UNICODE_STRING* PUNICODE_STRING; +alias const(UNICODE_STRING)* PCUNICODE_STRING; + +struct STRING { + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} +alias STRING ANSI_STRING, OEM_STRING; +alias STRING* PSTRING, PANSI_STRING, POEM_STRING; + +alias LARGE_INTEGER PHYSICAL_ADDRESS; +alias LARGE_INTEGER* PPHYSICAL_ADDRESS; + +enum SECTION_INHERIT { + ViewShare = 1, + ViewUnmap +} + +/* In MinGW, this is defined in ntdef.h and ntsecapi.h, each of which checks + * that the other isn't already included. + */ +struct OBJECT_ATTRIBUTES { + ULONG Length = OBJECT_ATTRIBUTES.sizeof; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} +alias OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES; diff --git a/src/urt/internal/sys/windows/ntsecapi.d b/src/urt/internal/sys/windows/ntsecapi.d new file mode 100644 index 0000000..bf372bf --- /dev/null +++ b/src/urt/internal/sys/windows/ntsecapi.d @@ -0,0 +1,796 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_ntsecapi.d) + */ +module urt.internal.sys.windows.ntsecapi; +version (Windows): +pragma(lib, "advapi32"); + +version (ANSI) {} else version = Unicode; + +private import + urt.internal.sys.windows.basetyps, urt.internal.sys.windows.ntdef, urt.internal.sys.windows.windef, urt.internal.sys.windows.winnt, urt.internal.sys.windows.w32api; + +// FIXME: check types and grouping of constants +// FIXME: check Windows version support + +enum KERB_WRAP_NO_ENCRYPT = 0x80000001; + +enum LOGON_GUEST = 0x00000001; +enum LOGON_NOENCRYPTION = 0x00000002; +enum LOGON_CACHED_ACCOUNT = 0x00000004; +enum LOGON_USED_LM_PASSWORD = 0x00000008; +enum LOGON_EXTRA_SIDS = 0x00000020; +enum LOGON_SUBAUTH_SESSION_KEY = 0x00000040; +enum LOGON_SERVER_TRUST_ACCOUNT = 0x00000080; +enum LOGON_NTLMV2_ENABLED = 0x00000100; +enum LOGON_RESOURCE_GROUPS = 0x00000200; +enum LOGON_PROFILE_PATH_RETURNED = 0x00000400; +enum LOGON_GRACE_LOGON = 0x01000000; + +enum { + LSA_MODE_PASSWORD_PROTECTED = 1, + LSA_MODE_INDIVIDUAL_ACCOUNTS, + LSA_MODE_MANDATORY_ACCESS, + LSA_MODE_LOG_FULL +} + +bool LSA_SUCCESS()(int x) { return x >= 0; } + +/* TOTHINKABOUT: These constants don't have ANSI/Unicode versioned + * aliases. Should we merge them anyway? + */ +const char[] MICROSOFT_KERBEROS_NAME_A = "Kerberos"; +const wchar[] MICROSOFT_KERBEROS_NAME_W = "Kerberos"; +const char[] MSV1_0_PACKAGE_NAME = "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"; +const wchar[] MSV1_0_PACKAGE_NAMEW = "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"; + +enum MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT = 32; +enum MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT = 2048; +enum MSV1_0_CLEARTEXT_PASSWORD_ALLOWED = 2; +enum MSV1_0_CRED_LM_PRESENT = 1; +enum MSV1_0_CRED_NT_PRESENT = 2; +enum MSV1_0_CRED_VERSION = 0; +enum MSV1_0_DONT_TRY_GUEST_ACCOUNT = 16; +enum MSV1_0_MAX_NTLM3_LIFE = 1800; +enum MSV1_0_MAX_AVL_SIZE = 64000; +enum MSV1_0_MNS_LOGON = 16777216; + +enum size_t + MSV1_0_CHALLENGE_LENGTH = 8, + MSV1_0_LANMAN_SESSION_KEY_LENGTH = 8, + MSV1_0_NTLM3_RESPONSE_LENGTH = 16, + MSV1_0_NTLM3_OWF_LENGTH = 16, + MSV1_0_NTLM3_INPUT_LENGTH = MSV1_0_NTLM3_RESPONSE.sizeof + - MSV1_0_NTLM3_RESPONSE_LENGTH, + MSV1_0_OWF_PASSWORD_LENGTH = 16, + MSV1_0_PACKAGE_NAMEW_LENGTH = MSV1_0_PACKAGE_NAMEW.sizeof + - WCHAR.sizeof; + +enum MSV1_0_RETURN_USER_PARAMETERS = 8; +enum MSV1_0_RETURN_PASSWORD_EXPIRY = 64; +enum MSV1_0_RETURN_PROFILE_PATH = 512; +enum MSV1_0_SUBAUTHENTICATION_DLL_EX = 1048576; +enum MSV1_0_SUBAUTHENTICATION_DLL = 0xff000000; +enum MSV1_0_SUBAUTHENTICATION_DLL_SHIFT = 24; +enum MSV1_0_SUBAUTHENTICATION_DLL_RAS = 2; +enum MSV1_0_SUBAUTHENTICATION_DLL_IIS = 132; +enum MSV1_0_SUBAUTHENTICATION_FLAGS = 0xff000000; +enum MSV1_0_TRY_GUEST_ACCOUNT_ONLY = 256; +enum MSV1_0_TRY_SPECIFIED_DOMAIN_ONLY = 1024; +enum MSV1_0_UPDATE_LOGON_STATISTICS = 4; +enum MSV1_0_USE_CLIENT_CHALLENGE = 128; +enum MSV1_0_USER_SESSION_KEY_LENGTH = 16; + +const char[] + MSV1_0_SUBAUTHENTICATION_KEY + = `System\CurrentControlSet\Control\Lsa\MSV1_0`, + MSV1_0_SUBAUTHENTICATION_VALUE = "Auth"; + +enum ACCESS_MASK + POLICY_VIEW_LOCAL_INFORMATION = 0x0001, + POLICY_VIEW_AUDIT_INFORMATION = 0x0002, + POLICY_GET_PRIVATE_INFORMATION = 0x0004, + POLICY_TRUST_ADMIN = 0x0008, + POLICY_CREATE_ACCOUNT = 0x0010, + POLICY_CREATE_SECRET = 0x0020, + POLICY_CREATE_PRIVILEGE = 0x0040, + POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x0080, + POLICY_SET_AUDIT_REQUIREMENTS = 0x0100, + POLICY_AUDIT_LOG_ADMIN = 0x0200, + POLICY_SERVER_ADMIN = 0x0400, + POLICY_LOOKUP_NAMES = 0x0800, + + POLICY_READ = STANDARD_RIGHTS_READ | 0x0006, + POLICY_WRITE = STANDARD_RIGHTS_WRITE | 0x07F8, + POLICY_EXECUTE = STANDARD_RIGHTS_EXECUTE | 0x0801, + POLICY_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | 0x0FFF; + +enum POLICY_AUDIT_EVENT_UNCHANGED = 0; +enum POLICY_AUDIT_EVENT_SUCCESS = 1; +enum POLICY_AUDIT_EVENT_FAILURE = 2; +enum POLICY_AUDIT_EVENT_NONE = 4; +enum POLICY_AUDIT_EVENT_MASK = 7; + +enum { + POLICY_LOCATION_LOCAL = 1, + POLICY_LOCATION_DS +} + +enum : uint { + POLICY_MACHINE_POLICY_LOCAL = 0, + POLICY_MACHINE_POLICY_DEFAULTED, + POLICY_MACHINE_POLICY_EXPLICIT, + POLICY_MACHINE_POLICY_UNKNOWN = 0xFFFFFFFF +} + + +enum POLICY_QOS_SCHANEL_REQUIRED = 0x0001; +enum POLICY_QOS_OUTBOUND_INTEGRITY = 0x0002; +enum POLICY_QOS_OUTBOUND_CONFIDENTIALITY = 0x0004; +enum POLICY_QOS_INBOUND_INTEGREITY = 0x0008; +enum POLICY_QOS_INBOUND_CONFIDENTIALITY = 0x0010; +enum POLICY_QOS_ALLOW_LOCAL_ROOT_CERT_STORE = 0x0020; +enum POLICY_QOS_RAS_SERVER_ALLOWED = 0x0040; +enum POLICY_QOS_DHCP_SERVER_ALLOWD = 0x0080; + +enum POLICY_KERBEROS_FORWARDABLE = 1; +enum POLICY_KERBEROS_PROXYABLE = 2; +enum POLICY_KERBEROS_RENEWABLE = 4; +enum POLICY_KERBEROS_POSTDATEABLE = 8; + +const char[] + SAM_PASSWORD_CHANGE_NOTIFY_ROUTINE = "PasswordChangeNotify", + SAM_INIT_NOTIFICATION_ROUTINE = "InitializeChangeNotify", + SAM_PASSWORD_FILTER_ROUTINE = "PasswordFilter"; + +const TCHAR[] + SE_INTERACTIVE_LOGON_NAME = "SeInteractiveLogonRight", + SE_NETWORK_LOGON_NAME = "SeNetworkLogonRight", + SE_BATCH_LOGON_NAME = "SeBatchLogonRight", + SE_SERVICE_LOGON_NAME = "SeServiceLogonRight"; + +enum { + TRUST_ATTRIBUTE_NON_TRANSITIVE = 1, + TRUST_ATTRIBUTE_UPLEVEL_ONLY = 2, + TRUST_ATTRIBUTE_TREE_PARENT = 4194304, + TRUST_ATTRIBUTES_VALID = -16580609 +} + +enum { + TRUST_AUTH_TYPE_NONE, + TRUST_AUTH_TYPE_NT4OWF, + TRUST_AUTH_TYPE_CLEAR +} + +enum { + TRUST_DIRECTION_DISABLED, + TRUST_DIRECTION_INBOUND, + TRUST_DIRECTION_OUTBOUND, + TRUST_DIRECTION_BIDIRECTIONAL +} + +enum { + TRUST_TYPE_DOWNLEVEL = 1, + TRUST_TYPE_UPLEVEL, + TRUST_TYPE_MIT, + TRUST_TYPE_DCE +} + +alias UNICODE_STRING LSA_UNICODE_STRING; +alias UNICODE_STRING* PLSA_UNICODE_STRING; +alias STRING LSA_STRING; +alias STRING* PLSA_STRING; + +enum MSV1_0_LOGON_SUBMIT_TYPE { + MsV1_0InteractiveLogon = 2, + MsV1_0Lm20Logon, + MsV1_0NetworkLogon, + MsV1_0SubAuthLogon, + MsV1_0WorkstationUnlockLogon = 7 +} +alias MSV1_0_LOGON_SUBMIT_TYPE* PMSV1_0_LOGON_SUBMIT_TYPE; + +enum MSV1_0_PROFILE_BUFFER_TYPE { + MsV1_0InteractiveProfile = 2, + MsV1_0Lm20LogonProfile, + MsV1_0SmartCardProfile +} +alias MSV1_0_PROFILE_BUFFER_TYPE* PMSV1_0_PROFILE_BUFFER_TYPE; + + +enum MSV1_0_AVID { + MsvAvEOL, + MsvAvNbComputerName, + MsvAvNbDomainName, + MsvAvDnsComputerName, + MsvAvDnsDomainName +} + +enum MSV1_0_PROTOCOL_MESSAGE_TYPE { + MsV1_0Lm20ChallengeRequest = 0, + MsV1_0Lm20GetChallengeResponse, + MsV1_0EnumerateUsers, + MsV1_0GetUserInfo, + MsV1_0ReLogonUsers, + MsV1_0ChangePassword, + MsV1_0ChangeCachedPassword, + MsV1_0GenericPassthrough, + MsV1_0CacheLogon, + MsV1_0SubAuth, + MsV1_0DeriveCredential, + MsV1_0CacheLookup +} +alias MSV1_0_PROTOCOL_MESSAGE_TYPE* PMSV1_0_PROTOCOL_MESSAGE_TYPE; + +enum POLICY_LSA_SERVER_ROLE { + PolicyServerRoleBackup = 2, + PolicyServerRolePrimary +} +alias POLICY_LSA_SERVER_ROLE* PPOLICY_LSA_SERVER_ROLE; + +enum POLICY_SERVER_ENABLE_STATE { + PolicyServerEnabled = 2, + PolicyServerDisabled +} +alias POLICY_SERVER_ENABLE_STATE* PPOLICY_SERVER_ENABLE_STATE; + +enum POLICY_INFORMATION_CLASS { + PolicyAuditLogInformation = 1, + PolicyAuditEventsInformation, + PolicyPrimaryDomainInformation, + PolicyPdAccountInformation, + PolicyAccountDomainInformation, + PolicyLsaServerRoleInformation, + PolicyReplicaSourceInformation, + PolicyDefaultQuotaInformation, + PolicyModificationInformation, + PolicyAuditFullSetInformation, + PolicyAuditFullQueryInformation, + PolicyDnsDomainInformation, + PolicyEfsInformation +} +alias POLICY_INFORMATION_CLASS* PPOLICY_INFORMATION_CLASS; + +enum POLICY_AUDIT_EVENT_TYPE { + AuditCategorySystem, + AuditCategoryLogon, + AuditCategoryObjectAccess, + AuditCategoryPrivilegeUse, + AuditCategoryDetailedTracking, + AuditCategoryPolicyChange, + AuditCategoryAccountManagement, + AuditCategoryDirectoryServiceAccess, + AuditCategoryAccountLogon +} +alias POLICY_AUDIT_EVENT_TYPE* PPOLICY_AUDIT_EVENT_TYPE; + +enum POLICY_LOCAL_INFORMATION_CLASS { + PolicyLocalAuditEventsInformation = 1, + PolicyLocalPdAccountInformation, + PolicyLocalAccountDomainInformation, + PolicyLocalLsaServerRoleInformation, + PolicyLocalReplicaSourceInformation, + PolicyLocalModificationInformation, + PolicyLocalAuditFullSetInformation, + PolicyLocalAuditFullQueryInformation, + PolicyLocalDnsDomainInformation, + PolicyLocalIPSecReferenceInformation, + PolicyLocalMachinePasswordInformation, + PolicyLocalQualityOfServiceInformation, + PolicyLocalPolicyLocationInformation +} +alias POLICY_LOCAL_INFORMATION_CLASS* PPOLICY_LOCAL_INFORMATION_CLASS; + +enum POLICY_DOMAIN_INFORMATION_CLASS { + PolicyDomainIPSecReferenceInformation = 1, + PolicyDomainQualityOfServiceInformation, + PolicyDomainEfsInformation, + PolicyDomainPublicKeyInformation, + PolicyDomainPasswordPolicyInformation, + PolicyDomainLockoutInformation, + PolicyDomainKerberosTicketInformation +} +alias POLICY_DOMAIN_INFORMATION_CLASS* PPOLICY_DOMAIN_INFORMATION_CLASS; + +enum SECURITY_LOGON_TYPE { + Interactive = 2, + Network, + Batch, + Service, + Proxy, + Unlock +} +alias SECURITY_LOGON_TYPE* PSECURITY_LOGON_TYPE; + +enum TRUSTED_INFORMATION_CLASS { + TrustedDomainNameInformation = 1, + TrustedControllersInformation, + TrustedPosixOffsetInformation, + TrustedPasswordInformation, + TrustedDomainInformationBasic, + TrustedDomainInformationEx, + TrustedDomainAuthInformation, + TrustedDomainFullInformation +} +alias TRUSTED_INFORMATION_CLASS* PTRUSTED_INFORMATION_CLASS; + +struct DOMAIN_PASSWORD_INFORMATION { + USHORT MinPasswordLength; + USHORT PasswordHistoryLength; + ULONG PasswordProperties; + LARGE_INTEGER MaxPasswordAge; + LARGE_INTEGER MinPasswordAge; +} +alias DOMAIN_PASSWORD_INFORMATION* PDOMAIN_PASSWORD_INFORMATION; + +struct LSA_ENUMERATION_INFORMATION { + PSID Sid; +} +alias LSA_ENUMERATION_INFORMATION* PLSA_ENUMERATION_INFORMATION; + +alias OBJECT_ATTRIBUTES LSA_OBJECT_ATTRIBUTES; +alias OBJECT_ATTRIBUTES* PLSA_OBJECT_ATTRIBUTES; + +struct LSA_TRUST_INFORMATION { + LSA_UNICODE_STRING Name; + PSID Sid; +} +alias LSA_TRUST_INFORMATION TRUSTED_DOMAIN_INFORMATION_BASIC; +alias LSA_TRUST_INFORMATION* PLSA_TRUST_INFORMATION; +/* in MinGW (further down the code): + * typedef PLSA_TRUST_INFORMATION *PTRUSTED_DOMAIN_INFORMATION_BASIC; + * but it doesn't look right.... + */ +alias LSA_TRUST_INFORMATION** PTRUSTED_DOMAIN_INFORMATION_BASIC; + +struct LSA_REFERENCED_DOMAIN_LIST { + ULONG Entries; + PLSA_TRUST_INFORMATION Domains; +} +alias LSA_REFERENCED_DOMAIN_LIST* PLSA_REFERENCED_DOMAIN_LIST; + +struct LSA_TRANSLATED_SID { + SID_NAME_USE Use; + ULONG RelativeId; + LONG DomainIndex; +} +alias LSA_TRANSLATED_SID* PLSA_TRANSLATED_SID; + +struct LSA_TRANSLATED_NAME { + SID_NAME_USE Use; + LSA_UNICODE_STRING Name; + LONG DomainIndex; +} +alias LSA_TRANSLATED_NAME* PLSA_TRANSLATED_NAME; + +struct MSV1_0_INTERACTIVE_LOGON { + MSV1_0_LOGON_SUBMIT_TYPE MessageType; + UNICODE_STRING LogonDomainName; + UNICODE_STRING UserName; + UNICODE_STRING Password; +} +alias MSV1_0_INTERACTIVE_LOGON* PMSV1_0_INTERACTIVE_LOGON; + +struct MSV1_0_INTERACTIVE_PROFILE { + MSV1_0_PROFILE_BUFFER_TYPE MessageType; + USHORT LogonCount; + USHORT BadPasswordCount; + LARGE_INTEGER LogonTime; + LARGE_INTEGER LogoffTime; + LARGE_INTEGER KickOffTime; + LARGE_INTEGER PasswordLastSet; + LARGE_INTEGER PasswordCanChange; + LARGE_INTEGER PasswordMustChange; + UNICODE_STRING LogonScript; + UNICODE_STRING HomeDirectory; + UNICODE_STRING FullName; + UNICODE_STRING ProfilePath; + UNICODE_STRING HomeDirectoryDrive; + UNICODE_STRING LogonServer; + ULONG UserFlags; +} +alias MSV1_0_INTERACTIVE_PROFILE* PMSV1_0_INTERACTIVE_PROFILE; + +struct MSV1_0_LM20_LOGON { + MSV1_0_LOGON_SUBMIT_TYPE MessageType; + UNICODE_STRING LogonDomainName; + UNICODE_STRING UserName; + UNICODE_STRING Workstation; + UCHAR[MSV1_0_CHALLENGE_LENGTH] ChallengeToClient; + STRING CaseSensitiveChallengeResponse; + STRING CaseInsensitiveChallengeResponse; + ULONG ParameterControl; +} +alias MSV1_0_LM20_LOGON* PMSV1_0_LM20_LOGON; + +//static if (_WIN32_WINNT >= 0x500) { + struct MSV1_0_SUBAUTH_LOGON { + MSV1_0_LOGON_SUBMIT_TYPE MessageType; + UNICODE_STRING LogonDomainName; + UNICODE_STRING UserName; + UNICODE_STRING Workstation; + UCHAR[MSV1_0_CHALLENGE_LENGTH] ChallengeToClient; + STRING AuthenticationInfo1; + STRING AuthenticationInfo2; + ULONG ParameterControl; + ULONG SubAuthPackageId; + } + alias MSV1_0_SUBAUTH_LOGON* PMSV1_0_SUBAUTH_LOGON; +//} + +struct MSV1_0_LM20_LOGON_PROFILE { + MSV1_0_PROFILE_BUFFER_TYPE MessageType; + LARGE_INTEGER KickOffTime; + LARGE_INTEGER LogoffTime; + ULONG UserFlags; + UCHAR[MSV1_0_USER_SESSION_KEY_LENGTH] UserSessionKey; + UNICODE_STRING LogonDomainName; + UCHAR[MSV1_0_LANMAN_SESSION_KEY_LENGTH] LanmanSessionKey; + UNICODE_STRING LogonServer; + UNICODE_STRING UserParameters; +} +alias MSV1_0_LM20_LOGON_PROFILE* PMSV1_0_LM20_LOGON_PROFILE; + +struct MSV1_0_SUPPLEMENTAL_CREDENTIAL { + ULONG Version; + ULONG Flags; + UCHAR[MSV1_0_OWF_PASSWORD_LENGTH] LmPassword; + UCHAR[MSV1_0_OWF_PASSWORD_LENGTH] NtPassword; +} +alias MSV1_0_SUPPLEMENTAL_CREDENTIAL* PMSV1_0_SUPPLEMENTAL_CREDENTIAL; + +struct MSV1_0_NTLM3_RESPONSE { + UCHAR[MSV1_0_NTLM3_RESPONSE_LENGTH] Response; + UCHAR RespType; + UCHAR HiRespType; + USHORT Flags; + ULONG MsgWord; + ULONGLONG TimeStamp; + UCHAR[MSV1_0_CHALLENGE_LENGTH] ChallengeFromClient; + ULONG AvPairsOff; + UCHAR _Buffer; + UCHAR* Buffer() return { return &_Buffer; } +} +alias MSV1_0_NTLM3_RESPONSE* PMSV1_0_NTLM3_RESPONSE; + +struct MSV1_0_AV_PAIR { + USHORT AvId; + USHORT AvLen; +} +alias MSV1_0_AV_PAIR* PMSV1_0_AV_PAIR; + +struct MSV1_0_CHANGEPASSWORD_REQUEST { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + UNICODE_STRING DomainName; + UNICODE_STRING AccountName; + UNICODE_STRING OldPassword; + UNICODE_STRING NewPassword; + BOOLEAN Impersonating; +} +alias MSV1_0_CHANGEPASSWORD_REQUEST* PMSV1_0_CHANGEPASSWORD_REQUEST; + +struct MSV1_0_CHANGEPASSWORD_RESPONSE { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + BOOLEAN PasswordInfoValid; + DOMAIN_PASSWORD_INFORMATION DomainPasswordInfo; +} +alias MSV1_0_CHANGEPASSWORD_RESPONSE* PMSV1_0_CHANGEPASSWORD_RESPONSE; + +struct MSV1_0_SUBAUTH_REQUEST { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + ULONG SubAuthPackageId; + ULONG SubAuthInfoLength; + PUCHAR SubAuthSubmitBuffer; +} +alias MSV1_0_SUBAUTH_REQUEST* PMSV1_0_SUBAUTH_REQUEST; + +struct MSV1_0_SUBAUTH_RESPONSE { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + ULONG SubAuthInfoLength; + PUCHAR SubAuthReturnBuffer; +} +alias MSV1_0_SUBAUTH_RESPONSE* PMSV1_0_SUBAUTH_RESPONSE; + +enum MSV1_0_DERIVECRED_TYPE_SHA1 = 0; + +struct MSV1_0_DERIVECRED_REQUEST { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + LUID LogonId; + ULONG DeriveCredType; + ULONG DeriveCredInfoLength; + UCHAR _DeriveCredSubmitBuffer; + UCHAR* DeriveCredSubmitBuffer() return { return &_DeriveCredSubmitBuffer; } +} +alias MSV1_0_DERIVECRED_REQUEST* PMSV1_0_DERIVECRED_REQUEST; + +struct MSV1_0_DERIVECRED_RESPONSE { + MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType; + ULONG DeriveCredInfoLength; + UCHAR _DeriveCredReturnBuffer; + UCHAR* DeriveCredReturnBuffer() return { return &_DeriveCredReturnBuffer; } +} +alias MSV1_0_DERIVECRED_RESPONSE* PMSV1_0_DERIVECRED_RESPONSE; + +alias uint LSA_ENUMERATION_HANDLE, LSA_OPERATIONAL_MODE, + POLICY_AUDIT_EVENT_OPTIONS; +alias uint* PLSA_ENUMERATION_HANDLE, PLSA_OPERATIONAL_MODE, + PPOLICY_AUDIT_EVENT_OPTIONS; + +struct POLICY_PRIVILEGE_DEFINITION { + LSA_UNICODE_STRING Name; + LUID LocalValue; +} +alias POLICY_PRIVILEGE_DEFINITION* PPOLICY_PRIVILEGE_DEFINITION; + +struct POLICY_AUDIT_LOG_INFO { + ULONG AuditLogPercentFull; + ULONG MaximumLogSize; + LARGE_INTEGER AuditRetentionPeriod; + BOOLEAN AuditLogFullShutdownInProgress; + LARGE_INTEGER TimeToShutdown; + ULONG NextAuditRecordId; +} +alias POLICY_AUDIT_LOG_INFO* PPOLICY_AUDIT_LOG_INFO; + +struct POLICY_AUDIT_EVENTS_INFO { + BOOLEAN AuditingMode; + PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions; + ULONG MaximumAuditEventCount; +} +alias POLICY_AUDIT_EVENTS_INFO* PPOLICY_AUDIT_EVENTS_INFO; + +struct POLICY_ACCOUNT_DOMAIN_INFO { + LSA_UNICODE_STRING DomainName; + PSID DomainSid; +} +alias POLICY_ACCOUNT_DOMAIN_INFO* PPOLICY_ACCOUNT_DOMAIN_INFO; + +struct POLICY_PRIMARY_DOMAIN_INFO { + LSA_UNICODE_STRING Name; + PSID Sid; +} +alias POLICY_PRIMARY_DOMAIN_INFO* PPOLICY_PRIMARY_DOMAIN_INFO; + +struct POLICY_DNS_DOMAIN_INFO { + LSA_UNICODE_STRING Name; + LSA_UNICODE_STRING DnsDomainName; + LSA_UNICODE_STRING DnsTreeName; + GUID DomainGuid; + PSID Sid; +} +alias POLICY_DNS_DOMAIN_INFO* PPOLICY_DNS_DOMAIN_INFO; + +struct POLICY_PD_ACCOUNT_INFO { + LSA_UNICODE_STRING Name; +} +alias POLICY_PD_ACCOUNT_INFO* PPOLICY_PD_ACCOUNT_INFO; + +struct POLICY_LSA_SERVER_ROLE_INFO { + POLICY_LSA_SERVER_ROLE LsaServerRole; +} +alias POLICY_LSA_SERVER_ROLE_INFO* PPOLICY_LSA_SERVER_ROLE_INFO; + +struct POLICY_REPLICA_SOURCE_INFO { + LSA_UNICODE_STRING ReplicaSource; + LSA_UNICODE_STRING ReplicaAccountName; +} +alias POLICY_REPLICA_SOURCE_INFO* PPOLICY_REPLICA_SOURCE_INFO; + +struct POLICY_DEFAULT_QUOTA_INFO { + QUOTA_LIMITS QuotaLimits; +} +alias POLICY_DEFAULT_QUOTA_INFO* PPOLICY_DEFAULT_QUOTA_INFO; + +struct POLICY_MODIFICATION_INFO { + LARGE_INTEGER ModifiedId; + LARGE_INTEGER DatabaseCreationTime; +} +alias POLICY_MODIFICATION_INFO* PPOLICY_MODIFICATION_INFO; + +struct POLICY_AUDIT_FULL_SET_INFO { + BOOLEAN ShutDownOnFull; +} +alias POLICY_AUDIT_FULL_SET_INFO* PPOLICY_AUDIT_FULL_SET_INFO; + +struct POLICY_AUDIT_FULL_QUERY_INFO { + BOOLEAN ShutDownOnFull; + BOOLEAN LogIsFull; +} +alias POLICY_AUDIT_FULL_QUERY_INFO* PPOLICY_AUDIT_FULL_QUERY_INFO; + +struct POLICY_EFS_INFO { + ULONG InfoLength; + PUCHAR EfsBlob; +} +alias POLICY_EFS_INFO* PPOLICY_EFS_INFO; + +struct POLICY_LOCAL_IPSEC_REFERENCE_INFO { + LSA_UNICODE_STRING ObjectPath; +} +alias POLICY_LOCAL_IPSEC_REFERENCE_INFO* PPOLICY_LOCAL_IPSEC_REFERENCE_INFO; + +struct POLICY_LOCAL_MACHINE_PASSWORD_INFO { + LARGE_INTEGER PasswordChangeInterval; +} +alias POLICY_LOCAL_MACHINE_PASSWORD_INFO* PPOLICY_LOCAL_MACHINE_PASSWORD_INFO; + +struct POLICY_LOCAL_POLICY_LOCATION_INFO { + ULONG PolicyLocation; +} +alias POLICY_LOCAL_POLICY_LOCATION_INFO* PPOLICY_LOCAL_POLICY_LOCATION_INFO; + +struct POLICY_LOCAL_QUALITY_OF_SERVICE_INFO{ + ULONG QualityOfService; +} +alias POLICY_LOCAL_QUALITY_OF_SERVICE_INFO + POLICY_DOMAIN_QUALITY_OF_SERVICE_INFO; +alias POLICY_LOCAL_QUALITY_OF_SERVICE_INFO* + PPOLICY_LOCAL_QUALITY_OF_SERVICE_INFO, + PPOLICY_DOMAIN_QUALITY_OF_SERVICE_INFO; + +struct POLICY_DOMAIN_PUBLIC_KEY_INFO { + ULONG InfoLength; + PUCHAR PublicKeyInfo; +} +alias POLICY_DOMAIN_PUBLIC_KEY_INFO* PPOLICY_DOMAIN_PUBLIC_KEY_INFO; + +struct POLICY_DOMAIN_LOCKOUT_INFO { + LARGE_INTEGER LockoutDuration; + LARGE_INTEGER LockoutObservationWindow; + USHORT LockoutThreshold; +} +alias POLICY_DOMAIN_LOCKOUT_INFO* PPOLICY_DOMAIN_LOCKOUT_INFO; + +struct POLICY_DOMAIN_PASSWORD_INFO { + USHORT MinPasswordLength; + USHORT PasswordHistoryLength; + ULONG PasswordProperties; + LARGE_INTEGER MaxPasswordAge; + LARGE_INTEGER MinPasswordAge; +} +alias POLICY_DOMAIN_PASSWORD_INFO* PPOLICY_DOMAIN_PASSWORD_INFO; + +struct POLICY_DOMAIN_KERBEROS_TICKET_INFO { + ULONG AuthenticationOptions; + LARGE_INTEGER MinTicketAge; + LARGE_INTEGER MaxTicketAge; + LARGE_INTEGER MaxRenewAge; + LARGE_INTEGER ProxyLifetime; + LARGE_INTEGER ForceLogoff; +} +alias POLICY_DOMAIN_KERBEROS_TICKET_INFO* PPOLICY_DOMAIN_KERBEROS_TICKET_INFO; + +alias LSA_HANDLE = HANDLE; +alias LSA_HANDLE* PLSA_HANDLE; + +struct TRUSTED_DOMAIN_NAME_INFO { + LSA_UNICODE_STRING Name; +} +alias TRUSTED_DOMAIN_NAME_INFO* PTRUSTED_DOMAIN_NAME_INFO; + +struct TRUSTED_CONTROLLERS_INFO { + ULONG Entries; + PLSA_UNICODE_STRING Names; +} +alias TRUSTED_CONTROLLERS_INFO* PTRUSTED_CONTROLLERS_INFO; + +struct TRUSTED_POSIX_OFFSET_INFO { + ULONG Offset; +} +alias TRUSTED_POSIX_OFFSET_INFO* PTRUSTED_POSIX_OFFSET_INFO; + +struct TRUSTED_PASSWORD_INFO { + LSA_UNICODE_STRING Password; + LSA_UNICODE_STRING OldPassword; +} +alias TRUSTED_PASSWORD_INFO* PTRUSTED_PASSWORD_INFO; + +struct TRUSTED_DOMAIN_INFORMATION_EX { + LSA_UNICODE_STRING Name; + LSA_UNICODE_STRING FlatName; + PSID Sid; + ULONG TrustDirection; + ULONG TrustType; + ULONG TrustAttributes; +} +alias TRUSTED_DOMAIN_INFORMATION_EX* PTRUSTED_DOMAIN_INFORMATION_EX; + +struct LSA_AUTH_INFORMATION { + LARGE_INTEGER LastUpdateTime; + ULONG AuthType; + ULONG AuthInfoLength; + PUCHAR AuthInfo; +} +alias LSA_AUTH_INFORMATION* PLSA_AUTH_INFORMATION; + +struct TRUSTED_DOMAIN_AUTH_INFORMATION { + ULONG IncomingAuthInfos; + PLSA_AUTH_INFORMATION IncomingAuthenticationInformation; + PLSA_AUTH_INFORMATION IncomingPreviousAuthenticationInformation; + ULONG OutgoingAuthInfos; + PLSA_AUTH_INFORMATION OutgoingAuthenticationInformation; + PLSA_AUTH_INFORMATION OutgoingPreviousAuthenticationInformation; +} +alias TRUSTED_DOMAIN_AUTH_INFORMATION* PTRUSTED_DOMAIN_AUTH_INFORMATION; + +struct TRUSTED_DOMAIN_FULL_INFORMATION { + TRUSTED_DOMAIN_INFORMATION_EX Information; + TRUSTED_POSIX_OFFSET_INFO PosixOffset; + TRUSTED_DOMAIN_AUTH_INFORMATION AuthInformation; +} +alias TRUSTED_DOMAIN_FULL_INFORMATION* PTRUSTED_DOMAIN_FULL_INFORMATION; + +extern (Windows) nothrow @nogc { + NTSTATUS LsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING, + ULONG); + NTSTATUS LsaCallAuthenticationPackage(HANDLE, ULONG, PVOID, ULONG, + PVOID*, PULONG, PNTSTATUS); + NTSTATUS LsaClose(LSA_HANDLE); + NTSTATUS LsaConnectUntrusted(PHANDLE); + NTSTATUS LsaCreateTrustedDomainEx(LSA_HANDLE, + PTRUSTED_DOMAIN_INFORMATION_EX, PTRUSTED_DOMAIN_AUTH_INFORMATION, + ACCESS_MASK, PLSA_HANDLE); + NTSTATUS LsaDeleteTrustedDomain(LSA_HANDLE, PSID); + NTSTATUS LsaDeregisterLogonProcess(HANDLE); + NTSTATUS LsaEnumerateAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING*, + PULONG); + NTSTATUS LsaEnumerateAccountsWithUserRight(LSA_HANDLE, + PLSA_UNICODE_STRING, PVOID*, PULONG); + NTSTATUS LsaEnumerateTrustedDomains(LSA_HANDLE, PLSA_ENUMERATION_HANDLE, + PVOID*, ULONG, PULONG); + NTSTATUS LsaEnumerateTrustedDomainsEx(LSA_HANDLE, PLSA_ENUMERATION_HANDLE, + TRUSTED_INFORMATION_CLASS, PVOID*, ULONG, PULONG); + NTSTATUS LsaFreeMemory(PVOID); + NTSTATUS LsaFreeReturnBuffer(PVOID); + NTSTATUS LsaLogonUser(HANDLE, PLSA_STRING, SECURITY_LOGON_TYPE, ULONG, + PVOID, ULONG, PTOKEN_GROUPS, PTOKEN_SOURCE, PVOID*, PULONG, PLUID, + PHANDLE, PQUOTA_LIMITS, PNTSTATUS); + NTSTATUS LsaLookupAuthenticationPackage(HANDLE, PLSA_STRING, PULONG); + NTSTATUS LsaLookupNames(LSA_HANDLE, ULONG, PLSA_UNICODE_STRING, + PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_SID*); + NTSTATUS LsaLookupSids(LSA_HANDLE, ULONG, PSID*, + PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_NAME*); + ULONG LsaNtStatusToWinError(NTSTATUS); + NTSTATUS LsaOpenPolicy(PLSA_UNICODE_STRING, PLSA_OBJECT_ATTRIBUTES, + ACCESS_MASK, PLSA_HANDLE); + NTSTATUS LsaQueryDomainInformationPolicy(LSA_HANDLE, + POLICY_DOMAIN_INFORMATION_CLASS, PVOID*); + NTSTATUS LsaQueryInformationPolicy(LSA_HANDLE, POLICY_INFORMATION_CLASS, + PVOID*); + NTSTATUS LsaQueryLocalInformationPolicy(LSA_HANDLE, + POLICY_LOCAL_INFORMATION_CLASS, PVOID*); + NTSTATUS LsaQueryTrustedDomainInfo(LSA_HANDLE, PSID, + TRUSTED_INFORMATION_CLASS, PVOID*); + NTSTATUS LsaQueryTrustedDomainInfoByName(LSA_HANDLE, PLSA_UNICODE_STRING, + TRUSTED_INFORMATION_CLASS, PVOID*); + NTSTATUS LsaRegisterLogonProcess(PLSA_STRING, PHANDLE, + PLSA_OPERATIONAL_MODE); + NTSTATUS LsaRemoveAccountRights(LSA_HANDLE, PSID, BOOLEAN, + PLSA_UNICODE_STRING, ULONG); + NTSTATUS LsaRetrievePrivateData(LSA_HANDLE, PLSA_UNICODE_STRING, + PLSA_UNICODE_STRING*); + NTSTATUS LsaSetDomainInformationPolicy(LSA_HANDLE, + POLICY_DOMAIN_INFORMATION_CLASS, PVOID); + NTSTATUS LsaSetInformationPolicy(LSA_HANDLE, POLICY_INFORMATION_CLASS, + PVOID); + NTSTATUS LsaSetLocalInformationPolicy(LSA_HANDLE, + POLICY_LOCAL_INFORMATION_CLASS, PVOID); + NTSTATUS LsaSetTrustedDomainInformation(LSA_HANDLE, PSID, + TRUSTED_INFORMATION_CLASS, PVOID); + NTSTATUS LsaSetTrustedDomainInfoByName(LSA_HANDLE, PLSA_UNICODE_STRING, + TRUSTED_INFORMATION_CLASS, PVOID); + NTSTATUS LsaStorePrivateData(LSA_HANDLE, PLSA_UNICODE_STRING, + PLSA_UNICODE_STRING); +} + +alias NTSTATUS function(PUNICODE_STRING, ULONG, PUNICODE_STRING) + PSAM_PASSWORD_NOTIFICATION_ROUTINE; +alias BOOLEAN function() PSAM_INIT_NOTIFICATION_ROUTINE; +alias BOOLEAN function(PUNICODE_STRING, PUNICODE_STRING, + PUNICODE_STRING, BOOLEAN) PSAM_PASSWORD_FILTER_ROUTINE; diff --git a/src/urt/internal/sys/windows/ntsecpkg.d b/src/urt/internal/sys/windows/ntsecpkg.d new file mode 100644 index 0000000..6a2dc69 --- /dev/null +++ b/src/urt/internal/sys/windows/ntsecpkg.d @@ -0,0 +1,445 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Ellery Newcomer + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_ntsecpkg.d) + */ +module urt.internal.sys.windows.ntsecpkg; +version (Windows): + +import urt.internal.sys.windows.windef, urt.internal.sys.windows.ntsecapi, urt.internal.sys.windows.security, urt.internal.sys.windows.ntdef, urt.internal.sys.windows.sspi; +import urt.internal.sys.windows.basetyps : GUID; +import urt.internal.sys.windows.winbase; + +extern(Windows): + +enum :ULONG{ + ISC_REQ_DELEGATE = 1, + ISC_REQ_MUTUAL_AUTH = 2, + ISC_REQ_REPLAY_DETECT = 4, + ISC_REQ_SEQUENCE_DETECT = 8, + ISC_REQ_CONFIDENTIALITY = 16, + ISC_REQ_USE_SESSION_KEY = 32, + ISC_REQ_PROMPT_FOR_CREDS = 64, + ISC_REQ_USE_SUPPLIED_CREDS = 128, + ISC_REQ_ALLOCATE_MEMORY = 256, + ISC_REQ_USE_DCE_STYLE = 512, + ISC_REQ_DATAGRAM = 1024, + ISC_REQ_CONNECTION = 2048, + ISC_REQ_EXTENDED_ERROR = 16384, + ISC_REQ_STREAM = 32768, + ISC_REQ_INTEGRITY = 65536, + ISC_REQ_MANUAL_CRED_VALIDATION = 524288, + ISC_REQ_HTTP = 268435456, +} + +enum ISC_RET_EXTENDED_ERROR = 16384; + +enum :ULONG{ + ASC_REQ_DELEGATE = 1, + ASC_REQ_MUTUAL_AUTH = 2, + ASC_REQ_REPLAY_DETECT = 4, + ASC_REQ_SEQUENCE_DETECT = 8, + ASC_REQ_CONFIDENTIALITY = 16, + ASC_REQ_USE_SESSION_KEY = 32, + ASC_REQ_ALLOCATE_MEMORY = 256, + ASC_REQ_USE_DCE_STYLE = 512, + ASC_REQ_DATAGRAM = 1024, + ASC_REQ_CONNECTION = 2048, + ASC_REQ_EXTENDED_ERROR = 32768, + ASC_REQ_STREAM = 65536, + ASC_REQ_INTEGRITY = 131072, +} + +enum SECURITY_NATIVE_DREP = 16; +enum SECURITY_NETWORK_DREP = 0; + +enum :ULONG{ + SECPKG_STATE_ENCRYPTION_PERMITTED = 0x01, + SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED = 0x02, + SECPKG_STATE_DOMAIN_CONTROLLER = 0x04, + SECPKG_STATE_WORKSTATION = 0x08, + SECPKG_STATE_STANDALONE = 0x10, +} + +/* enum definitions for Secure Service Provider/Authentication Packages */ +enum LSA_TOKEN_INFORMATION_TYPE { + LsaTokenInformationNull, + LsaTokenInformationV1 +} +alias LSA_TOKEN_INFORMATION_TYPE* PLSA_TOKEN_INFORMATION_TYPE; +enum SECPKG_EXTENDED_INFORMATION_CLASS +{ + SecpkgGssInfo = 1, + SecpkgContextThunks, + SecpkgMutualAuthLevel, + SecpkgMaxInfo +} +enum SECPKG_NAME_TYPE { + SecNameSamCompatible, + SecNameAlternateId, + SecNameFlat, + SecNameDN +} + +/* struct definitions for SSP/AP */ +struct SECPKG_PRIMARY_CRED { + LUID LogonId; + UNICODE_STRING DownlevelName; + UNICODE_STRING DomainName; + UNICODE_STRING Password; + UNICODE_STRING OldPassword; + PSID UserSid; + ULONG Flags; + UNICODE_STRING DnsDomainName; + UNICODE_STRING Upn; + UNICODE_STRING LogonServer; + UNICODE_STRING Spare1; + UNICODE_STRING Spare2; + UNICODE_STRING Spare3; + UNICODE_STRING Spare4; +} +alias SECPKG_PRIMARY_CRED* PSECPKG_PRIMARY_CRED; +struct SECPKG_SUPPLEMENTAL_CRED { + UNICODE_STRING PackageName; + ULONG CredentialSize; + PUCHAR Credentials; +} +alias SECPKG_SUPPLEMENTAL_CRED* PSECPKG_SUPPLEMENTAL_CRED; +struct SECPKG_SUPPLEMENTAL_CRED_ARRAY { + ULONG CredentialCount; + SECPKG_SUPPLEMENTAL_CRED[1] Credentials; +} +alias SECPKG_SUPPLEMENTAL_CRED_ARRAY* PSECPKG_SUPPLEMENTAL_CRED_ARRAY; +struct SECPKG_PARAMETERS { + ULONG Version; + ULONG MachineState; + ULONG SetupMode; + PSID DomainSid; + UNICODE_STRING DomainName; + UNICODE_STRING DnsDomainName; + GUID DomainGuid; +} +alias SECPKG_PARAMETERS* PSECPKG_PARAMETERS,PSECPKG_EVENT_DOMAIN_CHANGE; +alias SECPKG_PARAMETERS SECPKG_EVENT_DOMAIN_CHANGE; +struct SECPKG_CLIENT_INFO { + LUID LogonId; + ULONG ProcessID; + ULONG ThreadID; + BOOLEAN HasTcbPrivilege; + BOOLEAN Impersonating; + BOOLEAN Restricted; +} +alias SECPKG_CLIENT_INFO* PSECPKG_CLIENT_INFO; +struct SECURITY_USER_DATA { + SECURITY_STRING UserName; + SECURITY_STRING LogonDomainName; + SECURITY_STRING LogonServer; + PSID pSid; +} +alias SECURITY_USER_DATA* PSECURITY_USER_DATA,PSecurityUserData; +alias SECURITY_USER_DATA SecurityUserData; +struct SECPKG_GSS_INFO { + ULONG EncodedIdLength; + UCHAR[4] EncodedId; +} +alias SECPKG_GSS_INFO* PSECPKG_GSS_INFO; +struct SECPKG_CONTEXT_THUNKS { + ULONG InfoLevelCount; + ULONG[1] Levels; +} +alias SECPKG_CONTEXT_THUNKS* PSECPKG_CONTEXT_THUNKS; +struct SECPKG_MUTUAL_AUTH_LEVEL { + ULONG MutualAuthLevel; +} +alias SECPKG_MUTUAL_AUTH_LEVEL* PSECPKG_MUTUAL_AUTH_LEVEL; +struct SECPKG_CALL_INFO { + ULONG ProcessId; + ULONG ThreadId; + ULONG Attributes; + ULONG CallCount; +} +alias SECPKG_CALL_INFO* PSECPKG_CALL_INFO; +struct SECPKG_EXTENDED_INFORMATION { + SECPKG_EXTENDED_INFORMATION_CLASS Class; + union _Info{ + SECPKG_GSS_INFO GssInfo; + SECPKG_CONTEXT_THUNKS ContextThunks; + SECPKG_MUTUAL_AUTH_LEVEL MutualAuthLevel; + } + _Info Info; +} +alias SECPKG_EXTENDED_INFORMATION* PSECPKG_EXTENDED_INFORMATION; + +/* callbacks implemented by SSP/AP dlls and called by the LSA */ +alias void function(ULONG_PTR, ULONG_PTR, PSecBuffer, + PSecBuffer) PLSA_CALLBACK_FUNCTION; + +/* misc typedefs used in the below prototypes */ +alias PVOID* PLSA_CLIENT_REQUEST; +alias ULONG_PTR LSA_SEC_HANDLE; +alias LSA_SEC_HANDLE* PLSA_SEC_HANDLE; +alias LPTHREAD_START_ROUTINE SEC_THREAD_START; +alias PSECURITY_ATTRIBUTES SEC_ATTRS; + +/* functions used by SSP/AP obtainable by dispatch tables */ +alias NTSTATUS function(ULONG, PLSA_CALLBACK_FUNCTION) PLSA_REGISTER_CALLBACK; +alias NTSTATUS function(PLUID) PLSA_CREATE_LOGON_SESSION; +alias NTSTATUS function(PLUID) PLSA_DELETE_LOGON_SESSION; +alias NTSTATUS function(PLUID, ULONG, PLSA_STRING, + PLSA_STRING) PLSA_ADD_CREDENTIAL; +alias NTSTATUS function(PLUID, ULONG, PULONG, BOOLEAN, + PLSA_STRING, PULONG, PLSA_STRING) PLSA_GET_CREDENTIALS; +alias NTSTATUS function(PLUID, ULONG, PLSA_STRING) PLSA_DELETE_CREDENTIAL; +alias PVOID function(ULONG) PLSA_ALLOCATE_LSA_HEAP; +alias void function(PVOID) PLSA_FREE_LSA_HEAP; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, + ULONG, PVOID*) PLSA_ALLOCATE_CLIENT_BUFFER; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, PVOID) PLSA_FREE_CLIENT_BUFFER; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, ULONG, + PVOID, PVOID) PLSA_COPY_TO_CLIENT_BUFFER; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, + ULONG, PVOID, PVOID) PLSA_COPY_FROM_CLIENT_BUFFER; +alias NTSTATUS function() PLSA_IMPERSONATE_CLIENT; +alias NTSTATUS function() PLSA_UNLOAD_PACKAGE; +alias NTSTATUS function(HANDLE, PHANDLE) PLSA_DUPLICATE_HANDLE; +alias NTSTATUS function(PLUID, ULONG, + PVOID, BOOLEAN) PLSA_SAVE_SUPPLEMENTAL_CREDENTIALS; +alias HANDLE function(SEC_ATTRS, ULONG, SEC_THREAD_START, + PVOID, ULONG, PULONG) PLSA_CREATE_THREAD; +alias NTSTATUS function(PSECPKG_CLIENT_INFO) PLSA_GET_CLIENT_INFO; +alias HANDLE function(SEC_THREAD_START, PVOID, + ULONG, ULONG, ULONG, ULONG, HANDLE) PLSA_REGISTER_NOTIFICATION; +alias NTSTATUS function(HANDLE) PLSA_CANCEL_NOTIFICATION; +alias NTSTATUS function(PSecBuffer, PSecBuffer) PLSA_MAP_BUFFER; +alias NTSTATUS function(PLUID, PTOKEN_SOURCE, + SECURITY_LOGON_TYPE, SECURITY_IMPERSONATION_LEVEL, LSA_TOKEN_INFORMATION_TYPE, + PVOID, PTOKEN_GROUPS, PUNICODE_STRING, PUNICODE_STRING, PUNICODE_STRING, + PUNICODE_STRING, PHANDLE, PNTSTATUS) PLSA_CREATE_TOKEN; +alias void function(NTSTATUS, NTSTATUS, PUNICODE_STRING, + PUNICODE_STRING, PUNICODE_STRING, PSID, SECURITY_LOGON_TYPE, + PTOKEN_SOURCE, PLUID) PLSA_AUDIT_LOGON; +alias NTSTATUS function(PUNICODE_STRING, PVOID, ULONG, + PVOID*, PULONG, PNTSTATUS) PLSA_CALL_PACKAGE; +alias BOOLEAN function(PSECPKG_CALL_INFO) PLSA_GET_CALL_INFO; +alias NTSTATUS function(PUNICODE_STRING, PVOID, PVOID, + ULONG, PVOID*, PULONG, PNTSTATUS) PLSA_CALL_PACKAGEEX; +alias PVOID function(ULONG, ULONG) PLSA_CREATE_SHARED_MEMORY; +alias PVOID function(PVOID, ULONG) PLSA_ALLOCATE_SHARED_MEMORY; +alias void function(PVOID, PVOID) PLSA_FREE_SHARED_MEMORY; +alias BOOLEAN function(PVOID) PLSA_DELETE_SHARED_MEMORY; +alias NTSTATUS function(PSECURITY_STRING, SECPKG_NAME_TYPE, + PSECURITY_STRING, BOOLEAN, ULONG, PVOID*) PLSA_OPEN_SAM_USER; +alias NTSTATUS function(PVOID, PVOID *, PULONG, + PVOID *, PULONG) PLSA_GET_USER_CREDENTIALS; +alias NTSTATUS function(PVOID, PUCHAR *, PULONG) PLSA_GET_USER_AUTH_DATA; +alias NTSTATUS function(PVOID) PLSA_CLOSE_SAM_USER; +alias NTSTATUS function(PVOID, ULONG, + SECURITY_IMPERSONATION_LEVEL, PTOKEN_SOURCE, SECURITY_LOGON_TYPE, + PUNICODE_STRING, PHANDLE, PLUID, PUNICODE_STRING, PNTSTATUS) PLSA_CONVERT_AUTH_DATA_TO_TOKEN; +alias NTSTATUS function(PCHAR, ULONG_PTR, ULONG_PTR, + PSecBuffer, PSecBuffer) PLSA_CLIENT_CALLBACK; +alias NTSTATUS function(PSECPKG_PRIMARY_CRED, PSECPKG_SUPPLEMENTAL_CRED_ARRAY) PLSA_UPDATE_PRIMARY_CREDENTIALS; +alias NTSTATUS function(PSECURITY_STRING, + SECPKG_NAME_TYPE, PSECURITY_STRING, PUCHAR *, PULONG, PUNICODE_STRING) PLSA_GET_AUTH_DATA_FOR_USER; +alias NTSTATUS function(ULONG, BOOLEAN, + PUNICODE_STRING, PUNICODE_STRING, ULONG, PUNICODE_STRING, PUNICODE_STRING, + PULONG) PLSA_CRACK_SINGLE_NAME; +alias NTSTATUS function(ULONG, BOOLEAN, + PUNICODE_STRING, PUNICODE_STRING, PUNICODE_STRING, NTSTATUS) PLSA_AUDIT_ACCOUNT_LOGON; +alias NTSTATUS function(PUNICODE_STRING, PVOID, + PVOID, ULONG, PVOID*, PULONG, PNTSTATUS) PLSA_CALL_PACKAGE_PASSTHROUGH; + +/* Dispatch tables of functions used by SSP/AP */ +struct SECPKG_DLL_FUNCTIONS { + PLSA_ALLOCATE_LSA_HEAP AllocateHeap; + PLSA_FREE_LSA_HEAP FreeHeap; + PLSA_REGISTER_CALLBACK RegisterCallback; +} +alias SECPKG_DLL_FUNCTIONS* PSECPKG_DLL_FUNCTIONS; +struct LSA_DISPATCH_TABLE { + PLSA_CREATE_LOGON_SESSION CreateLogonSession; + PLSA_DELETE_LOGON_SESSION DeleteLogonSession; + PLSA_ADD_CREDENTIAL AddCredential; + PLSA_GET_CREDENTIALS GetCredentials; + PLSA_DELETE_CREDENTIAL DeleteCredential; + PLSA_ALLOCATE_LSA_HEAP AllocateLsaHeap; + PLSA_FREE_LSA_HEAP FreeLsaHeap; + PLSA_ALLOCATE_CLIENT_BUFFER AllocateClientBuffer; + PLSA_FREE_CLIENT_BUFFER FreeClientBuffer; + PLSA_COPY_TO_CLIENT_BUFFER CopyToClientBuffer; + PLSA_COPY_FROM_CLIENT_BUFFER CopyFromClientBuffer; +} +alias LSA_DISPATCH_TABLE* PLSA_DISPATCH_TABLE; +struct LSA_SECPKG_FUNCTION_TABLE { + PLSA_CREATE_LOGON_SESSION CreateLogonSession; + PLSA_DELETE_LOGON_SESSION DeleteLogonSession; + PLSA_ADD_CREDENTIAL AddCredential; + PLSA_GET_CREDENTIALS GetCredentials; + PLSA_DELETE_CREDENTIAL DeleteCredential; + PLSA_ALLOCATE_LSA_HEAP AllocateLsaHeap; + PLSA_FREE_LSA_HEAP FreeLsaHeap; + PLSA_ALLOCATE_CLIENT_BUFFER AllocateClientBuffer; + PLSA_FREE_CLIENT_BUFFER FreeClientBuffer; + PLSA_COPY_TO_CLIENT_BUFFER CopyToClientBuffer; + PLSA_COPY_FROM_CLIENT_BUFFER CopyFromClientBuffer; + PLSA_IMPERSONATE_CLIENT ImpersonateClient; + PLSA_UNLOAD_PACKAGE UnloadPackage; + PLSA_DUPLICATE_HANDLE DuplicateHandle; + PLSA_SAVE_SUPPLEMENTAL_CREDENTIALS SaveSupplementalCredentials; + PLSA_CREATE_THREAD CreateThread; + PLSA_GET_CLIENT_INFO GetClientInfo; + PLSA_REGISTER_NOTIFICATION RegisterNotification; + PLSA_CANCEL_NOTIFICATION CancelNotification; + PLSA_MAP_BUFFER MapBuffer; + PLSA_CREATE_TOKEN CreateToken; + PLSA_AUDIT_LOGON AuditLogon; + PLSA_CALL_PACKAGE CallPackage; + PLSA_FREE_LSA_HEAP FreeReturnBuffer; + PLSA_GET_CALL_INFO GetCallInfo; + PLSA_CALL_PACKAGEEX CallPackageEx; + PLSA_CREATE_SHARED_MEMORY CreateSharedMemory; + PLSA_ALLOCATE_SHARED_MEMORY AllocateSharedMemory; + PLSA_FREE_SHARED_MEMORY FreeSharedMemory; + PLSA_DELETE_SHARED_MEMORY DeleteSharedMemory; + PLSA_OPEN_SAM_USER OpenSamUser; + PLSA_GET_USER_CREDENTIALS GetUserCredentials; + PLSA_GET_USER_AUTH_DATA GetUserAuthData; + PLSA_CLOSE_SAM_USER CloseSamUser; + PLSA_CONVERT_AUTH_DATA_TO_TOKEN ConvertAuthDataToToken; + PLSA_CLIENT_CALLBACK ClientCallback; + PLSA_UPDATE_PRIMARY_CREDENTIALS UpdateCredentials; + PLSA_GET_AUTH_DATA_FOR_USER GetAuthDataForUser; + PLSA_CRACK_SINGLE_NAME CrackSingleName; + PLSA_AUDIT_ACCOUNT_LOGON AuditAccountLogon; + PLSA_CALL_PACKAGE_PASSTHROUGH CallPackagePassthrough; +} +alias LSA_SECPKG_FUNCTION_TABLE* PLSA_SECPKG_FUNCTION_TABLE; + +/* functions implemented by SSP/AP obtainable by dispatch tables */ +alias NTSTATUS function(ULONG, PLSA_DISPATCH_TABLE, + PLSA_STRING, PLSA_STRING, PLSA_STRING *) PLSA_AP_INITIALIZE_PACKAGE; +alias NTSTATUS function(LPWSTR, LPWSTR, LPWSTR, LPWSTR, + DWORD, DWORD, PHANDLE) PLSA_AP_LOGON_USER; +alias NTSTATUS function(PUNICODE_STRING, PVOID, ULONG, + PVOID *, PULONG, PNTSTATUS) PLSA_AP_CALL_PACKAGE; +alias void function(PLUID) PLSA_AP_LOGON_TERMINATED; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, + PVOID, PVOID, ULONG, PVOID *, PULONG, PNTSTATUS) PLSA_AP_CALL_PACKAGE_UNTRUSTED; +alias NTSTATUS function(PUNICODE_STRING, + PVOID, PVOID, ULONG, PVOID *, PULONG, PNTSTATUS) PLSA_AP_CALL_PACKAGE_PASSTHROUGH; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, + SECURITY_LOGON_TYPE, PVOID, PVOID, ULONG, PVOID *, PULONG, PLUID, PNTSTATUS, + PLSA_TOKEN_INFORMATION_TYPE, PVOID *, PUNICODE_STRING *, PUNICODE_STRING *, + PUNICODE_STRING *) PLSA_AP_LOGON_USER_EX; +alias NTSTATUS function(PLSA_CLIENT_REQUEST, + SECURITY_LOGON_TYPE, PVOID, PVOID, ULONG, PVOID *, PULONG, PLUID, PNTSTATUS, + PLSA_TOKEN_INFORMATION_TYPE, PVOID *, PUNICODE_STRING *, PUNICODE_STRING *, + PUNICODE_STRING *, PSECPKG_PRIMARY_CRED, PSECPKG_SUPPLEMENTAL_CRED_ARRAY *) PLSA_AP_LOGON_USER_EX2; +alias NTSTATUS function(ULONG_PTR, PSECPKG_PARAMETERS, + PLSA_SECPKG_FUNCTION_TABLE) SpInitializeFn; +alias NTSTATUS function() SpShutDownFn; +alias NTSTATUS function(PSecPkgInfoW) SpGetInfoFn; +alias NTSTATUS function(SECURITY_LOGON_TYPE, + PUNICODE_STRING, PSECPKG_PRIMARY_CRED, PSECPKG_SUPPLEMENTAL_CRED) SpAcceptCredentialsFn; +alias NTSTATUS function(PUNICODE_STRING, ULONG, + PLUID, PVOID, PVOID, PVOID, PLSA_SEC_HANDLE, PTimeStamp) SpAcquireCredentialsHandleFn; +alias NTSTATUS function(LSA_SEC_HANDLE, ULONG, PVOID) SpQueryCredentialsAttributesFn; +alias NTSTATUS function(LSA_SEC_HANDLE) SpFreeCredentialsHandleFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBuffer) SpSaveCredentialsFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBuffer) SpGetCredentialsFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBuffer) SpDeleteCredentialsFn; +alias NTSTATUS function(LSA_SEC_HANDLE, LSA_SEC_HANDLE, + PUNICODE_STRING, ULONG, ULONG, PSecBufferDesc, PLSA_SEC_HANDLE, PSecBufferDesc, + PULONG, PTimeStamp, PBOOLEAN, PSecBuffer) SpInitLsaModeContextFn; +alias NTSTATUS function(LSA_SEC_HANDLE, + LSA_SEC_HANDLE, PSecBufferDesc, ULONG, ULONG, PLSA_SEC_HANDLE, PSecBufferDesc, + PULONG, PTimeStamp, PBOOLEAN, PSecBuffer) SpAcceptLsaModeContextFn; +alias NTSTATUS function(LSA_SEC_HANDLE) SpDeleteContextFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBufferDesc) SpApplyControlTokenFn; +alias NTSTATUS function(PLUID, ULONG, PSecurityUserData *) SpGetUserInfoFn; +alias NTSTATUS function(SECPKG_EXTENDED_INFORMATION_CLASS, PSECPKG_EXTENDED_INFORMATION *) SpGetExtendedInformationFn; +alias NTSTATUS function(LSA_SEC_HANDLE, ULONG, PVOID) SpQueryContextAttributesFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PUNICODE_STRING, + PUNICODE_STRING, ULONG, PVOID, PVOID, PVOID, PTimeStamp) SpAddCredentialsFn; +alias NTSTATUS function( + SECPKG_EXTENDED_INFORMATION_CLASS, PSECPKG_EXTENDED_INFORMATION) SpSetExtendedInformationFn; +alias NTSTATUS function(ULONG, PSECPKG_DLL_FUNCTIONS, + PVOID *) SpInstanceInitFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBuffer) SpInitUserModeContextFn; +alias NTSTATUS function(LSA_SEC_HANDLE, ULONG, + PSecBufferDesc, ULONG) SpMakeSignatureFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBufferDesc, + ULONG, PULONG) SpVerifySignatureFn; +alias NTSTATUS function(LSA_SEC_HANDLE, ULONG, PSecBufferDesc, + ULONG) SpSealMessageFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBufferDesc, + ULONG, PULONG) SpUnsealMessageFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PHANDLE) SpGetContextTokenFn; +alias NTSTATUS function(LSA_SEC_HANDLE, PSecBufferDesc) SpCompleteAuthTokenFn; +alias NTSTATUS function(PSecBuffer, PSecBuffer) SpFormatCredentialsFn; +alias NTSTATUS function(ULONG, PUCHAR, PULONG, + PVOID *) SpMarshallSupplementalCredsFn; +alias NTSTATUS function(LSA_SEC_HANDLE, ULONG, + PSecBuffer, PHANDLE) SpExportSecurityContextFn; +alias NTSTATUS function(PSecBuffer, HANDLE, + PLSA_SEC_HANDLE) SpImportSecurityContextFn; + +/* Dispatch tables of functions implemented by SSP/AP */ +struct SECPKG_FUNCTION_TABLE { + PLSA_AP_INITIALIZE_PACKAGE InitializePackage; + PLSA_AP_LOGON_USER LogonUser; + PLSA_AP_CALL_PACKAGE CallPackage; + PLSA_AP_LOGON_TERMINATED LogonTerminated; + PLSA_AP_CALL_PACKAGE_UNTRUSTED CallPackageUntrusted; + PLSA_AP_CALL_PACKAGE_PASSTHROUGH CallPackagePassthrough; + PLSA_AP_LOGON_USER_EX LogonUserEx; + PLSA_AP_LOGON_USER_EX2 LogonUserEx2; + SpInitializeFn *Initialize; + SpShutDownFn *Shutdown; + SpGetInfoFn *GetInfo; + SpAcceptCredentialsFn *AcceptCredentials; + SpAcquireCredentialsHandleFn *AcquireCredentialsHandle; + SpQueryCredentialsAttributesFn *QueryCredentialsAttributes; + SpFreeCredentialsHandleFn *FreeCredentialsHandle; + SpSaveCredentialsFn *SaveCredentials; + SpGetCredentialsFn *GetCredentials; + SpDeleteCredentialsFn *DeleteCredentials; + SpInitLsaModeContextFn *InitLsaModeContext; + SpAcceptLsaModeContextFn *AcceptLsaModeContext; + SpDeleteContextFn *DeleteContext; + SpApplyControlTokenFn *ApplyControlToken; + SpGetUserInfoFn *GetUserInfo; + SpGetExtendedInformationFn *GetExtendedInformation; + SpQueryContextAttributesFn *QueryContextAttributes; + SpAddCredentialsFn *AddCredentials; + SpSetExtendedInformationFn *SetExtendedInformation; +} +alias SECPKG_FUNCTION_TABLE* PSECPKG_FUNCTION_TABLE; + +struct SECPKG_USER_FUNCTION_TABLE { + SpInstanceInitFn *InstanceInit; + SpInitUserModeContextFn *InitUserModeContext; + SpMakeSignatureFn *MakeSignature; + SpVerifySignatureFn *VerifySignature; + SpSealMessageFn *SealMessage; + SpUnsealMessageFn *UnsealMessage; + SpGetContextTokenFn *GetContextToken; + SpQueryContextAttributesFn *QueryContextAttributes; + SpCompleteAuthTokenFn *CompleteAuthToken; + SpDeleteContextFn *DeleteUserModeContext; + SpFormatCredentialsFn *FormatCredentials; + SpMarshallSupplementalCredsFn *MarshallSupplementalCreds; + SpExportSecurityContextFn *ExportContext; + SpImportSecurityContextFn *ImportContext; +} +alias SECPKG_USER_FUNCTION_TABLE* PSECPKG_USER_FUNCTION_TABLE; + +/* Entry points to SSP/AP */ +alias NTSTATUS function(ULONG, PULONG, + PSECPKG_FUNCTION_TABLE *, PULONG) SpLsaModeInitializeFn; +alias NTSTATUS function(ULONG, PULONG, + PSECPKG_USER_FUNCTION_TABLE *, PULONG) SpUserModeInitializeFn; diff --git a/src/urt/internal/sys/windows/package.d b/src/urt/internal/sys/windows/package.d new file mode 100644 index 0000000..a30b8fa --- /dev/null +++ b/src/urt/internal/sys/windows/package.d @@ -0,0 +1,13 @@ +/// Slim umbrella — re-exports only the vendored Windows modules. +module urt.internal.sys.windows; + +public import urt.internal.sys.windows.w32api; +public import urt.internal.sys.windows.basetsd; +public import urt.internal.sys.windows.basetyps; +public import urt.internal.sys.windows.windef; +public import urt.internal.sys.windows.winnt; +public import urt.internal.sys.windows.winbase; +public import urt.internal.sys.windows.wincon; +public import urt.internal.sys.windows.winerror; +public import urt.internal.sys.windows.winsock2; +public import urt.internal.sys.windows.winuser; diff --git a/src/urt/internal/sys/windows/schannel.d b/src/urt/internal/sys/windows/schannel.d new file mode 100644 index 0000000..10a83e9 --- /dev/null +++ b/src/urt/internal/sys/windows/schannel.d @@ -0,0 +1,106 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_schannel.d) + */ +module urt.internal.sys.windows.schannel; +version (Windows): + +import urt.internal.sys.windows.wincrypt; +import urt.internal.sys.windows.windef; + +enum DWORD SCHANNEL_CRED_VERSION = 4; +enum SCHANNEL_SHUTDOWN = 1; +/* Comment from MinGW + ? Do these belong here or in wincrypt.h + */ +enum : DWORD { + AUTHTYPE_CLIENT = 1, + AUTHTYPE_SERVER = 2 +} + +enum DWORD + SP_PROT_PCT1_SERVER = 0x01, + SP_PROT_PCT1_CLIENT = 0x02, + SP_PROT_SSL2_SERVER = 0x04, + SP_PROT_SSL2_CLIENT = 0x08, + SP_PROT_SSL3_SERVER = 0x10, + SP_PROT_SSL3_CLIENT = 0x20, + SP_PROT_TLS1_SERVER = 0x40, + SP_PROT_TLS1_CLIENT = 0x80, + SP_PROT_PCT1 = SP_PROT_PCT1_CLIENT | SP_PROT_PCT1_SERVER, + SP_PROT_TLS1 = SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_SERVER, + SP_PROT_SSL2 = SP_PROT_SSL2_CLIENT | SP_PROT_SSL2_SERVER, + SP_PROT_SSL3 = SP_PROT_SSL3_CLIENT | SP_PROT_SSL3_SERVER; + +enum DWORD + SCH_CRED_NO_SYSTEM_MAPPER = 0x0002, + SCH_CRED_NO_SERVERNAME_CHECK = 0x0004, + SCH_CRED_MANUAL_CRED_VALIDATION = 0x0008, + SCH_CRED_NO_DEFAULT_CREDS = 0x0010, + SCH_CRED_AUTO_CRED_VALIDATION = 0x0020, + SCH_CRED_USE_DEFAULT_CREDS = 0x0040, + SCH_CRED_REVOCATION_CHECK_END_CERT = 0x0100, + SCH_CRED_REVOCATION_CHECK_CHAIN = 0x0200, + SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x0400, + SCH_CRED_IGNORE_NO_REVOCATION_CHECK = 0x0800, + SCH_CRED_IGNORE_REVOCATION_OFFLINE = 0x1000; + +// No definition - presumably an opaque structure +struct _HMAPPER; + +struct SCHANNEL_CRED { + DWORD dwVersion = SCHANNEL_CRED_VERSION; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; + DWORD cMappers; + _HMAPPER** aphMappers; + DWORD cSupportedAlgs; + ALG_ID* palgSupportedAlgs; + DWORD grbitEnabledProtocols; + DWORD dwMinimumCypherStrength; + DWORD dwMaximumCypherStrength; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD reserved; +} +alias SCHANNEL_CRED* PSCHANNEL_CRED; + +struct SecPkgCred_SupportedAlgs { + DWORD cSupportedAlgs; + ALG_ID* palgSupportedAlgs; +} +alias SecPkgCred_SupportedAlgs* PSecPkgCred_SupportedAlgs; + +struct SecPkgCred_CypherStrengths { + DWORD dwMinimumCypherStrength; + DWORD dwMaximumCypherStrength; +} +alias SecPkgCred_CypherStrengths* PSecPkgCred_CypherStrengths; + +struct SecPkgCred_SupportedProtocols { + DWORD grbitProtocol; +} +alias SecPkgCred_SupportedProtocols* PSecPkgCred_SupportedProtocols; + +struct SecPkgContext_IssuerListInfoEx { + PCERT_NAME_BLOB aIssuers; + DWORD cIssuers; +} +alias SecPkgContext_IssuerListInfoEx* PSecPkgContext_IssuerListInfoEx; + +struct SecPkgContext_ConnectionInfo { + DWORD dwProtocol; + ALG_ID aiCipher; + DWORD dwCipherStrength; + ALG_ID aiHash; + DWORD dwHashStrength; + ALG_ID aiExch; + DWORD dwExchStrength; +} +alias SecPkgContext_ConnectionInfo* PSecPkgContext_ConnectionInfo; diff --git a/src/urt/internal/sys/windows/sdkddkver.d b/src/urt/internal/sys/windows/sdkddkver.d new file mode 100644 index 0000000..7cb696a --- /dev/null +++ b/src/urt/internal/sys/windows/sdkddkver.d @@ -0,0 +1,155 @@ +/** + * Windows API header module + * + * Translated from Windows SDK API + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/sdkddkver.d) + */ +module urt.internal.sys.windows.sdkddkver; +version (Windows): + +import urt.internal.sys.windows.w32api; + +enum _WIN32_WINNT_NT4 = 0x0400; +enum _WIN32_WINNT_WIN2K = 0x0500; +enum _WIN32_WINNT_WINXP = 0x0501; +enum _WIN32_WINNT_WS03 = 0x0502; +enum _WIN32_WINNT_WIN6 = 0x0600; +enum _WIN32_WINNT_VISTA = 0x0600; +enum _WIN32_WINNT_WS08 = 0x0600; +enum _WIN32_WINNT_LONGHORN = 0x0600; +enum _WIN32_WINNT_WIN7 = 0x0601; +enum _WIN32_WINNT_WIN8 = 0x0602; +enum _WIN32_WINNT_WINBLUE = 0x0603; +enum _WIN32_WINNT_WINTHRESHOLD = 0x0A00; +enum _WIN32_WINNT_WIN10 = 0x0A00; + +enum _WIN32_IE_IE20 = 0x0200; +enum _WIN32_IE_IE30 = 0x0300; +enum _WIN32_IE_IE302 = 0x0302; +enum _WIN32_IE_IE40 = 0x0400; +enum _WIN32_IE_IE401 = 0x0401; +enum _WIN32_IE_IE50 = 0x0500; +enum _WIN32_IE_IE501 = 0x0501; +enum _WIN32_IE_IE55 = 0x0550; +enum _WIN32_IE_IE60 = 0x0600; +enum _WIN32_IE_IE60SP1 = 0x0601; +enum _WIN32_IE_IE60SP2 = 0x0603; +enum _WIN32_IE_IE70 = 0x0700; +enum _WIN32_IE_IE80 = 0x0800; +enum _WIN32_IE_IE90 = 0x0900; +enum _WIN32_IE_IE100 = 0x0A00; +enum _WIN32_IE_IE110 = 0x0A00; + +enum _WIN32_IE_NT4 = _WIN32_IE_IE20; +enum _WIN32_IE_NT4SP1 = _WIN32_IE_IE20; +enum _WIN32_IE_NT4SP2 = _WIN32_IE_IE20; +enum _WIN32_IE_NT4SP3 = _WIN32_IE_IE302; +enum _WIN32_IE_NT4SP4 = _WIN32_IE_IE401; +enum _WIN32_IE_NT4SP5 = _WIN32_IE_IE401; +enum _WIN32_IE_NT4SP6 = _WIN32_IE_IE50; +enum _WIN32_IE_WIN98 = _WIN32_IE_IE401; +enum _WIN32_IE_WIN98SE = _WIN32_IE_IE50; +enum _WIN32_IE_WINME = _WIN32_IE_IE55; +enum _WIN32_IE_WIN2K = _WIN32_IE_IE501; +enum _WIN32_IE_WIN2KSP1 = _WIN32_IE_IE501; +enum _WIN32_IE_WIN2KSP2 = _WIN32_IE_IE501; +enum _WIN32_IE_WIN2KSP3 = _WIN32_IE_IE501; +enum _WIN32_IE_WIN2KSP4 = _WIN32_IE_IE501; +enum _WIN32_IE_XP = _WIN32_IE_IE60; +enum _WIN32_IE_XPSP1 = _WIN32_IE_IE60SP1; +enum _WIN32_IE_XPSP2 = _WIN32_IE_IE60SP2; +enum _WIN32_IE_WS03 = 0x0602; +enum _WIN32_IE_WS03SP1 = _WIN32_IE_IE60SP2; +enum _WIN32_IE_WIN6 = _WIN32_IE_IE70; +enum _WIN32_IE_LONGHORN = _WIN32_IE_IE70; +enum _WIN32_IE_WIN7 = _WIN32_IE_IE80; +enum _WIN32_IE_WIN8 = _WIN32_IE_IE100; +enum _WIN32_IE_WINBLUE = _WIN32_IE_IE100; +enum _WIN32_IE_WINTHRESHOLD = _WIN32_IE_IE110; +enum _WIN32_IE_WIN10 = _WIN32_IE_IE110; + + +enum NTDDI_WIN2K = 0x05000000; +enum NTDDI_WIN2KSP1 = 0x05000100; +enum NTDDI_WIN2KSP2 = 0x05000200; +enum NTDDI_WIN2KSP3 = 0x05000300; +enum NTDDI_WIN2KSP4 = 0x05000400; + +enum NTDDI_WINXP = 0x05010000; +enum NTDDI_WINXPSP1 = 0x05010100; +enum NTDDI_WINXPSP2 = 0x05010200; +enum NTDDI_WINXPSP3 = 0x05010300; +enum NTDDI_WINXPSP4 = 0x05010400; + +enum NTDDI_WS03 = 0x05020000; +enum NTDDI_WS03SP1 = 0x05020100; +enum NTDDI_WS03SP2 = 0x05020200; +enum NTDDI_WS03SP3 = 0x05020300; +enum NTDDI_WS03SP4 = 0x05020400; + +enum NTDDI_WIN6 = 0x06000000; +enum NTDDI_WIN6SP1 = 0x06000100; +enum NTDDI_WIN6SP2 = 0x06000200; +enum NTDDI_WIN6SP3 = 0x06000300; +enum NTDDI_WIN6SP4 = 0x06000400; + +enum NTDDI_VISTA = NTDDI_WIN6; +enum NTDDI_VISTASP1 = NTDDI_WIN6SP1; +enum NTDDI_VISTASP2 = NTDDI_WIN6SP2; +enum NTDDI_VISTASP3 = NTDDI_WIN6SP3; +enum NTDDI_VISTASP4 = NTDDI_WIN6SP4; + +enum NTDDI_LONGHORN = NTDDI_VISTA; + +enum NTDDI_WS08 = NTDDI_WIN6SP1; +enum NTDDI_WS08SP2 = NTDDI_WIN6SP2; +enum NTDDI_WS08SP3 = NTDDI_WIN6SP3; +enum NTDDI_WS08SP4 = NTDDI_WIN6SP4; + +enum NTDDI_WIN7 = 0x06010000; +enum NTDDI_WIN8 = 0x06020000; +enum NTDDI_WINBLUE = 0x06030000; +enum NTDDI_WINTHRESHOLD = 0x0A000000; +enum NTDDI_WIN10 = 0x0A000000; +enum NTDDI_WIN10_TH2 = 0x0A000001; +enum NTDDI_WIN10_RS1 = 0x0A000002; +enum NTDDI_WIN10_RS2 = 0x0A000003; +enum NTDDI_WIN10_RS3 = 0x0A000004; +enum NTDDI_WIN10_RS4 = 0x0A000005; +enum NTDDI_WIN10_RS5 = 0x0A000006; +enum NTDDI_WIN10_19H1 = 0x0A000007; +enum NTDDI_WIN10_VB = 0x0A000008; +enum NTDDI_WIN10_MN = 0x0A000009; +enum NTDDI_WIN10_FE = 0x0A00000A; +enum NTDDI_WIN10_CO = 0x0A00000B; +enum NTDDI_WIN10_NI = 0x0A00000C; +enum NTDDI_WIN10_CU = 0x0A00000D; +enum NTDDI_WIN11_ZN = 0x0A00000E; +enum NTDDI_WIN11_GA = 0x0A00000F; +enum NTDDI_WIN11_GE = 0x0A000010; + +enum WDK_NTDDI_VERSION = NTDDI_WIN11_GE; + +enum OSVERSION_MASK = 0xFFFF0000U; +enum SPVERSION_MASK = 0x0000FF00; +enum SUBVERSION_MASK = 0x000000FF; + +pragma(inline, true) nothrow @nogc pure @safe { + uint OSVER(uint Version) => Version & OSVERSION_MASK; + uint SPVER(uint Version) => (Version & SPVERSION_MASK) >> 8; + uint SUBVER(uint Version) => Version & SUBVERSION_MASK; + + uint NTDDI_VERSION_FROM_WIN32_WINNT2(uint Version) => Version * 0x10000; + alias NTDDI_VERSION_FROM_WIN32_WINNT = NTDDI_VERSION_FROM_WIN32_WINNT2; +} + + +static if (_WIN32_WINNT < _WIN32_WINNT_WIN10) { + enum NTDDI_VERSION = NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT); +} else { + enum NTDDI_VERSION = WDK_NTDDI_VERSION; +} + +enum WINVER = _WIN32_WINNT; diff --git a/src/urt/internal/sys/windows/security.d b/src/urt/internal/sys/windows/security.d new file mode 100644 index 0000000..5efa509 --- /dev/null +++ b/src/urt/internal/sys/windows/security.d @@ -0,0 +1,119 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Ellery Newcomer, John Colvin + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_security.d) + */ +module urt.internal.sys.windows.security; +version (Windows): + +enum : SECURITY_STATUS +{ + SEC_E_OK = 0x00000000, + SEC_E_INSUFFICIENT_MEMORY = 0x80090300, + SEC_E_INVALID_HANDLE = 0x80090301, + SEC_E_UNSUPPORTED_FUNCTION = 0x80090302, + SEC_E_TARGET_UNKNOWN = 0x80090303, + SEC_E_INTERNAL_ERROR = 0x80090304, + SEC_E_SECPKG_NOT_FOUND = 0x80090305, + SEC_E_NOT_OWNER = 0x80090306, + SEC_E_CANNOT_INSTALL = 0x80090307, + SEC_E_INVALID_TOKEN = 0x80090308, + SEC_E_CANNOT_PACK = 0x80090309, + SEC_E_QOP_NOT_SUPPORTED = 0x8009030A, + SEC_E_NO_IMPERSONATION = 0x8009030B, + SEC_E_LOGON_DENIED = 0x8009030C, + SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D, + SEC_E_NO_CREDENTIALS = 0x8009030E, + SEC_E_MESSAGE_ALTERED = 0x8009030F, + SEC_E_OUT_OF_SEQUENCE = 0x80090310, + SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311, + SEC_E_BAD_PKGID = 0x80090316, + SEC_E_CONTEXT_EXPIRED = 0x80090317, + SEC_E_INCOMPLETE_MESSAGE = 0x80090318, + SEC_E_INCOMPLETE_CREDENTIALS = 0x80090320, + SEC_E_BUFFER_TOO_SMALL = 0x80090321, + SEC_E_WRONG_PRINCIPAL = 0x80090322, + SEC_E_TIME_SKEW = 0x80090324, + SEC_E_UNTRUSTED_ROOT = 0x80090325, + SEC_E_ILLEGAL_MESSAGE = 0x80090326, + SEC_E_CERT_UNKNOWN = 0x80090327, + SEC_E_CERT_EXPIRED = 0x80090328, + SEC_E_ENCRYPT_FAILURE = 0x80090329, + SEC_E_DECRYPT_FAILURE = 0x80090330, + SEC_E_ALGORITHM_MISMATCH = 0x80090331, + SEC_E_SECURITY_QOS_FAILED = 0x80090332, + SEC_E_UNFINISHED_CONTEXT_DELETED = 0x80090333, + SEC_E_NO_TGT_REPLY = 0x80090334, + SEC_E_NO_IP_ADDRESSES = 0x80090335, + SEC_E_WRONG_CREDENTIAL_HANDLE = 0x80090336, + SEC_E_CRYPTO_SYSTEM_INVALID = 0x80090337, + SEC_E_MAX_REFERRALS_EXCEEDED = 0x80090338, + SEC_E_MUST_BE_KDC = 0x80090339, + SEC_E_STRONG_CRYPTO_NOT_SUPPORTED = 0x8009033A, + SEC_E_TOO_MANY_PRINCIPALS = 0x8009033B, + SEC_E_NO_PA_DATA = 0x8009033C, + SEC_E_PKINIT_NAME_MISMATCH = 0x8009033D, + SEC_E_SMARTCARD_LOGON_REQUIRED = 0x8009033E, + SEC_E_SHUTDOWN_IN_PROGRESS = 0x8009033F, + SEC_E_KDC_INVALID_REQUEST = 0x80090340, + SEC_E_KDC_UNABLE_TO_REFER = 0x80090341, + SEC_E_KDC_UNKNOWN_ETYPE = 0x80090342, + SEC_E_UNSUPPORTED_PREAUTH = 0x80090343, + SEC_E_DELEGATION_REQUIRED = 0x80090345, + SEC_E_BAD_BINDINGS = 0x80090346, + SEC_E_MULTIPLE_ACCOUNTS = 0x80090347, + SEC_E_NO_KERB_KEY = 0x80090348, + SEC_E_CERT_WRONG_USAGE = 0x80090349, + SEC_E_DOWNGRADE_DETECTED = 0x80090350, + SEC_E_SMARTCARD_CERT_REVOKED = 0x80090351, + SEC_E_ISSUING_CA_UNTRUSTED = 0x80090352, + SEC_E_REVOCATION_OFFLINE_C = 0x80090353, + SEC_E_PKINIT_CLIENT_FAILURE = 0x80090354, + SEC_E_SMARTCARD_CERT_EXPIRED = 0x80090355, + SEC_E_NO_S4U_PROT_SUPPORT = 0x80090356, + SEC_E_CROSSREALM_DELEGATION_FAILURE = 0x80090357, + SEC_E_REVOCATION_OFFLINE_KDC = 0x80090358, + SEC_E_ISSUING_CA_UNTRUSTED_KDC = 0x80090359, + SEC_E_KDC_CERT_EXPIRED = 0x8009035A, + SEC_E_KDC_CERT_REVOKED = 0x8009035B, + SEC_E_INVALID_PARAMETER = 0x8009035D, + SEC_E_DELEGATION_POLICY = 0x8009035E, + SEC_E_POLICY_NLTM_ONLY = 0x8009035F, + SEC_E_NO_CONTEXT = 0x80090361, + SEC_E_PKU2U_CERT_FAILURE = 0x80090362, + SEC_E_MUTUAL_AUTH_FAILED = 0x80090363, + SEC_E_ONLY_HTTPS_ALLOWED = 0x80090365, + SEC_E_APPLICATION_PROTOCOL_MISMATCH = 0x80090367, + SEC_E_INVALID_UPN_NAME = 0x80090369, + SEC_E_EXT_BUFFER_TOO_SMALL = 0x8009036A, + SEC_E_INSUFFICIENT_BUFFERS = 0x8009036B, + SEC_E_NO_SPM = SEC_E_INTERNAL_ERROR, + SEC_E_NOT_SUPPORTED = SEC_E_UNSUPPORTED_FUNCTION +} +enum : SECURITY_STATUS +{ + SEC_I_CONTINUE_NEEDED = 0x00090312, + SEC_I_COMPLETE_NEEDED = 0x00090313, + SEC_I_COMPLETE_AND_CONTINUE = 0x00090314, + SEC_I_LOCAL_LOGON = 0x00090315, + SEC_I_GENERIC_EXTENSION_RECEIVED = 0x00090316, + SEC_I_CONTEXT_EXPIRED = 0x00090317, + SEC_I_INCOMPLETE_CREDENTIALS = 0x00090320, + SEC_I_RENEGOTIATE = 0x00090321, + SEC_I_NO_LSA_CONTEXT = 0x00090323, + SEC_I_SIGNATURE_NEEDED = 0x0009035C, + SEC_I_NO_RENEGOTIATION = 0x00090360, + SEC_I_MESSAGE_FRAGMENT = 0x00090364, + SEC_I_CONTINUE_NEEDED_MESSAGE_OK = 0x00090366, + SEC_I_ASYNC_CALL_PENDING = 0x00090368, +} + +/* always a char */ +alias SEC_CHAR = char; +alias SEC_WCHAR = wchar; + +alias SECURITY_STATUS = int; diff --git a/src/urt/internal/sys/windows/sspi.d b/src/urt/internal/sys/windows/sspi.d new file mode 100644 index 0000000..d085c4a --- /dev/null +++ b/src/urt/internal/sys/windows/sspi.d @@ -0,0 +1,381 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Ellery Newcomer + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_sspi.d) + */ +module urt.internal.sys.windows.sspi; +version (Windows): + +version (ANSI) {} else version = Unicode; + +import urt.internal.sys.windows.windef; +import urt.internal.sys.windows.ntdef; +import urt.internal.sys.windows.w32api; +import urt.internal.sys.windows.security; +import urt.internal.sys.windows.ntsecapi; +import urt.internal.sys.windows.subauth; + +enum :ULONG{ + SECPKG_CRED_INBOUND = 1, + SECPKG_CRED_OUTBOUND = 2, + SECPKG_CRED_BOTH = (SECPKG_CRED_OUTBOUND|SECPKG_CRED_INBOUND), + SECPKG_CRED_ATTR_NAMES = 1, +} + +enum :ULONG{ + SECPKG_FLAG_INTEGRITY = 1, + SECPKG_FLAG_PRIVACY = 2, + SECPKG_FLAG_TOKEN_ONLY = 4, + SECPKG_FLAG_DATAGRAM = 8, + SECPKG_FLAG_CONNECTION = 16, + SECPKG_FLAG_MULTI_REQUIRED = 32, + SECPKG_FLAG_CLIENT_ONLY = 64, + SECPKG_FLAG_EXTENDED_ERROR = 128, + SECPKG_FLAG_IMPERSONATION = 256, + SECPKG_FLAG_ACCEPT_WIN32_NAME = 512, + SECPKG_FLAG_STREAM = 1024, +} + +enum :ULONG{ + SECPKG_ATTR_AUTHORITY = 6, + SECPKG_ATTR_CONNECTION_INFO = 90, + SECPKG_ATTR_ISSUER_LIST = 80, + SECPKG_ATTR_ISSUER_LIST_EX = 89, + SECPKG_ATTR_KEY_INFO = 5, + SECPKG_ATTR_LIFESPAN = 2, + SECPKG_ATTR_LOCAL_CERT_CONTEXT = 84, + SECPKG_ATTR_LOCAL_CRED = 82, + SECPKG_ATTR_NAMES = 1, + SECPKG_ATTR_PROTO_INFO = 7, + SECPKG_ATTR_REMOTE_CERT_CONTEXT = 83, + SECPKG_ATTR_REMOTE_CRED = 81, + SECPKG_ATTR_SIZES = 0, + SECPKG_ATTR_STREAM_SIZES = 4, +} + +enum :ULONG{ + SECBUFFER_EMPTY = 0, + SECBUFFER_DATA = 1, + SECBUFFER_TOKEN = 2, + SECBUFFER_PKG_PARAMS = 3, + SECBUFFER_MISSING = 4, + SECBUFFER_EXTRA = 5, + SECBUFFER_STREAM_TRAILER = 6, + SECBUFFER_STREAM_HEADER = 7, + SECBUFFER_PADDING = 9, + SECBUFFER_STREAM = 10, + SECBUFFER_READONLY = 0x80000000, + SECBUFFER_ATTRMASK = 0xf0000000, +} + +enum UNISP_NAME_A = "Microsoft Unified Security Protocol Provider"; +enum UNISP_NAME_W = "Microsoft Unified Security Protocol Provider"w; +enum SECBUFFER_VERSION = 0; + +alias UNICODE_STRING SECURITY_STRING; +alias UNICODE_STRING* PSECURITY_STRING; + +extern(Windows): + +struct SecHandle { + ULONG_PTR dwLower; + ULONG_PTR dwUpper; +} +alias SecHandle* PSecHandle; +struct SecBuffer { + ULONG cbBuffer; + ULONG BufferType; + PVOID pvBuffer; +} +alias SecBuffer* PSecBuffer; +alias SecHandle CredHandle; +alias PSecHandle PCredHandle; +alias SecHandle CtxtHandle; +alias PSecHandle PCtxtHandle; +struct SECURITY_INTEGER { + uint LowPart; + int HighPart; +} +alias SECURITY_INTEGER TimeStamp; +alias SECURITY_INTEGER* PTimeStamp; +struct SecBufferDesc { + ULONG ulVersion; + ULONG cBuffers; + PSecBuffer pBuffers; +} +alias SecBufferDesc* PSecBufferDesc; +struct SecPkgContext_StreamSizes { + ULONG cbHeader; + ULONG cbTrailer; + ULONG cbMaximumMessage; + ULONG cBuffers; + ULONG cbBlockSize; +} +alias SecPkgContext_StreamSizes* PSecPkgContext_StreamSizes; +struct SecPkgContext_Sizes { + ULONG cbMaxToken; + ULONG cbMaxSignature; + ULONG cbBlockSize; + ULONG cbSecurityTrailer; +} +alias SecPkgContext_Sizes* PSecPkgContext_Sizes; +struct SecPkgContext_AuthorityW { + SEC_WCHAR* sAuthorityName; +} +alias SecPkgContext_AuthorityW* PSecPkgContext_AuthorityW; +struct SecPkgContext_AuthorityA { + SEC_CHAR* sAuthorityName; +} +alias SecPkgContext_AuthorityA* PSecPkgContext_AuthorityA; +struct SecPkgContext_KeyInfoW { + SEC_WCHAR* sSignatureAlgorithmName; + SEC_WCHAR* sEncryptAlgorithmName; + ULONG KeySize; + ULONG SignatureAlgorithm; + ULONG EncryptAlgorithm; +} +alias SecPkgContext_KeyInfoW* PSecPkgContext_KeyInfoW; +struct SecPkgContext_KeyInfoA { + SEC_CHAR* sSignatureAlgorithmName; + SEC_CHAR* sEncryptAlgorithmName; + ULONG KeySize; + ULONG SignatureAlgorithm; + ULONG EncryptAlgorithm; +} +alias SecPkgContext_KeyInfoA* PSecPkgContext_KeyInfoA; +struct SecPkgContext_LifeSpan { + TimeStamp tsStart; + TimeStamp tsExpiry; +} +alias SecPkgContext_LifeSpan* PSecPkgContext_LifeSpan; +struct SecPkgContext_NamesW { + SEC_WCHAR* sUserName; +} +alias SecPkgContext_NamesW* PSecPkgContext_NamesW; +struct SecPkgContext_NamesA { + SEC_CHAR* sUserName; +} +alias SecPkgContext_NamesA* PSecPkgContext_NamesA; +struct SecPkgInfoW { + ULONG fCapabilities; + USHORT wVersion; + USHORT wRPCID; + ULONG cbMaxToken; + SEC_WCHAR* Name; + SEC_WCHAR* Comment; +} +alias SecPkgInfoW* PSecPkgInfoW; +struct SecPkgInfoA { + ULONG fCapabilities; + USHORT wVersion; + USHORT wRPCID; + ULONG cbMaxToken; + SEC_CHAR* Name; + SEC_CHAR* Comment; +} +alias SecPkgInfoA* PSecPkgInfoA; +/* supported only in win2k+, so it should be a PSecPkgInfoW */ +/* PSDK does not say it has ANSI/Unicode versions */ +struct SecPkgContext_PackageInfo { + PSecPkgInfoW PackageInfo; +} +alias SecPkgContext_PackageInfo* PSecPkgContext_PackageInfo; +struct SecPkgCredentials_NamesW { + SEC_WCHAR* sUserName; +} +alias SecPkgCredentials_NamesW* PSecPkgCredentials_NamesW; +struct SecPkgCredentials_NamesA { + SEC_CHAR* sUserName; +} +alias SecPkgCredentials_NamesA* PSecPkgCredentials_NamesA; + +/* TODO: missing type in SDK */ +alias void function() SEC_GET_KEY_FN; + +alias SECURITY_STATUS function(PULONG,PSecPkgInfoW*) ENUMERATE_SECURITY_PACKAGES_FN_W; +alias SECURITY_STATUS function(PULONG,PSecPkgInfoA*) ENUMERATE_SECURITY_PACKAGES_FN_A; +alias SECURITY_STATUS function(PCredHandle,ULONG,PVOID) QUERY_CREDENTIALS_ATTRIBUTES_FN_W; +alias SECURITY_STATUS function(PCredHandle,ULONG,PVOID) QUERY_CREDENTIALS_ATTRIBUTES_FN_A; +alias SECURITY_STATUS function(SEC_WCHAR*,SEC_WCHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp) ACQUIRE_CREDENTIALS_HANDLE_FN_W; +alias SECURITY_STATUS function(SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp) ACQUIRE_CREDENTIALS_HANDLE_FN_A; +alias SECURITY_STATUS function(PCredHandle) FREE_CREDENTIALS_HANDLE_FN; +alias SECURITY_STATUS function(PCredHandle,PCtxtHandle,SEC_WCHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp) INITIALIZE_SECURITY_CONTEXT_FN_W; +alias SECURITY_STATUS function(PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp) INITIALIZE_SECURITY_CONTEXT_FN_A; +alias SECURITY_STATUS function(PCredHandle,PCtxtHandle,PSecBufferDesc,ULONG,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp) ACCEPT_SECURITY_CONTEXT_FN; +alias SECURITY_STATUS function(PCtxtHandle,PSecBufferDesc) COMPLETE_AUTH_TOKEN_FN; +alias SECURITY_STATUS function(PCtxtHandle) DELETE_SECURITY_CONTEXT_FN; +alias SECURITY_STATUS function(PCtxtHandle,PSecBufferDesc) APPLY_CONTROL_TOKEN_FN_W; +alias SECURITY_STATUS function(PCtxtHandle,PSecBufferDesc) APPLY_CONTROL_TOKEN_FN_A; +alias SECURITY_STATUS function(PCtxtHandle,ULONG,PVOID) QUERY_CONTEXT_ATTRIBUTES_FN_A; +alias SECURITY_STATUS function(PCtxtHandle,ULONG,PVOID) QUERY_CONTEXT_ATTRIBUTES_FN_W; +alias SECURITY_STATUS function(PCtxtHandle) IMPERSONATE_SECURITY_CONTEXT_FN; +alias SECURITY_STATUS function(PCtxtHandle) REVERT_SECURITY_CONTEXT_FN; +alias SECURITY_STATUS function(PCtxtHandle,ULONG,PSecBufferDesc,ULONG) MAKE_SIGNATURE_FN; +alias SECURITY_STATUS function(PCtxtHandle,PSecBufferDesc,ULONG,PULONG) VERIFY_SIGNATURE_FN; +alias SECURITY_STATUS function(PVOID) FREE_CONTEXT_BUFFER_FN; +alias SECURITY_STATUS function(SEC_CHAR*,PSecPkgInfoA*) QUERY_SECURITY_PACKAGE_INFO_FN_A; +alias SECURITY_STATUS function(PCtxtHandle,HANDLE*) QUERY_SECURITY_CONTEXT_TOKEN_FN; +alias SECURITY_STATUS function(SEC_WCHAR*,PSecPkgInfoW*) QUERY_SECURITY_PACKAGE_INFO_FN_W; +alias SECURITY_STATUS function(PCtxtHandle,ULONG,PSecBufferDesc,ULONG) ENCRYPT_MESSAGE_FN; +alias SECURITY_STATUS function(PCtxtHandle,PSecBufferDesc,ULONG,PULONG) DECRYPT_MESSAGE_FN; + +/* No, it really is FreeCredentialsHandle, see the thread beginning + * http://sourceforge.net/mailarchive/message.php?msg_id=4321080 for a + * discovery discussion. */ +struct SecurityFunctionTableW{ + uint dwVersion; + ENUMERATE_SECURITY_PACKAGES_FN_W EnumerateSecurityPackagesW; + QUERY_CREDENTIALS_ATTRIBUTES_FN_W QueryCredentialsAttributesW; + ACQUIRE_CREDENTIALS_HANDLE_FN_W AcquireCredentialsHandleW; + FREE_CREDENTIALS_HANDLE_FN FreeCredentialsHandle; + void* Reserved2; + INITIALIZE_SECURITY_CONTEXT_FN_W InitializeSecurityContextW; + ACCEPT_SECURITY_CONTEXT_FN AcceptSecurityContext; + COMPLETE_AUTH_TOKEN_FN CompleteAuthToken; + DELETE_SECURITY_CONTEXT_FN DeleteSecurityContext; + APPLY_CONTROL_TOKEN_FN_W ApplyControlTokenW; + QUERY_CONTEXT_ATTRIBUTES_FN_W QueryContextAttributesW; + IMPERSONATE_SECURITY_CONTEXT_FN ImpersonateSecurityContext; + REVERT_SECURITY_CONTEXT_FN RevertSecurityContext; + MAKE_SIGNATURE_FN MakeSignature; + VERIFY_SIGNATURE_FN VerifySignature; + FREE_CONTEXT_BUFFER_FN FreeContextBuffer; + QUERY_SECURITY_PACKAGE_INFO_FN_W QuerySecurityPackageInfoW; + void* Reserved3; + void* Reserved4; + void* Reserved5; + void* Reserved6; + void* Reserved7; + void* Reserved8; + QUERY_SECURITY_CONTEXT_TOKEN_FN QuerySecurityContextToken; + ENCRYPT_MESSAGE_FN EncryptMessage; + DECRYPT_MESSAGE_FN DecryptMessage; +} +alias SecurityFunctionTableW* PSecurityFunctionTableW; +struct SecurityFunctionTableA{ + uint dwVersion; + ENUMERATE_SECURITY_PACKAGES_FN_A EnumerateSecurityPackagesA; + QUERY_CREDENTIALS_ATTRIBUTES_FN_A QueryCredentialsAttributesA; + ACQUIRE_CREDENTIALS_HANDLE_FN_A AcquireCredentialsHandleA; + FREE_CREDENTIALS_HANDLE_FN FreeCredentialsHandle; + void* Reserved2; + INITIALIZE_SECURITY_CONTEXT_FN_A InitializeSecurityContextA; + ACCEPT_SECURITY_CONTEXT_FN AcceptSecurityContext; + COMPLETE_AUTH_TOKEN_FN CompleteAuthToken; + DELETE_SECURITY_CONTEXT_FN DeleteSecurityContext; + APPLY_CONTROL_TOKEN_FN_A ApplyControlTokenA; + QUERY_CONTEXT_ATTRIBUTES_FN_A QueryContextAttributesA; + IMPERSONATE_SECURITY_CONTEXT_FN ImpersonateSecurityContext; + REVERT_SECURITY_CONTEXT_FN RevertSecurityContext; + MAKE_SIGNATURE_FN MakeSignature; + VERIFY_SIGNATURE_FN VerifySignature; + FREE_CONTEXT_BUFFER_FN FreeContextBuffer; + QUERY_SECURITY_PACKAGE_INFO_FN_A QuerySecurityPackageInfoA; + void* Reserved3; + void* Reserved4; + void* Unknown1; + void* Unknown2; + void* Unknown3; + void* Unknown4; + void* Unknown5; + ENCRYPT_MESSAGE_FN EncryptMessage; + DECRYPT_MESSAGE_FN DecryptMessage; +} +alias SecurityFunctionTableA* PSecurityFunctionTableA; +alias PSecurityFunctionTableA function() INIT_SECURITY_INTERFACE_A; +alias PSecurityFunctionTableW function() INIT_SECURITY_INTERFACE_W; + +SECURITY_STATUS FreeCredentialsHandle(PCredHandle); +SECURITY_STATUS EnumerateSecurityPackagesA(PULONG,PSecPkgInfoA*); +SECURITY_STATUS EnumerateSecurityPackagesW(PULONG,PSecPkgInfoW*); +SECURITY_STATUS AcquireCredentialsHandleA(SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); +SECURITY_STATUS AcquireCredentialsHandleW(SEC_WCHAR*,SEC_WCHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); +SECURITY_STATUS AcceptSecurityContext(PCredHandle,PCtxtHandle,PSecBufferDesc,ULONG,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); +SECURITY_STATUS InitializeSecurityContextA(PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); +SECURITY_STATUS InitializeSecurityContextW(PCredHandle,PCtxtHandle,SEC_WCHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); +SECURITY_STATUS FreeContextBuffer(PVOID); +SECURITY_STATUS QueryContextAttributesA(PCtxtHandle,ULONG,PVOID); +SECURITY_STATUS QueryContextAttributesW(PCtxtHandle,ULONG,PVOID); +SECURITY_STATUS QueryCredentialsAttributesA(PCredHandle,ULONG,PVOID); +SECURITY_STATUS QueryCredentialsAttributesW(PCredHandle,ULONG,PVOID); +static if (_WIN32_WINNT >= 0x500){ + SECURITY_STATUS QuerySecurityContextToken(PCtxtHandle,HANDLE*); +} +SECURITY_STATUS DecryptMessage(PCtxtHandle,PSecBufferDesc,ULONG,PULONG); +SECURITY_STATUS EncryptMessage(PCtxtHandle,ULONG,PSecBufferDesc,ULONG); +SECURITY_STATUS DeleteSecurityContext(PCtxtHandle); +SECURITY_STATUS CompleteAuthToken(PCtxtHandle,PSecBufferDesc); +SECURITY_STATUS ApplyControlTokenA(PCtxtHandle,PSecBufferDesc); +SECURITY_STATUS ApplyControlTokenW(PCtxtHandle,PSecBufferDesc); +SECURITY_STATUS ImpersonateSecurityContext(PCtxtHandle); +SECURITY_STATUS RevertSecurityContext(PCtxtHandle); +SECURITY_STATUS MakeSignature(PCtxtHandle,ULONG,PSecBufferDesc,ULONG); +SECURITY_STATUS VerifySignature(PCtxtHandle,PSecBufferDesc,ULONG,PULONG); +SECURITY_STATUS QuerySecurityPackageInfoA(SEC_CHAR*,PSecPkgInfoA*); +SECURITY_STATUS QuerySecurityPackageInfoW(SEC_WCHAR*,PSecPkgInfoW*); +PSecurityFunctionTableA InitSecurityInterfaceA(); +PSecurityFunctionTableW InitSecurityInterfaceW(); + +version (Unicode) { + alias UNISP_NAME_W UNISP_NAME; + alias SecPkgInfoW SecPkgInfo; + alias PSecPkgInfoW PSecPkgInfo; + alias SecPkgCredentials_NamesW SecPkgCredentials_Names; + alias PSecPkgCredentials_NamesW PSecPkgCredentials_Names; + alias SecPkgContext_AuthorityW SecPkgContext_Authority; + alias PSecPkgContext_AuthorityW PSecPkgContext_Authority; + alias SecPkgContext_KeyInfoW SecPkgContext_KeyInfo; + alias PSecPkgContext_KeyInfoW PSecPkgContext_KeyInfo; + alias SecPkgContext_NamesW SecPkgContext_Names; + alias PSecPkgContext_NamesW PSecPkgContext_Names; + alias SecurityFunctionTableW SecurityFunctionTable; + alias PSecurityFunctionTableW PSecurityFunctionTable; + alias AcquireCredentialsHandleW AcquireCredentialsHandle; + alias EnumerateSecurityPackagesW EnumerateSecurityPackages; + alias InitializeSecurityContextW InitializeSecurityContext; + alias QueryContextAttributesW QueryContextAttributes; + alias QueryCredentialsAttributesW QueryCredentialsAttributes; + alias QuerySecurityPackageInfoW QuerySecurityPackageInfo; + alias ApplyControlTokenW ApplyControlToken; + alias ENUMERATE_SECURITY_PACKAGES_FN_W ENUMERATE_SECURITY_PACKAGES_FN; + alias QUERY_CREDENTIALS_ATTRIBUTES_FN_W QUERY_CREDENTIALS_ATTRIBUTES_FN; + alias ACQUIRE_CREDENTIALS_HANDLE_FN_W ACQUIRE_CREDENTIALS_HANDLE_FN; + alias INITIALIZE_SECURITY_CONTEXT_FN_W INITIALIZE_SECURITY_CONTEXT_FN; + alias APPLY_CONTROL_TOKEN_FN_W APPLY_CONTROL_TOKEN_FN; + alias QUERY_CONTEXT_ATTRIBUTES_FN_W QUERY_CONTEXT_ATTRIBUTES_FN; + alias QUERY_SECURITY_PACKAGE_INFO_FN_W QUERY_SECURITY_PACKAGE_INFO_FN; + alias INIT_SECURITY_INTERFACE_W INIT_SECURITY_INTERFACE; +}else{ + alias UNISP_NAME_A UNISP_NAME; + alias SecPkgInfoA SecPkgInfo; + alias PSecPkgInfoA PSecPkgInfo; + alias SecPkgCredentials_NamesA SecPkgCredentials_Names; + alias PSecPkgCredentials_NamesA PSecPkgCredentials_Names; + alias SecPkgContext_AuthorityA SecPkgContext_Authority; + alias PSecPkgContext_AuthorityA PSecPkgContext_Authority; + alias SecPkgContext_KeyInfoA SecPkgContext_KeyInfo; + alias PSecPkgContext_KeyInfoA PSecPkgContext_KeyInfo; + alias SecPkgContext_NamesA SecPkgContext_Names; + alias PSecPkgContext_NamesA PSecPkgContext_Names; + alias SecurityFunctionTableA SecurityFunctionTable; + alias PSecurityFunctionTableA PSecurityFunctionTable; + alias AcquireCredentialsHandleA AcquireCredentialsHandle; + alias EnumerateSecurityPackagesA EnumerateSecurityPackages; + alias InitializeSecurityContextA InitializeSecurityContext; + alias QueryContextAttributesA QueryContextAttributes; + alias QueryCredentialsAttributesA QueryCredentialsAttributes; + alias QuerySecurityPackageInfoA QuerySecurityPackageInfo; + alias ApplyControlTokenA ApplyControlToken; + alias ENUMERATE_SECURITY_PACKAGES_FN_A ENUMERATE_SECURITY_PACKAGES_FN; + alias QUERY_CREDENTIALS_ATTRIBUTES_FN_A QUERY_CREDENTIALS_ATTRIBUTES_FN; + alias ACQUIRE_CREDENTIALS_HANDLE_FN_A ACQUIRE_CREDENTIALS_HANDLE_FN; + alias INITIALIZE_SECURITY_CONTEXT_FN_A INITIALIZE_SECURITY_CONTEXT_FN; + alias APPLY_CONTROL_TOKEN_FN_A APPLY_CONTROL_TOKEN_FN; + alias QUERY_CONTEXT_ATTRIBUTES_FN_A QUERY_CONTEXT_ATTRIBUTES_FN; + alias QUERY_SECURITY_PACKAGE_INFO_FN_A QUERY_SECURITY_PACKAGE_INFO_FN; + alias INIT_SECURITY_INTERFACE_A INIT_SECURITY_INTERFACE; +} diff --git a/src/urt/internal/sys/windows/subauth.d b/src/urt/internal/sys/windows/subauth.d new file mode 100644 index 0000000..fc43416 --- /dev/null +++ b/src/urt/internal/sys/windows/subauth.d @@ -0,0 +1,275 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_subauth.d) + */ +module urt.internal.sys.windows.subauth; +version (Windows): + +import urt.internal.sys.windows.ntdef, urt.internal.sys.windows.windef; + +/+ +alias LONG NTSTATUS; +alias NTSTATUS* PNTSTATUS; ++/ + +enum : ULONG { + MSV1_0_PASSTHRU = 1, + MSV1_0_GUEST_LOGON = 2 +} + +// USER_ALL_INFORMATION.WhichFields (Undocumented) +enum ULONG + MSV1_0_VALIDATION_LOGOFF_TIME = 1, + MSV1_0_VALIDATION_KICKOFF_TIME = 2, + MSV1_0_VALIDATION_LOGON_SERVER = 4, + MSV1_0_VALIDATION_LOGON_DOMAIN = 8, + MSV1_0_VALIDATION_SESSION_KEY = 16, + MSV1_0_VALIDATION_USER_FLAGS = 32, + MSV1_0_VALIDATION_USER_ID = 64; + +// ?ActionsPerformed? (Undocumented) +enum MSV1_0_SUBAUTH_ACCOUNT_DISABLED = 1; +enum MSV1_0_SUBAUTH_PASSWORD = 2; +enum MSV1_0_SUBAUTH_WORKSTATIONS = 4; +enum MSV1_0_SUBAUTH_LOGON_HOURS = 8; +enum MSV1_0_SUBAUTH_ACCOUNT_EXPIRY = 16; +enum MSV1_0_SUBAUTH_PASSWORD_EXPIRY = 32; +enum MSV1_0_SUBAUTH_ACCOUNT_TYPE = 64; +enum MSV1_0_SUBAUTH_LOCKOUT = 128; + +enum NEXT_FREE_ACCOUNT_CONTROL_BIT = 131072; + +enum SAM_DAYS_PER_WEEK = 7; +enum SAM_HOURS_PER_WEEK = 168; +enum SAM_MINUTES_PER_WEEK = 10080; + +enum : NTSTATUS { + STATUS_SUCCESS = 0, + STATUS_INVALID_INFO_CLASS = 0xC0000003, + STATUS_NO_SUCH_USER = 0xC0000064, + STATUS_WRONG_PASSWORD = 0xC000006A, + STATUS_PASSWORD_RESTRICTION = 0xC000006C, + STATUS_LOGON_FAILURE = 0xC000006D, + STATUS_ACCOUNT_RESTRICTION = 0xC000006E, + STATUS_INVALID_LOGON_HOURS = 0xC000006F, + STATUS_INVALID_WORKSTATION = 0xC0000070, + STATUS_PASSWORD_EXPIRED = 0xC0000071, + STATUS_ACCOUNT_DISABLED = 0xC0000072, + STATUS_INSUFFICIENT_RESOURCES = 0xC000009A, + STATUS_ACCOUNT_EXPIRED = 0xC0000193, + STATUS_PASSWORD_MUST_CHANGE = 0xC0000224, + STATUS_ACCOUNT_LOCKED_OUT = 0xC0000234 +} + +// Note: undocumented in MSDN +// USER_ALL_INFORMATION.UserAccountControl +enum ULONG + USER_ACCOUNT_DISABLED = 1, + USER_HOME_DIRECTORY_REQUIRED = 2, + USER_PASSWORD_NOT_REQUIRED = 4, + USER_TEMP_DUPLICATE_ACCOUNT = 8, + USER_NORMAL_ACCOUNT = 16, + USER_MNS_LOGON_ACCOUNT = 32, + USER_INTERDOMAIN_TRUST_ACCOUNT = 64, + USER_WORKSTATION_TRUST_ACCOUNT = 128, + USER_SERVER_TRUST_ACCOUNT = 256, + USER_DONT_EXPIRE_PASSWORD = 512, + USER_ACCOUNT_AUTO_LOCKED = 1024, + USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 2048, + USER_SMARTCARD_REQUIRED = 4096, + USER_TRUSTED_FOR_DELEGATION = 8192, + USER_NOT_DELEGATED = 16384, + USER_USE_DES_KEY_ONLY = 32768, + USER_DONT_REQUIRE_PREAUTH = 65536, + + USER_MACHINE_ACCOUNT_MASK = 448, + USER_ACCOUNT_TYPE_MASK = 472, + USER_ALL_PARAMETERS = 2097152; + +/+ +struct UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} +alias UNICODE_STRING* PUNICODE_STRING; + +struct STRING { + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} +alias STRING* PSTRING; ++/ + +alias SAM_HANDLE = HANDLE; +alias SAM_HANDLE* PSAM_HANDLE; + +struct OLD_LARGE_INTEGER { + ULONG LowPart; + LONG HighPart; +} +alias OLD_LARGE_INTEGER* POLD_LARGE_INTEGER; + +enum NETLOGON_LOGON_INFO_CLASS { + NetlogonInteractiveInformation = 1, + NetlogonNetworkInformation, + NetlogonServiceInformation, + NetlogonGenericInformation, + NetlogonInteractiveTransitiveInformation, + NetlogonNetworkTransitiveInformation, + NetlogonServiceTransitiveInformation +} + + +enum CYPHER_BLOCK_LENGTH = 8; +enum USER_SESSION_KEY_LENGTH = CYPHER_BLOCK_LENGTH * 2; +enum CLEAR_BLOCK_LENGTH = 8; + +struct CYPHER_BLOCK { + CHAR[CYPHER_BLOCK_LENGTH] data = 0; +} +alias CYPHER_BLOCK* PCYPHER_BLOCK; + +struct CLEAR_BLOCK { + CHAR[CLEAR_BLOCK_LENGTH] data = 0; +} +alias CLEAR_BLOCK* PCLEAR_BLOCK; + +struct LM_OWF_PASSWORD { + CYPHER_BLOCK[2] data; +} +alias LM_OWF_PASSWORD* PLM_OWF_PASSWORD; + +struct USER_SESSION_KEY { + CYPHER_BLOCK[2] data; +} +alias USER_SESSION_KEY* PUSER_SESSION_KEY; + +alias CLEAR_BLOCK LM_CHALLENGE; +alias LM_CHALLENGE* PLM_CHALLENGE; + +alias LM_OWF_PASSWORD NT_OWF_PASSWORD; +alias NT_OWF_PASSWORD* PNT_OWF_PASSWORD; +alias LM_CHALLENGE NT_CHALLENGE; +alias NT_CHALLENGE* PNT_CHALLENGE; + +struct LOGON_HOURS { + USHORT UnitsPerWeek; + PUCHAR LogonHours; +} +alias LOGON_HOURS* PLOGON_HOURS; + +struct SR_SECURITY_DESCRIPTOR { + ULONG Length; + PUCHAR SecurityDescriptor; +} +alias SR_SECURITY_DESCRIPTOR* PSR_SECURITY_DESCRIPTOR; + +align(4): +struct USER_ALL_INFORMATION { + LARGE_INTEGER LastLogon; + LARGE_INTEGER LastLogoff; + LARGE_INTEGER PasswordLastSet; + LARGE_INTEGER AccountExpires; + LARGE_INTEGER PasswordCanChange; + LARGE_INTEGER PasswordMustChange; + UNICODE_STRING UserName; + UNICODE_STRING FullName; + UNICODE_STRING HomeDirectory; + UNICODE_STRING HomeDirectoryDrive; + UNICODE_STRING ScriptPath; + UNICODE_STRING ProfilePath; + UNICODE_STRING AdminComment; + UNICODE_STRING WorkStations; + UNICODE_STRING UserComment; + UNICODE_STRING Parameters; + UNICODE_STRING LmPassword; + UNICODE_STRING NtPassword; + UNICODE_STRING PrivateData; + SR_SECURITY_DESCRIPTOR SecurityDescriptor; + ULONG UserId; + ULONG PrimaryGroupId; + ULONG UserAccountControl; + ULONG WhichFields; + LOGON_HOURS LogonHours; + USHORT BadPasswordCount; + USHORT LogonCount; + USHORT CountryCode; + USHORT CodePage; + BOOLEAN LmPasswordPresent; + BOOLEAN NtPasswordPresent; + BOOLEAN PasswordExpired; + BOOLEAN PrivateDataSensitive; +} +alias USER_ALL_INFORMATION* PUSER_ALL_INFORMATION; +align: + +struct MSV1_0_VALIDATION_INFO { + LARGE_INTEGER LogoffTime; + LARGE_INTEGER KickoffTime; + UNICODE_STRING LogonServer; + UNICODE_STRING LogonDomainName; + USER_SESSION_KEY SessionKey; + BOOLEAN Authoritative; + ULONG UserFlags; + ULONG WhichFields; + ULONG UserId; +} +alias MSV1_0_VALIDATION_INFO* PMSV1_0_VALIDATION_INFO; + +struct NETLOGON_LOGON_IDENTITY_INFO { + UNICODE_STRING LogonDomainName; + ULONG ParameterControl; + OLD_LARGE_INTEGER LogonId; + UNICODE_STRING UserName; + UNICODE_STRING Workstation; +} +alias NETLOGON_LOGON_IDENTITY_INFO* PNETLOGON_LOGON_IDENTITY_INFO; + +struct NETLOGON_INTERACTIVE_INFO { + NETLOGON_LOGON_IDENTITY_INFO Identity; + LM_OWF_PASSWORD LmOwfPassword; + NT_OWF_PASSWORD NtOwfPassword; +} +alias NETLOGON_INTERACTIVE_INFO* PNETLOGON_INTERACTIVE_INFO; + +struct NETLOGON_GENERIC_INFO { + NETLOGON_LOGON_IDENTITY_INFO Identity; + UNICODE_STRING PackageName; + ULONG DataLength; + PUCHAR LogonData; +} +alias NETLOGON_GENERIC_INFO* PNETLOGON_GENERIC_INFO; + +struct NETLOGON_NETWORK_INFO { + NETLOGON_LOGON_IDENTITY_INFO Identity; + LM_CHALLENGE LmChallenge; + STRING NtChallengeResponse; + STRING LmChallengeResponse; +} +alias NETLOGON_NETWORK_INFO* PNETLOGON_NETWORK_INFO; + +struct NETLOGON_SERVICE_INFO { + NETLOGON_LOGON_IDENTITY_INFO Identity; + LM_OWF_PASSWORD LmOwfPassword; + NT_OWF_PASSWORD NtOwfPassword; +} +alias NETLOGON_SERVICE_INFO* PNETLOGON_SERVICE_INFO; + +extern (Windows) { +NTSTATUS Msv1_0SubAuthenticationRoutine(NETLOGON_LOGON_INFO_CLASS,PVOID, + ULONG,PUSER_ALL_INFORMATION,PULONG,PULONG, + PBOOLEAN,PLARGE_INTEGER,PLARGE_INTEGER); +NTSTATUS Msv1_0SubAuthenticationFilter(NETLOGON_LOGON_INFO_CLASS,PVOID, + ULONG,PUSER_ALL_INFORMATION,PULONG,PULONG, + PBOOLEAN,PLARGE_INTEGER,PLARGE_INTEGER); +NTSTATUS Msv1_0SubAuthenticationRoutineGeneric(PVOID,ULONG,PULONG,PVOID*); +NTSTATUS Msv1_0SubAuthenticationRoutineEx(NETLOGON_LOGON_INFO_CLASS,PVOID, + ULONG,PUSER_ALL_INFORMATION,SAM_HANDLE, + PMSV1_0_VALIDATION_INFO,PULONG); +} diff --git a/src/urt/internal/sys/windows/w32api.d b/src/urt/internal/sys/windows/w32api.d new file mode 100644 index 0000000..94ed9ac --- /dev/null +++ b/src/urt/internal/sys/windows/w32api.d @@ -0,0 +1,138 @@ +/** + * Windows API header module + * + * Translated from MinGW API for MS-Windows 4.0 + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_w32api.d) + */ +module urt.internal.sys.windows.w32api; +version (Windows): + +import urt.internal.sys.windows.sdkddkver; + +version (ANSI) {} else version = Unicode; + +enum __W32API_VERSION = 3.17; +enum __W32API_MAJOR_VERSION = 3; +enum __W32API_MINOR_VERSION = 17; + +enum Windows95 = 0x0400; +enum Windows98 = 0x0410; +enum WindowsME = 0x0500; + +enum WindowsNT4 = 0x0400; +enum Windows2000 = 0x0500; +enum WindowsXP = 0x0501; +enum Windows2003 = 0x0502; +enum WindowsVista = 0x0600; +enum Windows7 = 0x0601; +enum Windows8 = 0x0602; + +enum IE3 = 0x0300; +enum IE301 = 0x0300; +enum IE302 = 0x0300; +enum IE4 = 0x0400; +enum IE401 = 0x0401; +enum IE5 = 0x0500; +enum IE5a = 0x0500; +enum IE5b = 0x0500; +enum IE501 = 0x0501; +enum IE55 = 0x0501; +enum IE56 = 0x0560; +enum IE6 = 0x0600; +enum IE601 = 0x0601; +enum IE602 = 0x0603; +enum IE7 = 0x0700; +enum IE8 = 0x0800; +enum IE9 = 0x0900; +enum IE10 = 0x0A00; + +/* These version identifiers are used to specify the minimum version of Windows that an + * application will support. + * + * Previously the minimum Windows 9x and Windows NT versions could be specified. However, since + * Windows 9x is no longer supported, either by Microsoft or by DMD, this distinction has been + * removed in order to simplify the bindings. + */ +version (Windows11) { + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN10; +} else version (Windows10) { + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN10; +} else version (Windows8_1) { // also Windows2012R2 + enum uint _WIN32_WINNT = _WIN32_WINNT_WINBLUE; +} else version (Windows8) { // also Windows2012 + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN8; +} else version (Windows7) { // also Windows2008R2 + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN7; +} else version (WindowsVista) { // also Windows2008 + enum uint _WIN32_WINNT = _WIN32_WINNT_VISTA; +} else version (Windows2003) { // also WindowsHomeServer, WindowsXP64 + enum uint _WIN32_WINNT = _WIN32_WINNT_WS03; +} else version (WindowsXP) { + enum uint _WIN32_WINNT = _WIN32_WINNT_WINXP; +} else version (Windows2000) { + // Current DMD doesn't support any version of Windows older than XP, + // but third-party compilers could use this + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN2K; +} else { + enum uint _WIN32_WINNT = _WIN32_WINNT_WIN7; +} + +version (IE11) { + enum uint _WIN32_IE = _WIN32_IE_IE110; +} else version (IE10) { + enum uint _WIN32_IE = _WIN32_IE_IE100; +} else version (IE9) { + enum uint _WIN32_IE = _WIN32_IE_IE90; +} else version (IE8) { + enum uint _WIN32_IE = _WIN32_IE_IE80; +} else version (IE7) { + enum uint _WIN32_IE = _WIN32_IE_IE70; +} else version (IE602) { + enum uint _WIN32_IE = _WIN32_IE_IE60SP2; +} else version (IE601) { + enum uint _WIN32_IE = _WIN32_IE_IE60SP1; +} else version (IE6) { + enum uint _WIN32_IE = _WIN32_IE_IE60; +} else version (IE56) { + enum uint _WIN32_IE = _WIN32_IE_IE60; +} else version (IE55) { + enum uint _WIN32_IE = _WIN32_IE_IE55; +} else version (IE501) { + enum uint _WIN32_IE = _WIN32_IE_IE501; +} else version (IE5) { + enum uint _WIN32_IE = _WIN32_IE_IE50; +} else version (IE401) { + enum uint _WIN32_IE = _WIN32_IE_IE401; +} else version (IE4) { + enum uint _WIN32_IE = _WIN32_IE_IE40; +} else version (IE3) { + enum uint _WIN32_IE = _WIN32_IE_IE30; +} else static if (_WIN32_WINNT >= _WIN32_WINNT_WIN2K) { + enum uint _WIN32_IE = _WIN32_IE_IE60; +} else static if (_WIN32_WINNT >= Windows98) { //NOTE: _WIN32_WINNT will never be set this low + enum uint _WIN32_IE = _WIN32_IE_IE40; +} else { + enum uint _WIN32_IE = 0; +} + +debug (WindowsUnitTest) { + unittest { + printf("Windows NT version: %03x\n", _WIN32_WINNT); + printf("IE version: %03x\n", _WIN32_IE); + } +} + +version (Unicode) { + enum bool _WIN32_UNICODE = true; + package template DECLARE_AW(string name) { + mixin("alias " ~ name ~ "W " ~ name ~ ";"); + } +} else { + enum bool _WIN32_UNICODE = false; + package template DECLARE_AW(string name) { + mixin("alias " ~ name ~ "A " ~ name ~ ";"); + } +} diff --git a/src/urt/internal/sys/windows/winbase.d b/src/urt/internal/sys/windows/winbase.d new file mode 100644 index 0000000..c379452 --- /dev/null +++ b/src/urt/internal/sys/windows/winbase.d @@ -0,0 +1,2941 @@ +/** + * Windows API header module + * + * Translated from MinGW API for MS-Windows 3.10 + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_winbase.d) + */ +module urt.internal.sys.windows.winbase; +version (Windows): + +version (ANSI) {} else version = Unicode; +pragma(lib, "kernel32"); + +/** +Translation Notes: +The following macros are obsolete, and have no effect. + +LockSegment(w), MakeProcInstance(p, i), UnlockResource(h), UnlockSegment(w) +FreeModule(m), FreeProcInstance(p), GetFreeSpace(w), DefineHandleTable(w) +SetSwapAreaSize(w), LimitEmsPages(n), Yield() + +// These are not required for DMD. + +//FIXME: +// #ifndef UNDER_CE + int WinMain(HINSTANCE, HINSTANCE, LPSTR, int); +#else + int WinMain(HINSTANCE, HINSTANCE, LPWSTR, int); +#endif +int wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int); + +*/ + +import urt.internal.sys.windows.windef; +import urt.internal.sys.windows.basetyps, urt.internal.sys.windows.w32api, urt.internal.sys.windows.winnt; + +// FIXME: +//alias void va_list; +import urt.internal.stdc : va_list; +import urt.mem : memset, memcpy, memmove; + + +// COMMPROP structure, used by GetCommProperties() +// ----------------------------------------------- + +// Communications provider type +enum : DWORD { + PST_UNSPECIFIED, + PST_RS232, + PST_PARALLELPORT, + PST_RS422, + PST_RS423, + PST_RS449, + PST_MODEM, // = 6 + PST_FAX = 0x0021, + PST_SCANNER = 0x0022, + PST_NETWORK_BRIDGE = 0x0100, + PST_LAT = 0x0101, + PST_TCPIP_TELNET = 0x0102, + PST_X25 = 0x0103 +} + +// Max baud rate +enum : DWORD { + BAUD_075 = 0x00000001, + BAUD_110 = 0x00000002, + BAUD_134_5 = 0x00000004, + BAUD_150 = 0x00000008, + BAUD_300 = 0x00000010, + BAUD_600 = 0x00000020, + BAUD_1200 = 0x00000040, + BAUD_1800 = 0x00000080, + BAUD_2400 = 0x00000100, + BAUD_4800 = 0x00000200, + BAUD_7200 = 0x00000400, + BAUD_9600 = 0x00000800, + BAUD_14400 = 0x00001000, + BAUD_19200 = 0x00002000, + BAUD_38400 = 0x00004000, + BAUD_56K = 0x00008000, + BAUD_128K = 0x00010000, + BAUD_115200 = 0x00020000, + BAUD_57600 = 0x00040000, + BAUD_USER = 0x10000000 +} + +// Comm capabilities +enum : DWORD { + PCF_DTRDSR = 0x0001, + PCF_RTSCTS = 0x0002, + PCF_RLSD = 0x0004, + PCF_PARITY_CHECK = 0x0008, + PCF_XONXOFF = 0x0010, + PCF_SETXCHAR = 0x0020, + PCF_TOTALTIMEOUTS = 0x0040, + PCF_INTTIMEOUTS = 0x0080, + PCF_SPECIALCHARS = 0x0100, + PCF_16BITMODE = 0x0200 +} + +enum : DWORD { + SP_PARITY = 1, + SP_BAUD = 2, + SP_DATABITS = 4, + SP_STOPBITS = 8, + SP_HANDSHAKING = 16, + SP_PARITY_CHECK = 32, + SP_RLSD = 64 +} + +enum : DWORD { + DATABITS_5 = 1, + DATABITS_6 = 2, + DATABITS_7 = 4, + DATABITS_8 = 8, + DATABITS_16 = 16, + DATABITS_16X = 32 +} + +enum : WORD { + STOPBITS_10 = 0x0001, + STOPBITS_15 = 0x0002, + STOPBITS_20 = 0x0004, + PARITY_NONE = 0x0100, + PARITY_ODD = 0x0200, + PARITY_EVEN = 0x0400, + PARITY_MARK = 0x0800, + PARITY_SPACE = 0x1000 +} + +// used by dwServiceMask +enum SP_SERIALCOMM = 1; + +struct COMMPROP { + WORD wPacketLength; + WORD wPacketVersion; + DWORD dwServiceMask; + DWORD dwReserved1; + DWORD dwMaxTxQueue; + DWORD dwMaxRxQueue; + DWORD dwMaxBaud; + DWORD dwProvSubType; + DWORD dwProvCapabilities; + DWORD dwSettableParams; + DWORD dwSettableBaud; + WORD wSettableData; + WORD wSettableStopParity; + DWORD dwCurrentTxQueue; + DWORD dwCurrentRxQueue; + DWORD dwProvSpec1; + DWORD dwProvSpec2; + WCHAR _wcProvChar = 0; + + WCHAR* wcProvChar() return { return &_wcProvChar; } +} +alias COMMPROP* LPCOMMPROP; + +// ---------- + +// for DEBUG_EVENT +enum : DWORD { + EXCEPTION_DEBUG_EVENT = 1, + CREATE_THREAD_DEBUG_EVENT, + CREATE_PROCESS_DEBUG_EVENT, + EXIT_THREAD_DEBUG_EVENT, + EXIT_PROCESS_DEBUG_EVENT, + LOAD_DLL_DEBUG_EVENT, + UNLOAD_DLL_DEBUG_EVENT, + OUTPUT_DEBUG_STRING_EVENT, + RIP_EVENT +} + +enum HFILE HFILE_ERROR = cast(HFILE) (-1); + +// for SetFilePointer() +enum : DWORD { + FILE_BEGIN = 0, + FILE_CURRENT = 1, + FILE_END = 2 +} +enum DWORD INVALID_SET_FILE_POINTER = -1; + + +// for OpenFile() +deprecated enum : UINT { + OF_READ = 0, + OF_WRITE = 0x0001, + OF_READWRITE = 0x0002, + OF_SHARE_COMPAT = 0, + OF_SHARE_EXCLUSIVE = 0x0010, + OF_SHARE_DENY_WRITE = 0x0020, + OF_SHARE_DENY_READ = 0x0030, + OF_SHARE_DENY_NONE = 0x0040, + OF_PARSE = 0x0100, + OF_DELETE = 0x0200, + OF_VERIFY = 0x0400, + OF_CANCEL = 0x0800, + OF_CREATE = 0x1000, + OF_PROMPT = 0x2000, + OF_EXIST = 0x4000, + OF_REOPEN = 0x8000 +} + +enum : DWORD { + NMPWAIT_NOWAIT = 1, + NMPWAIT_WAIT_FOREVER = -1, + NMPWAIT_USE_DEFAULT_WAIT = 0 +} + +// for ClearCommError() +enum DWORD + CE_RXOVER = 0x0001, + CE_OVERRUN = 0x0002, + CE_RXPARITY = 0x0004, + CE_FRAME = 0x0008, + CE_BREAK = 0x0010, + CE_TXFULL = 0x0100, + CE_PTO = 0x0200, + CE_IOE = 0x0400, + CE_DNS = 0x0800, + CE_OOP = 0x1000, + CE_MODE = 0x8000; + +// for CopyProgressRoutine callback. +enum : DWORD { + PROGRESS_CONTINUE = 0, + PROGRESS_CANCEL = 1, + PROGRESS_STOP = 2, + PROGRESS_QUIET = 3 +} + +enum : DWORD { + CALLBACK_CHUNK_FINISHED = 0, + CALLBACK_STREAM_SWITCH = 1 +} + +// CopyFileEx() +enum : DWORD { + COPY_FILE_FAIL_IF_EXISTS = 1, + COPY_FILE_RESTARTABLE = 2 +} + +enum : DWORD { + FILE_MAP_COPY = 1, + FILE_MAP_WRITE = 2, + FILE_MAP_READ = 4, + FILE_MAP_ALL_ACCESS = 0x000F001F +} + +enum : DWORD { + MUTEX_ALL_ACCESS = 0x001f0001, + MUTEX_MODIFY_STATE = 0x00000001, + SEMAPHORE_ALL_ACCESS = 0x001f0003, + SEMAPHORE_MODIFY_STATE = 0x00000002, + EVENT_ALL_ACCESS = 0x001f0003, + EVENT_MODIFY_STATE = 0x00000002 +} + +// CreateNamedPipe() +enum : DWORD { + PIPE_ACCESS_INBOUND = 1, + PIPE_ACCESS_OUTBOUND = 2, + PIPE_ACCESS_DUPLEX = 3 +} + +enum DWORD + PIPE_TYPE_BYTE = 0, + PIPE_TYPE_MESSAGE = 4, + PIPE_READMODE_BYTE = 0, + PIPE_READMODE_MESSAGE = 2, + PIPE_WAIT = 0, + PIPE_NOWAIT = 1; + +// GetNamedPipeInfo() +enum DWORD + PIPE_CLIENT_END = 0, + PIPE_SERVER_END = 1; + +enum DWORD PIPE_UNLIMITED_INSTANCES = 255; + +// dwCreationFlags for CreateProcess() and CreateProcessAsUser() +enum : DWORD { + DEBUG_PROCESS = 0x00000001, + DEBUG_ONLY_THIS_PROCESS = 0x00000002, + CREATE_SUSPENDED = 0x00000004, + DETACHED_PROCESS = 0x00000008, + CREATE_NEW_CONSOLE = 0x00000010, + NORMAL_PRIORITY_CLASS = 0x00000020, + IDLE_PRIORITY_CLASS = 0x00000040, + HIGH_PRIORITY_CLASS = 0x00000080, + REALTIME_PRIORITY_CLASS = 0x00000100, + CREATE_NEW_PROCESS_GROUP = 0x00000200, + CREATE_UNICODE_ENVIRONMENT = 0x00000400, + CREATE_SEPARATE_WOW_VDM = 0x00000800, + CREATE_SHARED_WOW_VDM = 0x00001000, + CREATE_FORCEDOS = 0x00002000, + BELOW_NORMAL_PRIORITY_CLASS = 0x00004000, + ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000, + CREATE_BREAKAWAY_FROM_JOB = 0x01000000, + CREATE_WITH_USERPROFILE = 0x02000000, + CREATE_DEFAULT_ERROR_MODE = 0x04000000, + CREATE_NO_WINDOW = 0x08000000, + PROFILE_USER = 0x10000000, + PROFILE_KERNEL = 0x20000000, + PROFILE_SERVER = 0x40000000 +} + +enum DWORD CONSOLE_TEXTMODE_BUFFER = 1; + +// CreateFile() +enum : DWORD { + CREATE_NEW = 1, + CREATE_ALWAYS, + OPEN_EXISTING, + OPEN_ALWAYS, + TRUNCATE_EXISTING +} + +// CreateFile() +enum DWORD + FILE_FLAG_WRITE_THROUGH = 0x80000000, + FILE_FLAG_OVERLAPPED = 0x40000000, + FILE_FLAG_NO_BUFFERING = 0x20000000, + FILE_FLAG_RANDOM_ACCESS = 0x10000000, + FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000, + FILE_FLAG_DELETE_ON_CLOSE = 0x04000000, + FILE_FLAG_BACKUP_SEMANTICS = 0x02000000, + FILE_FLAG_POSIX_SEMANTICS = 0x01000000, + FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000, + FILE_FLAG_OPEN_NO_RECALL = 0x00100000; + +static if (_WIN32_WINNT >= 0x500) { +enum DWORD FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000; +} + +// for CreateFile() +enum DWORD + SECURITY_ANONYMOUS = SECURITY_IMPERSONATION_LEVEL.SecurityAnonymous<<16, + SECURITY_IDENTIFICATION = SECURITY_IMPERSONATION_LEVEL.SecurityIdentification<<16, + SECURITY_IMPERSONATION = SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation<<16, + SECURITY_DELEGATION = SECURITY_IMPERSONATION_LEVEL.SecurityDelegation<<16, + SECURITY_CONTEXT_TRACKING = 0x00040000, + SECURITY_EFFECTIVE_ONLY = 0x00080000, + SECURITY_SQOS_PRESENT = 0x00100000, + SECURITY_VALID_SQOS_FLAGS = 0x001F0000; + +// for GetFinalPathNameByHandle() +enum DWORD + VOLUME_NAME_DOS = 0x0, + VOLUME_NAME_GUID = 0x1, + VOLUME_NAME_NT = 0x2, + VOLUME_NAME_NONE = 0x4, + FILE_NAME_NORMALIZED = 0x0, + FILE_NAME_OPENED = 0x8; + +// Thread exit code +enum DWORD STILL_ACTIVE = 0x103; + +/* ??? The only documentation of this seems to be about Windows CE and to + * state what _doesn't_ support it. + */ +enum DWORD FIND_FIRST_EX_CASE_SENSITIVE = 1; + +// GetBinaryType() +enum : DWORD { + SCS_32BIT_BINARY = 0, + SCS_DOS_BINARY, + SCS_WOW_BINARY, + SCS_PIF_BINARY, + SCS_POSIX_BINARY, + SCS_OS216_BINARY +} + +enum size_t + MAX_COMPUTERNAME_LENGTH = 15, + HW_PROFILE_GUIDLEN = 39, + MAX_PROFILE_LEN = 80; + +// HW_PROFILE_INFO +enum DWORD + DOCKINFO_UNDOCKED = 1, + DOCKINFO_DOCKED = 2, + DOCKINFO_USER_SUPPLIED = 4, + DOCKINFO_USER_UNDOCKED = DOCKINFO_USER_SUPPLIED | DOCKINFO_UNDOCKED, + DOCKINFO_USER_DOCKED = DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED; + +// DriveType(), RealDriveType() +enum : int { + DRIVE_UNKNOWN = 0, + DRIVE_NO_ROOT_DIR, + DRIVE_REMOVABLE, + DRIVE_FIXED, + DRIVE_REMOTE, + DRIVE_CDROM, + DRIVE_RAMDISK +} + +// GetFileType() +enum : DWORD { + FILE_TYPE_UNKNOWN = 0, + FILE_TYPE_DISK, + FILE_TYPE_CHAR, + FILE_TYPE_PIPE, + FILE_TYPE_REMOTE = 0x8000 +} + +// Get/SetHandleInformation() +enum DWORD + HANDLE_FLAG_INHERIT = 0x01, + HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x02; + +enum : DWORD { + STD_INPUT_HANDLE = 0xFFFFFFF6, + STD_OUTPUT_HANDLE = 0xFFFFFFF5, + STD_ERROR_HANDLE = 0xFFFFFFF4 +} + +@trusted enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) (-1); + +enum : DWORD { + GET_TAPE_MEDIA_INFORMATION = 0, + GET_TAPE_DRIVE_INFORMATION = 1 +} + +enum : DWORD { + SET_TAPE_MEDIA_INFORMATION = 0, + SET_TAPE_DRIVE_INFORMATION = 1 +} + +// SetThreadPriority()/GetThreadPriority() +enum : int { + THREAD_PRIORITY_IDLE = -15, + THREAD_PRIORITY_LOWEST = -2, + THREAD_PRIORITY_BELOW_NORMAL = -1, + THREAD_PRIORITY_NORMAL = 0, + THREAD_PRIORITY_ABOVE_NORMAL = 1, + THREAD_PRIORITY_HIGHEST = 2, + THREAD_PRIORITY_TIME_CRITICAL = 15, + THREAD_PRIORITY_ERROR_RETURN = 2147483647 +} + +enum : DWORD { + TIME_ZONE_ID_UNKNOWN, + TIME_ZONE_ID_STANDARD, + TIME_ZONE_ID_DAYLIGHT, + TIME_ZONE_ID_INVALID = 0xFFFFFFFF +} + +enum DWORD + FS_CASE_SENSITIVE = 1, + FS_CASE_IS_PRESERVED = 2, + FS_UNICODE_STORED_ON_DISK = 4, + FS_PERSISTENT_ACLS = 8, + FS_FILE_COMPRESSION = 16, + FS_VOL_IS_COMPRESSED = 32768; + +// Flags for GlobalAlloc +enum UINT + GMEM_FIXED = 0, + GMEM_MOVEABLE = 0x0002, + GMEM_ZEROINIT = 0x0040, + GPTR = 0x0040, + GHND = 0x0042, + GMEM_MODIFY = 0x0080, // used only for GlobalRealloc + GMEM_VALID_FLAGS = 0x7F72; + +/+ // Obselete flags (Win16 only) + GMEM_NOCOMPACT=16; + GMEM_NODISCARD=32; + GMEM_DISCARDABLE=256; + GMEM_NOT_BANKED=4096; + GMEM_LOWER=4096; + GMEM_SHARE=8192; + GMEM_DDESHARE=8192; + + GMEM_LOCKCOUNT=255; + +// for GlobalFlags() + GMEM_DISCARDED = 16384; + GMEM_INVALID_HANDLE = 32768; + + GMEM_NOTIFY = 16384; ++/ + +enum UINT + LMEM_FIXED = 0, + LMEM_MOVEABLE = 0x0002, + LMEM_NONZEROLPTR = 0, + NONZEROLPTR = 0, + LMEM_NONZEROLHND = 0x0002, + NONZEROLHND = 0x0002, + LMEM_DISCARDABLE = 0x0F00, + LMEM_NOCOMPACT = 0x0010, + LMEM_NODISCARD = 0x0020, + LMEM_ZEROINIT = 0x0040, + LPTR = 0x0040, + LHND = 0x0042, + LMEM_MODIFY = 0x0080, + LMEM_LOCKCOUNT = 0x00FF, + LMEM_DISCARDED = 0x4000, + LMEM_INVALID_HANDLE = 0x8000; + + + +// used in EXCEPTION_RECORD +enum : DWORD { + STATUS_WAIT_0 = 0, + STATUS_ABANDONED_WAIT_0 = 0x00000080, + STATUS_USER_APC = 0x000000C0, + STATUS_TIMEOUT = 0x00000102, + STATUS_PENDING = 0x00000103, + + STATUS_SEGMENT_NOTIFICATION = 0x40000005, + STATUS_GUARD_PAGE_VIOLATION = 0x80000001, + STATUS_DATATYPE_MISALIGNMENT = 0x80000002, + STATUS_BREAKPOINT = 0x80000003, + STATUS_SINGLE_STEP = 0x80000004, + + STATUS_ACCESS_VIOLATION = 0xC0000005, + STATUS_IN_PAGE_ERROR = 0xC0000006, + STATUS_INVALID_HANDLE = 0xC0000008, + + STATUS_NO_MEMORY = 0xC0000017, + STATUS_ILLEGAL_INSTRUCTION = 0xC000001D, + STATUS_NONCONTINUABLE_EXCEPTION = 0xC0000025, + STATUS_INVALID_DISPOSITION = 0xC0000026, + STATUS_ARRAY_BOUNDS_EXCEEDED = 0xC000008C, + STATUS_FLOAT_DENORMAL_OPERAND = 0xC000008D, + STATUS_FLOAT_DIVIDE_BY_ZERO = 0xC000008E, + STATUS_FLOAT_INEXACT_RESULT = 0xC000008F, + STATUS_FLOAT_INVALID_OPERATION = 0xC0000090, + STATUS_FLOAT_OVERFLOW = 0xC0000091, + STATUS_FLOAT_STACK_CHECK = 0xC0000092, + STATUS_FLOAT_UNDERFLOW = 0xC0000093, + STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094, + STATUS_INTEGER_OVERFLOW = 0xC0000095, + STATUS_PRIVILEGED_INSTRUCTION = 0xC0000096, + STATUS_STACK_OVERFLOW = 0xC00000FD, + STATUS_CONTROL_C_EXIT = 0xC000013A, + STATUS_DLL_INIT_FAILED = 0xC0000142, + STATUS_DLL_INIT_FAILED_LOGOFF = 0xC000026B, + + CONTROL_C_EXIT = STATUS_CONTROL_C_EXIT, + + EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION, + EXCEPTION_DATATYPE_MISALIGNMENT = STATUS_DATATYPE_MISALIGNMENT, + EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT, + EXCEPTION_SINGLE_STEP = STATUS_SINGLE_STEP, + EXCEPTION_ARRAY_BOUNDS_EXCEEDED = STATUS_ARRAY_BOUNDS_EXCEEDED, + EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND, + EXCEPTION_FLT_DIVIDE_BY_ZERO = STATUS_FLOAT_DIVIDE_BY_ZERO, + EXCEPTION_FLT_INEXACT_RESULT = STATUS_FLOAT_INEXACT_RESULT, + EXCEPTION_FLT_INVALID_OPERATION = STATUS_FLOAT_INVALID_OPERATION, + EXCEPTION_FLT_OVERFLOW = STATUS_FLOAT_OVERFLOW, + EXCEPTION_FLT_STACK_CHECK = STATUS_FLOAT_STACK_CHECK, + EXCEPTION_FLT_UNDERFLOW = STATUS_FLOAT_UNDERFLOW, + EXCEPTION_INT_DIVIDE_BY_ZERO = STATUS_INTEGER_DIVIDE_BY_ZERO, + EXCEPTION_INT_OVERFLOW = STATUS_INTEGER_OVERFLOW, + EXCEPTION_PRIV_INSTRUCTION = STATUS_PRIVILEGED_INSTRUCTION, + EXCEPTION_IN_PAGE_ERROR = STATUS_IN_PAGE_ERROR, + EXCEPTION_ILLEGAL_INSTRUCTION = STATUS_ILLEGAL_INSTRUCTION, + EXCEPTION_NONCONTINUABLE_EXCEPTION = STATUS_NONCONTINUABLE_EXCEPTION, + EXCEPTION_STACK_OVERFLOW = STATUS_STACK_OVERFLOW, + EXCEPTION_INVALID_DISPOSITION = STATUS_INVALID_DISPOSITION, + EXCEPTION_GUARD_PAGE = STATUS_GUARD_PAGE_VIOLATION, + EXCEPTION_INVALID_HANDLE = STATUS_INVALID_HANDLE +} + +// for PROCESS_HEAP_ENTRY +enum WORD + PROCESS_HEAP_REGION = 1, + PROCESS_HEAP_UNCOMMITTED_RANGE = 2, + PROCESS_HEAP_ENTRY_BUSY = 4, + PROCESS_HEAP_ENTRY_MOVEABLE = 16, + PROCESS_HEAP_ENTRY_DDESHARE = 32; + +// for LoadLibraryEx() +enum DWORD + DONT_RESOLVE_DLL_REFERENCES = 0x01, // not for WinME and earlier + LOAD_LIBRARY_AS_DATAFILE = 0x02, + LOAD_WITH_ALTERED_SEARCH_PATH = 0x08, + LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x10; // only for XP and later + +// for LockFile() +enum DWORD + LOCKFILE_FAIL_IMMEDIATELY = 1, + LOCKFILE_EXCLUSIVE_LOCK = 2; + +enum MAXIMUM_WAIT_OBJECTS = 64; +enum MAXIMUM_SUSPEND_COUNT = 0x7F; + +enum WAIT_OBJECT_0 = 0; +enum WAIT_ABANDONED_0 = 128; + +//const WAIT_TIMEOUT=258; // also in winerror.h + +enum : DWORD { + WAIT_IO_COMPLETION = 0x000000C0, + WAIT_ABANDONED = 0x00000080, + WAIT_FAILED = 0xFFFFFFFF +} + +// PurgeComm() +enum DWORD + PURGE_TXABORT = 1, + PURGE_RXABORT = 2, + PURGE_TXCLEAR = 4, + PURGE_RXCLEAR = 8; + +// ReadEventLog() +enum DWORD + EVENTLOG_SEQUENTIAL_READ = 1, + EVENTLOG_SEEK_READ = 2, + EVENTLOG_FORWARDS_READ = 4, + EVENTLOG_BACKWARDS_READ = 8; + +// ReportEvent() +enum : WORD { + EVENTLOG_SUCCESS = 0, + EVENTLOG_ERROR_TYPE = 1, + EVENTLOG_WARNING_TYPE = 2, + EVENTLOG_INFORMATION_TYPE = 4, + EVENTLOG_AUDIT_SUCCESS = 8, + EVENTLOG_AUDIT_FAILURE = 16 +} + +// FormatMessage() +enum DWORD + FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x0100, + FORMAT_MESSAGE_IGNORE_INSERTS = 0x0200, + FORMAT_MESSAGE_FROM_STRING = 0x0400, + FORMAT_MESSAGE_FROM_HMODULE = 0x0800, + FORMAT_MESSAGE_FROM_SYSTEM = 0x1000, + FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000; + +enum DWORD FORMAT_MESSAGE_MAX_WIDTH_MASK = 255; + +// also in ddk/ntapi.h +// To restore default error mode, call SetErrorMode(0) +enum { + SEM_FAILCRITICALERRORS = 0x0001, + SEM_NOGPFAULTERRORBOX = 0x0002, + SEM_NOALIGNMENTFAULTEXCEPT = 0x0004, + SEM_NOOPENFILEERRORBOX = 0x8000 +} +// end ntapi.h + +enum { + SLE_ERROR = 1, + SLE_MINORERROR, + SLE_WARNING +} + +enum SHUTDOWN_NORETRY = 1; + +// Return type for exception filters. +enum : LONG { + EXCEPTION_EXECUTE_HANDLER = 1, + EXCEPTION_CONTINUE_EXECUTION = -1, + EXCEPTION_CONTINUE_SEARCH = 0 +} + +enum : ATOM { + MAXINTATOM = 0xC000, + INVALID_ATOM = 0 +} + +enum IGNORE = 0; +enum INFINITE = 0xFFFFFFFF; + +// EscapeCommFunction() +enum { + SETXOFF = 1, + SETXON, + SETRTS, + CLRRTS, + SETDTR, + CLRDTR, // = 6 + SETBREAK = 8, + CLRBREAK = 9 +} + + +// for SetCommMask() +enum DWORD + EV_RXCHAR = 0x0001, + EV_RXFLAG = 0x0002, + EV_TXEMPTY = 0x0004, + EV_CTS = 0x0008, + EV_DSR = 0x0010, + EV_RLSD = 0x0020, + EV_BREAK = 0x0040, + EV_ERR = 0x0080, + EV_RING = 0x0100, + EV_PERR = 0x0200, + EV_RX80FULL = 0x0400, + EV_EVENT1 = 0x0800, + EV_EVENT2 = 0x1000; + +// GetCommModemStatus() +enum DWORD + MS_CTS_ON = 0x0010, + MS_DSR_ON = 0x0020, + MS_RING_ON = 0x0040, + MS_RLSD_ON = 0x0080; + + +// DCB +enum : BYTE { + NOPARITY = 0, + ODDPARITY, + EVENPARITY, + MARKPARITY, + SPACEPARITY +} +// DCB +enum : BYTE { + ONESTOPBIT = 0, + ONE5STOPBITS, + TWOSTOPBITS +} +// DCB +enum : DWORD { + CBR_110 = 110, + CBR_300 = 300, + CBR_600 = 600, + CBR_1200 = 1200, + CBR_2400 = 2400, + CBR_4800 = 4800, + CBR_9600 = 9600, + CBR_14400 = 14400, + CBR_19200 = 19200, + CBR_38400 = 38400, + CBR_56000 = 56000, + CBR_57600 = 57600, + CBR_115200 = 115200, + CBR_128000 = 128000, + CBR_256000 = 256000 +} +// DCB, 2-bit bitfield +enum { + DTR_CONTROL_DISABLE = 0, + DTR_CONTROL_ENABLE, + DTR_CONTROL_HANDSHAKE +} + +// DCB, 2-bit bitfield +enum { + RTS_CONTROL_DISABLE = 0, + RTS_CONTROL_ENABLE, + RTS_CONTROL_HANDSHAKE, + RTS_CONTROL_TOGGLE, +} + +// WIN32_STREAM_ID +enum : DWORD { + BACKUP_INVALID = 0, + BACKUP_DATA, + BACKUP_EA_DATA, + BACKUP_SECURITY_DATA, + BACKUP_ALTERNATE_DATA, + BACKUP_LINK, + BACKUP_PROPERTY_DATA, + BACKUP_OBJECT_ID, + BACKUP_REPARSE_DATA, + BACKUP_SPARSE_BLOCK +} + +// WIN32_STREAM_ID +enum : DWORD { + STREAM_NORMAL_ATTRIBUTE = 0, + STREAM_MODIFIED_WHEN_READ = 1, + STREAM_CONTAINS_SECURITY = 2, + STREAM_CONTAINS_PROPERTIES = 4 +} + +// STARTUPINFO +enum DWORD + STARTF_USESHOWWINDOW = 0x0001, + STARTF_USESIZE = 0x0002, + STARTF_USEPOSITION = 0x0004, + STARTF_USECOUNTCHARS = 0x0008, + STARTF_USEFILLATTRIBUTE = 0x0010, + STARTF_RUNFULLSCREEN = 0x0020, + STARTF_FORCEONFEEDBACK = 0x0040, + STARTF_FORCEOFFFEEDBACK = 0x0080, + STARTF_USESTDHANDLES = 0x0100, + STARTF_USEHOTKEY = 0x0200; + +// ??? +enum { + TC_NORMAL = 0, + TC_HARDERR = 1, + TC_GP_TRAP = 2, + TC_SIGNAL = 3 +} + +/+ These seem to be Windows CE-specific +enum { + AC_LINE_OFFLINE = 0, + AC_LINE_ONLINE = 1, + AC_LINE_BACKUP_POWER = 2, + AC_LINE_UNKNOWN = 255 +} + +enum { + BATTERY_FLAG_HIGH = 1, + BATTERY_FLAG_LOW = 2, + BATTERY_FLAG_CRITICAL = 4, + BATTERY_FLAG_CHARGING = 8, + BATTERY_FLAG_NO_BATTERY = 128, + BATTERY_FLAG_UNKNOWN = 255, + BATTERY_PERCENTAGE_UNKNOWN = 255, + BATTERY_LIFE_UNKNOWN = 0xFFFFFFFF +} ++/ + +// ??? +enum HINSTANCE_ERROR = 32; + +// returned from GetFileSize() +enum DWORD INVALID_FILE_SIZE = 0xFFFFFFFF; + +enum DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFF; + +// GetWriteWatch() +enum DWORD WRITE_WATCH_FLAG_RESET = 1; + +// for LogonUser() +enum : DWORD { + LOGON32_LOGON_INTERACTIVE = 2, + LOGON32_LOGON_NETWORK = 3, + LOGON32_LOGON_BATCH = 4, + LOGON32_LOGON_SERVICE = 5, + LOGON32_LOGON_UNLOCK = 7 +} + +// for LogonUser() +enum : DWORD { + LOGON32_PROVIDER_DEFAULT, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 +} + +// for MoveFileEx() +enum DWORD + MOVEFILE_REPLACE_EXISTING = 1, + MOVEFILE_COPY_ALLOWED = 2, + MOVEFILE_DELAY_UNTIL_REBOOT = 4, + MOVEFILE_WRITE_THROUGH = 8; + +// DefineDosDevice() +enum DWORD + DDD_RAW_TARGET_PATH = 1, + DDD_REMOVE_DEFINITION = 2, + DDD_EXACT_MATCH_ON_REMOVE = 4; + +static if (_WIN32_WINNT >= 0x500) { + enum : DWORD { + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + LOGON32_LOGON_NEW_CREDENTIALS = 9 + } + + // ReplaceFile() +enum DWORD + REPLACEFILE_WRITE_THROUGH = 1, + REPLACEFILE_IGNORE_MERGE_ERRORS = 2; +} + +static if (_WIN32_WINNT >= 0x501) { +enum DWORD + GET_MODULE_HANDLE_EX_FLAG_PIN = 1, + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2, + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4; + + // for ACTCTX +enum DWORD + ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID = 0x01, + ACTCTX_FLAG_LANGID_VALID = 0x02, + ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x04, + ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x08, + ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x10, + ACTCTX_FLAG_APPLICATION_NAME_VALID = 0x20, + ACTCTX_FLAG_HMODULE_VALID = 0x80; + + // DeactivateActCtx() +enum DWORD DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION = 1; + // FindActCtxSectionString() +enum DWORD FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX = 1; + // QueryActCtxW() +enum DWORD + QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX = 0x04, + QUERY_ACTCTX_FLAG_ACTCTX_IS_HMODULE = 0x08, + QUERY_ACTCTX_FLAG_ACTCTX_IS_ADDRESS = 0x10; + + enum { + LOGON_WITH_PROFILE = 1, + LOGON_NETCREDENTIALS_ONLY + } +} + +// ---- + +struct FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} +alias FILETIME* PFILETIME, LPFILETIME; + +struct BY_HANDLE_FILE_INFORMATION { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD dwVolumeSerialNumber; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD nNumberOfLinks; + DWORD nFileIndexHigh; + DWORD nFileIndexLow; +} +alias BY_HANDLE_FILE_INFORMATION* LPBY_HANDLE_FILE_INFORMATION; + +struct DCB { + DWORD DCBlength = DCB.sizeof; + DWORD BaudRate; +/+ + DWORD fBinary:1; // Binary Mode (skip EOF check) + DWORD fParity:1; // Enable parity checking + DWORD fOutxCtsFlow:1; // CTS handshaking on output + DWORD fOutxDsrFlow:1; // DSR handshaking on output + DWORD fDtrControl:2; // DTR Flow control + DWORD fDsrSensitivity:1; // DSR Sensitivity + DWORD fTXContinueOnXoff:1; // Continue TX when Xoff sent + DWORD fOutX:1; // Enable output X-ON/X-OFF + DWORD fInX:1; // Enable input X-ON/X-OFF + DWORD fErrorChar:1; // Enable Err Replacement + DWORD fNull:1; // Enable Null stripping + DWORD fRtsControl:2; // Rts Flow control + DWORD fAbortOnError:1; // Abort all reads and writes on Error + DWORD fDummy2:17; // Reserved ++/ + uint _bf; + bool fBinary(bool f) { _bf = (_bf & ~0x0001) | f; return f; } + bool fParity(bool f) { _bf = (_bf & ~0x0002) | (f<<1); return f; } + bool fOutxCtsFlow(bool f) { _bf = (_bf & ~0x0004) | (f<<2); return f; } + bool fOutxDsrFlow(bool f) { _bf = (_bf & ~0x0008) | (f<<3); return f; } + byte fDtrControl(byte x) { _bf = (_bf & ~0x0030) | (x<<4); return cast(byte)(x & 3); } + bool fDsrSensitivity(bool f) { _bf = (_bf & ~0x0040) | (f<<6); return f; } + bool fTXContinueOnXoff(bool f) { _bf = (_bf & ~0x0080) | (f<<7); return f; } + bool fOutX(bool f) { _bf = (_bf & ~0x0100) | (f<<8); return f; } + bool fInX(bool f) { _bf = (_bf & ~0x0200) | (f<<9); return f; } + bool fErrorChar(bool f) { _bf = (_bf & ~0x0400) | (f<<10); return f; } + bool fNull(bool f) { _bf = (_bf & ~0x0800) | (f<<11); return f; } + byte fRtsControl(byte x) { _bf = (_bf & ~0x3000) | (x<<12); return cast(byte)(x & 3); } + bool fAbortOnError(bool f) { _bf = (_bf & ~0x4000) | (f<<14); return f; } + + bool fBinary() { return cast(bool) (_bf & 1); } + bool fParity() { return cast(bool) (_bf & 2); } + bool fOutxCtsFlow() { return cast(bool) (_bf & 4); } + bool fOutxDsrFlow() { return cast(bool) (_bf & 8); } + byte fDtrControl() { return cast(byte) ((_bf & (32+16))>>4); } + bool fDsrSensitivity() { return cast(bool) (_bf & 64); } + bool fTXContinueOnXoff()() { return cast(bool) (_bf & 128); } + bool fOutX() { return cast(bool) (_bf & 256); } + bool fInX() { return cast(bool) (_bf & 512); } + bool fErrorChar() { return cast(bool) (_bf & 1024); } + bool fNull() { return cast(bool) (_bf & 2048); } + byte fRtsControl() { return cast(byte) ((_bf & (4096+8192))>>12); } + bool fAbortOnError() { return cast(bool) (_bf & 16384); } + + WORD wReserved; + WORD XonLim; + WORD XoffLim; + BYTE ByteSize; + BYTE Parity; + BYTE StopBits; + char XonChar = 0; + char XoffChar = 0; + char ErrorChar = 0; + char EofChar = 0; + char EvtChar = 0; + WORD wReserved1; +} +alias DCB* LPDCB; + +struct COMMCONFIG { + DWORD dwSize = COMMCONFIG.sizeof; + WORD wVersion; + WORD wReserved; + DCB dcb; + DWORD dwProviderSubType; + DWORD dwProviderOffset; + DWORD dwProviderSize; + WCHAR _wcProviderData = 0; + + WCHAR* wcProviderData() return { return &_wcProviderData; } +} +alias COMMCONFIG* LPCOMMCONFIG; + +struct COMMTIMEOUTS { + DWORD ReadIntervalTimeout; + DWORD ReadTotalTimeoutMultiplier; + DWORD ReadTotalTimeoutConstant; + DWORD WriteTotalTimeoutMultiplier; + DWORD WriteTotalTimeoutConstant; +} +alias COMMTIMEOUTS* LPCOMMTIMEOUTS; + +struct COMSTAT { +/+ + DWORD fCtsHold:1; + DWORD fDsrHold:1; + DWORD fRlsdHold:1; + DWORD fXoffHold:1; + DWORD fXoffSent:1; + DWORD fEof:1; + DWORD fTxim:1; + DWORD fReserved:25; ++/ + DWORD _bf; + bool fCtsHold(bool f) { _bf = (_bf & ~1) | f; return f; } + bool fDsrHold(bool f) { _bf = (_bf & ~2) | (f<<1); return f; } + bool fRlsdHold(bool f) { _bf = (_bf & ~4) | (f<<2); return f; } + bool fXoffHold(bool f) { _bf = (_bf & ~8) | (f<<3); return f; } + bool fXoffSent(bool f) { _bf = (_bf & ~16) | (f<<4); return f; } + bool fEof(bool f) { _bf = (_bf & ~32) | (f<<5); return f; } + bool fTxim(bool f) { _bf = (_bf & ~64) | (f<<6); return f; } + + bool fCtsHold() { return cast(bool) (_bf & 1); } + bool fDsrHold() { return cast(bool) (_bf & 2); } + bool fRlsdHold()() { return cast(bool) (_bf & 4); } + bool fXoffHold()() { return cast(bool) (_bf & 8); } + bool fXoffSent()() { return cast(bool) (_bf & 16); } + bool fEof() { return cast(bool) (_bf & 32); } + bool fTxim() { return cast(bool) (_bf & 64); } + + DWORD cbInQue; + DWORD cbOutQue; +} +alias COMSTAT* LPCOMSTAT; + +struct CREATE_PROCESS_DEBUG_INFO { + HANDLE hFile; + HANDLE hProcess; + HANDLE hThread; + LPVOID lpBaseOfImage; + DWORD dwDebugInfoFileOffset; + DWORD nDebugInfoSize; + LPVOID lpThreadLocalBase; + LPTHREAD_START_ROUTINE lpStartAddress; + LPVOID lpImageName; + WORD fUnicode; +} +alias CREATE_PROCESS_DEBUG_INFO* LPCREATE_PROCESS_DEBUG_INFO; + +struct CREATE_THREAD_DEBUG_INFO { + HANDLE hThread; + LPVOID lpThreadLocalBase; + LPTHREAD_START_ROUTINE lpStartAddress; +} +alias CREATE_THREAD_DEBUG_INFO* LPCREATE_THREAD_DEBUG_INFO; + +struct EXCEPTION_DEBUG_INFO { + EXCEPTION_RECORD ExceptionRecord; + DWORD dwFirstChance; +} +alias EXCEPTION_DEBUG_INFO* LPEXCEPTION_DEBUG_INFO; + +struct EXIT_THREAD_DEBUG_INFO { + DWORD dwExitCode; +} +alias EXIT_THREAD_DEBUG_INFO* LPEXIT_THREAD_DEBUG_INFO; + +struct EXIT_PROCESS_DEBUG_INFO { + DWORD dwExitCode; +} +alias EXIT_PROCESS_DEBUG_INFO* LPEXIT_PROCESS_DEBUG_INFO; + +struct LOAD_DLL_DEBUG_INFO { + HANDLE hFile; + LPVOID lpBaseOfDll; + DWORD dwDebugInfoFileOffset; + DWORD nDebugInfoSize; + LPVOID lpImageName; + WORD fUnicode; +} +alias LOAD_DLL_DEBUG_INFO* LPLOAD_DLL_DEBUG_INFO; + +struct UNLOAD_DLL_DEBUG_INFO { + LPVOID lpBaseOfDll; +} +alias UNLOAD_DLL_DEBUG_INFO* LPUNLOAD_DLL_DEBUG_INFO; + +struct OUTPUT_DEBUG_STRING_INFO { + LPSTR lpDebugStringData; + WORD fUnicode; + WORD nDebugStringLength; +} +alias OUTPUT_DEBUG_STRING_INFO* LPOUTPUT_DEBUG_STRING_INFO; + +struct RIP_INFO { + DWORD dwError; + DWORD dwType; +} +alias RIP_INFO* LPRIP_INFO; + +struct DEBUG_EVENT { + DWORD dwDebugEventCode; + DWORD dwProcessId; + DWORD dwThreadId; + union { + EXCEPTION_DEBUG_INFO Exception; + CREATE_THREAD_DEBUG_INFO CreateThread; + CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; + EXIT_THREAD_DEBUG_INFO ExitThread; + EXIT_PROCESS_DEBUG_INFO ExitProcess; + LOAD_DLL_DEBUG_INFO LoadDll; + UNLOAD_DLL_DEBUG_INFO UnloadDll; + OUTPUT_DEBUG_STRING_INFO DebugString; + RIP_INFO RipInfo; + } +} +alias DEBUG_EVENT* LPDEBUG_EVENT; + +struct OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + } + PVOID Pointer; + } + HANDLE hEvent; +} +alias OVERLAPPED* POVERLAPPED, LPOVERLAPPED; + +struct STARTUPINFOA { + DWORD cb = STARTUPINFOA.sizeof; + LPSTR lpReserved; + LPSTR lpDesktop; + LPSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + PBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} +alias STARTUPINFOA* LPSTARTUPINFOA; + +struct STARTUPINFOW { + DWORD cb = STARTUPINFOW.sizeof; + LPWSTR lpReserved; + LPWSTR lpDesktop; + LPWSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + PBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} +alias STARTUPINFOW STARTUPINFO_W; +alias STARTUPINFOW* LPSTARTUPINFOW, LPSTARTUPINFO_W; + +struct PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +} +alias PROCESS_INFORMATION* PPROCESS_INFORMATION, LPPROCESS_INFORMATION; + +/* +struct CRITICAL_SECTION_DEBUG { + WORD Type; + WORD CreatorBackTraceIndex; + CRITICAL_SECTION* CriticalSection; + LIST_ENTRY ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD[2] Spare; +} +alias CRITICAL_SECTION_DEBUG* PCRITICAL_SECTION_DEBUG; + +struct CRITICAL_SECTION { + PCRITICAL_SECTION_DEBUG DebugInfo; + LONG LockCount; + LONG RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; + DWORD SpinCount; +} +alias CRITICAL_SECTION* PCRITICAL_SECTION, LPCRITICAL_SECTION; +*/ + +alias CRITICAL_SECTION_DEBUG = RTL_CRITICAL_SECTION_DEBUG; +alias CRITICAL_SECTION_DEBUG* PCRITICAL_SECTION_DEBUG; + +alias CRITICAL_SECTION = RTL_CRITICAL_SECTION; +alias CRITICAL_SECTION* PCRITICAL_SECTION, LPCRITICAL_SECTION; + +struct SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} +alias SYSTEMTIME* LPSYSTEMTIME; + +struct WIN32_FILE_ATTRIBUTE_DATA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +} +alias WIN32_FILE_ATTRIBUTE_DATA* LPWIN32_FILE_ATTRIBUTE_DATA; + +struct WIN32_FIND_DATAA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +// #ifdef _WIN32_WCE +// DWORD dwOID; +// #else + DWORD dwReserved0; + DWORD dwReserved1; +// #endif + CHAR[MAX_PATH] cFileName = 0; +// #ifndef _WIN32_WCE + CHAR[14] cAlternateFileName = 0; +// #endif +} +alias WIN32_FIND_DATAA* PWIN32_FIND_DATAA, LPWIN32_FIND_DATAA; + +struct WIN32_FIND_DATAW { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +// #ifdef _WIN32_WCE +// DWORD dwOID; +// #else + DWORD dwReserved0; + DWORD dwReserved1; +// #endif + WCHAR[MAX_PATH] cFileName = 0; +// #ifndef _WIN32_WCE + WCHAR[14] cAlternateFileName = 0; +// #endif +} +alias WIN32_FIND_DATAW* PWIN32_FIND_DATAW, LPWIN32_FIND_DATAW; + +struct WIN32_STREAM_ID { + DWORD dwStreamId; + DWORD dwStreamAttributes; + LARGE_INTEGER Size; + DWORD dwStreamNameSize; + WCHAR _cStreamName = 0; + + WCHAR* cStreamName() return { return &_cStreamName; } +} +alias WIN32_STREAM_ID* LPWIN32_STREAM_ID; + +static if (_WIN32_WINNT >= 0x601) { + enum FINDEX_INFO_LEVELS { + FindExInfoStandard, + FindExInfoBasic, + FindExInfoMaxInfoLevel, + } +} else { + enum FINDEX_INFO_LEVELS { + FindExInfoStandard, + FindExInfoMaxInfoLevel, + } +} + +enum FINDEX_SEARCH_OPS { + FindExSearchNameMatch, + FindExSearchLimitToDirectories, + FindExSearchLimitToDevices, + FindExSearchMaxSearchOp +} + +enum ACL_INFORMATION_CLASS { + AclRevisionInformation = 1, + AclSizeInformation +} + +struct HW_PROFILE_INFOA { + DWORD dwDockInfo; + CHAR[HW_PROFILE_GUIDLEN] szHwProfileGuid = 0; + CHAR[MAX_PROFILE_LEN] szHwProfileName = 0; +} +alias HW_PROFILE_INFOA* LPHW_PROFILE_INFOA; + +struct HW_PROFILE_INFOW { + DWORD dwDockInfo; + WCHAR[HW_PROFILE_GUIDLEN] szHwProfileGuid = 0; + WCHAR[MAX_PROFILE_LEN] szHwProfileName = 0; +} +alias HW_PROFILE_INFOW* LPHW_PROFILE_INFOW; + +/* ??? MSDN documents this only for Windows CE/Mobile, but it's used by + * GetFileAttributesEx, which is in desktop Windows. + */ +enum GET_FILEEX_INFO_LEVELS { + GetFileExInfoStandard, + GetFileExMaxInfoLevel +} + +import urt.internal.sys.windows.sdkddkver : NTDDI_VERSION, NTDDI_LONGHORN; + +static if (NTDDI_VERSION >= NTDDI_LONGHORN) +{ + enum FILE_INFO_BY_HANDLE_CLASS + { + FileBasicInfo, + FileStandardInfo, + FileNameInfo, + FileRenameInfo, + FileDispositionInfo, + FileAllocationInfo, + FileEndOfFileInfo, + FileStreamInfo, + FileCompressionInfo, + FileAttributeTagInfo, + FileIdBothDirectoryInfo, + FileIdBothDirectoryRestartInfo, + FileIoPriorityHintInfo, + FileRemoteProtocolInfo, + FileFullDirectoryInfo, + FileFullDirectoryRestartInfo, +// static if (NTDDI_VERSION >= NTDDI_WIN8) +// { + FileStorageInfo, + FileAlignmentInfo, + FileIdInfo, + FileIdExtdDirectoryInfo, + FileIdExtdDirectoryRestartInfo, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_RS1) +// { + FileDispositionInfoEx, + FileRenameInfoEx, +// } +// static if (NTDDI_VERSION >= NTDDI_WIN10_19H1) +// { + FileCaseSensitiveInfo, + FileNormalizedNameInfo, +// } + MaximumFileInfoByHandleClass + } + alias PFILE_INFO_BY_HANDLE_CLASS = FILE_INFO_BY_HANDLE_CLASS*; +} + +struct SYSTEM_INFO { + union { + DWORD dwOemId; + struct { + WORD wProcessorArchitecture; + WORD wReserved; + } + } + DWORD dwPageSize; + PVOID lpMinimumApplicationAddress; + PVOID lpMaximumApplicationAddress; + DWORD_PTR dwActiveProcessorMask; + DWORD dwNumberOfProcessors; + DWORD dwProcessorType; + DWORD dwAllocationGranularity; + WORD wProcessorLevel; + WORD wProcessorRevision; +} +alias SYSTEM_INFO* LPSYSTEM_INFO; + +static if (_WIN32_WINNT >= 0x500) { + struct SYSTEM_POWER_STATUS { + BYTE ACLineStatus; + BYTE BatteryFlag; + BYTE BatteryLifePercent; + BYTE Reserved1; + DWORD BatteryLifeTime; + DWORD BatteryFullLifeTime; + } + alias SYSTEM_POWER_STATUS* LPSYSTEM_POWER_STATUS; +} + +struct TIME_ZONE_INFORMATION { + LONG Bias; + WCHAR[32] StandardName = 0; + SYSTEMTIME StandardDate; + LONG StandardBias; + WCHAR[32] DaylightName = 0; + SYSTEMTIME DaylightDate; + LONG DaylightBias; +} +alias TIME_ZONE_INFORMATION* LPTIME_ZONE_INFORMATION; + +// Does not exist in Windows headers, only MSDN +// documentation (for TIME_ZONE_INFORMATION). +// Provided solely for compatibility with the old +// urt.internal.sys.windows.windows +struct REG_TZI_FORMAT { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +} + +// MSDN documents this, possibly erroneously, as Win2000+. +struct MEMORYSTATUS { + DWORD dwLength; + DWORD dwMemoryLoad; + SIZE_T dwTotalPhys; + SIZE_T dwAvailPhys; + SIZE_T dwTotalPageFile; + SIZE_T dwAvailPageFile; + SIZE_T dwTotalVirtual; + SIZE_T dwAvailVirtual; +} +alias MEMORYSTATUS* LPMEMORYSTATUS; + +static if (_WIN32_WINNT >= 0x500) { + struct MEMORYSTATUSEX { + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; + } + alias MEMORYSTATUSEX* LPMEMORYSTATUSEX; +} + +struct LDT_ENTRY { + WORD LimitLow; + WORD BaseLow; + struct { + BYTE BaseMid; + BYTE Flags1; + BYTE Flags2; + BYTE BaseHi; + + byte Type(byte f) { Flags1 = cast(BYTE) ((Flags1 & 0xE0) | f); return cast(byte)(f & 0x1F); } + byte Dpl(byte f) { Flags1 = cast(BYTE) ((Flags1 & 0x9F) | (f<<5)); return cast(byte)(f & 3); } + bool Pres(bool f) { Flags1 = cast(BYTE) ((Flags1 & 0x7F) | (f<<7)); return f; } + + byte LimitHi(byte f) { Flags2 = cast(BYTE) ((Flags2 & 0xF0) | (f&0x0F)); return cast(byte)(f & 0x0F); } + bool Sys(bool f) { Flags2 = cast(BYTE) ((Flags2 & 0xEF) | (f<<4)); return f; } + // Next bit is reserved + bool Default_Big(bool f) { Flags2 = cast(BYTE) ((Flags2 & 0xBF) | (f<<6)); return f; } + bool Granularity(bool f) { Flags2 = cast(BYTE) ((Flags2 & 0x7F) | (f<<7)); return f; } + + byte Type() { return cast(byte) (Flags1 & 0x1F); } + byte Dpl() { return cast(byte) ((Flags1 & 0x60)>>5); } + bool Pres() { return cast(bool) (Flags1 & 0x80); } + + byte LimitHi() { return cast(byte) (Flags2 & 0x0F); } + bool Sys() { return cast(bool) (Flags2 & 0x10); } + bool Default_Big()() { return cast(bool) (Flags2 & 0x40); } + bool Granularity()() { return cast(bool) (Flags2 & 0x80); } + } +/+ + union HighWord { + struct Bytes { + BYTE BaseMid; + BYTE Flags1; + BYTE Flags2; + BYTE BaseHi; + } + struct Bits { + DWORD BaseMid:8; + DWORD Type:5; + DWORD Dpl:2; + DWORD Pres:1; + DWORD LimitHi:4; + DWORD Sys:1; + DWORD Reserved_0:1; + DWORD Default_Big:1; + DWORD Granularity:1; + DWORD BaseHi:8; + } + } ++/ +} +alias LDT_ENTRY* PLDT_ENTRY, LPLDT_ENTRY; + +/* As with the other memory management functions and structures, MSDN's + * Windows version info shall be taken with a cup of salt. + */ +struct PROCESS_HEAP_ENTRY { + PVOID lpData; + DWORD cbData; + BYTE cbOverhead; + BYTE iRegionIndex; + WORD wFlags; + union { + struct _Block { + HANDLE hMem; + DWORD[3] dwReserved; + } + _Block Block; + struct _Region { + DWORD dwCommittedSize; + DWORD dwUnCommittedSize; + LPVOID lpFirstBlock; + LPVOID lpLastBlock; + } + _Region Region; + } +} +alias PROCESS_HEAP_ENTRY* LPPROCESS_HEAP_ENTRY; + +struct OFSTRUCT { + BYTE cBytes = OFSTRUCT.sizeof; + BYTE fFixedDisk; + WORD nErrCode; + WORD Reserved1; + WORD Reserved2; + CHAR[128] szPathName = 0; // const OFS_MAXPATHNAME = 128; +} +alias OFSTRUCT* LPOFSTRUCT, POFSTRUCT; + +/* ??? MSDN documents this only for Windows CE, but it's used by + * ImageGetCertificateData, which is in desktop Windows. + */ +struct WIN_CERTIFICATE { + DWORD dwLength; + WORD wRevision; + WORD wCertificateType; + BYTE _bCertificate; + + BYTE* bCertificate() return { return &_bCertificate; } +} +alias WIN_CERTIFICATE* LPWIN_CERTIFICATE; + +static if (_WIN32_WINNT >= 0x500) { + enum COMPUTER_NAME_FORMAT { + ComputerNameNetBIOS, + ComputerNameDnsHostname, + ComputerNameDnsDomain, + ComputerNameDnsFullyQualified, + ComputerNamePhysicalNetBIOS, + ComputerNamePhysicalDnsHostname, + ComputerNamePhysicalDnsDomain, + ComputerNamePhysicalDnsFullyQualified, + ComputerNameMax + } +} + +static if (_WIN32_WINNT >= 0x501) { + struct ACTCTXA { + ULONG cbSize = this.sizeof; + DWORD dwFlags; + LPCSTR lpSource; + USHORT wProcessorArchitecture; + LANGID wLangId; + LPCSTR lpAssemblyDirectory; + LPCSTR lpResourceName; + LPCSTR lpApplicationName; + HMODULE hModule; + } + alias ACTCTXA* PACTCTXA; + alias const(ACTCTXA)* PCACTCTXA; + + struct ACTCTXW { + ULONG cbSize = this.sizeof; + DWORD dwFlags; + LPCWSTR lpSource; + USHORT wProcessorArchitecture; + LANGID wLangId; + LPCWSTR lpAssemblyDirectory; + LPCWSTR lpResourceName; + LPCWSTR lpApplicationName; + HMODULE hModule; + } + alias ACTCTXW* PACTCTXW; + alias const(ACTCTXW)* PCACTCTXW; + + struct ACTCTX_SECTION_KEYED_DATA { + ULONG cbSize = this.sizeof; + ULONG ulDataFormatVersion; + PVOID lpData; + ULONG ulLength; + PVOID lpSectionGlobalData; + ULONG ulSectionGlobalDataLength; + PVOID lpSectionBase; + ULONG ulSectionTotalLength; + HANDLE hActCtx; + HANDLE ulAssemblyRosterIndex; + } + alias ACTCTX_SECTION_KEYED_DATA* PACTCTX_SECTION_KEYED_DATA; + alias const(ACTCTX_SECTION_KEYED_DATA)* PCACTCTX_SECTION_KEYED_DATA; + + enum MEMORY_RESOURCE_NOTIFICATION_TYPE { + LowMemoryResourceNotification, + HighMemoryResourceNotification + } + +} // (_WIN32_WINNT >= 0x501) + +static if (_WIN32_WINNT >= 0x410) { + /* apparently used only by SetThreadExecutionState (Win2000+) + * and DDK functions (version compatibility not established) + */ + alias DWORD EXECUTION_STATE; +} + +// CreateSymbolicLink, GetFileInformationByHandleEx +static if (_WIN32_WINNT >= 0x600) { + enum { + SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1, + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 + } + + struct FILE_BASIC_INFO + { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; + } + alias PFILE_BASIC_INFO = FILE_BASIC_INFO*; + + struct FILE_STANDARD_INFO + { + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + DWORD NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; + } + alias PFILE_STANDARD_INFO = FILE_STANDARD_INFO*; +} + +// Callbacks +extern (Windows) { + alias DWORD function(LPVOID) LPTHREAD_START_ROUTINE; + alias DWORD function(LARGE_INTEGER, LARGE_INTEGER, LARGE_INTEGER, LARGE_INTEGER, + DWORD, DWORD, HANDLE, HANDLE, LPVOID) LPPROGRESS_ROUTINE; + alias void function(PVOID) LPFIBER_START_ROUTINE; + + alias BOOL function(HMODULE, LPCSTR, LPCSTR, WORD, LONG_PTR) ENUMRESLANGPROCA; + alias BOOL function(HMODULE, LPCWSTR, LPCWSTR, WORD, LONG_PTR) ENUMRESLANGPROCW; + alias BOOL function(HMODULE, LPCSTR, LPSTR, LONG_PTR) ENUMRESNAMEPROCA; + alias BOOL function(HMODULE, LPCWSTR, LPWSTR, LONG_PTR) ENUMRESNAMEPROCW; + alias BOOL function(HMODULE, LPSTR, LONG_PTR) ENUMRESTYPEPROCA; + alias BOOL function(HMODULE, LPWSTR, LONG_PTR) ENUMRESTYPEPROCW; + alias void function(DWORD, DWORD, LPOVERLAPPED) LPOVERLAPPED_COMPLETION_ROUTINE; + alias LONG function(LPEXCEPTION_POINTERS) PTOP_LEVEL_EXCEPTION_FILTER; + alias PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER; + + alias void function(ULONG_PTR) PAPCFUNC; + alias void function(PVOID, DWORD, DWORD) PTIMERAPCROUTINE; + + static if (_WIN32_WINNT >= 0x500) { + alias void function(PVOID, BOOLEAN) WAITORTIMERCALLBACK; + } +} + +LPTSTR MAKEINTATOM()(ushort i) { + return cast(LPTSTR) cast(size_t) i; +} + +extern (Windows) nothrow @nogc { + // The following Win16 functions are obselete in Win32. + int _hread(HFILE, LPVOID, int); + int _hwrite(HFILE, LPCSTR, int); + HFILE _lclose(HFILE); + HFILE _lcreat(LPCSTR, int); + LONG _llseek(HFILE, LONG, int); + HFILE _lopen(LPCSTR, int); + UINT _lread(HFILE, LPVOID, UINT); + UINT _lwrite(HFILE, LPCSTR, UINT); + SIZE_T GlobalCompact(DWORD); + VOID GlobalFix(HGLOBAL); + + // MSDN contradicts itself on GlobalFlags: + // "This function is provided only for compatibility with 16-bit versions of Windows." + // but also requires Windows 2000 or above + UINT GlobalFlags(HGLOBAL); + VOID GlobalUnfix(HGLOBAL); + BOOL GlobalUnWire(HGLOBAL); + PVOID GlobalWire(HGLOBAL); + SIZE_T LocalCompact(UINT); + UINT LocalFlags(HLOCAL); + SIZE_T LocalShrink(HLOCAL, UINT); + + /+ + //-------------------------------------- + // These functions are problematic + + version (UseNtoSKernel) {}else { + /* CAREFUL: These are exported from ntoskrnl.exe and declared in winddk.h + as __fastcall functions, but are exported from kernel32.dll as __stdcall */ + static if (_WIN32_WINNT >= 0x501) { + VOID InitializeSListHead(PSLIST_HEADER); + } + LONG InterlockedCompareExchange(LPLONG, LONG, LONG); + // PVOID WINAPI InterlockedCompareExchangePointer(PVOID*, PVOID, PVOID); + (PVOID)InterlockedCompareExchange((LPLONG)(d) (PVOID)InterlockedCompareExchange((LPLONG)(d), (LONG)(e), (LONG)(c)) + LONG InterlockedDecrement(LPLONG); + LONG InterlockedExchange(LPLONG, LONG); + // PVOID WINAPI InterlockedExchangePointer(PVOID*, PVOID); + (PVOID)InterlockedExchange((LPLONG)((PVOID)InterlockedExchange((LPLONG)(t), (LONG)(v)) + LONG InterlockedExchangeAdd(LPLONG, LONG); + + static if (_WIN32_WINNT >= 0x501) { + PSLIST_ENTRY InterlockedFlushSList(PSLIST_HEADER); + } + LONG InterlockedIncrement(LPLONG); + static if (_WIN32_WINNT >= 0x501) { + PSLIST_ENTRY InterlockedPopEntrySList(PSLIST_HEADER); + PSLIST_ENTRY InterlockedPushEntrySList(PSLIST_HEADER, PSLIST_ENTRY); + } + } // #endif // __USE_NTOSKRNL__ + //-------------------------------------- + +/ + + LONG InterlockedIncrement(LPLONG lpAddend); + LONG InterlockedDecrement(LPLONG lpAddend); + LONG InterlockedExchange(LPLONG Target, LONG Value); + LONG InterlockedExchangeAdd(LPLONG Addend, LONG Value); + LONG InterlockedCompareExchange(LONG *Destination, LONG Exchange, LONG Comperand); + + ATOM AddAtomA(LPCSTR); + ATOM AddAtomW(LPCWSTR); + BOOL AreFileApisANSI(); + BOOL Beep(DWORD, DWORD); + HANDLE BeginUpdateResourceA(LPCSTR, BOOL); + HANDLE BeginUpdateResourceW(LPCWSTR, BOOL); + BOOL BuildCommDCBA(LPCSTR, LPDCB); + BOOL BuildCommDCBW(LPCWSTR, LPDCB); + BOOL BuildCommDCBAndTimeoutsA(LPCSTR, LPDCB, LPCOMMTIMEOUTS); + BOOL BuildCommDCBAndTimeoutsW(LPCWSTR, LPDCB, LPCOMMTIMEOUTS); + BOOL CallNamedPipeA(LPCSTR, PVOID, DWORD, PVOID, DWORD, PDWORD, DWORD); + BOOL CallNamedPipeW(LPCWSTR, PVOID, DWORD, PVOID, DWORD, PDWORD, DWORD); + BOOL CancelDeviceWakeupRequest(HANDLE); + BOOL CheckTokenMembership(HANDLE, PSID, PBOOL); + BOOL ClearCommBreak(HANDLE); + BOOL ClearCommError(HANDLE, PDWORD, LPCOMSTAT); + BOOL CloseHandle(HANDLE) @trusted; + BOOL CommConfigDialogA(LPCSTR, HWND, LPCOMMCONFIG); + BOOL CommConfigDialogW(LPCWSTR, HWND, LPCOMMCONFIG); + LONG CompareFileTime(const(FILETIME)*, const(FILETIME)*); + BOOL ContinueDebugEvent(DWORD, DWORD, DWORD); + BOOL CopyFileA(LPCSTR, LPCSTR, BOOL); + BOOL CopyFileW(LPCWSTR, LPCWSTR, BOOL); + BOOL CopyFileExA(LPCSTR, LPCSTR, LPPROGRESS_ROUTINE, LPVOID, LPBOOL, DWORD); + BOOL CopyFileExW(LPCWSTR, LPCWSTR, LPPROGRESS_ROUTINE, LPVOID, LPBOOL, DWORD); + + alias RtlMoveMemory = memmove; + alias RtlCopyMemory = memcpy; + pragma(inline, true) void RtlFillMemory(PVOID Destination, SIZE_T Length, BYTE Fill) pure { memset(Destination, Fill, Length); } + pragma(inline, true) void RtlZeroMemory(PVOID Destination, SIZE_T Length) pure { memset(Destination, 0, Length); } + alias MoveMemory = RtlMoveMemory; + alias CopyMemory = RtlCopyMemory; + alias FillMemory = RtlFillMemory; + alias ZeroMemory = RtlZeroMemory; + + BOOL CreateDirectoryA(LPCSTR, LPSECURITY_ATTRIBUTES); + BOOL CreateDirectoryW(LPCWSTR, LPSECURITY_ATTRIBUTES); + BOOL CreateDirectoryExA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); + BOOL CreateDirectoryExW(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + HANDLE CreateEventA(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR); + HANDLE CreateEventW(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR); + HANDLE CreateFileA(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); + HANDLE CreateFileW(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); + HANDLE CreateIoCompletionPort(HANDLE, HANDLE, ULONG_PTR, DWORD); + HANDLE CreateMailslotA(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES); + HANDLE CreateMailslotW(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES); + HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES, BOOL, LPCSTR); + HANDLE CreateMutexW(LPSECURITY_ATTRIBUTES, BOOL, LPCWSTR); + BOOL CreatePipe(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD); + BOOL CreateProcessA(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, PVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); + BOOL CreateProcessW(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, PVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); + HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES, LONG, LONG, LPCSTR) @trusted; + HANDLE CreateSemaphoreW(LPSECURITY_ATTRIBUTES, LONG, LONG, LPCWSTR) @trusted; + HANDLE CreateThread(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, PVOID, DWORD, PDWORD); + BOOL DebugActiveProcess(DWORD); + void DebugBreak(); + ATOM DeleteAtom(ATOM); + void DeleteCriticalSection(PCRITICAL_SECTION); + BOOL DeleteFileA(LPCSTR); + BOOL DeleteFileW(LPCWSTR); + BOOL DisableThreadLibraryCalls(HMODULE); + BOOL DosDateTimeToFileTime(WORD, WORD, LPFILETIME); + BOOL DuplicateHandle(HANDLE, HANDLE, HANDLE, PHANDLE, DWORD, BOOL, DWORD); + BOOL EndUpdateResourceA(HANDLE, BOOL); + BOOL EndUpdateResourceW(HANDLE, BOOL); + void EnterCriticalSection(LPCRITICAL_SECTION); + void EnterCriticalSection(shared(CRITICAL_SECTION)*); + BOOL EnumResourceLanguagesA(HMODULE, LPCSTR, LPCSTR, ENUMRESLANGPROC, LONG_PTR); + BOOL EnumResourceLanguagesW(HMODULE, LPCWSTR, LPCWSTR, ENUMRESLANGPROC, LONG_PTR); + BOOL EnumResourceNamesA(HMODULE, LPCSTR, ENUMRESNAMEPROC, LONG_PTR); + BOOL EnumResourceNamesW(HMODULE, LPCWSTR, ENUMRESNAMEPROC, LONG_PTR); + BOOL EnumResourceTypesA(HMODULE, ENUMRESTYPEPROC, LONG_PTR); + BOOL EnumResourceTypesW(HMODULE, ENUMRESTYPEPROC, LONG_PTR); + BOOL EscapeCommFunction(HANDLE, DWORD); + void ExitProcess(UINT); // Never returns + void ExitThread(DWORD); // Never returns + DWORD ExpandEnvironmentStringsA(LPCSTR, LPSTR, DWORD); + DWORD ExpandEnvironmentStringsW(LPCWSTR, LPWSTR, DWORD); + void FatalAppExitA(UINT, LPCSTR); + void FatalAppExitW(UINT, LPCWSTR); + void FatalExit(int); + BOOL FileTimeToDosDateTime(const(FILETIME)*, LPWORD, LPWORD); + BOOL FileTimeToLocalFileTime(const(FILETIME)*, LPFILETIME); + BOOL FileTimeToSystemTime(const(FILETIME)*, LPSYSTEMTIME); + ATOM FindAtomA(LPCSTR); + ATOM FindAtomW(LPCWSTR); + BOOL FindClose(HANDLE); + BOOL FindCloseChangeNotification(HANDLE); + HANDLE FindFirstChangeNotificationA(LPCSTR, BOOL, DWORD); + HANDLE FindFirstChangeNotificationW(LPCWSTR, BOOL, DWORD); + HANDLE FindFirstFileA(LPCSTR, LPWIN32_FIND_DATAA); + HANDLE FindFirstFileW(LPCWSTR, LPWIN32_FIND_DATAW); + BOOL FindNextChangeNotification(HANDLE); + BOOL FindNextFileA(HANDLE, LPWIN32_FIND_DATAA); + BOOL FindNextFileW(HANDLE, LPWIN32_FIND_DATAW); + HRSRC FindResourceA(HMODULE, LPCSTR, LPCSTR); + HRSRC FindResourceW(HINSTANCE, LPCWSTR, LPCWSTR); + HRSRC FindResourceExA(HINSTANCE, LPCSTR, LPCSTR, WORD); + HRSRC FindResourceExW(HINSTANCE, LPCWSTR, LPCWSTR, WORD); + BOOL FlushFileBuffers(HANDLE); + BOOL FlushInstructionCache(HANDLE, PCVOID, SIZE_T); + DWORD FormatMessageA(DWORD, PCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); + DWORD FormatMessageW(DWORD, PCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list*); + BOOL FreeEnvironmentStringsA(LPSTR); + BOOL FreeEnvironmentStringsW(LPWSTR); + BOOL FreeLibrary(HMODULE); + void FreeLibraryAndExitThread(HMODULE, DWORD); // never returns + BOOL FreeResource(HGLOBAL); + UINT GetAtomNameA(ATOM, LPSTR, int); + UINT GetAtomNameW(ATOM, LPWSTR, int); + LPSTR GetCommandLineA(); + LPWSTR GetCommandLineW(); + BOOL GetCommConfig(HANDLE, LPCOMMCONFIG, PDWORD); + BOOL GetCommMask(HANDLE, PDWORD); + BOOL GetCommModemStatus(HANDLE, PDWORD); + BOOL GetCommProperties(HANDLE, LPCOMMPROP); + BOOL GetCommState(HANDLE, LPDCB); + BOOL GetCommTimeouts(HANDLE, LPCOMMTIMEOUTS); + BOOL GetComputerNameA(LPSTR, PDWORD); + BOOL GetComputerNameW(LPWSTR, PDWORD); + DWORD GetCurrentDirectoryA(DWORD, LPSTR); + DWORD GetCurrentDirectoryW(DWORD, LPWSTR); + HANDLE GetCurrentProcess(); + DWORD GetCurrentProcessId(); + HANDLE GetCurrentThread(); +/* In MinGW: +#ifdef _WIN32_WCE +extern DWORD GetCurrentThreadId(void); +#else +WINBASEAPI DWORD WINAPI GetCurrentThreadId(void); +#endif +*/ + DWORD GetCurrentThreadId(); + + alias GetTickCount GetCurrentTime; + + BOOL GetDefaultCommConfigA(LPCSTR, LPCOMMCONFIG, PDWORD); + BOOL GetDefaultCommConfigW(LPCWSTR, LPCOMMCONFIG, PDWORD); + BOOL GetDiskFreeSpaceA(LPCSTR, PDWORD, PDWORD, PDWORD, PDWORD); + BOOL GetDiskFreeSpaceW(LPCWSTR, PDWORD, PDWORD, PDWORD, PDWORD); + BOOL GetDiskFreeSpaceExA(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); + BOOL GetDiskFreeSpaceExW(LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); + UINT GetDriveTypeA(LPCSTR); + UINT GetDriveTypeW(LPCWSTR); + LPSTR GetEnvironmentStringsA(); + LPWSTR GetEnvironmentStringsW(); + DWORD GetEnvironmentVariableA(LPCSTR, LPSTR, DWORD); + DWORD GetEnvironmentVariableW(LPCWSTR, LPWSTR, DWORD); + BOOL GetExitCodeProcess(HANDLE, PDWORD); + BOOL GetExitCodeThread(HANDLE, PDWORD); + DWORD GetFileAttributesA(LPCSTR); + DWORD GetFileAttributesW(LPCWSTR); + BOOL GetFileInformationByHandle(HANDLE, LPBY_HANDLE_FILE_INFORMATION); + DWORD GetFileSize(HANDLE, PDWORD); + BOOL GetFileTime(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME); + DWORD GetFileType(HANDLE); + DWORD GetFinalPathNameByHandleA(HANDLE, LPSTR, DWORD, DWORD); + DWORD GetFinalPathNameByHandleW(HANDLE, LPWSTR, DWORD, DWORD); + DWORD GetFullPathNameA(LPCSTR, DWORD, LPSTR, LPSTR*); + DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*); + DWORD GetLastError() @trusted; + void GetLocalTime(LPSYSTEMTIME); + DWORD GetLogicalDrives(); + DWORD GetLogicalDriveStringsA(DWORD, LPSTR); + DWORD GetLogicalDriveStringsW(DWORD, LPWSTR); + BOOL GetMailslotInfo(HANDLE, PDWORD, PDWORD, PDWORD, PDWORD); + DWORD GetModuleFileNameA(HINSTANCE, LPSTR, DWORD); + DWORD GetModuleFileNameW(HINSTANCE, LPWSTR, DWORD); + HMODULE GetModuleHandleA(LPCSTR); + HMODULE GetModuleHandleW(LPCWSTR); + BOOL GetNamedPipeHandleStateA(HANDLE, PDWORD, PDWORD, PDWORD, PDWORD, LPSTR, DWORD); + BOOL GetNamedPipeHandleStateW(HANDLE, PDWORD, PDWORD, PDWORD, PDWORD, LPWSTR, DWORD); + BOOL GetNamedPipeInfo(HANDLE, PDWORD, PDWORD, PDWORD, PDWORD); + BOOL GetOverlappedResult(HANDLE, LPOVERLAPPED, PDWORD, BOOL); + DWORD GetPriorityClass(HANDLE); + UINT GetPrivateProfileIntA(LPCSTR, LPCSTR, INT, LPCSTR); + UINT GetPrivateProfileIntW(LPCWSTR, LPCWSTR, INT, LPCWSTR); + DWORD GetPrivateProfileSectionA(LPCSTR, LPSTR, DWORD, LPCSTR); + DWORD GetPrivateProfileSectionW(LPCWSTR, LPWSTR, DWORD, LPCWSTR); + DWORD GetPrivateProfileSectionNamesA(LPSTR, DWORD, LPCSTR); + DWORD GetPrivateProfileSectionNamesW(LPWSTR, DWORD, LPCWSTR); + DWORD GetPrivateProfileStringA(LPCSTR, LPCSTR, LPCSTR, LPSTR, DWORD, LPCSTR); + DWORD GetPrivateProfileStringW(LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, DWORD, LPCWSTR); + BOOL GetPrivateProfileStructA(LPCSTR, LPCSTR, LPVOID, UINT, LPCSTR); + BOOL GetPrivateProfileStructW(LPCWSTR, LPCWSTR, LPVOID, UINT, LPCWSTR); + FARPROC GetProcAddress(HMODULE, LPCSTR); // 1st param wrongly HINSTANCE in MinGW + BOOL GetProcessAffinityMask(HANDLE, PDWORD_PTR, PDWORD_PTR); + DWORD GetProcessVersion(DWORD); + UINT GetProfileIntA(LPCSTR, LPCSTR, INT); + UINT GetProfileIntW(LPCWSTR, LPCWSTR, INT); + DWORD GetProfileSectionA(LPCSTR, LPSTR, DWORD); + DWORD GetProfileSectionW(LPCWSTR, LPWSTR, DWORD); + DWORD GetProfileStringA(LPCSTR, LPCSTR, LPCSTR, LPSTR, DWORD); + DWORD GetProfileStringW(LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, DWORD); + DWORD GetShortPathNameA(LPCSTR, LPSTR, DWORD); + DWORD GetShortPathNameW(LPCWSTR, LPWSTR, DWORD); + VOID GetStartupInfoA(LPSTARTUPINFOA); + VOID GetStartupInfoW(LPSTARTUPINFOW); + HANDLE GetStdHandle(DWORD); + UINT GetSystemDirectoryA(LPSTR, UINT); + UINT GetSystemDirectoryW(LPWSTR, UINT); + VOID GetSystemInfo(LPSYSTEM_INFO); + VOID GetSystemTime(LPSYSTEMTIME); + BOOL GetSystemTimeAdjustment(PDWORD, PDWORD, PBOOL); + void GetSystemTimeAsFileTime(LPFILETIME); + UINT GetTempFileNameA(LPCSTR, LPCSTR, UINT, LPSTR); + UINT GetTempFileNameW(LPCWSTR, LPCWSTR, UINT, LPWSTR); + DWORD GetTempPathA(DWORD, LPSTR); + DWORD GetTempPathW(DWORD, LPWSTR); + BOOL GetThreadContext(HANDLE, LPCONTEXT); + int GetThreadPriority(HANDLE); + BOOL GetThreadSelectorEntry(HANDLE, DWORD, LPLDT_ENTRY); + DWORD GetTickCount(); + DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION); + BOOL GetUserNameA (LPSTR, PDWORD); + BOOL GetUserNameW(LPWSTR, PDWORD); + DWORD GetVersion(); + BOOL GetVersionExA(LPOSVERSIONINFOA); + BOOL GetVersionExW(LPOSVERSIONINFOW); + BOOL GetVolumeInformationA(LPCSTR, LPSTR, DWORD, PDWORD, PDWORD, PDWORD, LPSTR, DWORD); + BOOL GetVolumeInformationW(LPCWSTR, LPWSTR, DWORD, PDWORD, PDWORD, PDWORD, LPWSTR, DWORD); + UINT GetWindowsDirectoryA(LPSTR, UINT); + UINT GetWindowsDirectoryW(LPWSTR, UINT); + DWORD GetWindowThreadProcessId(HWND, PDWORD); + ATOM GlobalAddAtomA(LPCSTR); + ATOM GlobalAddAtomW(LPCWSTR); + ATOM GlobalDeleteAtom(ATOM); + ATOM GlobalFindAtomA(LPCSTR); + ATOM GlobalFindAtomW(LPCWSTR); + UINT GlobalGetAtomNameA(ATOM, LPSTR, int); + UINT GlobalGetAtomNameW(ATOM, LPWSTR, int); + + bool HasOverlappedIoCompleted(LPOVERLAPPED lpOverlapped) { + return lpOverlapped.Internal != STATUS_PENDING; + } + + BOOL InitAtomTable(DWORD); + VOID InitializeCriticalSection(LPCRITICAL_SECTION) @trusted; + /* ??? The next two are allegedly obsolete and "supported only for + * backward compatibility with the 16-bit Windows API". Yet the + * replacements IsBadReadPtr and IsBadWritePtr are apparently Win2000+ + * only. Where's the mistake? + */ + BOOL IsBadHugeReadPtr(PCVOID, UINT_PTR); + BOOL IsBadHugeWritePtr(PVOID, UINT_PTR); + BOOL IsBadReadPtr(PCVOID, UINT_PTR); + BOOL IsBadStringPtrA(LPCSTR, UINT_PTR); + BOOL IsBadStringPtrW(LPCWSTR, UINT_PTR); + BOOL IsBadWritePtr(PVOID, UINT_PTR); + void LeaveCriticalSection(LPCRITICAL_SECTION); + void LeaveCriticalSection(shared(CRITICAL_SECTION)*); + HINSTANCE LoadLibraryA(LPCSTR); + HINSTANCE LoadLibraryW(LPCWSTR); + HINSTANCE LoadLibraryExA(LPCSTR, HANDLE, DWORD); + HINSTANCE LoadLibraryExW(LPCWSTR, HANDLE, DWORD); + DWORD LoadModule(LPCSTR, PVOID); + HGLOBAL LoadResource(HINSTANCE, HRSRC); + BOOL LocalFileTimeToFileTime(const(FILETIME)*, LPFILETIME); + BOOL LockFile(HANDLE, DWORD, DWORD, DWORD, DWORD); + PVOID LockResource(HGLOBAL); + + LPSTR lstrcatA(LPSTR, LPCSTR); + LPWSTR lstrcatW(LPWSTR, LPCWSTR); + int lstrcmpA(LPCSTR, LPCSTR); + int lstrcmpiA(LPCSTR, LPCSTR); + int lstrcmpiW(LPCWSTR, LPCWSTR); + int lstrcmpW(LPCWSTR, LPCWSTR); + LPSTR lstrcpyA(LPSTR, LPCSTR); + LPSTR lstrcpynA(LPSTR, LPCSTR, int); + LPWSTR lstrcpynW(LPWSTR, LPCWSTR, int); + LPWSTR lstrcpyW(LPWSTR, LPCWSTR); + int lstrlenA(LPCSTR); + int lstrlenW(LPCWSTR); + + BOOL MoveFileA(LPCSTR, LPCSTR); + BOOL MoveFileW(LPCWSTR, LPCWSTR); + int MulDiv(int, int, int); + HANDLE OpenEventA(DWORD, BOOL, LPCSTR); + HANDLE OpenEventW(DWORD, BOOL, LPCWSTR); + deprecated HFILE OpenFile(LPCSTR, LPOFSTRUCT, UINT); + HANDLE OpenMutexA(DWORD, BOOL, LPCSTR); + HANDLE OpenMutexW(DWORD, BOOL, LPCWSTR); + HANDLE OpenProcess(DWORD, BOOL, DWORD); + HANDLE OpenSemaphoreA(DWORD, BOOL, LPCSTR); + HANDLE OpenSemaphoreW(DWORD, BOOL, LPCWSTR); + void OutputDebugStringA(LPCSTR); + void OutputDebugStringW(LPCWSTR); + BOOL PeekNamedPipe(HANDLE, PVOID, DWORD, PDWORD, PDWORD, PDWORD); + BOOL PulseEvent(HANDLE); + BOOL PurgeComm(HANDLE, DWORD); + BOOL QueryPerformanceCounter(PLARGE_INTEGER); + BOOL QueryPerformanceFrequency(PLARGE_INTEGER); + DWORD QueueUserAPC(PAPCFUNC, HANDLE, ULONG_PTR); + void RaiseException(DWORD, DWORD, DWORD, const(ULONG_PTR)*); + BOOL ReadFile(HANDLE, PVOID, DWORD, PDWORD, LPOVERLAPPED); + BOOL ReadFileEx(HANDLE, PVOID, DWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE); + BOOL ReadProcessMemory(HANDLE, PCVOID, PVOID, SIZE_T, SIZE_T*); + BOOL ReleaseMutex(HANDLE); + BOOL ReleaseSemaphore(HANDLE, LONG, LPLONG); + BOOL RemoveDirectoryA(LPCSTR); + BOOL RemoveDirectoryW(LPCWSTR); +/* In MinGW: +#ifdef _WIN32_WCE +extern BOOL ResetEvent(HANDLE); +#else +WINBASEAPI BOOL WINAPI ResetEvent(HANDLE); +#endif +*/ + BOOL ResetEvent(HANDLE); + DWORD ResumeThread(HANDLE); + DWORD SearchPathA(LPCSTR, LPCSTR, LPCSTR, DWORD, LPSTR, LPSTR*); + DWORD SearchPathW(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPWSTR, LPWSTR*); + BOOL SetCommBreak(HANDLE); + BOOL SetCommConfig(HANDLE, LPCOMMCONFIG, DWORD); + BOOL SetCommMask(HANDLE, DWORD); + BOOL SetCommState(HANDLE, LPDCB); + BOOL SetCommTimeouts(HANDLE, LPCOMMTIMEOUTS); + BOOL SetComputerNameA(LPCSTR); + BOOL SetComputerNameW(LPCWSTR); + BOOL SetCurrentDirectoryA(LPCSTR); + BOOL SetCurrentDirectoryW(LPCWSTR); + BOOL SetDefaultCommConfigA(LPCSTR, LPCOMMCONFIG, DWORD); + BOOL SetDefaultCommConfigW(LPCWSTR, LPCOMMCONFIG, DWORD); + BOOL SetEndOfFile(HANDLE); + BOOL SetEnvironmentVariableA(LPCSTR, LPCSTR); + BOOL SetEnvironmentVariableW(LPCWSTR, LPCWSTR); + UINT SetErrorMode(UINT); +/* In MinGW: +#ifdef _WIN32_WCE +extern BOOL SetEvent(HANDLE); +#else +WINBASEAPI BOOL WINAPI SetEvent(HANDLE); +#endif +*/ + BOOL SetEvent(HANDLE); + VOID SetFileApisToANSI(); + VOID SetFileApisToOEM(); + BOOL SetFileAttributesA(LPCSTR, DWORD); + BOOL SetFileAttributesW(LPCWSTR, DWORD); + DWORD SetFilePointer(HANDLE, LONG, PLONG, DWORD); + BOOL SetFileTime(HANDLE, const(FILETIME)*, const(FILETIME)*, const(FILETIME)*); + deprecated UINT SetHandleCount(UINT); + void SetLastError(DWORD); + void SetLastErrorEx(DWORD, DWORD); + BOOL SetLocalTime(const(SYSTEMTIME)*); + BOOL SetMailslotInfo(HANDLE, DWORD); + BOOL SetNamedPipeHandleState(HANDLE, PDWORD, PDWORD, PDWORD); + BOOL SetPriorityClass(HANDLE, DWORD); + BOOL SetStdHandle(DWORD, HANDLE); + BOOL SetSystemTime(const(SYSTEMTIME)*); + DWORD_PTR SetThreadAffinityMask(HANDLE, DWORD_PTR); + BOOL SetThreadContext(HANDLE, const(CONTEXT)*); + BOOL SetThreadPriority(HANDLE, int); + BOOL SetTimeZoneInformation(const(TIME_ZONE_INFORMATION)*); + LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER); + BOOL SetupComm(HANDLE, DWORD, DWORD); + BOOL SetVolumeLabelA(LPCSTR, LPCSTR); + BOOL SetVolumeLabelW(LPCWSTR, LPCWSTR); + + DWORD SizeofResource(HINSTANCE, HRSRC); + void Sleep(DWORD); + DWORD SleepEx(DWORD, BOOL); + DWORD SuspendThread(HANDLE); + BOOL SystemTimeToFileTime(const(SYSTEMTIME)*, LPFILETIME); + BOOL TerminateProcess(HANDLE, UINT); + BOOL TerminateThread(HANDLE, DWORD); + DWORD TlsAlloc(); + BOOL TlsFree(DWORD); + PVOID TlsGetValue(DWORD); + BOOL TlsSetValue(DWORD, PVOID); + BOOL TransactNamedPipe(HANDLE, PVOID, DWORD, PVOID, DWORD, PDWORD, LPOVERLAPPED); + BOOL TransmitCommChar(HANDLE, char); + LONG UnhandledExceptionFilter(LPEXCEPTION_POINTERS); + BOOL UnlockFile(HANDLE, DWORD, DWORD, DWORD, DWORD); + BOOL WaitCommEvent(HANDLE, PDWORD, LPOVERLAPPED); + BOOL WaitForDebugEvent(LPDEBUG_EVENT, DWORD); + DWORD WaitForMultipleObjects(DWORD, const(HANDLE)*, BOOL, DWORD); + DWORD WaitForMultipleObjectsEx(DWORD, const(HANDLE)*, BOOL, DWORD, BOOL); + DWORD WaitForSingleObject(HANDLE, DWORD); + DWORD WaitForSingleObjectEx(HANDLE, DWORD, BOOL); + BOOL WaitNamedPipeA(LPCSTR, DWORD); + BOOL WaitNamedPipeW(LPCWSTR, DWORD); + // undocumented on MSDN + BOOL WinLoadTrustProvider(GUID*); + BOOL WriteFile(HANDLE, PCVOID, DWORD, PDWORD, LPOVERLAPPED); + BOOL WriteFileEx(HANDLE, PCVOID, DWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE); + BOOL WritePrivateProfileSectionA(LPCSTR, LPCSTR, LPCSTR); + BOOL WritePrivateProfileSectionW(LPCWSTR, LPCWSTR, LPCWSTR); + BOOL WritePrivateProfileStringA(LPCSTR, LPCSTR, LPCSTR, LPCSTR); + BOOL WritePrivateProfileStringW(LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR); + BOOL WritePrivateProfileStructA(LPCSTR, LPCSTR, LPVOID, UINT, LPCSTR); + BOOL WritePrivateProfileStructW(LPCWSTR, LPCWSTR, LPVOID, UINT, LPCWSTR); + BOOL WriteProcessMemory(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*); + BOOL WriteProfileSectionA(LPCSTR, LPCSTR); + BOOL WriteProfileSectionW(LPCWSTR, LPCWSTR); + BOOL WriteProfileStringA(LPCSTR, LPCSTR, LPCSTR); + BOOL WriteProfileStringW(LPCWSTR, LPCWSTR, LPCWSTR); + + /* Memory allocation functions. + * MSDN documents these erroneously as Win2000+; thus it is uncertain what + * version compatibility they really have. + */ + HGLOBAL GlobalAlloc(UINT, SIZE_T); + HGLOBAL GlobalDiscard(HGLOBAL); + HGLOBAL GlobalFree(HGLOBAL); + HGLOBAL GlobalHandle(PCVOID); + LPVOID GlobalLock(HGLOBAL); + VOID GlobalMemoryStatus(LPMEMORYSTATUS); + HGLOBAL GlobalReAlloc(HGLOBAL, SIZE_T, UINT); + SIZE_T GlobalSize(HGLOBAL); + BOOL GlobalUnlock(HGLOBAL); + PVOID HeapAlloc(HANDLE, DWORD, SIZE_T); + SIZE_T HeapCompact(HANDLE, DWORD); + HANDLE HeapCreate(DWORD, SIZE_T, SIZE_T); + BOOL HeapDestroy(HANDLE); + BOOL HeapFree(HANDLE, DWORD, PVOID); + BOOL HeapLock(HANDLE); + PVOID HeapReAlloc(HANDLE, DWORD, PVOID, SIZE_T); + SIZE_T HeapSize(HANDLE, DWORD, PCVOID); + BOOL HeapUnlock(HANDLE); + BOOL HeapValidate(HANDLE, DWORD, PCVOID); + BOOL HeapWalk(HANDLE, LPPROCESS_HEAP_ENTRY); + HLOCAL LocalAlloc(UINT, SIZE_T); + HLOCAL LocalDiscard(HLOCAL); + HLOCAL LocalFree(HLOCAL); + HLOCAL LocalHandle(LPCVOID); + PVOID LocalLock(HLOCAL); + HLOCAL LocalReAlloc(HLOCAL, SIZE_T, UINT); + SIZE_T LocalSize(HLOCAL); + BOOL LocalUnlock(HLOCAL); + PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD); + PVOID VirtualAllocEx(HANDLE, PVOID, SIZE_T, DWORD, DWORD); + BOOL VirtualFree(PVOID, SIZE_T, DWORD); + BOOL VirtualFreeEx(HANDLE, PVOID, SIZE_T, DWORD); + BOOL VirtualLock(PVOID, SIZE_T); + BOOL VirtualProtect(PVOID, SIZE_T, DWORD, PDWORD); + BOOL VirtualProtectEx(HANDLE, PVOID, SIZE_T, DWORD, PDWORD); + SIZE_T VirtualQuery(LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T); + SIZE_T VirtualQueryEx(HANDLE, LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T); + BOOL VirtualUnlock(PVOID, SIZE_T); +// not in MinGW 4.0 - ??? + static if (_WIN32_WINNT >= 0x600) { + BOOL CancelIoEx(HANDLE, LPOVERLAPPED); + } + + BOOL CancelIo(HANDLE); + BOOL CancelWaitableTimer(HANDLE); + PVOID ConvertThreadToFiber(PVOID); + LPVOID CreateFiber(SIZE_T, LPFIBER_START_ROUTINE, LPVOID); + HANDLE CreateWaitableTimerA(LPSECURITY_ATTRIBUTES, BOOL, LPCSTR); + HANDLE CreateWaitableTimerW(LPSECURITY_ATTRIBUTES, BOOL, LPCWSTR); + void DeleteFiber(PVOID); + BOOL GetFileAttributesExA(LPCSTR, GET_FILEEX_INFO_LEVELS, PVOID); + BOOL GetFileAttributesExW(LPCWSTR, GET_FILEEX_INFO_LEVELS, PVOID); + DWORD GetLongPathNameA(LPCSTR, LPSTR, DWORD); + DWORD GetLongPathNameW(LPCWSTR, LPWSTR, DWORD); + BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION, DWORD); + BOOL IsDebuggerPresent(); + HANDLE OpenWaitableTimerA(DWORD, BOOL, LPCSTR); + HANDLE OpenWaitableTimerW(DWORD, BOOL, LPCWSTR); + DWORD QueryDosDeviceA(LPCSTR, LPSTR, DWORD); + DWORD QueryDosDeviceW(LPCWSTR, LPWSTR, DWORD); + BOOL SetWaitableTimer(HANDLE, const(LARGE_INTEGER)*, LONG, PTIMERAPCROUTINE, PVOID, BOOL); + void SwitchToFiber(PVOID); + + static if (_WIN32_WINNT >= 0x500) { + HANDLE OpenThread(DWORD, BOOL, DWORD); + } + + BOOL AccessCheck(PSECURITY_DESCRIPTOR, HANDLE, DWORD, PGENERIC_MAPPING, PPRIVILEGE_SET, PDWORD, PDWORD, PBOOL); + BOOL AccessCheckAndAuditAlarmA(LPCSTR, LPVOID, LPSTR, LPSTR, PSECURITY_DESCRIPTOR, DWORD, PGENERIC_MAPPING, BOOL, PDWORD, PBOOL, PBOOL); + BOOL AccessCheckAndAuditAlarmW(LPCWSTR, LPVOID, LPWSTR, LPWSTR, PSECURITY_DESCRIPTOR, DWORD, PGENERIC_MAPPING, BOOL, PDWORD, PBOOL, PBOOL); + BOOL AddAccessAllowedAce(PACL, DWORD, DWORD, PSID); + BOOL AddAccessDeniedAce(PACL, DWORD, DWORD, PSID); + BOOL AddAce(PACL, DWORD, DWORD, PVOID, DWORD); + BOOL AddAuditAccessAce(PACL, DWORD, DWORD, PSID, BOOL, BOOL); + BOOL AdjustTokenGroups(HANDLE, BOOL, PTOKEN_GROUPS, DWORD, PTOKEN_GROUPS, PDWORD); + BOOL AdjustTokenPrivileges(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); + BOOL AllocateAndInitializeSid(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*); + BOOL AllocateLocallyUniqueId(PLUID); + BOOL AreAllAccessesGranted(DWORD, DWORD); + BOOL AreAnyAccessesGranted(DWORD, DWORD); + BOOL BackupEventLogA(HANDLE, LPCSTR); + BOOL BackupEventLogW(HANDLE, LPCWSTR); + BOOL BackupRead(HANDLE, LPBYTE, DWORD, LPDWORD, BOOL, BOOL, LPVOID*); + BOOL BackupSeek(HANDLE, DWORD, DWORD, LPDWORD, LPDWORD, LPVOID*); + BOOL BackupWrite(HANDLE, LPBYTE, DWORD, LPDWORD, BOOL, BOOL, LPVOID*); + BOOL ClearEventLogA(HANDLE, LPCSTR); + BOOL ClearEventLogW(HANDLE, LPCWSTR); + BOOL CloseEventLog(HANDLE); + BOOL ConnectNamedPipe(HANDLE, LPOVERLAPPED); + BOOL CopySid(DWORD, PSID, PSID); + HANDLE CreateNamedPipeA(LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES); + HANDLE CreateNamedPipeW(LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES); + BOOL CreatePrivateObjectSecurity(PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR*, BOOL, HANDLE, PGENERIC_MAPPING); + BOOL CreateProcessAsUserA(HANDLE, LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, PVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); + BOOL CreateProcessAsUserW(HANDLE, LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, PVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); + HANDLE CreateRemoteThread(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD); + DWORD CreateTapePartition(HANDLE, DWORD, DWORD, DWORD); + BOOL DefineDosDeviceA(DWORD, LPCSTR, LPCSTR); + BOOL DefineDosDeviceW(DWORD, LPCWSTR, LPCWSTR); + BOOL DeleteAce(PACL, DWORD); + BOOL DeregisterEventSource(HANDLE); + BOOL DestroyPrivateObjectSecurity(PSECURITY_DESCRIPTOR*); + BOOL DeviceIoControl(HANDLE, DWORD, PVOID, DWORD, PVOID, DWORD, PDWORD, POVERLAPPED); + BOOL DisconnectNamedPipe(HANDLE); + BOOL DuplicateToken(HANDLE, SECURITY_IMPERSONATION_LEVEL, PHANDLE); + BOOL DuplicateTokenEx(HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE); + BOOL EqualPrefixSid(PSID, PSID); + BOOL EqualSid(PSID, PSID); + DWORD EraseTape(HANDLE, DWORD, BOOL); + HANDLE FindFirstFileExA(LPCSTR, FINDEX_INFO_LEVELS, PVOID, FINDEX_SEARCH_OPS, PVOID, DWORD); + HANDLE FindFirstFileExW(LPCWSTR, FINDEX_INFO_LEVELS, PVOID, FINDEX_SEARCH_OPS, PVOID, DWORD); + BOOL FindFirstFreeAce(PACL, PVOID*); + PVOID FreeSid(PSID); + BOOL GetAce(PACL, DWORD, LPVOID*); + BOOL GetAclInformation(PACL, PVOID, DWORD, ACL_INFORMATION_CLASS); + BOOL GetBinaryTypeA(LPCSTR, PDWORD); + BOOL GetBinaryTypeW(LPCWSTR, PDWORD); + DWORD GetCompressedFileSizeA(LPCSTR, PDWORD); + DWORD GetCompressedFileSizeW(LPCWSTR, PDWORD); + BOOL GetCurrentHwProfileA(LPHW_PROFILE_INFOA); + BOOL GetCurrentHwProfileW(LPHW_PROFILE_INFOW); + BOOL GetFileSecurityA(LPCSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, PDWORD); + BOOL GetFileSecurityW(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, PDWORD); + BOOL GetHandleInformation(HANDLE, PDWORD); + BOOL GetKernelObjectSecurity(HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, PDWORD); + DWORD GetLengthSid(PSID); + BOOL GetNumberOfEventLogRecords(HANDLE, PDWORD); + BOOL GetOldestEventLogRecord(HANDLE, PDWORD); + BOOL GetPrivateObjectSecurity(PSECURITY_DESCRIPTOR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, PDWORD); + BOOL GetProcessPriorityBoost(HANDLE, PBOOL); + BOOL GetProcessShutdownParameters(PDWORD, PDWORD); + BOOL GetProcessTimes(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); + HWINSTA GetProcessWindowStation(); + BOOL GetProcessWorkingSetSize(HANDLE, PSIZE_T, PSIZE_T); + BOOL GetQueuedCompletionStatus(HANDLE, PDWORD, PULONG_PTR, LPOVERLAPPED*, DWORD); + BOOL GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR_CONTROL, PDWORD); + BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR, LPBOOL, PACL*, LPBOOL); + BOOL GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR, PSID*, LPBOOL); + DWORD GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR); + BOOL GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR, PSID*, LPBOOL); + BOOL GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR, LPBOOL, PACL*, LPBOOL); + PSID_IDENTIFIER_AUTHORITY GetSidIdentifierAuthority(PSID); + DWORD GetSidLengthRequired(UCHAR); + PDWORD GetSidSubAuthority(PSID, DWORD); + PUCHAR GetSidSubAuthorityCount(PSID); + DWORD GetTapeParameters(HANDLE, DWORD, PDWORD, PVOID); + DWORD GetTapePosition(HANDLE, DWORD, PDWORD, PDWORD, PDWORD); + DWORD GetTapeStatus(HANDLE); + BOOL GetThreadPriorityBoost(HANDLE, PBOOL); + BOOL GetThreadTimes(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); + BOOL GetTokenInformation(HANDLE, TOKEN_INFORMATION_CLASS, PVOID, DWORD, PDWORD); + BOOL ImpersonateLoggedOnUser(HANDLE); + BOOL ImpersonateNamedPipeClient(HANDLE); + BOOL ImpersonateSelf(SECURITY_IMPERSONATION_LEVEL); + BOOL InitializeAcl(PACL, DWORD, DWORD); + DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION, DWORD); + BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR, DWORD); + BOOL InitializeSid(PSID, PSID_IDENTIFIER_AUTHORITY, BYTE); + BOOL IsProcessorFeaturePresent(DWORD); + BOOL IsTextUnicode(PCVOID, int, LPINT); + BOOL IsValidAcl(PACL); + BOOL IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR); + BOOL IsValidSid(PSID); + BOOL CreateWellKnownSid(WELL_KNOWN_SID_TYPE, PSID, PSID, PDWORD); + BOOL LockFileEx(HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED); + BOOL LogonUserA(LPSTR, LPSTR, LPSTR, DWORD, DWORD, PHANDLE); + BOOL LogonUserW(LPWSTR, LPWSTR, LPWSTR, DWORD, DWORD, PHANDLE); + BOOL LookupAccountNameA(LPCSTR, LPCSTR, PSID, PDWORD, LPSTR, PDWORD, PSID_NAME_USE); + BOOL LookupAccountNameW(LPCWSTR, LPCWSTR, PSID, PDWORD, LPWSTR, PDWORD, PSID_NAME_USE); + BOOL LookupAccountSidA(LPCSTR, PSID, LPSTR, PDWORD, LPSTR, PDWORD, PSID_NAME_USE); + BOOL LookupAccountSidW(LPCWSTR, PSID, LPWSTR, PDWORD, LPWSTR, PDWORD, PSID_NAME_USE); + BOOL LookupPrivilegeDisplayNameA(LPCSTR, LPCSTR, LPSTR, PDWORD, PDWORD); + BOOL LookupPrivilegeDisplayNameW(LPCWSTR, LPCWSTR, LPWSTR, PDWORD, PDWORD); + BOOL LookupPrivilegeNameA(LPCSTR, PLUID, LPSTR, PDWORD); + BOOL LookupPrivilegeNameW(LPCWSTR, PLUID, LPWSTR, PDWORD); + BOOL LookupPrivilegeValueA(LPCSTR, LPCSTR, PLUID); + BOOL LookupPrivilegeValueW(LPCWSTR, LPCWSTR, PLUID); + BOOL MakeAbsoluteSD(PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR, PDWORD, PACL, PDWORD, PACL, PDWORD, PSID, PDWORD, PSID, PDWORD); + BOOL MakeSelfRelativeSD(PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR, PDWORD); + VOID MapGenericMask(PDWORD, PGENERIC_MAPPING); + BOOL MoveFileExA(LPCSTR, LPCSTR, DWORD); + BOOL MoveFileExW(LPCWSTR, LPCWSTR, DWORD); + BOOL NotifyChangeEventLog(HANDLE, HANDLE); + BOOL ObjectCloseAuditAlarmA(LPCSTR, PVOID, BOOL); + BOOL ObjectCloseAuditAlarmW(LPCWSTR, PVOID, BOOL); + BOOL ObjectDeleteAuditAlarmA(LPCSTR, PVOID, BOOL); + BOOL ObjectDeleteAuditAlarmW(LPCWSTR, PVOID, BOOL); + BOOL ObjectOpenAuditAlarmA(LPCSTR, PVOID, LPSTR, LPSTR, PSECURITY_DESCRIPTOR, HANDLE, DWORD, DWORD, PPRIVILEGE_SET, BOOL, BOOL, PBOOL); + BOOL ObjectOpenAuditAlarmW(LPCWSTR, PVOID, LPWSTR, LPWSTR, PSECURITY_DESCRIPTOR, HANDLE, DWORD, DWORD, PPRIVILEGE_SET, BOOL, BOOL, PBOOL); + BOOL ObjectPrivilegeAuditAlarmA(LPCSTR, PVOID, HANDLE, DWORD, PPRIVILEGE_SET, BOOL); + BOOL ObjectPrivilegeAuditAlarmW(LPCWSTR, PVOID, HANDLE, DWORD, PPRIVILEGE_SET, BOOL); + HANDLE OpenBackupEventLogA(LPCSTR, LPCSTR); + HANDLE OpenBackupEventLogW(LPCWSTR, LPCWSTR); + HANDLE OpenEventLogA(LPCSTR, LPCSTR); + HANDLE OpenEventLogW(LPCWSTR, LPCWSTR); + BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE); + BOOL OpenThreadToken(HANDLE, DWORD, BOOL, PHANDLE); + BOOL PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); + DWORD PrepareTape(HANDLE, DWORD, BOOL); + BOOL PrivilegeCheck(HANDLE, PPRIVILEGE_SET, PBOOL); + BOOL PrivilegedServiceAuditAlarmA(LPCSTR, LPCSTR, HANDLE, PPRIVILEGE_SET, BOOL); + BOOL PrivilegedServiceAuditAlarmW(LPCWSTR, LPCWSTR, HANDLE, PPRIVILEGE_SET, BOOL); + BOOL ReadDirectoryChangesW(HANDLE, PVOID, DWORD, BOOL, DWORD, PDWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE); + BOOL ReadEventLogA(HANDLE, DWORD, DWORD, PVOID, DWORD, DWORD*, DWORD*); + BOOL ReadEventLogW(HANDLE, DWORD, DWORD, PVOID, DWORD, DWORD*, DWORD*); + BOOL ReadFileScatter(HANDLE, FILE_SEGMENT_ELEMENT*, DWORD, LPDWORD, LPOVERLAPPED); + HANDLE RegisterEventSourceA (LPCSTR, LPCSTR); + HANDLE RegisterEventSourceW(LPCWSTR, LPCWSTR); + BOOL ReportEventA(HANDLE, WORD, WORD, DWORD, PSID, WORD, DWORD, LPCSTR*, PVOID); + BOOL ReportEventW(HANDLE, WORD, WORD, DWORD, PSID, WORD, DWORD, LPCWSTR*, PVOID); + BOOL RevertToSelf(); + BOOL SetAclInformation(PACL, PVOID, DWORD, ACL_INFORMATION_CLASS); + BOOL SetFileSecurityA(LPCSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + BOOL SetFileSecurityW(LPCWSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + BOOL SetHandleInformation(HANDLE, DWORD, DWORD); + BOOL SetKernelObjectSecurity(HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); + BOOL SetPrivateObjectSecurity(SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSECURITY_DESCRIPTOR*, PGENERIC_MAPPING, HANDLE); + BOOL SetProcessAffinityMask(HANDLE, DWORD_PTR); + BOOL SetProcessPriorityBoost(HANDLE, BOOL); + BOOL SetProcessShutdownParameters(DWORD, DWORD); + BOOL SetProcessWorkingSetSize(HANDLE, SIZE_T, SIZE_T); + BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR, BOOL, PACL, BOOL); + BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR, PSID, BOOL); + BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR, PSID, BOOL); + BOOL SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR, BOOL, PACL, BOOL); + BOOL SetSystemTimeAdjustment(DWORD, BOOL); + DWORD SetTapeParameters(HANDLE, DWORD, PVOID); + DWORD SetTapePosition(HANDLE, DWORD, DWORD, DWORD, DWORD, BOOL); + BOOL SetThreadPriorityBoost(HANDLE, BOOL); + BOOL SetThreadToken(PHANDLE, HANDLE); + BOOL SetTokenInformation(HANDLE, TOKEN_INFORMATION_CLASS, PVOID, DWORD); + DWORD SignalObjectAndWait(HANDLE, HANDLE, DWORD, BOOL); + BOOL SwitchToThread(); + BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME); + BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME); + BOOL TryEnterCriticalSection(LPCRITICAL_SECTION); + BOOL TryEnterCriticalSection(shared(CRITICAL_SECTION)*); + BOOL UnlockFileEx(HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED); + BOOL UpdateResourceA(HANDLE, LPCSTR, LPCSTR, WORD, PVOID, DWORD); + BOOL UpdateResourceW(HANDLE, LPCWSTR, LPCWSTR, WORD, PVOID, DWORD); + BOOL WriteFileGather(HANDLE, FILE_SEGMENT_ELEMENT*, DWORD, LPDWORD, LPOVERLAPPED); + DWORD WriteTapemark(HANDLE, DWORD, DWORD, BOOL); + + static if (_WIN32_WINNT >= 0x500) { + BOOL AddAccessAllowedAceEx(PACL, DWORD, DWORD, DWORD, PSID); + BOOL AddAccessDeniedAceEx(PACL, DWORD, DWORD, DWORD, PSID); + PVOID AddVectoredExceptionHandler(ULONG, PVECTORED_EXCEPTION_HANDLER); + BOOL AllocateUserPhysicalPages(HANDLE, PULONG_PTR, PULONG_PTR); + BOOL AssignProcessToJobObject(HANDLE, HANDLE); + BOOL ChangeTimerQueueTimer(HANDLE,HANDLE,ULONG,ULONG); + LPVOID CreateFiberEx(SIZE_T, SIZE_T, DWORD, LPFIBER_START_ROUTINE, LPVOID); + HANDLE CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR); + HANDLE CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCWSTR); + BOOL CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); + BOOL CreateHardLinkW(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + HANDLE CreateJobObjectA(LPSECURITY_ATTRIBUTES, LPCSTR); + HANDLE CreateJobObjectW(LPSECURITY_ATTRIBUTES, LPCWSTR); + BOOL CreateProcessWithLogonW(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); + HANDLE CreateTimerQueue(); + BOOL CreateTimerQueueTimer(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG); + BOOL DeleteTimerQueue(HANDLE); + BOOL DeleteTimerQueueEx(HANDLE, HANDLE); + BOOL DeleteTimerQueueTimer(HANDLE, HANDLE, HANDLE); + BOOL DeleteVolumeMountPointA(LPCSTR); + BOOL DeleteVolumeMountPointW(LPCWSTR); + BOOL DnsHostnameToComputerNameA(LPCSTR, LPSTR, LPDWORD); + BOOL DnsHostnameToComputerNameW(LPCWSTR, LPWSTR, LPDWORD); + BOOL EncryptFileA(LPCSTR); + BOOL EncryptFileW(LPCWSTR); + BOOL FileEncryptionStatusA(LPCSTR, LPDWORD); + BOOL FileEncryptionStatusW(LPCWSTR, LPDWORD); + HANDLE FindFirstVolumeA(LPCSTR, DWORD); + HANDLE FindFirstVolumeMountPointA(LPSTR, LPSTR, DWORD); + HANDLE FindFirstVolumeMountPointW(LPWSTR, LPWSTR, DWORD); + HANDLE FindFirstVolumeW(LPCWSTR, DWORD); + BOOL FindNextVolumeA(HANDLE, LPCSTR, DWORD); + BOOL FindNextVolumeW(HANDLE, LPWSTR, DWORD); + BOOL FindNextVolumeMountPointA(HANDLE, LPSTR, DWORD); + BOOL FindNextVolumeMountPointW(HANDLE, LPWSTR, DWORD); + BOOL FindVolumeClose(HANDLE); + BOOL FindVolumeMountPointClose(HANDLE); + BOOL FlushViewOfFile(PCVOID, SIZE_T); + BOOL FreeUserPhysicalPages(HANDLE, PULONG_PTR, PULONG_PTR); + BOOL GetComputerNameExA(COMPUTER_NAME_FORMAT, LPSTR, LPDWORD); + BOOL GetComputerNameExW(COMPUTER_NAME_FORMAT, LPWSTR, LPDWORD); + BOOL GetFileSizeEx(HANDLE, PLARGE_INTEGER); + BOOL GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); + BOOL GetModuleHandleExW(DWORD, LPCWSTR, HMODULE*); + HANDLE GetProcessHeap(); + DWORD GetProcessHeaps(DWORD, PHANDLE); + BOOL GetProcessIoCounters(HANDLE, PIO_COUNTERS); + BOOL GetSystemPowerStatus(LPSYSTEM_POWER_STATUS); + UINT GetSystemWindowsDirectoryA(LPSTR, UINT); + UINT GetSystemWindowsDirectoryW(LPWSTR, UINT); + BOOL GetVolumeNameForVolumeMountPointA(LPCSTR, LPSTR, DWORD); + BOOL GetVolumeNameForVolumeMountPointW(LPCWSTR, LPWSTR, DWORD); + BOOL GetVolumePathNameA(LPCSTR, LPSTR, DWORD); + BOOL GetVolumePathNameW(LPCWSTR, LPWSTR, DWORD); + BOOL GlobalMemoryStatusEx(LPMEMORYSTATUSEX); + BOOL IsBadCodePtr(FARPROC); + BOOL IsSystemResumeAutomatic(); + BOOL MapUserPhysicalPages(PVOID, ULONG_PTR, PULONG_PTR); + BOOL MapUserPhysicalPagesScatter(PVOID*, ULONG_PTR, PULONG_PTR); + PVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); + PVOID MapViewOfFileEx(HANDLE, DWORD, DWORD, DWORD, SIZE_T, PVOID); + HANDLE OpenFileMappingA(DWORD, BOOL, LPCSTR); + HANDLE OpenFileMappingW(DWORD, BOOL, LPCWSTR); + BOOL ProcessIdToSessionId(DWORD, DWORD*); + BOOL QueryInformationJobObject(HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD); + ULONG RemoveVectoredExceptionHandler(PVOID); + BOOL ReplaceFileA(LPCSTR, LPCSTR, LPCSTR, DWORD, LPVOID, LPVOID); + BOOL ReplaceFileW(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID); + BOOL SetComputerNameExA(COMPUTER_NAME_FORMAT, LPCSTR); + BOOL SetComputerNameExW(COMPUTER_NAME_FORMAT, LPCWSTR); + BOOL SetFilePointerEx(HANDLE, LARGE_INTEGER, PLARGE_INTEGER, DWORD); + BOOL SetInformationJobObject(HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD); + BOOL SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR_CONTROL, SECURITY_DESCRIPTOR_CONTROL); + BOOL SetSystemPowerState(BOOL, BOOL); + EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE); + DWORD SetThreadIdealProcessor(HANDLE, DWORD); + BOOL SetVolumeMountPointA(LPCSTR, LPCSTR); + BOOL SetVolumeMountPointW(LPCWSTR, LPCWSTR); + BOOL TerminateJobObject(HANDLE, UINT); + BOOL UnmapViewOfFile(PCVOID); + BOOL UnregisterWait(HANDLE); + BOOL UnregisterWaitEx(HANDLE, HANDLE); + BOOL VerifyVersionInfoA(LPOSVERSIONINFOEXA, DWORD, DWORDLONG); + BOOL VerifyVersionInfoW(LPOSVERSIONINFOEXW, DWORD, DWORDLONG); + } + + static if (_WIN32_WINNT >= 0x501) { + BOOL ActivateActCtx(HANDLE, ULONG_PTR*); + void AddRefActCtx(HANDLE); + BOOL CheckNameLegalDOS8Dot3A(LPCSTR, LPSTR, DWORD, PBOOL, PBOOL); + BOOL CheckNameLegalDOS8Dot3W(LPCWSTR, LPSTR, DWORD, PBOOL, PBOOL); + BOOL CheckRemoteDebuggerPresent(HANDLE, PBOOL); + BOOL ConvertFiberToThread(); + HANDLE CreateActCtxA(PCACTCTXA); + HANDLE CreateActCtxW(PCACTCTXW); + HANDLE CreateMemoryResourceNotification(MEMORY_RESOURCE_NOTIFICATION_TYPE); + BOOL DeactivateActCtx(DWORD, ULONG_PTR); + BOOL DebugActiveProcessStop(DWORD); + BOOL DebugBreakProcess(HANDLE); + BOOL DebugSetProcessKillOnExit(BOOL); + BOOL FindActCtxSectionGuid(DWORD, const(GUID)*, ULONG, const(GUID)*, + PACTCTX_SECTION_KEYED_DATA); + BOOL FindActCtxSectionStringA(DWORD, const(GUID)*, ULONG, LPCSTR, + PACTCTX_SECTION_KEYED_DATA); + BOOL FindActCtxSectionStringW(DWORD, const(GUID)*, ULONG, LPCWSTR, + PACTCTX_SECTION_KEYED_DATA); + BOOL GetCurrentActCtx(HANDLE*); + VOID GetNativeSystemInfo(LPSYSTEM_INFO); + BOOL GetProcessHandleCount(HANDLE, PDWORD); + BOOL GetSystemRegistryQuota(PDWORD, PDWORD); + BOOL GetSystemTimes(LPFILETIME, LPFILETIME, LPFILETIME); + UINT GetSystemWow64DirectoryA(LPSTR, UINT); + UINT GetSystemWow64DirectoryW(LPWSTR, UINT); + BOOL GetThreadIOPendingFlag(HANDLE, PBOOL); + BOOL GetVolumePathNamesForVolumeNameA(LPCSTR, LPSTR, DWORD, PDWORD); + BOOL GetVolumePathNamesForVolumeNameW(LPCWSTR, LPWSTR, DWORD, PDWORD); + UINT GetWriteWatch(DWORD, PVOID, SIZE_T, PVOID*, PULONG_PTR, PULONG); + BOOL HeapQueryInformation(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); + BOOL HeapSetInformation(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); + BOOL IsProcessInJob(HANDLE, HANDLE, PBOOL); + BOOL IsWow64Process(HANDLE, PBOOL); + BOOL QueryActCtxW(DWORD, HANDLE, PVOID, ULONG, PVOID, SIZE_T, SIZE_T*); + BOOL QueryMemoryResourceNotification(HANDLE, PBOOL); + void ReleaseActCtx(HANDLE); + UINT ResetWriteWatch(LPVOID, SIZE_T); + BOOL SetFileShortNameA(HANDLE, LPCSTR); + BOOL SetFileShortNameW(HANDLE, LPCWSTR); + BOOL SetFileValidData(HANDLE, LONGLONG); + BOOL ZombifyActCtx(HANDLE); + } + + static if (_WIN32_WINNT >= 0x502) { + DWORD GetFirmwareEnvironmentVariableA(LPCSTR, LPCSTR, PVOID, DWORD); + DWORD GetFirmwareEnvironmentVariableW(LPCWSTR, LPCWSTR, PVOID, DWORD); + DWORD GetDllDirectoryA(DWORD, LPSTR); + DWORD GetDllDirectoryW(DWORD, LPWSTR); + DWORD GetThreadId(HANDLE); + DWORD GetProcessId(HANDLE); + HANDLE ReOpenFile(HANDLE, DWORD, DWORD, DWORD); + BOOL SetDllDirectoryA(LPCSTR); + BOOL SetDllDirectoryW(LPCWSTR); + BOOL SetFirmwareEnvironmentVariableA(LPCSTR, LPCSTR, PVOID, DWORD); + BOOL SetFirmwareEnvironmentVariableW(LPCWSTR, LPCWSTR, PVOID, DWORD); + } + + // ??? + static if (_WIN32_WINNT >= 0x510) { + VOID RestoreLastError(DWORD); + } + + static if (_WIN32_WINNT >= 0x600) { + BOOL CreateSymbolicLinkA(LPCSTR, LPCSTR, DWORD); + BOOL CreateSymbolicLinkW(LPCWSTR, LPCWSTR, DWORD); + BOOL GetFileInformationByHandleEx(HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); + } +} + +// For compatibility with old urt.internal.sys.windows.windows: +version (LittleEndian) nothrow @nogc +{ + BOOL QueryPerformanceCounter()(long* lpPerformanceCount) { return QueryPerformanceCounter(cast(PLARGE_INTEGER)lpPerformanceCount); } + BOOL QueryPerformanceFrequency()(long* lpFrequency) { return QueryPerformanceFrequency(cast(PLARGE_INTEGER)lpFrequency); } +} + +mixin DECLARE_AW!("STARTUPINFO"); +version (Unicode) { + //alias STARTUPINFOW STARTUPINFO; + alias WIN32_FIND_DATAW WIN32_FIND_DATA; + alias ENUMRESLANGPROCW ENUMRESLANGPROC; + alias ENUMRESNAMEPROCW ENUMRESNAMEPROC; + alias ENUMRESTYPEPROCW ENUMRESTYPEPROC; + alias AddAtomW AddAtom; + alias BeginUpdateResourceW BeginUpdateResource; + alias BuildCommDCBW BuildCommDCB; + alias BuildCommDCBAndTimeoutsW BuildCommDCBAndTimeouts; + alias CallNamedPipeW CallNamedPipe; + alias CommConfigDialogW CommConfigDialog; + alias CopyFileW CopyFile; + alias CopyFileExW CopyFileEx; + alias CreateDirectoryW CreateDirectory; + alias CreateDirectoryExW CreateDirectoryEx; + alias CreateEventW CreateEvent; + alias CreateFileW CreateFile; + alias CreateMailslotW CreateMailslot; + alias CreateMutexW CreateMutex; + alias CreateProcessW CreateProcess; + alias CreateSemaphoreW CreateSemaphore; + alias DeleteFileW DeleteFile; + alias EndUpdateResourceW EndUpdateResource; + alias EnumResourceLanguagesW EnumResourceLanguages; + alias EnumResourceNamesW EnumResourceNames; + alias EnumResourceTypesW EnumResourceTypes; + alias ExpandEnvironmentStringsW ExpandEnvironmentStrings; + alias FatalAppExitW FatalAppExit; + alias FindAtomW FindAtom; + alias FindFirstChangeNotificationW FindFirstChangeNotification; + alias FindFirstFileW FindFirstFile; + alias FindNextFileW FindNextFile; + alias FindResourceW FindResource; + alias FindResourceExW FindResourceEx; + alias FormatMessageW FormatMessage; + alias FreeEnvironmentStringsW FreeEnvironmentStrings; + alias GetAtomNameW GetAtomName; + alias GetCommandLineW GetCommandLine; + alias GetComputerNameW GetComputerName; + alias GetCurrentDirectoryW GetCurrentDirectory; + alias GetDefaultCommConfigW GetDefaultCommConfig; + alias GetDiskFreeSpaceW GetDiskFreeSpace; + alias GetDiskFreeSpaceExW GetDiskFreeSpaceEx; + alias GetDriveTypeW GetDriveType; + alias GetEnvironmentStringsW GetEnvironmentStrings; + alias GetEnvironmentVariableW GetEnvironmentVariable; + alias GetFileAttributesW GetFileAttributes; + alias GetFullPathNameW GetFullPathName; + alias GetLogicalDriveStringsW GetLogicalDriveStrings; + alias GetModuleFileNameW GetModuleFileName; + alias GetModuleHandleW GetModuleHandle; + alias GetNamedPipeHandleStateW GetNamedPipeHandleState; + alias GetPrivateProfileIntW GetPrivateProfileInt; + alias GetPrivateProfileSectionW GetPrivateProfileSection; + alias GetPrivateProfileSectionNamesW GetPrivateProfileSectionNames; + alias GetPrivateProfileStringW GetPrivateProfileString; + alias GetPrivateProfileStructW GetPrivateProfileStruct; + alias GetProfileIntW GetProfileInt; + alias GetProfileSectionW GetProfileSection; + alias GetProfileStringW GetProfileString; + alias GetShortPathNameW GetShortPathName; + alias GetStartupInfoW GetStartupInfo; + alias GetSystemDirectoryW GetSystemDirectory; + alias GetTempFileNameW GetTempFileName; + alias GetTempPathW GetTempPath; + alias GetUserNameW GetUserName; + alias GetVersionExW GetVersionEx; + alias GetVolumeInformationW GetVolumeInformation; + alias GetWindowsDirectoryW GetWindowsDirectory; + alias GlobalAddAtomW GlobalAddAtom; + alias GlobalFindAtomW GlobalFindAtom; + alias GlobalGetAtomNameW GlobalGetAtomName; + alias IsBadStringPtrW IsBadStringPtr; + alias LoadLibraryW LoadLibrary; + alias LoadLibraryExW LoadLibraryEx; + alias lstrcatW lstrcat; + alias lstrcmpW lstrcmp; + alias lstrcmpiW lstrcmpi; + alias lstrcpyW lstrcpy; + alias lstrcpynW lstrcpyn; + alias lstrlenW lstrlen; + alias MoveFileW MoveFile; + alias OpenEventW OpenEvent; + alias OpenMutexW OpenMutex; + alias OpenSemaphoreW OpenSemaphore; + alias OutputDebugStringW OutputDebugString; + alias RemoveDirectoryW RemoveDirectory; + alias SearchPathW SearchPath; + alias SetComputerNameW SetComputerName; + alias SetCurrentDirectoryW SetCurrentDirectory; + alias SetDefaultCommConfigW SetDefaultCommConfig; + alias SetEnvironmentVariableW SetEnvironmentVariable; + alias SetFileAttributesW SetFileAttributes; + alias SetVolumeLabelW SetVolumeLabel; + alias WaitNamedPipeW WaitNamedPipe; + alias WritePrivateProfileSectionW WritePrivateProfileSection; + alias WritePrivateProfileStringW WritePrivateProfileString; + alias WritePrivateProfileStructW WritePrivateProfileStruct; + alias WriteProfileSectionW WriteProfileSection; + alias WriteProfileStringW WriteProfileString; + alias CreateWaitableTimerW CreateWaitableTimer; + alias GetFileAttributesExW GetFileAttributesEx; + alias GetLongPathNameW GetLongPathName; + alias QueryDosDeviceW QueryDosDevice; + + alias HW_PROFILE_INFOW HW_PROFILE_INFO; + alias AccessCheckAndAuditAlarmW AccessCheckAndAuditAlarm; + alias BackupEventLogW BackupEventLog; + alias ClearEventLogW ClearEventLog; + alias CreateNamedPipeW CreateNamedPipe; + alias CreateProcessAsUserW CreateProcessAsUser; + alias DefineDosDeviceW DefineDosDevice; + alias FindFirstFileExW FindFirstFileEx; + alias GetBinaryTypeW GetBinaryType; + alias GetCompressedFileSizeW GetCompressedFileSize; + alias GetFileSecurityW GetFileSecurity; + alias LogonUserW LogonUser; + alias LookupAccountNameW LookupAccountName; + alias LookupAccountSidW LookupAccountSid; + alias LookupPrivilegeDisplayNameW LookupPrivilegeDisplayName; + alias LookupPrivilegeNameW LookupPrivilegeName; + alias LookupPrivilegeValueW LookupPrivilegeValue; + alias MoveFileExW MoveFileEx; + alias ObjectCloseAuditAlarmW ObjectCloseAuditAlarm; + alias ObjectDeleteAuditAlarmW ObjectDeleteAuditAlarm; + alias ObjectOpenAuditAlarmW ObjectOpenAuditAlarm; + alias ObjectPrivilegeAuditAlarmW ObjectPrivilegeAuditAlarm; + alias OpenBackupEventLogW OpenBackupEventLog; + alias OpenEventLogW OpenEventLog; + alias PrivilegedServiceAuditAlarmW PrivilegedServiceAuditAlarm; + alias ReadEventLogW ReadEventLog; + alias RegisterEventSourceW RegisterEventSource; + alias ReportEventW ReportEvent; + alias SetFileSecurityW SetFileSecurity; + alias UpdateResourceW UpdateResource; + + static if (_WIN32_WINNT >= 0x500) { + alias CreateFileMappingW CreateFileMapping; + alias CreateHardLinkW CreateHardLink; + alias CreateJobObjectW CreateJobObject; + alias DeleteVolumeMountPointW DeleteVolumeMountPoint; + alias DnsHostnameToComputerNameW DnsHostnameToComputerName; + alias EncryptFileW EncryptFile; + alias FileEncryptionStatusW FileEncryptionStatus; + alias FindFirstVolumeW FindFirstVolume; + alias FindFirstVolumeMountPointW FindFirstVolumeMountPoint; + alias FindNextVolumeW FindNextVolume; + alias FindNextVolumeMountPointW FindNextVolumeMountPoint; + alias GetModuleHandleExW GetModuleHandleEx; + alias GetSystemWindowsDirectoryW GetSystemWindowsDirectory; + alias GetVolumeNameForVolumeMountPointW GetVolumeNameForVolumeMountPoint; + alias GetVolumePathNameW GetVolumePathName; + alias OpenFileMappingW OpenFileMapping; + alias ReplaceFileW ReplaceFile; + alias SetVolumeMountPointW SetVolumeMountPoint; + alias VerifyVersionInfoW VerifyVersionInfo; + } + + static if (_WIN32_WINNT >= 0x501) { + alias ACTCTXW ACTCTX; + alias CheckNameLegalDOS8Dot3W CheckNameLegalDOS8Dot3; + alias CreateActCtxW CreateActCtx; + alias FindActCtxSectionStringW FindActCtxSectionString; + alias GetSystemWow64DirectoryW GetSystemWow64Directory; + alias GetVolumePathNamesForVolumeNameW GetVolumePathNamesForVolumeName; + alias SetFileShortNameW SetFileShortName; + } + + static if (_WIN32_WINNT >= 0x502) { + alias SetFirmwareEnvironmentVariableW SetFirmwareEnvironmentVariable; + alias SetDllDirectoryW SetDllDirectory; + alias GetDllDirectoryW GetDllDirectory; + } + + static if (_WIN32_WINNT >= 0x600) { + alias CreateSymbolicLinkW CreateSymbolicLink; + } + +} else { + //alias STARTUPINFOA STARTUPINFO; + alias WIN32_FIND_DATAA WIN32_FIND_DATA; + alias ENUMRESLANGPROCW ENUMRESLANGPROC; + alias ENUMRESNAMEPROCW ENUMRESNAMEPROC; + alias ENUMRESTYPEPROCW ENUMRESTYPEPROC; + alias AddAtomA AddAtom; + alias BeginUpdateResourceA BeginUpdateResource; + alias BuildCommDCBA BuildCommDCB; + alias BuildCommDCBAndTimeoutsA BuildCommDCBAndTimeouts; + alias CallNamedPipeA CallNamedPipe; + alias CommConfigDialogA CommConfigDialog; + alias CopyFileA CopyFile; + alias CopyFileExA CopyFileEx; + alias CreateDirectoryA CreateDirectory; + alias CreateDirectoryExA CreateDirectoryEx; + alias CreateEventA CreateEvent; + alias CreateFileA CreateFile; + alias CreateMailslotA CreateMailslot; + alias CreateMutexA CreateMutex; + alias CreateProcessA CreateProcess; + alias CreateSemaphoreA CreateSemaphore; + alias DeleteFileA DeleteFile; + alias EndUpdateResourceA EndUpdateResource; + alias EnumResourceLanguagesA EnumResourceLanguages; + alias EnumResourceNamesA EnumResourceNames; + alias EnumResourceTypesA EnumResourceTypes; + alias ExpandEnvironmentStringsA ExpandEnvironmentStrings; + alias FatalAppExitA FatalAppExit; + alias FindAtomA FindAtom; + alias FindFirstChangeNotificationA FindFirstChangeNotification; + alias FindFirstFileA FindFirstFile; + alias FindNextFileA FindNextFile; + alias FindResourceA FindResource; + alias FindResourceExA FindResourceEx; + alias FormatMessageA FormatMessage; + alias FreeEnvironmentStringsA FreeEnvironmentStrings; + alias GetAtomNameA GetAtomName; + alias GetCommandLineA GetCommandLine; + alias GetComputerNameA GetComputerName; + alias GetCurrentDirectoryA GetCurrentDirectory; + alias GetDefaultCommConfigA GetDefaultCommConfig; + alias GetDiskFreeSpaceA GetDiskFreeSpace; + alias GetDiskFreeSpaceExA GetDiskFreeSpaceEx; + alias GetDriveTypeA GetDriveType; + alias GetEnvironmentStringsA GetEnvironmentStrings; + alias GetEnvironmentVariableA GetEnvironmentVariable; + alias GetFileAttributesA GetFileAttributes; + alias GetFullPathNameA GetFullPathName; + alias GetLogicalDriveStringsA GetLogicalDriveStrings; + alias GetNamedPipeHandleStateA GetNamedPipeHandleState; + alias GetModuleHandleA GetModuleHandle; + alias GetModuleFileNameA GetModuleFileName; + alias GetPrivateProfileIntA GetPrivateProfileInt; + alias GetPrivateProfileSectionA GetPrivateProfileSection; + alias GetPrivateProfileSectionNamesA GetPrivateProfileSectionNames; + alias GetPrivateProfileStringA GetPrivateProfileString; + alias GetPrivateProfileStructA GetPrivateProfileStruct; + alias GetProfileIntA GetProfileInt; + alias GetProfileSectionA GetProfileSection; + alias GetProfileStringA GetProfileString; + alias GetShortPathNameA GetShortPathName; + alias GetStartupInfoA GetStartupInfo; + alias GetSystemDirectoryA GetSystemDirectory; + alias GetTempFileNameA GetTempFileName; + alias GetTempPathA GetTempPath; + alias GetUserNameA GetUserName; + alias GetVersionExA GetVersionEx; + alias GetVolumeInformationA GetVolumeInformation; + alias GetWindowsDirectoryA GetWindowsDirectory; + alias GlobalAddAtomA GlobalAddAtom; + alias GlobalFindAtomA GlobalFindAtom; + alias GlobalGetAtomNameA GlobalGetAtomName; + alias IsBadStringPtrA IsBadStringPtr; + alias LoadLibraryA LoadLibrary; + alias LoadLibraryExA LoadLibraryEx; + alias lstrcatA lstrcat; + alias lstrcmpA lstrcmp; + alias lstrcmpiA lstrcmpi; + alias lstrcpyA lstrcpy; + alias lstrcpynA lstrcpyn; + alias lstrlenA lstrlen; + alias MoveFileA MoveFile; + alias OpenEventA OpenEvent; + alias OpenMutexA OpenMutex; + alias OpenSemaphoreA OpenSemaphore; + alias OutputDebugStringA OutputDebugString; + alias RemoveDirectoryA RemoveDirectory; + alias SearchPathA SearchPath; + alias SetComputerNameA SetComputerName; + alias SetCurrentDirectoryA SetCurrentDirectory; + alias SetDefaultCommConfigA SetDefaultCommConfig; + alias SetEnvironmentVariableA SetEnvironmentVariable; + alias SetFileAttributesA SetFileAttributes; + alias SetVolumeLabelA SetVolumeLabel; + alias WaitNamedPipeA WaitNamedPipe; + alias WritePrivateProfileSectionA WritePrivateProfileSection; + alias WritePrivateProfileStringA WritePrivateProfileString; + alias WritePrivateProfileStructA WritePrivateProfileStruct; + alias WriteProfileSectionA WriteProfileSection; + alias WriteProfileStringA WriteProfileString; + alias CreateWaitableTimerA CreateWaitableTimer; + alias GetFileAttributesExA GetFileAttributesEx; + alias GetLongPathNameA GetLongPathName; + alias QueryDosDeviceA QueryDosDevice; + + alias HW_PROFILE_INFOA HW_PROFILE_INFO; + alias AccessCheckAndAuditAlarmA AccessCheckAndAuditAlarm; + alias BackupEventLogA BackupEventLog; + alias ClearEventLogA ClearEventLog; + alias CreateNamedPipeA CreateNamedPipe; + alias CreateProcessAsUserA CreateProcessAsUser; + alias DefineDosDeviceA DefineDosDevice; + alias FindFirstFileExA FindFirstFileEx; + alias GetBinaryTypeA GetBinaryType; + alias GetCompressedFileSizeA GetCompressedFileSize; + alias GetFileSecurityA GetFileSecurity; + alias LogonUserA LogonUser; + alias LookupAccountNameA LookupAccountName; + alias LookupAccountSidA LookupAccountSid; + alias LookupPrivilegeDisplayNameA LookupPrivilegeDisplayName; + alias LookupPrivilegeNameA LookupPrivilegeName; + alias LookupPrivilegeValueA LookupPrivilegeValue; + alias MoveFileExA MoveFileEx; + alias ObjectCloseAuditAlarmA ObjectCloseAuditAlarm; + alias ObjectDeleteAuditAlarmA ObjectDeleteAuditAlarm; + alias ObjectOpenAuditAlarmA ObjectOpenAuditAlarm; + alias ObjectPrivilegeAuditAlarmA ObjectPrivilegeAuditAlarm; + alias OpenBackupEventLogA OpenBackupEventLog; + alias OpenEventLogA OpenEventLog; + alias PrivilegedServiceAuditAlarmA PrivilegedServiceAuditAlarm; + alias ReadEventLogA ReadEventLog; + alias RegisterEventSourceA RegisterEventSource; + alias ReportEventA ReportEvent; + alias SetFileSecurityA SetFileSecurity; + alias UpdateResourceA UpdateResource; + + static if (_WIN32_WINNT >= 0x500) { + alias CreateFileMappingA CreateFileMapping; + alias CreateHardLinkA CreateHardLink; + alias CreateJobObjectA CreateJobObject; + alias DeleteVolumeMountPointA DeleteVolumeMountPoint; + alias DnsHostnameToComputerNameA DnsHostnameToComputerName; + alias EncryptFileA EncryptFile; + alias FileEncryptionStatusA FileEncryptionStatus; + alias FindFirstVolumeA FindFirstVolume; + alias FindFirstVolumeMountPointA FindFirstVolumeMountPoint; + alias FindNextVolumeA FindNextVolume; + alias FindNextVolumeMountPointA FindNextVolumeMountPoint; + alias GetModuleHandleExA GetModuleHandleEx; + alias GetSystemWindowsDirectoryA GetSystemWindowsDirectory; + alias GetVolumeNameForVolumeMountPointA GetVolumeNameForVolumeMountPoint; + alias GetVolumePathNameA GetVolumePathName; + alias OpenFileMappingA OpenFileMapping; + alias ReplaceFileA ReplaceFile; + alias SetVolumeMountPointA SetVolumeMountPoint; + alias VerifyVersionInfoA VerifyVersionInfo; + } + + static if (_WIN32_WINNT >= 0x501) { + alias ACTCTXA ACTCTX; + alias CheckNameLegalDOS8Dot3A CheckNameLegalDOS8Dot3; + alias CreateActCtxA CreateActCtx; + alias FindActCtxSectionStringA FindActCtxSectionString; + alias GetSystemWow64DirectoryA GetSystemWow64Directory; + alias GetVolumePathNamesForVolumeNameA GetVolumePathNamesForVolumeName; + alias SetFileShortNameA SetFileShortName; + } + + static if (_WIN32_WINNT >= 0x502) { + alias GetDllDirectoryA GetDllDirectory; + alias SetDllDirectoryA SetDllDirectory; + alias SetFirmwareEnvironmentVariableA SetFirmwareEnvironmentVariable; + } + + static if (_WIN32_WINNT >= 0x600) { + alias CreateSymbolicLinkA CreateSymbolicLink; + } +} + +alias STARTUPINFO* LPSTARTUPINFO; +alias WIN32_FIND_DATA* LPWIN32_FIND_DATA; + +alias HW_PROFILE_INFO* LPHW_PROFILE_INFO; + +static if (_WIN32_WINNT >= 0x501) { + alias ACTCTX* PACTCTX, PCACTCTX; +} diff --git a/src/urt/internal/sys/windows/wincon.d b/src/urt/internal/sys/windows/wincon.d new file mode 100644 index 0000000..d378237 --- /dev/null +++ b/src/urt/internal/sys/windows/wincon.d @@ -0,0 +1,316 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_wincon.d) + */ +module urt.internal.sys.windows.wincon; +version (Windows): + +version (ANSI) {} else version = Unicode; +pragma(lib, "kernel32"); + +import urt.internal.sys.windows.w32api, urt.internal.sys.windows.windef; + +// FIXME: clean up Windows version support + +enum { + FOREGROUND_BLUE = 0x0001, + FOREGROUND_GREEN = 0x0002, + FOREGROUND_RED = 0x0004, + FOREGROUND_INTENSITY = 0x0008, + BACKGROUND_BLUE = 0x0010, + BACKGROUND_GREEN = 0x0020, + BACKGROUND_RED = 0x0040, + BACKGROUND_INTENSITY = 0x0080, + + COMMON_LVB_LEADING_BYTE = 0x0100, + COMMON_LVB_TRAILING_BYTE = 0x0200, + COMMON_LVB_GRID_HORIZONTAL = 0x0400, + COMMON_LVB_GRID_LVERTICAL = 0x0800, + COMMON_LVB_GRID_RVERTICAL = 0x1000, + COMMON_LVB_REVERSE_VIDEO = 0x4000, + COMMON_LVB_UNDERSCORE = 0x8000, + + COMMON_LVB_SBCSDBCS = 0x0300, +} + +static if (_WIN32_WINNT >= 0x501) { + enum { + CONSOLE_FULLSCREEN_MODE = 1, + CONSOLE_WINDOWED_MODE = 0 + } +} + +enum { + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 1, + CTRL_CLOSE_EVENT = 2, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT = 6 +} + +enum { + ENABLE_PROCESSED_INPUT = 1, + ENABLE_LINE_INPUT = 2, + ENABLE_ECHO_INPUT = 4, + ENABLE_WINDOW_INPUT = 8, + ENABLE_MOUSE_INPUT = 16, + ENABLE_INSERT_MODE = 32, + ENABLE_QUICK_EDIT_MODE = 64, + ENABLE_EXTENDED_FLAGS = 128, + ENABLE_AUTO_POSITION = 256, + ENABLE_VIRTUAL_TERMINAL_INPUT = 512 +} + +enum { + ENABLE_PROCESSED_OUTPUT = 1, + ENABLE_WRAP_AT_EOL_OUTPUT = 2, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4, + DISABLE_NEWLINE_AUTO_RETURN = 8, + ENABLE_LVB_GRID_WORLDWIDE = 16 +} + +enum { + KEY_EVENT = 1, + MOUSE_EVENT = 2, + WINDOW_BUFFER_SIZE_EVENT = 4, + MENU_EVENT = 8, + FOCUS_EVENT = 16 +} +enum { + RIGHT_ALT_PRESSED = 1, + LEFT_ALT_PRESSED = 2, + RIGHT_CTRL_PRESSED = 4, + LEFT_CTRL_PRESSED = 8, + SHIFT_PRESSED = 16, + NUMLOCK_ON = 32, + SCROLLLOCK_ON = 64, + CAPSLOCK_ON = 128, + ENHANCED_KEY = 256 +} +enum { + FROM_LEFT_1ST_BUTTON_PRESSED = 1, + RIGHTMOST_BUTTON_PRESSED = 2, + FROM_LEFT_2ND_BUTTON_PRESSED = 4, + FROM_LEFT_3RD_BUTTON_PRESSED = 8, + FROM_LEFT_4TH_BUTTON_PRESSED = 16 +} + +enum { + MOUSE_MOVED = 1, + DOUBLE_CLICK = 2, + MOUSE_WHEELED = 4 +} + +struct CHAR_INFO { + union _Char { + WCHAR UnicodeChar = 0; + CHAR AsciiChar; + } + union { + _Char Char; + WCHAR UnicodeChar; + CHAR AsciiChar; + } + WORD Attributes; +} +alias CHAR_INFO* PCHAR_INFO; + +struct SMALL_RECT { + SHORT Left; + SHORT Top; + SHORT Right; + SHORT Bottom; +} +alias SMALL_RECT* PSMALL_RECT; + +struct CONSOLE_CURSOR_INFO { + DWORD dwSize; + BOOL bVisible; +} +alias CONSOLE_CURSOR_INFO* PCONSOLE_CURSOR_INFO; + +struct COORD { + SHORT X; + SHORT Y; +} +alias COORD* PCOORD; + +struct CONSOLE_FONT_INFO { + DWORD nFont; + COORD dwFontSize; +} +alias CONSOLE_FONT_INFO* PCONSOLE_FONT_INFO; + +struct CONSOLE_SCREEN_BUFFER_INFO { + COORD dwSize; + COORD dwCursorPosition; + WORD wAttributes; + SMALL_RECT srWindow; + COORD dwMaximumWindowSize; +} +alias CONSOLE_SCREEN_BUFFER_INFO* PCONSOLE_SCREEN_BUFFER_INFO; + +alias extern(Windows) BOOL function(DWORD) nothrow PHANDLER_ROUTINE; + +struct KEY_EVENT_RECORD { + BOOL bKeyDown; + WORD wRepeatCount; + WORD wVirtualKeyCode; + WORD wVirtualScanCode; + union _uChar { + WCHAR UnicodeChar = 0; + CHAR AsciiChar; + } + union { + WCHAR UnicodeChar = 0; + CHAR AsciiChar; + _uChar uChar; + } + DWORD dwControlKeyState; +} +alias KEY_EVENT_RECORD* PKEY_EVENT_RECORD; + +struct MOUSE_EVENT_RECORD { + COORD dwMousePosition; + DWORD dwButtonState; + DWORD dwControlKeyState; + DWORD dwEventFlags; +} +alias MOUSE_EVENT_RECORD* PMOUSE_EVENT_RECORD; + +struct WINDOW_BUFFER_SIZE_RECORD { + COORD dwSize; +} +alias WINDOW_BUFFER_SIZE_RECORD* PWINDOW_BUFFER_SIZE_RECORD; + +struct MENU_EVENT_RECORD { + UINT dwCommandId; +} +alias MENU_EVENT_RECORD* PMENU_EVENT_RECORD; + +struct FOCUS_EVENT_RECORD { + BOOL bSetFocus; +} +alias FOCUS_EVENT_RECORD* PFOCUS_EVENT_RECORD; + +struct INPUT_RECORD { + WORD EventType; + union _Event { + KEY_EVENT_RECORD KeyEvent; + MOUSE_EVENT_RECORD MouseEvent; + WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; + MENU_EVENT_RECORD MenuEvent; + FOCUS_EVENT_RECORD FocusEvent; + } + union { + KEY_EVENT_RECORD KeyEvent; + MOUSE_EVENT_RECORD MouseEvent; + WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent; + MENU_EVENT_RECORD MenuEvent; + FOCUS_EVENT_RECORD FocusEvent; + _Event Event; + } +} +alias INPUT_RECORD* PINPUT_RECORD; + +extern (Windows) nothrow @nogc: + +BOOL AllocConsole(); +HANDLE CreateConsoleScreenBuffer(DWORD, DWORD, const(SECURITY_ATTRIBUTES)*, DWORD, LPVOID); +BOOL FillConsoleOutputAttribute(HANDLE, WORD, DWORD, COORD, PDWORD); +BOOL FillConsoleOutputCharacterA(HANDLE, CHAR, DWORD, COORD, PDWORD); +BOOL FillConsoleOutputCharacterW(HANDLE, WCHAR, DWORD, COORD, PDWORD); +BOOL FlushConsoleInputBuffer(HANDLE); +BOOL FreeConsole(); +BOOL GenerateConsoleCtrlEvent(DWORD, DWORD); +UINT GetConsoleCP(); +BOOL GetConsoleCursorInfo(HANDLE, PCONSOLE_CURSOR_INFO); +BOOL GetConsoleMode(HANDLE,PDWORD); +UINT GetConsoleOutputCP(); +BOOL GetConsoleScreenBufferInfo(HANDLE, PCONSOLE_SCREEN_BUFFER_INFO); +DWORD GetConsoleTitleA(LPSTR, DWORD); +DWORD GetConsoleTitleW(LPWSTR, DWORD); +COORD GetLargestConsoleWindowSize(HANDLE); +BOOL GetNumberOfConsoleInputEvents(HANDLE, PDWORD); +BOOL GetNumberOfConsoleMouseButtons(PDWORD); +BOOL PeekConsoleInputA(HANDLE, PINPUT_RECORD, DWORD, PDWORD); +BOOL PeekConsoleInputW(HANDLE, PINPUT_RECORD, DWORD, PDWORD); +BOOL ReadConsoleA(HANDLE, PVOID, DWORD, PDWORD, PVOID); +BOOL ReadConsoleW(HANDLE, PVOID, DWORD, PDWORD, PVOID); +BOOL ReadConsoleInputA(HANDLE, PINPUT_RECORD, DWORD, PDWORD); +BOOL ReadConsoleInputW(HANDLE, PINPUT_RECORD, DWORD, PDWORD); +BOOL ReadConsoleOutputAttribute(HANDLE, LPWORD, DWORD, COORD, LPDWORD); +BOOL ReadConsoleOutputCharacterA(HANDLE, LPSTR, DWORD, COORD, PDWORD); +BOOL ReadConsoleOutputCharacterW(HANDLE, LPWSTR, DWORD, COORD, PDWORD); +BOOL ReadConsoleOutputA(HANDLE, PCHAR_INFO, COORD, COORD, PSMALL_RECT); +BOOL ReadConsoleOutputW(HANDLE, PCHAR_INFO, COORD, COORD, PSMALL_RECT); +BOOL ScrollConsoleScreenBufferA(HANDLE, const(SMALL_RECT)*, const(SMALL_RECT)*, COORD, const(CHAR_INFO)*); +BOOL ScrollConsoleScreenBufferW(HANDLE, const(SMALL_RECT)*, const(SMALL_RECT)*, COORD, const(CHAR_INFO)*); +BOOL SetConsoleActiveScreenBuffer(HANDLE); +BOOL SetConsoleCP(UINT); +BOOL SetConsoleCtrlHandler(PHANDLER_ROUTINE, BOOL); +BOOL SetConsoleCursorInfo(HANDLE, const(CONSOLE_CURSOR_INFO)*); +BOOL SetConsoleCursorPosition(HANDLE, COORD); + + +static if (_WIN32_WINNT >= 0x500) { +BOOL GetConsoleDisplayMode(LPDWORD); +HWND GetConsoleWindow(); +} + +static if (_WIN32_WINNT >= 0x501) { +BOOL AttachConsole(DWORD); +BOOL SetConsoleDisplayMode(HANDLE, DWORD, PCOORD); +enum DWORD ATTACH_PARENT_PROCESS = cast(DWORD)-1; +} + +BOOL SetConsoleMode(HANDLE, DWORD); +BOOL SetConsoleOutputCP(UINT); +BOOL SetConsoleScreenBufferSize(HANDLE, COORD); +BOOL SetConsoleTextAttribute(HANDLE, WORD); +BOOL SetConsoleTitleA(LPCSTR); +BOOL SetConsoleTitleW(LPCWSTR); +BOOL SetConsoleWindowInfo(HANDLE, BOOL, const(SMALL_RECT)*); +BOOL WriteConsoleA(HANDLE, PCVOID, DWORD, PDWORD, PVOID); +BOOL WriteConsoleW(HANDLE, PCVOID, DWORD, PDWORD, PVOID); +BOOL WriteConsoleInputA(HANDLE, const(INPUT_RECORD)*, DWORD, PDWORD); +BOOL WriteConsoleInputW(HANDLE, const(INPUT_RECORD)*, DWORD, PDWORD); +BOOL WriteConsoleOutputA(HANDLE, const(CHAR_INFO)*, COORD, COORD, PSMALL_RECT); +BOOL WriteConsoleOutputW(HANDLE, const(CHAR_INFO)*, COORD, COORD, PSMALL_RECT); +BOOL WriteConsoleOutputAttribute(HANDLE, const(WORD)*, DWORD, COORD, PDWORD); +BOOL WriteConsoleOutputCharacterA(HANDLE, LPCSTR, DWORD, COORD, PDWORD); +BOOL WriteConsoleOutputCharacterW(HANDLE, LPCWSTR, DWORD, COORD, PDWORD); + +version (Unicode) { + alias FillConsoleOutputCharacterW FillConsoleOutputCharacter; + alias GetConsoleTitleW GetConsoleTitle; + alias PeekConsoleInputW PeekConsoleInput; + alias ReadConsoleW ReadConsole; + alias ReadConsoleInputW ReadConsoleInput; + alias ReadConsoleOutputW ReadConsoleOutput; + alias ReadConsoleOutputCharacterW ReadConsoleOutputCharacter; + alias ScrollConsoleScreenBufferW ScrollConsoleScreenBuffer; + alias SetConsoleTitleW SetConsoleTitle; + alias WriteConsoleW WriteConsole; + alias WriteConsoleInputW WriteConsoleInput; + alias WriteConsoleOutputW WriteConsoleOutput; + alias WriteConsoleOutputCharacterW WriteConsoleOutputCharacter; +} else { + alias FillConsoleOutputCharacterA FillConsoleOutputCharacter; + alias GetConsoleTitleA GetConsoleTitle; + alias PeekConsoleInputA PeekConsoleInput; + alias ReadConsoleA ReadConsole; + alias ReadConsoleInputA ReadConsoleInput; + alias ReadConsoleOutputA ReadConsoleOutput; + alias ReadConsoleOutputCharacterA ReadConsoleOutputCharacter; + alias ScrollConsoleScreenBufferA ScrollConsoleScreenBuffer; + alias SetConsoleTitleA SetConsoleTitle; + alias WriteConsoleA WriteConsole; + alias WriteConsoleInputA WriteConsoleInput; + alias WriteConsoleOutputA WriteConsoleOutput; + alias WriteConsoleOutputCharacterA WriteConsoleOutputCharacter; +} diff --git a/src/urt/internal/sys/windows/wincrypt.d b/src/urt/internal/sys/windows/wincrypt.d new file mode 100644 index 0000000..38e00b5 --- /dev/null +++ b/src/urt/internal/sys/windows/wincrypt.d @@ -0,0 +1,902 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_wincrypt.d) + */ +module urt.internal.sys.windows.wincrypt; +version (Windows): +pragma(lib, "advapi32"); + +version (ANSI) {} else version = Unicode; + +import urt.internal.sys.windows.w32api, urt.internal.sys.windows.winbase, urt.internal.sys.windows.windef; + +/* FIXME: + * Types of some constants + * Types of macros + * Inits of various "size" and "version" members + * Why are some #ifdefs commented out? + */ + +const TCHAR[] + MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0", + MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0", + MS_STRONG_PROV = "Microsoft Strong Cryptographic Provider", + MS_DEF_RSA_SIG_PROV = "Microsoft RSA Signature Cryptographic Provider", + MS_DEF_RSA_SCHANNEL_PROV = "Microsoft RSA SChannel Cryptographic Provider", + MS_DEF_DSS_PROV = "Microsoft Base DSS Cryptographic Provider", + MS_DEF_DSS_DH_PROV + = "Microsoft Base DSS and Diffie-Hellman Cryptographic Provider", + MS_ENH_DSS_DH_PROV + = "Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider", + MS_DEF_DH_SCHANNEL_PROV = "Microsoft DH SChannel Cryptographic Provider", + MS_SCARD_PROV = "Microsoft Base Smart Card Crypto Provider"; + +static if (_WIN32_WINNT > 0x501) { +const TCHAR[] MS_ENH_RSA_AES_PROV + = "Microsoft Enhanced RSA and AES Cryptographic Provider"; +} else static if (_WIN32_WINNT == 0x501) { +const TCHAR[] MS_ENH_RSA_AES_PROV + = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"; +} + +ALG_ID GET_ALG_CLASS()(ALG_ID x) { return x & 0xE000; } +ALG_ID GET_ALG_TYPE ()(ALG_ID x) { return x & 0x1E00; } +ALG_ID GET_ALG_SID ()(ALG_ID x) { return x & 0x01FF; } + +enum : ALG_ID { + ALG_CLASS_ANY = 0, + ALG_CLASS_SIGNATURE = 0x2000, + ALG_CLASS_MSG_ENCRYPT = 0x4000, + ALG_CLASS_DATA_ENCRYPT = 0x6000, + ALG_CLASS_HASH = 0x8000, + ALG_CLASS_KEY_EXCHANGE = 0xA000, + ALG_CLASS_ALL = 0xE000 +} + +enum : ALG_ID { + ALG_TYPE_ANY = 0, + ALG_TYPE_DSS = 0x0200, + ALG_TYPE_RSA = 0x0400, + ALG_TYPE_BLOCK = 0x0600, + ALG_TYPE_STREAM = 0x0800, + ALG_TYPE_DH = 0x0A00, + ALG_TYPE_SECURECHANNEL = 0x0C00 +} + +enum : ALG_ID { + ALG_SID_ANY = 0, + ALG_SID_RSA_ANY = 0, + ALG_SID_RSA_PKCS, + ALG_SID_RSA_MSATWORK, + ALG_SID_RSA_ENTRUST, + ALG_SID_RSA_PGP, // = 4 + ALG_SID_DSS_ANY = 0, + ALG_SID_DSS_PKCS, + ALG_SID_DSS_DMS, // = 2 + ALG_SID_DES = 1, + ALG_SID_3DES = 3, + ALG_SID_DESX, + ALG_SID_IDEA, + ALG_SID_CAST, + ALG_SID_SAFERSK64, + ALG_SID_SAFERSK128, + ALG_SID_3DES_112, + ALG_SID_SKIPJACK, + ALG_SID_TEK, + ALG_SID_CYLINK_MEK, + ALG_SID_RC5, // = 13 + ALG_SID_RC2 = 2, + ALG_SID_RC4 = 1, + ALG_SID_SEAL = 2, + ALG_SID_MD2 = 1, + ALG_SID_MD4, + ALG_SID_MD5, + ALG_SID_SHA, + ALG_SID_MAC, + ALG_SID_RIPEMD, + ALG_SID_RIPEMD160, + ALG_SID_SSL3SHAMD5, + ALG_SID_HMAC, + ALG_SID_TLS1PRF, // = 10 + ALG_SID_AES_128 = 14, + ALG_SID_AES_192, + ALG_SID_AES_256, + ALG_SID_AES, // = 17 + ALG_SID_EXAMPLE = 80 +} + +enum : ALG_ID { + CALG_MD2 = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2, + CALG_MD4 = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4, + CALG_MD5 = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5, + CALG_SHA = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA, + CALG_SHA1 = CALG_SHA, + CALG_MAC = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MAC, + CALG_3DES = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | 3, + CALG_CYLINK_MEK = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | 12, + CALG_SKIPJACK = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | 10, + CALG_KEA_KEYX = ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_STREAM | ALG_TYPE_DSS | 4, + CALG_RSA_SIGN = ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY, + CALG_DSS_SIGN = ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY, + CALG_RSA_KEYX = ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY, + CALG_DES = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DES, + CALG_RC2 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC2, + CALG_RC4 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4, + CALG_SEAL = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_SEAL, + CALG_DH_EPHEM = ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_STREAM | ALG_TYPE_DSS + | ALG_SID_DSS_DMS, + CALG_DESX = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DESX, +// is undefined ALG_CLASS_DHASH in MinGW - presuming typo + CALG_TLS1PRF = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF, + CALG_AES_128 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_128, + CALG_AES_192 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_192, + CALG_AES_256 = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256, + CALG_AES = ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES, +} + +enum { + CRYPT_VERIFYCONTEXT = 0xF0000000, +} + +enum { + CRYPT_NEWKEYSET = 8, + CRYPT_DELETEKEYSET = 16, + CRYPT_MACHINE_KEYSET = 32, + CRYPT_SILENT = 64, +} + +enum { + CRYPT_EXPORTABLE = 1, + CRYPT_USER_PROTECTED = 2, + CRYPT_CREATE_SALT = 4, + CRYPT_UPDATE_KEY = 8, +} + +enum { + SIMPLEBLOB = 1, + PUBLICKEYBLOB = 6, + PRIVATEKEYBLOB = 7, + PLAINTEXTKEYBLOB = 8, + OPAQUEKEYBLOB = 9, + PUBLICKEYBLOBEX = 10, + SYMMETRICWRAPKEYBLOB = 11, +} + +enum { + AT_KEYEXCHANGE = 1, + AT_SIGNATURE = 2, +} + +enum { + CRYPT_USERDATA = 1, +} + +enum { + PKCS5_PADDING = 1, +} + +enum { + CRYPT_MODE_CBC = 1, + CRYPT_MODE_ECB = 2, + CRYPT_MODE_OFB = 3, + CRYPT_MODE_CFB = 4, + CRYPT_MODE_CTS = 5, + CRYPT_MODE_CBCI = 6, + CRYPT_MODE_CFBP = 7, + CRYPT_MODE_OFBP = 8, + CRYPT_MODE_CBCOFM = 9, + CRYPT_MODE_CBCOFMI = 10, +} + +enum { + CRYPT_ENCRYPT = 1, + CRYPT_DECRYPT = 2, + CRYPT_EXPORT = 4, + CRYPT_READ = 8, + CRYPT_WRITE = 16, + CRYPT_MAC = 32, +} + +enum { + HP_ALGID = 1, + HP_HASHVAL = 2, + HP_HASHSIZE = 4, + HP_HMAC_INFO = 5, +} + +enum { + CRYPT_FAILED = FALSE, + CRYPT_SUCCEED = TRUE, +} + +bool RCRYPT_SUCCEEDED()(BOOL r) { return r==CRYPT_SUCCEED; } +bool RCRYPT_FAILED()(BOOL r) { return r==CRYPT_FAILED; } + +enum { + PP_ENUMALGS = 1, + PP_ENUMCONTAINERS = 2, + PP_IMPTYPE = 3, + PP_NAME = 4, + PP_VERSION = 5, + PP_CONTAINER = 6, + PP_CHANGE_PASSWORD = 7, + PP_KEYSET_SEC_DESCR = 8, + PP_CERTCHAIN = 9, + PP_KEY_TYPE_SUBTYPE = 10, + PP_PROVTYPE = 16, + PP_KEYSTORAGE = 17, + PP_APPLI_CERT = 18, + PP_SYM_KEYSIZE = 19, + PP_SESSION_KEYSIZE = 20, + PP_UI_PROMPT = 21, + PP_ENUMALGS_EX = 22, + PP_ENUMMANDROOTS = 25, + PP_ENUMELECTROOTS = 26, + PP_KEYSET_TYPE = 27, + PP_ADMIN_PIN = 31, + PP_KEYEXCHANGE_PIN = 32, + PP_SIGNATURE_PIN = 33, + PP_SIG_KEYSIZE_INC = 34, + PP_KEYX_KEYSIZE_INC = 35, + PP_UNIQUE_CONTAINER = 36, + PP_SGC_INFO = 37, + PP_USE_HARDWARE_RNG = 38, + PP_KEYSPEC = 39, + PP_ENUMEX_SIGNING_PROT = 40, +} + +enum { + CRYPT_FIRST = 1, + CRYPT_NEXT = 2, +} + +enum { + CRYPT_IMPL_HARDWARE = 1, + CRYPT_IMPL_SOFTWARE = 2, + CRYPT_IMPL_MIXED = 3, + CRYPT_IMPL_UNKNOWN = 4, +} + +enum { + PROV_RSA_FULL = 1, + PROV_RSA_SIG = 2, + PROV_DSS = 3, + PROV_FORTEZZA = 4, + PROV_MS_MAIL = 5, + PROV_SSL = 6, + PROV_STT_MER = 7, + PROV_STT_ACQ = 8, + PROV_STT_BRND = 9, + PROV_STT_ROOT = 10, + PROV_STT_ISS = 11, + PROV_RSA_SCHANNEL = 12, + PROV_DSS_DH = 13, + PROV_EC_ECDSA_SIG = 14, + PROV_EC_ECNRA_SIG = 15, + PROV_EC_ECDSA_FULL = 16, + PROV_EC_ECNRA_FULL = 17, + PROV_DH_SCHANNEL = 18, + PROV_SPYRUS_LYNKS = 20, + PROV_RNG = 21, + PROV_INTEL_SEC = 22, + PROV_RSA_AES = 24, + MAXUIDLEN = 64, +} + +enum { + CUR_BLOB_VERSION = 2, +} + +enum { + X509_ASN_ENCODING = 1, + PKCS_7_ASN_ENCODING = 65536, +} + +enum { + CERT_V1 = 0, + CERT_V2 = 1, + CERT_V3 = 2, +} + +enum { + CERT_E_CHAINING = (-2146762486), + CERT_E_CN_NO_MATCH = (-2146762481), + CERT_E_EXPIRED = (-2146762495), + CERT_E_PURPOSE = (-2146762490), + CERT_E_REVOCATION_FAILURE = (-2146762482), + CERT_E_REVOKED = (-2146762484), + CERT_E_ROLE = (-2146762493), + CERT_E_UNTRUSTEDROOT = (-2146762487), + CERT_E_UNTRUSTEDTESTROOT = (-2146762483), + CERT_E_VALIDITYPERIODNESTING = (-2146762494), + CERT_E_WRONG_USAGE = (-2146762480), + CERT_E_PATHLENCONST = (-2146762492), + CERT_E_CRITICAL = (-2146762491), + CERT_E_ISSUERCHAINING = (-2146762489), + CERT_E_MALFORMED = (-2146762488), + CRYPT_E_REVOCATION_OFFLINE = (-2146885613), + CRYPT_E_REVOKED = (-2146885616), + TRUST_E_BASIC_CONSTRAINTS = (-2146869223), + TRUST_E_CERT_SIGNATURE = (-2146869244), + TRUST_E_FAIL = (-2146762485), +} + +enum { + CERT_TRUST_NO_ERROR = 0, + CERT_TRUST_IS_NOT_TIME_VALID = 1, + CERT_TRUST_IS_NOT_TIME_NESTED = 2, + CERT_TRUST_IS_REVOKED = 4, + CERT_TRUST_IS_NOT_SIGNATURE_VALID = 8, + CERT_TRUST_IS_NOT_VALID_FOR_USAGE = 16, + CERT_TRUST_IS_UNTRUSTED_ROOT = 32, + CERT_TRUST_REVOCATION_STATUS_UNKNOWN = 64, + CERT_TRUST_IS_CYCLIC = 128, + CERT_TRUST_IS_PARTIAL_CHAIN = 65536, + CERT_TRUST_CTL_IS_NOT_TIME_VALID = 131072, + CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID = 262144, + CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE = 524288, +} + +enum { + CERT_TRUST_HAS_EXACT_MATCH_ISSUER = 1, + CERT_TRUST_HAS_KEY_MATCH_ISSUER = 2, + CERT_TRUST_HAS_NAME_MATCH_ISSUER = 4, + CERT_TRUST_IS_SELF_SIGNED = 8, + CERT_TRUST_IS_COMPLEX_CHAIN = 65536, +} + +enum { + CERT_CHAIN_POLICY_BASE = cast(LPCSTR) 1, + CERT_CHAIN_POLICY_AUTHENTICODE = cast(LPCSTR) 2, + CERT_CHAIN_POLICY_AUTHENTICODE_TS = cast(LPCSTR) 3, + CERT_CHAIN_POLICY_SSL = cast(LPCSTR) 4, + CERT_CHAIN_POLICY_BASIC_CONSTRAINTS = cast(LPCSTR) 5, + CERT_CHAIN_POLICY_NT_AUTH = cast(LPCSTR) 6, +} + +enum { + USAGE_MATCH_TYPE_AND = 0, + USAGE_MATCH_TYPE_OR = 1, +} + +enum { + CERT_SIMPLE_NAME_STR = 1, + CERT_OID_NAME_STR = 2, + CERT_X500_NAME_STR = 3, +} +enum { + CERT_NAME_STR_SEMICOLON_FLAG = 1073741824, + CERT_NAME_STR_CRLF_FLAG = 134217728, + CERT_NAME_STR_NO_PLUS_FLAG = 536870912, + CERT_NAME_STR_NO_QUOTING_FLAG = 268435456, + CERT_NAME_STR_REVERSE_FLAG = 33554432, + CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG = 131072, +} + +enum { + CERT_FIND_ANY = 0, + CERT_FIND_CERT_ID = 1048576, + CERT_FIND_CTL_USAGE = 655360, + CERT_FIND_ENHKEY_USAGE = 655360, + CERT_FIND_EXISTING = 851968, + CERT_FIND_HASH = 65536, + CERT_FIND_ISSUER_ATTR = 196612, + CERT_FIND_ISSUER_NAME = 131076, + CERT_FIND_ISSUER_OF = 786432, + CERT_FIND_KEY_IDENTIFIER = 983040, + CERT_FIND_KEY_SPEC = 589824, + CERT_FIND_MD5_HASH = 262144, + CERT_FIND_PROPERTY = 327680, + CERT_FIND_PUBLIC_KEY = 393216, + CERT_FIND_SHA1_HASH = 65536, + CERT_FIND_SIGNATURE_HASH = 917504, + CERT_FIND_SUBJECT_ATTR = 196615, + CERT_FIND_SUBJECT_CERT = 720896, + CERT_FIND_SUBJECT_NAME = 131079, + CERT_FIND_SUBJECT_STR_A = 458759, + CERT_FIND_SUBJECT_STR_W = 524295, + CERT_FIND_ISSUER_STR_A = 458756, + CERT_FIND_ISSUER_STR_W = 524292, +} + +enum { + CERT_FIND_OR_ENHKEY_USAGE_FLAG = 16, + CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG = 1, + CERT_FIND_NO_ENHKEY_USAGE_FLAG = 8, + CERT_FIND_VALID_ENHKEY_USAGE_FLAG = 32, + CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG = 2, +} + +enum { + CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG = 2, + CERT_UNICODE_IS_RDN_ATTRS_FLAG = 1, + CERT_CHAIN_FIND_BY_ISSUER = 1, +} + +enum { + CERT_CHAIN_FIND_BY_ISSUER_COMPARE_KEY_FLAG = 1, + CERT_CHAIN_FIND_BY_ISSUER_COMPLEX_CHAIN_FLAG = 2, + CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG = 4, + CERT_CHAIN_FIND_BY_ISSUER_LOCAL_MACHINE_FLAG = 8, + CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG = 16384, + CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG = 32768, +} + +enum { + CERT_STORE_PROV_SYSTEM = 10, + CERT_SYSTEM_STORE_LOCAL_MACHINE = 131072, +} + +enum { + szOID_PKIX_KP_SERVER_AUTH = "4235600", + szOID_SERVER_GATED_CRYPTO = "4235658", + szOID_SGC_NETSCAPE = "2.16.840.1.113730.4.1", + szOID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2", +} + +enum { + CRYPT_NOHASHOID = 0x00000001, + CRYPT_NO_SALT = 0x10, + CRYPT_PREGEN = 0x40, +} + +enum { + CRYPT_RECIPIENT = 0x10, + CRYPT_INITIATOR = 0x40, + CRYPT_ONLINE = 0x80, + CRYPT_SF = 0x100, + CRYPT_CREATE_IV = 0x200, + CRYPT_KEK = 0x400, + CRYPT_DATA_KEY = 0x800, + CRYPT_VOLATILE = 0x1000, + CRYPT_SGCKEY = 0x2000, +} + +enum { + KP_IV = 0x00000001, + KP_SALT = 0x00000002, + KP_PADDING = 0x00000003, + KP_MODE = 0x00000004, + KP_MODE_BITS = 0x00000005, + KP_PERMISSIONS = 0x00000006, + KP_ALGID = 0x00000007, + KP_BLOCKLEN = 0x00000008, + KP_KEYLEN = 0x00000009, + KP_SALT_EX = 0x0000000a, + KP_P = 0x0000000b, + KP_G = 0x0000000c, + KP_Q = 0x0000000d, + KP_X = 0x0000000e, + KP_Y = 0x0000000f, + KP_RA = 0x00000010, + KP_RB = 0x00000011, + KP_INFO = 0x00000012, + KP_EFFECTIVE_KEYLEN = 0x00000013, + KP_SCHANNEL_ALG = 0x00000014, + KP_PUB_PARAMS = 0x00000027, +} + +enum { + CRYPT_FLAG_PCT1 = 0x0001, + CRYPT_FLAG_SSL2 = 0x0002, + CRYPT_FLAG_SSL3 = 0x0004, + CRYPT_FLAG_TLS1 = 0x0008, + CRYPT_FLAG_IPSEC = 0x0010, + CRYPT_FLAG_SIGNING = 0x0020, +} + +enum { + SCHANNEL_MAC_KEY = 0x00000000, + SCHANNEL_ENC_KEY = 0x00000001, +} + +enum { + INTERNATIONAL_USAGE = 0x00000001, +} + + +alias UINT ALG_ID; +alias ULONG_PTR HCRYPTPROV, HCRYPTKEY, HCRYPTHASH; +alias PVOID HCERTSTORE, HCRYPTMSG, HCERTCHAINENGINE; + +struct VTableProvStruc { + FARPROC FuncVerifyImage; +} +alias VTableProvStruc* PVTableProvStruc; + +struct _CRYPTOAPI_BLOB { + DWORD cbData; + BYTE* pbData; +} +alias _CRYPTOAPI_BLOB CRYPT_INTEGER_BLOB, CRYPT_UINT_BLOB, + CRYPT_OBJID_BLOB, CERT_NAME_BLOB, CERT_RDN_VALUE_BLOB, CERT_BLOB, + CRL_BLOB, DATA_BLOB, CRYPT_DATA_BLOB, CRYPT_HASH_BLOB, + CRYPT_DIGEST_BLOB, CRYPT_DER_BLOB, CRYPT_ATTR_BLOB; +alias _CRYPTOAPI_BLOB* PCRYPT_INTEGER_BLOB, PCRYPT_UINT_BLOB, + PCRYPT_OBJID_BLOB, PCERT_NAME_BLOB, PCERT_RDN_VALUE_BLOB, PCERT_BLOB, + PCRL_BLOB, PDATA_BLOB, PCRYPT_DATA_BLOB, PCRYPT_HASH_BLOB, + PCRYPT_DIGEST_BLOB, PCRYPT_DER_BLOB, PCRYPT_ATTR_BLOB; + +// not described in SDK; has the same layout as HTTPSPolicyCallbackData +struct SSL_EXTRA_CERT_CHAIN_POLICY_PARA { + DWORD cbStruct; + DWORD dwAuthType; + DWORD fdwChecks; + LPWSTR pwszServerName; +} +alias SSL_EXTRA_CERT_CHAIN_POLICY_PARA HTTPSPolicyCallbackData; +alias SSL_EXTRA_CERT_CHAIN_POLICY_PARA* PSSL_EXTRA_CERT_CHAIN_POLICY_PARA, + PHTTPSPolicyCallbackData; + +/* #if (_WIN32_WINNT>=0x500) */ +struct CERT_CHAIN_POLICY_PARA { + DWORD cbSize = CERT_CHAIN_POLICY_PARA.sizeof; + DWORD dwFlags; + void* pvExtraPolicyPara; +} +alias CERT_CHAIN_POLICY_PARA* PCERT_CHAIN_POLICY_PARA; + +struct CERT_CHAIN_POLICY_STATUS { + DWORD cbSize = CERT_CHAIN_POLICY_STATUS.sizeof; + DWORD dwError; + LONG lChainIndex; + LONG lElementIndex; + void* pvExtraPolicyStatus; +} +alias CERT_CHAIN_POLICY_STATUS* PCERT_CHAIN_POLICY_STATUS; +/* #endif */ + +struct CRYPT_ALGORITHM_IDENTIFIER { + LPSTR pszObjId; + CRYPT_OBJID_BLOB Parameters; +} +alias CRYPT_ALGORITHM_IDENTIFIER* PCRYPT_ALGORITHM_IDENTIFIER; + +struct CRYPT_BIT_BLOB { + DWORD cbData; + BYTE* pbData; + DWORD cUnusedBits; +} +alias CRYPT_BIT_BLOB* PCRYPT_BIT_BLOB; + +struct CERT_PUBLIC_KEY_INFO { + CRYPT_ALGORITHM_IDENTIFIER Algorithm; + CRYPT_BIT_BLOB PublicKey; +} +alias CERT_PUBLIC_KEY_INFO* PCERT_PUBLIC_KEY_INFO; + +struct CERT_EXTENSION { + LPSTR pszObjId; + BOOL fCritical; + CRYPT_OBJID_BLOB Value; +} +alias CERT_EXTENSION* PCERT_EXTENSION; + +struct CERT_INFO { + DWORD dwVersion; + CRYPT_INTEGER_BLOB SerialNumber; + CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + CERT_NAME_BLOB Issuer; + FILETIME NotBefore; + FILETIME NotAfter; + CERT_NAME_BLOB Subject; + CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; + CRYPT_BIT_BLOB IssuerUniqueId; + CRYPT_BIT_BLOB SubjectUniqueId; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} +alias CERT_INFO* PCERT_INFO; + +struct CERT_CONTEXT { + DWORD dwCertEncodingType; + BYTE* pbCertEncoded; + DWORD cbCertEncoded; + PCERT_INFO pCertInfo; + HCERTSTORE hCertStore; +} +alias CERT_CONTEXT* PCERT_CONTEXT; +alias const(CERT_CONTEXT)* PCCERT_CONTEXT; + +struct CTL_USAGE { + DWORD cUsageIdentifier; + LPSTR* rgpszUsageIdentifier; +} +alias CTL_USAGE CERT_ENHKEY_USAGE; +alias CTL_USAGE* PCTRL_USAGE, PCERT_ENHKEY_USAGE; + +struct CERT_USAGE_MATCH { + DWORD dwType; + CERT_ENHKEY_USAGE Usage; +} +alias CERT_USAGE_MATCH* PCERT_USAGE_MATCH; +/* #if (_WIN32_WINNT>=0x500) */ + +struct CERT_CHAIN_PARA { + DWORD cbSize = CERT_CHAIN_PARA.sizeof; + CERT_USAGE_MATCH RequestedUsage; +//#if CERT_CHAIN_PARA_HAS_EXTRA_FIELDS + CERT_USAGE_MATCH RequestedIssuancePolicy; + DWORD dwUrlRetrievalTimeout; + BOOL fCheckRevocationFreshnessTime; + DWORD dwRevocationFreshnessTime; +//#endif +} +alias CERT_CHAIN_PARA* PCERT_CHAIN_PARA; + +extern (Windows) alias BOOL function(PCCERT_CONTEXT, void*) + PFN_CERT_CHAIN_FIND_BY_ISSUER_CALLBACK; + +struct CERT_CHAIN_FIND_BY_ISSUER_PARA { + DWORD cbSize = CERT_CHAIN_FIND_BY_ISSUER_PARA.sizeof; + LPCSTR pszUsageIdentifier; + DWORD dwKeySpec; + DWORD dwAcquirePrivateKeyFlags; + DWORD cIssuer; + CERT_NAME_BLOB* rgIssuer; + PFN_CERT_CHAIN_FIND_BY_ISSUER_CALLBACK pfnFIndCallback; + void* pvFindArg; + DWORD* pdwIssuerChainIndex; + DWORD* pdwIssuerElementIndex; +} +alias CERT_CHAIN_FIND_BY_ISSUER_PARA* PCERT_CHAIN_FIND_BY_ISSUER_PARA; +/* #endif */ + +struct CERT_TRUST_STATUS { + DWORD dwErrorStatus; + DWORD dwInfoStatus; +} +alias CERT_TRUST_STATUS* PCERT_TRUST_STATUS; + +struct CRL_ENTRY { + CRYPT_INTEGER_BLOB SerialNumber; + FILETIME RevocationDate; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} +alias CRL_ENTRY* PCRL_ENTRY; + +struct CRL_INFO { + DWORD dwVersion; + CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + CERT_NAME_BLOB Issuer; + FILETIME ThisUpdate; + FILETIME NextUpdate; + DWORD cCRLEntry; + PCRL_ENTRY rgCRLEntry; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} +alias CRL_INFO* PCRL_INFO; + +struct CRL_CONTEXT { + DWORD dwCertEncodingType; + BYTE* pbCrlEncoded; + DWORD cbCrlEncoded; + PCRL_INFO pCrlInfo; + HCERTSTORE hCertStore; +} +alias CRL_CONTEXT* PCRL_CONTEXT; +alias const(CRL_CONTEXT)* PCCRL_CONTEXT; + +struct CERT_REVOCATION_CRL_INFO { + DWORD cbSize = CERT_REVOCATION_CRL_INFO.sizeof; + PCCRL_CONTEXT pBaseCRLContext; + PCCRL_CONTEXT pDeltaCRLContext; + PCRL_ENTRY pCrlEntry; + BOOL fDeltaCrlEntry; +} +alias CERT_REVOCATION_CRL_INFO* PCERT_REVOCATION_CRL_INFO; + +struct CERT_REVOCATION_INFO { + DWORD cbSize = CERT_REVOCATION_INFO.sizeof; + DWORD dwRevocationResult; + LPCSTR pszRevocationOid; + LPVOID pvOidSpecificInfo; + BOOL fHasFreshnessTime; + DWORD dwFreshnessTime; + PCERT_REVOCATION_CRL_INFO pCrlInfo; +} +alias CERT_REVOCATION_INFO* PCERT_REVOCATION_INFO; + +/* #if (_WIN32_WINNT>=0x500) */ +struct CERT_CHAIN_ELEMENT { + DWORD cbSize = CERT_CHAIN_ELEMENT.sizeof; + PCCERT_CONTEXT pCertContext; + CERT_TRUST_STATUS TrustStatus; + PCERT_REVOCATION_INFO pRevocationInfo; + PCERT_ENHKEY_USAGE pIssuanceUsage; + PCERT_ENHKEY_USAGE pApplicationUsage; +} +alias CERT_CHAIN_ELEMENT* PCERT_CHAIN_ELEMENT; +/* #endif */ + +struct CRYPT_ATTRIBUTE { + LPSTR pszObjId; + DWORD cValue; + PCRYPT_ATTR_BLOB rgValue; +} +alias CRYPT_ATTRIBUTE* PCRYPT_ATTRIBUTE; + +struct CTL_ENTRY { + CRYPT_DATA_BLOB SubjectIdentifier; + DWORD cAttribute; + PCRYPT_ATTRIBUTE rgAttribute; +} +alias CTL_ENTRY* PCTL_ENTRY; + +struct CTL_INFO { + DWORD dwVersion; + CTL_USAGE SubjectUsage; + CRYPT_DATA_BLOB ListIdentifier; + CRYPT_INTEGER_BLOB SequenceNumber; + FILETIME ThisUpdate; + FILETIME NextUpdate; + CRYPT_ALGORITHM_IDENTIFIER SubjectAlgorithm; + DWORD cCTLEntry; + PCTL_ENTRY rgCTLEntry; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} +alias CTL_INFO* PCTL_INFO; + +struct CTL_CONTEXT { + DWORD dwMsgAndCertEncodingType; + BYTE* pbCtlEncoded; + DWORD cbCtlEncoded; + PCTL_INFO pCtlInfo; + HCERTSTORE hCertStore; + HCRYPTMSG hCryptMsg; + BYTE* pbCtlContent; + DWORD cbCtlContent; +} +alias CTL_CONTEXT* PCTL_CONTEXT; +alias const(CTL_CONTEXT)* PCCTL_CONTEXT; + +struct CERT_TRUST_LIST_INFO { + DWORD cbSize = CERT_TRUST_LIST_INFO.sizeof; + PCTL_ENTRY pCtlEntry; + PCCTL_CONTEXT pCtlContext; +} +alias CERT_TRUST_LIST_INFO* PCERT_TRUST_LIST_INFO; + +struct CERT_SIMPLE_CHAIN { + DWORD cbSize = CERT_SIMPLE_CHAIN.sizeof; + CERT_TRUST_STATUS TrustStatus; + DWORD cElement; + PCERT_CHAIN_ELEMENT* rgpElement; + PCERT_TRUST_LIST_INFO pTrustListInfo; + BOOL fHasRevocationFreshnessTime; + DWORD dwRevocationFreshnessTime; +} +alias CERT_SIMPLE_CHAIN* PCERT_SIMPLE_CHAIN; + +/* #if (_WIN32_WINNT>=0x500) */ +alias const(CERT_CHAIN_CONTEXT)* PCCERT_CHAIN_CONTEXT; +struct CERT_CHAIN_CONTEXT { + DWORD cbSize = CERT_CHAIN_CONTEXT.sizeof; + CERT_TRUST_STATUS TrustStatus; + DWORD cChain; + PCERT_SIMPLE_CHAIN* rgpChain; + DWORD cLowerQualityChainContext; + PCCERT_CHAIN_CONTEXT* rgpLowerQualityChainContext; + BOOL fHasRevocationFreshnessTime; + DWORD dwRevocationFreshnessTime; +} +alias CERT_CHAIN_CONTEXT* PCERT_CHAIN_CONTEXT; +/* #endif */ + +struct PROV_ENUMALGS { + ALG_ID aiAlgid; + DWORD dwBitLen; + DWORD dwNameLen; + CHAR[20] szName = 0; +} + +struct PUBLICKEYSTRUC { + BYTE bType; + BYTE bVersion; + WORD reserved; + ALG_ID aiKeyAlg; +} +alias PUBLICKEYSTRUC BLOBHEADER; + +struct RSAPUBKEY { + DWORD magic; + DWORD bitlen; + DWORD pubexp; +} + +struct HMAC_INFO { + ALG_ID HashAlgid; + BYTE* pbInnerString; + DWORD cbInnerString; + BYTE* pbOuterString; + DWORD cbOuterString; +} +alias HMAC_INFO* PHMAC_INFO; + +extern (Windows) nothrow @nogc { + BOOL CertCloseStore(HCERTSTORE, DWORD); + BOOL CertGetCertificateChain(HCERTCHAINENGINE, PCCERT_CONTEXT, LPFILETIME, + HCERTSTORE, PCERT_CHAIN_PARA, DWORD, LPVOID, PCCERT_CHAIN_CONTEXT*); + BOOL CertVerifyCertificateChainPolicy(LPCSTR, PCCERT_CHAIN_CONTEXT, + PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS); + void CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT); + DWORD CertNameToStrA(DWORD, PCERT_NAME_BLOB, DWORD, LPSTR, DWORD); + DWORD CertNameToStrW(DWORD, PCERT_NAME_BLOB, DWORD, LPWSTR, DWORD); + HCERTSTORE CertOpenSystemStoreA(HCRYPTPROV, LPCSTR); + HCERTSTORE CertOpenSystemStoreW(HCRYPTPROV, LPCWSTR); + HCERTSTORE CertOpenStore(LPCSTR, DWORD, HCRYPTPROV, DWORD, const(void)*); + PCCERT_CONTEXT CertFindCertificateInStore(HCERTSTORE, DWORD, DWORD, DWORD, +const(void)*, PCCERT_CONTEXT); + BOOL CertFreeCertificateContext(PCCERT_CONTEXT); + PCCERT_CONTEXT CertGetIssuerCertificateFromStore(HCERTSTORE, + PCCERT_CONTEXT, PCCERT_CONTEXT, DWORD*); + PCCERT_CHAIN_CONTEXT CertFindChainInStore(HCERTSTORE, DWORD, DWORD, DWORD, +const(void)*, PCCERT_CHAIN_CONTEXT); + + BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD); + BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD); + BOOL CryptContextAddRef(HCRYPTPROV, DWORD*, DWORD); + BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR); + BOOL CryptGenKey(HCRYPTPROV, ALG_ID, DWORD, HCRYPTKEY*); + BOOL CryptDeriveKey(HCRYPTPROV, ALG_ID, HCRYPTHASH, DWORD, HCRYPTKEY*); + BOOL CryptDestroyKey(HCRYPTKEY); + static if (_WIN32_WINNT >= 0x500) { + BOOL CryptDuplicateHash(HCRYPTHASH, DWORD*, DWORD, HCRYPTHASH*); + BOOL CryptDuplicateKey(HCRYPTKEY, DWORD*, DWORD, HCRYPTKEY*); + } + BOOL CryptSetKeyParam(HCRYPTKEY, DWORD, PBYTE, DWORD); + BOOL CryptGetKeyParam(HCRYPTKEY, DWORD, PBYTE, PDWORD, DWORD); + BOOL CryptSetHashParam(HCRYPTHASH, DWORD, PBYTE, DWORD); + BOOL CryptGetHashParam(HCRYPTHASH, DWORD, PBYTE, PDWORD, DWORD); + BOOL CryptSetProvParam(HCRYPTPROV, DWORD, PBYTE, DWORD); + BOOL CryptGetProvParam(HCRYPTPROV, DWORD, PBYTE, PDWORD, DWORD); + BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE); + BOOL CryptGetUserKey(HCRYPTPROV, DWORD, HCRYPTKEY*); + BOOL CryptExportKey(HCRYPTKEY, HCRYPTKEY, DWORD, DWORD, PBYTE, PDWORD); + BOOL CryptImportKey(HCRYPTPROV, PBYTE, DWORD, HCRYPTKEY, DWORD, + HCRYPTKEY*); + BOOL CryptEncrypt(HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, PBYTE, PDWORD, + DWORD); + BOOL CryptDecrypt(HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, PBYTE, PDWORD); + BOOL CryptCreateHash(HCRYPTPROV, ALG_ID, HCRYPTKEY, DWORD, HCRYPTHASH*); + BOOL CryptHashData(HCRYPTHASH, PBYTE, DWORD, DWORD); + BOOL CryptHashSessionKey(HCRYPTHASH, HCRYPTKEY, DWORD); + BOOL CryptGetHashValue(HCRYPTHASH, DWORD, PBYTE, PDWORD); + BOOL CryptDestroyHash(HCRYPTHASH); + BOOL CryptSignHashA(HCRYPTHASH, DWORD, LPCSTR, DWORD, PBYTE, PDWORD); + BOOL CryptSignHashW(HCRYPTHASH, DWORD, LPCWSTR, DWORD, PBYTE, PDWORD); + BOOL CryptVerifySignatureA(HCRYPTHASH, PBYTE, DWORD, HCRYPTKEY, LPCSTR, + DWORD); + BOOL CryptVerifySignatureW(HCRYPTHASH, PBYTE, DWORD, HCRYPTKEY, LPCWSTR, + DWORD); + BOOL CryptSetProviderA(LPCSTR, DWORD); + BOOL CryptSetProviderW(LPCWSTR, DWORD); +} + +version (Unicode) { + alias CertNameToStrW CertNameToStr; + alias CryptAcquireContextW CryptAcquireContext; + alias CryptSignHashW CryptSignHash; + alias CryptVerifySignatureW CryptVerifySignature; + alias CryptSetProviderW CryptSetProvider; + alias CertOpenSystemStoreW CertOpenSystemStore; + /+alias CERT_FIND_SUBJECT_STR_W CERT_FIND_SUBJECT_STR; + alias CERT_FIND_ISSUER_STR_W CERT_FIND_ISSUER_STR;+/ +} else { + alias CertNameToStrA CertNameToStr; + alias CryptAcquireContextA CryptAcquireContext; + alias CryptSignHashA CryptSignHash; + alias CryptVerifySignatureA CryptVerifySignature; + alias CryptSetProviderA CryptSetProvider; + alias CertOpenSystemStoreA CertOpenSystemStore; + /+alias CERT_FIND_SUBJECT_STR_A CERT_FIND_SUBJECT_STR; + alias CERT_FIND_ISSUER_STR_A CERT_FIND_ISSUER_STR;+/ +} diff --git a/src/urt/internal/sys/windows/windef.d b/src/urt/internal/sys/windows/windef.d new file mode 100644 index 0000000..20767e1 --- /dev/null +++ b/src/urt/internal/sys/windows/windef.d @@ -0,0 +1,151 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * Authors: Stewart Gordon + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_windef.d) + */ +module urt.internal.sys.windows.windef; +version (Windows): + +public import urt.internal.sys.windows.winnt; +import urt.internal.sys.windows.w32api; + +enum size_t MAX_PATH = 260; + +pure nothrow @nogc { + ushort MAKEWORD(ubyte a, ubyte b) { + return cast(ushort) ((b << 8) | a); + } + + ushort MAKEWORD(ushort a, ushort b) { + assert((a & 0xFF00) == 0); + assert((b & 0xFF00) == 0); + return MAKEWORD(cast(ubyte)a, cast(ubyte)b); + } + + uint MAKELONG(ushort a, ushort b) { + return cast(uint) ((b << 16) | a); + } + + uint MAKELONG(uint a, uint b) { + assert((a & 0xFFFF0000) == 0); + assert((b & 0xFFFF0000) == 0); + return MAKELONG(cast(ushort)a, cast(ushort)b); + } + + ushort LOWORD(ulong l) { + return cast(ushort) l; + } + + ushort HIWORD(ulong l) { + return cast(ushort) (l >>> 16); + } + + ubyte LOBYTE(ushort w) { + return cast(ubyte) w; + } + + ubyte HIBYTE(ushort w) { + return cast(ubyte) (w >>> 8); + } +} + +enum NULL = null; +static assert (is(typeof({ + void test(int* p) {} + test(NULL); +}))); + +alias ubyte BYTE; +alias ubyte* PBYTE, LPBYTE; +alias ushort USHORT, WORD, ATOM; +alias ushort* PUSHORT, PWORD, LPWORD; +alias uint ULONG, DWORD, UINT, COLORREF; +alias uint* PULONG, PDWORD, LPDWORD, PUINT, LPUINT, LPCOLORREF; +alias int WINBOOL, BOOL, INT, LONG, HFILE, HRESULT; +alias int* PWINBOOL, LPWINBOOL, PBOOL, LPBOOL, PINT, LPINT, LPLONG; +alias float FLOAT; +alias float* PFLOAT; +alias const(void)* PCVOID, LPCVOID; + +alias UINT_PTR WPARAM; +alias LONG_PTR LPARAM, LRESULT; + +alias HHOOK = HANDLE; +alias HGLOBAL = HANDLE; +alias HLOCAL = HANDLE; +alias GLOBALHANDLE = HANDLE; +alias LOCALHANDLE = HANDLE; +alias HGDIOBJ = HANDLE; +alias HACCEL = HANDLE; +alias HBITMAP = HGDIOBJ; +alias HBRUSH = HGDIOBJ; +alias HCOLORSPACE = HANDLE; +alias HDC = HANDLE; +alias HGLRC = HANDLE; +alias HDESK = HANDLE; +alias HENHMETAFILE = HANDLE; +alias HFONT = HGDIOBJ; +alias HICON = HANDLE; +alias HINSTANCE = HANDLE; +alias HKEY = HANDLE; +alias HMENU = HANDLE; +alias HMETAFILE = HANDLE; +alias HMODULE = HANDLE; +alias HMONITOR = HANDLE; +alias HPALETTE = HANDLE; +alias HPEN = HGDIOBJ; +alias HRGN = HGDIOBJ; +alias HRSRC = HANDLE; +alias HSTR = HANDLE; +alias HTASK = HANDLE; +alias HWND = HANDLE; +alias HWINSTA = HANDLE; +alias HKL = HANDLE; +alias HCURSOR = HANDLE; +alias HKEY* PHKEY; + +static if (_WIN32_WINNT >= 0x500) { + alias HTERMINAL = HANDLE; + alias HWINEVENTHOOK = HANDLE; +} + +alias extern (Windows) INT_PTR function() nothrow FARPROC, NEARPROC, PROC; + +struct RECT { + LONG left; + LONG top; + LONG right; + LONG bottom; +} +alias RECT RECTL; +alias RECT* PRECT, NPRECT, LPRECT, PRECTL, LPRECTL; +alias const(RECT)* LPCRECT, LPCRECTL; + +struct POINT { + LONG x; + LONG y; +} +alias POINT POINTL; +alias POINT* PPOINT, NPPOINT, LPPOINT, PPOINTL, LPPOINTL; + +struct SIZE { + LONG cx; + LONG cy; +} +alias SIZE SIZEL; +alias SIZE* PSIZE, LPSIZE, PSIZEL, LPSIZEL; + +struct POINTS { + SHORT x; + SHORT y; +} +alias POINTS* PPOINTS, LPPOINTS; + +enum : BOOL { + FALSE = 0, + TRUE = 1 +} diff --git a/src/urt/internal/sys/windows/winerror.d b/src/urt/internal/sys/windows/winerror.d new file mode 100644 index 0000000..6178028 --- /dev/null +++ b/src/urt/internal/sys/windows/winerror.d @@ -0,0 +1,2312 @@ +/** + * Windows API header module + * + * Translated from MinGW Windows headers + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_winerror.d) + */ +module urt.internal.sys.windows.winerror; +version (Windows): + +/* Comments from the Mingw header: + * WAIT_TIMEOUT is also defined in winbase.h + */ + +import urt.internal.sys.windows.windef; + +alias int SCODE; // was in urt.internal.sys.windows.wtypes. + +enum : uint { + ERROR_SUCCESS = 0, + NO_ERROR = 0, + ERROR_INVALID_FUNCTION, + ERROR_FILE_NOT_FOUND, + ERROR_PATH_NOT_FOUND, + ERROR_TOO_MANY_OPEN_FILES, + ERROR_ACCESS_DENIED, + ERROR_INVALID_HANDLE, + ERROR_ARENA_TRASHED, + ERROR_NOT_ENOUGH_MEMORY, + ERROR_INVALID_BLOCK, + ERROR_BAD_ENVIRONMENT, + ERROR_BAD_FORMAT, + ERROR_INVALID_ACCESS, + ERROR_INVALID_DATA, + ERROR_OUTOFMEMORY, + ERROR_INVALID_DRIVE, + ERROR_CURRENT_DIRECTORY, + ERROR_NOT_SAME_DEVICE, + ERROR_NO_MORE_FILES, + ERROR_WRITE_PROTECT, + ERROR_BAD_UNIT, + ERROR_NOT_READY, + ERROR_BAD_COMMAND, + ERROR_CRC, + ERROR_BAD_LENGTH, + ERROR_SEEK, + ERROR_NOT_DOS_DISK, + ERROR_SECTOR_NOT_FOUND, + ERROR_OUT_OF_PAPER, + ERROR_WRITE_FAULT, + ERROR_READ_FAULT, + ERROR_GEN_FAILURE, + ERROR_SHARING_VIOLATION, + ERROR_LOCK_VIOLATION, + ERROR_WRONG_DISK, // = 34 + ERROR_SHARING_BUFFER_EXCEEDED = 36, + ERROR_HANDLE_EOF = 38, + ERROR_HANDLE_DISK_FULL, // = 39 + ERROR_NOT_SUPPORTED = 50, + ERROR_REM_NOT_LIST, + ERROR_DUP_NAME, + ERROR_BAD_NETPATH, + ERROR_NETWORK_BUSY, + ERROR_DEV_NOT_EXIST, + ERROR_TOO_MANY_CMDS, + ERROR_ADAP_HDW_ERR, + ERROR_BAD_NET_RESP, + ERROR_UNEXP_NET_ERR, + ERROR_BAD_REM_ADAP, + ERROR_PRINTQ_FULL, + ERROR_NO_SPOOL_SPACE, + ERROR_PRINT_CANCELLED, + ERROR_NETNAME_DELETED, + ERROR_NETWORK_ACCESS_DENIED, + ERROR_BAD_DEV_TYPE, + ERROR_BAD_NET_NAME, + ERROR_TOO_MANY_NAMES, + ERROR_TOO_MANY_SESS, + ERROR_SHARING_PAUSED, + ERROR_REQ_NOT_ACCEP, + ERROR_REDIR_PAUSED, // = 72 + ERROR_FILE_EXISTS = 80, + ERROR_CANNOT_MAKE = 82, + ERROR_FAIL_I24, + ERROR_OUT_OF_STRUCTURES, + ERROR_ALREADY_ASSIGNED, + ERROR_INVALID_PASSWORD, + ERROR_INVALID_PARAMETER, + ERROR_NET_WRITE_FAULT, + ERROR_NO_PROC_SLOTS, // = 89 + ERROR_TOO_MANY_SEMAPHORES = 100, + ERROR_EXCL_SEM_ALREADY_OWNED, + ERROR_SEM_IS_SET, + ERROR_TOO_MANY_SEM_REQUESTS, + ERROR_INVALID_AT_INTERRUPT_TIME, + ERROR_SEM_OWNER_DIED, + ERROR_SEM_USER_LIMIT, + ERROR_DISK_CHANGE, + ERROR_DRIVE_LOCKED, + ERROR_BROKEN_PIPE, + ERROR_OPEN_FAILED, + ERROR_BUFFER_OVERFLOW, + ERROR_DISK_FULL, + ERROR_NO_MORE_SEARCH_HANDLES, + ERROR_INVALID_TARGET_HANDLE, // = 114 + ERROR_INVALID_CATEGORY = 117, + ERROR_INVALID_VERIFY_SWITCH, + ERROR_BAD_DRIVER_LEVEL, + ERROR_CALL_NOT_IMPLEMENTED, + ERROR_SEM_TIMEOUT, + ERROR_INSUFFICIENT_BUFFER, + ERROR_INVALID_NAME, + ERROR_INVALID_LEVEL, + ERROR_NO_VOLUME_LABEL, + ERROR_MOD_NOT_FOUND, + ERROR_PROC_NOT_FOUND, + ERROR_WAIT_NO_CHILDREN, + ERROR_CHILD_NOT_COMPLETE, + ERROR_DIRECT_ACCESS_HANDLE, + ERROR_NEGATIVE_SEEK, + ERROR_SEEK_ON_DEVICE, + ERROR_IS_JOIN_TARGET, + ERROR_IS_JOINED, + ERROR_IS_SUBSTED, + ERROR_NOT_JOINED, + ERROR_NOT_SUBSTED, + ERROR_JOIN_TO_JOIN, + ERROR_SUBST_TO_SUBST, + ERROR_JOIN_TO_SUBST, + ERROR_SUBST_TO_JOIN, + ERROR_BUSY_DRIVE, + ERROR_SAME_DRIVE, + ERROR_DIR_NOT_ROOT, + ERROR_DIR_NOT_EMPTY, + ERROR_IS_SUBST_PATH, + ERROR_IS_JOIN_PATH, + ERROR_PATH_BUSY, + ERROR_IS_SUBST_TARGET, + ERROR_SYSTEM_TRACE, + ERROR_INVALID_EVENT_COUNT, + ERROR_TOO_MANY_MUXWAITERS, + ERROR_INVALID_LIST_FORMAT, + ERROR_LABEL_TOO_LONG, + ERROR_TOO_MANY_TCBS, + ERROR_SIGNAL_REFUSED, + ERROR_DISCARDED, + ERROR_NOT_LOCKED, + ERROR_BAD_THREADID_ADDR, + ERROR_BAD_ARGUMENTS, + ERROR_BAD_PATHNAME, + ERROR_SIGNAL_PENDING, // = 162 + ERROR_MAX_THRDS_REACHED = 164, + ERROR_LOCK_FAILED = 167, + ERROR_BUSY = 170, + ERROR_CANCEL_VIOLATION = 173, + ERROR_ATOMIC_LOCKS_NOT_SUPPORTED, // = 174 + ERROR_INVALID_SEGMENT_NUMBER = 180, + ERROR_INVALID_ORDINAL = 182, + ERROR_ALREADY_EXISTS, // = 183 + ERROR_INVALID_FLAG_NUMBER = 186, + ERROR_SEM_NOT_FOUND, + ERROR_INVALID_STARTING_CODESEG, + ERROR_INVALID_STACKSEG, + ERROR_INVALID_MODULETYPE, + ERROR_INVALID_EXE_SIGNATURE, + ERROR_EXE_MARKED_INVALID, + ERROR_BAD_EXE_FORMAT, + ERROR_ITERATED_DATA_EXCEEDS_64k, + ERROR_INVALID_MINALLOCSIZE, + ERROR_DYNLINK_FROM_INVALID_RING, + ERROR_IOPL_NOT_ENABLED, + ERROR_INVALID_SEGDPL, + ERROR_AUTODATASEG_EXCEEDS_64k, + ERROR_RING2SEG_MUST_BE_MOVABLE, + ERROR_RELOC_CHAIN_XEEDS_SEGLIM, + ERROR_INFLOOP_IN_RELOC_CHAIN, + ERROR_ENVVAR_NOT_FOUND, // = 203 + ERROR_NO_SIGNAL_SENT = 205, + ERROR_FILENAME_EXCED_RANGE, + ERROR_RING2_STACK_IN_USE, + ERROR_META_EXPANSION_TOO_LONG, + ERROR_INVALID_SIGNAL_NUMBER, + ERROR_THREAD_1_INACTIVE, // = 210 + ERROR_LOCKED = 212, + ERROR_TOO_MANY_MODULES = 214, + ERROR_NESTING_NOT_ALLOWED, + ERROR_EXE_MACHINE_TYPE_MISMATCH, + ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY, + ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY, // = 218 + ERROR_BAD_PIPE = 230, + ERROR_PIPE_BUSY, + ERROR_NO_DATA, + ERROR_PIPE_NOT_CONNECTED, + ERROR_MORE_DATA, // = 234 + ERROR_VC_DISCONNECTED = 240, + ERROR_INVALID_EA_NAME = 254, + ERROR_EA_LIST_INCONSISTENT, // = 255 + WAIT_TIMEOUT = 258, + ERROR_NO_MORE_ITEMS, // = 259 + ERROR_CANNOT_COPY = 266, + ERROR_DIRECTORY, // = 267 + ERROR_EAS_DIDNT_FIT = 275, + ERROR_EA_FILE_CORRUPT, + ERROR_EA_TABLE_FULL, + ERROR_INVALID_EA_HANDLE, // = 278 + ERROR_EAS_NOT_SUPPORTED = 282, + ERROR_NOT_OWNER = 288, + ERROR_TOO_MANY_POSTS = 298, + ERROR_PARTIAL_COPY, + ERROR_OPLOCK_NOT_GRANTED, + ERROR_INVALID_OPLOCK_PROTOCOL, + ERROR_DISK_TOO_FRAGMENTED, + ERROR_DELETE_PENDING, // = 303 + ERROR_MR_MID_NOT_FOUND = 317, + ERROR_SCOPE_NOT_FOUND, // = 318 + ERROR_INVALID_ADDRESS = 487, + ERROR_ARITHMETIC_OVERFLOW = 534, + ERROR_PIPE_CONNECTED, + ERROR_PIPE_LISTENING, // = 536 + ERROR_EA_ACCESS_DENIED = 994, + ERROR_OPERATION_ABORTED, + ERROR_IO_INCOMPLETE, + ERROR_IO_PENDING, + ERROR_NOACCESS, + ERROR_SWAPERROR, // = 999 + ERROR_STACK_OVERFLOW = 1001, + ERROR_INVALID_MESSAGE, + ERROR_CAN_NOT_COMPLETE, + ERROR_INVALID_FLAGS, + ERROR_UNRECOGNIZED_VOLUME, + ERROR_FILE_INVALID, + ERROR_FULLSCREEN_MODE, + ERROR_NO_TOKEN, + ERROR_BADDB, + ERROR_BADKEY, + ERROR_CANTOPEN, + ERROR_CANTREAD, + ERROR_CANTWRITE, + ERROR_REGISTRY_RECOVERED, + ERROR_REGISTRY_CORRUPT, + ERROR_REGISTRY_IO_FAILED, + ERROR_NOT_REGISTRY_FILE, + ERROR_KEY_DELETED, + ERROR_NO_LOG_SPACE, + ERROR_KEY_HAS_CHILDREN, + ERROR_CHILD_MUST_BE_VOLATILE, + ERROR_NOTIFY_ENUM_DIR, // = 1022 + ERROR_DEPENDENT_SERVICES_RUNNING = 1051, + ERROR_INVALID_SERVICE_CONTROL, + ERROR_SERVICE_REQUEST_TIMEOUT, + ERROR_SERVICE_NO_THREAD, + ERROR_SERVICE_DATABASE_LOCKED, + ERROR_SERVICE_ALREADY_RUNNING, + ERROR_INVALID_SERVICE_ACCOUNT, + ERROR_SERVICE_DISABLED, + ERROR_CIRCULAR_DEPENDENCY, + ERROR_SERVICE_DOES_NOT_EXIST, + ERROR_SERVICE_CANNOT_ACCEPT_CTRL, + ERROR_SERVICE_NOT_ACTIVE, + ERROR_FAILED_SERVICE_CONTROLLER_CONNECT, + ERROR_EXCEPTION_IN_SERVICE, + ERROR_DATABASE_DOES_NOT_EXIST, + ERROR_SERVICE_SPECIFIC_ERROR, + ERROR_PROCESS_ABORTED, + ERROR_SERVICE_DEPENDENCY_FAIL, + ERROR_SERVICE_LOGON_FAILED, + ERROR_SERVICE_START_HANG, + ERROR_INVALID_SERVICE_LOCK, + ERROR_SERVICE_MARKED_FOR_DELETE, + ERROR_SERVICE_EXISTS, + ERROR_ALREADY_RUNNING_LKG, + ERROR_SERVICE_DEPENDENCY_DELETED, + ERROR_BOOT_ALREADY_ACCEPTED, + ERROR_SERVICE_NEVER_STARTED, + ERROR_DUPLICATE_SERVICE_NAME, + ERROR_DIFFERENT_SERVICE_ACCOUNT, + ERROR_CANNOT_DETECT_DRIVER_FAILURE, + ERROR_CANNOT_DETECT_PROCESS_ABORT, + ERROR_NO_RECOVERY_PROGRAM, + ERROR_SERVICE_NOT_IN_EXE, + ERROR_NOT_SAFEBOOT_SERVICE, // = 1084 + ERROR_END_OF_MEDIA = 1100, + ERROR_FILEMARK_DETECTED, + ERROR_BEGINNING_OF_MEDIA, + ERROR_SETMARK_DETECTED, + ERROR_NO_DATA_DETECTED, + ERROR_PARTITION_FAILURE, + ERROR_INVALID_BLOCK_LENGTH, + ERROR_DEVICE_NOT_PARTITIONED, + ERROR_UNABLE_TO_LOCK_MEDIA, + ERROR_UNABLE_TO_UNLOAD_MEDIA, + ERROR_MEDIA_CHANGED, + ERROR_BUS_RESET, + ERROR_NO_MEDIA_IN_DRIVE, + ERROR_NO_UNICODE_TRANSLATION, + ERROR_DLL_INIT_FAILED, + ERROR_SHUTDOWN_IN_PROGRESS, + ERROR_NO_SHUTDOWN_IN_PROGRESS, + ERROR_IO_DEVICE, + ERROR_SERIAL_NO_DEVICE, + ERROR_IRQ_BUSY, + ERROR_MORE_WRITES, + ERROR_COUNTER_TIMEOUT, + ERROR_FLOPPY_ID_MARK_NOT_FOUND, + ERROR_FLOPPY_WRONG_CYLINDER, + ERROR_FLOPPY_UNKNOWN_ERROR, + ERROR_FLOPPY_BAD_REGISTERS, + ERROR_DISK_RECALIBRATE_FAILED, + ERROR_DISK_OPERATION_FAILED, + ERROR_DISK_RESET_FAILED, + ERROR_EOM_OVERFLOW, + ERROR_NOT_ENOUGH_SERVER_MEMORY, + ERROR_POSSIBLE_DEADLOCK, + ERROR_MAPPED_ALIGNMENT, // = 1132 + ERROR_SET_POWER_STATE_VETOED = 1140, + ERROR_SET_POWER_STATE_FAILED, + ERROR_TOO_MANY_LINKS, // = 1142 + ERROR_OLD_WIN_VERSION = 1150, + ERROR_APP_WRONG_OS, + ERROR_SINGLE_INSTANCE_APP, + ERROR_RMODE_APP, + ERROR_INVALID_DLL, + ERROR_NO_ASSOCIATION, + ERROR_DDE_FAIL, + ERROR_DLL_NOT_FOUND, + ERROR_NO_MORE_USER_HANDLES, + ERROR_MESSAGE_SYNC_ONLY, + ERROR_SOURCE_ELEMENT_EMPTY, + ERROR_DESTINATION_ELEMENT_FULL, + ERROR_ILLEGAL_ELEMENT_ADDRESS, + ERROR_MAGAZINE_NOT_PRESENT, + ERROR_DEVICE_REINITIALIZATION_NEEDED, + ERROR_DEVICE_REQUIRES_CLEANING, + ERROR_DEVICE_DOOR_OPEN, + ERROR_DEVICE_NOT_CONNECTED, + ERROR_NOT_FOUND, + ERROR_NO_MATCH, + ERROR_SET_NOT_FOUND, + ERROR_POINT_NOT_FOUND, + ERROR_NO_TRACKING_SERVICE, + ERROR_NO_VOLUME_ID, // = 1173 + ERROR_UNABLE_TO_REMOVE_REPLACED = 1175, + ERROR_UNABLE_TO_MOVE_REPLACEMENT, + ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, + ERROR_JOURNAL_DELETE_IN_PROGRESS, + ERROR_JOURNAL_NOT_ACTIVE, + ERROR_POTENTIAL_FILE_FOUND, + ERROR_JOURNAL_ENTRY_DELETED, // = 1181 + ERROR_BAD_DEVICE = 1200, + ERROR_CONNECTION_UNAVAIL, + ERROR_DEVICE_ALREADY_REMEMBERED, + ERROR_NO_NET_OR_BAD_PATH, + ERROR_BAD_PROVIDER, + ERROR_CANNOT_OPEN_PROFILE, + ERROR_BAD_PROFILE, + ERROR_NOT_CONTAINER, + ERROR_EXTENDED_ERROR, + ERROR_INVALID_GROUPNAME, + ERROR_INVALID_COMPUTERNAME, + ERROR_INVALID_EVENTNAME, + ERROR_INVALID_DOMAINNAME, + ERROR_INVALID_SERVICENAME, + ERROR_INVALID_NETNAME, + ERROR_INVALID_SHARENAME, + ERROR_INVALID_PASSWORDNAME, + ERROR_INVALID_MESSAGENAME, + ERROR_INVALID_MESSAGEDEST, + ERROR_SESSION_CREDENTIAL_CONFLICT, + ERROR_REMOTE_SESSION_LIMIT_EXCEEDED, + ERROR_DUP_DOMAINNAME, + ERROR_NO_NETWORK, + ERROR_CANCELLED, + ERROR_USER_MAPPED_FILE, + ERROR_CONNECTION_REFUSED, + ERROR_GRACEFUL_DISCONNECT, + ERROR_ADDRESS_ALREADY_ASSOCIATED, + ERROR_ADDRESS_NOT_ASSOCIATED, + ERROR_CONNECTION_INVALID, + ERROR_CONNECTION_ACTIVE, + ERROR_NETWORK_UNREACHABLE, + ERROR_HOST_UNREACHABLE, + ERROR_PROTOCOL_UNREACHABLE, + ERROR_PORT_UNREACHABLE, + ERROR_REQUEST_ABORTED, + ERROR_CONNECTION_ABORTED, + ERROR_RETRY, + ERROR_CONNECTION_COUNT_LIMIT, + ERROR_LOGIN_TIME_RESTRICTION, + ERROR_LOGIN_WKSTA_RESTRICTION, + ERROR_INCORRECT_ADDRESS, + ERROR_ALREADY_REGISTERED, + ERROR_SERVICE_NOT_FOUND, + ERROR_NOT_AUTHENTICATED, + ERROR_NOT_LOGGED_ON, + ERROR_CONTINUE, + ERROR_ALREADY_INITIALIZED, + ERROR_NO_MORE_DEVICES, + ERROR_NO_SUCH_SITE, + ERROR_DOMAIN_CONTROLLER_EXISTS, + ERROR_ONLY_IF_CONNECTED, + ERROR_OVERRIDE_NOCHANGES, + ERROR_BAD_USER_PROFILE, + ERROR_NOT_SUPPORTED_ON_SBS, + ERROR_SERVER_SHUTDOWN_IN_PROGRESS, + ERROR_HOST_DOWN, + ERROR_NON_ACCOUNT_SID, + ERROR_NON_DOMAIN_SID, + ERROR_APPHELP_BLOCK, + ERROR_ACCESS_DISABLED_BY_POLICY, + ERROR_REG_NAT_CONSUMPTION, + ERROR_CSCSHARE_OFFLINE, + ERROR_PKINIT_FAILURE, + ERROR_SMARTCARD_SUBSYSTEM_FAILURE, + ERROR_DOWNGRADE_DETECTED, + SEC_E_SMARTCARD_CERT_REVOKED, + SEC_E_ISSUING_CA_UNTRUSTED, + SEC_E_REVOCATION_OFFLINE_C, + SEC_E_PKINIT_CLIENT_FAILUR, + SEC_E_SMARTCARD_CERT_EXPIRED, + ERROR_MACHINE_LOCKED, // = 1271 + ERROR_CALLBACK_SUPPLIED_INVALID_DATA = 1273, + ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED, + ERROR_DRIVER_BLOCKED, + ERROR_INVALID_IMPORT_OF_NON_DLL, + ERROR_ACCESS_DISABLED_WEBBLADE, + ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER, + ERROR_RECOVERY_FAILURE, + ERROR_ALREADY_FIBER, + ERROR_ALREADY_THREAD, + ERROR_STACK_BUFFER_OVERRUN, + ERROR_PARAMETER_QUOTA_EXCEEDED, + ERROR_DEBUGGER_INACTIVE, // = 1284 + ERROR_NOT_ALL_ASSIGNED = 1300, + ERROR_SOME_NOT_MAPPED, + ERROR_NO_QUOTAS_FOR_ACCOUNT, + ERROR_LOCAL_USER_SESSION_KEY, + ERROR_NULL_LM_PASSWORD, + ERROR_UNKNOWN_REVISION, + ERROR_REVISION_MISMATCH, + ERROR_INVALID_OWNER, + ERROR_INVALID_PRIMARY_GROUP, + ERROR_NO_IMPERSONATION_TOKEN, + ERROR_CANT_DISABLE_MANDATORY, + ERROR_NO_LOGON_SERVERS, + ERROR_NO_SUCH_LOGON_SESSION, + ERROR_NO_SUCH_PRIVILEGE, + ERROR_PRIVILEGE_NOT_HELD, + ERROR_INVALID_ACCOUNT_NAME, + ERROR_USER_EXISTS, + ERROR_NO_SUCH_USER, + ERROR_GROUP_EXISTS, + ERROR_NO_SUCH_GROUP, + ERROR_MEMBER_IN_GROUP, + ERROR_MEMBER_NOT_IN_GROUP, + ERROR_LAST_ADMIN, + ERROR_WRONG_PASSWORD, + ERROR_ILL_FORMED_PASSWORD, + ERROR_PASSWORD_RESTRICTION, + ERROR_LOGON_FAILURE, + ERROR_ACCOUNT_RESTRICTION, + ERROR_INVALID_LOGON_HOURS, + ERROR_INVALID_WORKSTATION, + ERROR_PASSWORD_EXPIRED, + ERROR_ACCOUNT_DISABLED, + ERROR_NONE_MAPPED, + ERROR_TOO_MANY_LUIDS_REQUESTED, + ERROR_LUIDS_EXHAUSTED, + ERROR_INVALID_SUB_AUTHORITY, + ERROR_INVALID_ACL, + ERROR_INVALID_SID, + ERROR_INVALID_SECURITY_DESCR, // = 1338 + ERROR_BAD_INHERITANCE_ACL = 1340, + ERROR_SERVER_DISABLED, + ERROR_SERVER_NOT_DISABLED, + ERROR_INVALID_ID_AUTHORITY, + ERROR_ALLOTTED_SPACE_EXCEEDED, + ERROR_INVALID_GROUP_ATTRIBUTES, + ERROR_BAD_IMPERSONATION_LEVEL, + ERROR_CANT_OPEN_ANONYMOUS, + ERROR_BAD_VALIDATION_CLASS, + ERROR_BAD_TOKEN_TYPE, + ERROR_NO_SECURITY_ON_OBJECT, + ERROR_CANT_ACCESS_DOMAIN_INFO, + ERROR_INVALID_SERVER_STATE, + ERROR_INVALID_DOMAIN_STATE, + ERROR_INVALID_DOMAIN_ROLE, + ERROR_NO_SUCH_DOMAIN, + ERROR_DOMAIN_EXISTS, + ERROR_DOMAIN_LIMIT_EXCEEDED, + ERROR_INTERNAL_DB_CORRUPTION, + ERROR_INTERNAL_ERROR, + ERROR_GENERIC_NOT_MAPPED, + ERROR_BAD_DESCRIPTOR_FORMAT, + ERROR_NOT_LOGON_PROCESS, + ERROR_LOGON_SESSION_EXISTS, + ERROR_NO_SUCH_PACKAGE, + ERROR_BAD_LOGON_SESSION_STATE, + ERROR_LOGON_SESSION_COLLISION, + ERROR_INVALID_LOGON_TYPE, + ERROR_CANNOT_IMPERSONATE, + ERROR_RXACT_INVALID_STATE, + ERROR_RXACT_COMMIT_FAILURE, + ERROR_SPECIAL_ACCOUNT, + ERROR_SPECIAL_GROUP, + ERROR_SPECIAL_USER, + ERROR_MEMBERS_PRIMARY_GROUP, + ERROR_TOKEN_ALREADY_IN_USE, + ERROR_NO_SUCH_ALIAS, + ERROR_MEMBER_NOT_IN_ALIAS, + ERROR_MEMBER_IN_ALIAS, + ERROR_ALIAS_EXISTS, + ERROR_LOGON_NOT_GRANTED, + ERROR_TOO_MANY_SECRETS, + ERROR_SECRET_TOO_LONG, + ERROR_INTERNAL_DB_ERROR, + ERROR_TOO_MANY_CONTEXT_IDS, + ERROR_LOGON_TYPE_NOT_GRANTED, + ERROR_NT_CROSS_ENCRYPTION_REQUIRED, + ERROR_NO_SUCH_MEMBER, + ERROR_INVALID_MEMBER, + ERROR_TOO_MANY_SIDS, + ERROR_LM_CROSS_ENCRYPTION_REQUIRED, + ERROR_NO_INHERITANCE, + ERROR_FILE_CORRUPT, + ERROR_DISK_CORRUPT, + ERROR_NO_USER_SESSION_KEY, + ERROR_LICENSE_QUOTA_EXCEEDED, + ERROR_WRONG_TARGET_NAME, + ERROR_MUTUAL_AUTH_FAILED, + ERROR_TIME_SKEW, + ERROR_CURRENT_DOMAIN_NOT_ALLOWED, + ERROR_INVALID_WINDOW_HANDLE, + ERROR_INVALID_MENU_HANDLE, + ERROR_INVALID_CURSOR_HANDLE, + ERROR_INVALID_ACCEL_HANDLE, + ERROR_INVALID_HOOK_HANDLE, + ERROR_INVALID_DWP_HANDLE, + ERROR_TLW_WITH_WSCHILD, + ERROR_CANNOT_FIND_WND_CLASS, + ERROR_WINDOW_OF_OTHER_THREAD, + ERROR_HOTKEY_ALREADY_REGISTERED, + ERROR_CLASS_ALREADY_EXISTS, + ERROR_CLASS_DOES_NOT_EXIST, + ERROR_CLASS_HAS_WINDOWS, + ERROR_INVALID_INDEX, + ERROR_INVALID_ICON_HANDLE, + ERROR_PRIVATE_DIALOG_INDEX, + ERROR_LISTBOX_ID_NOT_FOUND, + ERROR_NO_WILDCARD_CHARACTERS, + ERROR_CLIPBOARD_NOT_OPEN, + ERROR_HOTKEY_NOT_REGISTERED, + ERROR_WINDOW_NOT_DIALOG, + ERROR_CONTROL_ID_NOT_FOUND, + ERROR_INVALID_COMBOBOX_MESSAGE, + ERROR_WINDOW_NOT_COMBOBOX, + ERROR_INVALID_EDIT_HEIGHT, + ERROR_DC_NOT_FOUND, + ERROR_INVALID_HOOK_FILTER, + ERROR_INVALID_FILTER_PROC, + ERROR_HOOK_NEEDS_HMOD, + ERROR_GLOBAL_ONLY_HOOK, + ERROR_JOURNAL_HOOK_SET, + ERROR_HOOK_NOT_INSTALLED, + ERROR_INVALID_LB_MESSAGE, + ERROR_SETCOUNT_ON_BAD_LB, + ERROR_LB_WITHOUT_TABSTOPS, + ERROR_DESTROY_OBJECT_OF_OTHER_THREAD, + ERROR_CHILD_WINDOW_MENU, + ERROR_NO_SYSTEM_MENU, + ERROR_INVALID_MSGBOX_STYLE, + ERROR_INVALID_SPI_VALUE, + ERROR_SCREEN_ALREADY_LOCKED, + ERROR_HWNDS_HAVE_DIFF_PARENT, + ERROR_NOT_CHILD_WINDOW, + ERROR_INVALID_GW_COMMAND, + ERROR_INVALID_THREAD_ID, + ERROR_NON_MDICHILD_WINDOW, + ERROR_POPUP_ALREADY_ACTIVE, + ERROR_NO_SCROLLBARS, + ERROR_INVALID_SCROLLBAR_RANGE, + ERROR_INVALID_SHOWWIN_COMMAND, + ERROR_NO_SYSTEM_RESOURCES, + ERROR_NONPAGED_SYSTEM_RESOURCES, + ERROR_PAGED_SYSTEM_RESOURCES, + ERROR_WORKING_SET_QUOTA, + ERROR_PAGEFILE_QUOTA, + ERROR_COMMITMENT_LIMIT, + ERROR_MENU_ITEM_NOT_FOUND, + ERROR_INVALID_KEYBOARD_HANDLE, + ERROR_HOOK_TYPE_NOT_ALLOWED, + ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION, + ERROR_TIMEOUT, + ERROR_INVALID_MONITOR_HANDLE, // = 1461 + ERROR_EVENTLOG_FILE_CORRUPT = 1500, + ERROR_EVENTLOG_CANT_START, + ERROR_LOG_FILE_FULL, + ERROR_EVENTLOG_FILE_CHANGED, // = 1503 + ERROR_INSTALL_SERVICE_FAILURE = 1601, + ERROR_INSTALL_USEREXIT, + ERROR_INSTALL_FAILURE, + ERROR_INSTALL_SUSPEND, + ERROR_UNKNOWN_PRODUCT, + ERROR_UNKNOWN_FEATURE, + ERROR_UNKNOWN_COMPONENT, + ERROR_UNKNOWN_PROPERTY, + ERROR_INVALID_HANDLE_STATE, + ERROR_BAD_CONFIGURATION, + ERROR_INDEX_ABSENT, + ERROR_INSTALL_SOURCE_ABSENT, + ERROR_INSTALL_PACKAGE_VERSION, + ERROR_PRODUCT_UNINSTALLED, + ERROR_BAD_QUERY_SYNTAX, + ERROR_INVALID_FIELD, + ERROR_DEVICE_REMOVED, + ERROR_INSTALL_ALREADY_RUNNING, + ERROR_INSTALL_PACKAGE_OPEN_FAILED, + ERROR_INSTALL_PACKAGE_INVALID, + ERROR_INSTALL_UI_FAILURE, + ERROR_INSTALL_LOG_FAILURE, + ERROR_INSTALL_LANGUAGE_UNSUPPORTED, + ERROR_INSTALL_TRANSFORM_FAILURE, + ERROR_INSTALL_PACKAGE_REJECTED, + ERROR_FUNCTION_NOT_CALLED, + ERROR_FUNCTION_FAILED, + ERROR_INVALID_TABLE, + ERROR_DATATYPE_MISMATCH, + ERROR_UNSUPPORTED_TYPE, + ERROR_CREATE_FAILED, + ERROR_INSTALL_TEMP_UNWRITABLE, + ERROR_INSTALL_PLATFORM_UNSUPPORTED, + ERROR_INSTALL_NOTUSED, + ERROR_PATCH_PACKAGE_OPEN_FAILED, + ERROR_PATCH_PACKAGE_INVALID, + ERROR_PATCH_PACKAGE_UNSUPPORTED, + ERROR_PRODUCT_VERSION, + ERROR_INVALID_COMMAND_LINE, + ERROR_INSTALL_REMOTE_DISALLOWED, + ERROR_SUCCESS_REBOOT_INITIATED, + ERROR_PATCH_TARGET_NOT_FOUND, + ERROR_PATCH_PACKAGE_REJECTED, + ERROR_INSTALL_TRANSFORM_REJECTED, + ERROR_INSTALL_REMOTE_PROHIBITED, // = 1645 + RPC_S_INVALID_STRING_BINDING = 1700, + RPC_S_WRONG_KIND_OF_BINDING, + RPC_S_INVALID_BINDING, + RPC_S_PROTSEQ_NOT_SUPPORTED, + RPC_S_INVALID_RPC_PROTSEQ, + RPC_S_INVALID_STRING_UUID, + RPC_S_INVALID_ENDPOINT_FORMAT, + RPC_S_INVALID_NET_ADDR, + RPC_S_NO_ENDPOINT_FOUND, + RPC_S_INVALID_TIMEOUT, + RPC_S_OBJECT_NOT_FOUND, + RPC_S_ALREADY_REGISTERED, + RPC_S_TYPE_ALREADY_REGISTERED, + RPC_S_ALREADY_LISTENING, + RPC_S_NO_PROTSEQS_REGISTERED, + RPC_S_NOT_LISTENING, + RPC_S_UNKNOWN_MGR_TYPE, + RPC_S_UNKNOWN_IF, + RPC_S_NO_BINDINGS, + RPC_S_NO_PROTSEQS, + RPC_S_CANT_CREATE_ENDPOINT, + RPC_S_OUT_OF_RESOURCES, + RPC_S_SERVER_UNAVAILABLE, + RPC_S_SERVER_TOO_BUSY, + RPC_S_INVALID_NETWORK_OPTIONS, + RPC_S_NO_CALL_ACTIVE, + RPC_S_CALL_FAILED, + RPC_S_CALL_FAILED_DNE, + RPC_S_PROTOCOL_ERROR, // = 1728 + RPC_S_UNSUPPORTED_TRANS_SYN = 1730, + RPC_S_UNSUPPORTED_TYPE = 1732, + RPC_S_INVALID_TAG, + RPC_S_INVALID_BOUND, + RPC_S_NO_ENTRY_NAME, + RPC_S_INVALID_NAME_SYNTAX, + RPC_S_UNSUPPORTED_NAME_SYNTAX, // = 1737 + RPC_S_UUID_NO_ADDRESS = 1739, + RPC_S_DUPLICATE_ENDPOINT, + RPC_S_UNKNOWN_AUTHN_TYPE, + RPC_S_MAX_CALLS_TOO_SMALL, + RPC_S_STRING_TOO_LONG, + RPC_S_PROTSEQ_NOT_FOUND, + RPC_S_PROCNUM_OUT_OF_RANGE, + RPC_S_BINDING_HAS_NO_AUTH, + RPC_S_UNKNOWN_AUTHN_SERVICE, + RPC_S_UNKNOWN_AUTHN_LEVEL, + RPC_S_INVALID_AUTH_IDENTITY, + RPC_S_UNKNOWN_AUTHZ_SERVICE, + EPT_S_INVALID_ENTRY, + EPT_S_CANT_PERFORM_OP, + EPT_S_NOT_REGISTERED, + RPC_S_NOTHING_TO_EXPORT, + RPC_S_INCOMPLETE_NAME, + RPC_S_INVALID_VERS_OPTION, + RPC_S_NO_MORE_MEMBERS, + RPC_S_NOT_ALL_OBJS_UNEXPORTED, + RPC_S_INTERFACE_NOT_FOUND, + RPC_S_ENTRY_ALREADY_EXISTS, + RPC_S_ENTRY_NOT_FOUND, + RPC_S_NAME_SERVICE_UNAVAILABLE, + RPC_S_INVALID_NAF_ID, + RPC_S_CANNOT_SUPPORT, + RPC_S_NO_CONTEXT_AVAILABLE, + RPC_S_INTERNAL_ERROR, + RPC_S_ZERO_DIVIDE, + RPC_S_ADDRESS_ERROR, + RPC_S_FP_DIV_ZERO, + RPC_S_FP_UNDERFLOW, + RPC_S_FP_OVERFLOW, + RPC_X_NO_MORE_ENTRIES, + RPC_X_SS_CHAR_TRANS_OPEN_FAIL, + RPC_X_SS_CHAR_TRANS_SHORT_FILE, + RPC_X_SS_IN_NULL_CONTEXT, // = 1775 + RPC_X_SS_CONTEXT_DAMAGED = 1777, + RPC_X_SS_HANDLES_MISMATCH, + RPC_X_SS_CANNOT_GET_CALL_HANDLE, + RPC_X_NULL_REF_POINTER, + RPC_X_ENUM_VALUE_OUT_OF_RANGE, + RPC_X_BYTE_COUNT_TOO_SMALL, + RPC_X_BAD_STUB_DATA, + ERROR_INVALID_USER_BUFFER, + ERROR_UNRECOGNIZED_MEDIA, + ERROR_NO_TRUST_LSA_SECRET, + ERROR_NO_TRUST_SAM_ACCOUNT, + ERROR_TRUSTED_DOMAIN_FAILURE, + ERROR_TRUSTED_RELATIONSHIP_FAILURE, + ERROR_TRUST_FAILURE, + RPC_S_CALL_IN_PROGRESS, + ERROR_NETLOGON_NOT_STARTED, + ERROR_ACCOUNT_EXPIRED, + ERROR_REDIRECTOR_HAS_OPEN_HANDLES, + ERROR_PRINTER_DRIVER_ALREADY_INSTALLED, + ERROR_UNKNOWN_PORT, + ERROR_UNKNOWN_PRINTER_DRIVER, + ERROR_UNKNOWN_PRINTPROCESSOR, + ERROR_INVALID_SEPARATOR_FILE, + ERROR_INVALID_PRIORITY, + ERROR_INVALID_PRINTER_NAME, + ERROR_PRINTER_ALREADY_EXISTS, + ERROR_INVALID_PRINTER_COMMAND, + ERROR_INVALID_DATATYPE, + ERROR_INVALID_ENVIRONMENT, + RPC_S_NO_MORE_BINDINGS, + ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, + ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT, + ERROR_NOLOGON_SERVER_TRUST_ACCOUNT, + ERROR_DOMAIN_TRUST_INCONSISTENT, + ERROR_SERVER_HAS_OPEN_HANDLES, + ERROR_RESOURCE_DATA_NOT_FOUND, + ERROR_RESOURCE_TYPE_NOT_FOUND, + ERROR_RESOURCE_NAME_NOT_FOUND, + ERROR_RESOURCE_LANG_NOT_FOUND, + ERROR_NOT_ENOUGH_QUOTA, + RPC_S_NO_INTERFACES, + RPC_S_CALL_CANCELLED, + RPC_S_BINDING_INCOMPLETE, + RPC_S_COMM_FAILURE, + RPC_S_UNSUPPORTED_AUTHN_LEVEL, + RPC_S_NO_PRINC_NAME, + RPC_S_NOT_RPC_ERROR, + RPC_S_UUID_LOCAL_ONLY, + RPC_S_SEC_PKG_ERROR, + RPC_S_NOT_CANCELLED, + RPC_X_INVALID_ES_ACTION, + RPC_X_WRONG_ES_VERSION, + RPC_X_WRONG_STUB_VERSION, + RPC_X_INVALID_PIPE_OBJECT, + RPC_X_WRONG_PIPE_ORDER, + RPC_X_WRONG_PIPE_VERSION, // = 1832 + RPC_S_GROUP_MEMBER_NOT_FOUND = 1898, + EPT_S_CANT_CREATE, + RPC_S_INVALID_OBJECT, + ERROR_INVALID_TIME, + ERROR_INVALID_FORM_NAME, + ERROR_INVALID_FORM_SIZE, + ERROR_ALREADY_WAITING, + ERROR_PRINTER_DELETED, + ERROR_INVALID_PRINTER_STATE, + ERROR_PASSWORD_MUST_CHANGE, + ERROR_DOMAIN_CONTROLLER_NOT_FOUND, + ERROR_ACCOUNT_LOCKED_OUT, + OR_INVALID_OXID, + OR_INVALID_OID, + OR_INVALID_SET, + RPC_S_SEND_INCOMPLETE, + RPC_S_INVALID_ASYNC_HANDLE, + RPC_S_INVALID_ASYNC_CALL, + RPC_X_PIPE_CLOSED, + RPC_X_PIPE_DISCIPLINE_ERROR, + RPC_X_PIPE_EMPTY, + ERROR_NO_SITENAME, + ERROR_CANT_ACCESS_FILE, + ERROR_CANT_RESOLVE_FILENAME, + RPC_S_ENTRY_TYPE_MISMATCH, + RPC_S_NOT_ALL_OBJS_EXPORTED, + RPC_S_INTERFACE_NOT_EXPORTED, + RPC_S_PROFILE_NOT_ADDED, + RPC_S_PRF_ELT_NOT_ADDED, + RPC_S_PRF_ELT_NOT_REMOVED, + RPC_S_GRP_ELT_NOT_ADDED, + RPC_S_GRP_ELT_NOT_REMOVED, + ERROR_KM_DRIVER_BLOCKED, + ERROR_CONTEXT_EXPIRED, + ERROR_PER_USER_TRUST_QUOTA_EXCEEDED, + ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED, + ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED, // = 1934 + ERROR_INVALID_PIXEL_FORMAT = 2000, + ERROR_BAD_DRIVER, + ERROR_INVALID_WINDOW_STYLE, + ERROR_METAFILE_NOT_SUPPORTED, + ERROR_TRANSFORM_NOT_SUPPORTED, + ERROR_CLIPPING_NOT_SUPPORTED, // = 2005 + ERROR_INVALID_CMM = 2010, + ERROR_INVALID_PROFILE, + ERROR_TAG_NOT_FOUND, + ERROR_TAG_NOT_PRESENT, + ERROR_DUPLICATE_TAG, + ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE, + ERROR_PROFILE_NOT_FOUND, + ERROR_INVALID_COLORSPACE, + ERROR_ICM_NOT_ENABLED, + ERROR_DELETING_ICM_XFORM, + ERROR_INVALID_TRANSFORM, + ERROR_COLORSPACE_MISMATCH, + ERROR_INVALID_COLORINDEX, // = 2022 + ERROR_CONNECTED_OTHER_PASSWORD = 2108, + ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT, // = 2109 + ERROR_BAD_USERNAME = 2202, + ERROR_NOT_CONNECTED = 2250, + ERROR_OPEN_FILES = 2401, + ERROR_ACTIVE_CONNECTIONS, // = 2402 + ERROR_DEVICE_IN_USE = 2404, + ERROR_UNKNOWN_PRINT_MONITOR = 3000, + ERROR_PRINTER_DRIVER_IN_USE, + ERROR_SPOOL_FILE_NOT_FOUND, + ERROR_SPL_NO_STARTDOC, + ERROR_SPL_NO_ADDJOB, + ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED, + ERROR_PRINT_MONITOR_ALREADY_INSTALLED, + ERROR_INVALID_PRINT_MONITOR, + ERROR_PRINT_MONITOR_IN_USE, + ERROR_PRINTER_HAS_JOBS_QUEUED, + ERROR_SUCCESS_REBOOT_REQUIRED, + ERROR_SUCCESS_RESTART_REQUIRED, + ERROR_PRINTER_NOT_FOUND, + ERROR_PRINTER_DRIVER_WARNED, + ERROR_PRINTER_DRIVER_BLOCKED, // = 3014 + ERROR_WINS_INTERNAL = 4000, + ERROR_CAN_NOT_DEL_LOCAL_WINS, + ERROR_STATIC_INIT, + ERROR_INC_BACKUP, + ERROR_FULL_BACKUP, + ERROR_REC_NON_EXISTENT, + ERROR_RPL_NOT_ALLOWED, // = 4006 + ERROR_DHCP_ADDRESS_CONFLICT = 4100, + ERROR_WMI_GUID_NOT_FOUND = 4200, + ERROR_WMI_INSTANCE_NOT_FOUND, + ERROR_WMI_ITEMID_NOT_FOUND, + ERROR_WMI_TRY_AGAIN, + ERROR_WMI_DP_NOT_FOUND, + ERROR_WMI_UNRESOLVED_INSTANCE_REF, + ERROR_WMI_ALREADY_ENABLED, + ERROR_WMI_GUID_DISCONNECTED, + ERROR_WMI_SERVER_UNAVAILABLE, + ERROR_WMI_DP_FAILED, + ERROR_WMI_INVALID_MOF, + ERROR_WMI_INVALID_REGINFO, + ERROR_WMI_ALREADY_DISABLED, + ERROR_WMI_READ_ONLY, + ERROR_WMI_SET_FAILURE, // = 4214 + ERROR_INVALID_MEDIA = 4300, + ERROR_INVALID_LIBRARY, + ERROR_INVALID_MEDIA_POOL, + ERROR_DRIVE_MEDIA_MISMATCH, + ERROR_MEDIA_OFFLINE, + ERROR_LIBRARY_OFFLINE, + ERROR_EMPTY, + ERROR_NOT_EMPTY, + ERROR_MEDIA_UNAVAILABLE, + ERROR_RESOURCE_DISABLED, + ERROR_INVALID_CLEANER, + ERROR_UNABLE_TO_CLEAN, + ERROR_OBJECT_NOT_FOUND, + ERROR_DATABASE_FAILURE, + ERROR_DATABASE_FULL, + ERROR_MEDIA_INCOMPATIBLE, + ERROR_RESOURCE_NOT_PRESENT, + ERROR_INVALID_OPERATION, + ERROR_MEDIA_NOT_AVAILABLE, + ERROR_DEVICE_NOT_AVAILABLE, + ERROR_REQUEST_REFUSED, + ERROR_INVALID_DRIVE_OBJECT, + ERROR_LIBRARY_FULL, + ERROR_MEDIUM_NOT_ACCESSIBLE, + ERROR_UNABLE_TO_LOAD_MEDIUM, + ERROR_UNABLE_TO_INVENTORY_DRIVE, + ERROR_UNABLE_TO_INVENTORY_SLOT, + ERROR_UNABLE_TO_INVENTORY_TRANSPORT, + ERROR_TRANSPORT_FULL, + ERROR_CONTROLLING_IEPORT, + ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA, + ERROR_CLEANER_SLOT_SET, + ERROR_CLEANER_SLOT_NOT_SET, + ERROR_CLEANER_CARTRIDGE_SPENT, + ERROR_UNEXPECTED_OMID, + ERROR_CANT_DELETE_LAST_ITEM, + ERROR_MESSAGE_EXCEEDS_MAX_SIZE, + ERROR_VOLUME_CONTAINS_SYS_FILES, + ERROR_INDIGENOUS_TYPE, + ERROR_NO_SUPPORTING_DRIVES, + ERROR_CLEANER_CARTRIDGE_INSTALLED, // = 4340 + ERROR_FILE_OFFLINE = 4350, + ERROR_REMOTE_STORAGE_NOT_ACTIVE, + ERROR_REMOTE_STORAGE_MEDIA_ERROR, // = 4352 + ERROR_NOT_A_REPARSE_POINT = 4390, + ERROR_REPARSE_ATTRIBUTE_CONFLICT, + ERROR_INVALID_REPARSE_DATA, + ERROR_REPARSE_TAG_INVALID, + ERROR_REPARSE_TAG_MISMATCH, // = 4394 + ERROR_VOLUME_NOT_SIS_ENABLED = 4500, + ERROR_DEPENDENT_RESOURCE_EXISTS = 5001, + ERROR_DEPENDENCY_NOT_FOUND, + ERROR_DEPENDENCY_ALREADY_EXISTS, + ERROR_RESOURCE_NOT_ONLINE, + ERROR_HOST_NODE_NOT_AVAILABLE, + ERROR_RESOURCE_NOT_AVAILABLE, + ERROR_RESOURCE_NOT_FOUND, + ERROR_SHUTDOWN_CLUSTER, + ERROR_CANT_EVICT_ACTIVE_NODE, + ERROR_OBJECT_ALREADY_EXISTS, + ERROR_OBJECT_IN_LIST, + ERROR_GROUP_NOT_AVAILABLE, + ERROR_GROUP_NOT_FOUND, + ERROR_GROUP_NOT_ONLINE, + ERROR_HOST_NODE_NOT_RESOURCE_OWNER, + ERROR_HOST_NODE_NOT_GROUP_OWNER, + ERROR_RESMON_CREATE_FAILED, + ERROR_RESMON_ONLINE_FAILED, + ERROR_RESOURCE_ONLINE, + ERROR_QUORUM_RESOURCE, + ERROR_NOT_QUORUM_CAPABLE, + ERROR_CLUSTER_SHUTTING_DOWN, + ERROR_INVALID_STATE, + ERROR_RESOURCE_PROPERTIES_STORED, + ERROR_NOT_QUORUM_CLASS, + ERROR_CORE_RESOURCE, + ERROR_QUORUM_RESOURCE_ONLINE_FAILED, + ERROR_QUORUMLOG_OPEN_FAILED, + ERROR_CLUSTERLOG_CORRUPT, + ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE, + ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE, + ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND, + ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE, + ERROR_QUORUM_OWNER_ALIVE, + ERROR_NETWORK_NOT_AVAILABLE, + ERROR_NODE_NOT_AVAILABLE, + ERROR_ALL_NODES_NOT_AVAILABLE, + ERROR_RESOURCE_FAILED, + ERROR_CLUSTER_INVALID_NODE, + ERROR_CLUSTER_NODE_EXISTS, + ERROR_CLUSTER_JOIN_IN_PROGRESS, + ERROR_CLUSTER_NODE_NOT_FOUND, + ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND, + ERROR_CLUSTER_NETWORK_EXISTS, + ERROR_CLUSTER_NETWORK_NOT_FOUND, + ERROR_CLUSTER_NETINTERFACE_EXISTS, + ERROR_CLUSTER_NETINTERFACE_NOT_FOUND, + ERROR_CLUSTER_INVALID_REQUEST, + ERROR_CLUSTER_INVALID_NETWORK_PROVIDER, + ERROR_CLUSTER_NODE_DOWN, + ERROR_CLUSTER_NODE_UNREACHABLE, + ERROR_CLUSTER_NODE_NOT_MEMBER, + ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS, + ERROR_CLUSTER_INVALID_NETWORK, // = 5054 + ERROR_CLUSTER_NODE_UP = 5056, + ERROR_CLUSTER_IPADDR_IN_USE, + ERROR_CLUSTER_NODE_NOT_PAUSED, + ERROR_CLUSTER_NO_SECURITY_CONTEXT, + ERROR_CLUSTER_NETWORK_NOT_INTERNAL, + ERROR_CLUSTER_NODE_ALREADY_UP, + ERROR_CLUSTER_NODE_ALREADY_DOWN, + ERROR_CLUSTER_NETWORK_ALREADY_ONLINE, + ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE, + ERROR_CLUSTER_NODE_ALREADY_MEMBER, + ERROR_CLUSTER_LAST_INTERNAL_NETWORK, + ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS, + ERROR_INVALID_OPERATION_ON_QUORUM, + ERROR_DEPENDENCY_NOT_ALLOWED, + ERROR_CLUSTER_NODE_PAUSED, + ERROR_NODE_CANT_HOST_RESOURCE, + ERROR_CLUSTER_NODE_NOT_READY, + ERROR_CLUSTER_NODE_SHUTTING_DOWN, + ERROR_CLUSTER_JOIN_ABORTED, + ERROR_CLUSTER_INCOMPATIBLE_VERSIONS, + ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED, + ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED, + ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND, + ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED, + ERROR_CLUSTER_RESNAME_NOT_FOUND, + ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED, + ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST, + ERROR_CLUSTER_DATABASE_SEQMISMATCH, + ERROR_RESMON_INVALID_STATE, + ERROR_CLUSTER_GUM_NOT_LOCKER, + ERROR_QUORUM_DISK_NOT_FOUND, + ERROR_DATABASE_BACKUP_CORRUPT, + ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT, + ERROR_RESOURCE_PROPERTY_UNCHANGEABLE, // = 5089 + ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE = 5890, + ERROR_CLUSTER_QUORUMLOG_NOT_FOUND, + ERROR_CLUSTER_MEMBERSHIP_HALT, + ERROR_CLUSTER_INSTANCE_ID_MISMATCH, + ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP, + ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH, + ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP, + ERROR_CLUSTER_PARAMETER_MISMATCH, + ERROR_NODE_CANNOT_BE_CLUSTERED, + ERROR_CLUSTER_WRONG_OS_VERSION, + ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME, + ERROR_CLUSCFG_ALREADY_COMMITTED, + ERROR_CLUSCFG_ROLLBACK_FAILED, + ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT, + ERROR_CLUSTER_OLD_VERSION, + ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME, // = 5905 + ERROR_ENCRYPTION_FAILED = 6000, + ERROR_DECRYPTION_FAILED, + ERROR_FILE_ENCRYPTED, + ERROR_NO_RECOVERY_POLICY, + ERROR_NO_EFS, + ERROR_WRONG_EFS, + ERROR_NO_USER_KEYS, + ERROR_FILE_NOT_ENCRYPTED, + ERROR_NOT_EXPORT_FORMAT, + ERROR_FILE_READ_ONLY, + ERROR_DIR_EFS_DISALLOWED, + ERROR_EFS_SERVER_NOT_TRUSTED, + ERROR_BAD_RECOVERY_POLICY, + ERROR_EFS_ALG_BLOB_TOO_BIG, + ERROR_VOLUME_NOT_SUPPORT_EFS, + ERROR_EFS_DISABLED, + ERROR_EFS_VERSION_NOT_SUPPORT, // = 6016 + ERROR_NO_BROWSER_SERVERS_FOUND = 6118, + SCHED_E_SERVICE_NOT_LOCALSYSTEM = 6200, + + ERROR_CTX_WINSTATION_NAME_INVALID = 7001, + ERROR_CTX_INVALID_PD, + ERROR_CTX_PD_NOT_FOUND, + ERROR_CTX_WD_NOT_FOUND, + ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY, + ERROR_CTX_SERVICE_NAME_COLLISION, + ERROR_CTX_CLOSE_PENDING, + ERROR_CTX_NO_OUTBUF, + ERROR_CTX_MODEM_INF_NOT_FOUND, + ERROR_CTX_INVALID_MODEMNAME, + ERROR_CTX_MODEM_RESPONSE_ERROR, + ERROR_CTX_MODEM_RESPONSE_TIMEOUT, + ERROR_CTX_MODEM_RESPONSE_NO_CARRIER, + ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE, + ERROR_CTX_MODEM_RESPONSE_BUSY, + ERROR_CTX_MODEM_RESPONSE_VOICE, + ERROR_CTX_TD_ERROR, // = 7017 + ERROR_CTX_WINSTATION_NOT_FOUND = 7022, + ERROR_CTX_WINSTATION_ALREADY_EXISTS, + ERROR_CTX_WINSTATION_BUSY, + ERROR_CTX_BAD_VIDEO_MODE, // = 7025 + ERROR_CTX_GRAPHICS_INVALID = 7035, + ERROR_CTX_LOGON_DISABLED = 7037, + ERROR_CTX_NOT_CONSOLE, // = 7038 + ERROR_CTX_CLIENT_QUERY_TIMEOUT = 7040, + ERROR_CTX_CONSOLE_DISCONNECT, + ERROR_CTX_CONSOLE_CONNECT, // = 7042 + ERROR_CTX_SHADOW_DENIED = 7044, + ERROR_CTX_WINSTATION_ACCESS_DENIED, // = 7045 + ERROR_CTX_INVALID_WD = 7049, + ERROR_CTX_SHADOW_INVALID, + ERROR_CTX_SHADOW_DISABLED, + ERROR_CTX_CLIENT_LICENSE_IN_USE, + ERROR_CTX_CLIENT_LICENSE_NOT_SET, + ERROR_CTX_LICENSE_NOT_AVAILABLE, + ERROR_CTX_LICENSE_CLIENT_INVALID, + ERROR_CTX_LICENSE_EXPIRED, + ERROR_CTX_SHADOW_NOT_RUNNING, + ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE, + ERROR_ACTIVATION_COUNT_EXCEEDED, // = 7059 + + FRS_ERR_INVALID_API_SEQUENCE = 8001, + FRS_ERR_STARTING_SERVICE, + FRS_ERR_STOPPING_SERVICE, + FRS_ERR_INTERNAL_API, + FRS_ERR_INTERNAL, + FRS_ERR_SERVICE_COMM, + FRS_ERR_INSUFFICIENT_PRIV, + FRS_ERR_AUTHENTICATION, + FRS_ERR_PARENT_INSUFFICIENT_PRIV, + FRS_ERR_PARENT_AUTHENTICATION, + FRS_ERR_CHILD_TO_PARENT_COMM, + FRS_ERR_PARENT_TO_CHILD_COMM, + FRS_ERR_SYSVOL_POPULATE, + FRS_ERR_SYSVOL_POPULATE_TIMEOUT, + FRS_ERR_SYSVOL_IS_BUSY, + FRS_ERR_SYSVOL_DEMOTE, + FRS_ERR_INVALID_SERVICE_PARAMETER, // = 8017 + ERROR_DS_NOT_INSTALLED = 8200, + ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY, + ERROR_DS_NO_ATTRIBUTE_OR_VALUE, + ERROR_DS_INVALID_ATTRIBUTE_SYNTAX, + ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED, + ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS, + ERROR_DS_BUSY, + ERROR_DS_UNAVAILABLE, + ERROR_DS_NO_RIDS_ALLOCATED, + ERROR_DS_NO_MORE_RIDS, + ERROR_DS_INCORRECT_ROLE_OWNER, + ERROR_DS_RIDMGR_INIT_ERROR, + ERROR_DS_OBJ_CLASS_VIOLATION, + ERROR_DS_CANT_ON_NON_LEAF, + ERROR_DS_CANT_ON_RDN, + ERROR_DS_CANT_MOD_OBJ_CLASS, + ERROR_DS_CROSS_DOM_MOVE_ERROR, + ERROR_DS_GC_NOT_AVAILABLE, + ERROR_SHARED_POLICY, + ERROR_POLICY_OBJECT_NOT_FOUND, + ERROR_POLICY_ONLY_IN_DS, + ERROR_PROMOTION_ACTIVE, + ERROR_NO_PROMOTION_ACTIVE, // = 8222 + ERROR_DS_OPERATIONS_ERROR = 8224, + ERROR_DS_PROTOCOL_ERROR, + ERROR_DS_TIMELIMIT_EXCEEDED, + ERROR_DS_SIZELIMIT_EXCEEDED, + ERROR_DS_ADMIN_LIMIT_EXCEEDED, + ERROR_DS_COMPARE_FALSE, + ERROR_DS_COMPARE_TRUE, + ERROR_DS_AUTH_METHOD_NOT_SUPPORTED, + ERROR_DS_STRONG_AUTH_REQUIRED, + ERROR_DS_INAPPROPRIATE_AUTH, + ERROR_DS_AUTH_UNKNOWN, + ERROR_DS_REFERRAL, + ERROR_DS_UNAVAILABLE_CRIT_EXTENSION, + ERROR_DS_CONFIDENTIALITY_REQUIRED, + ERROR_DS_INAPPROPRIATE_MATCHING, + ERROR_DS_CONSTRAINT_VIOLATION, + ERROR_DS_NO_SUCH_OBJECT, + ERROR_DS_ALIAS_PROBLEM, + ERROR_DS_INVALID_DN_SYNTAX, + ERROR_DS_IS_LEAF, + ERROR_DS_ALIAS_DEREF_PROBLEM, + ERROR_DS_UNWILLING_TO_PERFORM, + ERROR_DS_LOOP_DETECT, + ERROR_DS_NAMING_VIOLATION, + ERROR_DS_OBJECT_RESULTS_TOO_LARGE, + ERROR_DS_AFFECTS_MULTIPLE_DSAS, + ERROR_DS_SERVER_DOWN, + ERROR_DS_LOCAL_ERROR, + ERROR_DS_ENCODING_ERROR, + ERROR_DS_DECODING_ERROR, + ERROR_DS_FILTER_UNKNOWN, + ERROR_DS_PARAM_ERROR, + ERROR_DS_NOT_SUPPORTED, + ERROR_DS_NO_RESULTS_RETURNED, + ERROR_DS_CONTROL_NOT_FOUND, + ERROR_DS_CLIENT_LOOP, + ERROR_DS_REFERRAL_LIMIT_EXCEEDED, + ERROR_DS_SORT_CONTROL_MISSING, + ERROR_DS_OFFSET_RANGE_ERROR, // = 8262 + ERROR_DS_ROOT_MUST_BE_NC = 8301, + ERROR_DS_ADD_REPLICA_INHIBITED, + ERROR_DS_ATT_NOT_DEF_IN_SCHEMA, + ERROR_DS_MAX_OBJ_SIZE_EXCEEDED, + ERROR_DS_OBJ_STRING_NAME_EXISTS, + ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA, + ERROR_DS_RDN_DOESNT_MATCH_SCHEMA, + ERROR_DS_NO_REQUESTED_ATTS_FOUND, + ERROR_DS_USER_BUFFER_TO_SMALL, + ERROR_DS_ATT_IS_NOT_ON_OBJ, + ERROR_DS_ILLEGAL_MOD_OPERATION, + ERROR_DS_OBJ_TOO_LARGE, + ERROR_DS_BAD_INSTANCE_TYPE, + ERROR_DS_MASTERDSA_REQUIRED, + ERROR_DS_OBJECT_CLASS_REQUIRED, + ERROR_DS_MISSING_REQUIRED_ATT, + ERROR_DS_ATT_NOT_DEF_FOR_CLASS, + ERROR_DS_ATT_ALREADY_EXISTS, // = 8318 + ERROR_DS_CANT_ADD_ATT_VALUES = 8320, + ERROR_DS_SINGLE_VALUE_CONSTRAINT, + ERROR_DS_RANGE_CONSTRAINT, + ERROR_DS_ATT_VAL_ALREADY_EXISTS, + ERROR_DS_CANT_REM_MISSING_ATT, + ERROR_DS_CANT_REM_MISSING_ATT_VAL, + ERROR_DS_ROOT_CANT_BE_SUBREF, + ERROR_DS_NO_CHAINING, + ERROR_DS_NO_CHAINED_EVAL, + ERROR_DS_NO_PARENT_OBJECT, + ERROR_DS_PARENT_IS_AN_ALIAS, + ERROR_DS_CANT_MIX_MASTER_AND_REPS, + ERROR_DS_CHILDREN_EXIST, + ERROR_DS_OBJ_NOT_FOUND, + ERROR_DS_ALIASED_OBJ_MISSING, + ERROR_DS_BAD_NAME_SYNTAX, + ERROR_DS_ALIAS_POINTS_TO_ALIAS, + ERROR_DS_CANT_DEREF_ALIAS, + ERROR_DS_OUT_OF_SCOPE, + ERROR_DS_OBJECT_BEING_REMOVED, + ERROR_DS_CANT_DELETE_DSA_OBJ, + ERROR_DS_GENERIC_ERROR, + ERROR_DS_DSA_MUST_BE_INT_MASTER, + ERROR_DS_CLASS_NOT_DSA, + ERROR_DS_INSUFF_ACCESS_RIGHTS, + ERROR_DS_ILLEGAL_SUPERIOR, + ERROR_DS_ATTRIBUTE_OWNED_BY_SAM, + ERROR_DS_NAME_TOO_MANY_PARTS, + ERROR_DS_NAME_TOO_LONG, + ERROR_DS_NAME_VALUE_TOO_LONG, + ERROR_DS_NAME_UNPARSEABLE, + ERROR_DS_NAME_TYPE_UNKNOWN, + ERROR_DS_NOT_AN_OBJECT, + ERROR_DS_SEC_DESC_TOO_SHORT, + ERROR_DS_SEC_DESC_INVALID, + ERROR_DS_NO_DELETED_NAME, + ERROR_DS_SUBREF_MUST_HAVE_PARENT, + ERROR_DS_NCNAME_MUST_BE_NC, + ERROR_DS_CANT_ADD_SYSTEM_ONLY, + ERROR_DS_CLASS_MUST_BE_CONCRETE, + ERROR_DS_INVALID_DMD, + ERROR_DS_OBJ_GUID_EXISTS, + ERROR_DS_NOT_ON_BACKLINK, + ERROR_DS_NO_CROSSREF_FOR_NC, + ERROR_DS_SHUTTING_DOWN, + ERROR_DS_UNKNOWN_OPERATION, + ERROR_DS_INVALID_ROLE_OWNER, + ERROR_DS_COULDNT_CONTACT_FSMO, + ERROR_DS_CROSS_NC_DN_RENAME, + ERROR_DS_CANT_MOD_SYSTEM_ONLY, + ERROR_DS_REPLICATOR_ONLY, + ERROR_DS_OBJ_CLASS_NOT_DEFINED, + ERROR_DS_OBJ_CLASS_NOT_SUBCLASS, + ERROR_DS_NAME_REFERENCE_INVALID, + ERROR_DS_CROSS_REF_EXISTS, + ERROR_DS_CANT_DEL_MASTER_CROSSREF, + ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD, + ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX, + ERROR_DS_DUP_RDN, + ERROR_DS_DUP_OID, + ERROR_DS_DUP_MAPI_ID, + ERROR_DS_DUP_SCHEMA_ID_GUID, + ERROR_DS_DUP_LDAP_DISPLAY_NAME, + ERROR_DS_SEMANTIC_ATT_TEST, + ERROR_DS_SYNTAX_MISMATCH, + ERROR_DS_EXISTS_IN_MUST_HAVE, + ERROR_DS_EXISTS_IN_MAY_HAVE, + ERROR_DS_NONEXISTENT_MAY_HAVE, + ERROR_DS_NONEXISTENT_MUST_HAVE, + ERROR_DS_AUX_CLS_TEST_FAIL, + ERROR_DS_NONEXISTENT_POSS_SUP, + ERROR_DS_SUB_CLS_TEST_FAIL, + ERROR_DS_BAD_RDN_ATT_ID_SYNTAX, + ERROR_DS_EXISTS_IN_AUX_CLS, + ERROR_DS_EXISTS_IN_SUB_CLS, + ERROR_DS_EXISTS_IN_POSS_SUP, + ERROR_DS_RECALCSCHEMA_FAILED, + ERROR_DS_TREE_DELETE_NOT_FINISHED, + ERROR_DS_CANT_DELETE, + ERROR_DS_ATT_SCHEMA_REQ_ID, + ERROR_DS_BAD_ATT_SCHEMA_SYNTAX, + ERROR_DS_CANT_CACHE_ATT, + ERROR_DS_CANT_CACHE_CLASS, + ERROR_DS_CANT_REMOVE_ATT_CACHE, + ERROR_DS_CANT_REMOVE_CLASS_CACHE, + ERROR_DS_CANT_RETRIEVE_DN, + ERROR_DS_MISSING_SUPREF, + ERROR_DS_CANT_RETRIEVE_INSTANCE, + ERROR_DS_CODE_INCONSISTENCY, + ERROR_DS_DATABASE_ERROR, + ERROR_DS_GOVERNSID_MISSING, + ERROR_DS_MISSING_EXPECTED_ATT, + ERROR_DS_NCNAME_MISSING_CR_REF, + ERROR_DS_SECURITY_CHECKING_ERROR, + ERROR_DS_SCHEMA_NOT_LOADED, + ERROR_DS_SCHEMA_ALLOC_FAILED, + ERROR_DS_ATT_SCHEMA_REQ_SYNTAX, + ERROR_DS_GCVERIFY_ERROR, + ERROR_DS_DRA_SCHEMA_MISMATCH, + ERROR_DS_CANT_FIND_DSA_OBJ, + ERROR_DS_CANT_FIND_EXPECTED_NC, + ERROR_DS_CANT_FIND_NC_IN_CACHE, + ERROR_DS_CANT_RETRIEVE_CHILD, + ERROR_DS_SECURITY_ILLEGAL_MODIFY, + ERROR_DS_CANT_REPLACE_HIDDEN_REC, + ERROR_DS_BAD_HIERARCHY_FILE, + ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED, + ERROR_DS_CONFIG_PARAM_MISSING, + ERROR_DS_COUNTING_AB_INDICES_FAILED, + ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED, + ERROR_DS_INTERNAL_FAILURE, + ERROR_DS_UNKNOWN_ERROR, + ERROR_DS_ROOT_REQUIRES_CLASS_TOP, + ERROR_DS_REFUSING_FSMO_ROLES, + ERROR_DS_MISSING_FSMO_SETTINGS, + ERROR_DS_UNABLE_TO_SURRENDER_ROLES, + ERROR_DS_DRA_GENERIC, + ERROR_DS_DRA_INVALID_PARAMETER, + ERROR_DS_DRA_BUSY, + ERROR_DS_DRA_BAD_DN, + ERROR_DS_DRA_BAD_NC, + ERROR_DS_DRA_DN_EXISTS, + ERROR_DS_DRA_INTERNAL_ERROR, + ERROR_DS_DRA_INCONSISTENT_DIT, + ERROR_DS_DRA_CONNECTION_FAILED, + ERROR_DS_DRA_BAD_INSTANCE_TYPE, + ERROR_DS_DRA_OUT_OF_MEM, + ERROR_DS_DRA_MAIL_PROBLEM, + ERROR_DS_DRA_REF_ALREADY_EXISTS, + ERROR_DS_DRA_REF_NOT_FOUND, + ERROR_DS_DRA_OBJ_IS_REP_SOURCE, + ERROR_DS_DRA_DB_ERROR, + ERROR_DS_DRA_NO_REPLICA, + ERROR_DS_DRA_ACCESS_DENIED, + ERROR_DS_DRA_NOT_SUPPORTED, + ERROR_DS_DRA_RPC_CANCELLED, + ERROR_DS_DRA_SOURCE_DISABLED, + ERROR_DS_DRA_SINK_DISABLED, + ERROR_DS_DRA_NAME_COLLISION, + ERROR_DS_DRA_SOURCE_REINSTALLED, + ERROR_DS_DRA_MISSING_PARENT, + ERROR_DS_DRA_PREEMPTED, + ERROR_DS_DRA_ABANDON_SYNC, + ERROR_DS_DRA_SHUTDOWN, + ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET, + ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA, + ERROR_DS_DRA_EXTN_CONNECTION_FAILED, + ERROR_DS_INSTALL_SCHEMA_MISMATCH, + ERROR_DS_DUP_LINK_ID, + ERROR_DS_NAME_ERROR_RESOLVING, + ERROR_DS_NAME_ERROR_NOT_FOUND, + ERROR_DS_NAME_ERROR_NOT_UNIQUE, + ERROR_DS_NAME_ERROR_NO_MAPPING, + ERROR_DS_NAME_ERROR_DOMAIN_ONLY, + ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING, + ERROR_DS_CONSTRUCTED_ATT_MOD, + ERROR_DS_WRONG_OM_OBJ_CLASS, + ERROR_DS_DRA_REPL_PENDING, + ERROR_DS_DS_REQUIRED, + ERROR_DS_INVALID_LDAP_DISPLAY_NAME, + ERROR_DS_NON_BASE_SEARCH, + ERROR_DS_CANT_RETRIEVE_ATTS, + ERROR_DS_BACKLINK_WITHOUT_LINK, + ERROR_DS_EPOCH_MISMATCH, + ERROR_DS_SRC_NAME_MISMATCH, + ERROR_DS_SRC_AND_DST_NC_IDENTICAL, + ERROR_DS_DST_NC_MISMATCH, + ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC, + ERROR_DS_SRC_GUID_MISMATCH, + ERROR_DS_CANT_MOVE_DELETED_OBJECT, + ERROR_DS_PDC_OPERATION_IN_PROGRESS, + ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD, + ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION, + ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS, + ERROR_DS_NC_MUST_HAVE_NC_PARENT, + ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE, + ERROR_DS_DST_DOMAIN_NOT_NATIVE, + ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER, + ERROR_DS_CANT_MOVE_ACCOUNT_GROUP, + ERROR_DS_CANT_MOVE_RESOURCE_GROUP, + ERROR_DS_INVALID_SEARCH_FLAG, + ERROR_DS_NO_TREE_DELETE_ABOVE_NC, + ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE, + ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE, + ERROR_DS_SAM_INIT_FAILURE, + ERROR_DS_SENSITIVE_GROUP_VIOLATION, + ERROR_DS_CANT_MOD_PRIMARYGROUPID, + ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD, + ERROR_DS_NONSAFE_SCHEMA_CHANGE, + ERROR_DS_SCHEMA_UPDATE_DISALLOWED, + ERROR_DS_CANT_CREATE_UNDER_SCHEMA, + ERROR_DS_INSTALL_NO_SRC_SCH_VERSION, + ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE, + ERROR_DS_INVALID_GROUP_TYPE, + ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, + ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, + ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, + ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, + ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, + ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, + ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, + ERROR_DS_HAVE_PRIMARY_MEMBERS, + ERROR_DS_STRING_SD_CONVERSION_FAILED, + ERROR_DS_NAMING_MASTER_GC, + ERROR_DS_LOOKUP_FAILURE, + ERROR_DS_COULDNT_UPDATE_SPNS, + ERROR_DS_CANT_RETRIEVE_SD, + ERROR_DS_KEY_NOT_UNIQUE, + ERROR_DS_WRONG_LINKED_ATT_SYNTAX, + ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD, + ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY, + ERROR_DS_CANT_START, + ERROR_DS_INIT_FAILURE, + ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION, + ERROR_DS_SOURCE_DOMAIN_IN_FOREST, + ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST, + ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED, + ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN, + ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER, + ERROR_DS_SRC_SID_EXISTS_IN_FOREST, + ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH, + ERROR_SAM_INIT_FAILURE, + ERROR_DS_DRA_SCHEMA_INFO_SHIP, + ERROR_DS_DRA_SCHEMA_CONFLICT, + ERROR_DS_DRA_EARLIER_SCHEMA_CONLICT, + ERROR_DS_DRA_OBJ_NC_MISMATCH, + ERROR_DS_NC_STILL_HAS_DSAS, + ERROR_DS_GC_REQUIRED, + ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, + ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS, + ERROR_DS_CANT_ADD_TO_GC, + ERROR_DS_NO_CHECKPOINT_WITH_PDC, + ERROR_DS_SOURCE_AUDITING_NOT_ENABLED, + ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC, + ERROR_DS_INVALID_NAME_FOR_SPN, + ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS, + ERROR_DS_UNICODEPWD_NOT_IN_QUOTES, + ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, + ERROR_DS_MUST_BE_RUN_ON_DST_DC, + ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER, + ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ, + ERROR_DS_INIT_FAILURE_CONSOLE, + ERROR_DS_SAM_INIT_FAILURE_CONSOLE, + ERROR_DS_FOREST_VERSION_TOO_HIGH, + ERROR_DS_DOMAIN_VERSION_TOO_HIGH, + ERROR_DS_FOREST_VERSION_TOO_LOW, + ERROR_DS_DOMAIN_VERSION_TOO_LOW, + ERROR_DS_INCOMPATIBLE_VERSION, + ERROR_DS_LOW_DSA_VERSION, + ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN, + ERROR_DS_NOT_SUPPORTED_SORT_ORDER, + ERROR_DS_NAME_NOT_UNIQUE, + ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4, + ERROR_DS_OUT_OF_VERSION_STORE, + ERROR_DS_INCOMPATIBLE_CONTROLS_USED, + ERROR_DS_NO_REF_DOMAIN, + ERROR_DS_RESERVED_LINK_ID, + ERROR_DS_LINK_ID_NOT_AVAILABLE, + ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, + ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE, + ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC, + ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG, + ERROR_DS_MODIFYDN_WRONG_GRANDPARENT, + ERROR_DS_NAME_ERROR_TRUST_REFERRAL, + ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER, + ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD, + ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2, + ERROR_DS_THREAD_LIMIT_EXCEEDED, + ERROR_DS_NOT_CLOSEST, + ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF, + ERROR_DS_SINGLE_USER_MODE_FAILED, + ERROR_DS_NTDSCRIPT_SYNTAX_ERROR, + ERROR_DS_NTDSCRIPT_PROCESS_ERROR, + ERROR_DS_DIFFERENT_REPL_EPOCHS, + ERROR_DS_DRS_EXTENSIONS_CHANGED, + ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR, + ERROR_DS_NO_MSDS_INTID, + ERROR_DS_DUP_MSDS_INTID, + ERROR_DS_EXISTS_IN_RDNATTID, + ERROR_DS_AUTHORIZATION_FAILED, + ERROR_DS_INVALID_SCRIPT, + ERROR_DS_REMOTE_CROSSREF_OP_FAILED, + ERROR_DS_CROSS_REF_BUSY, + ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN, + ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC, + ERROR_DS_DUPLICATE_ID_FOUND, + ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT, + ERROR_DS_GROUP_CONVERSION_ERROR, + ERROR_DS_CANT_MOVE_APP_BASIC_GROUP, + ERROR_DS_CANT_MOVE_APP_QUERY_GROUP, + ERROR_DS_ROLE_NOT_VERIFIED, + ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL, + ERROR_DS_DOMAIN_RENAME_IN_PROGRESS, + ERROR_DS_EXISTING_AD_CHILD_NC, // = 8613 + DNS_ERROR_RCODE_FORMAT_ERROR = 9001, + DNS_ERROR_RCODE_SERVER_FAILURE, + DNS_ERROR_RCODE_NAME_ERROR, + DNS_ERROR_RCODE_NOT_IMPLEMENTED, + DNS_ERROR_RCODE_REFUSED, + DNS_ERROR_RCODE_YXDOMAIN, + DNS_ERROR_RCODE_YXRRSET, + DNS_ERROR_RCODE_NXRRSET, + DNS_ERROR_RCODE_NOTAUTH, + DNS_ERROR_RCODE_NOTZONE, // = 9010 + DNS_ERROR_RCODE_BADSIG = 9016, + DNS_ERROR_RCODE_BADKEY, + DNS_ERROR_RCODE_BADTIME, // = 9018 + DNS_INFO_NO_RECORDS = 9501, + DNS_ERROR_BAD_PACKET, + DNS_ERROR_NO_PACKET, + DNS_ERROR_RCODE, + DNS_ERROR_UNSECURE_PACKET, // = 9505 + DNS_ERROR_INVALID_TYPE = 9551, + DNS_ERROR_INVALID_IP_ADDRESS, + DNS_ERROR_INVALID_PROPERTY, + DNS_ERROR_TRY_AGAIN_LATER, + DNS_ERROR_NOT_UNIQUE, + DNS_ERROR_NON_RFC_NAME, + DNS_STATUS_FQDN, + DNS_STATUS_DOTTED_NAME, + DNS_STATUS_SINGLE_PART_NAME, + DNS_ERROR_INVALID_NAME_CHAR, + DNS_ERROR_NUMERIC_NAME, + DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER, + DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION, + DNS_ERROR_CANNOT_FIND_ROOT_HINTS, + DNS_ERROR_INCONSISTENT_ROOT_HINTS, // = 9565 + DNS_ERROR_ZONE_DOES_NOT_EXIST = 9601, + DNS_ERROR_NO_ZONE_INFO, + DNS_ERROR_INVALID_ZONE_OPERATION, + DNS_ERROR_ZONE_CONFIGURATION_ERROR, + DNS_ERROR_ZONE_HAS_NO_SOA_RECORD, + DNS_ERROR_ZONE_HAS_NO_NS_RECORDS, + DNS_ERROR_ZONE_LOCKED, + DNS_ERROR_ZONE_CREATION_FAILED, + DNS_ERROR_ZONE_ALREADY_EXISTS, + DNS_ERROR_AUTOZONE_ALREADY_EXISTS, + DNS_ERROR_INVALID_ZONE_TYPE, + DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP, + DNS_ERROR_ZONE_NOT_SECONDARY, + DNS_ERROR_NEED_SECONDARY_ADDRESSES, + DNS_ERROR_WINS_INIT_FAILED, + DNS_ERROR_NEED_WINS_SERVERS, + DNS_ERROR_NBSTAT_INIT_FAILED, + DNS_ERROR_SOA_DELETE_INVALID, + DNS_ERROR_FORWARDER_ALREADY_EXISTS, + DNS_ERROR_ZONE_REQUIRES_MASTER_IP, + DNS_ERROR_ZONE_IS_SHUTDOWN, // = 9621 + DNS_ERROR_PRIMARY_REQUIRES_DATAFILE = 9651, + DNS_ERROR_INVALID_DATAFILE_NAME, + DNS_ERROR_DATAFILE_OPEN_FAILURE, + DNS_ERROR_FILE_WRITEBACK_FAILED, + DNS_ERROR_DATAFILE_PARSING, // = 9655 + DNS_ERROR_RECORD_DOES_NOT_EXIST = 9701, + DNS_ERROR_RECORD_FORMAT, + DNS_ERROR_NODE_CREATION_FAILED, + DNS_ERROR_UNKNOWN_RECORD_TYPE, + DNS_ERROR_RECORD_TIMED_OUT, + DNS_ERROR_NAME_NOT_IN_ZONE, + DNS_ERROR_CNAME_LOOP, + DNS_ERROR_NODE_IS_CNAME, + DNS_ERROR_CNAME_COLLISION, + DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT, + DNS_ERROR_RECORD_ALREADY_EXISTS, + DNS_ERROR_SECONDARY_DATA, + DNS_ERROR_NO_CREATE_CACHE_DATA, + DNS_ERROR_NAME_DOES_NOT_EXIST, + DNS_WARNING_PTR_CREATE_FAILED, + DNS_WARNING_DOMAIN_UNDELETED, + DNS_ERROR_DS_UNAVAILABLE, + DNS_ERROR_DS_ZONE_ALREADY_EXISTS, + DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE, // = 9719 + DNS_INFO_AXFR_COMPLETE = 9751, + DNS_ERROR_AXFR, + DNS_INFO_ADDED_LOCAL_WINS, // = 9753 + DNS_STATUS_CONTINUE_NEEDED = 9801, + DNS_ERROR_NO_TCPIP = 9851, + DNS_ERROR_NO_DNS_SERVERS, // = 9852 + DNS_ERROR_DP_DOES_NOT_EXIST = 9901, + DNS_ERROR_DP_ALREADY_EXISTS, + DNS_ERROR_DP_NOT_ENLISTED, + DNS_ERROR_DP_ALREADY_ENLISTED, + DNS_ERROR_DP_NOT_AVAILABLE, // = 9905 + +/+ already in winsock2.d defined! + + WSABASEERR = 10000, + WSAEINTR = 10004, + WSAEBADF = 10009, + WSAEACCES = 10013, + WSAEFAULT, // = 10014 + WSAEINVAL = 10022, + WSAEMFILE = 10024, + WSAEWOULDBLOCK = 10035, + WSAEINPROGRESS, + WSAEALREADY, + WSAENOTSOCK, + WSAEDESTADDRREQ, + WSAEMSGSIZE, + WSAEPROTOTYPE, + WSAENOPROTOOPT, + WSAEPROTONOSUPPORT, + WSAESOCKTNOSUPPORT, + WSAEOPNOTSUPP, + WSAEPFNOSUPPORT, + WSAEAFNOSUPPORT, + WSAEADDRINUSE, + WSAEADDRNOTAVAIL, + WSAENETDOWN, + WSAENETUNREACH, + WSAENETRESET, + WSAECONNABORTED, + WSAECONNRESET, + WSAENOBUFS, + WSAEISCONN, + WSAENOTCONN, + WSAESHUTDOWN, + WSAETOOMANYREFS, + WSAETIMEDOUT, + WSAECONNREFUSED, + WSAELOOP, + WSAENAMETOOLONG, + WSAEHOSTDOWN, + WSAEHOSTUNREACH, + WSAENOTEMPTY, + WSAEPROCLIM, + WSAEUSERS, + WSAEDQUOT, + WSAESTALE, + WSAEREMOTE, // = 10071 + WSASYSNOTREADY = 10091, + WSAVERNOTSUPPORTED, + WSANOTINITIALISED, // = 10093 + WSAEDISCON = 10101, + WSAENOMORE, + WSAECANCELLED, + WSAEINVALIDPROCTABLE, + WSAEINVALIDPROVIDER, + WSAEPROVIDERFAILEDINIT, + WSASYSCALLFAILURE, + WSASERVICE_NOT_FOUND, + WSATYPE_NOT_FOUND, + WSA_E_NO_MORE, + WSA_E_CANCELLED, + WSAEREFUSED, // = 10112 + WSAHOST_NOT_FOUND = 11001, + WSATRY_AGAIN, + WSANO_RECOVERY, + WSANO_DATA, + WSA_QOS_RECEIVERS, + WSA_QOS_SENDERS, + WSA_QOS_NO_SENDERS, + WSA_QOS_NO_RECEIVERS, + WSA_QOS_REQUEST_CONFIRMED, + WSA_QOS_ADMISSION_FAILURE, + WSA_QOS_POLICY_FAILURE, + WSA_QOS_BAD_STYLE, + WSA_QOS_BAD_OBJECT, + WSA_QOS_TRAFFIC_CTRL_ERROR, + WSA_QOS_GENERIC_ERROR, + WSA_QOS_ESERVICETYPE, + WSA_QOS_EFLOWSPEC, + WSA_QOS_EPROVSPECBUF, + WSA_QOS_EFILTERSTYLE, + WSA_QOS_EFILTERTYPE, + WSA_QOS_EFILTERCOUNT, + WSA_QOS_EOBJLENGTH, + WSA_QOS_EFLOWCOUNT, + WSA_QOS_EUNKNOWNPSOBJ, + WSA_QOS_EPOLICYOBJ, + WSA_QOS_EFLOWDESC, + WSA_QOS_EPSFLOWSPEC, + WSA_QOS_EPSFILTERSPEC, + WSA_QOS_ESDMODEOBJ, + WSA_QOS_ESHAPERATEOBJ, + WSA_QOS_RESERVED_PETYPE, // = 11031 + ++/ + + ERROR_IPSEC_QM_POLICY_EXISTS = 13000, + ERROR_IPSEC_QM_POLICY_NOT_FOUND, + ERROR_IPSEC_QM_POLICY_IN_USE, + ERROR_IPSEC_MM_POLICY_EXISTS, + ERROR_IPSEC_MM_POLICY_NOT_FOUND, + ERROR_IPSEC_MM_POLICY_IN_USE, + ERROR_IPSEC_MM_FILTER_EXISTS, + ERROR_IPSEC_MM_FILTER_NOT_FOUND, + ERROR_IPSEC_TRANSPORT_FILTER_EXISTS, + ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND, + ERROR_IPSEC_MM_AUTH_EXISTS, + ERROR_IPSEC_MM_AUTH_NOT_FOUND, + ERROR_IPSEC_MM_AUTH_IN_USE, + ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND, + ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND, + ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND, + ERROR_IPSEC_TUNNEL_FILTER_EXISTS, + ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND, + ERROR_IPSEC_MM_FILTER_PENDING_DELETION, + ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION, + ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION, + ERROR_IPSEC_MM_POLICY_PENDING_DELETION, + ERROR_IPSEC_MM_AUTH_PENDING_DELETION, + ERROR_IPSEC_QM_POLICY_PENDING_DELETION, + WARNING_IPSEC_MM_POLICY_PRUNED, + WARNING_IPSEC_QM_POLICY_PRUNED, // = 13025 + ERROR_IPSEC_IKE_AUTH_FAIL = 13801, + ERROR_IPSEC_IKE_ATTRIB_FAIL, + ERROR_IPSEC_IKE_NEGOTIATION_PENDING, + ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR, + ERROR_IPSEC_IKE_TIMED_OUT, + ERROR_IPSEC_IKE_NO_CERT, + ERROR_IPSEC_IKE_SA_DELETED, + ERROR_IPSEC_IKE_SA_REAPED, + ERROR_IPSEC_IKE_MM_ACQUIRE_DROP, + ERROR_IPSEC_IKE_QM_ACQUIRE_DROP, + ERROR_IPSEC_IKE_QUEUE_DROP_MM, + ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM, + ERROR_IPSEC_IKE_DROP_NO_RESPONSE, + ERROR_IPSEC_IKE_MM_DELAY_DROP, + ERROR_IPSEC_IKE_QM_DELAY_DROP, + ERROR_IPSEC_IKE_ERROR, + ERROR_IPSEC_IKE_CRL_FAILED, + ERROR_IPSEC_IKE_INVALID_KEY_USAGE, + ERROR_IPSEC_IKE_INVALID_CERT_TYPE, + ERROR_IPSEC_IKE_NO_PRIVATE_KEY, // = 13820 + ERROR_IPSEC_IKE_DH_FAIL = 13822, + ERROR_IPSEC_IKE_INVALID_HEADER = 13824, + ERROR_IPSEC_IKE_NO_POLICY, + ERROR_IPSEC_IKE_INVALID_SIGNATURE, + ERROR_IPSEC_IKE_KERBEROS_ERROR, + ERROR_IPSEC_IKE_NO_PUBLIC_KEY, + ERROR_IPSEC_IKE_PROCESS_ERR, + ERROR_IPSEC_IKE_PROCESS_ERR_SA, + ERROR_IPSEC_IKE_PROCESS_ERR_PROP, + ERROR_IPSEC_IKE_PROCESS_ERR_TRANS, + ERROR_IPSEC_IKE_PROCESS_ERR_KE, + ERROR_IPSEC_IKE_PROCESS_ERR_ID, + ERROR_IPSEC_IKE_PROCESS_ERR_CERT, + ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ, + ERROR_IPSEC_IKE_PROCESS_ERR_HASH, + ERROR_IPSEC_IKE_PROCESS_ERR_SIG, + ERROR_IPSEC_IKE_PROCESS_ERR_NONCE, + ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY, + ERROR_IPSEC_IKE_PROCESS_ERR_DELETE, + ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR, + ERROR_IPSEC_IKE_INVALID_PAYLOAD, + ERROR_IPSEC_IKE_LOAD_SOFT_SA, + ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN, + ERROR_IPSEC_IKE_INVALID_COOKIE, + ERROR_IPSEC_IKE_NO_PEER_CERT, + ERROR_IPSEC_IKE_PEER_CRL_FAILED, + ERROR_IPSEC_IKE_POLICY_CHANGE, + ERROR_IPSEC_IKE_NO_MM_POLICY, + ERROR_IPSEC_IKE_NOTCBPRIV, + ERROR_IPSEC_IKE_SECLOADFAIL, + ERROR_IPSEC_IKE_FAILSSPINIT, + ERROR_IPSEC_IKE_FAILQUERYSSP, + ERROR_IPSEC_IKE_SRVACQFAIL, + ERROR_IPSEC_IKE_SRVQUERYCRED, + ERROR_IPSEC_IKE_GETSPIFAIL, + ERROR_IPSEC_IKE_INVALID_FILTER, + ERROR_IPSEC_IKE_OUT_OF_MEMORY, + ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED, + ERROR_IPSEC_IKE_INVALID_POLICY, + ERROR_IPSEC_IKE_UNKNOWN_DOI, + ERROR_IPSEC_IKE_INVALID_SITUATION, + ERROR_IPSEC_IKE_DH_FAILURE, + ERROR_IPSEC_IKE_INVALID_GROUP, + ERROR_IPSEC_IKE_ENCRYPT, + ERROR_IPSEC_IKE_DECRYPT, + ERROR_IPSEC_IKE_POLICY_MATCH, + ERROR_IPSEC_IKE_UNSUPPORTED_ID, + ERROR_IPSEC_IKE_INVALID_HASH, + ERROR_IPSEC_IKE_INVALID_HASH_ALG, + ERROR_IPSEC_IKE_INVALID_HASH_SIZE, + ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG, + ERROR_IPSEC_IKE_INVALID_AUTH_ALG, + ERROR_IPSEC_IKE_INVALID_SIG, + ERROR_IPSEC_IKE_LOAD_FAILED, + ERROR_IPSEC_IKE_RPC_DELETE, + ERROR_IPSEC_IKE_BENIGN_REINIT, + ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY, // = 13879 + ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN = 13881, + ERROR_IPSEC_IKE_MM_LIMIT, + ERROR_IPSEC_IKE_NEGOTIATION_DISABLED, + ERROR_IPSEC_IKE_NEG_STATUS_END, + ERROR_SXS_SECTION_NOT_FOUND, + ERROR_SXS_CANT_GEN_ACTCTX, + ERROR_SXS_INVALID_ACTCTXDATA_FORMAT, + ERROR_SXS_ASSEMBLY_NOT_FOUND, + ERROR_SXS_MANIFEST_FORMAT_ERROR, + ERROR_SXS_MANIFEST_PARSE_ERROR, + ERROR_SXS_ACTIVATION_CONTEXT_DISABLED, + ERROR_SXS_KEY_NOT_FOUND, + ERROR_SXS_VERSION_CONFLICT, + ERROR_SXS_WRONG_SECTION_TYPE, + ERROR_SXS_THREAD_QUERIES_DISABLED, + ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET, + ERROR_SXS_UNKNOWN_ENCODING_GROUP, + ERROR_SXS_UNKNOWN_ENCODING, + ERROR_SXS_INVALID_XML_NAMESPACE_URI, + ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED, + ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED, + ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE, + ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE, + ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE, + ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT, + ERROR_SXS_DUPLICATE_DLL_NAME, + ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME, + ERROR_SXS_DUPLICATE_CLSID, + ERROR_SXS_DUPLICATE_IID, + ERROR_SXS_DUPLICATE_TLBID, + ERROR_SXS_DUPLICATE_PROGID, + ERROR_SXS_DUPLICATE_ASSEMBLY_NAME, + ERROR_SXS_FILE_HASH_MISMATCH, + ERROR_SXS_POLICY_PARSE_ERROR, + ERROR_SXS_XML_E_MISSINGQUOTE, + ERROR_SXS_XML_E_COMMENTSYNTAX, + ERROR_SXS_XML_E_BADSTARTNAMECHAR, + ERROR_SXS_XML_E_BADNAMECHAR, + ERROR_SXS_XML_E_BADCHARINSTRING, + ERROR_SXS_XML_E_XMLDECLSYNTAX, + ERROR_SXS_XML_E_BADCHARDATA, + ERROR_SXS_XML_E_MISSINGWHITESPACE, + ERROR_SXS_XML_E_EXPECTINGTAGEND, + ERROR_SXS_XML_E_MISSINGSEMICOLON, + ERROR_SXS_XML_E_UNBALANCEDPAREN, + ERROR_SXS_XML_E_INTERNALERROR, + ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE, + ERROR_SXS_XML_E_INCOMPLETE_ENCODING, + ERROR_SXS_XML_E_MISSING_PAREN, + ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE, + ERROR_SXS_XML_E_MULTIPLE_COLONS, + ERROR_SXS_XML_E_INVALID_DECIMAL, + ERROR_SXS_XML_E_INVALID_HEXIDECIMAL, + ERROR_SXS_XML_E_INVALID_UNICODE, + ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK, + ERROR_SXS_XML_E_UNEXPECTEDENDTAG, + ERROR_SXS_XML_E_UNCLOSEDTAG, + ERROR_SXS_XML_E_DUPLICATEATTRIBUTE, + ERROR_SXS_XML_E_MULTIPLEROOTS, + ERROR_SXS_XML_E_INVALIDATROOTLEVEL, + ERROR_SXS_XML_E_BADXMLDECL, + ERROR_SXS_XML_E_MISSINGROOT, + ERROR_SXS_XML_E_UNEXPECTEDEOF, + ERROR_SXS_XML_E_BADPEREFINSUBSET, + ERROR_SXS_XML_E_UNCLOSEDSTARTTAG, + ERROR_SXS_XML_E_UNCLOSEDENDTAG, + ERROR_SXS_XML_E_UNCLOSEDSTRING, + ERROR_SXS_XML_E_UNCLOSEDCOMMENT, + ERROR_SXS_XML_E_UNCLOSEDDECL, + ERROR_SXS_XML_E_UNCLOSEDCDATA, + ERROR_SXS_XML_E_RESERVEDNAMESPACE, + ERROR_SXS_XML_E_INVALIDENCODING, + ERROR_SXS_XML_E_INVALIDSWITCH, + ERROR_SXS_XML_E_BADXMLCASE, + ERROR_SXS_XML_E_INVALID_STANDALONE, + ERROR_SXS_XML_E_UNEXPECTED_STANDALONE, + ERROR_SXS_XML_E_INVALID_VERSION, + ERROR_SXS_XML_E_MISSINGEQUALS, + ERROR_SXS_PROTECTION_RECOVERY_FAILED, + ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT, + ERROR_SXS_PROTECTION_CATALOG_NOT_VALID, + ERROR_SXS_UNTRANSLATABLE_HRESULT, + ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING, + ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE, + ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME // = 14080 +} + +enum : HRESULT { + S_OK = 0x00000000, + S_FALSE = 0x00000001, + + NOERROR = 0x00000000, + + E_PENDING = 0x8000000A, + E_NOTIMPL = 0x80004001, + E_NOINTERFACE = 0x80004002, + E_POINTER = 0x80004003, + E_ABORT = 0x80004004, + E_FAIL = 0x80004005, + E_ACCESSDENIED = 0x80070005, + E_HANDLE = 0x80070006, + E_OUTOFMEMORY = 0x8007000E, + E_INVALIDARG = 0x80070057, + E_UNEXPECTED = 0x8000FFFF, + + CO_E_INIT_TLS = 0x80004006, + CO_E_INIT_SHARED_ALLOCATOR = 0x80004007, + CO_E_INIT_MEMORY_ALLOCATOR = 0x80004008, + CO_E_INIT_CLASS_CACHE = 0x80004009, + CO_E_INIT_RPC_CHANNEL = 0x8000400A, + CO_E_INIT_TLS_SET_CHANNEL_CONTROL = 0x8000400B, + CO_E_INIT_TLS_CHANNEL_CONTROL = 0x8000400C, + CO_E_INIT_UNACCEPTED_USER_ALLOCATOR = 0x8000400D, + CO_E_INIT_SCM_MUTEX_EXISTS = 0x8000400E, + CO_E_INIT_SCM_FILE_MAPPING_EXISTS = 0x8000400F, + CO_E_INIT_SCM_MAP_VIEW_OF_FILE = 0x80004010, + CO_E_INIT_SCM_EXEC_FAILURE = 0x80004011, + CO_E_INIT_ONLY_SINGLE_THREADED = 0x80004012, + + RPC_E_CALL_REJECTED = 0x80010001, + RPC_E_CALL_CANCELED = 0x80010002, + RPC_E_CANTPOST_INSENDCALL = 0x80010003, + RPC_E_CANTCALLOUT_INASYNCCALL = 0x80010004, + RPC_E_CANTCALLOUT_INEXTERNALCALL = 0x80010005, + RPC_E_CONNECTION_TERMINATED = 0x80010006, + RPC_E_SERVER_DIED = 0x80010007, + RPC_E_CLIENT_DIED = 0x80010008, + RPC_E_INVALID_DATAPACKET = 0x80010009, + RPC_E_CANTTRANSMIT_CALL = 0x8001000A, + RPC_E_CLIENT_CANTMARSHAL_DATA = 0x8001000B, + RPC_E_CLIENT_CANTUNMARSHAL_DATA = 0x8001000C, + RPC_E_SERVER_CANTMARSHAL_DATA = 0x8001000D, + RPC_E_SERVER_CANTUNMARSHAL_DATA = 0x8001000E, + RPC_E_INVALID_DATA = 0x8001000F, + RPC_E_INVALID_PARAMETER = 0x80010010, + RPC_E_CANTCALLOUT_AGAIN = 0x80010011, + RPC_E_SERVER_DIED_DNE = 0x80010012, + RPC_E_SYS_CALL_FAILED = 0x80010100, + RPC_E_OUT_OF_RESOURCES = 0x80010101, + RPC_E_ATTEMPTED_MULTITHREAD = 0x80010102, + RPC_E_NOT_REGISTERED = 0x80010103, + RPC_E_FAULT = 0x80010104, + RPC_E_SERVERFAULT = 0x80010105, + RPC_E_CHANGED_MODE = 0x80010106, + RPC_E_INVALIDMETHOD = 0x80010107, + RPC_E_DISCONNECTED = 0x80010108, + RPC_E_RETRY = 0x80010109, + RPC_E_SERVERCALL_RETRYLATER = 0x8001010A, + RPC_E_SERVERCALL_REJECTED = 0x8001010B, + RPC_E_INVALID_CALLDATA = 0x8001010C, + RPC_E_CANTCALLOUT_ININPUTSYNCCALL = 0x8001010D, + RPC_E_WRONG_THREAD = 0x8001010E, + RPC_E_THREAD_NOT_INIT = 0x8001010F, + RPC_E_UNEXPECTED = 0x8001FFFF, + + DISP_E_UNKNOWNINTERFACE = 0x80020001, + DISP_E_MEMBERNOTFOUND = 0x80020003, + DISP_E_PARAMNOTFOUND = 0x80020004, + DISP_E_TYPEMISMATCH = 0x80020005, + DISP_E_UNKNOWNNAME = 0x80020006, + DISP_E_NONAMEDARGS = 0x80020007, + DISP_E_BADVARTYPE = 0x80020008, + DISP_E_EXCEPTION = 0x80020009, + DISP_E_OVERFLOW = 0x8002000A, + DISP_E_BADINDEX = 0x8002000B, + DISP_E_UNKNOWNLCID = 0x8002000C, + DISP_E_ARRAYISLOCKED = 0x8002000D, + DISP_E_BADPARAMCOUNT = 0x8002000E, + DISP_E_PARAMNOTOPTIONAL = 0x8002000F, + DISP_E_BADCALLEE = 0x80020010, + DISP_E_NOTACOLLECTION = 0x80020011, + DISP_E_DIVBYZERO = 0x80020012, + + TYPE_E_BUFFERTOOSMALL = 0x80028016, + TYPE_E_INVDATAREAD = 0x80028018, + TYPE_E_UNSUPFORMAT = 0x80028019, + TYPE_E_REGISTRYACCESS = 0x8002801C, + TYPE_E_LIBNOTREGISTERED = 0x8002801D, + TYPE_E_UNDEFINEDTYPE = 0x80028027, + TYPE_E_QUALIFIEDNAMEDISALLOWED = 0x80028028, + TYPE_E_INVALIDSTATE = 0x80028029, + TYPE_E_WRONGTYPEKIND = 0x8002802A, + TYPE_E_ELEMENTNOTFOUND = 0x8002802B, + TYPE_E_AMBIGUOUSNAME = 0x8002802C, + TYPE_E_NAMECONFLICT = 0x8002802D, + TYPE_E_UNKNOWNLCID = 0x8002802E, + TYPE_E_DLLFUNCTIONNOTFOUND = 0x8002802F, + TYPE_E_BADMODULEKIND = 0x800288BD, + TYPE_E_SIZETOOBIG = 0x800288C5, + TYPE_E_DUPLICATEID = 0x800288C6, + TYPE_E_INVALIDID = 0x800288CF, + TYPE_E_TYPEMISMATCH = 0x80028CA0, + TYPE_E_OUTOFBOUNDS = 0x80028CA1, + TYPE_E_IOERROR = 0x80028CA2, + TYPE_E_CANTCREATETMPFILE = 0x80028CA3, + TYPE_E_CANTLOADLIBRARY = 0x80029C4A, + TYPE_E_INCONSISTENTPROPFUNCS = 0x80029C83, + TYPE_E_CIRCULARTYPE = 0x80029C84, + + STG_E_INVALIDFUNCTION = 0x80030001, + STG_E_FILENOTFOUND = 0x80030002, + STG_E_PATHNOTFOUND = 0x80030003, + STG_E_TOOMANYOPENFILES = 0x80030004, + STG_E_ACCESSDENIED = 0x80030005, + STG_E_INVALIDHANDLE = 0x80030006, + STG_E_INSUFFICIENTMEMORY = 0x80030008, + STG_E_INVALIDPOINTER = 0x80030009, + STG_E_NOMOREFILES = 0x80030012, + STG_E_DISKISWRITEPROTECTED = 0x80030013, + STG_E_SEEKERROR = 0x80030019, + STG_E_WRITEFAULT = 0x8003001D, + STG_E_READFAULT = 0x8003001E, + STG_E_SHAREVIOLATION = 0x80030020, + STG_E_LOCKVIOLATION = 0x80030021, + STG_E_FILEALREADYEXISTS = 0x80030050, + STG_E_INVALIDPARAMETER = 0x80030057, + STG_E_MEDIUMFULL = 0x80030070, + STG_E_ABNORMALAPIEXIT = 0x800300FA, + STG_E_INVALIDHEADER = 0x800300FB, + STG_E_INVALIDNAME = 0x800300FC, + STG_E_UNKNOWN = 0x800300FD, + STG_E_UNIMPLEMENTEDFUNCTION = 0x800300FE, + STG_E_INVALIDFLAG = 0x800300FF, + STG_E_INUSE = 0x80030100, + STG_E_NOTCURRENT = 0x80030101, + STG_E_REVERTED = 0x80030102, + STG_E_CANTSAVE = 0x80030103, + STG_E_OLDFORMAT = 0x80030104, + STG_E_OLDDLL = 0x80030105, + STG_E_SHAREREQUIRED = 0x80030106, + STG_E_NOTFILEBASEDSTORAGE = 0x80030107, + STG_E_EXTANTMARSHALLINGS = 0x80030108, + STG_S_CONVERTED = 0x00030200, + + OLE_E_FIRST = 0x80040000, + OLE_S_FIRST = 0x00040000, + OLE_E_OLEVERB = 0x80040000, + OLE_S_USEREG = 0x00040000, + OLE_E_ADVF = 0x80040001, + OLE_S_STATIC = 0x00040001, + OLE_E_ENUM_NOMORE = 0x80040002, + OLE_S_MAC_CLIPFORMAT = 0x00040002, + OLE_E_ADVISENOTSUPPORTED = 0x80040003, + OLE_E_NOCONNECTION = 0x80040004, + OLE_E_NOTRUNNING = 0x80040005, + OLE_E_NOCACHE = 0x80040006, + OLE_E_BLANK = 0x80040007, + OLE_E_CLASSDIFF = 0x80040008, + OLE_E_CANT_GETMONIKER = 0x80040009, + OLE_E_CANT_BINDTOSOURCE = 0x8004000A, + OLE_E_STATIC = 0x8004000B, + OLE_E_PROMPTSAVECANCELLED = 0x8004000C, + OLE_E_INVALIDRECT = 0x8004000D, + OLE_E_WRONGCOMPOBJ = 0x8004000E, + OLE_E_INVALIDHWND = 0x8004000F, + OLE_E_NOT_INPLACEACTIVE = 0x80040010, + OLE_E_CANTCONVERT = 0x80040011, + OLE_E_NOSTORAGE = 0x80040012, + + DV_E_FORMATETC = 0x80040064, + DV_E_DVTARGETDEVICE = 0x80040065, + DV_E_STGMEDIUM = 0x80040066, + DV_E_STATDATA = 0x80040067, + DV_E_LINDEX = 0x80040068, + DV_E_TYMED = 0x80040069, + DV_E_CLIPFORMAT = 0x8004006A, + DV_E_DVASPECT = 0x8004006B, + DV_E_DVTARGETDEVICE_SIZE = 0x8004006C, + DV_E_NOIVIEWOBJECT = 0x8004006D, + + OLE_E_LAST = 0x800400FF, + OLE_S_LAST = 0x000400FF, + DRAGDROP_E_FIRST = 0x80040100, + DRAGDROP_S_FIRST = 0x00040100, + DRAGDROP_E_NOTREGISTERED = 0x80040100, + DRAGDROP_S_DROP = 0x00040100, + DRAGDROP_E_ALREADYREGISTERED = 0x80040101, + DRAGDROP_S_CANCEL = 0x00040101, + DRAGDROP_E_INVALIDHWND = 0x80040102, + DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102, + DRAGDROP_E_LAST = 0x8004010F, + DRAGDROP_S_LAST = 0x0004010F, + CLASSFACTORY_E_FIRST = 0x80040110, + CLASSFACTORY_S_FIRST = 0x00040110, + CLASS_E_NOAGGREGATION = 0x80040110, + CLASS_E_CLASSNOTAVAILABLE = 0x80040111, + CLASSFACTORY_E_LAST = 0x8004011F, + CLASSFACTORY_S_LAST = 0x0004011F, + MARSHAL_E_FIRST = 0x80040120, + MARSHAL_S_FIRST = 0x00040120, + MARSHAL_E_LAST = 0x8004012F, + MARSHAL_S_LAST = 0x0004012F, + DATA_E_FIRST = 0x80040130, + DATA_S_FIRST = 0x00040130, + DATA_S_SAMEFORMATETC = 0x00040130, + DATA_E_LAST = 0x8004013F, + DATA_S_LAST = 0x0004013F, + VIEW_E_FIRST = 0x80040140, + VIEW_S_FIRST = 0x00040140, + VIEW_E_DRAW = 0x80040140, + VIEW_S_ALREADY_FROZEN = 0x00040140, + VIEW_E_LAST = 0x8004014F, + VIEW_S_LAST = 0x0004014F, + REGDB_E_FIRST = 0x80040150, + REGDB_S_FIRST = 0x00040150, + REGDB_E_READREGDB = 0x80040150, + REGDB_E_WRITEREGDB = 0x80040151, + REGDB_E_KEYMISSING = 0x80040152, + REGDB_E_INVALIDVALUE = 0x80040153, + REGDB_E_CLASSNOTREG = 0x80040154, + REGDB_E_IIDNOTREG = 0x80040155, + REGDB_E_LAST = 0x8004015F, + REGDB_S_LAST = 0x0004015F, + CACHE_E_FIRST = 0x80040170, + CACHE_S_FIRST = 0x00040170, + CACHE_E_NOCACHE_UPDATED = 0x80040170, + CACHE_S_FORMATETC_NOTSUPPORTED = 0x00040170, + CACHE_S_SAMECACHE = 0x00040171, + CACHE_S_SOMECACHES_NOTUPDATED = 0x00040172, + CACHE_E_LAST = 0x8004017F, + CACHE_S_LAST = 0x0004017F, + OLEOBJ_E_FIRST = 0x80040180, + OLEOBJ_S_FIRST = 0x00040180, + OLEOBJ_E_NOVERBS = 0x80040180, + OLEOBJ_S_INVALIDVERB = 0x00040180, + OLEOBJ_E_INVALIDVERB = 0x80040181, + OLEOBJ_S_CANNOT_DOVERB_NOW = 0x00040181, + OLEOBJ_S_INVALIDHWND = 0x00040182, + OLEOBJ_E_LAST = 0x8004018F, + OLEOBJ_S_LAST = 0x0004018F, + CLIENTSITE_E_FIRST = 0x80040190, + CLIENTSITE_S_FIRST = 0x00040190, + CLIENTSITE_E_LAST = 0x8004019F, + CLIENTSITE_S_LAST = 0x0004019F, + INPLACE_E_NOTUNDOABLE = 0x800401A0, + INPLACE_E_FIRST = 0x800401A0, + INPLACE_S_FIRST = 0x000401A0, + INPLACE_S_TRUNCATED = 0x000401A0, + INPLACE_E_NOTOOLSPACE = 0x800401A1, + INPLACE_E_LAST = 0x800401AF, + INPLACE_S_LAST = 0x000401AF, + ENUM_E_FIRST = 0x800401B0, + ENUM_S_FIRST = 0x000401B0, + ENUM_E_LAST = 0x800401BF, + ENUM_S_LAST = 0x000401BF, + CONVERT10_E_FIRST = 0x800401C0, + CONVERT10_S_FIRST = 0x000401C0, + CONVERT10_E_OLESTREAM_GET = 0x800401C0, + CONVERT10_S_NO_PRESENTATION = 0x000401C0, + CONVERT10_E_OLESTREAM_PUT = 0x800401C1, + CONVERT10_E_OLESTREAM_FMT = 0x800401C2, + CONVERT10_E_OLESTREAM_BITMAP_TO_DIB = 0x800401C3, + CONVERT10_E_STG_FMT = 0x800401C4, + CONVERT10_E_STG_NO_STD_STREAM = 0x800401C5, + CONVERT10_E_STG_DIB_TO_BITMAP = 0x800401C6, + CONVERT10_E_LAST = 0x800401CF, + CONVERT10_S_LAST = 0x000401CF, + CLIPBRD_E_FIRST = 0x800401D0, + CLIPBRD_S_FIRST = 0x000401D0, + CLIPBRD_E_CANT_OPEN = 0x800401D0, + CLIPBRD_E_CANT_EMPTY = 0x800401D1, + CLIPBRD_E_CANT_SET = 0x800401D2, + CLIPBRD_E_BAD_DATA = 0x800401D3, + CLIPBRD_E_CANT_CLOSE = 0x800401D4, + CLIPBRD_E_LAST = 0x800401DF, + CLIPBRD_S_LAST = 0x000401DF, + MK_E_FIRST = 0x800401E0, + MK_S_FIRST = 0x000401E0, + MK_E_CONNECTMANUALLY = 0x800401E0, + MK_E_EXCEEDEDDEADLINE = 0x800401E1, + MK_E_NEEDGENERIC = 0x800401E2, + MK_S_REDUCED_TO_SELF = 0x000401E2, + MK_E_UNAVAILABLE = 0x800401E3, + MK_E_SYNTAX = 0x800401E4, + MK_S_ME = 0x000401E4, + MK_E_NOOBJECT = 0x800401E5, + MK_S_HIM = 0x000401E5, + MK_E_INVALIDEXTENSION = 0x800401E6, + MK_S_US = 0x000401E6, + MK_E_INTERMEDIATEINTERFACENOTSUPPORTED = 0x800401E7, + MK_S_MONIKERALREADYREGISTERED = 0x000401E7, + MK_E_NOTBINDABLE = 0x800401E8, + MK_E_NOTBOUND = 0x800401E9, + MK_E_CANTOPENFILE = 0x800401EA, + MK_E_MUSTBOTHERUSER = 0x800401EB, + MK_E_NOINVERSE = 0x800401EC, + MK_E_NOSTORAGE = 0x800401ED, + MK_E_NOPREFIX = 0x800401EE, + MK_E_LAST = 0x800401EF, + MK_S_LAST = 0x000401EF, + MK_E_ENUMERATION_FAILED = 0x800401EF, + CO_E_FIRST = 0x800401F0, + CO_S_FIRST = 0x000401F0, + CO_E_NOTINITIALIZED = 0x800401F0, + CO_E_ALREADYINITIALIZED = 0x800401F1, + CO_E_CANTDETERMINECLASS = 0x800401F2, + CO_E_CLASSSTRING = 0x800401F3, + CO_E_IIDSTRING = 0x800401F4, + CO_E_APPNOTFOUND = 0x800401F5, + CO_E_APPSINGLEUSE = 0x800401F6, + CO_E_ERRORINAPP = 0x800401F7, + CO_E_DLLNOTFOUND = 0x800401F8, + CO_E_ERRORINDLL = 0x800401F9, + CO_E_WRONGOSFORAPP = 0x800401FA, + CO_E_OBJNOTREG = 0x800401FB, + CO_E_OBJISREG = 0x800401FC, + CO_E_OBJNOTCONNECTED = 0x800401FD, + CO_E_APPDIDNTREG = 0x800401FE, + CO_E_LAST = 0x800401FF, + CO_S_LAST = 0x000401FF, + CO_E_RELEASED = 0x800401FF, + + CO_E_CLASS_CREATE_FAILED = 0x80080001, + CO_E_SCM_ERROR = 0x80080002, + CO_E_SCM_RPC_FAILURE = 0x80080003, + CO_E_BAD_PATH = 0x80080004, + CO_E_SERVER_EXEC_FAILURE = 0x80080005, + CO_E_OBJSRV_RPC_FAILURE = 0x80080006, + MK_E_NO_NORMALIZED = 0x80080007, + CO_E_SERVER_STOPPING = 0x80080008, + MEM_E_INVALID_ROOT = 0x80080009, + MEM_E_INVALID_LINK = 0x80080010, + MEM_E_INVALID_SIZE = 0x80080011, + CO_S_NOTALLINTERFACES = 0x00080012, + + NTE_BAD_UID = 0x80090001, + NTE_BAD_HASH = 0x80090002, + NTE_BAD_KEY = 0x80090003, + NTE_BAD_LEN = 0x80090004, + NTE_BAD_DATA = 0x80090005, + NTE_BAD_SIGNATURE = 0x80090006, + NTE_BAD_VER = 0x80090007, + NTE_BAD_ALGID = 0x80090008, + NTE_BAD_FLAGS = 0x80090009, + NTE_BAD_TYPE = 0x8009000A, + NTE_BAD_KEY_STATE = 0x8009000B, + NTE_BAD_HASH_STATE = 0x8009000C, + NTE_NO_KEY = 0x8009000D, + NTE_NO_MEMORY = 0x8009000E, + NTE_EXISTS = 0x8009000F, + NTE_PERM = 0x80090010, + NTE_NOT_FOUND = 0x80090011, + NTE_DOUBLE_ENCRYPT = 0x80090012, + NTE_BAD_PROVIDER = 0x80090013, + NTE_BAD_PROV_TYPE = 0x80090014, + NTE_BAD_PUBLIC_KEY = 0x80090015, + NTE_BAD_KEYSET = 0x80090016, + NTE_PROV_TYPE_NOT_DEF = 0x80090017, + NTE_PROV_TYPE_ENTRY_BAD = 0x80090018, + NTE_KEYSET_NOT_DEF = 0x80090019, + NTE_KEYSET_ENTRY_BAD = 0x8009001A, + NTE_PROV_TYPE_NO_MATCH = 0x8009001B, + NTE_SIGNATURE_FILE_BAD = 0x8009001C, + NTE_PROVIDER_DLL_FAIL = 0x8009001D, + NTE_PROV_DLL_NOT_FOUND = 0x8009001E, + NTE_BAD_KEYSET_PARAM = 0x8009001F, + NTE_FAIL = 0x80090020, + NTE_SYS_ERR = 0x80090021 +} + + +enum : uint { + SEVERITY_SUCCESS = 0, + SEVERITY_ERROR = 1 +} + +enum : uint { + FACILITY_NULL = 0, + FACILITY_RPC, + FACILITY_DISPATCH, + FACILITY_STORAGE, + FACILITY_ITF, // = 4 + FACILITY_WIN32 = 7, + FACILITY_WINDOWS = 8, + FACILITY_CONTROL = 10, + FACILITY_NT_BIT = 0x10000000 +} + +// C Macros + +pure nothrow @nogc { + bool SUCCEEDED(HRESULT Status) { + return Status >= 0; + } + + bool FAILED(HRESULT Status) { + return Status < 0; + } + + bool IS_ERROR(HRESULT Status) { + return (Status >>> 31) == SEVERITY_ERROR; + } + + ushort HRESULT_CODE(HRESULT r) { + return cast(ushort) (r & 0xFFFF); + } + + ushort SCODE_CODE(SCODE r) { + return cast(ushort) (r & 0xFFFF); + } + + ushort HRESULT_FACILITY(HRESULT r) { + return cast(ushort) ((r>>16) & 0x1fff); + } + + ushort SCODE_FACILITY(SCODE r) { + return cast(ushort) ((r>>16) & 0x1fff); + } + + ushort HRESULT_SEVERITY(HRESULT r) { + return cast(ushort) ((r>>31) & 0x1); + } + + ushort SCODE_SEVERITY(SCODE r) { + return cast(ushort) ((r>>31) & 0x1); + } + + HRESULT MAKE_HRESULT(bool s, uint f, uint c) { + return (s << 31) | (f << 16) | c; + } + + SCODE MAKE_SCODE(bool s, uint f, uint c) { + return (s << 31) | (f << 16) | c; + } + + SCODE GetScode(HRESULT hr) { + return hr; + } + + HRESULT ResultFromScode(SCODE c) { + return c; + } + + HRESULT HRESULT_FROM_NT(HRESULT x) { + return x | FACILITY_NT_BIT; + } + + HRESULT HRESULT_FROM_WIN32(HRESULT x) { + return x ? (x & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000 : 0; + } + + HRESULT PropagateResult(HRESULT hrPrevious, SCODE scBase) { + return scBase; + } +} diff --git a/src/urt/internal/sys/windows/winnt.d b/src/urt/internal/sys/windows/winnt.d new file mode 100644 index 0000000..882b21d --- /dev/null +++ b/src/urt/internal/sys/windows/winnt.d @@ -0,0 +1,4321 @@ +/** + * Windows API header module + * + * Translated from MinGW API for MS-Windows 3.12 + * + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(DRUNTIMESRC core/sys/windows/_winnt.d) + */ +module urt.internal.sys.windows.winnt; +version (Windows): + +version (ANSI) {} else version = Unicode; + +public import urt.internal.sys.windows.basetsd, urt.internal.sys.windows.windef, urt.internal.sys.windows.winerror; +import urt.internal.sys.windows.w32api; + +/* Translation Notes: +The following macros are unneeded for D: +FIELD_OFFSET(t,f), CONTAINING_RECORD(address, type, field) +*/ + +alias void VOID; +alias char CHAR, CCHAR; +alias wchar WCHAR; +alias bool BOOLEAN; +alias byte FCHAR; +alias ubyte UCHAR; +alias short SHORT; +alias ushort LANGID, FSHORT; +alias uint LCID, FLONG, ACCESS_MASK; +alias long LONGLONG, USN; +alias ulong DWORDLONG, ULONGLONG; + +alias void* PVOID, LPVOID; +alias char* PSZ, PCHAR, PCCHAR, LPCH, PCH, LPSTR, PSTR; +alias wchar* PWCHAR, LPWCH, PWCH, LPWSTR, PWSTR; +alias bool* PBOOLEAN; +alias ubyte* PUCHAR; +alias short* PSHORT; +alias int* PLONG; +alias uint* PLCID, PACCESS_MASK; +alias long* PLONGLONG; +alias ulong* PDWORDLONG, PULONGLONG; + +// FIXME(MinGW) for __WIN64 +alias void* PVOID64; + +// const versions +alias const(char)* PCCH, LPCCH, PCSTR, LPCSTR; +alias const(wchar)* LPCWCH, PCWCH, LPCWSTR, PCWSTR; + +alias PSTR* PZPSTR; +alias PWSTR* PZPWSTR; + +version (Unicode) { + alias WCHAR TCHAR, _TCHAR; +} else { + alias CHAR TCHAR, _TCHAR; +} + +alias TCHAR TBYTE; +alias TCHAR* PTCH , PTBYTE, LPTCH , PTSTR , LPTSTR , LP, PTCHAR; +alias const(TCHAR)* PCTCH, LPCTCH, PCTSTR, LPCTSTR ; + +enum char ANSI_NULL = '\0'; +enum wchar UNICODE_NULL = '\0'; + +enum APPLICATION_ERROR_MASK = 0x20000000; +enum ERROR_SEVERITY_SUCCESS = 0x00000000; +enum ERROR_SEVERITY_INFORMATIONAL = 0x40000000; +enum ERROR_SEVERITY_WARNING = 0x80000000; +enum ERROR_SEVERITY_ERROR = 0xC0000000; + +// MinGW: also in ddk/ntifs.h +enum : USHORT { + COMPRESSION_FORMAT_NONE = 0x0000, + COMPRESSION_FORMAT_DEFAULT = 0x0001, + COMPRESSION_FORMAT_LZNT1 = 0x0002, + COMPRESSION_ENGINE_STANDARD = 0x0000, + COMPRESSION_ENGINE_MAXIMUM = 0x0100, + COMPRESSION_ENGINE_HIBER = 0x0200 +} + +// ACCESS_DENIED_OBJECT_ACE, etc +enum DWORD + ACE_OBJECT_TYPE_PRESENT = 0x00000001, + ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x00000002; + +// ACE_HEADER.AceType +// also in ddk/ntifs.h +enum : BYTE { + ACCESS_ALLOWED_ACE_TYPE, + ACCESS_DENIED_ACE_TYPE, + SYSTEM_AUDIT_ACE_TYPE, + SYSTEM_ALARM_ACE_TYPE +} + +// ACE_HEADER.AceFlags +enum BYTE + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, + VALID_INHERIT_FLAGS = 0x1F, + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80; + +// Access Mask Format +enum ACCESS_MASK + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + ACCESS_SYSTEM_SECURITY = 0x01000000, + MAXIMUM_ALLOWED = 0x02000000, + GENERIC_READ = 0x80000000, + GENERIC_WRITE = 0x40000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_ALL = 0x10000000, + STANDARD_RIGHTS_REQUIRED = 0x000F0000, + STANDARD_RIGHTS_READ = 0x00020000, + STANDARD_RIGHTS_WRITE = 0x00020000, + STANDARD_RIGHTS_EXECUTE = 0x00020000, + STANDARD_RIGHTS_ALL = 0x001F0000, + SPECIFIC_RIGHTS_ALL = 0x0000FFFF; + + +enum DWORD INVALID_FILE_ATTRIBUTES = -1; + +// MinGW: Also in ddk/winddk.h +enum DWORD + FILE_LIST_DIRECTORY = 0x00000001, + FILE_READ_DATA = 0x00000001, + FILE_ADD_FILE = 0x00000002, + FILE_WRITE_DATA = 0x00000002, + FILE_ADD_SUBDIRECTORY = 0x00000004, + FILE_APPEND_DATA = 0x00000004, + FILE_CREATE_PIPE_INSTANCE = 0x00000004, + FILE_READ_EA = 0x00000008, + FILE_READ_PROPERTIES = 0x00000008, + FILE_WRITE_EA = 0x00000010, + FILE_WRITE_PROPERTIES = 0x00000010, + FILE_EXECUTE = 0x00000020, + FILE_TRAVERSE = 0x00000020, + FILE_DELETE_CHILD = 0x00000040, + FILE_READ_ATTRIBUTES = 0x00000080, + FILE_WRITE_ATTRIBUTES = 0x00000100; + +enum DWORD + FILE_SHARE_READ = 0x00000001, + FILE_SHARE_WRITE = 0x00000002, + FILE_SHARE_DELETE = 0x00000004, + FILE_SHARE_VALID_FLAGS = 0x00000007; + +enum DWORD + FILE_ATTRIBUTE_READONLY = 0x00000001, + FILE_ATTRIBUTE_HIDDEN = 0x00000002, + FILE_ATTRIBUTE_SYSTEM = 0x00000004, + FILE_ATTRIBUTE_DIRECTORY = 0x00000010, + FILE_ATTRIBUTE_ARCHIVE = 0x00000020, + FILE_ATTRIBUTE_DEVICE = 0x00000040, + FILE_ATTRIBUTE_NORMAL = 0x00000080, + FILE_ATTRIBUTE_TEMPORARY = 0x00000100, + FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200, + FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400, + FILE_ATTRIBUTE_COMPRESSED = 0x00000800, + FILE_ATTRIBUTE_OFFLINE = 0x00001000, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000, + FILE_ATTRIBUTE_ENCRYPTED = 0x00004000, + FILE_ATTRIBUTE_VALID_FLAGS = 0x00007fb7, + FILE_ATTRIBUTE_VALID_SET_FLAGS = 0x000031a7; + +// These are not documented on MSDN +enum FILE_COPY_STRUCTURED_STORAGE = 0x00000041; +enum FILE_STRUCTURED_STORAGE = 0x00000441; + +// Nor are these +enum FILE_VALID_OPTION_FLAGS = 0x00ffffff; +enum FILE_VALID_PIPE_OPTION_FLAGS = 0x00000032; +enum FILE_VALID_MAILSLOT_OPTION_FLAGS = 0x00000032; +enum FILE_VALID_SET_FLAGS = 0x00000036; + +enum ULONG + FILE_SUPERSEDE = 0x00000000, + FILE_OPEN = 0x00000001, + FILE_CREATE = 0x00000002, + FILE_OPEN_IF = 0x00000003, + FILE_OVERWRITE = 0x00000004, + FILE_OVERWRITE_IF = 0x00000005, + FILE_MAXIMUM_DISPOSITION = 0x00000005; + +enum ULONG + FILE_DIRECTORY_FILE = 0x00000001, + FILE_WRITE_THROUGH = 0x00000002, + FILE_SEQUENTIAL_ONLY = 0x00000004, + FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008, + FILE_SYNCHRONOUS_IO_ALERT = 0x00000010, + FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020, + FILE_NON_DIRECTORY_FILE = 0x00000040, + FILE_CREATE_TREE_CONNECTION = 0x00000080, + FILE_COMPLETE_IF_OPLOCKED = 0x00000100, + FILE_NO_EA_KNOWLEDGE = 0x00000200, + FILE_OPEN_FOR_RECOVERY = 0x00000400, + FILE_RANDOM_ACCESS = 0x00000800, + FILE_DELETE_ON_CLOSE = 0x00001000, + FILE_OPEN_BY_FILE_ID = 0x00002000, + FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000, + FILE_NO_COMPRESSION = 0x00008000, + FILE_RESERVE_OPFILTER = 0x00100000, + FILE_OPEN_REPARSE_POINT = 0x00200000, + FILE_OPEN_NO_RECALL = 0x00400000, + FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000; + + +enum ACCESS_MASK + FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x01FF, + FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES + | FILE_EXECUTE | SYNCHRONIZE, + FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA + | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE, + FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA + | SYNCHRONIZE; + +// MinGW: end winddk.h +// MinGW: also in ddk/ntifs.h +enum DWORD + FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001, + FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002, + FILE_NOTIFY_CHANGE_NAME = 0x00000003, + FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004, + FILE_NOTIFY_CHANGE_SIZE = 0x00000008, + FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010, + FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020, + FILE_NOTIFY_CHANGE_CREATION = 0x00000040, + FILE_NOTIFY_CHANGE_EA = 0x00000080, + FILE_NOTIFY_CHANGE_SECURITY = 0x00000100, + FILE_NOTIFY_CHANGE_STREAM_NAME = 0x00000200, + FILE_NOTIFY_CHANGE_STREAM_SIZE = 0x00000400, + FILE_NOTIFY_CHANGE_STREAM_WRITE = 0x00000800, + FILE_NOTIFY_VALID_MASK = 0x00000fff; + +enum DWORD + FILE_CASE_SENSITIVE_SEARCH = 0x00000001, + FILE_CASE_PRESERVED_NAMES = 0x00000002, + FILE_UNICODE_ON_DISK = 0x00000004, + FILE_PERSISTENT_ACLS = 0x00000008, + FILE_FILE_COMPRESSION = 0x00000010, + FILE_VOLUME_QUOTAS = 0x00000020, + FILE_SUPPORTS_SPARSE_FILES = 0x00000040, + FILE_SUPPORTS_REPARSE_POINTS = 0x00000080, + FILE_SUPPORTS_REMOTE_STORAGE = 0x00000100, + FS_LFN_APIS = 0x00004000, + FILE_VOLUME_IS_COMPRESSED = 0x00008000, + FILE_SUPPORTS_OBJECT_IDS = 0x00010000, + FILE_SUPPORTS_ENCRYPTION = 0x00020000, + FILE_NAMED_STREAMS = 0x00040000, + FILE_READ_ONLY_VOLUME = 0x00080000, + FILE_SEQUENTIAL_WRITE_ONCE = 0x00100000, + FILE_SUPPORTS_TRANSACTIONS = 0x00200000; + +// These are not documented on MSDN +enum ACCESS_MASK + IO_COMPLETION_QUERY_STATE = 1, + IO_COMPLETION_MODIFY_STATE = 2, + IO_COMPLETION_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 3; +// MinGW: end ntifs.h + +// MinGW: also in ddk/winddk.h +enum DWORD + DUPLICATE_CLOSE_SOURCE = 1, + DUPLICATE_SAME_ACCESS = 2, + DUPLICATE_SAME_ATTRIBUTES = 4; +// MinGW: end winddk.k + +enum DWORD + MAILSLOT_NO_MESSAGE = -1, + MAILSLOT_WAIT_FOREVER = -1; + +enum ACCESS_MASK + PROCESS_TERMINATE = 0x0001, + PROCESS_CREATE_THREAD = 0x0002, + PROCESS_SET_SESSIONID = 0x0004, + PROCESS_VM_OPERATION = 0x0008, + PROCESS_VM_READ = 0x0010, + PROCESS_VM_WRITE = 0x0020, + PROCESS_DUP_HANDLE = 0x0040, + PROCESS_CREATE_PROCESS = 0x0080, + PROCESS_SET_QUOTA = 0x0100, + PROCESS_SET_INFORMATION = 0x0200, + PROCESS_QUERY_INFORMATION = 0x0400, + PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x0FFF; + +enum ACCESS_MASK + THREAD_TERMINATE = 0x0001, + THREAD_SUSPEND_RESUME = 0x0002, + THREAD_GET_CONTEXT = 0x0008, + THREAD_SET_CONTEXT = 0x0010, + THREAD_SET_INFORMATION = 0x0020, + THREAD_QUERY_INFORMATION = 0x0040, + THREAD_SET_THREAD_TOKEN = 0x0080, + THREAD_IMPERSONATE = 0x0100, + THREAD_DIRECT_IMPERSONATION = 0x0200, + THREAD_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3FF; + +// These are not documented on MSDN +enum THREAD_BASE_PRIORITY_LOWRT = 15; +enum THREAD_BASE_PRIORITY_MAX = 2; +enum THREAD_BASE_PRIORITY_MIN = -2; +enum THREAD_BASE_PRIORITY_IDLE = -15; + +enum DWORD EXCEPTION_NONCONTINUABLE = 1; +enum size_t EXCEPTION_MAXIMUM_PARAMETERS = 15; + +// These are not documented on MSDN +enum ACCESS_MASK + MUTANT_QUERY_STATE = 1, + MUTANT_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | MUTANT_QUERY_STATE; + +enum ACCESS_MASK + TIMER_QUERY_STATE = 1, + TIMER_MODIFY_STATE = 2, + TIMER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE + | TIMER_MODIFY_STATE; + +enum SID_IDENTIFIER_AUTHORITY + SECURITY_NULL_SID_AUTHORITY = {[5: 0]}, + SECURITY_WORLD_SID_AUTHORITY = {[5: 1]}, + SECURITY_LOCAL_SID_AUTHORITY = {[5: 2]}, + SECURITY_CREATOR_SID_AUTHORITY = {[5: 3]}, + SECURITY_NON_UNIQUE_AUTHORITY = {[5: 4]}, + SECURITY_NT_AUTHORITY = {[5: 5]}, + SECURITY_MANDATORY_LABEL_AUTHORITY = {[5: 6]}; + +enum DWORD + SECURITY_NULL_RID = 0, + SECURITY_WORLD_RID = 0, + SECURITY_LOCAL_RID = 0, + SECURITY_CREATOR_OWNER_RID = 0, + SECURITY_CREATOR_GROUP_RID = 1, + SECURITY_DIALUP_RID = 1, + SECURITY_NETWORK_RID = 2, + SECURITY_BATCH_RID = 3, + SECURITY_INTERACTIVE_RID = 4, + SECURITY_LOGON_IDS_RID = 5, + SECURITY_SERVICE_RID = 6, + SECURITY_LOCAL_SYSTEM_RID = 18, + SECURITY_BUILTIN_DOMAIN_RID = 32, + SECURITY_PRINCIPAL_SELF_RID = 10, + SECURITY_CREATOR_OWNER_SERVER_RID = 2, + SECURITY_CREATOR_GROUP_SERVER_RID = 3, + SECURITY_LOGON_IDS_RID_COUNT = 3, + SECURITY_ANONYMOUS_LOGON_RID = 7, + SECURITY_PROXY_RID = 8, + SECURITY_ENTERPRISE_CONTROLLERS_RID = 9, + SECURITY_SERVER_LOGON_RID = SECURITY_ENTERPRISE_CONTROLLERS_RID, + SECURITY_AUTHENTICATED_USER_RID = 11, + SECURITY_RESTRICTED_CODE_RID = 12, + SECURITY_NT_NON_UNIQUE_RID = 21, + SID_REVISION = 1; + +enum : DWORD { + DOMAIN_USER_RID_ADMIN = 0x01F4, + DOMAIN_USER_RID_GUEST = 0x01F5, + DOMAIN_GROUP_RID_ADMINS = 0x0200, + DOMAIN_GROUP_RID_USERS = 0x0201, + DOMAIN_ALIAS_RID_ADMINS = 0x0220, + DOMAIN_ALIAS_RID_USERS = 0x0221, + DOMAIN_ALIAS_RID_GUESTS = 0x0222, + DOMAIN_ALIAS_RID_POWER_USERS = 0x0223, + DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x0224, + DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x0225, + DOMAIN_ALIAS_RID_PRINT_OPS = 0x0226, + DOMAIN_ALIAS_RID_BACKUP_OPS = 0x0227, + DOMAIN_ALIAS_RID_REPLICATOR = 0x0228 +} + +enum : WORD { + SECURITY_MANDATORY_UNTRUSTED_RID = 0, + SECURITY_MANDATORY_LOW_RID = 0x1000, + SECURITY_MANDATORY_MEDIUM_RID = 0x2000, + SECURITY_MANDATORY_HIGH_RID = 0x3000, + SECURITY_MANDATORY_SYSTEM_RID = 0x4000, + SECURITY_MANDATORY_PROTECTED_PROCESS_RID = 0x5000, + SECURITY_MANDATORY_MAXIMUM_USER_RID = SECURITY_MANDATORY_SYSTEM_RID +} + +const TCHAR[] + SE_CREATE_TOKEN_NAME = "SeCreateTokenPrivilege", + SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege", + SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege", + SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege", + SE_UNSOLICITED_INPUT_NAME = "SeUnsolicitedInputPrivilege", + SE_MACHINE_ACCOUNT_NAME = "SeMachineAccountPrivilege", + SE_TCB_NAME = "SeTcbPrivilege", + SE_SECURITY_NAME = "SeSecurityPrivilege", + SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege", + SE_LOAD_DRIVER_NAME = "SeLoadDriverPrivilege", + SE_SYSTEM_PROFILE_NAME = "SeSystemProfilePrivilege", + SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege", + SE_PROF_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege", + SE_INC_BASE_PRIORITY_NAME = "SeIncreaseBasePriorityPrivilege", + SE_CREATE_PAGEFILE_NAME = "SeCreatePagefilePrivilege", + SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege", + SE_BACKUP_NAME = "SeBackupPrivilege", + SE_RESTORE_NAME = "SeRestorePrivilege", + SE_SHUTDOWN_NAME = "SeShutdownPrivilege", + SE_DEBUG_NAME = "SeDebugPrivilege", + SE_AUDIT_NAME = "SeAuditPrivilege", + SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege", + SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege", + SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege", + SE_CREATE_GLOBAL_NAME = "SeCreateGlobalPrivilege", + SE_UNDOCK_NAME = "SeUndockPrivilege", + SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege", + SE_IMPERSONATE_NAME = "SeImpersonatePrivilege", + SE_ENABLE_DELEGATION_NAME = "SeEnableDelegationPrivilege", + SE_SYNC_AGENT_NAME = "SeSyncAgentPrivilege", + SE_TRUSTED_CREDMAN_ACCESS_NAME = "SeTrustedCredManAccessPrivilege", + SE_RELABEL_NAME = "SeRelabelPrivilege", + SE_INCREASE_WORKING_SET_NAME = "SeIncreaseWorkingSetPrivilege", + SE_TIME_ZONE_NAME = "SeTimeZonePrivilege", + SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege"; + +enum DWORD + SE_GROUP_MANDATORY = 0x00000001, + SE_GROUP_ENABLED_BY_DEFAULT = 0x00000002, + SE_GROUP_ENABLED = 0x00000004, + SE_GROUP_OWNER = 0x00000008, + SE_GROUP_USE_FOR_DENY_ONLY = 0x00000010, + SE_GROUP_INTEGRITY = 0x00000020, + SE_GROUP_INTEGRITY_ENABLED = 0x00000040, + SE_GROUP_RESOURCE = 0x20000000, + SE_GROUP_LOGON_ID = 0xC0000000; + +// Primary language identifiers +enum : USHORT { + LANG_NEUTRAL, + LANG_ARABIC, + LANG_BULGARIAN, + LANG_CATALAN, + LANG_CHINESE, + LANG_CZECH, + LANG_DANISH, + LANG_GERMAN, + LANG_GREEK, + LANG_ENGLISH, + LANG_SPANISH, + LANG_FINNISH, + LANG_FRENCH, + LANG_HEBREW, + LANG_HUNGARIAN, + LANG_ICELANDIC, + LANG_ITALIAN, + LANG_JAPANESE, + LANG_KOREAN, + LANG_DUTCH, + LANG_NORWEGIAN, + LANG_POLISH, + LANG_PORTUGUESE, // = 0x16 + LANG_ROMANIAN = 0x18, + LANG_RUSSIAN, + LANG_CROATIAN, // = 0x1A + LANG_SERBIAN = 0x1A, + LANG_BOSNIAN = 0x1A, + LANG_SLOVAK, + LANG_ALBANIAN, + LANG_SWEDISH, + LANG_THAI, + LANG_TURKISH, + LANG_URDU, + LANG_INDONESIAN, + LANG_UKRAINIAN, + LANG_BELARUSIAN, + LANG_SLOVENIAN, + LANG_ESTONIAN, + LANG_LATVIAN, + LANG_LITHUANIAN, // = 0x27 + LANG_FARSI = 0x29, + LANG_PERSIAN = 0x29, + LANG_VIETNAMESE, + LANG_ARMENIAN, + LANG_AZERI, + LANG_BASQUE, + LANG_LOWER_SORBIAN, // = 0x2E + LANG_UPPER_SORBIAN = 0x2E, + LANG_MACEDONIAN, // = 0x2F + LANG_TSWANA = 0x32, + LANG_XHOSA = 0x34, + LANG_ZULU, + LANG_AFRIKAANS, + LANG_GEORGIAN, + LANG_FAEROESE, + LANG_HINDI, + LANG_MALTESE, + LANG_SAMI, + LANG_IRISH, // = 0x3C + LANG_MALAY = 0x3E, + LANG_KAZAK, + LANG_KYRGYZ, + LANG_SWAHILI, // = 0x41 + LANG_UZBEK = 0x43, + LANG_TATAR, + LANG_BENGALI, + LANG_PUNJABI, + LANG_GUJARATI, + LANG_ORIYA, + LANG_TAMIL, + LANG_TELUGU, + LANG_KANNADA, + LANG_MALAYALAM, + LANG_ASSAMESE, + LANG_MARATHI, + LANG_SANSKRIT, + LANG_MONGOLIAN, + LANG_TIBETAN, + LANG_WELSH, + LANG_KHMER, + LANG_LAO, // = 0x54 + LANG_GALICIAN = 0x56, + LANG_KONKANI, + LANG_MANIPURI, + LANG_SINDHI, + LANG_SYRIAC, + LANG_SINHALESE, // = 0x5B + LANG_INUKTITUT = 0x5D, + LANG_AMHARIC, + LANG_TAMAZIGHT, + LANG_KASHMIRI, + LANG_NEPALI, + LANG_FRISIAN, + LANG_PASHTO, + LANG_FILIPINO, + LANG_DIVEHI, // = 0x65 + LANG_HAUSA = 0x68, + LANG_YORUBA = 0x6A, + LANG_QUECHUA, + LANG_SOTHO, + LANG_BASHKIR, + LANG_LUXEMBOURGISH, + LANG_GREENLANDIC, + LANG_IGBO, // = 0x70 + LANG_TIGRIGNA = 0x73, + LANG_YI = 0x78, + LANG_MAPUDUNGUN = 0x7A, + LANG_MOHAWK = 0x7C, + LANG_BRETON = 0x7E, + LANG_UIGHUR = 0x80, + LANG_MAORI, + LANG_OCCITAN, + LANG_CORSICAN, + LANG_ALSATIAN, + LANG_YAKUT, + LANG_KICHE, + LANG_KINYARWANDA, + LANG_WOLOF, // = 0x88 + LANG_DARI = 0x8C, + LANG_MALAGASY, // = 0x8D + + LANG_SERBIAN_NEUTRAL = 0x7C1A, + LANG_BOSNIAN_NEUTRAL = 0x781A, + + LANG_INVARIANT = 0x7F +} + + +// Sublanguage identifiers +enum : USHORT { + SUBLANG_NEUTRAL, + SUBLANG_DEFAULT, + SUBLANG_SYS_DEFAULT, + SUBLANG_CUSTOM_DEFAULT, // = 3 + SUBLANG_UI_CUSTOM_DEFAULT = 3, + SUBLANG_CUSTOM_UNSPECIFIED, // = 4 + + SUBLANG_AFRIKAANS_SOUTH_AFRICA = 1, + SUBLANG_ALBANIAN_ALBANIA = 1, + SUBLANG_ALSATIAN_FRANCE = 1, + SUBLANG_AMHARIC_ETHIOPIA = 1, + + SUBLANG_ARABIC_SAUDI_ARABIA = 1, + SUBLANG_ARABIC_IRAQ, + SUBLANG_ARABIC_EGYPT, + SUBLANG_ARABIC_LIBYA, + SUBLANG_ARABIC_ALGERIA, + SUBLANG_ARABIC_MOROCCO, + SUBLANG_ARABIC_TUNISIA, + SUBLANG_ARABIC_OMAN, + SUBLANG_ARABIC_YEMEN, + SUBLANG_ARABIC_SYRIA, + SUBLANG_ARABIC_JORDAN, + SUBLANG_ARABIC_LEBANON, + SUBLANG_ARABIC_KUWAIT, + SUBLANG_ARABIC_UAE, + SUBLANG_ARABIC_BAHRAIN, + SUBLANG_ARABIC_QATAR, // = 16 + + SUBLANG_ARMENIAN_ARMENIA = 1, + SUBLANG_ASSAMESE_INDIA = 1, + + SUBLANG_AZERI_LATIN = 1, + SUBLANG_AZERI_CYRILLIC, // = 2 + + SUBLANG_BASHKIR_RUSSIA = 1, + SUBLANG_BASQUE_BASQUE = 1, + SUBLANG_BELARUSIAN_BELARUS = 1, + SUBLANG_BENGALI_INDIA = 1, + + SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN = 5, + SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC = 8, + + SUBLANG_BRETON_FRANCE = 1, + SUBLANG_BULGARIAN_BULGARIA = 1, + SUBLANG_CATALAN_CATALAN = 1, + + SUBLANG_CHINESE_TRADITIONAL = 1, + SUBLANG_CHINESE_SIMPLIFIED, + SUBLANG_CHINESE_HONGKONG, + SUBLANG_CHINESE_SINGAPORE, + SUBLANG_CHINESE_MACAU, // = 5 + + SUBLANG_CORSICAN_FRANCE = 1, + + SUBLANG_CROATIAN_CROATIA = 1, + SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN = 4, + + SUBLANG_CZECH_CZECH_REPUBLIC = 1, + SUBLANG_DANISH_DENMARK = 1, + SUBLANG_DIVEHI_MALDIVES = 1, + + SUBLANG_DUTCH = 1, + SUBLANG_DUTCH_BELGIAN, // = 2 + + SUBLANG_ENGLISH_US = 1, + SUBLANG_ENGLISH_UK, + SUBLANG_ENGLISH_AUS, + SUBLANG_ENGLISH_CAN, + SUBLANG_ENGLISH_NZ, + SUBLANG_ENGLISH_EIRE, // = 6 + SUBLANG_ENGLISH_IRELAND = 6, + SUBLANG_ENGLISH_SOUTH_AFRICA, + SUBLANG_ENGLISH_JAMAICA, + SUBLANG_ENGLISH_CARIBBEAN, + SUBLANG_ENGLISH_BELIZE, + SUBLANG_ENGLISH_TRINIDAD, + SUBLANG_ENGLISH_ZIMBABWE, + SUBLANG_ENGLISH_PHILIPPINES, // = 13 + SUBLANG_ENGLISH_INDIA = 16, + SUBLANG_ENGLISH_MALAYSIA, + SUBLANG_ENGLISH_SINGAPORE, // = 18 + + SUBLANG_ESTONIAN_ESTONIA = 1, + SUBLANG_FAEROESE_FAROE_ISLANDS = 1, + SUBLANG_FILIPINO_PHILIPPINES = 1, + SUBLANG_FINNISH_FINLAND = 1, + + SUBLANG_FRENCH = 1, + SUBLANG_FRENCH_BELGIAN, + SUBLANG_FRENCH_CANADIAN, + SUBLANG_FRENCH_SWISS, + SUBLANG_FRENCH_LUXEMBOURG, + SUBLANG_FRENCH_MONACO, // = 6 + + SUBLANG_FRISIAN_NETHERLANDS = 1, + SUBLANG_GALICIAN_GALICIAN = 1, + SUBLANG_GEORGIAN_GEORGIA = 1, + + SUBLANG_GERMAN = 1, + SUBLANG_GERMAN_SWISS, + SUBLANG_GERMAN_AUSTRIAN, + SUBLANG_GERMAN_LUXEMBOURG, + SUBLANG_GERMAN_LIECHTENSTEIN, // = 5 + + SUBLANG_GREEK_GREECE = 1, + SUBLANG_GREENLANDIC_GREENLAND = 1, + SUBLANG_GUJARATI_INDIA = 1, + SUBLANG_HAUSA_NIGERIA = 1, + SUBLANG_HEBREW_ISRAEL = 1, + SUBLANG_HINDI_INDIA = 1, + SUBLANG_HUNGARIAN_HUNGARY = 1, + SUBLANG_ICELANDIC_ICELAND = 1, + SUBLANG_IGBO_NIGERIA = 1, + SUBLANG_INDONESIAN_INDONESIA = 1, + + SUBLANG_INUKTITUT_CANADA = 1, + SUBLANG_INUKTITUT_CANADA_LATIN = 1, + + SUBLANG_IRISH_IRELAND = 1, + + SUBLANG_ITALIAN = 1, + SUBLANG_ITALIAN_SWISS, // = 2 + + SUBLANG_JAPANESE_JAPAN = 1, + + SUBLANG_KASHMIRI_INDIA = 2, + SUBLANG_KASHMIRI_SASIA = 2, + + SUBLANG_KAZAK_KAZAKHSTAN = 1, + SUBLANG_KHMER_CAMBODIA = 1, + SUBLANG_KICHE_GUATEMALA = 1, + SUBLANG_KINYARWANDA_RWANDA = 1, + SUBLANG_KONKANI_INDIA = 1, + SUBLANG_KOREAN = 1, + SUBLANG_KOREAN_JOHAB = 2, + SUBLANG_KYRGYZ_KYRGYZSTAN = 1, + SUBLANG_LAO_LAO_PDR = 1, + SUBLANG_LATVIAN_LATVIA = 1, + + SUBLANG_LITHUANIAN = 1, + SUBLANG_LITHUANIAN_LITHUANIA = 1, + + SUBLANG_LOWER_SORBIAN_GERMANY = 1, + SUBLANG_LUXEMBOURGISH_LUXEMBOURG = 1, + SUBLANG_MACEDONIAN_MACEDONIA = 1, + SUBLANG_MALAYALAM_INDIA = 1, + SUBLANG_MALTESE_MALTA = 1, + SUBLANG_MAORI_NEW_ZEALAND = 1, + SUBLANG_MAPUDUNGUN_CHILE = 1, + SUBLANG_MARATHI_INDIA = 1, + SUBLANG_MOHAWK_MOHAWK = 1, + + SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA = 1, + SUBLANG_MONGOLIAN_PRC, // = 2 + + SUBLANG_MALAY_MALAYSIA = 1, + SUBLANG_MALAY_BRUNEI_DARUSSALAM, // = 2 + + SUBLANG_NEPALI_NEPAL = 1, + SUBLANG_NEPALI_INDIA, // = 2 + + SUBLANG_NORWEGIAN_BOKMAL = 1, + SUBLANG_NORWEGIAN_NYNORSK, // = 2 + + SUBLANG_OCCITAN_FRANCE = 1, + SUBLANG_ORIYA_INDIA = 1, + SUBLANG_PASHTO_AFGHANISTAN = 1, + SUBLANG_PERSIAN_IRAN = 1, + SUBLANG_POLISH_POLAND = 1, + + SUBLANG_PORTUGUESE_BRAZILIAN = 1, + SUBLANG_PORTUGUESE = 2, + SUBLANG_PORTUGUESE_PORTUGAL, // = 2 + + SUBLANG_PUNJABI_INDIA = 1, + + SUBLANG_QUECHUA_BOLIVIA = 1, + SUBLANG_QUECHUA_ECUADOR, + SUBLANG_QUECHUA_PERU, // = 3 + + SUBLANG_ROMANIAN_ROMANIA = 1, + SUBLANG_ROMANSH_SWITZERLAND = 1, + SUBLANG_RUSSIAN_RUSSIA = 1, + + SUBLANG_SAMI_NORTHERN_NORWAY = 1, + SUBLANG_SAMI_NORTHERN_SWEDEN, + SUBLANG_SAMI_NORTHERN_FINLAND, // = 3 + SUBLANG_SAMI_SKOLT_FINLAND = 3, + SUBLANG_SAMI_INARI_FINLAND = 3, + SUBLANG_SAMI_LULE_NORWAY, + SUBLANG_SAMI_LULE_SWEDEN, + SUBLANG_SAMI_SOUTHERN_NORWAY, + SUBLANG_SAMI_SOUTHERN_SWEDEN, // = 7 + + SUBLANG_SANSKRIT_INDIA = 1, + + SUBLANG_SERBIAN_LATIN = 2, + SUBLANG_SERBIAN_CYRILLIC, // = 3 + SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN = 6, + SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC = 7, + + SUBLANG_SINDHI_AFGHANISTAN = 2, + SUBLANG_SINHALESE_SRI_LANKA = 1, + SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA = 1, + SUBLANG_SLOVAK_SLOVAKIA = 1, + SUBLANG_SLOVENIAN_SLOVENIA = 1, + + SUBLANG_SPANISH = 1, + SUBLANG_SPANISH_MEXICAN, + SUBLANG_SPANISH_MODERN, + SUBLANG_SPANISH_GUATEMALA, + SUBLANG_SPANISH_COSTA_RICA, + SUBLANG_SPANISH_PANAMA, + SUBLANG_SPANISH_DOMINICAN_REPUBLIC, + SUBLANG_SPANISH_VENEZUELA, + SUBLANG_SPANISH_COLOMBIA, + SUBLANG_SPANISH_PERU, + SUBLANG_SPANISH_ARGENTINA, + SUBLANG_SPANISH_ECUADOR, + SUBLANG_SPANISH_CHILE, + SUBLANG_SPANISH_URUGUAY, + SUBLANG_SPANISH_PARAGUAY, + SUBLANG_SPANISH_BOLIVIA, + SUBLANG_SPANISH_EL_SALVADOR, + SUBLANG_SPANISH_HONDURAS, + SUBLANG_SPANISH_NICARAGUA, + SUBLANG_SPANISH_PUERTO_RICO, + SUBLANG_SPANISH_US, // = 21 + + SUBLANG_SWEDISH = 1, + SUBLANG_SWEDISH_SWEDEN = 1, + SUBLANG_SWEDISH_FINLAND, // = 2 + + SUBLANG_SYRIAC = 1, + SUBLANG_TAJIK_TAJIKISTAN = 1, + SUBLANG_TAMAZIGHT_ALGERIA_LATIN = 2, + SUBLANG_TAMIL_INDIA = 1, + SUBLANG_TATAR_RUSSIA = 1, + SUBLANG_TELUGU_INDIA = 1, + SUBLANG_THAI_THAILAND = 1, + SUBLANG_TIBETAN_PRC = 1, + SUBLANG_TIBETAN_BHUTAN = 2, + SUBLANG_TIGRIGNA_ERITREA = 1, + SUBLANG_TSWANA_SOUTH_AFRICA = 1, + SUBLANG_TURKISH_TURKEY = 1, + SUBLANG_TURKMEN_TURKMENISTAN = 1, + SUBLANG_UIGHUR_PRC = 1, + SUBLANG_UKRAINIAN_UKRAINE = 1, + SUBLANG_UPPER_SORBIAN_GERMANY = 1, + + SUBLANG_URDU_PAKISTAN = 1, + SUBLANG_URDU_INDIA, // = 2 + + SUBLANG_UZBEK_LATIN = 1, + SUBLANG_UZBEK_CYRILLIC, // = 2 + + SUBLANG_VIETNAMESE_VIETNAM = 1, + SUBLANG_WELSH_UNITED_KINGDOM = 1, + SUBLANG_WOLOF_SENEGAL = 1, + SUBLANG_YORUBA_NIGERIA = 1, + SUBLANG_XHOSA_SOUTH_AFRICA = 1, + SUBLANG_YAKUT_RUSSIA = 1, + SUBLANG_YI_PRC = 1, + SUBLANG_ZULU_SOUTH_AFRICA = 1 +} + +// This is not documented on MSDN +enum NLS_VALID_LOCALE_MASK = 1048575; + +// Sorting identifiers +enum : WORD { + SORT_DEFAULT = 0, + SORT_JAPANESE_XJIS = 0, + SORT_JAPANESE_UNICODE = 1, + SORT_CHINESE_BIG5 = 0, + SORT_CHINESE_PRCP = 0, + SORT_CHINESE_UNICODE = 1, + SORT_CHINESE_PRC = 2, + SORT_CHINESE_BOPOMOFO = 3, + SORT_KOREAN_KSC = 0, + SORT_KOREAN_UNICODE = 1, + SORT_GERMAN_PHONE_BOOK = 1, + SORT_HUNGARIAN_DEFAULT = 0, + SORT_HUNGARIAN_TECHNICAL = 1, + SORT_GEORGIAN_TRADITIONAL = 0, + SORT_GEORGIAN_MODERN = 1 +} + +pure nothrow @nogc { + WORD MAKELANGID()(/*USHORT*/uint p, /*USHORT*/ uint s) { return cast(WORD)((s << 10) | p); } + WORD PRIMARYLANGID()(/*WORD*/uint lgid) { return cast(WORD)(lgid & 0x3FF); } + WORD SUBLANGID()(/*WORD*/uint lgid) { return cast(WORD)(lgid >>> 10); } + + DWORD MAKELCID()(/*WORD*/uint lgid, /*WORD*/uint srtid) { return (cast(DWORD) srtid << 16) | cast(DWORD) lgid; } + // ??? + //DWORD MAKESORTLCID()(WORD lgid, WORD srtid, WORD ver) { return (MAKELCID(lgid, srtid)) | ((cast(DWORD)ver) << 20); } + WORD LANGIDFROMLCID()(LCID lcid) { return cast(WORD) lcid; } + WORD SORTIDFROMLCID()(LCID lcid) { return cast(WORD) ((lcid >>> 16) & 0x0F); } + WORD SORTVERSIONFROMLCID()(LCID lcid) { return cast(WORD) ((lcid >>> 20) & 0x0F); } +} + +enum WORD LANG_SYSTEM_DEFAULT = (SUBLANG_SYS_DEFAULT << 10) | LANG_NEUTRAL; +enum WORD LANG_USER_DEFAULT = (SUBLANG_DEFAULT << 10) | LANG_NEUTRAL; +enum DWORD LOCALE_NEUTRAL = (SORT_DEFAULT << 16) + | (SUBLANG_NEUTRAL << 10) | LANG_NEUTRAL; + +// --- +enum : BYTE { + ACL_REVISION = 2, + ACL_REVISION_DS = 4 +} + +// These are not documented on MSDN +enum : BYTE { + ACL_REVISION1 = 1, + ACL_REVISION2, + ACL_REVISION3, + ACL_REVISION4 // = 4 +} + +enum BYTE + MIN_ACL_REVISION = 2, + MAX_ACL_REVISION = 4; + +/+ +// These aren't necessary for D. +enum MINCHAR=0x80; +enum MAXCHAR=0x7f; +enum MINSHORT=0x8000; +enum MAXSHORT=0x7fff; +enum MINLONG=0x80000000; +enum MAXLONG=0x7fffffff; +enum MAXBYTE=0xff; +enum MAXWORD=0xffff; +enum MAXDWORD=0xffffffff; ++/ + +// SYSTEM_INFO.dwProcessorType +enum : DWORD { + PROCESSOR_INTEL_386 = 386, + PROCESSOR_INTEL_486 = 486, + PROCESSOR_INTEL_PENTIUM = 586, + PROCESSOR_MIPS_R4000 = 4000, + PROCESSOR_ALPHA_21064 = 21064, + PROCESSOR_INTEL_IA64 = 2200 +} + +// SYSTEM_INFO.wProcessorArchitecture +enum : WORD { + PROCESSOR_ARCHITECTURE_INTEL, + PROCESSOR_ARCHITECTURE_MIPS, + PROCESSOR_ARCHITECTURE_ALPHA, + PROCESSOR_ARCHITECTURE_PPC, + PROCESSOR_ARCHITECTURE_SHX, + PROCESSOR_ARCHITECTURE_ARM, + PROCESSOR_ARCHITECTURE_IA64, + PROCESSOR_ARCHITECTURE_ALPHA64, + PROCESSOR_ARCHITECTURE_MSIL, + PROCESSOR_ARCHITECTURE_AMD64, + PROCESSOR_ARCHITECTURE_IA32_ON_WIN64, // = 10 + PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFF +} + +// IsProcessorFeaturePresent() +enum : DWORD { + PF_FLOATING_POINT_PRECISION_ERRATA, + PF_FLOATING_POINT_EMULATED, + PF_COMPARE_EXCHANGE_DOUBLE, + PF_MMX_INSTRUCTIONS_AVAILABLE, + PF_PPC_MOVEMEM_64BIT_OK, + PF_ALPHA_BYTE_INSTRUCTIONS, + PF_XMMI_INSTRUCTIONS_AVAILABLE, + PF_3DNOW_INSTRUCTIONS_AVAILABLE, + PF_RDTSC_INSTRUCTION_AVAILABLE, + PF_PAE_ENABLED, + PF_XMMI64_INSTRUCTIONS_AVAILABLE +} + +// MinGW: also in ddk/ntifs.h +enum : DWORD { + FILE_ACTION_ADDED = 1, + FILE_ACTION_REMOVED, + FILE_ACTION_MODIFIED, + FILE_ACTION_RENAMED_OLD_NAME, + FILE_ACTION_RENAMED_NEW_NAME, + FILE_ACTION_ADDED_STREAM, + FILE_ACTION_REMOVED_STREAM, + FILE_ACTION_MODIFIED_STREAM, + FILE_ACTION_REMOVED_BY_DELETE, + FILE_ACTION_ID_NOT_TUNNELLED, + FILE_ACTION_TUNNELLED_ID_COLLISION // = 11 +} +// MinGW: end ntifs.h + +enum DWORD + HEAP_NO_SERIALIZE = 0x01, + HEAP_GROWABLE = 0x02, + HEAP_GENERATE_EXCEPTIONS = 0x04, + HEAP_ZERO_MEMORY = 0x08, + HEAP_REALLOC_IN_PLACE_ONLY = 0x10, + HEAP_TAIL_CHECKING_ENABLED = 0x20, + HEAP_FREE_CHECKING_ENABLED = 0x40, + HEAP_DISABLE_COALESCE_ON_FREE = 0x80; + +// These are not documented on MSDN +enum HEAP_CREATE_ALIGN_16 = 0; +enum HEAP_CREATE_ENABLE_TRACING = 0x020000; +enum HEAP_MAXIMUM_TAG = 0x000FFF; +enum HEAP_PSEUDO_TAG_FLAG = 0x008000; +enum HEAP_TAG_SHIFT = 16; +// ??? +//MACRO #define HEAP_MAKE_TAG_FLAGS(b,o) ((DWORD)((b)+(o)<<16))) + +enum ACCESS_MASK + KEY_QUERY_VALUE = 0x000001, + KEY_SET_VALUE = 0x000002, + KEY_CREATE_SUB_KEY = 0x000004, + KEY_ENUMERATE_SUB_KEYS = 0x000008, + KEY_NOTIFY = 0x000010, + KEY_CREATE_LINK = 0x000020, + KEY_WRITE = 0x020006, + KEY_EXECUTE = 0x020019, + KEY_READ = 0x020019, + KEY_ALL_ACCESS = 0x0F003F; + +static if (_WIN32_WINNT >= 0x502) { +enum ACCESS_MASK + KEY_WOW64_64KEY = 0x000100, + KEY_WOW64_32KEY = 0x000200; +} + +enum DWORD + REG_WHOLE_HIVE_VOLATILE = 1, + REG_REFRESH_HIVE = 2, + REG_NO_LAZY_FLUSH = 4; + +enum DWORD + REG_OPTION_RESERVED = 0, + REG_OPTION_NON_VOLATILE = 0, + REG_OPTION_VOLATILE = 1, + REG_OPTION_CREATE_LINK = 2, + REG_OPTION_BACKUP_RESTORE = 4, + REG_OPTION_OPEN_LINK = 8, + REG_LEGAL_OPTION = 15; + +enum SECURITY_INFORMATION + OWNER_SECURITY_INFORMATION = 0x00000001, + GROUP_SECURITY_INFORMATION = 0x00000002, + DACL_SECURITY_INFORMATION = 0x00000004, + SACL_SECURITY_INFORMATION = 0x00000008, + LABEL_SECURITY_INFORMATION = 0x00000010, + UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000, + UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000, + PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000, + PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000; + +enum DWORD MAXIMUM_PROCESSORS = 32; + +// VirtualAlloc(), etc +// ------------------- + +enum : DWORD { + PAGE_NOACCESS = 0x0001, + PAGE_READONLY = 0x0002, + PAGE_READWRITE = 0x0004, + PAGE_WRITECOPY = 0x0008, + PAGE_EXECUTE = 0x0010, + PAGE_EXECUTE_READ = 0x0020, + PAGE_EXECUTE_READWRITE = 0x0040, + PAGE_EXECUTE_WRITECOPY = 0x0080, + PAGE_GUARD = 0x0100, + PAGE_NOCACHE = 0x0200 +} + +enum : DWORD { + MEM_COMMIT = 0x00001000, + MEM_RESERVE = 0x00002000, + MEM_DECOMMIT = 0x00004000, + MEM_RELEASE = 0x00008000, + MEM_FREE = 0x00010000, + MEM_PRIVATE = 0x00020000, + MEM_MAPPED = 0x00040000, + MEM_RESET = 0x00080000, + MEM_TOP_DOWN = 0x00100000, + MEM_WRITE_WATCH = 0x00200000, // MinGW (???): 98/Me + MEM_PHYSICAL = 0x00400000, + MEM_4MB_PAGES = 0x80000000 +} + +// MinGW: also in ddk/ntifs.h +// CreateFileMapping() +enum DWORD + SEC_BASED = 0x00200000, + SEC_NO_CHANGE = 0x00400000, + SEC_FILE = 0x00800000, + SEC_IMAGE = 0x01000000, + SEC_VLM = 0x02000000, + SEC_RESERVE = 0x04000000, + SEC_COMMIT = 0x08000000, + SEC_NOCACHE = 0x10000000, + MEM_IMAGE = SEC_IMAGE; +// MinGW: end ntifs.h + +// ??? +enum ACCESS_MASK + SECTION_QUERY = 0x000001, + SECTION_MAP_WRITE = 0x000002, + SECTION_MAP_READ = 0x000004, + SECTION_MAP_EXECUTE = 0x000008, + SECTION_EXTEND_SIZE = 0x000010, + SECTION_ALL_ACCESS = 0x0F001F; + +// These are not documented on MSDN +enum MESSAGE_RESOURCE_UNICODE = 1; +enum RTL_CRITSECT_TYPE = 0; +enum RTL_RESOURCE_TYPE = 1; + +// COFF file format +// ---------------- + +// IMAGE_FILE_HEADER.Characteristics +enum WORD + IMAGE_FILE_RELOCS_STRIPPED = 0x0001, + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002, + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004, + IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008, + IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010, + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020, + IMAGE_FILE_BYTES_REVERSED_LO = 0x0080, + IMAGE_FILE_32BIT_MACHINE = 0x0100, + IMAGE_FILE_DEBUG_STRIPPED = 0x0200, + IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400, + IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800, + IMAGE_FILE_SYSTEM = 0x1000, + IMAGE_FILE_DLL = 0x2000, + IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000, + IMAGE_FILE_BYTES_REVERSED_HI = 0x8000; + +// IMAGE_FILE_HEADER.Machine +enum : WORD { + IMAGE_FILE_MACHINE_UNKNOWN = 0x0000, + IMAGE_FILE_MACHINE_I386 = 0x014C, + IMAGE_FILE_MACHINE_R3000 = 0x0162, + IMAGE_FILE_MACHINE_R4000 = 0x0166, + IMAGE_FILE_MACHINE_R10000 = 0x0168, + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169, + IMAGE_FILE_MACHINE_ALPHA = 0x0184, + IMAGE_FILE_MACHINE_SH3 = 0x01A2, + IMAGE_FILE_MACHINE_SH3DSP = 0x01A3, + IMAGE_FILE_MACHINE_SH4 = 0x01A6, + IMAGE_FILE_MACHINE_SH5 = 0x01A8, + IMAGE_FILE_MACHINE_ARM = 0x01C0, + IMAGE_FILE_MACHINE_THUMB = 0x01C2, + IMAGE_FILE_MACHINE_AM33 = 0x01D3, + IMAGE_FILE_MACHINE_POWERPC = 0x01F0, + IMAGE_FILE_MACHINE_POWERPCFP = 0x01F1, + IMAGE_FILE_MACHINE_IA64 = 0x0200, + IMAGE_FILE_MACHINE_MIPS16 = 0x0266, + IMAGE_FILE_MACHINE_MIPSFPU = 0x0366, + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466, + IMAGE_FILE_MACHINE_EBC = 0x0EBC, + IMAGE_FILE_MACHINE_AMD64 = 0x8664, + IMAGE_FILE_MACHINE_M32R = 0x9041, + IMAGE_FILE_MACHINE_ARM64 = 0xAA64, +} + +// ??? +enum { + IMAGE_DOS_SIGNATURE = 0x5A4D, + IMAGE_OS2_SIGNATURE = 0x454E, + IMAGE_OS2_SIGNATURE_LE = 0x454C, + IMAGE_VXD_SIGNATURE = 0x454C, + IMAGE_NT_SIGNATURE = 0x4550 +} + +// IMAGE_OPTIONAL_HEADER.Magic +enum : WORD { + IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x010B, + IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x0107, + IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x020B +} + +// IMAGE_OPTIONAL_HEADER.Subsystem +enum : WORD { + IMAGE_SUBSYSTEM_UNKNOWN = 0, + IMAGE_SUBSYSTEM_NATIVE, + IMAGE_SUBSYSTEM_WINDOWS_GUI, + IMAGE_SUBSYSTEM_WINDOWS_CUI, // = 3 + IMAGE_SUBSYSTEM_OS2_CUI = 5, + IMAGE_SUBSYSTEM_POSIX_CUI = 7, + IMAGE_SUBSYSTEM_NATIVE_WINDOWS, + IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, + IMAGE_SUBSYSTEM_EFI_APPLICATION, + IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, + IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, + IMAGE_SUBSYSTEM_EFI_ROM, + IMAGE_SUBSYSTEM_XBOX, // = 14 + IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16 +} + +// IMAGE_OPTIONAL_HEADER.DllCharacteristics +enum WORD + IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040, + IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080, + IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100, + IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, + IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, + IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, + IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000; + +// ??? +enum IMAGE_SEPARATE_DEBUG_SIGNATURE = 0x4944; + +enum size_t + IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16, + IMAGE_SIZEOF_ROM_OPTIONAL_HEADER = 56, + IMAGE_SIZEOF_STD_OPTIONAL_HEADER = 28, + IMAGE_SIZEOF_NT_OPTIONAL_HEADER = 224, + IMAGE_SIZEOF_SHORT_NAME = 8, + IMAGE_SIZEOF_SECTION_HEADER = 40, + IMAGE_SIZEOF_SYMBOL = 18, + IMAGE_SIZEOF_AUX_SYMBOL = 18, + IMAGE_SIZEOF_RELOCATION = 10, + IMAGE_SIZEOF_BASE_RELOCATION = 8, + IMAGE_SIZEOF_LINENUMBER = 6, + IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR = 60, + SIZEOF_RFPO_DATA = 16; + +PIMAGE_SECTION_HEADER IMAGE_FIRST_SECTION(PIMAGE_NT_HEADERS h) { + return cast(PIMAGE_SECTION_HEADER) + (cast(ubyte*) &h.OptionalHeader + h.FileHeader.SizeOfOptionalHeader); +} + +// ImageDirectoryEntryToDataEx() +enum : USHORT { + IMAGE_DIRECTORY_ENTRY_EXPORT = 0, + IMAGE_DIRECTORY_ENTRY_IMPORT, + IMAGE_DIRECTORY_ENTRY_RESOURCE, + IMAGE_DIRECTORY_ENTRY_EXCEPTION, + IMAGE_DIRECTORY_ENTRY_SECURITY, + IMAGE_DIRECTORY_ENTRY_BASERELOC, + IMAGE_DIRECTORY_ENTRY_DEBUG, + IMAGE_DIRECTORY_ENTRY_COPYRIGHT, // = 7 + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, + IMAGE_DIRECTORY_ENTRY_GLOBALPTR, + IMAGE_DIRECTORY_ENTRY_TLS, + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, + IMAGE_DIRECTORY_ENTRY_IAT, + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, // = 14 +} + +// IMAGE_SECTION_HEADER.Characteristics +enum DWORD + IMAGE_SCN_TYPE_REG = 0x00000000, + IMAGE_SCN_TYPE_DSECT = 0x00000001, + IMAGE_SCN_TYPE_NOLOAD = 0x00000002, + IMAGE_SCN_TYPE_GROUP = 0x00000004, + IMAGE_SCN_TYPE_NO_PAD = 0x00000008, + IMAGE_SCN_TYPE_COPY = 0x00000010, + IMAGE_SCN_CNT_CODE = 0x00000020, + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, + IMAGE_SCN_LNK_OTHER = 0x00000100, + IMAGE_SCN_LNK_INFO = 0x00000200, + IMAGE_SCN_TYPE_OVER = 0x00000400, + IMAGE_SCN_LNK_REMOVE = 0x00000800, + IMAGE_SCN_LNK_COMDAT = 0x00001000, + IMAGE_SCN_MEM_FARDATA = 0x00008000, + IMAGE_SCN_GPREL = 0x00008000, + IMAGE_SCN_MEM_PURGEABLE = 0x00020000, + IMAGE_SCN_MEM_16BIT = 0x00020000, + IMAGE_SCN_MEM_LOCKED = 0x00040000, + IMAGE_SCN_MEM_PRELOAD = 0x00080000, + IMAGE_SCN_ALIGN_1BYTES = 0x00100000, + IMAGE_SCN_ALIGN_2BYTES = 0x00200000, + IMAGE_SCN_ALIGN_4BYTES = 0x00300000, + IMAGE_SCN_ALIGN_8BYTES = 0x00400000, + IMAGE_SCN_ALIGN_16BYTES = 0x00500000, + IMAGE_SCN_ALIGN_32BYTES = 0x00600000, + IMAGE_SCN_ALIGN_64BYTES = 0x00700000, + IMAGE_SCN_ALIGN_128BYTES = 0x00800000, + IMAGE_SCN_ALIGN_256BYTES = 0x00900000, + IMAGE_SCN_ALIGN_512BYTES = 0x00A00000, + IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000, + IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000, + IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000, + IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000, + IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, + IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, + IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, + IMAGE_SCN_MEM_SHARED = 0x10000000, + IMAGE_SCN_MEM_EXECUTE = 0x20000000, + IMAGE_SCN_MEM_READ = 0x40000000, + IMAGE_SCN_MEM_WRITE = 0x80000000; + +/* The following constants are mostlydocumented at + * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pecoff.doc + * but don't seem to be defined in the HTML docs. + */ +enum : SHORT { + IMAGE_SYM_UNDEFINED = 0, + IMAGE_SYM_ABSOLUTE = -1, + IMAGE_SYM_DEBUG = -2 +} + +enum : ubyte { + IMAGE_SYM_TYPE_NULL, + IMAGE_SYM_TYPE_VOID, + IMAGE_SYM_TYPE_CHAR, + IMAGE_SYM_TYPE_SHORT, + IMAGE_SYM_TYPE_INT, + IMAGE_SYM_TYPE_LONG, + IMAGE_SYM_TYPE_FLOAT, + IMAGE_SYM_TYPE_DOUBLE, + IMAGE_SYM_TYPE_STRUCT, + IMAGE_SYM_TYPE_UNION, + IMAGE_SYM_TYPE_ENUM, + IMAGE_SYM_TYPE_MOE, + IMAGE_SYM_TYPE_BYTE, + IMAGE_SYM_TYPE_WORD, + IMAGE_SYM_TYPE_UINT, + IMAGE_SYM_TYPE_DWORD // = 15 +} +enum IMAGE_SYM_TYPE_PCODE = 32768; // ??? + +enum : ubyte { + IMAGE_SYM_DTYPE_NULL, + IMAGE_SYM_DTYPE_POINTER, + IMAGE_SYM_DTYPE_FUNCTION, + IMAGE_SYM_DTYPE_ARRAY +} + +enum : BYTE { + IMAGE_SYM_CLASS_END_OF_FUNCTION = 0xFF, + IMAGE_SYM_CLASS_NULL = 0, + IMAGE_SYM_CLASS_AUTOMATIC, + IMAGE_SYM_CLASS_EXTERNAL, + IMAGE_SYM_CLASS_STATIC, + IMAGE_SYM_CLASS_REGISTER, + IMAGE_SYM_CLASS_EXTERNAL_DEF, + IMAGE_SYM_CLASS_LABEL, + IMAGE_SYM_CLASS_UNDEFINED_LABEL, + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT, + IMAGE_SYM_CLASS_ARGUMENT, + IMAGE_SYM_CLASS_STRUCT_TAG, + IMAGE_SYM_CLASS_MEMBER_OF_UNION, + IMAGE_SYM_CLASS_UNION_TAG, + IMAGE_SYM_CLASS_TYPE_DEFINITION, + IMAGE_SYM_CLASS_UNDEFINED_STATIC, + IMAGE_SYM_CLASS_ENUM_TAG, + IMAGE_SYM_CLASS_MEMBER_OF_ENUM, + IMAGE_SYM_CLASS_REGISTER_PARAM, + IMAGE_SYM_CLASS_BIT_FIELD, // = 18 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 68, + IMAGE_SYM_CLASS_BLOCK = 100, + IMAGE_SYM_CLASS_FUNCTION, + IMAGE_SYM_CLASS_END_OF_STRUCT, + IMAGE_SYM_CLASS_FILE, + IMAGE_SYM_CLASS_SECTION, + IMAGE_SYM_CLASS_WEAK_EXTERNAL,// = 105 + IMAGE_SYM_CLASS_CLR_TOKEN = 107 +} + +enum : BYTE { + IMAGE_COMDAT_SELECT_NODUPLICATES = 1, + IMAGE_COMDAT_SELECT_ANY, + IMAGE_COMDAT_SELECT_SAME_SIZE, + IMAGE_COMDAT_SELECT_EXACT_MATCH, + IMAGE_COMDAT_SELECT_ASSOCIATIVE, + IMAGE_COMDAT_SELECT_LARGEST, + IMAGE_COMDAT_SELECT_NEWEST // = 7 +} + +enum : DWORD { + IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY = 1, + IMAGE_WEAK_EXTERN_SEARCH_LIBRARY, + IMAGE_WEAK_EXTERN_SEARCH_ALIAS +} + +enum : WORD { + IMAGE_REL_I386_ABSOLUTE = 0x0000, + IMAGE_REL_I386_DIR16 = 0x0001, + IMAGE_REL_I386_REL16 = 0x0002, + IMAGE_REL_I386_DIR32 = 0x0006, + IMAGE_REL_I386_DIR32NB = 0x0007, + IMAGE_REL_I386_SEG12 = 0x0009, + IMAGE_REL_I386_SECTION = 0x000A, + IMAGE_REL_I386_SECREL = 0x000B, + IMAGE_REL_I386_TOKEN = 0x000C, + IMAGE_REL_I386_SECREL7 = 0x000D, + IMAGE_REL_I386_REL32 = 0x0014 +} + +enum : WORD { + IMAGE_REL_AMD64_ABSOLUTE = 0x0000, + IMAGE_REL_AMD64_ADDR64 = 0x0001, + IMAGE_REL_AMD64_ADDR32 = 0x0002, + IMAGE_REL_AMD64_ADDR32NB = 0x0003, + IMAGE_REL_AMD64_REL32 = 0x0004, + IMAGE_REL_AMD64_REL32_1 = 0x0005, + IMAGE_REL_AMD64_REL32_2 = 0x0006, + IMAGE_REL_AMD64_REL32_3 = 0x0007, + IMAGE_REL_AMD64_REL32_4 = 0x0008, + IMAGE_REL_AMD64_REL32_5 = 0x0009, + IMAGE_REL_AMD64_SECTION = 0x000A, + IMAGE_REL_AMD64_SECREL = 0x000B, + IMAGE_REL_AMD64_SECREL7 = 0x000C, + IMAGE_REL_AMD64_TOKEN = 0x000D, + IMAGE_REL_AMD64_SREL32 = 0x000E, + IMAGE_REL_AMD64_PAIR = 0x000F, + IMAGE_REL_AMD64_SSPAN32 = 0x0010 +} + +enum : WORD { + IMAGE_REL_IA64_ABSOLUTE = 0x0000, + IMAGE_REL_IA64_IMM14 = 0x0001, + IMAGE_REL_IA64_IMM22 = 0x0002, + IMAGE_REL_IA64_IMM64 = 0x0003, + IMAGE_REL_IA64_DIR32 = 0x0004, + IMAGE_REL_IA64_DIR64 = 0x0005, + IMAGE_REL_IA64_PCREL21B = 0x0006, + IMAGE_REL_IA64_PCREL21M = 0x0007, + IMAGE_REL_IA64_PCREL21F = 0x0008, + IMAGE_REL_IA64_GPREL22 = 0x0009, + IMAGE_REL_IA64_LTOFF22 = 0x000A, + IMAGE_REL_IA64_SECTION = 0x000B, + IMAGE_REL_IA64_SECREL22 = 0x000C, + IMAGE_REL_IA64_SECREL64I = 0x000D, + IMAGE_REL_IA64_SECREL32 = 0x000E, + IMAGE_REL_IA64_DIR32NB = 0x0010, + IMAGE_REL_IA64_SREL14 = 0x0011, + IMAGE_REL_IA64_SREL22 = 0x0012, + IMAGE_REL_IA64_SREL32 = 0x0013, + IMAGE_REL_IA64_UREL32 = 0x0014, + IMAGE_REL_IA64_PCREL60X = 0x0015, + IMAGE_REL_IA64_PCREL60B = 0x0016, + IMAGE_REL_IA64_PCREL60F = 0x0017, + IMAGE_REL_IA64_PCREL60I = 0x0018, + IMAGE_REL_IA64_PCREL60M = 0x0019, + IMAGE_REL_IA64_IMMGPREL64 = 0x001A, + IMAGE_REL_IA64_TOKEN = 0x001B, + IMAGE_REL_IA64_GPREL32 = 0x001C, + IMAGE_REL_IA64_ADDEND = 0x001F +} + +enum : WORD { + IMAGE_REL_SH3_ABSOLUTE = 0x0000, + IMAGE_REL_SH3_DIRECT16 = 0x0001, + IMAGE_REL_SH3_DIRECT32 = 0x0002, + IMAGE_REL_SH3_DIRECT8 = 0x0003, + IMAGE_REL_SH3_DIRECT8_WORD = 0x0004, + IMAGE_REL_SH3_DIRECT8_LONG = 0x0005, + IMAGE_REL_SH3_DIRECT4 = 0x0006, + IMAGE_REL_SH3_DIRECT4_WORD = 0x0007, + IMAGE_REL_SH3_DIRECT4_LONG = 0x0008, + IMAGE_REL_SH3_PCREL8_WORD = 0x0009, + IMAGE_REL_SH3_PCREL8_LONG = 0x000A, + IMAGE_REL_SH3_PCREL12_WORD = 0x000B, + IMAGE_REL_SH3_STARTOF_SECTION = 0x000C, + IMAGE_REL_SH3_SIZEOF_SECTION = 0x000D, + IMAGE_REL_SH3_SECTION = 0x000E, + IMAGE_REL_SH3_SECREL = 0x000F, + IMAGE_REL_SH3_DIRECT32_NB = 0x0010, + IMAGE_REL_SH3_GPREL4_LONG = 0x0011, + IMAGE_REL_SH3_TOKEN = 0x0012, + IMAGE_REL_SHM_PCRELPT = 0x0013, + IMAGE_REL_SHM_REFLO = 0x0014, + IMAGE_REL_SHM_REFHALF = 0x0015, + IMAGE_REL_SHM_RELLO = 0x0016, + IMAGE_REL_SHM_RELHALF = 0x0017, + IMAGE_REL_SHM_PAIR = 0x0018, + IMAGE_REL_SHM_NOMODE = 0x8000 +} + +enum : WORD { + IMAGE_REL_M32R_ABSOLUTE = 0x0000, + IMAGE_REL_M32R_ADDR32 = 0x0001, + IMAGE_REL_M32R_ADDR32NB = 0x0002, + IMAGE_REL_M32R_ADDR24 = 0x0003, + IMAGE_REL_M32R_GPREL16 = 0x0004, + IMAGE_REL_M32R_PCREL24 = 0x0005, + IMAGE_REL_M32R_PCREL16 = 0x0006, + IMAGE_REL_M32R_PCREL8 = 0x0007, + IMAGE_REL_M32R_REFHALF = 0x0008, + IMAGE_REL_M32R_REFHI = 0x0009, + IMAGE_REL_M32R_REFLO = 0x000A, + IMAGE_REL_M32R_PAIR = 0x000B, + IMAGE_REL_M32R_SECTION = 0x000C, + IMAGE_REL_M32R_SECREL = 0x000D, + IMAGE_REL_M32R_TOKEN = 0x000E +} + +enum : WORD { + IMAGE_REL_MIPS_ABSOLUTE = 0x0000, + IMAGE_REL_MIPS_REFHALF = 0x0001, + IMAGE_REL_MIPS_REFWORD = 0x0002, + IMAGE_REL_MIPS_JMPADDR = 0x0003, + IMAGE_REL_MIPS_REFHI = 0x0004, + IMAGE_REL_MIPS_REFLO = 0x0005, + IMAGE_REL_MIPS_GPREL = 0x0006, + IMAGE_REL_MIPS_LITERAL = 0x0007, + IMAGE_REL_MIPS_SECTION = 0x000A, + IMAGE_REL_MIPS_SECREL = 0x000B, + IMAGE_REL_MIPS_SECRELLO = 0x000C, + IMAGE_REL_MIPS_SECRELHI = 0x000D, + IMAGE_REL_MIPS_JMPADDR16 = 0x0010, + IMAGE_REL_MIPS_REFWORDNB = 0x0022, + IMAGE_REL_MIPS_PAIR = 0x0025 +} + + +enum : WORD { + IMAGE_REL_ALPHA_ABSOLUTE, + IMAGE_REL_ALPHA_REFLONG, + IMAGE_REL_ALPHA_REFQUAD, + IMAGE_REL_ALPHA_GPREL32, + IMAGE_REL_ALPHA_LITERAL, + IMAGE_REL_ALPHA_LITUSE, + IMAGE_REL_ALPHA_GPDISP, + IMAGE_REL_ALPHA_BRADDR, + IMAGE_REL_ALPHA_HINT, + IMAGE_REL_ALPHA_INLINE_REFLONG, + IMAGE_REL_ALPHA_REFHI, + IMAGE_REL_ALPHA_REFLO, + IMAGE_REL_ALPHA_PAIR, + IMAGE_REL_ALPHA_MATCH, + IMAGE_REL_ALPHA_SECTION, + IMAGE_REL_ALPHA_SECREL, + IMAGE_REL_ALPHA_REFLONGNB, + IMAGE_REL_ALPHA_SECRELLO, + IMAGE_REL_ALPHA_SECRELHI // = 18 +} + +enum : WORD { + IMAGE_REL_PPC_ABSOLUTE, + IMAGE_REL_PPC_ADDR64, + IMAGE_REL_PPC_ADDR32, + IMAGE_REL_PPC_ADDR24, + IMAGE_REL_PPC_ADDR16, + IMAGE_REL_PPC_ADDR14, + IMAGE_REL_PPC_REL24, + IMAGE_REL_PPC_REL14, + IMAGE_REL_PPC_TOCREL16, + IMAGE_REL_PPC_TOCREL14, + IMAGE_REL_PPC_ADDR32NB, + IMAGE_REL_PPC_SECREL, + IMAGE_REL_PPC_SECTION, + IMAGE_REL_PPC_IFGLUE, + IMAGE_REL_PPC_IMGLUE, + IMAGE_REL_PPC_SECREL16, + IMAGE_REL_PPC_REFHI, + IMAGE_REL_PPC_REFLO, + IMAGE_REL_PPC_PAIR // = 18 +} + +// ??? +enum IMAGE_REL_PPC_TYPEMASK = 0x00FF; +enum IMAGE_REL_PPC_NEG = 0x0100; +enum IMAGE_REL_PPC_BRTAKEN = 0x0200; +enum IMAGE_REL_PPC_BRNTAKEN = 0x0400; +enum IMAGE_REL_PPC_TOCDEFN = 0x0800; + +enum { + IMAGE_REL_BASED_ABSOLUTE, + IMAGE_REL_BASED_HIGH, + IMAGE_REL_BASED_LOW, + IMAGE_REL_BASED_HIGHLOW, + IMAGE_REL_BASED_HIGHADJ, + IMAGE_REL_BASED_MIPS_JMPADDR +} +// End of constants documented in pecoff.doc + +enum size_t IMAGE_ARCHIVE_START_SIZE = 8; + +const TCHAR[] + IMAGE_ARCHIVE_START = "!\n", + IMAGE_ARCHIVE_END = "`\n", + IMAGE_ARCHIVE_PAD = "\n", + IMAGE_ARCHIVE_LINKER_MEMBER = "/ ", + IMAGE_ARCHIVE_LONGNAMES_MEMBER = "// "; + +enum IMAGE_ORDINAL_FLAG32 = 0x80000000; + +ulong IMAGE_ORDINAL64()(ulong Ordinal) { return Ordinal & 0xFFFF; } +uint IMAGE_ORDINAL32()(uint Ordinal) { return Ordinal & 0xFFFF; } + +bool IMAGE_SNAP_BY_ORDINAL32(uint Ordinal) { + return (Ordinal & IMAGE_ORDINAL_FLAG32) != 0; +} + +enum ulong IMAGE_ORDINAL_FLAG64 = 0x8000000000000000; + +bool IMAGE_SNAP_BY_ORDINAL64(ulong Ordinal) { + return (Ordinal & IMAGE_ORDINAL_FLAG64) != 0; +} + +// ??? +enum IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000; +enum IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000; + +enum : DWORD { + IMAGE_DEBUG_TYPE_UNKNOWN, + IMAGE_DEBUG_TYPE_COFF, + IMAGE_DEBUG_TYPE_CODEVIEW, + IMAGE_DEBUG_TYPE_FPO, + IMAGE_DEBUG_TYPE_MISC, + IMAGE_DEBUG_TYPE_EXCEPTION, + IMAGE_DEBUG_TYPE_FIXUP, + IMAGE_DEBUG_TYPE_OMAP_TO_SRC, + IMAGE_DEBUG_TYPE_OMAP_FROM_SRC, + IMAGE_DEBUG_TYPE_BORLAND // = 9 +} + +enum : ubyte { + FRAME_FPO, + FRAME_TRAP, + FRAME_TSS, + FRAME_NONFPO +} + +// ??? +enum IMAGE_DEBUG_MISC_EXENAME = 1; + +// ??? +enum N_BTMASK = 0x000F; +enum N_TMASK = 0x0030; +enum N_TMASK1 = 0x00C0; +enum N_TMASK2 = 0x00F0; +enum N_BTSHFT = 4; +enum N_TSHIFT = 2; + +enum int + IS_TEXT_UNICODE_ASCII16 = 0x0001, + IS_TEXT_UNICODE_STATISTICS = 0x0002, + IS_TEXT_UNICODE_CONTROLS = 0x0004, + IS_TEXT_UNICODE_SIGNATURE = 0x0008, + IS_TEXT_UNICODE_REVERSE_ASCII16 = 0x0010, + IS_TEXT_UNICODE_REVERSE_STATISTICS = 0x0020, + IS_TEXT_UNICODE_REVERSE_CONTROLS = 0x0040, + IS_TEXT_UNICODE_REVERSE_SIGNATURE = 0x0080, + IS_TEXT_UNICODE_ILLEGAL_CHARS = 0x0100, + IS_TEXT_UNICODE_ODD_LENGTH = 0x0200, + IS_TEXT_UNICODE_NULL_BYTES = 0x1000, + IS_TEXT_UNICODE_UNICODE_MASK = 0x000F, + IS_TEXT_UNICODE_REVERSE_MASK = 0x00F0, + IS_TEXT_UNICODE_NOT_UNICODE_MASK = 0x0F00, + IS_TEXT_UNICODE_NOT_ASCII_MASK = 0xF000; + +enum DWORD + SERVICE_KERNEL_DRIVER = 0x0001, + SERVICE_FILE_SYSTEM_DRIVER = 0x0002, + SERVICE_ADAPTER = 0x0004, + SERVICE_RECOGNIZER_DRIVER = 0x0008, + SERVICE_WIN32_OWN_PROCESS = 0x0010, + SERVICE_WIN32_SHARE_PROCESS = 0x0020, + SERVICE_INTERACTIVE_PROCESS = 0x0100, + SERVICE_DRIVER = 0x000B, + SERVICE_WIN32 = 0x0030, + SERVICE_TYPE_ALL = 0x013F; + +enum : DWORD { + SERVICE_BOOT_START = 0, + SERVICE_SYSTEM_START = 1, + SERVICE_AUTO_START = 2, + SERVICE_DEMAND_START = 3, + SERVICE_DISABLED = 4 +} + +enum : DWORD { + SERVICE_ERROR_IGNORE = 0, + SERVICE_ERROR_NORMAL = 1, + SERVICE_ERROR_SEVERE = 2, + SERVICE_ERROR_CRITICAL = 3 +} + + +enum uint + SE_OWNER_DEFAULTED = 0x0001, + SE_GROUP_DEFAULTED = 0x0002, + SE_DACL_PRESENT = 0x0004, + SE_DACL_DEFAULTED = 0x0008, + SE_SACL_PRESENT = 0x0010, + SE_SACL_DEFAULTED = 0x0020, + SE_DACL_AUTO_INHERIT_REQ = 0x0100, + SE_SACL_AUTO_INHERIT_REQ = 0x0200, + SE_DACL_AUTO_INHERITED = 0x0400, + SE_SACL_AUTO_INHERITED = 0x0800, + SE_DACL_PROTECTED = 0x1000, + SE_SACL_PROTECTED = 0x2000, + SE_SELF_RELATIVE = 0x8000; + +enum SECURITY_IMPERSONATION_LEVEL { + SecurityAnonymous, + SecurityIdentification, + SecurityImpersonation, + SecurityDelegation +} +alias SECURITY_IMPERSONATION_LEVEL* PSECURITY_IMPERSONATION_LEVEL; + +alias BOOLEAN SECURITY_CONTEXT_TRACKING_MODE; +alias BOOLEAN* PSECURITY_CONTEXT_TRACKING_MODE; + +enum size_t SECURITY_DESCRIPTOR_MIN_LENGTH = 20; + +enum DWORD + SECURITY_DESCRIPTOR_REVISION = 1, + SECURITY_DESCRIPTOR_REVISION1 = 1; + +enum DWORD + SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001, + SE_PRIVILEGE_ENABLED = 0x00000002, + SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000; + +enum DWORD PRIVILEGE_SET_ALL_NECESSARY = 1; + +enum SECURITY_IMPERSONATION_LEVEL + SECURITY_MAX_IMPERSONATION_LEVEL = SECURITY_IMPERSONATION_LEVEL.SecurityDelegation, + DEFAULT_IMPERSONATION_LEVEL = SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation; + +enum BOOLEAN + SECURITY_DYNAMIC_TRACKING = true, + SECURITY_STATIC_TRACKING = false; + +// also in ddk/ntifs.h +enum DWORD + TOKEN_ASSIGN_PRIMARY = 0x0001, + TOKEN_DUPLICATE = 0x0002, + TOKEN_IMPERSONATE = 0x0004, + TOKEN_QUERY = 0x0008, + TOKEN_QUERY_SOURCE = 0x0010, + TOKEN_ADJUST_PRIVILEGES = 0x0020, + TOKEN_ADJUST_GROUPS = 0x0040, + TOKEN_ADJUST_DEFAULT = 0x0080, + + TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED + | TOKEN_ASSIGN_PRIMARY + | TOKEN_DUPLICATE + | TOKEN_IMPERSONATE + | TOKEN_QUERY + | TOKEN_QUERY_SOURCE + | TOKEN_ADJUST_PRIVILEGES + | TOKEN_ADJUST_GROUPS + | TOKEN_ADJUST_DEFAULT, + TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY, + TOKEN_WRITE = STANDARD_RIGHTS_WRITE + | TOKEN_ADJUST_PRIVILEGES + | TOKEN_ADJUST_GROUPS + | TOKEN_ADJUST_DEFAULT, + TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE; + +enum size_t TOKEN_SOURCE_LENGTH = 8; +// end ddk/ntifs.h + +enum : DWORD { + DLL_PROCESS_DETACH, + DLL_PROCESS_ATTACH, + DLL_THREAD_ATTACH, + DLL_THREAD_DETACH +} + +enum : DWORD { + DBG_CONTINUE = 0x00010002, + DBG_TERMINATE_THREAD = 0x40010003, + DBG_TERMINATE_PROCESS = 0x40010004, + DBG_CONTROL_C = 0x40010005, + DBG_CONTROL_BREAK = 0x40010008, + DBG_EXCEPTION_NOT_HANDLED = 0x80010001 +} + +enum : DWORD { + TAPE_ABSOLUTE_POSITION, + TAPE_LOGICAL_POSITION, + TAPE_PSEUDO_LOGICAL_POSITION +} + +enum : DWORD { + TAPE_REWIND, + TAPE_ABSOLUTE_BLOCK, + TAPE_LOGICAL_BLOCK, + TAPE_PSEUDO_LOGICAL_BLOCK, + TAPE_SPACE_END_OF_DATA, + TAPE_SPACE_RELATIVE_BLOCKS, + TAPE_SPACE_FILEMARKS, + TAPE_SPACE_SEQUENTIAL_FMKS, + TAPE_SPACE_SETMARKS, + TAPE_SPACE_SEQUENTIAL_SMKS +} + +enum DWORD + TAPE_DRIVE_FIXED = 0x00000001, + TAPE_DRIVE_SELECT = 0x00000002, + TAPE_DRIVE_INITIATOR = 0x00000004, + TAPE_DRIVE_ERASE_SHORT = 0x00000010, + TAPE_DRIVE_ERASE_LONG = 0x00000020, + TAPE_DRIVE_ERASE_BOP_ONLY = 0x00000040, + TAPE_DRIVE_ERASE_IMMEDIATE = 0x00000080, + TAPE_DRIVE_TAPE_CAPACITY = 0x00000100, + TAPE_DRIVE_TAPE_REMAINING = 0x00000200, + TAPE_DRIVE_FIXED_BLOCK = 0x00000400, + TAPE_DRIVE_VARIABLE_BLOCK = 0x00000800, + TAPE_DRIVE_WRITE_PROTECT = 0x00001000, + TAPE_DRIVE_EOT_WZ_SIZE = 0x00002000, + TAPE_DRIVE_ECC = 0x00010000, + TAPE_DRIVE_COMPRESSION = 0x00020000, + TAPE_DRIVE_PADDING = 0x00040000, + TAPE_DRIVE_REPORT_SMKS = 0x00080000, + TAPE_DRIVE_GET_ABSOLUTE_BLK = 0x00100000, + TAPE_DRIVE_GET_LOGICAL_BLK = 0x00200000, + TAPE_DRIVE_SET_EOT_WZ_SIZE = 0x00400000, + TAPE_DRIVE_EJECT_MEDIA = 0x01000000, + TAPE_DRIVE_CLEAN_REQUESTS = 0x02000000, + TAPE_DRIVE_SET_CMP_BOP_ONLY = 0x04000000, + TAPE_DRIVE_RESERVED_BIT = 0x80000000; + +enum DWORD + TAPE_DRIVE_LOAD_UNLOAD = 0x80000001, + TAPE_DRIVE_TENSION = 0x80000002, + TAPE_DRIVE_LOCK_UNLOCK = 0x80000004, + TAPE_DRIVE_REWIND_IMMEDIATE = 0x80000008, + TAPE_DRIVE_SET_BLOCK_SIZE = 0x80000010, + TAPE_DRIVE_LOAD_UNLD_IMMED = 0x80000020, + TAPE_DRIVE_TENSION_IMMED = 0x80000040, + TAPE_DRIVE_LOCK_UNLK_IMMED = 0x80000080, + TAPE_DRIVE_SET_ECC = 0x80000100, + TAPE_DRIVE_SET_COMPRESSION = 0x80000200, + TAPE_DRIVE_SET_PADDING = 0x80000400, + TAPE_DRIVE_SET_REPORT_SMKS = 0x80000800, + TAPE_DRIVE_ABSOLUTE_BLK = 0x80001000, + TAPE_DRIVE_ABS_BLK_IMMED = 0x80002000, + TAPE_DRIVE_LOGICAL_BLK = 0x80004000, + TAPE_DRIVE_LOG_BLK_IMMED = 0x80008000, + TAPE_DRIVE_END_OF_DATA = 0x80010000, + TAPE_DRIVE_RELATIVE_BLKS = 0x80020000, + TAPE_DRIVE_FILEMARKS = 0x80040000, + TAPE_DRIVE_SEQUENTIAL_FMKS = 0x80080000, + TAPE_DRIVE_SETMARKS = 0x80100000, + TAPE_DRIVE_SEQUENTIAL_SMKS = 0x80200000, + TAPE_DRIVE_REVERSE_POSITION = 0x80400000, + TAPE_DRIVE_SPACE_IMMEDIATE = 0x80800000, + TAPE_DRIVE_WRITE_SETMARKS = 0x81000000, + TAPE_DRIVE_WRITE_FILEMARKS = 0x82000000, + TAPE_DRIVE_WRITE_SHORT_FMKS = 0x84000000, + TAPE_DRIVE_WRITE_LONG_FMKS = 0x88000000, + TAPE_DRIVE_WRITE_MARK_IMMED = 0x90000000, + TAPE_DRIVE_FORMAT = 0xA0000000, + TAPE_DRIVE_FORMAT_IMMEDIATE = 0xC0000000, + TAPE_DRIVE_HIGH_FEATURES = 0x80000000; + +enum : DWORD { + TAPE_FIXED_PARTITIONS = 0, + TAPE_SELECT_PARTITIONS = 1, + TAPE_INITIATOR_PARTITIONS = 2 +} + +enum : DWORD { + TAPE_SETMARKS, + TAPE_FILEMARKS, + TAPE_SHORT_FILEMARKS, + TAPE_LONG_FILEMARKS +} + +enum : DWORD { + TAPE_ERASE_SHORT, + TAPE_ERASE_LONG +} + +enum : DWORD { + TAPE_LOAD, + TAPE_UNLOAD, + TAPE_TENSION, + TAPE_LOCK, + TAPE_UNLOCK, + TAPE_FORMAT +} + +enum : ULONG32 { + VER_PLATFORM_WIN32s, + VER_PLATFORM_WIN32_WINDOWS, + VER_PLATFORM_WIN32_NT +} + +enum : UCHAR { + VER_NT_WORKSTATION = 1, + VER_NT_DOMAIN_CONTROLLER, + VER_NT_SERVER +} + +enum USHORT + VER_SUITE_SMALLBUSINESS = 0x0001, + VER_SUITE_ENTERPRISE = 0x0002, + VER_SUITE_BACKOFFICE = 0x0004, + VER_SUITE_TERMINAL = 0x0010, + VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x0020, + VER_SUITE_EMBEDDEDNT = 0x0040, + VER_SUITE_DATACENTER = 0x0080, + VER_SUITE_SINGLEUSERTS = 0x0100, + VER_SUITE_PERSONAL = 0x0200, + VER_SUITE_BLADE = 0x0400, + VER_SUITE_STORAGE_SERVER = 0x2000, + VER_SUITE_COMPUTE_SERVER = 0x4000; + +enum ULONG + WT_EXECUTEDEFAULT = 0x00000000, + WT_EXECUTEINIOTHREAD = 0x00000001, + WT_EXECUTEINWAITTHREAD = 0x00000004, + WT_EXECUTEONLYONCE = 0x00000008, + WT_EXECUTELONGFUNCTION = 0x00000010, + WT_EXECUTEINTIMERTHREAD = 0x00000020, + WT_EXECUTEINPERSISTENTTHREAD = 0x00000080, + WT_TRANSFER_IMPERSONATION = 0x00000100; + +static if (_WIN32_WINNT >= 0x500) { +enum DWORD + VER_MINORVERSION = 0x01, + VER_MAJORVERSION = 0x02, + VER_BUILDNUMBER = 0x04, + VER_PLATFORMID = 0x08, + VER_SERVICEPACKMINOR = 0x10, + VER_SERVICEPACKMAJOR = 0x20, + VER_SUITENAME = 0x40, + VER_PRODUCT_TYPE = 0x80; + + enum : DWORD { + VER_EQUAL = 1, + VER_GREATER, + VER_GREATER_EQUAL, + VER_LESS, + VER_LESS_EQUAL, + VER_AND, + VER_OR // = 7 + } +} + +static if (_WIN32_WINNT >= 0x501) { + enum : ULONG { + ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION = 1, + ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, + ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, + ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION, + ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION, + ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION, + ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION, // = 7 + ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES = 9 + } +} + +// Macros +BYTE BTYPE()(BYTE x) { return cast(BYTE) (x & N_BTMASK); } +bool ISPTR()(uint x) { return (x & N_TMASK) == (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT); } +bool ISFCN()(uint x) { return (x & N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT); } +bool ISARY()(uint x) { return (x & N_TMASK) == (IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT); } +bool ISTAG(uint x) { + return x == IMAGE_SYM_CLASS_STRUCT_TAG + || x == IMAGE_SYM_CLASS_UNION_TAG + || x == IMAGE_SYM_CLASS_ENUM_TAG; +} +uint INCREF(uint x) { + return ((x & ~N_BTMASK) << N_TSHIFT) | (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT) + | (x & N_BTMASK); +} +uint DECREF()(uint x) { return ((x >>> N_TSHIFT) & ~N_BTMASK) | (x & N_BTMASK); } + +enum DWORD TLS_MINIMUM_AVAILABLE = 64; + +enum ULONG + IO_REPARSE_TAG_RESERVED_ZERO = 0, + IO_REPARSE_TAG_RESERVED_ONE = 1, + IO_REPARSE_TAG_RESERVED_RANGE = IO_REPARSE_TAG_RESERVED_ONE, + IO_REPARSE_TAG_SYMBOLIC_LINK = IO_REPARSE_TAG_RESERVED_ZERO, + IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003, + IO_REPARSE_TAG_SYMLINK = 0xA000000C, + IO_REPARSE_TAG_VALID_VALUES = 0xE000FFFF; + +/* Although these are semantically boolean, they are documented and + * implemented to return ULONG; this behaviour is preserved for compatibility + */ +ULONG IsReparseTagMicrosoft()(ULONG x) { return x & 0x80000000; } +ULONG IsReparseTagHighLatency()(ULONG x) { return x & 0x40000000; } +ULONG IsReparseTagNameSurrogate()(ULONG x) { return x & 0x20000000; } + +bool IsReparseTagValid(ULONG x) { + return !(x & ~IO_REPARSE_TAG_VALID_VALUES) && (x > IO_REPARSE_TAG_RESERVED_RANGE); +} + +// Doesn't seem to make sense, but anyway.... +ULONG WT_SET_MAX_THREADPOOL_THREADS(ref ULONG Flags, ushort Limit) { + return Flags |= Limit << 16; +} + +import urt.internal.sys.windows.basetyps; +/* also in urt.internal.sys.windows.basetyps +struct GUID { + uint Data1; + ushort Data2; + ushort Data3; + ubyte Data4[8]; +} +alias GUID* REFGUID, LPGUID; +*/ + +struct GENERIC_MAPPING { + ACCESS_MASK GenericRead; + ACCESS_MASK GenericWrite; + ACCESS_MASK GenericExecute; + ACCESS_MASK GenericAll; +} +alias GENERIC_MAPPING* PGENERIC_MAPPING; + +struct ACE_HEADER { + BYTE AceType; + BYTE AceFlags; + WORD AceSize; +} +alias ACE_HEADER* PACE_HEADER; + +struct ACCESS_ALLOWED_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD SidStart; +} +alias ACCESS_ALLOWED_ACE* PACCESS_ALLOWED_ACE; + +struct ACCESS_DENIED_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD SidStart; +} +alias ACCESS_DENIED_ACE* PACCESS_DENIED_ACE; + +struct SYSTEM_AUDIT_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD SidStart; +} +alias SYSTEM_AUDIT_ACE *PSYSTEM_AUDIT_ACE; + +struct SYSTEM_ALARM_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD SidStart; +} +alias SYSTEM_ALARM_ACE* PSYSTEM_ALARM_ACE; + +struct ACCESS_ALLOWED_OBJECT_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD Flags; + GUID ObjectType; + GUID InheritedObjectType; + DWORD SidStart; +} +alias ACCESS_ALLOWED_OBJECT_ACE* PACCESS_ALLOWED_OBJECT_ACE; + +struct ACCESS_DENIED_OBJECT_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD Flags; + GUID ObjectType; + GUID InheritedObjectType; + DWORD SidStart; +} +alias ACCESS_DENIED_OBJECT_ACE* PACCESS_DENIED_OBJECT_ACE; + +struct SYSTEM_AUDIT_OBJECT_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD Flags; + GUID ObjectType; + GUID InheritedObjectType; + DWORD SidStart; +} +alias SYSTEM_AUDIT_OBJECT_ACE* PSYSTEM_AUDIT_OBJECT_ACE; + +struct SYSTEM_ALARM_OBJECT_ACE { + ACE_HEADER Header; + ACCESS_MASK Mask; + DWORD Flags; + GUID ObjectType; + GUID InheritedObjectType; + DWORD SidStart; +} +alias SYSTEM_ALARM_OBJECT_ACE* PSYSTEM_ALARM_OBJECT_ACE; + +struct ACL { + BYTE AclRevision; + BYTE Sbz1; + WORD AclSize; + WORD AceCount; + WORD Sbz2; +} +alias ACL* PACL; + +struct ACL_REVISION_INFORMATION { + DWORD AclRevision; +} + +struct ACL_SIZE_INFORMATION { + DWORD AceCount; + DWORD AclBytesInUse; + DWORD AclBytesFree; +} + +version (X86) { + // ??? +enum SIZE_OF_80387_REGISTERS = 80; +enum CONTEXT_i386 = 0x010000; +enum CONTEXT_i486 = 0x010000; +enum CONTEXT_CONTROL = CONTEXT_i386 | 0x01; +enum CONTEXT_INTEGER = CONTEXT_i386 | 0x02; +enum CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04; +enum CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08; +enum CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10; +enum CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20; +enum CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; +enum CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | + CONTEXT_EXTENDED_REGISTERS; + +enum MAXIMUM_SUPPORTED_EXTENSION = 512; + + struct FLOATING_SAVE_AREA { + DWORD ControlWord; + DWORD StatusWord; + DWORD TagWord; + DWORD ErrorOffset; + DWORD ErrorSelector; + DWORD DataOffset; + DWORD DataSelector; + BYTE[80] RegisterArea; + DWORD Cr0NpxState; + } + + struct CONTEXT { + DWORD ContextFlags; + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; + FLOATING_SAVE_AREA FloatSave; + DWORD SegGs; + DWORD SegFs; + DWORD SegEs; + DWORD SegDs; + DWORD Edi; + DWORD Esi; + DWORD Ebx; + DWORD Edx; + DWORD Ecx; + DWORD Eax; + DWORD Ebp; + DWORD Eip; + DWORD SegCs; + DWORD EFlags; + DWORD Esp; + DWORD SegSs; + BYTE[MAXIMUM_SUPPORTED_EXTENSION] ExtendedRegisters; + } + +} else version (X86_64) +{ +enum CONTEXT_AMD64 = 0x100000; + +enum CONTEXT_CONTROL = (CONTEXT_AMD64 | 0x1L); +enum CONTEXT_INTEGER = (CONTEXT_AMD64 | 0x2L); +enum CONTEXT_SEGMENTS = (CONTEXT_AMD64 | 0x4L); +enum CONTEXT_FLOATING_POINT = (CONTEXT_AMD64 | 0x8L); +enum CONTEXT_DEBUG_REGISTERS = (CONTEXT_AMD64 | 0x10L); + +enum CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT); +enum CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS); + +enum CONTEXT_EXCEPTION_ACTIVE = 0x8000000; +enum CONTEXT_SERVICE_ACTIVE = 0x10000000; +enum CONTEXT_EXCEPTION_REQUEST = 0x40000000; +enum CONTEXT_EXCEPTION_REPORTING = 0x80000000; + +enum INITIAL_MXCSR = 0x1f80; +enum INITIAL_FPCSR = 0x027f; + + align(16) struct M128A + { + ULONGLONG Low; + LONGLONG High; + } + alias M128A* PM128A; + + struct XMM_SAVE_AREA32 + { + WORD ControlWord; + WORD StatusWord; + BYTE TagWord; + BYTE Reserved1; + WORD ErrorOpcode; + DWORD ErrorOffset; + WORD ErrorSelector; + WORD Reserved2; + DWORD DataOffset; + WORD DataSelector; + WORD Reserved3; + DWORD MxCsr; + DWORD MxCsr_Mask; + M128A[8] FloatRegisters; + M128A[16] XmmRegisters; + BYTE[96] Reserved4; + } + alias XMM_SAVE_AREA32 PXMM_SAVE_AREA32; +enum LEGACY_SAVE_AREA_LENGTH = XMM_SAVE_AREA32.sizeof; + + align(16) struct CONTEXT + { + DWORD64 P1Home; + DWORD64 P2Home; + DWORD64 P3Home; + DWORD64 P4Home; + DWORD64 P5Home; + DWORD64 P6Home; + DWORD ContextFlags; + DWORD MxCsr; + WORD SegCs; + WORD SegDs; + WORD SegEs; + WORD SegFs; + WORD SegGs; + WORD SegSs; + DWORD EFlags; + DWORD64 Dr0; + DWORD64 Dr1; + DWORD64 Dr2; + DWORD64 Dr3; + DWORD64 Dr6; + DWORD64 Dr7; + DWORD64 Rax; + DWORD64 Rcx; + DWORD64 Rdx; + DWORD64 Rbx; + DWORD64 Rsp; + DWORD64 Rbp; + DWORD64 Rsi; + DWORD64 Rdi; + DWORD64 R8; + DWORD64 R9; + DWORD64 R10; + DWORD64 R11; + DWORD64 R12; + DWORD64 R13; + DWORD64 R14; + DWORD64 R15; + DWORD64 Rip; + union + { + XMM_SAVE_AREA32 FltSave; + XMM_SAVE_AREA32 FloatSave; + struct + { + M128A[2] Header; + M128A[8] Legacy; + M128A Xmm0; + M128A Xmm1; + M128A Xmm2; + M128A Xmm3; + M128A Xmm4; + M128A Xmm5; + M128A Xmm6; + M128A Xmm7; + M128A Xmm8; + M128A Xmm9; + M128A Xmm10; + M128A Xmm11; + M128A Xmm12; + M128A Xmm13; + M128A Xmm14; + M128A Xmm15; + } + } + M128A[26] VectorRegister; + DWORD64 VectorControl; + DWORD64 DebugControl; + DWORD64 LastBranchToRip; + DWORD64 LastBranchFromRip; + DWORD64 LastExceptionToRip; + DWORD64 LastExceptionFromRip; + } + +} else version(AArch64) { + enum CONTEXT_ARM64 = 0x400000; + + enum CONTEXT_CONTROL = (CONTEXT_ARM64 | 0x1L); + enum CONTEXT_INTEGER = (CONTEXT_ARM64 | 0x2L); + enum CONTEXT_SEGMENTS = (CONTEXT_ARM64 | 0x4L); + enum CONTEXT_FLOATING_POINT = (CONTEXT_ARM64 | 0x8L); + enum CONTEXT_DEBUG_REGISTERS = (CONTEXT_ARM64 | 0x10L); + + enum CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT); + enum CONTEXT_ALL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS); + + enum ARM64_MAX_BREAKPOINTS = 8; + enum ARM64_MAX_WATCHPOINTS = 2; + + union ARM64_NT_NEON128 { + struct { + ULONGLONG Low; + LONGLONG High; + }; + double[2] D; + float[4] S; + WORD[8] H; + BYTE[16] B; + } + alias PARM64_NT_NEON128 = ARM64_NT_NEON128*; + + align(16) struct CONTEXT + { + DWORD ContextFlags; + DWORD Cpsr; + DWORD64[31] X; + DWORD64 Sp; + DWORD64 Pc; + + ARM64_NT_NEON128[32] V; + DWORD Fpcr; + + DWORD Fpsr; + + DWORD[ARM64_MAX_BREAKPOINTS] Bcr; + DWORD64[ARM64_MAX_BREAKPOINTS] Bvr; + DWORD[ARM64_MAX_WATCHPOINTS] Wcr; + DWORD64[ARM64_MAX_WATCHPOINTS] Wvr; + } +} else { + static assert(false, "Unsupported CPU"); + // Versions for PowerPC, Alpha, SHX, and MIPS removed. +} + +alias CONTEXT* PCONTEXT, LPCONTEXT; + +struct EXCEPTION_RECORD { + DWORD ExceptionCode; + DWORD ExceptionFlags; + EXCEPTION_RECORD* ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR[EXCEPTION_MAXIMUM_PARAMETERS] ExceptionInformation; +} +alias EXCEPTION_RECORD* PEXCEPTION_RECORD, LPEXCEPTION_RECORD; + +struct EXCEPTION_POINTERS { + PEXCEPTION_RECORD ExceptionRecord; + PCONTEXT ContextRecord; +} +alias EXCEPTION_POINTERS* PEXCEPTION_POINTERS, LPEXCEPTION_POINTERS; + +union LARGE_INTEGER { + struct { + uint LowPart; + int HighPart; + } + long QuadPart; +} +alias LARGE_INTEGER* PLARGE_INTEGER; + +union ULARGE_INTEGER { + struct { + uint LowPart; + uint HighPart; + } + ulong QuadPart; +} +alias ULARGE_INTEGER* PULARGE_INTEGER; + +alias LARGE_INTEGER LUID; +alias LUID* PLUID; + +enum LUID SYSTEM_LUID = { QuadPart:999 }; + +align(4) struct LUID_AND_ATTRIBUTES { + LUID Luid; + DWORD Attributes; +} +alias LUID_AND_ATTRIBUTES* PLUID_AND_ATTRIBUTES; + +align(4) struct PRIVILEGE_SET { + DWORD PrivilegeCount; + DWORD Control; + LUID_AND_ATTRIBUTES _Privilege; + + LUID_AND_ATTRIBUTES* Privilege() return { return &_Privilege; } +} +alias PRIVILEGE_SET* PPRIVILEGE_SET; + +struct SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +} +alias SECURITY_ATTRIBUTES* PSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES; + +struct SECURITY_QUALITY_OF_SERVICE { + DWORD Length; + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; + SECURITY_CONTEXT_TRACKING_MODE ContextTrackingMode; + BOOLEAN EffectiveOnly; +} +alias SECURITY_QUALITY_OF_SERVICE* PSECURITY_QUALITY_OF_SERVICE; + +alias PVOID PACCESS_TOKEN; + +struct SE_IMPERSONATION_STATE { + PACCESS_TOKEN Token; + BOOLEAN CopyOnOpen; + BOOLEAN EffectiveOnly; + SECURITY_IMPERSONATION_LEVEL Level; +} +alias SE_IMPERSONATION_STATE* PSE_IMPERSONATION_STATE; + +struct SID_IDENTIFIER_AUTHORITY { + BYTE[6] Value; +} +alias SID_IDENTIFIER_AUTHORITY* PSID_IDENTIFIER_AUTHORITY, LPSID_IDENTIFIER_AUTHORITY; + +alias PVOID PSID; + +struct SID { + BYTE Revision; + BYTE SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + DWORD _SubAuthority; + + DWORD* SubAuthority() return { return &_SubAuthority; } +} +alias SID* PISID; + +struct SID_AND_ATTRIBUTES { + PSID Sid; + DWORD Attributes; +} +alias SID_AND_ATTRIBUTES* PSID_AND_ATTRIBUTES; + +struct TOKEN_SOURCE { + CHAR[TOKEN_SOURCE_LENGTH] SourceName = 0; + LUID SourceIdentifier; +} +alias TOKEN_SOURCE* PTOKEN_SOURCE; + +struct TOKEN_CONTROL { + LUID TokenId; + LUID AuthenticationId; + LUID ModifiedId; + TOKEN_SOURCE TokenSource; +} +alias TOKEN_CONTROL* PTOKEN_CONTROL; + +struct TOKEN_DEFAULT_DACL { + PACL DefaultDacl; +} +alias TOKEN_DEFAULT_DACL* PTOKEN_DEFAULT_DACL; + +struct TOKEN_GROUPS { + DWORD GroupCount; + SID_AND_ATTRIBUTES _Groups; + + SID_AND_ATTRIBUTES* Groups() return { return &_Groups; } +} +alias TOKEN_GROUPS* PTOKEN_GROUPS, LPTOKEN_GROUPS; + +struct TOKEN_OWNER { + PSID Owner; +} +alias TOKEN_OWNER* PTOKEN_OWNER; +enum SECURITY_MAX_SID_SIZE = 68; + +struct TOKEN_PRIMARY_GROUP { + PSID PrimaryGroup; +} +alias TOKEN_PRIMARY_GROUP* PTOKEN_PRIMARY_GROUP; + +struct TOKEN_PRIVILEGES { + DWORD PrivilegeCount; + LUID_AND_ATTRIBUTES _Privileges; + + LUID_AND_ATTRIBUTES* Privileges() return { return &_Privileges; } +} +alias TOKEN_PRIVILEGES* PTOKEN_PRIVILEGES, LPTOKEN_PRIVILEGES; + +enum TOKEN_TYPE { + TokenPrimary = 1, + TokenImpersonation +} +alias TOKEN_TYPE* PTOKEN_TYPE; + +struct TOKEN_STATISTICS { + LUID TokenId; + LUID AuthenticationId; + LARGE_INTEGER ExpirationTime; + TOKEN_TYPE TokenType; + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; + DWORD DynamicCharged; + DWORD DynamicAvailable; + DWORD GroupCount; + DWORD PrivilegeCount; + LUID ModifiedId; +} +alias TOKEN_STATISTICS* PTOKEN_STATISTICS; + +struct TOKEN_USER { + SID_AND_ATTRIBUTES User; +} +alias TOKEN_USER* PTOKEN_USER; + +struct TOKEN_MANDATORY_LABEL { + SID_AND_ATTRIBUTES Label; +} +alias PTOKEN_MANDATORY_LABEL = TOKEN_MANDATORY_LABEL*; +alias DWORD SECURITY_INFORMATION; +alias SECURITY_INFORMATION* PSECURITY_INFORMATION; +alias WORD SECURITY_DESCRIPTOR_CONTROL; +alias SECURITY_DESCRIPTOR_CONTROL* PSECURITY_DESCRIPTOR_CONTROL; + +struct SECURITY_DESCRIPTOR { + BYTE Revision; + BYTE Sbz1; + SECURITY_DESCRIPTOR_CONTROL Control; + PSID Owner; + PSID Group; + PACL Sacl; + PACL Dacl; +} +alias SECURITY_DESCRIPTOR* PSECURITY_DESCRIPTOR, PISECURITY_DESCRIPTOR; +enum TOKEN_ELEVATION_TYPE { + TokenElevationTypeDefault = 1, + TokenElevationTypeFull, + TokenElevationTypeLimited +} + +alias PTOKEN_ELEVATION_TYPE = TOKEN_ELEVATION_TYPE*; + +struct TOKEN_ELEVATION { + DWORD TokenIsElevated; +} +alias PTOKEN_ELEVATION = TOKEN_ELEVATION*; + +enum TOKEN_INFORMATION_CLASS { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin, + TokenElevationType, + TokenLinkedToken, + TokenElevation, + TokenHasRestrictions, + TokenAccessInformation, + TokenVirtualizationAllowed, + TokenVirtualizationEnabled, + TokenIntegrityLevel, + TokenUIAccess, + TokenMandatoryPolicy, + TokenLogonSid, + TokenIsAppContainer, + TokenCapabilities, + TokenAppContainerSid, + TokenAppContainerNumber, + TokenUserClaimAttributes, + TokenDeviceClaimAttributes, + TokenRestrictedUserClaimAttributes, + TokenRestrictedDeviceClaimAttributes, + TokenDeviceGroups, + TokenRestrictedDeviceGroups, + TokenSecurityAttributes, + TokenIsRestricted, + TokenProcessTrustLevel, + MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum +} + +enum SID_NAME_USE { + SidTypeUser = 1, + SidTypeGroup, + SidTypeDomain, + SidTypeAlias, + SidTypeWellKnownGroup, + SidTypeDeletedAccount, + SidTypeInvalid, + SidTypeUnknown, + SidTypeComputer +} +alias SID_NAME_USE* PSID_NAME_USE; + +enum WELL_KNOWN_SID_TYPE { + WinNullSid = 0, + WinWorldSid = 1, + WinLocalSid = 2, + WinCreatorOwnerSid = 3, + WinCreatorGroupSid = 4, + WinCreatorOwnerServerSid = 5, + WinCreatorGroupServerSid = 6, + WinNtAuthoritySid = 7, + WinDialupSid = 8, + WinNetworkSid = 9, + WinBatchSid = 10, + WinInteractiveSid = 11, + WinServiceSid = 12, + WinAnonymousSid = 13, + WinProxySid = 14, + WinEnterpriseControllersSid = 15, + WinSelfSid = 16, + WinAuthenticatedUserSid = 17, + WinRestrictedCodeSid = 18, + WinTerminalServerSid = 19, + WinRemoteLogonIdSid = 20, + WinLogonIdsSid = 21, + WinLocalSystemSid = 22, + WinLocalServiceSid = 23, + WinNetworkServiceSid = 24, + WinBuiltinDomainSid = 25, + WinBuiltinAdministratorsSid = 26, + WinBuiltinUsersSid = 27, + WinBuiltinGuestsSid = 28, + WinBuiltinPowerUsersSid = 29, + WinBuiltinAccountOperatorsSid = 30, + WinBuiltinSystemOperatorsSid = 31, + WinBuiltinPrintOperatorsSid = 32, + WinBuiltinBackupOperatorsSid = 33, + WinBuiltinReplicatorSid = 34, + WinBuiltinPreWindows2000CompatibleAccessSid = 35, + WinBuiltinRemoteDesktopUsersSid = 36, + WinBuiltinNetworkConfigurationOperatorsSid = 37, + WinAccountAdministratorSid = 38, + WinAccountGuestSid = 39, + WinAccountKrbtgtSid = 40, + WinAccountDomainAdminsSid = 41, + WinAccountDomainUsersSid = 42, + WinAccountDomainGuestsSid = 43, + WinAccountComputersSid = 44, + WinAccountControllersSid = 45, + WinAccountCertAdminsSid = 46, + WinAccountSchemaAdminsSid = 47, + WinAccountEnterpriseAdminsSid = 48, + WinAccountPolicyAdminsSid = 49, + WinAccountRasAndIasServersSid = 50, + WinNTLMAuthenticationSid = 51, + WinDigestAuthenticationSid = 52, + WinSChannelAuthenticationSid = 53, + WinThisOrganizationSid = 54, + WinOtherOrganizationSid = 55, + WinBuiltinIncomingForestTrustBuildersSid = 56, + WinBuiltinPerfMonitoringUsersSid = 57, + WinBuiltinPerfLoggingUsersSid = 58, + WinBuiltinAuthorizationAccessSid = 59, + WinBuiltinTerminalServerLicenseServersSid = 60, + WinBuiltinDCOMUsersSid = 61, + WinBuiltinIUsersSid = 62, + WinIUserSid = 63, + WinBuiltinCryptoOperatorsSid = 64, + WinUntrustedLabelSid = 65, + WinLowLabelSid = 66, + WinMediumLabelSid = 67, + WinHighLabelSid = 68, + WinSystemLabelSid = 69, + WinWriteRestrictedCodeSid = 70, + WinCreatorOwnerRightsSid = 71, + WinCacheablePrincipalsGroupSid = 72, + WinNonCacheablePrincipalsGroupSid = 73, + WinEnterpriseReadonlyControllersSid = 74, + WinAccountReadonlyControllersSid = 75, + WinBuiltinEventLogReadersGroup = 76, + WinNewEnterpriseReadonlyControllersSid = 77, + WinBuiltinCertSvcDComAccessGroup = 78, + WinMediumPlusLabelSid = 79, + WinLocalLogonSid = 80, + WinConsoleLogonSid = 81, + WinThisOrganizationCertificateSid = 82, + WinApplicationPackageAuthoritySid = 83, + WinBuiltinAnyPackageSid = 84, + WinCapabilityInternetClientSid = 85, + WinCapabilityInternetClientServerSid = 86, + WinCapabilityPrivateNetworkClientServerSid = 87, + WinCapabilityPicturesLibrarySid = 88, + WinCapabilityVideosLibrarySid = 89, + WinCapabilityMusicLibrarySid = 90, + WinCapabilityDocumentsLibrarySid = 91, + WinCapabilitySharedUserCertificatesSid = 92, + WinCapabilityEnterpriseAuthenticationSid = 93, + WinCapabilityRemovableStorageSid = 94 +} +struct QUOTA_LIMITS { + SIZE_T PagedPoolLimit; + SIZE_T NonPagedPoolLimit; + SIZE_T MinimumWorkingSetSize; + SIZE_T MaximumWorkingSetSize; + SIZE_T PagefileLimit; + LARGE_INTEGER TimeLimit; +} +alias QUOTA_LIMITS* PQUOTA_LIMITS; + +struct IO_COUNTERS { + ULONGLONG ReadOperationCount; + ULONGLONG WriteOperationCount; + ULONGLONG OtherOperationCount; + ULONGLONG ReadTransferCount; + ULONGLONG WriteTransferCount; + ULONGLONG OtherTransferCount; +} +alias IO_COUNTERS* PIO_COUNTERS; + +struct FILE_NOTIFY_INFORMATION { + DWORD NextEntryOffset; + DWORD Action; + DWORD FileNameLength = 0; + WCHAR _FileName = 0; + + WCHAR* FileName() return { return &_FileName; } +} +alias FILE_NOTIFY_INFORMATION* PFILE_NOTIFY_INFORMATION; + +struct TAPE_ERASE { + DWORD Type; + BOOLEAN Immediate; +} +alias TAPE_ERASE* PTAPE_ERASE; + +struct TAPE_GET_DRIVE_PARAMETERS { + BOOLEAN ECC; + BOOLEAN Compression; + BOOLEAN DataPadding; + BOOLEAN ReportSetmarks; + DWORD DefaultBlockSize; + DWORD MaximumBlockSize; + DWORD MinimumBlockSize; + DWORD MaximumPartitionCount; + DWORD FeaturesLow; + DWORD FeaturesHigh; + DWORD EOTWarningZoneSize; +} +alias TAPE_GET_DRIVE_PARAMETERS* PTAPE_GET_DRIVE_PARAMETERS; + +struct TAPE_GET_MEDIA_PARAMETERS { + LARGE_INTEGER Capacity; + LARGE_INTEGER Remaining; + DWORD BlockSize; + DWORD PartitionCount; + BOOLEAN WriteProtected; +} +alias TAPE_GET_MEDIA_PARAMETERS* PTAPE_GET_MEDIA_PARAMETERS; + +struct TAPE_GET_POSITION { + ULONG Type; + ULONG Partition; + ULONG OffsetLow; + ULONG OffsetHigh; +} +alias TAPE_GET_POSITION* PTAPE_GET_POSITION; + +struct TAPE_PREPARE { + DWORD Operation; + BOOLEAN Immediate; +} +alias TAPE_PREPARE* PTAPE_PREPARE; + +struct TAPE_SET_DRIVE_PARAMETERS { + BOOLEAN ECC; + BOOLEAN Compression; + BOOLEAN DataPadding; + BOOLEAN ReportSetmarks; + ULONG EOTWarningZoneSize; +} +alias TAPE_SET_DRIVE_PARAMETERS* PTAPE_SET_DRIVE_PARAMETERS; + +struct TAPE_SET_MEDIA_PARAMETERS { + ULONG BlockSize; +} +alias TAPE_SET_MEDIA_PARAMETERS* PTAPE_SET_MEDIA_PARAMETERS; + +struct TAPE_SET_POSITION { + DWORD Method; + DWORD Partition; + LARGE_INTEGER Offset; + BOOLEAN Immediate; +} +alias TAPE_SET_POSITION* PTAPE_SET_POSITION; + +struct TAPE_WRITE_MARKS { + DWORD Type; + DWORD Count; + BOOLEAN Immediate; +} +alias TAPE_WRITE_MARKS* PTAPE_WRITE_MARKS; + +struct TAPE_CREATE_PARTITION { + DWORD Method; + DWORD Count; + DWORD Size; +} +alias TAPE_CREATE_PARTITION* PTAPE_CREATE_PARTITION; + +struct MEMORY_BASIC_INFORMATION { + PVOID BaseAddress; + PVOID AllocationBase; + DWORD AllocationProtect; + SIZE_T RegionSize; + DWORD State; + DWORD Protect; + DWORD Type; +} +alias MEMORY_BASIC_INFORMATION* PMEMORY_BASIC_INFORMATION; + +struct MESSAGE_RESOURCE_ENTRY { + WORD Length; + WORD Flags; + BYTE _Text; + + BYTE* Text() return { return &_Text; } +} +alias MESSAGE_RESOURCE_ENTRY* PMESSAGE_RESOURCE_ENTRY; + +struct MESSAGE_RESOURCE_BLOCK { + DWORD LowId; + DWORD HighId; + DWORD OffsetToEntries; +} +alias MESSAGE_RESOURCE_BLOCK* PMESSAGE_RESOURCE_BLOCK; + +struct MESSAGE_RESOURCE_DATA { + DWORD NumberOfBlocks; + MESSAGE_RESOURCE_BLOCK _Blocks; + + MESSAGE_RESOURCE_BLOCK* Blocks() return { return &_Blocks; } +} +alias MESSAGE_RESOURCE_DATA* PMESSAGE_RESOURCE_DATA; + +struct LIST_ENTRY { + LIST_ENTRY* Flink; + LIST_ENTRY* Blink; +} +alias LIST_ENTRY* PLIST_ENTRY; +alias LIST_ENTRY _LIST_ENTRY; + +struct SINGLE_LIST_ENTRY { + SINGLE_LIST_ENTRY* Next; +} + +version (Win64) { + align (16) + struct SLIST_ENTRY { + SLIST_ENTRY* Next; + } +} else { + alias SINGLE_LIST_ENTRY SLIST_ENTRY; +} +alias SINGLE_LIST_ENTRY* PSINGLE_LIST_ENTRY, PSLIST_ENTRY; + +union SLIST_HEADER { + ULONGLONG Alignment; + struct { + SLIST_ENTRY Next; + WORD Depth; + WORD Sequence; + } +} +alias SLIST_HEADER* PSLIST_HEADER; + +struct RTL_CRITICAL_SECTION_DEBUG { + WORD Type; + WORD CreatorBackTraceIndex; + RTL_CRITICAL_SECTION* CriticalSection; + LIST_ENTRY ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD[2] Spare; +} +alias RTL_CRITICAL_SECTION_DEBUG* PRTL_CRITICAL_SECTION_DEBUG; +alias RTL_CRITICAL_SECTION_DEBUG _RTL_CRITICAL_SECTION_DEBUG; + +struct RTL_CRITICAL_SECTION { + PRTL_CRITICAL_SECTION_DEBUG DebugInfo; + LONG LockCount; + LONG RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; + ULONG_PTR SpinCount; + alias Reserved = SpinCount; +} +alias RTL_CRITICAL_SECTION* PRTL_CRITICAL_SECTION; +alias RTL_CRITICAL_SECTION _RTL_CRITICAL_SECTION; + +struct EVENTLOGRECORD { + DWORD Length; + DWORD Reserved; + DWORD RecordNumber; + DWORD TimeGenerated; + DWORD TimeWritten; + DWORD EventID; + WORD EventType; + WORD NumStrings; + WORD EventCategory; + WORD ReservedFlags; + DWORD ClosingRecordNumber; + DWORD StringOffset; + DWORD UserSidLength; + DWORD UserSidOffset; + DWORD DataLength; + DWORD DataOffset; +} +alias EVENTLOGRECORD* PEVENTLOGRECORD; + +struct OSVERSIONINFOA { + DWORD dwOSVersionInfoSize = OSVERSIONINFOA.sizeof; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + CHAR[128] szCSDVersion = 0; +} +alias OSVERSIONINFOA* POSVERSIONINFOA, LPOSVERSIONINFOA; + +struct OSVERSIONINFOW { + DWORD dwOSVersionInfoSize = OSVERSIONINFOW.sizeof; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR[128] szCSDVersion = 0; +} +alias OSVERSIONINFOW* POSVERSIONINFOW, LPOSVERSIONINFOW; + +struct OSVERSIONINFOEXA { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + CHAR[128] szCSDVersion = 0; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; +} +alias OSVERSIONINFOEXA* POSVERSIONINFOEXA, LPOSVERSIONINFOEXA; + +struct OSVERSIONINFOEXW { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR[128] szCSDVersion = 0; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; +} +alias OSVERSIONINFOEXW* POSVERSIONINFOEXW, LPOSVERSIONINFOEXW; + +align(2) struct IMAGE_VXD_HEADER { + WORD e32_magic; + BYTE e32_border; + BYTE e32_worder; + DWORD e32_level; + WORD e32_cpu; + WORD e32_os; + DWORD e32_ver; + DWORD e32_mflags; + DWORD e32_mpages; + DWORD e32_startobj; + DWORD e32_eip; + DWORD e32_stackobj; + DWORD e32_esp; + DWORD e32_pagesize; + DWORD e32_lastpagesize; + DWORD e32_fixupsize; + DWORD e32_fixupsum; + DWORD e32_ldrsize; + DWORD e32_ldrsum; + DWORD e32_objtab; + DWORD e32_objcnt; + DWORD e32_objmap; + DWORD e32_itermap; + DWORD e32_rsrctab; + DWORD e32_rsrccnt; + DWORD e32_restab; + DWORD e32_enttab; + DWORD e32_dirtab; + DWORD e32_dircnt; + DWORD e32_fpagetab; + DWORD e32_frectab; + DWORD e32_impmod; + DWORD e32_impmodcnt; + DWORD e32_impproc; + DWORD e32_pagesum; + DWORD e32_datapage; + DWORD e32_preload; + DWORD e32_nrestab; + DWORD e32_cbnrestab; + DWORD e32_nressum; + DWORD e32_autodata; + DWORD e32_debuginfo; + DWORD e32_debuglen; + DWORD e32_instpreload; + DWORD e32_instdemand; + DWORD e32_heapsize; + BYTE[12] e32_res3; + DWORD e32_winresoff; + DWORD e32_winreslen; + WORD e32_devid; + WORD e32_ddkver; +} +alias IMAGE_VXD_HEADER* PIMAGE_VXD_HEADER; + +align(4): +struct IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} +alias IMAGE_FILE_HEADER* PIMAGE_FILE_HEADER; +// const IMAGE_SIZEOF_FILE_HEADER = IMAGE_FILE_HEADER.sizeof; + +struct IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} +alias IMAGE_DATA_DIRECTORY* PIMAGE_DATA_DIRECTORY; + +struct IMAGE_OPTIONAL_HEADER32 { + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + DWORD ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + DWORD SizeOfStackReserve; + DWORD SizeOfStackCommit; + DWORD SizeOfHeapReserve; + DWORD SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] DataDirectory; +} +alias IMAGE_OPTIONAL_HEADER32* PIMAGE_OPTIONAL_HEADER32; + +struct IMAGE_OPTIONAL_HEADER64 { + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + ULONGLONG ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + ULONGLONG SizeOfStackReserve; + ULONGLONG SizeOfStackCommit; + ULONGLONG SizeOfHeapReserve; + ULONGLONG SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] DataDirectory; +} +alias IMAGE_OPTIONAL_HEADER64* PIMAGE_OPTIONAL_HEADER64; + +struct IMAGE_ROM_OPTIONAL_HEADER { + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + DWORD BaseOfBss; + DWORD GprMask; + DWORD[4] CprMask; + DWORD GpValue; +} +alias IMAGE_ROM_OPTIONAL_HEADER* PIMAGE_ROM_OPTIONAL_HEADER; + +align(2): +struct IMAGE_DOS_HEADER { + WORD e_magic; + WORD e_cblp; + WORD e_cp; + WORD e_crlc; + WORD e_cparhdr; + WORD e_minalloc; + WORD e_maxalloc; + WORD e_ss; + WORD e_sp; + WORD e_csum; + WORD e_ip; + WORD e_cs; + WORD e_lfarlc; + WORD e_ovno; + WORD[4] e_res; + WORD e_oemid; + WORD e_oeminfo; + WORD[10] e_res2; + LONG e_lfanew; +} +alias IMAGE_DOS_HEADER* PIMAGE_DOS_HEADER; + +struct IMAGE_OS2_HEADER { + WORD ne_magic; + CHAR ne_ver = 0; + CHAR ne_rev = 0; + WORD ne_enttab; + WORD ne_cbenttab; + LONG ne_crc; + WORD ne_flags; + WORD ne_autodata; + WORD ne_heap; + WORD ne_stack; + LONG ne_csip; + LONG ne_sssp; + WORD ne_cseg; + WORD ne_cmod; + WORD ne_cbnrestab; + WORD ne_segtab; + WORD ne_rsrctab; + WORD ne_restab; + WORD ne_modtab; + WORD ne_imptab; + LONG ne_nrestab; + WORD ne_cmovent; + WORD ne_align; + WORD ne_cres; + BYTE ne_exetyp; + BYTE ne_flagsothers; + WORD ne_pretthunks; + WORD ne_psegrefbytes; + WORD ne_swaparea; + WORD ne_expver; +} +alias IMAGE_OS2_HEADER* PIMAGE_OS2_HEADER; + +align(4) struct IMAGE_NT_HEADERS32 { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER32 OptionalHeader; +} +alias IMAGE_NT_HEADERS32* PIMAGE_NT_HEADERS32; + +align(4) struct IMAGE_NT_HEADERS64 { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} +alias IMAGE_NT_HEADERS64* PIMAGE_NT_HEADERS64; + +struct IMAGE_ROM_HEADERS { + IMAGE_FILE_HEADER FileHeader; + IMAGE_ROM_OPTIONAL_HEADER OptionalHeader; +} +alias IMAGE_ROM_HEADERS* PIMAGE_ROM_HEADERS; + +struct IMAGE_SECTION_HEADER { + BYTE[IMAGE_SIZEOF_SHORT_NAME] Name; + union _Misc { + DWORD PhysicalAddress; + DWORD VirtualSize; + } + _Misc Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} +alias IMAGE_SECTION_HEADER* PIMAGE_SECTION_HEADER; + +struct IMAGE_SYMBOL { + union _N { + BYTE[8] ShortName; + struct _Name { + DWORD Short; + DWORD Long; + } + _Name Name; + DWORD[2] LongName; // PBYTE[2] + } + _N N; + DWORD Value; + SHORT SectionNumber; + WORD Type; + BYTE StorageClass; + BYTE NumberOfAuxSymbols; +} +alias IMAGE_SYMBOL* PIMAGE_SYMBOL; + +union IMAGE_AUX_SYMBOL { + struct _Sym { + DWORD TagIndex; + union _Misc { + struct _LnSz { + WORD Linenumber; + WORD Size; + } + _LnSz LnSz; + DWORD TotalSize; + } + _Misc Misc; + union _FcnAry { + struct _Function { + DWORD PointerToLinenumber; + DWORD PointerToNextFunction; + } + _Function Function; + struct _Array { + WORD[4] Dimension; + } + _Array Array; + } + _FcnAry FcnAry; + WORD TvIndex; + } + _Sym Sym; + struct _File { + BYTE[IMAGE_SIZEOF_SYMBOL] Name; + } + _File File; + struct _Section { + DWORD Length; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD CheckSum; + SHORT Number; + BYTE Selection; + } + _Section Section; +} +alias IMAGE_AUX_SYMBOL* PIMAGE_AUX_SYMBOL; + +struct IMAGE_COFF_SYMBOLS_HEADER { + DWORD NumberOfSymbols; + DWORD LvaToFirstSymbol; + DWORD NumberOfLinenumbers; + DWORD LvaToFirstLinenumber; + DWORD RvaToFirstByteOfCode; + DWORD RvaToLastByteOfCode; + DWORD RvaToFirstByteOfData; + DWORD RvaToLastByteOfData; +} +alias IMAGE_COFF_SYMBOLS_HEADER* PIMAGE_COFF_SYMBOLS_HEADER; + +struct IMAGE_RELOCATION { + union { + DWORD VirtualAddress; + DWORD RelocCount; + } + DWORD SymbolTableIndex; + WORD Type; +} +alias IMAGE_RELOCATION* PIMAGE_RELOCATION; + +align(4) struct IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; +} +alias IMAGE_BASE_RELOCATION* PIMAGE_BASE_RELOCATION; + +align(2) struct IMAGE_LINENUMBER { + union _Type { + DWORD SymbolTableIndex; + DWORD VirtualAddress; + } + _Type Type; + WORD Linenumber; +} +alias IMAGE_LINENUMBER* PIMAGE_LINENUMBER; + +align(4): +struct IMAGE_ARCHIVE_MEMBER_HEADER { + BYTE[16] Name; + BYTE[12] Date; + BYTE[6] UserID; + BYTE[6] GroupID; + BYTE[8] Mode; + BYTE[10] Size; + BYTE[2] EndHeader; +} +alias IMAGE_ARCHIVE_MEMBER_HEADER* PIMAGE_ARCHIVE_MEMBER_HEADER; + +struct IMAGE_EXPORT_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; + DWORD AddressOfNames; + DWORD AddressOfNameOrdinals; +} +alias IMAGE_EXPORT_DIRECTORY* PIMAGE_EXPORT_DIRECTORY; + +struct IMAGE_IMPORT_BY_NAME { + WORD Hint; + BYTE _Name; + + BYTE* Name() return { return &_Name; } +} +alias IMAGE_IMPORT_BY_NAME* PIMAGE_IMPORT_BY_NAME; + +struct IMAGE_THUNK_DATA32 { + union _u1 { + DWORD ForwarderString; + DWORD Function; + DWORD Ordinal; + DWORD AddressOfData; + } + _u1 u1; +} +alias IMAGE_THUNK_DATA32* PIMAGE_THUNK_DATA32; + +struct IMAGE_THUNK_DATA64 { + union _u1 { + ULONGLONG ForwarderString; + ULONGLONG Function; + ULONGLONG Ordinal; + ULONGLONG AddressOfData; + } + _u1 u1; +} +alias IMAGE_THUNK_DATA64* PIMAGE_THUNK_DATA64; + +struct IMAGE_IMPORT_DESCRIPTOR { + union { + DWORD Characteristics; + DWORD OriginalFirstThunk; + } + DWORD TimeDateStamp; + DWORD ForwarderChain; + DWORD Name; + DWORD FirstThunk; +} +alias IMAGE_IMPORT_DESCRIPTOR* PIMAGE_IMPORT_DESCRIPTOR; + +struct IMAGE_BOUND_IMPORT_DESCRIPTOR { + DWORD TimeDateStamp; + WORD OffsetModuleName; + WORD NumberOfModuleForwarderRefs; +} +alias IMAGE_BOUND_IMPORT_DESCRIPTOR* PIMAGE_BOUND_IMPORT_DESCRIPTOR; + +struct IMAGE_BOUND_FORWARDER_REF { + DWORD TimeDateStamp; + WORD OffsetModuleName; + WORD Reserved; +} +alias IMAGE_BOUND_FORWARDER_REF* PIMAGE_BOUND_FORWARDER_REF; + +struct IMAGE_TLS_DIRECTORY32 { + DWORD StartAddressOfRawData; + DWORD EndAddressOfRawData; + DWORD AddressOfIndex; + DWORD AddressOfCallBacks; + DWORD SizeOfZeroFill; + DWORD Characteristics; +} +alias IMAGE_TLS_DIRECTORY32* PIMAGE_TLS_DIRECTORY32; + +struct IMAGE_TLS_DIRECTORY64 { + ULONGLONG StartAddressOfRawData; + ULONGLONG EndAddressOfRawData; + ULONGLONG AddressOfIndex; + ULONGLONG AddressOfCallBacks; + DWORD SizeOfZeroFill; + DWORD Characteristics; +} +alias IMAGE_TLS_DIRECTORY64* PIMAGE_TLS_DIRECTORY64; + +struct IMAGE_RESOURCE_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + WORD NumberOfNamedEntries; + WORD NumberOfIdEntries; +} +alias IMAGE_RESOURCE_DIRECTORY* PIMAGE_RESOURCE_DIRECTORY; + +struct IMAGE_RESOURCE_DIRECTORY_ENTRY { + union { + /+struct { + DWORD NameOffset:31; + DWORD NameIsString:1; + }+/ + DWORD Name; + WORD Id; + } + DWORD OffsetToData; + /+struct { + DWORD OffsetToDirectory:31; + DWORD DataIsDirectory:1; + }+/ + + uint NameOffset() { return Name & 0x7FFFFFFF; } + bool NameIsString() { return cast(bool)(Name & 0x80000000); } + uint OffsetToDirectory()() { return OffsetToData & 0x7FFFFFFF; } + bool DataIsDirectory() { return cast(bool)(OffsetToData & 0x80000000); } + + uint NameOffset(uint n) { + Name = (Name & 0x80000000) | (n & 0x7FFFFFFF); + return n & 0x7FFFFFFF; + } + + bool NameIsString(bool n) { + Name = (Name & 0x7FFFFFFF) | (n << 31); return n; + } + + uint OffsetToDirectory(uint o) { + OffsetToData = (OffsetToData & 0x80000000) | (o & 0x7FFFFFFF); + return o & 0x7FFFFFFF; + } + + bool DataIsDirectory(bool d) { + OffsetToData = (OffsetToData & 0x7FFFFFFF) | (d << 31); return d; + } +} +alias IMAGE_RESOURCE_DIRECTORY_ENTRY* PIMAGE_RESOURCE_DIRECTORY_ENTRY; + +struct IMAGE_RESOURCE_DIRECTORY_STRING { + WORD Length; + CHAR _NameString = 0; + + CHAR* NameString() return { return &_NameString; } +} +alias IMAGE_RESOURCE_DIRECTORY_STRING* PIMAGE_RESOURCE_DIRECTORY_STRING; + +struct IMAGE_RESOURCE_DIR_STRING_U { + WORD Length; + WCHAR _NameString = 0; + + WCHAR* NameString() return { return &_NameString; } +} +alias IMAGE_RESOURCE_DIR_STRING_U* PIMAGE_RESOURCE_DIR_STRING_U; + +struct IMAGE_RESOURCE_DATA_ENTRY { + DWORD OffsetToData; + DWORD Size; + DWORD CodePage; + DWORD Reserved; +} +alias IMAGE_RESOURCE_DATA_ENTRY* PIMAGE_RESOURCE_DATA_ENTRY; + +struct IMAGE_LOAD_CONFIG_DIRECTORY32 { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD GlobalFlagsClear; + DWORD GlobalFlagsSet; + DWORD CriticalSectionDefaultTimeout; + DWORD DeCommitFreeBlockThreshold; + DWORD DeCommitTotalFreeThreshold; + PVOID LockPrefixTable; + DWORD MaximumAllocationSize; + DWORD VirtualMemoryThreshold; + DWORD ProcessHeapFlags; + DWORD[4] Reserved; +} +alias IMAGE_LOAD_CONFIG_DIRECTORY32* PIMAGE_LOAD_CONFIG_DIRECTORY32; + +struct IMAGE_LOAD_CONFIG_DIRECTORY64 { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD GlobalFlagsClear; + DWORD GlobalFlagsSet; + DWORD CriticalSectionDefaultTimeout; + ULONGLONG DeCommitFreeBlockThreshold; + ULONGLONG DeCommitTotalFreeThreshold; + ULONGLONG LockPrefixTable; + ULONGLONG MaximumAllocationSize; + ULONGLONG VirtualMemoryThreshold; + ULONGLONG ProcessAffinityMask; + DWORD ProcessHeapFlags; + WORD CSDFlags; + WORD Reserved1; + ULONGLONG EditList; + DWORD[2] Reserved; +} +alias IMAGE_LOAD_CONFIG_DIRECTORY64* PIMAGE_LOAD_CONFIG_DIRECTORY64; + +version (Win64) { + alias IMAGE_LOAD_CONFIG_DIRECTORY64 IMAGE_LOAD_CONFIG_DIRECTORY; +} else { + alias IMAGE_LOAD_CONFIG_DIRECTORY32 IMAGE_LOAD_CONFIG_DIRECTORY; +} +alias IMAGE_LOAD_CONFIG_DIRECTORY* PIMAGE_LOAD_CONFIG_DIRECTORY; + +// Note versions for Alpha, Alpha64, ARM removed. +struct IMAGE_RUNTIME_FUNCTION_ENTRY { + DWORD BeginAddress; + DWORD EndAddress; + union { + DWORD UnwindInfoAddress; + DWORD UnwindData; + } +} +alias IMAGE_RUNTIME_FUNCTION_ENTRY* PIMAGE_RUNTIME_FUNCTION_ENTRY; + +struct IMAGE_CE_RUNTIME_FUNCTION_ENTRY { + uint FuncStart; + union { + ubyte PrologLen; + uint _bf; + } +/+ + unsigned int FuncLen:22; + unsigned int ThirtyTwoBit:1; + unsigned int ExceptionFlag:1; ++/ + uint FuncLen() { return (_bf >> 8) & 0x3FFFFF; } + bool ThirtyTwoBit() { return cast(bool)(_bf & 0x40000000); } + bool ExceptionFlag()() { return cast(bool)(_bf & 0x80000000); } + + uint FuncLen(uint f) { + _bf = (_bf & ~0x3FFFFF00) | ((f & 0x3FFFFF) << 8); return f & 0x3FFFFF; + } + + bool ThirtyTwoBit(bool t) { + _bf = (_bf & ~0x40000000) | (t << 30); return t; + } + + bool ExceptionFlag(bool e) { + _bf = (_bf & ~0x80000000) | (e << 31); return e; + } +} +alias IMAGE_CE_RUNTIME_FUNCTION_ENTRY* PIMAGE_CE_RUNTIME_FUNCTION_ENTRY; + +struct IMAGE_DEBUG_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Type; + DWORD SizeOfData; + DWORD AddressOfRawData; + DWORD PointerToRawData; +} +alias IMAGE_DEBUG_DIRECTORY* PIMAGE_DEBUG_DIRECTORY; + +struct FPO_DATA { + DWORD ulOffStart; + DWORD cbProcSize; + DWORD cdwLocals; + WORD cdwParams; + ubyte cbProlog; + ubyte _bf; +/+ + WORD cbRegs:3; + WORD fHasSEH:1; + WORD fUseBP:1; + WORD reserved:1; + WORD cbFrame:2; ++/ + ubyte cbRegs() { return cast(ubyte)(_bf & 0x07); } + bool fHasSEH() { return cast(bool)(_bf & 0x08); } + bool fUseBP() { return cast(bool)(_bf & 0x10); } + bool reserved()() { return cast(bool)(_bf & 0x20); } + ubyte cbFrame()() { return cast(ubyte)(_bf >> 6); } + + ubyte cbRegs(ubyte c) { + _bf = cast(ubyte) ((_bf & ~0x07) | (c & 0x07)); + return cast(ubyte)(c & 0x07); + } + + bool fHasSEH(bool f) { _bf = cast(ubyte)((_bf & ~0x08) | (f << 3)); return f; } + bool fUseBP(bool f) { _bf = cast(ubyte)((_bf & ~0x10) | (f << 4)); return f; } + bool reserved(bool r) { _bf = cast(ubyte)((_bf & ~0x20) | (r << 5)); return r; } + + ubyte cbFrame(ubyte c) { + _bf = cast(ubyte) ((_bf & ~0xC0) | ((c & 0x03) << 6)); + return cast(ubyte)(c & 0x03); + } +} +alias FPO_DATA* PFPO_DATA; + +struct IMAGE_DEBUG_MISC { + DWORD DataType; + DWORD Length; + BOOLEAN Unicode; + BYTE[3] Reserved; + BYTE _Data; + + BYTE* Data() return { return &_Data; } +} +alias IMAGE_DEBUG_MISC* PIMAGE_DEBUG_MISC; + +struct IMAGE_FUNCTION_ENTRY { + DWORD StartingAddress; + DWORD EndingAddress; + DWORD EndOfPrologue; +} +alias IMAGE_FUNCTION_ENTRY* PIMAGE_FUNCTION_ENTRY; + +struct IMAGE_FUNCTION_ENTRY64 { + ULONGLONG StartingAddress; + ULONGLONG EndingAddress; + union { + ULONGLONG EndOfPrologue; + ULONGLONG UnwindInfoAddress; + } +} +alias IMAGE_FUNCTION_ENTRY64* PIMAGE_FUNCTION_ENTRY64; + +struct IMAGE_SEPARATE_DEBUG_HEADER { + WORD Signature; + WORD Flags; + WORD Machine; + WORD Characteristics; + DWORD TimeDateStamp; + DWORD CheckSum; + DWORD ImageBase; + DWORD SizeOfImage; + DWORD NumberOfSections; + DWORD ExportedNamesSize; + DWORD DebugDirectorySize; + DWORD SectionAlignment; + DWORD[2] Reserved; +} +alias IMAGE_SEPARATE_DEBUG_HEADER* PIMAGE_SEPARATE_DEBUG_HEADER; + +enum SERVICE_NODE_TYPE { + DriverType = SERVICE_KERNEL_DRIVER, + FileSystemType = SERVICE_FILE_SYSTEM_DRIVER, + Win32ServiceOwnProcess = SERVICE_WIN32_OWN_PROCESS, + Win32ServiceShareProcess = SERVICE_WIN32_SHARE_PROCESS, + AdapterType = SERVICE_ADAPTER, + RecognizerType = SERVICE_RECOGNIZER_DRIVER +} + +enum SERVICE_LOAD_TYPE { + BootLoad = SERVICE_BOOT_START, + SystemLoad = SERVICE_SYSTEM_START, + AutoLoad = SERVICE_AUTO_START, + DemandLoad = SERVICE_DEMAND_START, + DisableLoad = SERVICE_DISABLED +} + +enum SERVICE_ERROR_TYPE { + IgnoreError = SERVICE_ERROR_IGNORE, + NormalError = SERVICE_ERROR_NORMAL, + SevereError = SERVICE_ERROR_SEVERE, + CriticalError = SERVICE_ERROR_CRITICAL +} +alias SERVICE_ERROR_TYPE _CM_ERROR_CONTROL_TYPE; + +//DAC: According to MSJ, 'UnderTheHood', May 1996, this +// structure is not documented in any official Microsoft header file. +alias void EXCEPTION_REGISTRATION_RECORD; + +align: +struct NT_TIB { + EXCEPTION_REGISTRATION_RECORD *ExceptionList; + PVOID StackBase; + PVOID StackLimit; + PVOID SubSystemTib; + union { + PVOID FiberData; + DWORD Version; + } + PVOID ArbitraryUserPointer; + NT_TIB *Self; +} +alias NT_TIB* PNT_TIB; + +struct REPARSE_DATA_BUFFER { + DWORD ReparseTag; + WORD ReparseDataLength; + WORD Reserved; + union { + struct _GenericReparseBuffer { + BYTE _DataBuffer; + + BYTE* DataBuffer() return { return &_DataBuffer; } + } + _GenericReparseBuffer GenericReparseBuffer; + struct _SymbolicLinkReparseBuffer { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + // ??? This is in MinGW, but absent in MSDN docs + ULONG Flags; + WCHAR _PathBuffer = 0; + + WCHAR* PathBuffer() return { return &_PathBuffer; } + } + _SymbolicLinkReparseBuffer SymbolicLinkReparseBuffer; + struct _MountPointReparseBuffer { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR _PathBuffer = 0; + + WCHAR* PathBuffer() return { return &_PathBuffer; } + } + _MountPointReparseBuffer MountPointReparseBuffer; + } +} +alias REPARSE_DATA_BUFFER *PREPARSE_DATA_BUFFER; + +struct REPARSE_GUID_DATA_BUFFER { + DWORD ReparseTag; + WORD ReparseDataLength; + WORD Reserved; + GUID ReparseGuid; + struct _GenericReparseBuffer { + BYTE _DataBuffer; + + BYTE* DataBuffer() return { return &_DataBuffer; } + } + _GenericReparseBuffer GenericReparseBuffer; +} +alias REPARSE_GUID_DATA_BUFFER* PREPARSE_GUID_DATA_BUFFER; + +enum size_t + REPARSE_DATA_BUFFER_HEADER_SIZE = REPARSE_DATA_BUFFER.GenericReparseBuffer.offsetof, + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE = REPARSE_GUID_DATA_BUFFER.GenericReparseBuffer.offsetof, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384; + + +struct REPARSE_POINT_INFORMATION { + WORD ReparseDataLength; + WORD UnparsedNameLength; +} +alias REPARSE_POINT_INFORMATION* PREPARSE_POINT_INFORMATION; + +union FILE_SEGMENT_ELEMENT { + PVOID64 Buffer; + ULONGLONG Alignment; +} +alias FILE_SEGMENT_ELEMENT* PFILE_SEGMENT_ELEMENT; + +// JOBOBJECT_BASIC_LIMIT_INFORMATION.LimitFlags constants +enum DWORD + JOB_OBJECT_LIMIT_WORKINGSET = 0x0001, + JOB_OBJECT_LIMIT_PROCESS_TIME = 0x0002, + JOB_OBJECT_LIMIT_JOB_TIME = 0x0004, + JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x0008, + JOB_OBJECT_LIMIT_AFFINITY = 0x0010, + JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x0020, + JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x0040, + JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x0080, + JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x0100, + JOB_OBJECT_LIMIT_JOB_MEMORY = 0x0200, + JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x0400, + JOB_OBJECT_BREAKAWAY_OK = 0x0800, + JOB_OBJECT_SILENT_BREAKAWAY = 0x1000; + +// JOBOBJECT_BASIC_UI_RESTRICTIONS.UIRestrictionsClass constants +enum DWORD + JOB_OBJECT_UILIMIT_HANDLES = 0x0001, + JOB_OBJECT_UILIMIT_READCLIPBOARD = 0x0002, + JOB_OBJECT_UILIMIT_WRITECLIPBOARD = 0x0004, + JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS = 0x0008, + JOB_OBJECT_UILIMIT_DISPLAYSETTINGS = 0x0010, + JOB_OBJECT_UILIMIT_GLOBALATOMS = 0x0020, + JOB_OBJECT_UILIMIT_DESKTOP = 0x0040, + JOB_OBJECT_UILIMIT_EXITWINDOWS = 0x0080; + +// JOBOBJECT_SECURITY_LIMIT_INFORMATION.SecurityLimitFlags constants +enum DWORD + JOB_OBJECT_SECURITY_NO_ADMIN = 0x0001, + JOB_OBJECT_SECURITY_RESTRICTED_TOKEN = 0x0002, + JOB_OBJECT_SECURITY_ONLY_TOKEN = 0x0004, + JOB_OBJECT_SECURITY_FILTER_TOKENS = 0x0008; + +// JOBOBJECT_END_OF_JOB_TIME_INFORMATION.EndOfJobTimeAction constants +enum : DWORD { + JOB_OBJECT_TERMINATE_AT_END_OF_JOB, + JOB_OBJECT_POST_AT_END_OF_JOB +} + +enum : DWORD { + JOB_OBJECT_MSG_END_OF_JOB_TIME = 1, + JOB_OBJECT_MSG_END_OF_PROCESS_TIME, + JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT, + JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO, + JOB_OBJECT_MSG_NEW_PROCESS, + JOB_OBJECT_MSG_EXIT_PROCESS, + JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS, + JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT, + JOB_OBJECT_MSG_JOB_MEMORY_LIMIT +} + +enum JOBOBJECTINFOCLASS { + JobObjectBasicAccountingInformation = 1, + JobObjectBasicLimitInformation, + JobObjectBasicProcessIdList, + JobObjectBasicUIRestrictions, + JobObjectSecurityLimitInformation, + JobObjectEndOfJobTimeInformation, + JobObjectAssociateCompletionPortInformation, + JobObjectBasicAndIoAccountingInformation, + JobObjectExtendedLimitInformation, + JobObjectJobSetInformation, + MaxJobObjectInfoClass +} + +struct JOBOBJECT_BASIC_ACCOUNTING_INFORMATION { + LARGE_INTEGER TotalUserTime; + LARGE_INTEGER TotalKernelTime; + LARGE_INTEGER ThisPeriodTotalUserTime; + LARGE_INTEGER ThisPeriodTotalKernelTime; + DWORD TotalPageFaultCount; + DWORD TotalProcesses; + DWORD ActiveProcesses; + DWORD TotalTerminatedProcesses; +} +alias JOBOBJECT_BASIC_ACCOUNTING_INFORMATION* PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION; + +struct JOBOBJECT_BASIC_LIMIT_INFORMATION { + LARGE_INTEGER PerProcessUserTimeLimit; + LARGE_INTEGER PerJobUserTimeLimit; + DWORD LimitFlags; + SIZE_T MinimumWorkingSetSize; + SIZE_T MaximumWorkingSetSize; + DWORD ActiveProcessLimit; + ULONG_PTR Affinity; + DWORD PriorityClass; + DWORD SchedulingClass; +} +alias JOBOBJECT_BASIC_LIMIT_INFORMATION* PJOBOBJECT_BASIC_LIMIT_INFORMATION; + +struct JOBOBJECT_BASIC_PROCESS_ID_LIST { + DWORD NumberOfAssignedProcesses; + DWORD NumberOfProcessIdsInList; + ULONG_PTR _ProcessIdList; + + ULONG_PTR* ProcessIdList() return { return &_ProcessIdList; } +} +alias JOBOBJECT_BASIC_PROCESS_ID_LIST* PJOBOBJECT_BASIC_PROCESS_ID_LIST; + +struct JOBOBJECT_BASIC_UI_RESTRICTIONS { + DWORD UIRestrictionsClass; +} +alias JOBOBJECT_BASIC_UI_RESTRICTIONS* PJOBOBJECT_BASIC_UI_RESTRICTIONS; + +struct JOBOBJECT_SECURITY_LIMIT_INFORMATION { + DWORD SecurityLimitFlags; + HANDLE JobToken; + PTOKEN_GROUPS SidsToDisable; + PTOKEN_PRIVILEGES PrivilegesToDelete; + PTOKEN_GROUPS RestrictedSids; +} +alias JOBOBJECT_SECURITY_LIMIT_INFORMATION* PJOBOBJECT_SECURITY_LIMIT_INFORMATION; + +struct JOBOBJECT_END_OF_JOB_TIME_INFORMATION { + DWORD EndOfJobTimeAction; +} +alias JOBOBJECT_END_OF_JOB_TIME_INFORMATION* PJOBOBJECT_END_OF_JOB_TIME_INFORMATION; + +struct JOBOBJECT_ASSOCIATE_COMPLETION_PORT { + PVOID CompletionKey; + HANDLE CompletionPort; +} +alias JOBOBJECT_ASSOCIATE_COMPLETION_PORT* PJOBOBJECT_ASSOCIATE_COMPLETION_PORT; + +struct JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION { + JOBOBJECT_BASIC_ACCOUNTING_INFORMATION BasicInfo; + IO_COUNTERS IoInfo; +} +alias JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION *PJOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION; + +struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION { + JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; + IO_COUNTERS IoInfo; + SIZE_T ProcessMemoryLimit; + SIZE_T JobMemoryLimit; + SIZE_T PeakProcessMemoryUsed; + SIZE_T PeakJobMemoryUsed; +} +alias JOBOBJECT_EXTENDED_LIMIT_INFORMATION* PJOBOBJECT_EXTENDED_LIMIT_INFORMATION; + +struct JOBOBJECT_JOBSET_INFORMATION { + DWORD MemberLevel; +} +alias JOBOBJECT_JOBSET_INFORMATION* PJOBOBJECT_JOBSET_INFORMATION; + +// MinGW: Making these defines conditional on _WIN32_WINNT will break ddk includes +//static if (_WIN32_WINNT >= 0x500) { + +enum DWORD + ES_SYSTEM_REQUIRED = 0x00000001, + ES_DISPLAY_REQUIRED = 0x00000002, + ES_USER_PRESENT = 0x00000004, + ES_AWAYMODE_REQUIRED = 0x00000040, + ES_CONTINUOUS = 0x80000000; + +enum LATENCY_TIME { + LT_DONT_CARE, + LT_LOWEST_LATENCY +} +alias LATENCY_TIME* PLATENCY_TIME; + +enum SYSTEM_POWER_STATE { + PowerSystemUnspecified, + PowerSystemWorking, + PowerSystemSleeping1, + PowerSystemSleeping2, + PowerSystemSleeping3, + PowerSystemHibernate, + PowerSystemShutdown, + PowerSystemMaximum +} +alias SYSTEM_POWER_STATE* PSYSTEM_POWER_STATE; + +enum POWER_SYSTEM_MAXIMUM = SYSTEM_POWER_STATE.PowerSystemMaximum; + +enum POWER_ACTION { + PowerActionNone, + PowerActionReserved, + PowerActionSleep, + PowerActionHibernate, + PowerActionShutdown, + PowerActionShutdownReset, + PowerActionShutdownOff, + PowerActionWarmEject +} +alias POWER_ACTION* PPOWER_ACTION; + +static if (_WIN32_WINNT >= 0x600) { + enum SYSTEM_POWER_CONDITION { + PoAc, + PoDc, + PoHot, + PoConditionMaximum + } + alias SYSTEM_POWER_CONDITION* PSYSTEM_POWER_CONDITION; +} + +enum DEVICE_POWER_STATE { + PowerDeviceUnspecified, + PowerDeviceD0, + PowerDeviceD1, + PowerDeviceD2, + PowerDeviceD3, + PowerDeviceMaximum +} +alias DEVICE_POWER_STATE* PDEVICE_POWER_STATE; + +align(4): +struct BATTERY_REPORTING_SCALE { + DWORD Granularity; + DWORD Capacity; +} +alias BATTERY_REPORTING_SCALE* PBATTERY_REPORTING_SCALE; + +struct POWER_ACTION_POLICY { + POWER_ACTION Action; + ULONG Flags; + ULONG EventCode; +} +alias POWER_ACTION_POLICY* PPOWER_ACTION_POLICY; + +// POWER_ACTION_POLICY.Flags constants +enum ULONG + POWER_ACTION_QUERY_ALLOWED = 0x00000001, + POWER_ACTION_UI_ALLOWED = 0x00000002, + POWER_ACTION_OVERRIDE_APPS = 0x00000004, + POWER_ACTION_LIGHTEST_FIRST = 0x10000000, + POWER_ACTION_LOCK_CONSOLE = 0x20000000, + POWER_ACTION_DISABLE_WAKES = 0x40000000, + POWER_ACTION_CRITICAL = 0x80000000; + +// POWER_ACTION_POLICY.EventCode constants +enum ULONG + POWER_LEVEL_USER_NOTIFY_TEXT = 0x00000001, + POWER_LEVEL_USER_NOTIFY_SOUND = 0x00000002, + POWER_LEVEL_USER_NOTIFY_EXEC = 0x00000004, + POWER_USER_NOTIFY_BUTTON = 0x00000008, + POWER_USER_NOTIFY_SHUTDOWN = 0x00000010, + POWER_FORCE_TRIGGER_RESET = 0x80000000; + +enum size_t + DISCHARGE_POLICY_CRITICAL = 0, + DISCHARGE_POLICY_LOW = 1, + NUM_DISCHARGE_POLICIES = 4; + +enum : BYTE { + PO_THROTTLE_NONE, + PO_THROTTLE_CONSTANT, + PO_THROTTLE_DEGRADE, + PO_THROTTLE_ADAPTIVE, + PO_THROTTLE_MAXIMUM +} + +struct SYSTEM_POWER_LEVEL { + BOOLEAN Enable; + UCHAR[3] Spare; + ULONG BatteryLevel; + POWER_ACTION_POLICY PowerPolicy; + SYSTEM_POWER_STATE MinSystemState; +} +alias SYSTEM_POWER_LEVEL* PSYSTEM_POWER_LEVEL; + +struct SYSTEM_POWER_POLICY { + ULONG Revision; + POWER_ACTION_POLICY PowerButton; + POWER_ACTION_POLICY SleepButton; + POWER_ACTION_POLICY LidClose; + SYSTEM_POWER_STATE LidOpenWake; + ULONG Reserved; + POWER_ACTION_POLICY Idle; + ULONG IdleTimeout; + UCHAR IdleSensitivity; + UCHAR DynamicThrottle; + UCHAR[2] Spare2; + SYSTEM_POWER_STATE MinSleep; + SYSTEM_POWER_STATE MaxSleep; + SYSTEM_POWER_STATE ReducedLatencySleep; + ULONG WinLogonFlags; + ULONG Spare3; + ULONG DozeS4Timeout; + ULONG BroadcastCapacityResolution; + SYSTEM_POWER_LEVEL[NUM_DISCHARGE_POLICIES] DischargePolicy; + ULONG VideoTimeout; + BOOLEAN VideoDimDisplay; + ULONG[3] VideoReserved; + ULONG SpindownTimeout; + BOOLEAN OptimizeForPower; + UCHAR FanThrottleTolerance; + UCHAR ForcedThrottle; + UCHAR MinThrottle; + POWER_ACTION_POLICY OverThrottled; +} +alias SYSTEM_POWER_POLICY* PSYSTEM_POWER_POLICY; + +struct SYSTEM_POWER_CAPABILITIES { + BOOLEAN PowerButtonPresent; + BOOLEAN SleepButtonPresent; + BOOLEAN LidPresent; + BOOLEAN SystemS1; + BOOLEAN SystemS2; + BOOLEAN SystemS3; + BOOLEAN SystemS4; + BOOLEAN SystemS5; + BOOLEAN HiberFilePresent; + BOOLEAN FullWake; + BOOLEAN VideoDimPresent; + BOOLEAN ApmPresent; + BOOLEAN UpsPresent; + BOOLEAN ThermalControl; + BOOLEAN ProcessorThrottle; + UCHAR ProcessorMinThrottle; + UCHAR ProcessorMaxThrottle; + UCHAR[4] spare2; + BOOLEAN DiskSpinDown; + UCHAR[8] spare3; + BOOLEAN SystemBatteriesPresent; + BOOLEAN BatteriesAreShortTerm; + BATTERY_REPORTING_SCALE[3] BatteryScale; + SYSTEM_POWER_STATE AcOnLineWake; + SYSTEM_POWER_STATE SoftLidWake; + SYSTEM_POWER_STATE RtcWake; + SYSTEM_POWER_STATE MinDeviceWakeState; + SYSTEM_POWER_STATE DefaultLowLatencyWake; +} +alias SYSTEM_POWER_CAPABILITIES* PSYSTEM_POWER_CAPABILITIES; + +struct SYSTEM_BATTERY_STATE { + BOOLEAN AcOnLine; + BOOLEAN BatteryPresent; + BOOLEAN Charging; + BOOLEAN Discharging; + BOOLEAN[4] Spare1; + ULONG MaxCapacity; + ULONG RemainingCapacity; + ULONG Rate; + ULONG EstimatedTime; + ULONG DefaultAlert1; + ULONG DefaultAlert2; +} +alias SYSTEM_BATTERY_STATE* PSYSTEM_BATTERY_STATE; + +enum POWER_INFORMATION_LEVEL { + SystemPowerPolicyAc, + SystemPowerPolicyDc, + VerifySystemPolicyAc, + VerifySystemPolicyDc, + SystemPowerCapabilities, + SystemBatteryState, + SystemPowerStateHandler, + ProcessorStateHandler, + SystemPowerPolicyCurrent, + AdministratorPowerPolicy, + SystemReserveHiberFile, + ProcessorInformation, + SystemPowerInformation, + ProcessorStateHandler2, + LastWakeTime, + LastSleepTime, + SystemExecutionState, + SystemPowerStateNotifyHandler, + ProcessorPowerPolicyAc, + ProcessorPowerPolicyDc, + VerifyProcessorPowerPolicyAc, + VerifyProcessorPowerPolicyDc, + ProcessorPowerPolicyCurrent +} + +//#if 1 /* (WIN32_WINNT >= 0x0500) */ +struct SYSTEM_POWER_INFORMATION { + ULONG MaxIdlenessAllowed; + ULONG Idleness; + ULONG TimeRemaining; + UCHAR CoolingMode; +} +alias SYSTEM_POWER_INFORMATION* PSYSTEM_POWER_INFORMATION; +//#endif + +struct PROCESSOR_POWER_POLICY_INFO { + ULONG TimeCheck; + ULONG DemoteLimit; + ULONG PromoteLimit; + UCHAR DemotePercent; + UCHAR PromotePercent; + UCHAR[2] Spare; + uint _bf; + + bool AllowDemotion() { return cast(bool)(_bf & 1); } + bool AllowPromotion()() { return cast(bool)(_bf & 2); } + + bool AllowDemotion(bool a) { _bf = (_bf & ~1) | a; return a; } + bool AllowPromotion(bool a) { _bf = (_bf & ~2) | (a << 1); return a; } +/+ + ULONG AllowDemotion : 1; + ULONG AllowPromotion : 1; + ULONG Reserved : 30; ++/ +} +alias PROCESSOR_POWER_POLICY_INFO* PPROCESSOR_POWER_POLICY_INFO; + +struct PROCESSOR_POWER_POLICY { + ULONG Revision; + UCHAR DynamicThrottle; + UCHAR[3] Spare; + ULONG Reserved; + ULONG PolicyCount; + PROCESSOR_POWER_POLICY_INFO[3] Policy; +} +alias PROCESSOR_POWER_POLICY* PPROCESSOR_POWER_POLICY; + +struct ADMINISTRATOR_POWER_POLICY { + SYSTEM_POWER_STATE MinSleep; + SYSTEM_POWER_STATE MaxSleep; + ULONG MinVideoTimeout; + ULONG MaxVideoTimeout; + ULONG MinSpindownTimeout; + ULONG MaxSpindownTimeout; +} +alias ADMINISTRATOR_POWER_POLICY* PADMINISTRATOR_POWER_POLICY; + +//}//#endif /* _WIN32_WINNT >= 0x500 */ + +extern (Windows) { + alias void function(PVOID, DWORD, PVOID) PIMAGE_TLS_CALLBACK; + + static if (_WIN32_WINNT >= 0x500) { + alias LONG function(PEXCEPTION_POINTERS) PVECTORED_EXCEPTION_HANDLER; + alias void function(PVOID, BOOLEAN) WAITORTIMERCALLBACKFUNC; + } +} + +static if (_WIN32_WINNT >= 0x501) { + enum HEAP_INFORMATION_CLASS { + HeapCompatibilityInformation + } + + enum ACTIVATION_CONTEXT_INFO_CLASS { + ActivationContextBasicInformation = 1, + ActivationContextDetailedInformation, + AssemblyDetailedInformationInActivationContext, + FileInformationInAssemblyOfAssemblyInActivationContext + } + + align struct ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION { + DWORD ulFlags; + DWORD ulEncodedAssemblyIdentityLength; + DWORD ulManifestPathType; + DWORD ulManifestPathLength; + LARGE_INTEGER liManifestLastWriteTime; + DWORD ulPolicyPathType; + DWORD ulPolicyPathLength; + LARGE_INTEGER liPolicyLastWriteTime; + DWORD ulMetadataSatelliteRosterIndex; + DWORD ulManifestVersionMajor; + DWORD ulManifestVersionMinor; + DWORD ulPolicyVersionMajor; + DWORD ulPolicyVersionMinor; + DWORD ulAssemblyDirectoryNameLength; + PCWSTR lpAssemblyEncodedAssemblyIdentity; + PCWSTR lpAssemblyManifestPath; + PCWSTR lpAssemblyPolicyPath; + PCWSTR lpAssemblyDirectoryName; + } + alias ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION* + PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION; + alias const(ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION)* + PCACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION; + + struct ACTIVATION_CONTEXT_DETAILED_INFORMATION { + DWORD dwFlags; + DWORD ulFormatVersion; + DWORD ulAssemblyCount; + DWORD ulRootManifestPathType; + DWORD ulRootManifestPathChars; + DWORD ulRootConfigurationPathType; + DWORD ulRootConfigurationPathChars; + DWORD ulAppDirPathType; + DWORD ulAppDirPathChars; + PCWSTR lpRootManifestPath; + PCWSTR lpRootConfigurationPath; + PCWSTR lpAppDirPath; + } + alias ACTIVATION_CONTEXT_DETAILED_INFORMATION* + PACTIVATION_CONTEXT_DETAILED_INFORMATION; + alias const(ACTIVATION_CONTEXT_DETAILED_INFORMATION)* + PCACTIVATION_CONTEXT_DETAILED_INFORMATION; + + struct ACTIVATION_CONTEXT_QUERY_INDEX { + ULONG ulAssemblyIndex; + ULONG ulFileIndexInAssembly; + } + alias ACTIVATION_CONTEXT_QUERY_INDEX* PACTIVATION_CONTEXT_QUERY_INDEX; + alias const(ACTIVATION_CONTEXT_QUERY_INDEX)* PCACTIVATION_CONTEXT_QUERY_INDEX; + + struct ASSEMBLY_FILE_DETAILED_INFORMATION { + DWORD ulFlags; + DWORD ulFilenameLength; + DWORD ulPathLength; + PCWSTR lpFileName; + PCWSTR lpFilePath; + } + alias ASSEMBLY_FILE_DETAILED_INFORMATION* + PASSEMBLY_FILE_DETAILED_INFORMATION; + alias const(ASSEMBLY_FILE_DETAILED_INFORMATION)* + PCASSEMBLY_FILE_DETAILED_INFORMATION; +} + +version (Unicode) { + alias OSVERSIONINFOW OSVERSIONINFO; + alias OSVERSIONINFOEXW OSVERSIONINFOEX; +} else { + alias OSVERSIONINFOA OSVERSIONINFO; + alias OSVERSIONINFOEXA OSVERSIONINFOEX; +} + +alias OSVERSIONINFO* POSVERSIONINFO, LPOSVERSIONINFO; +alias OSVERSIONINFOEX* POSVERSIONINFOEX, LPOSVERSIONINFOEX; + + +static if (_WIN32_WINNT >= 0x500) { + extern (Windows) ULONGLONG VerSetConditionMask(ULONGLONG, DWORD, BYTE); +} + +version (Win64) { +enum WORD IMAGE_NT_OPTIONAL_HDR_MAGIC = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + + alias IMAGE_ORDINAL_FLAG64 IMAGE_ORDINAL_FLAG; + alias IMAGE_SNAP_BY_ORDINAL64 IMAGE_SNAP_BY_ORDINAL; + alias IMAGE_ORDINAL64 IMAGE_ORDINAL; + alias IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER; + alias IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; + alias IMAGE_THUNK_DATA64 IMAGE_THUNK_DATA; + alias IMAGE_TLS_DIRECTORY64 IMAGE_TLS_DIRECTORY; +} else { +enum WORD IMAGE_NT_OPTIONAL_HDR_MAGIC = IMAGE_NT_OPTIONAL_HDR32_MAGIC; + + alias IMAGE_ORDINAL_FLAG32 IMAGE_ORDINAL_FLAG; + alias IMAGE_ORDINAL32 IMAGE_ORDINAL; + alias IMAGE_SNAP_BY_ORDINAL32 IMAGE_SNAP_BY_ORDINAL; + alias IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER; + alias IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; + alias IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA; + alias IMAGE_TLS_DIRECTORY32 IMAGE_TLS_DIRECTORY; +} + +alias IMAGE_OPTIONAL_HEADER* PIMAGE_OPTIONAL_HEADER; +alias IMAGE_NT_HEADERS* PIMAGE_NT_HEADERS; +alias IMAGE_THUNK_DATA* PIMAGE_THUNK_DATA; +alias IMAGE_TLS_DIRECTORY* PIMAGE_TLS_DIRECTORY; + +// TODO: MinGW implements these in assembly. How to translate? +PVOID GetCurrentFiber(); +PVOID GetFiberData(); diff --git a/src/urt/internal/sys/windows/winsock2.d b/src/urt/internal/sys/windows/winsock2.d new file mode 100644 index 0000000..9ea6983 --- /dev/null +++ b/src/urt/internal/sys/windows/winsock2.d @@ -0,0 +1,766 @@ +/* + Written by Christopher E. Miller + $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). +*/ + + +module urt.internal.sys.windows.winsock2; +version (Windows): + +pragma(lib, "ws2_32"); + +extern(Windows): +nothrow: + +alias SOCKET = size_t; +alias socklen_t = int; + +enum SOCKET INVALID_SOCKET = cast(SOCKET)~0; +enum int SOCKET_ERROR = -1; + +enum WSADESCRIPTION_LEN = 256; +enum WSASYS_STATUS_LEN = 128; + +struct WSADATA +{ + ushort wVersion; + ushort wHighVersion; + char[WSADESCRIPTION_LEN + 1] szDescription = 0; + char[WSASYS_STATUS_LEN + 1] szSystemStatus = 0; + ushort iMaxSockets; + ushort iMaxUdpDg; + char* lpVendorInfo; +} +alias LPWSADATA = WSADATA*; + + +enum int IOCPARM_MASK = 0x7F; +enum int IOC_IN = cast(int)0x80000000; +enum int FIONBIO = cast(int)(IOC_IN | ((uint.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126); + +enum NI_MAXHOST = 1025; +enum NI_MAXSERV = 32; + +@nogc +{ +int WSAStartup(ushort wVersionRequested, LPWSADATA lpWSAData); +@trusted int WSACleanup(); +@trusted SOCKET socket(int af, int type, int protocol); +int ioctlsocket(SOCKET s, int cmd, uint* argp); +int bind(SOCKET s, const(sockaddr)* name, socklen_t namelen); +int connect(SOCKET s, const(sockaddr)* name, socklen_t namelen); +@trusted int listen(SOCKET s, int backlog); +SOCKET accept(SOCKET s, sockaddr* addr, socklen_t* addrlen); +@trusted int closesocket(SOCKET s); +@trusted int shutdown(SOCKET s, int how); +int getpeername(SOCKET s, sockaddr* name, socklen_t* namelen); +int getsockname(SOCKET s, sockaddr* name, socklen_t* namelen); +int send(SOCKET s, const(void)* buf, int len, int flags); +int sendto(SOCKET s, const(void)* buf, int len, int flags, const(sockaddr)* to, socklen_t tolen); +int recv(SOCKET s, void* buf, int len, int flags); +int recvfrom(SOCKET s, void* buf, int len, int flags, sockaddr* from, socklen_t* fromlen); +int getsockopt(SOCKET s, int level, int optname, void* optval, socklen_t* optlen); +int setsockopt(SOCKET s, int level, int optname, const(void)* optval, socklen_t optlen); +uint inet_addr(const char* cp); +int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, const(timeval)* timeout); +char* inet_ntoa(in_addr ina); +hostent* gethostbyname(const char* name); +hostent* gethostbyaddr(const(void)* addr, int len, int type); +protoent* getprotobyname(const char* name); +protoent* getprotobynumber(int number); +servent* getservbyname(const char* name, const char* proto); +servent* getservbyport(int port, const char* proto); +} + +enum: int +{ + NI_NOFQDN = 0x01, + NI_NUMERICHOST = 0x02, + NI_NAMEREQD = 0x04, + NI_NUMERICSERV = 0x08, + NI_DGRAM = 0x10, +} + +@nogc +{ +int gethostname(const char* name, int namelen); +int getaddrinfo(const(char)* nodename, const(char)* servname, const(addrinfo)* hints, addrinfo** res); +void freeaddrinfo(addrinfo* ai); +int getnameinfo(const(sockaddr)* sa, socklen_t salen, char* host, uint hostlen, char* serv, uint servlen, int flags); +} + +enum WSABASEERR = 10000; + +enum: int +{ + /* + * Windows Sockets definitions of regular Microsoft C error constants + */ + WSAEINTR = (WSABASEERR+4), + WSAEBADF = (WSABASEERR+9), + WSAEACCES = (WSABASEERR+13), + WSAEFAULT = (WSABASEERR+14), + WSAEINVAL = (WSABASEERR+22), + WSAEMFILE = (WSABASEERR+24), + + /* + * Windows Sockets definitions of regular Berkeley error constants + */ + WSAEWOULDBLOCK = (WSABASEERR+35), + WSAEINPROGRESS = (WSABASEERR+36), + WSAEALREADY = (WSABASEERR+37), + WSAENOTSOCK = (WSABASEERR+38), + WSAEDESTADDRREQ = (WSABASEERR+39), + WSAEMSGSIZE = (WSABASEERR+40), + WSAEPROTOTYPE = (WSABASEERR+41), + WSAENOPROTOOPT = (WSABASEERR+42), + WSAEPROTONOSUPPORT = (WSABASEERR+43), + WSAESOCKTNOSUPPORT = (WSABASEERR+44), + WSAEOPNOTSUPP = (WSABASEERR+45), + WSAEPFNOSUPPORT = (WSABASEERR+46), + WSAEAFNOSUPPORT = (WSABASEERR+47), + WSAEADDRINUSE = (WSABASEERR+48), + WSAEADDRNOTAVAIL = (WSABASEERR+49), + WSAENETDOWN = (WSABASEERR+50), + WSAENETUNREACH = (WSABASEERR+51), + WSAENETRESET = (WSABASEERR+52), + WSAECONNABORTED = (WSABASEERR+53), + WSAECONNRESET = (WSABASEERR+54), + WSAENOBUFS = (WSABASEERR+55), + WSAEISCONN = (WSABASEERR+56), + WSAENOTCONN = (WSABASEERR+57), + WSAESHUTDOWN = (WSABASEERR+58), + WSAETOOMANYREFS = (WSABASEERR+59), + WSAETIMEDOUT = (WSABASEERR+60), + WSAECONNREFUSED = (WSABASEERR+61), + WSAELOOP = (WSABASEERR+62), + WSAENAMETOOLONG = (WSABASEERR+63), + WSAEHOSTDOWN = (WSABASEERR+64), + WSAEHOSTUNREACH = (WSABASEERR+65), + WSAENOTEMPTY = (WSABASEERR+66), + WSAEPROCLIM = (WSABASEERR+67), + WSAEUSERS = (WSABASEERR+68), + WSAEDQUOT = (WSABASEERR+69), + WSAESTALE = (WSABASEERR+70), + WSAEREMOTE = (WSABASEERR+71), + + /* + * Extended Windows Sockets error constant definitions + */ + WSASYSNOTREADY = (WSABASEERR+91), + WSAVERNOTSUPPORTED = (WSABASEERR+92), + WSANOTINITIALISED = (WSABASEERR+93), + + /* Authoritative Answer: Host not found */ + WSAHOST_NOT_FOUND = (WSABASEERR+1001), + HOST_NOT_FOUND = WSAHOST_NOT_FOUND, + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + WSATRY_AGAIN = (WSABASEERR+1002), + TRY_AGAIN = WSATRY_AGAIN, + + /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + WSANO_RECOVERY = (WSABASEERR+1003), + NO_RECOVERY = WSANO_RECOVERY, + + /* Valid name, no data record of requested type */ + WSANO_DATA = (WSABASEERR+1004), + NO_DATA = WSANO_DATA, + + /* no address, look for MX record */ + WSANO_ADDRESS = WSANO_DATA, + NO_ADDRESS = WSANO_ADDRESS +} + +/* + * Windows Sockets errors redefined as regular Berkeley error constants + */ +enum: int +{ + EWOULDBLOCK = WSAEWOULDBLOCK, + EINPROGRESS = WSAEINPROGRESS, + EALREADY = WSAEALREADY, + ENOTSOCK = WSAENOTSOCK, + EDESTADDRREQ = WSAEDESTADDRREQ, + EMSGSIZE = WSAEMSGSIZE, + EPROTOTYPE = WSAEPROTOTYPE, + ENOPROTOOPT = WSAENOPROTOOPT, + EPROTONOSUPPORT = WSAEPROTONOSUPPORT, + ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT, + EOPNOTSUPP = WSAEOPNOTSUPP, + EPFNOSUPPORT = WSAEPFNOSUPPORT, + EAFNOSUPPORT = WSAEAFNOSUPPORT, + EADDRINUSE = WSAEADDRINUSE, + EADDRNOTAVAIL = WSAEADDRNOTAVAIL, + ENETDOWN = WSAENETDOWN, + ENETUNREACH = WSAENETUNREACH, + ENETRESET = WSAENETRESET, + ECONNABORTED = WSAECONNABORTED, + ECONNRESET = WSAECONNRESET, + ENOBUFS = WSAENOBUFS, + EISCONN = WSAEISCONN, + ENOTCONN = WSAENOTCONN, + ESHUTDOWN = WSAESHUTDOWN, + ETOOMANYREFS = WSAETOOMANYREFS, + ETIMEDOUT = WSAETIMEDOUT, + ECONNREFUSED = WSAECONNREFUSED, + ELOOP = WSAELOOP, + ENAMETOOLONG = WSAENAMETOOLONG, + EHOSTDOWN = WSAEHOSTDOWN, + EHOSTUNREACH = WSAEHOSTUNREACH, + ENOTEMPTY = WSAENOTEMPTY, + EPROCLIM = WSAEPROCLIM, + EUSERS = WSAEUSERS, + EDQUOT = WSAEDQUOT, + ESTALE = WSAESTALE, + EREMOTE = WSAEREMOTE +} + +enum: int +{ + EAI_NONAME = WSAHOST_NOT_FOUND, +} + +int WSAGetLastError() @nogc @trusted; + + +enum: int +{ + AF_UNSPEC = 0, + + AF_UNIX = 1, + AF_INET = 2, + AF_IMPLINK = 3, + AF_PUP = 4, + AF_CHAOS = 5, + AF_NS = 6, + AF_IPX = AF_NS, + AF_ISO = 7, + AF_OSI = AF_ISO, + AF_ECMA = 8, + AF_DATAKIT = 9, + AF_CCITT = 10, + AF_SNA = 11, + AF_DECnet = 12, + AF_DLI = 13, + AF_LAT = 14, + AF_HYLINK = 15, + AF_APPLETALK = 16, + AF_NETBIOS = 17, + AF_VOICEVIEW = 18, + AF_FIREFOX = 19, + AF_UNKNOWN1 = 20, + AF_BAN = 21, + AF_ATM = 22, + AF_INET6 = 23, + AF_CLUSTER = 24, + AF_12844 = 25, + AF_IRDA = 26, + AF_NETDES = 28, + + AF_MAX = 29, + + + PF_UNSPEC = AF_UNSPEC, + + PF_UNIX = AF_UNIX, + PF_INET = AF_INET, + PF_IMPLINK = AF_IMPLINK, + PF_PUP = AF_PUP, + PF_CHAOS = AF_CHAOS, + PF_NS = AF_NS, + PF_IPX = AF_IPX, + PF_ISO = AF_ISO, + PF_OSI = AF_OSI, + PF_ECMA = AF_ECMA, + PF_DATAKIT = AF_DATAKIT, + PF_CCITT = AF_CCITT, + PF_SNA = AF_SNA, + PF_DECnet = AF_DECnet, + PF_DLI = AF_DLI, + PF_LAT = AF_LAT, + PF_HYLINK = AF_HYLINK, + PF_APPLETALK = AF_APPLETALK, + PF_VOICEVIEW = AF_VOICEVIEW, + PF_FIREFOX = AF_FIREFOX, + PF_UNKNOWN1 = AF_UNKNOWN1, + PF_BAN = AF_BAN, + PF_INET6 = AF_INET6, + + PF_MAX = AF_MAX, +} + + +enum: int +{ + SOL_SOCKET = 0xFFFF, +} + + +enum: int +{ + SO_DEBUG = 0x0001, + SO_ACCEPTCONN = 0x0002, + SO_REUSEADDR = 0x0004, + SO_KEEPALIVE = 0x0008, + SO_DONTROUTE = 0x0010, + SO_BROADCAST = 0x0020, + SO_USELOOPBACK = 0x0040, + SO_LINGER = 0x0080, + SO_DONTLINGER = ~SO_LINGER, + SO_OOBINLINE = 0x0100, + SO_SNDBUF = 0x1001, + SO_RCVBUF = 0x1002, + SO_SNDLOWAT = 0x1003, + SO_RCVLOWAT = 0x1004, + SO_SNDTIMEO = 0x1005, + SO_RCVTIMEO = 0x1006, + SO_ERROR = 0x1007, + SO_TYPE = 0x1008, + SO_EXCLUSIVEADDRUSE = ~SO_REUSEADDR, + + TCP_NODELAY = 1, + + IP_OPTIONS = 1, + + IP_HDRINCL = 2, + IP_TOS = 3, + IP_TTL = 4, + IP_MULTICAST_IF = 9, + IP_MULTICAST_TTL = 10, + IP_MULTICAST_LOOP = 11, + IP_ADD_MEMBERSHIP = 12, + IP_DROP_MEMBERSHIP = 13, + IP_DONTFRAGMENT = 14, + IP_ADD_SOURCE_MEMBERSHIP = 15, + IP_DROP_SOURCE_MEMBERSHIP = 16, + IP_BLOCK_SOURCE = 17, + IP_UNBLOCK_SOURCE = 18, + IP_PKTINFO = 19, + + IPV6_UNICAST_HOPS = 4, + IPV6_MULTICAST_IF = 9, + IPV6_MULTICAST_HOPS = 10, + IPV6_MULTICAST_LOOP = 11, + IPV6_ADD_MEMBERSHIP = 12, + IPV6_DROP_MEMBERSHIP = 13, + IPV6_JOIN_GROUP = IPV6_ADD_MEMBERSHIP, + IPV6_LEAVE_GROUP = IPV6_DROP_MEMBERSHIP, + IPV6_V6ONLY = 27, +} + + +/// Default FD_SETSIZE value. +/// In C/C++, it is redefinable by #define-ing the macro before #include-ing +/// winsock.h. In D, use the $(D FD_CREATE) function to allocate a $(D fd_set) +/// of an arbitrary size. +enum int FD_SETSIZE = 64; + + +struct fd_set_custom(uint SETSIZE) +{ + uint fd_count; + SOCKET[SETSIZE] fd_array; +} + +alias fd_set = fd_set_custom!FD_SETSIZE; + +// Removes. +void FD_CLR(SOCKET fd, fd_set* set) pure @nogc +{ + uint c = set.fd_count; + SOCKET* start = set.fd_array.ptr; + SOCKET* stop = start + c; + + for (; start != stop; start++) + { + if (*start == fd) + goto found; + } + return; //not found + + found: + for (++start; start != stop; start++) + { + *(start - 1) = *start; + } + + set.fd_count = c - 1; +} + + +// Tests. +int FD_ISSET(SOCKET fd, const(fd_set)* set) pure @nogc +{ +const(SOCKET)* start = set.fd_array.ptr; +const(SOCKET)* stop = start + set.fd_count; + + for (; start != stop; start++) + { + if (*start == fd) + return true; + } + return false; +} + + +// Adds. +void FD_SET(SOCKET fd, fd_set* set) pure @nogc +{ + uint c = set.fd_count; + set.fd_array.ptr[c] = fd; + set.fd_count = c + 1; +} + + +// Resets to zero. +void FD_ZERO(fd_set* set) pure @nogc +{ + set.fd_count = 0; +} + + +/// Creates a new $(D fd_set) with the specified capacity. +fd_set* FD_CREATE(uint capacity) pure +{ + // Take into account alignment (SOCKET may be 64-bit and require 64-bit alignment on 64-bit systems) + size_t size = (fd_set_custom!1).sizeof - SOCKET.sizeof + (SOCKET.sizeof * capacity); + auto data = new ubyte[size]; + auto set = cast(fd_set*)data.ptr; + FD_ZERO(set); + return set; +} + +struct linger +{ + ushort l_onoff; + ushort l_linger; +} + + +struct protoent +{ + char* p_name; + char** p_aliases; + short p_proto; +} + + +struct servent +{ + char* s_name; + char** s_aliases; + + version (Win64) + { + char* s_proto; + short s_port; + } + else version (Win32) + { + short s_port; + char* s_proto; + } +} + + +/+ +union in6_addr +{ + private union _u_t + { + ubyte[16] Byte; + ushort[8] Word; + } + _u_t u; +} + + +struct in_addr6 +{ + ubyte[16] s6_addr; +} ++/ + +@safe pure @nogc +{ + ushort htons(ushort x); + uint htonl(uint x); + ushort ntohs(ushort x); + uint ntohl(uint x); +} + + +enum: int +{ + SOCK_STREAM = 1, + SOCK_DGRAM = 2, + SOCK_RAW = 3, + SOCK_RDM = 4, + SOCK_SEQPACKET = 5, +} + + +enum: int +{ + IPPROTO_IP = 0, + IPPROTO_ICMP = 1, + IPPROTO_IGMP = 2, + IPPROTO_GGP = 3, + IPPROTO_TCP = 6, + IPPROTO_PUP = 12, + IPPROTO_UDP = 17, + IPPROTO_IDP = 22, + IPPROTO_IPV6 = 41, + IPPROTO_ND = 77, + IPPROTO_RAW = 255, + + IPPROTO_MAX = 256, +} + + +enum: int +{ + MSG_OOB = 0x1, + MSG_PEEK = 0x2, + MSG_DONTROUTE = 0x4 +} + + +enum: int +{ + SD_RECEIVE = 0, + SD_SEND = 1, + SD_BOTH = 2, +} + + +enum: uint +{ + INADDR_ANY = 0, + INADDR_LOOPBACK = 0x7F000001, + INADDR_BROADCAST = 0xFFFFFFFF, + INADDR_NONE = 0xFFFFFFFF, + ADDR_ANY = INADDR_ANY, +} + + +enum: int +{ + AI_PASSIVE = 0x1, // Socket address will be used in bind() call + AI_CANONNAME = 0x2, // Return canonical name in first ai_canonname + AI_NUMERICHOST = 0x4, // Nodename must be a numeric address string + AI_NUMERICSERV = 0x8, // Servicename must be a numeric port number + AI_DNS_ONLY = 0x10, // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.) + AI_FORCE_CLEAR_TEXT = 0x20, // Force clear text DNS query + AI_BYPASS_DNS_CACHE = 0x40, // Bypass DNS cache + AI_RETURN_TTL = 0x80, // Return record TTL + AI_ALL = 0x0100, // Query both IP6 and IP4 with AI_V4MAPPED + AI_ADDRCONFIG = 0x0400, // Resolution only if global address configured + AI_V4MAPPED = 0x0800, // On v6 failure, query v4 and convert to V4MAPPED format + AI_NON_AUTHORITATIVE = 0x04000, // LUP_NON_AUTHORITATIVE + AI_SECURE = 0x08000, // LUP_SECURE + AI_RETURN_PREFERRED_NAMES = 0x010000, // LUP_RETURN_PREFERRED_NAMES + AI_FQDN = 0x00020000, // Return the FQDN in ai_canonname + AI_FILESERVER = 0x00040000, // Resolving fileserver name resolution + AI_DISABLE_IDN_ENCODING = 0x00080000, // Disable Internationalized Domain Names handling + AI_SECURE_WITH_FALLBACK = 0x00100000, // Forces clear text fallback if the secure DNS query fails + AI_EXCLUSIVE_CUSTOM_SERVERS = 0x00200000, // Use exclusively the custom DNS servers + AI_RETURN_RESPONSE_FLAGS = 0x10000000, // Requests extra information about the DNS results + AI_REQUIRE_SECURE = 0x20000000, // Forces the DNS query to be done over seucre protocols + AI_RESOLUTION_HANDLE = 0x40000000, // Request resolution handle + AI_EXTENDED = 0x80000000, // Indicates this is extended ADDRINFOEX(2/..) struct +} + + +struct timeval +{ + int tv_sec; + int tv_usec; +} + + +union in_addr +{ + private union _S_un_t + { + private struct _S_un_b_t + { + ubyte s_b1, s_b2, s_b3, s_b4; + } + _S_un_b_t S_un_b; + + private struct _S_un_w_t + { + ushort s_w1, s_w2; + } + _S_un_w_t S_un_w; + + uint S_addr; + } + _S_un_t S_un; + + uint s_addr; + + struct + { + ubyte s_net, s_host; + + union + { + ushort s_imp; + + struct + { + ubyte s_lh, s_impno; + } + } + } +} + + +union in6_addr +{ + private union _in6_u_t + { + ubyte[16] u6_addr8; + ushort[8] u6_addr16; + uint[4] u6_addr32; + } + _in6_u_t in6_u; + + ubyte[16] s6_addr8; + ushort[8] s6_addr16; + uint[4] s6_addr32; + + alias s6_addr = s6_addr8; +} + + +enum in6_addr IN6ADDR_ANY = { s6_addr8: [0] }; +enum in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }; +//alias IN6ADDR_ANY_INIT = IN6ADDR_ANY; +//alias IN6ADDR_LOOPBACK_INIT = IN6ADDR_LOOPBACK; + +enum int INET_ADDRSTRLEN = 16; +enum int INET6_ADDRSTRLEN = 46; + + + + +struct sockaddr +{ + short sa_family; + ubyte[14] sa_data; +} +alias sockaddr SOCKADDR; +alias SOCKADDR* PSOCKADDR, LPSOCKADDR; + +struct sockaddr_storage +{ + short ss_family; + char[6] __ss_pad1 = void; + long __ss_align; + char[112] __ss_pad2 = void; +} +alias sockaddr_storage SOCKADDR_STORAGE; +alias SOCKADDR_STORAGE* PSOCKADDR_STORAGE; + +struct sockaddr_in +{ + short sin_family = AF_INET; + ushort sin_port; + in_addr sin_addr; + ubyte[8] sin_zero; +} +alias sockaddr_in SOCKADDR_IN; +alias SOCKADDR_IN* PSOCKADDR_IN, LPSOCKADDR_IN; + + +struct sockaddr_in6 +{ + short sin6_family = AF_INET6; + ushort sin6_port; + uint sin6_flowinfo; + in6_addr sin6_addr; + uint sin6_scope_id; +} + + +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo* ai_next; +} + + +struct hostent +{ + char* h_name; + char** h_aliases; + short h_addrtype; + short h_length; + char** h_addr_list; + + + char* h_addr() @safe pure nothrow @nogc + { + return h_addr_list[0]; + } +} + +// Note: These are Winsock2!! +struct WSAOVERLAPPED; +alias LPWSAOVERLAPPED = WSAOVERLAPPED*; +alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint) nothrow @nogc; +int WSAIoctl(SOCKET s, uint dwIoControlCode, + void* lpvInBuffer, uint cbInBuffer, + void* lpvOutBuffer, uint cbOutBuffer, + uint* lpcbBytesReturned, + LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) @nogc; + + +enum IOC_VENDOR = 0x18000000; +enum SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4; + +/* Argument structure for SIO_KEEPALIVE_VALS */ +struct tcp_keepalive +{ + uint onoff; + uint keepalivetime; + uint keepaliveinterval; +} + + +struct pollfd +{ + SOCKET fd; // Socket handle + short events; // Requested events to monitor + short revents; // Returned events indicating status +} +alias WSAPOLLFD = pollfd; +alias PWSAPOLLFD = pollfd*; +alias LPWSAPOLLFD = pollfd*; + +enum: short { + POLLRDNORM = 0x0100, + POLLRDBAND = 0x0200, + POLLIN = (POLLRDNORM | POLLRDBAND), + POLLPRI = 0x0400, + + POLLWRNORM = 0x0010, + POLLOUT = (POLLWRNORM), + POLLWRBAND = 0x0020, + + POLLERR = 0x0001, + POLLHUP = 0x0002, + POLLNVAL = 0x0004 +} + +int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout) @nogc; diff --git a/src/urt/internal/sys/windows/winuser.d b/src/urt/internal/sys/windows/winuser.d new file mode 100644 index 0000000..0f524d5 --- /dev/null +++ b/src/urt/internal/sys/windows/winuser.d @@ -0,0 +1,144 @@ +/// Minimal winuser bindings — only virtual key codes. +/// Full winuser.d was trimmed; GUI/display/font APIs removed. +module urt.internal.sys.windows.winuser; +version (Windows): + +enum { + VK_LBUTTON = 0x01, + VK_RBUTTON = 0x02, + VK_CANCEL = 0x03, + VK_MBUTTON = 0x04, + VK_XBUTTON1 = 0x05, + VK_XBUTTON2 = 0x06, + VK_BACK = 0x08, + VK_TAB = 0x09, + VK_CLEAR = 0x0C, + VK_RETURN = 0x0D, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12, + VK_PAUSE = 0x13, + VK_CAPITAL = 0x14, + VK_KANA = 0x15, + VK_HANGEUL = 0x15, + VK_HANGUL = 0x15, + VK_JUNJA = 0x17, + VK_FINAL = 0x18, + VK_HANJA = 0x19, + VK_KANJI = 0x19, + VK_ESCAPE = 0x1B, + VK_CONVERT = 0x1C, + VK_NONCONVERT = 0x1D, + VK_ACCEPT = 0x1E, + VK_MODECHANGE = 0x1F, + VK_SPACE = 0x20, + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_END = 0x23, + VK_HOME = 0x24, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_SELECT = 0x29, + VK_PRINT = 0x2A, + VK_EXECUTE = 0x2B, + VK_SNAPSHOT = 0x2C, + VK_INSERT = 0x2D, + VK_DELETE = 0x2E, + VK_HELP = 0x2F, + VK_LWIN = 0x5B, + VK_RWIN = 0x5C, + VK_APPS = 0x5D, + VK_SLEEP = 0x5F, + VK_NUMPAD0 = 0x60, + VK_NUMPAD1 = 0x61, + VK_NUMPAD2 = 0x62, + VK_NUMPAD3 = 0x63, + VK_NUMPAD4 = 0x64, + VK_NUMPAD5 = 0x65, + VK_NUMPAD6 = 0x66, + VK_NUMPAD7 = 0x67, + VK_NUMPAD8 = 0x68, + VK_NUMPAD9 = 0x69, + VK_MULTIPLY = 0x6A, + VK_ADD = 0x6B, + VK_SEPARATOR = 0x6C, + VK_SUBTRACT = 0x6D, + VK_DECIMAL = 0x6E, + VK_DIVIDE = 0x6F, + VK_F1 = 0x70, + VK_F2 = 0x71, + VK_F3 = 0x72, + VK_F4 = 0x73, + VK_F5 = 0x74, + VK_F6 = 0x75, + VK_F7 = 0x76, + VK_F8 = 0x77, + VK_F9 = 0x78, + VK_F10 = 0x79, + VK_F11 = 0x7A, + VK_F12 = 0x7B, + VK_F13 = 0x7C, + VK_F14 = 0x7D, + VK_F15 = 0x7E, + VK_F16 = 0x7F, + VK_F17 = 0x80, + VK_F18 = 0x81, + VK_F19 = 0x82, + VK_F20 = 0x83, + VK_F21 = 0x84, + VK_F22 = 0x85, + VK_F23 = 0x86, + VK_F24 = 0x87, + VK_NUMLOCK = 0x90, + VK_SCROLL = 0x91, + VK_LSHIFT = 0xA0, + VK_RSHIFT = 0xA1, + VK_LCONTROL = 0xA2, + VK_RCONTROL = 0xA3, + VK_LMENU = 0xA4, + VK_RMENU = 0xA5, + VK_BROWSER_BACK = 0xA6, + VK_BROWSER_FORWARD = 0xA7, + VK_BROWSER_REFRESH = 0xA8, + VK_BROWSER_STOP = 0xA9, + VK_BROWSER_SEARCH = 0xAA, + VK_BROWSER_FAVORITES = 0xAB, + VK_BROWSER_HOME = 0xAC, + VK_VOLUME_MUTE = 0xAD, + VK_VOLUME_DOWN = 0xAE, + VK_VOLUME_UP = 0xAF, + VK_MEDIA_NEXT_TRACK = 0xB0, + VK_MEDIA_PREV_TRACK = 0xB1, + VK_MEDIA_STOP = 0xB2, + VK_MEDIA_PLAY_PAUSE = 0xB3, + VK_LAUNCH_MAIL = 0xB4, + VK_LAUNCH_MEDIA_SELECT = 0xB5, + VK_LAUNCH_APP1 = 0xB6, + VK_LAUNCH_APP2 = 0xB7, + VK_OEM_1 = 0xBA, + VK_OEM_PLUS = 0xBB, + VK_OEM_COMMA = 0xBC, + VK_OEM_MINUS = 0xBD, + VK_OEM_PERIOD = 0xBE, + VK_OEM_2 = 0xBF, + VK_OEM_3 = 0xC0, + VK_OEM_4 = 0xDB, + VK_OEM_5 = 0xDC, + VK_OEM_6 = 0xDD, + VK_OEM_7 = 0xDE, + VK_OEM_8 = 0xDF, + VK_OEM_102 = 0xE2, + VK_PROCESSKEY = 0xE5, + VK_PACKET = 0xE7, + VK_ATTN = 0xF6, + VK_CRSEL = 0xF7, + VK_EXSEL = 0xF8, + VK_EREOF = 0xF9, + VK_PLAY = 0xFA, + VK_ZOOM = 0xFB, + VK_NONAME = 0xFC, + VK_PA1 = 0xFD, + VK_OEM_CLEAR = 0xFE, +} diff --git a/src/urt/internal/traits.d b/src/urt/internal/traits.d new file mode 100644 index 0000000..cf52442 --- /dev/null +++ b/src/urt/internal/traits.d @@ -0,0 +1,1227 @@ +// TODO: THIS NEEDS TO BE DISSOLVED... +module urt.internal.traits; + +import urt.traits; + +template Fields(T) +{ + static if (is(T == struct) || is(T == union)) + alias Fields = typeof(T.tupleof[0 .. $ - __traits(isNested, T)]); + else static if (is(T == class) || is(T == interface)) + alias Fields = typeof(T.tupleof); + else + alias Fields = AliasSeq!T; +} + +T trustedCast(T, U)(auto ref U u) @trusted pure nothrow +{ + return cast(T)u; +} + +template BaseElemOf(T) +{ + static if (is(OriginalType!T == E[N], E, size_t N)) + alias BaseElemOf = BaseElemOf!E; + else + alias BaseElemOf = T; +} + +unittest +{ + static assert(is(BaseElemOf!(int) == int)); + static assert(is(BaseElemOf!(int[1]) == int)); + static assert(is(BaseElemOf!(int[1][2]) == int)); + static assert(is(BaseElemOf!(int[1][]) == int[1][])); + static assert(is(BaseElemOf!(int[][1]) == int[])); + enum E : int[2]{ test = [0, 1] } + static assert(is(BaseElemOf!(E) == int)); +} + +// [For internal use] +template ModifyTypePreservingTQ(alias Modifier, T) +{ + static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; + else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; + else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; + else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; + else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; + else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; + else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; + else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; + else alias ModifyTypePreservingTQ = Modifier!T; +} +@safe unittest +{ + alias Intify(T) = int; + static assert(is(ModifyTypePreservingTQ!(Intify, real) == int)); + static assert(is(ModifyTypePreservingTQ!(Intify, const real) == const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, inout real) == inout int)); + static assert(is(ModifyTypePreservingTQ!(Intify, inout const real) == inout const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared real) == shared int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared const real) == shared const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared inout real) == shared inout int)); + static assert(is(ModifyTypePreservingTQ!(Intify, shared inout const real) == shared inout const int)); + static assert(is(ModifyTypePreservingTQ!(Intify, immutable real) == immutable int)); +} + +// Substitute all `inout` qualifiers that appears in T to `const` +template substInout(T) +{ + static if (is(T == immutable)) + { + alias substInout = T; + } + else static if (is(T : shared const U, U) || is(T : const U, U)) + { + // U is top-unqualified + mixin("alias substInout = " + ~ (is(T == shared) ? "shared " : "") + ~ (is(T == const) || is(T == inout) ? "const " : "") // substitute inout to const + ~ "substInoutForm!U;"); + } + else + static assert(0); +} + +private template substInoutForm(T) +{ + static if (is(T == struct) || is(T == class) || is(T == union) || is(T == interface)) + { + alias substInoutForm = T; // prevent matching to the form of alias-this-ed type + } + else static if (is(T == V[K], K, V)) alias substInoutForm = substInout!V[substInout!K]; + else static if (is(T == U[n], U, size_t n)) alias substInoutForm = substInout!U[n]; + else static if (is(T == U[], U)) alias substInoutForm = substInout!U[]; + else static if (is(T == U*, U)) alias substInoutForm = substInout!U*; + else alias substInoutForm = T; +} + +unittest +{ + // https://github.com/dlang/dmd/issues/21452 + struct S { int x; } + struct T { int x; alias x this; } + + enum EnumInt { a = 123 } + enum EnumUInt : uint { a = 123 } + enum EnumFloat : float { a = 123 } + enum EnumString : string { a = "123" } + enum EnumStringW : wstring { a = "123" } + enum EnumStruct : S { a = S(7) } + enum EnumAliasThis : T { a = T(7) } + enum EnumDArray : int[] { a = [1] } + enum EnumAArray : int[int] { a = [0 : 1] } + + static assert(substInout!(EnumInt).stringof == "EnumInt"); + static assert(substInout!(inout(EnumUInt)).stringof == "const(EnumUInt)"); + static assert(substInout!(EnumFloat).stringof == "EnumFloat"); + static assert(substInout!(EnumString).stringof == "EnumString"); + static assert(substInout!(inout(EnumStringW)).stringof == "const(EnumStringW)"); + static assert(substInout!(EnumStruct).stringof == "EnumStruct"); + static assert(substInout!(EnumAliasThis).stringof == "EnumAliasThis"); + static assert(substInout!(EnumDArray).stringof == "EnumDArray"); + static assert(substInout!(inout(EnumAArray)[int]).stringof == "const(EnumAArray)[int]"); +} + +/// used to declare an extern(D) function that is defined in a different module +template externDFunc(string fqn, T:FT*, FT) if (is(FT == function)) +{ + static if (is(FT RT == return) && is(FT Args == function)) + { + import core.demangle : mangleFunc; + enum decl = { + string s = "extern(D) RT externDFunc(Args)"; + foreach (attr; __traits(getFunctionAttributes, FT)) + s ~= " " ~ attr; + return s ~ ";"; + }(); + pragma(mangle, mangleFunc!T(fqn)) mixin(decl); + } + else + static assert(0); +} + +template staticIota(int beg, int end) +{ + static if (beg + 1 >= end) + { + static if (beg >= end) + { + alias staticIota = AliasSeq!(); + } + else + { + alias staticIota = AliasSeq!(+beg); + } + } + else + { + enum mid = beg + (end - beg) / 2; + alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end)); + } +} + +private struct __InoutWorkaroundStruct {} +@property T rvalueOf(T)(T val) { return val; } +@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); +@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); + +// taken from std.traits.isAssignable +template isAssignable(Lhs, Rhs = Lhs) +{ + enum isAssignable = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs) && __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs); +} + +// taken from std.traits.isInnerClass +template isInnerClass(T) if (is(T == class)) +{ + static if (is(typeof(T.outer))) + { + template hasOuterMember(T...) + { + static if (T.length == 0) + enum hasOuterMember = false; + else + enum hasOuterMember = T[0] == "outer" || hasOuterMember!(T[1 .. $]); + } + enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && !hasOuterMember!(__traits(allMembers, T)); + } + else + enum isInnerClass = false; +} + +template dtorIsNothrow(T) +{ + enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow); +} + +// taken from std.meta.allSatisfy +template allSatisfy(alias F, T...) +{ + static foreach (Ti; T) + { + static if (!is(typeof(allSatisfy) == bool) && // not yet defined + !F!(Ti)) + { + enum allSatisfy = false; + } + } + static if (!is(typeof(allSatisfy) == bool)) // if not yet defined + { + enum allSatisfy = true; + } +} + +// taken from std.meta.anySatisfy +template anySatisfy(alias F, Ts...) +{ + static foreach (T; Ts) + { + static if (!is(typeof(anySatisfy) == bool) && // not yet defined + F!T) + { + enum anySatisfy = true; + } + } + static if (!is(typeof(anySatisfy) == bool)) // if not yet defined + { + enum anySatisfy = false; + } +} + +// simplified from std.traits.maxAlignment +template maxAlignment(Ts...) +if (Ts.length > 0) +{ + enum maxAlignment = + { + size_t result = 0; + static foreach (T; Ts) + if (T.alignof > result) result = T.alignof; + return result; + }(); +} + +template classInstanceAlignment(T) +if (is(T == class)) +{ + enum classInstanceAlignment = __traits(classInstanceAlignment, T); +} + +/// See $(REF hasElaborateMove, std,traits) +template hasElaborateMove(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateMove = S.sizeof && hasElaborateMove!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!S))) && + !is(typeof(S.init.opPostMove(rvalueOf!S)))) || + anySatisfy!(.hasElaborateMove, Fields!S); + } + else + { + enum bool hasElaborateMove = false; + } +} + +// std.traits.hasElaborateDestructor +template hasElaborateDestructor(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateDestructor = S.sizeof && hasElaborateDestructor!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then + // this should be the implementation, but until that's fixed, we need the + // uncommented code. + // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor"); + + enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]); + } + else + { + enum bool hasElaborateDestructor = false; + } +} + +private bool hasDtor(string[] members) +{ + foreach (name; members) + { + if (name == "__xdtor") + return true; + } + + return false; +} + +@safe unittest +{ + static struct NoDestructor {} + static assert(!hasElaborateDestructor!NoDestructor); + static assert(!hasElaborateDestructor!(NoDestructor[42])); + static assert(!hasElaborateDestructor!(NoDestructor[0])); + static assert(!hasElaborateDestructor!(NoDestructor[])); + + static struct HasDestructor { ~this() {} } + static assert( hasElaborateDestructor!HasDestructor); + static assert( hasElaborateDestructor!(HasDestructor[42])); + static assert(!hasElaborateDestructor!(HasDestructor[0])); + static assert(!hasElaborateDestructor!(HasDestructor[])); + + static struct HasDestructor2 { HasDestructor s; } + static assert( hasElaborateDestructor!HasDestructor2); + static assert( hasElaborateDestructor!(HasDestructor2[42])); + static assert(!hasElaborateDestructor!(HasDestructor2[0])); + static assert(!hasElaborateDestructor!(HasDestructor2[])); + + static class HasFinalizer { ~this() {} } + static assert(!hasElaborateDestructor!HasFinalizer); + + static struct HasUnion { union { HasDestructor s; } } + static assert(!hasElaborateDestructor!HasUnion); + static assert(!hasElaborateDestructor!(HasUnion[42])); + static assert(!hasElaborateDestructor!(HasUnion[0])); + static assert(!hasElaborateDestructor!(HasUnion[])); + + static assert(!hasElaborateDestructor!int); + static assert(!hasElaborateDestructor!(int[0])); + static assert(!hasElaborateDestructor!(int[42])); + static assert(!hasElaborateDestructor!(int[])); +} + +// https://issues.dlang.org/show_bug.cgi?id=24865 +@safe unittest +{ + static struct S2 { ~this() {} } + static struct S3 { S2 field; } + static struct S6 { S3[0] field; } + + static assert( hasElaborateDestructor!S2); + static assert( hasElaborateDestructor!S3); + static assert(!hasElaborateDestructor!S6); +} + +// std.traits.hasElaborateCopyDestructor +template hasElaborateCopyConstructor(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateCopyConstructor = S.sizeof && hasElaborateCopyConstructor!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, S) || __traits(hasPostblit, S); + } + else + { + enum bool hasElaborateCopyConstructor = false; + } +} + +@safe unittest +{ + static struct S + { + int x; + this(return scope ref typeof(this) rhs) { } + this(int x, int y) {} + } + + static assert( hasElaborateCopyConstructor!S); + static assert(!hasElaborateCopyConstructor!(S[0][1])); + + static struct S2 + { + int x; + this(int x, int y) {} + } + + static assert(!hasElaborateCopyConstructor!S2); + + static struct S3 + { + int x; + this(return scope ref typeof(this) rhs, int x = 42) { } + this(int x, int y) {} + } + + static assert( hasElaborateCopyConstructor!S3); + + static struct S4 { union { S s; } } + + static assert(!hasElaborateCopyConstructor!S4); +} + +template hasElaborateAssign(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateAssign = S.sizeof && hasElaborateAssign!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || + is(typeof(S.init.opAssign(lvalueOf!S))); + } + else + { + enum bool hasElaborateAssign = false; + } +} + +unittest +{ + { + static struct S {} + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { int i; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { void opAssign(int) {} } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { this(this) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + // https://issues.dlang.org/show_bug.cgi?id=24834 + /+ + { + static struct S { this(ref S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + +/ + { + static struct S { ~this() {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct S { @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member {} + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { @disable void opAssign(Member); } + static struct S { Member member; void opAssign(S) {} } + static assert( hasElaborateAssign!S); + static assert( hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { Member member; @disable void opAssign(S); } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + { + static struct Member { void opAssign(Member) {} } + static struct S { union { Member member; } } + static assert(!hasElaborateAssign!S); + static assert(!hasElaborateAssign!(S[10])); + static assert(!hasElaborateAssign!(S[0])); + static assert(!hasElaborateAssign!(S[])); + } + + static assert(!hasElaborateAssign!int); + static assert(!hasElaborateAssign!(string[])); + static assert(!hasElaborateAssign!Object); +} + +template hasIndirections(T) +{ + static if (is(T == enum)) + enum hasIndirections = hasIndirections!(OriginalType!T); + else static if (is(T == struct) || is(T == union)) + enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof)); + else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface)) + enum hasIndirections = true; + else static if (is(T == E[N], E, size_t N)) + enum hasIndirections = T.sizeof && (is(immutable E == immutable void) || hasIndirections!(BaseElemOf!E)); + else static if (isFunctionPointer!T) + enum hasIndirections = false; + else + enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T; +} + +@safe unittest +{ + static assert(!hasIndirections!int); + static assert(!hasIndirections!(const int)); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(const int*)); + + static assert( hasIndirections!(int[])); + static assert(!hasIndirections!(int[42])); + static assert(!hasIndirections!(int[0])); + + static assert( hasIndirections!(int*)); + static assert( hasIndirections!(int*[])); + static assert( hasIndirections!(int*[42])); + static assert(!hasIndirections!(int*[0])); + + static assert( hasIndirections!string); + static assert( hasIndirections!(string[])); + static assert( hasIndirections!(string[42])); + static assert(!hasIndirections!(string[0])); + + static assert( hasIndirections!(void[])); + static assert( hasIndirections!(void[17])); + static assert(!hasIndirections!(void[0])); + + static assert( hasIndirections!(string[int])); + static assert( hasIndirections!(string[int]*)); + static assert( hasIndirections!(string[int][])); + static assert( hasIndirections!(string[int][12])); + static assert(!hasIndirections!(string[int][0])); + + static assert(!hasIndirections!(int function(string))); + static assert( hasIndirections!(int delegate(string))); + static assert(!hasIndirections!(const(int function(string)))); + static assert( hasIndirections!(const(int delegate(string)))); + static assert(!hasIndirections!(immutable(int function(string)))); + static assert( hasIndirections!(immutable(int delegate(string)))); + + static class C {} + static assert( hasIndirections!C); + + static interface I {} + static assert( hasIndirections!I); + + { + enum E : int { a } + static assert(!hasIndirections!E); + } + { + enum E : int* { a } + static assert( hasIndirections!E); + } + { + enum E : string { a = "" } + static assert( hasIndirections!E); + } + { + enum E : int[] { a = null } + static assert( hasIndirections!E); + } + { + enum E : int[3] { a = [1, 2, 3] } + static assert(!hasIndirections!E); + } + { + enum E : int*[3] { a = [null, null, null] } + static assert( hasIndirections!E); + } + { + enum E : int*[0] { a = int*[0].init } + static assert(!hasIndirections!E); + } + { + enum E : C { a = null } + static assert( hasIndirections!E); + } + { + enum E : I { a = null } + static assert( hasIndirections!E); + } + + { + static struct S {} + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { int i; } + static assert(!hasIndirections!S); + + enum E : S { a = S.init } + static assert(!hasIndirections!S); + } + { + static struct S { C c; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + static struct S { int[] arr; } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + { + int local; + struct S { void foo() { ++local; } } + static assert( hasIndirections!S); + + enum E : S { a = S.init } + static assert( hasIndirections!S); + } + + { + static union U {} + static assert(!hasIndirections!U); + } + { + static union U { int i; } + static assert(!hasIndirections!U); + } + { + static union U { C c; } + static assert( hasIndirections!U); + } + { + static union U { int[] arr; } + static assert( hasIndirections!U); + } +} + +// https://issues.dlang.org/show_bug.cgi?id=12000 +@safe unittest +{ + static struct S(T) + { + static assert(hasIndirections!T); + } + + static class A(T) + { + S!A a; + } + + A!int dummy; +} + +// https://github.com/dlang/dmd/issues/20812 +@safe unittest +{ + static assert(!hasIndirections!void); + static assert(!hasIndirections!(const void)); + static assert(!hasIndirections!(inout void)); + static assert(!hasIndirections!(immutable void)); + static assert(!hasIndirections!(shared void)); + + static assert( hasIndirections!(void*)); + static assert( hasIndirections!(const void*)); + static assert( hasIndirections!(inout void*)); + static assert( hasIndirections!(immutable void*)); + static assert( hasIndirections!(shared void*)); + + static assert( hasIndirections!(void[])); + static assert( hasIndirections!(const void[])); + static assert( hasIndirections!(inout void[])); + static assert( hasIndirections!(immutable void[])); + static assert( hasIndirections!(shared void[])); + + static assert( hasIndirections!(void[42])); + static assert( hasIndirections!(const void[42])); + static assert( hasIndirections!(inout void[42])); + static assert( hasIndirections!(immutable void[42])); + static assert( hasIndirections!(shared void[42])); + + static assert(!hasIndirections!(void[0])); + static assert(!hasIndirections!(const void[0])); + static assert(!hasIndirections!(inout void[0])); + static assert(!hasIndirections!(immutable void[0])); + static assert(!hasIndirections!(shared void[0])); +} + +template hasUnsharedIndirections(T) +{ + static if (is(T == immutable)) + enum hasUnsharedIndirections = false; + else static if (is(T == struct) || is(T == union)) + enum hasUnsharedIndirections = anySatisfy!(.hasUnsharedIndirections, Fields!T); + else static if (is(T : E[N], E, size_t N)) + enum hasUnsharedIndirections = is(E == void) ? false : hasUnsharedIndirections!E; + else static if (isFunctionPointer!T) + enum hasUnsharedIndirections = false; + else static if (isPointer!T) + enum hasUnsharedIndirections = !is(T : shared(U)*, U) && !is(T : immutable(U)*, U); + else static if (isDynamicArray!T) + enum hasUnsharedIndirections = !is(T : shared(V)[], V) && !is(T : immutable(V)[], V); + else static if (is(T == class) || is(T == interface)) + enum hasUnsharedIndirections = !is(T : shared(W), W); + else + enum hasUnsharedIndirections = isDelegate!T || __traits(isAssociativeArray, T); // TODO: how to handle these? +} + +unittest +{ + static struct Foo { shared(int)* val; } + + static assert(!hasUnsharedIndirections!(immutable(char)*)); + static assert(!hasUnsharedIndirections!(string)); + + static assert(!hasUnsharedIndirections!(Foo)); + static assert( hasUnsharedIndirections!(Foo*)); + static assert(!hasUnsharedIndirections!(shared(Foo)*)); + static assert(!hasUnsharedIndirections!(immutable(Foo)*)); + + int local; + struct HasContextPointer { int opCall() { return ++local; } } + static assert(hasIndirections!HasContextPointer); +} + +enum bool isAggregateType(T) = is(T == struct) || is(T == union) || + is(T == class) || is(T == interface); + +enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; + +enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; + +template OriginalType(T) +{ + template Impl(T) + { + static if (is(T U == enum)) alias Impl = OriginalType!U; + else alias Impl = T; + } + + alias OriginalType = ModifyTypePreservingTQ!(Impl, T); +} + +template DynamicArrayTypeOf(T) +{ + static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) + alias X = DynamicArrayTypeOf!AT; + else + alias X = OriginalType!T; + + static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; }))) + alias DynamicArrayTypeOf = X; + else + static assert(0, T.stringof ~ " is not a dynamic array"); +} + +private template AliasThisTypeOf(T) + if (isAggregateType!T) +{ + alias members = __traits(getAliasThis, T); + + static if (members.length == 1) + alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); + else + static assert(0, T.stringof~" does not have alias this type"); +} + +template isFunctionPointer(T...) + if (T.length == 1) +{ + static if (is(T[0] U) || is(typeof(T[0]) U)) + { + static if (is(U F : F*) && is(F == function)) + enum bool isFunctionPointer = true; + else + enum bool isFunctionPointer = false; + } + else + enum bool isFunctionPointer = false; +} + +template isDelegate(T...) + if (T.length == 1) +{ + static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate)) + { + // T is a (nested) function symbol. + enum bool isDelegate = true; + } + else static if (is(T[0] W) || is(typeof(T[0]) W)) + { + // T is an expression or a type. Take the type of it and examine. + enum bool isDelegate = is(W == delegate); + } + else + enum bool isDelegate = false; +} + +// std.meta.Filter +template Filter(alias pred, TList...) +{ + static if (TList.length == 0) + { + alias Filter = AliasSeq!(); + } + else static if (TList.length == 1) + { + static if (pred!(TList[0])) + alias Filter = AliasSeq!(TList[0]); + else + alias Filter = AliasSeq!(); + } + /* The next case speeds up compilation by reducing + * the number of Filter instantiations + */ + else static if (TList.length == 2) + { + static if (pred!(TList[0])) + { + static if (pred!(TList[1])) + alias Filter = AliasSeq!(TList[0], TList[1]); + else + alias Filter = AliasSeq!(TList[0]); + } + else + { + static if (pred!(TList[1])) + alias Filter = AliasSeq!(TList[1]); + else + alias Filter = AliasSeq!(); + } + } + else + { + alias Filter = + AliasSeq!( + Filter!(pred, TList[ 0 .. $/2]), + Filter!(pred, TList[$/2 .. $ ])); + } +} + +// std.meta.staticMap +template staticMap(alias F, T...) +{ + static if (T.length == 0) + { + alias staticMap = AliasSeq!(); + } + else static if (T.length == 1) + { + alias staticMap = AliasSeq!(F!(T[0])); + } + /* Cases 2 to 8 improve compile performance by reducing + * the number of recursive instantiations of staticMap + */ + else static if (T.length == 2) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1])); + } + else static if (T.length == 3) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2])); + } + else static if (T.length == 4) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3])); + } + else static if (T.length == 5) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4])); + } + else static if (T.length == 6) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5])); + } + else static if (T.length == 7) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6])); + } + else static if (T.length == 8) + { + alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]), F!(T[7])); + } + else + { + alias staticMap = + AliasSeq!( + staticMap!(F, T[ 0 .. $/2]), + staticMap!(F, T[$/2 .. $ ])); + } +} + +// std.exception.assertCTFEable +version (CoreUnittest) package(core) +void assertCTFEable(alias dg)() +{ + static assert({ cast(void) dg(); return true; }()); + cast(void) dg(); +} + +// std.traits.FunctionTypeOf +/* +Get the function type from a callable object `func`. + +Using builtin `typeof` on a property function yields the types of the +property value, not of the property function itself. Still, +`FunctionTypeOf` is able to obtain function types of properties. + +Note: +Do not confuse function types with function pointer types; function types are +usually used for compile-time reflection purposes. + */ +template FunctionTypeOf(func...) +if (func.length == 1 /*&& isCallable!func*/) +{ + static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate)) + { + alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol + } + else static if (is(typeof(& func[0].opCall) Fobj == delegate)) + { + alias FunctionTypeOf = Fobj; // HIT: callable object + } + else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) + { + alias FunctionTypeOf = Ftyp; // HIT: callable type + } + else static if (is(func[0] T) || is(typeof(func[0]) T)) + { + static if (is(T == function)) + alias FunctionTypeOf = T; // HIT: function + else static if (is(T Fptr : Fptr*) && is(Fptr == function)) + alias FunctionTypeOf = Fptr; // HIT: function pointer + else static if (is(T Fdlg == delegate)) + alias FunctionTypeOf = Fdlg; // HIT: delegate + else + static assert(0); + } + else + static assert(0); +} + +@safe unittest +{ + class C + { + int value() @property { return 0; } + } + static assert(is( typeof(C.value) == int )); + static assert(is( FunctionTypeOf!(C.value) == function )); +} + +// Disabled: uses `new Callable` (GC allocation) which is unsupported in @nogc uRT build +version (none) +@system unittest +{ + int test(int a); + int propGet() @property; + int propSet(int a) @property; + int function(int) test_fp; + int delegate(int) test_dg; + static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); + static assert(is( typeof(test) == FunctionTypeOf!test )); + static assert(is( typeof(test) == FunctionTypeOf!test_fp )); + static assert(is( typeof(test) == FunctionTypeOf!test_dg )); + alias int GetterType() @property; + alias int SetterType(int) @property; + static assert(is( FunctionTypeOf!propGet == GetterType )); + static assert(is( FunctionTypeOf!propSet == SetterType )); + + interface Prop { int prop() @property; } + Prop prop; + static assert(is( FunctionTypeOf!(Prop.prop) == GetterType )); + static assert(is( FunctionTypeOf!(prop.prop) == GetterType )); + + class Callable { int opCall(int) { return 0; } } + auto call = new Callable; + static assert(is( FunctionTypeOf!call == typeof(test) )); + + struct StaticCallable { static int opCall(int) { return 0; } } + StaticCallable stcall_val; + StaticCallable* stcall_ptr; + static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); + static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); + + interface Overloads + { + void test(string); + real test(real); + int test(int); + int test() @property; + } + alias ov = __traits(getVirtualMethods, Overloads, "test"); + alias F_ov0 = FunctionTypeOf!(ov[0]); + alias F_ov1 = FunctionTypeOf!(ov[1]); + alias F_ov2 = FunctionTypeOf!(ov[2]); + alias F_ov3 = FunctionTypeOf!(ov[3]); + static assert(is(F_ov0* == void function(string))); + static assert(is(F_ov1* == real function(real))); + static assert(is(F_ov2* == int function(int))); + static assert(is(F_ov3* == int function() @property)); + + alias F_dglit = FunctionTypeOf!((int a){ return a; }); + static assert(is(F_dglit* : int function(int))); +} + +// std.traits.ReturnType +/* +Get the type of the return value from a function, +a pointer to function, a delegate, a struct +with an opCall, a pointer to a struct with an opCall, +or a class with an `opCall`. Please note that $(D_KEYWORD ref) +is not part of a type, but the attribute of the function +(see template $(LREF functionAttributes)). +*/ +template ReturnType(func...) +if (func.length == 1 /*&& isCallable!func*/) +{ + static if (is(FunctionTypeOf!func R == return)) + alias ReturnType = R; + else + static assert(0, "argument has no return type"); +} + +// +@safe unittest +{ + int foo(); + ReturnType!foo x; // x is declared as int +} + +@safe unittest +{ + struct G + { + int opCall (int i) { return 1;} + } + + alias ShouldBeInt = ReturnType!G; + static assert(is(ShouldBeInt == int)); + + G g; + static assert(is(ReturnType!g == int)); + + G* p; + alias pg = ReturnType!p; + static assert(is(pg == int)); + + class C + { + int opCall (int i) { return 1;} + } + + static assert(is(ReturnType!C == int)); + + C c; + static assert(is(ReturnType!c == int)); + + class Test + { + int prop() @property { return 0; } + } + alias R_Test_prop = ReturnType!(Test.prop); + static assert(is(R_Test_prop == int)); + + alias R_dglit = ReturnType!((int a) { return a; }); + static assert(is(R_dglit == int)); +} + +// std.traits.Parameters +/* +Get, as a tuple, the types of the parameters to a function, a pointer +to function, a delegate, a struct with an `opCall`, a pointer to a +struct with an `opCall`, or a class with an `opCall`. +*/ +template Parameters(func...) +if (func.length == 1 /*&& isCallable!func*/) +{ + static if (is(FunctionTypeOf!func P == function)) + alias Parameters = P; + else + static assert(0, "argument has no parameters"); +} + +// +@safe unittest +{ + int foo(int, long); + void bar(Parameters!foo); // declares void bar(int, long); + void abc(Parameters!foo[1]); // declares void abc(long); +} + +@safe unittest +{ + int foo(int i, bool b) { return 0; } + static assert(is(Parameters!foo == AliasSeq!(int, bool))); + static assert(is(Parameters!(typeof(&foo)) == AliasSeq!(int, bool))); + + struct S { real opCall(real r, int i) { return 0.0; } } + S s; + static assert(is(Parameters!S == AliasSeq!(real, int))); + static assert(is(Parameters!(S*) == AliasSeq!(real, int))); + static assert(is(Parameters!s == AliasSeq!(real, int))); + + class Test + { + int prop() @property { return 0; } + } + alias P_Test_prop = Parameters!(Test.prop); + static assert(P_Test_prop.length == 0); + + alias P_dglit = Parameters!((int a){}); + static assert(P_dglit.length == 1); + static assert(is(P_dglit[0] == int)); +} + +// Return `true` if `Type` has `member` that evaluates to `true` in a static if condition +enum isTrue(Type, string member) = __traits(compiles, { static if (__traits(getMember, Type, member)) {} else static assert(0); }); + +unittest +{ + static struct T + { + enum a = true; + enum b = false; + enum c = 1; + enum d = 45; + enum e = "true"; + enum f = ""; + enum g = null; + alias h = bool; + } + + static assert( isTrue!(T, "a")); + static assert(!isTrue!(T, "b")); + static assert( isTrue!(T, "c")); + static assert( isTrue!(T, "d")); + static assert( isTrue!(T, "e")); + static assert( isTrue!(T, "f")); + static assert(!isTrue!(T, "g")); + static assert(!isTrue!(T, "h")); +} + +template hasUDA(alias symbol, alias attribute) +{ + enum isAttr(T) = is(T == attribute); + + enum hasUDA = anySatisfy!(isAttr, __traits(getAttributes, symbol)); +} + +unittest +{ + enum SomeUDA; + + struct Test + { + int woUDA; + @SomeUDA int oneUDA; + @SomeUDA @SomeUDA int twoUDAs; + } + + static assert(hasUDA!(Test.oneUDA, SomeUDA)); + static assert(hasUDA!(Test.twoUDAs, SomeUDA)); + static assert(!hasUDA!(Test.woUDA, SomeUDA)); +} diff --git a/src/urt/io.d b/src/urt/io.d index 73fba66..58b19df 100644 --- a/src/urt/io.d +++ b/src/urt/io.d @@ -1,20 +1,47 @@ module urt.io; -import core.stdc.stdio; - - nothrow @nogc: -int write(const(char)[] str) +enum WriteTarget : ubyte { - return printf("%.*s", cast(int)str.length, str.ptr); + stdout = 0, + stderr = 1, + debugstring = 2, // Windows OutputDebugStringA } -int writeln(const(char)[] str) + +int write_to(WriteTarget target, bool newline = false)(const(char)[] str) { - return printf("%.*s\n", cast(int)str.length, str.ptr); + static if (target == WriteTarget.stdout) + { + import urt.internal.stdc; + return printf("%.*s" ~ (newline ? "\n" : ""), cast(int)str.length, str.ptr); + } + else static if (target == WriteTarget.stderr) + { + import urt.internal.stdc; + return fprintf(stderr, "%.*s" ~ (newline ? "\n" : ""), cast(int)str.length, str.ptr); + } + else static if (target == WriteTarget.debugstring) + { + version (Windows) + { + import core.sys.windows.windows; + OutputDebugStringA(str.ptr); + static if (newline) + OutputDebugStringA("\n"); + return cast(int)str.length + newline; + } + else + { + // is stderr the best analogy on other platforms? + return write_to!(WriteTarget.stderr, newline)(str); + } + } + else + static assert(0, "Invalid WriteTarget"); } -int write(Args...)(ref Args args) +int write_to(WriteTarget target, bool newline = false, Args...)(ref Args args) if (Args.length != 1 || !is(Args[0] : const(char)[])) { import urt.string.format; @@ -22,19 +49,10 @@ int write(Args...)(ref Args args) size_t len = concat(null, args).length; const(char)[] t = concat(cast(char[])talloc(len), args); - return write(t); -} - -int writeln(Args...)(ref Args args) - if (Args.length != 1 || !is(Args[0] : const(char)[])) -{ - import urt.string.format; - import urt.mem.temp; - - return tconcat(args).writeln; + return write_to!(target, newline)(t); } -int writef(Args...)(const(char)[] fmt, ref Args args) +int writef_to(WriteTarget target, bool newline = false, Args...)(const(char)[] fmt, ref Args args) if (Args.length > 0) { import urt.string.format; @@ -42,19 +60,27 @@ int writef(Args...)(const(char)[] fmt, ref Args args) size_t len = format(null, fmt, args).length; const(char)[] t = format(cast(char[])talloc(len), fmt, args); - return write(t); + return write_to!(target, newline)(t); } -int writelnf(Args...)(const(char)[] fmt, ref Args args) - if (Args.length > 0) -{ - import urt.string.format; - import urt.mem.temp; +alias write = write_to!(WriteTarget.stdout, false); +alias writeln = write_to!(WriteTarget.stdout, true); +alias write_err = write_to!(WriteTarget.stderr, false); +alias writeln_err = write_to!(WriteTarget.stderr, true); +alias write_debug = write_to!(WriteTarget.debugstring, false); +alias writeln_debug = write_to!(WriteTarget.debugstring, true); - size_t len = format(null, fmt, args).length; - const(char)[] t = format(cast(char[])talloc(len), fmt, args); - return writeln(t); -} +int write(Args...)(ref Args args) + if (Args.length != 1 || !is(Args[0] : const(char)[])) + => write_to!(WriteTarget.stdout, false)(args); +int writeln(Args...)(ref Args args) + if (Args.length != 1 || !is(Args[0] : const(char)[])) + => write_to!(WriteTarget.stdout, true)(args); + +int writef(Args...)(ref Args args) + => writef_to!(WriteTarget.stdout, false)(args); +int writelnf(Args...)(ref Args args) + => writef_to!(WriteTarget.stdout, true)(args); unittest { diff --git a/src/urt/lifetime.d b/src/urt/lifetime.d index acb04d1..d08eb32 100644 --- a/src/urt/lifetime.d +++ b/src/urt/lifetime.d @@ -1,10 +1,9 @@ module urt.lifetime; +import urt.internal.lifetime : emplaceRef; // TODO: DESTROY THIS! T* emplace(T)(T* chunk) @safe pure { - import core.internal.lifetime : emplaceRef; - emplaceRef!T(*chunk); return chunk; } @@ -12,8 +11,6 @@ T* emplace(T)(T* chunk) @safe pure T* emplace(T, Args...)(T* chunk, auto ref Args args) if (is(T == struct) || Args.length == 1) { - import core.internal.lifetime : emplaceRef; - emplaceRef!T(*chunk, forward!args); return chunk; } @@ -21,7 +18,7 @@ T* emplace(T, Args...)(T* chunk, auto ref Args args) T emplace(T, Args...)(T chunk, auto ref Args args) if (is(T == class)) { - import core.internal.traits : isInnerClass; + import urt.internal.traits : isInnerClass; static assert(!__traits(isAbstractClass, T), T.stringof ~ " is abstract and it can't be emplaced"); @@ -73,8 +70,7 @@ T emplace(T, Args...)(void[] chunk, auto ref Args args) T* emplace(T, Args...)(void[] chunk, auto ref Args args) if (!is(T == class)) { - import core.internal.traits : Unqual; - import core.internal.lifetime : emplaceRef; + import urt.traits : Unqual; assert(chunk.length >= T.sizeof, "chunk size too small."); assert((cast(size_t) chunk.ptr) % T.alignof == 0, "emplace: Chunk is not aligned."); @@ -83,12 +79,11 @@ T* emplace(T, Args...)(void[] chunk, auto ref Args args) return cast(T*) chunk.ptr; } - /+ void copyEmplace(S, T)(ref S source, ref T target) @system if (is(immutable S == immutable T)) { - import core.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual; + import urt.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual; // cannot have the following as simple template constraint due to nested-struct special case... static if (!__traits(compiles, (ref S src) { T tgt = src; })) @@ -100,7 +95,7 @@ void copyEmplace(S, T)(ref S source, ref T target) @system void blit() { - import core.stdc.string : memcpy; + import urt.mem : memcpy; memcpy(cast(Unqual!(T)*) &target, cast(Unqual!(T)*) &source, T.sizeof); } @@ -203,7 +198,7 @@ T move(T)(return scope ref T source) nothrow @nogc private void moveImpl(T)(scope ref T target, return scope ref T source) nothrow @nogc { - import core.internal.traits : hasElaborateDestructor; + import urt.internal.traits : hasElaborateDestructor; static if (is(T == struct)) { @@ -225,7 +220,7 @@ private T moveImpl(T)(return scope ref T source) nothrow @nogc return trustedMoveImpl(source); } -private T trustedMoveImpl(T)(return scope ref T source) @trusted nothrow @nogc +private T trustedMoveImpl(T)(return scope ref T source) nothrow @nogc @trusted { T result = void; moveEmplaceImpl(result, source); @@ -242,7 +237,7 @@ private enum bool hasContextPointers(T) = { } else static if (is(T == struct)) { - import core.internal.traits : anySatisfy; + import urt.internal.traits : anySatisfy; return __traits(isNested, T) || anySatisfy!(hasContextPointers, typeof(T.tupleof)); } else return false; @@ -259,8 +254,8 @@ private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) @ // "Cannot move object with internal pointer unless `opPostMove` is defined."); // } - import core.internal.traits : hasElaborateAssign, isAssignable, hasElaborateMove, - hasElaborateDestructor, hasElaborateCopyConstructor; + import urt.internal.traits : hasElaborateAssign, isAssignable, hasElaborateMove, + hasElaborateDestructor, hasElaborateCopyConstructor; static if (is(T == struct)) { @@ -269,7 +264,7 @@ private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) @ static if (hasElaborateAssign!T || !isAssignable!T) { - import core.stdc.string : memcpy; + import urt.mem : memcpy; () @trusted { memcpy(&target, &source, T.sizeof); }(); } else @@ -327,20 +322,11 @@ void moveEmplace(T)(ref T source, ref T target) @system @nogc -//debug = PRINTF; - -debug(PRINTF) -{ - import core.stdc.stdio; -} - /// Implementation of `_d_delstruct` and `_d_delstructTrace` template _d_delstructImpl(T) { private void _d_delstructImpure(ref T p) { - debug(PRINTF) printf("_d_delstruct(%p)\n", p); - destroy(*p); p = null; } @@ -360,7 +346,7 @@ template _d_delstructImpl(T) * `@trusted` until the implementation can be brought up to modern D * expectations. */ - void _d_delstruct(ref T p) @trusted @nogc pure nothrow + void _d_delstruct(ref T p) nothrow @nogc pure @trusted { if (p) { @@ -392,7 +378,7 @@ template _d_delstructImpl(T) // wipes source after moving pragma(inline, true) -private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted nothrow @nogc +private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) nothrow @nogc @trusted if (!Init.length || ((Init.length == 1) && (is(immutable T == immutable Init[0])))) { @@ -406,7 +392,7 @@ if (!Init.length || } else static if (is(T == struct) && hasContextPointers!T) { - import core.internal.traits : anySatisfy; + import urt.internal.traits : anySatisfy; static if (anySatisfy!(hasContextPointers, typeof(T.tupleof))) { static foreach (i; 0 .. T.tupleof.length - __traits(isNested, T)) @@ -430,7 +416,7 @@ if (!Init.length || } else { - import core.internal.traits : hasElaborateAssign, isAssignable; + import urt.internal.traits : hasElaborateAssign, isAssignable; static if (Init.length) { static if (hasElaborateAssign!T || !isAssignable!T) @@ -449,7 +435,7 @@ if (!Init.length || T _d_newclassT(T)() @trusted if (is(T == class)) { - import core.internal.traits : hasIndirections; + import urt.internal.traits : hasIndirections; import core.exception : onOutOfMemoryError; import core.memory : pureMalloc; import core.memory : GC; @@ -478,27 +464,11 @@ T _d_newclassT(T)() @trusted attr |= BlkAttr.NO_SCAN; p = GC.malloc(init.length, attr, typeid(T)); - debug(PRINTF) printf(" p = %p\n", p); - } - - debug(PRINTF) - { - printf("p = %p\n", p); - printf("init.ptr = %p, len = %llu\n", init.ptr, cast(ulong)init.length); - printf("vptr = %p\n", *cast(void**) init); - printf("vtbl[0] = %p\n", (*cast(void***) init)[0]); - printf("vtbl[1] = %p\n", (*cast(void***) init)[1]); - printf("init[0] = %x\n", (cast(uint*) init)[0]); - printf("init[1] = %x\n", (cast(uint*) init)[1]); - printf("init[2] = %x\n", (cast(uint*) init)[2]); - printf("init[3] = %x\n", (cast(uint*) init)[3]); - printf("init[4] = %x\n", (cast(uint*) init)[4]); } // initialize it p[0 .. init.length] = init[]; - debug(PRINTF) printf("initialization done\n"); return cast(T) p; } @@ -541,7 +511,7 @@ T _d_newclassTTrace(T)(string file, int line, string funcname) @trusted T* _d_newitemT(T)() @trusted { import core.internal.lifetime : emplaceInitializer; - import core.internal.traits : hasIndirections; + import urt.internal.traits : hasIndirections; import core.memory : GC; auto flags = !hasIndirections!T ? GC.BlkAttr.NO_SCAN : GC.BlkAttr.NONE; @@ -587,7 +557,7 @@ version (D_ProfileGC) template TypeInfoSize(T) { - import core.internal.traits : hasElaborateDestructor; + import urt.internal.traits : hasElaborateDestructor; enum TypeInfoSize = hasElaborateDestructor!T ? size_t.sizeof : 0; } +/ diff --git a/src/urt/log.d b/src/urt/log.d index 71af7fd..66c6a38 100644 --- a/src/urt/log.d +++ b/src/urt/log.d @@ -1,8 +1,188 @@ module urt.log; -import urt.io; +import urt.mem.temp : tconcat, tformat; +import urt.time; -enum Level +nothrow @nogc: + + +enum Severity : ubyte +{ + emergency = 0, + alert = 1, + critical = 2, + error = 3, + warning = 4, + notice = 5, + info = 6, + debug_ = 7, + trace = 8, +} + +immutable string[9] severity_names = [ + "Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Info", "Debug", "Trace" +]; + +struct LogMessage +{ + Severity severity; + const(char)[] tag; + const(char)[] object_name; + const(char)[] message; + MonoTime timestamp; +} + +struct LogFilter +{ + Severity max_severity = Severity.info; + const(char)[] tag_prefix; +} + +alias SinkOutputFn = void function(void* context, scope ref const LogMessage msg) nothrow @nogc; + +struct LogSinkHandle +{ + int index = -1; + bool valid() const pure nothrow @nogc + => index >= 0 && index < max_sinks; +} + +LogSinkHandle register_log_sink(SinkOutputFn output, void* context = null, LogFilter filter = LogFilter.init) +{ + foreach (i, ref sink; g_sinks) + { + if (!sink.active) + { + sink.output = output; + sink.context = context; + sink.filter = filter; + sink.enabled = true; + sink.active = true; + recalc_max_severity(); + return LogSinkHandle(cast(int)i); + } + } + return LogSinkHandle(-1); +} + +void unregister_log_sink(LogSinkHandle handle) +{ + if (!handle.valid) + return; + g_sinks[handle.index] = SinkSlot.init; + recalc_max_severity(); +} + +void set_sink_filter(LogSinkHandle handle, LogFilter filter) +{ + if (!handle.valid) + return; + g_sinks[handle.index].filter = filter; + recalc_max_severity(); +} + +void set_sink_enabled(LogSinkHandle handle, bool enabled) +{ + if (!handle.valid) + return; + g_sinks[handle.index].enabled = enabled; + recalc_max_severity(); +} + +void log_emergency(T...)(const(char)[] tag, ref T args) { write_log(Severity.emergency, tag, null, args); } +void log_alert(T...)(const(char)[] tag, ref T args) { write_log(Severity.alert, tag, null, args); } +void log_critical(T...)(const(char)[] tag, ref T args) { write_log(Severity.critical, tag, null, args); } +void log_error(T...)(const(char)[] tag, ref T args) { write_log(Severity.error, tag, null, args); } +void log_warning(T...)(const(char)[] tag, ref T args) { write_log(Severity.warning, tag, null, args); } +void log_notice(T...)(const(char)[] tag, ref T args) { write_log(Severity.notice, tag, null, args); } +void log_info(T...)(const(char)[] tag, ref T args) { write_log(Severity.info, tag, null, args); } + +void log_emergencyf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.emergency, tag, null, fmt, args); } +void log_alertf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.alert, tag, null, fmt, args); } +void log_criticalf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.critical, tag, null, fmt, args); } +void log_errorf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.error, tag, null, fmt, args); } +void log_warningf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.warning, tag, null, fmt, args); } +void log_noticef(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.notice, tag, null, fmt, args); } +void log_infof(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.info, tag, null, fmt, args); } + +debug +{ + void log_debug(T...)(const(char)[] tag, ref T args) { write_log(Severity.debug_, tag, null, args); } + void log_trace(T...)(const(char)[] tag, ref T args) { write_log(Severity.trace, tag, null, args); } + void log_debugf(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.debug_, tag, null, fmt, args); } + void log_tracef(T...)(const(char)[] tag, const(char)[] fmt, ref T args) { write_logf(Severity.trace, tag, null, fmt, args); } +} + +// this can be declared in any scope to automatically prefix log messages with a tag (e.g. module name) +// eg: alias log = Log!"my.module"; +// log.warn("oh no!"); +template Log(string tag) +{ + void info(T...)(ref T args) { write_log(Severity.info, tag, null, args); } + void warning(T...)(ref T args) { write_log(Severity.warning, tag, null, args); } + void error(T...)(ref T args) { write_log(Severity.error, tag, null, args); } + void notice(T...)(ref T args) { write_log(Severity.notice, tag, null, args); } + void critical(T...)(ref T args) { write_log(Severity.critical, tag, null, args); } + void alert(T...)(ref T args) { write_log(Severity.alert, tag, null, args); } + void emergency(T...)(ref T args) { write_log(Severity.emergency, tag, null, args); } + + void infof(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.info, tag, null, fmt, args); } + void warningf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.warning, tag, null, fmt, args); } + void errorf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.error, tag, null, fmt, args); } + void noticef(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.notice, tag, null, fmt, args); } + void criticalf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.critical, tag, null, fmt, args); } + void alertf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.alert, tag, null, fmt, args); } + void emergencyf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.emergency, tag, null, fmt, args); } + + debug + { + void debug_(T...)(ref T args) { write_log(Severity.debug_, tag, null, args); } + void trace(T...)(ref T args) { write_log(Severity.trace, tag, null, args); } + void debugf(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.debug_, tag, null, fmt, args); } + void tracef(T...)(const(char)[] fmt, ref T args) { write_logf(Severity.trace, tag, null, fmt, args); } + } +} + +void write_log(T...)(Severity severity, const(char)[] tag, const(char)[] object_name, ref T args) +{ + if (severity > g_max_severity) + return; + import urt.string, urt.array; + static if (T.length == 1 && (is(T[0] : const(char)[]) || is(T[0] : const String) || is(T[0] : const MutableString!N, size_t N) || is(T[0] : const Array!char))) + auto msg = LogMessage(severity, tag, object_name, args[0][], getTime()); + else + auto msg = LogMessage(severity, tag, object_name, tconcat(args), getTime()); + write_log(msg); +} + +void write_logf(T...)(Severity severity, const(char)[] tag, const(char)[] object_name, const(char)[] fmt, ref T args) +{ + if (severity > g_max_severity) + return; + auto msg = LogMessage(severity, tag, object_name, tformat(fmt, args), getTime()); + write_log(msg); +} + +void write_log(scope ref const LogMessage msg) +{ + import urt.string : startsWith; + + foreach (ref sink; g_sinks) + { + if (!sink.active || !sink.enabled) + continue; + if (msg.severity > sink.filter.max_severity) + continue; + if (sink.filter.tag_prefix.length > 0 && !msg.tag.startsWith(sink.filter.tag_prefix)) + continue; + sink.output(sink.context, msg); + } +} + + +// --- backward compatibility (deprecated) --- + +enum Level : ubyte { Error = 0, Warning, @@ -10,7 +190,18 @@ enum Level Debug } -immutable string[] levelNames = [ "Error", "Warning", "Info", "Debug" ]; +immutable string[] levelNames = ["Error", "Warning", "Info", "Debug"]; + +Severity level_to_severity(Level level) +{ + final switch (level) + { + case Level.Error: return Severity.error; + case Level.Warning: return Severity.warning; + case Level.Info: return Severity.info; + case Level.Debug: return Severity.debug_; + } +} __gshared Level logLevel = Level.Info; @@ -26,14 +217,52 @@ void writeErrorf(T...)(const(char)[] format, ref T things) { writeLogf(Level.Err void writeLog(T...)(Level level, ref T things) { - if (level > logLevel) + Severity sev = level_to_severity(level); + if (sev > g_max_severity) return; - writeln(levelNames[level], ": ", things); + write_log(sev, null, null, things); } void writeLogf(T...)(Level level, const(char)[] format, ref T things) { - if (level > logLevel) - return; - writelnf("{-2}: {@-1}", things, levelNames[level], format); + write_logf(level_to_severity(level), null, null, format, things); +} + +alias LegacyLogSink = void function(Level level, scope const(char)[] message) nothrow @nogc; + +private void legacy_sink_adapter(void* context, scope ref const LogMessage msg) nothrow @nogc +{ + __gshared immutable Level[9] map = [Level.Error, Level.Error, Level.Error, Level.Error, Level.Warning, Level.Info, Level.Info, Level.Debug, Level.Debug]; + (cast(LegacyLogSink)context)(map[msg.severity], msg.message); +} + +LogSinkHandle register_log_sink(LegacyLogSink sink) + => register_log_sink(&legacy_sink_adapter, cast(void*)sink); + + +private: + +enum max_sinks = 16; + +struct SinkSlot +{ + SinkOutputFn output; + void* context; + LogFilter filter; + bool enabled; + bool active; +} + +__gshared SinkSlot[max_sinks] g_sinks; +__gshared Severity g_max_severity = Severity.info; + +void recalc_max_severity() +{ + Severity max_sev = Severity.emergency; + foreach (ref sink; g_sinks) + { + if (sink.active && sink.enabled && sink.filter.max_severity > max_sev) + max_sev = sink.filter.max_severity; + } + g_max_severity = max_sev; } diff --git a/src/urt/map.d b/src/urt/map.d index 732fc79..3a624ac 100644 --- a/src/urt/map.d +++ b/src/urt/map.d @@ -292,13 +292,76 @@ struct AVLTree(K, V, alias Pred = DefCmp!K, Allocator = Mallocator) => Range!(IterateBy.Keys, true)(pRoot); auto values() nothrow => Range!(IterateBy.Values)(pRoot); -// auto values() const nothrow -// => Range!(IterateBy.Values, true)(pRoot); + auto values() const nothrow + => Range!(IterateBy.Values, true)(pRoot); auto opIndex() nothrow => Range!(IterateBy.KVP)(pRoot); -// auto opIndex() const nothrow -// => Range!(IterateBy.KVP, true)(pRoot); + auto opIndex() const nothrow + => Range!(IterateBy.KVP, true)(pRoot); + + import urt.string.format : FormatArg, formatValue; + ptrdiff_t toString()(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const + { + if (buffer.ptr is null) + { + // count the buffer size + size_t size = 2, comma = 0; + foreach (kvp; this) + { + size += comma; + comma = 1; + ptrdiff_t len = formatValue(kvp.key, buffer, format, formatArgs); + if (len < 0) + return len; + size += len + 1; + len = formatValue(kvp.value, buffer, format, formatArgs); + if (len < 0) + return len; + size += len; + } + return size; + } + + if (buffer.length < 2) + return -1; + buffer[0] = '{'; + + size_t offset = 1; + bool add_comma = false; + foreach (kvp; this) + { + if (add_comma) + { + if (offset >= buffer.length) + return -1; + buffer[offset++] = ','; + } + else + add_comma = true; + ptrdiff_t len = formatValue(kvp.key, buffer[offset .. $], format, formatArgs); + if (len < 0) + return len; + offset += len; + if (offset >= buffer.length) + return -1; + buffer[offset++] = ':'; + len = formatValue(kvp.value, buffer[offset .. $], format, formatArgs); + if (len < 0) + return len; + offset += len; + } + + if (offset >= buffer.length) + return -1; + buffer[offset++] = '}'; + return offset; + } + + ptrdiff_t fromString()(const(char)[] s) + { + assert(false, "TODO"); + } private: nothrow: @@ -657,9 +720,7 @@ public: PN n; ref const(K) key() @property const pure => n.kvp.key; -// ref const(V) value() @property const pure -// => n.kvp.value; - ref V value() @property pure + ref inout(V) value() @property inout pure => n.kvp.value; } return KV(n); @@ -900,7 +961,7 @@ unittest assert(map.get(2) is null); } - // Iteration (opApply) + // Iteration (range) { TestAVLTree map; map.insert(3, 30); @@ -908,25 +969,43 @@ unittest map.insert(2, 20); map.insert(4, 40); - int sumKeys = 0; - int sumValues = 0; - int count = 0; - // Iterate key-value pairs + int sumKeys = 0, sumValues = 0, count = 0; foreach (kv; map) { sumKeys += kv.key; sumValues += kv.value; count++; } + assert(count == 4); + assert(sumKeys == 1 + 2 + 3 + 4); + assert(sumValues == 10 + 20 + 30 + 40); + // Iterate const key-value pairs + ref const cmap = map; + sumKeys = sumValues = count = 0; + foreach (kv; cmap) + { + sumKeys += kv.key; + sumValues += kv.value; + count++; + } assert(count == 4); assert(sumKeys == 1 + 2 + 3 + 4); assert(sumValues == 10 + 20 + 30 + 40); - sumValues = 0; - count = 0; + // Iterate keys only + sumKeys = 0, count = 0; + foreach (v; map.keys) + { + sumKeys += v; + count++; + } + assert(count == 4); + assert(sumKeys == 1 + 2 + 3 + 4); + // Iterate values only + sumValues = 0, count = 0; foreach (v; map.values) { sumValues += v; @@ -935,6 +1014,16 @@ unittest assert(count == 4); assert(sumValues == 10 + 20 + 30 + 40); + // Iterate const values only + sumValues = 0, count = 0; + foreach (v; cmap.values) + { + sumValues += v; + count++; + } + assert(count == 4); + assert(sumValues == 10 + 20 + 30 + 40); + // Test stopping iteration count = 0; foreach (k; map.keys) @@ -946,27 +1035,6 @@ unittest assert(count == 2); // Should stop after 1 and 2 } - // Iteration (Iterator struct) - { - TestAVLTree map; - map.insert(3, 30); - map.insert(1, 10); - map.insert(2, 20); - map.insert(4, 40); - - foreach (kvp; map) // Uses Iterator internally - { - // Note: D's foreach over structs with opApply might not directly use the Iterator struct - // but opApply tests cover the iteration logic. - // This loop tests if the basic range primitives work. - // A direct Iterator test: - } - - // Test empty map iteration - TestAVLTree emptyMap; - assert(emptyMap[].empty); - } - // Test with string keys { alias StringMap = AVLTree!(const(char)[], int); diff --git a/src/urt/math.d b/src/urt/math.d index 236c867..e92a83c 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -1,8 +1,9 @@ module urt.math; import urt.intrinsic; -import core.stdc.stdio; // For writeDebugf -import std.format; // For format + +// for arch where using FPU for int<->float conversions is preferred +//version = PreferFPUIntConv; version (LDC) version = GDC_OR_LDC; version (GNU) version = GDC_OR_LDC; @@ -62,8 +63,150 @@ extern(C) double exp(double x); double log(double x); double acos(double x); +// double pow(double x, double e); +} + +int float_is_integer(double f, out ulong i) +{ + version (PreferFPUIntConv) + { + if (!(f == f)) + return 0; // NaN + if (f < 0) + { + if (f < long.min) + return 0; // out of range + long t = cast(long)f; + if (cast(double)t != f) + return 0; // not an integer + i = cast(ulong)t; + return -1; + } + if (f >= ulong.max) + return 0; // out of range + ulong t = cast(ulong)f; + if (cast(double)t != f) + return 0; // not an integer + i = t; + return 1; + } + else + { + import urt.meta : bit_mask; + enum M = 52, E = 11, B = 1023; + + ulong u = *cast(const(ulong)*)&f; + int e = (u >> M) & bit_mask!E; + ulong m = u & bit_mask!M; + + if (e == bit_mask!E) + return 0; // NaN/Inf + if (e == 0) + { + if (m) + return 0; // denormal + i = 0; + return 1; // +/- 0 + } + int shift = e - B; + if (shift < 0) + return 0; // |f| < 1 + bool integral = shift >= M || (m & bit_mask(M - shift)) == 0; + if (!integral) + return 0; // not an integer + if (f < 0) + { + if (f < long.min) + return 0; // out of range + i = cast(ulong)cast(long)f; + return -1; + } + if (f >= ulong.max) + return 0; // out of range + i = cast(ulong)f; + return 1; + } +} + +unittest +{ + // this covers all the branches, but maybe test some extreme cases? + ulong i; + assert(float_is_integer(double.nan, i) == 0); + assert(float_is_integer(double.infinity, i) == 0); + assert(float_is_integer(-double.infinity, i) == 0); + assert(float_is_integer(double.max, i) == 0); + assert(float_is_integer(-double.max, i) == 0); + assert(float_is_integer(0.5, i) == 0); + assert(float_is_integer(1.5, i) == 0); + assert(float_is_integer(cast(double)ulong.max, i) == 0); + assert(float_is_integer(0.0, i) == 1 && i == 0); + assert(float_is_integer(-0.0, i) == 1 && i == 0); + assert(float_is_integer(200, i) == 1 && i == 200); + assert(float_is_integer(-200, i) == -1 && cast(long)i == -200); } +auto pow(B, E)(B base, E exp) @trusted +{ + enum isFloatB = is(B == float) || is(B == double) || is(B == real); + enum isFloatE = is(E == float) || is(E == double) || is(E == real); + + static if (isFloatB) + { + // Floating-point base + B result = cast(B) 1.0; + B b = base; + + static if (isFloatE) + { + // F ^^ F — handle integer-valued exponents (covers 99% of + // real-world `^^` uses: value^^2, 10.0^^e, etc.) + if (exp == 0) return cast(B) 1.0; + long iexp = cast(long) exp; + if (cast(E) iexp == exp) + return _powfi!(B)(b, iexp); + // True non-integer exponent: not supported without libm. + assert(false, "Non-integer float exponent needs libm"); + } + else + { + // F ^^ I — binary exponentiation + return _powfi!(B)(b, cast(long) exp); + } + } + else + { + // I ^^ I — integer power + if (exp == 0) return cast(B) 1; + B result = cast(B) 1; + B b = base; + auto e = cast(ulong) exp; + while (e > 0) + { + if (e & 1) + result *= b; + b *= b; + e >>= 1; + } + return result; + } +} +// binary exponentiation: float base, integer exponent. +private F _powfi(F)(F base, long exp) @trusted +{ + if (exp == 0) return cast(F) 1.0; + bool neg = exp < 0; + ulong e = neg ? cast(ulong)(-exp) : cast(ulong) exp; + F result = cast(F) 1.0; + while (e > 0) + { + if (e & 1) + result *= base; + base *= base; + e >>= 1; + } + return neg ? cast(F) 1.0 / result : result; +} pragma(inline, true) bool addc(T = uint)(T a, T b, out T r, bool c_in) diff --git a/src/urt/mem/alloc.d b/src/urt/mem/alloc.d index 7408808..3f8f362 100644 --- a/src/urt/mem/alloc.d +++ b/src/urt/mem/alloc.d @@ -1,6 +1,6 @@ module urt.mem.alloc; -import core.stdc.stdlib; +import urt.internal.stdc; nothrow @nogc: @@ -11,15 +11,15 @@ void[] alloc(size_t size) nothrow @nogc return malloc(size)[0 .. size]; } -void[] allocAligned(size_t size, size_t alignment) nothrow @nogc +void[] alloc_aligned(size_t size, size_t alignment) nothrow @nogc { - import urt.util : isPowerOf2, max; + import urt.util : is_power_of_2, max; alignment = max(alignment, (void*).sizeof); - assert(isPowerOf2(alignment), "Alignment must be a power of two!"); + assert(is_power_of_2(alignment), "Alignment must be a power of two!"); version (Windows) { - import urt.util : alignDown; + import urt.util : align_down; // This is how Visual Studio's _aligned_malloc works... // see C:\Program Files (x86)\Windows Kits\10\Source\10.0.15063.0\ucrt\heap\align.cpp @@ -34,7 +34,7 @@ void[] allocAligned(size_t size, size_t alignment) nothrow @nogc return null; size_t ptr = cast(size_t)mem; - size_t allocptr = alignDown(ptr + header_size, alignment); + size_t allocptr = align_down(ptr + header_size, alignment); (cast(void**)allocptr)[-1] = mem; return (cast(void*)allocptr)[0 .. size]; @@ -57,27 +57,27 @@ void[] allocAligned(size_t size, size_t alignment) nothrow @nogc void[] realloc(void[] mem, size_t newSize) nothrow @nogc { // TODO: we might pin the length to a debug table somewhere... - return core.stdc.stdlib.realloc(mem.ptr, newSize)[0 .. newSize]; + return urt.mem.realloc(mem.ptr, newSize)[0 .. newSize]; } -void[] reallocAligned(void[] mem, size_t newSize, size_t alignment) nothrow @nogc +void[] realloc_aligned(void[] mem, size_t newSize, size_t alignment) nothrow @nogc { - import urt.util : isPowerOf2, min, max; + import urt.util : is_power_of_2, min, max; alignment = max(alignment, (void*).sizeof); - assert(isPowerOf2(alignment), "Alignment must be a power of two!"); + assert(is_power_of_2(alignment), "Alignment must be a power of two!"); - void[] newAlloc = newSize > 0 ? allocAligned(newSize, alignment) : null; + void[] newAlloc = newSize > 0 ? alloc_aligned(newSize, alignment) : null; if (newAlloc !is null && mem !is null) { size_t toCopy = min(mem.length, newSize); newAlloc[0 .. toCopy] = mem[0 .. toCopy]; } - freeAligned(mem); + free_aligned(mem); return newAlloc; } -// NOTE: This function is only compatible with allocAligned! +// NOTE: This function is only compatible with alloc_aligned! void[] expand(void[] mem, size_t newSize) nothrow @nogc { version (Windows) @@ -104,20 +104,20 @@ void free(void[] mem) nothrow @nogc // maybe check the length passed to free matches the alloc? // ... or you know, just don't do that. - core.stdc.stdlib.free(mem.ptr); + urt.mem.free(mem.ptr); } -void freeAligned(void[] mem) nothrow @nogc +void free_aligned(void[] mem) nothrow @nogc { version (Windows) { if (mem.ptr is null) return; void* p = (cast(void**)mem.ptr)[-1]; - core.stdc.stdlib.free(p); + urt.mem.free(p); } else - core.stdc.stdlib.free(mem.ptr); + urt.mem.free(mem.ptr); } size_t memsize(void* ptr) nothrow @nogc @@ -140,11 +140,11 @@ size_t memsize(void* ptr) nothrow @nogc unittest { - void[] mem = allocAligned(16, 8); + void[] mem = alloc_aligned(16, 8); size_t s = memsize(mem.ptr); mem = expand(mem, 8); mem = expand(mem, 16); - freeAligned(mem); + free_aligned(mem); } diff --git a/src/urt/mem/allocator.d b/src/urt/mem/allocator.d index 75cadff..f6a54ca 100644 --- a/src/urt/mem/allocator.d +++ b/src/urt/mem/allocator.d @@ -356,12 +356,12 @@ class Mallocator : NoGCAllocator override void[] alloc(size_t size, size_t alignment = DefaultAlign) nothrow @nogc { - return urt.mem.alloc.allocAligned(size, alignment); + return urt.mem.alloc.alloc_aligned(size, alignment); } override void[] realloc(void[] mem, size_t newSize, size_t alignment = DefaultAlign) nothrow @nogc { - return urt.mem.alloc.reallocAligned(mem, newSize, alignment); + return urt.mem.alloc.realloc_aligned(mem, newSize, alignment); } override void[] expand(void[] mem, size_t newSize) nothrow @@ -371,7 +371,7 @@ class Mallocator : NoGCAllocator override void free(void[] mem) nothrow @nogc { - urt.mem.alloc.freeAligned(mem); + urt.mem.alloc.free_aligned(mem); } private: @@ -434,7 +434,10 @@ unittest } } - Allocator a = new Mallocator; + import urt.util; + auto mallocator = InPlace!Mallocator(Default); + + Allocator a = mallocator; S* s = a.allocT!S(10); a.freeT(s); C c = a.allocT!C(); diff --git a/src/urt/mem/package.d b/src/urt/mem/package.d index 795a4e3..ec4e82e 100644 --- a/src/urt/mem/package.d +++ b/src/urt/mem/package.d @@ -1,29 +1,206 @@ module urt.mem; -public import core.stdc.stddef : wchar_t; - +// TODO: remove these public imports, because this is pulled by object.d! public import urt.lifetime : emplace, moveEmplace, forward, move; public import urt.mem.allocator; +nothrow @nogc: + + +version (LDC) + pragma(LDC_alloca) void* alloca(size_t size) pure @safe; +else + extern(C) void* alloca(size_t size) pure @trusted; extern(C) { nothrow @nogc: - void* alloca(size_t size); - void* memcpy(void* dest, const void* src, size_t n); - void* memmove(void* dest, const void* src, size_t n); - void* memset(void* s, int c, size_t n); - void* memzero(void* s, size_t n) => memset(s, 0, n); + void* malloc(size_t size) @trusted; + void* calloc(size_t num, size_t size) @trusted; + void* realloc(void* ptr, size_t new_size) @trusted; + void free(void* ptr) @trusted; + + void* memcpy(void* dest, const void* src, size_t n) pure; + void* memmove(void* dest, const void* src, size_t n) pure; + void* memset(void* s, int c, size_t n) pure; + void* memzero(void* s, size_t n) pure => memset(s, 0, n); + int memcmp(const void *s1, const void *s2, size_t n) pure; - size_t strlen(const char* s); - int strcmp(const char* s1, const char* s2); - char* strcpy(char* dest, const char* src); + size_t strlen(const char* s) pure; + int strcmp(const char* s1, const char* s2) pure; + char* strcpy(char* dest, const char* src) pure; char* strcat(char* dest, const char* src); - size_t wcslen(const wchar_t* s); -// wchar_t* wcscpy(wchar_t* dest, const wchar_t* src); + size_t wcslen(const wchar_t* s) pure; +// wchar_t* wcscpy(wchar_t* dest, const wchar_t* src) pure; // wchar_t* wcscat(wchar_t* dest, const wchar_t* src); -// wchar_t* wcsncpy(wchar_t* dest, const wchar_t* src, size_t n); +// wchar_t* wcsncpy(wchar_t* dest, const wchar_t* src, size_t n) pure; // wchar_t* wcsncat(wchar_t* dest, const wchar_t* src, size_t n); } + + +private: + +version(DigitalMars) +{ + // DMD lowers alloca(n) calls to __alloca(n) + extern (C) void* __alloca(int nbytes) + { + version (D_InlineAsm_X86) + { + asm nothrow @nogc + { + naked ; + mov EDX,ECX ; + mov EAX,4[ESP] ; // get nbytes + push EBX ; + push EDI ; + push ESI ; + + add EAX,15 ; + and EAX,0xFFFFFFF0 ; // round up to 16 byte boundary + jnz Abegin ; + mov EAX,16 ; // minimum allocation is 16 + Abegin: + mov ESI,EAX ; // ESI = nbytes + neg EAX ; + add EAX,ESP ; // EAX is now what the new ESP will be. + jae Aoverflow ; + } + version (Win32) + { + asm nothrow @nogc + { + // Touch guard pages to commit stack memory + mov ECX,EAX ; + mov EBX,ESI ; + L1: + test [ECX+EBX],EBX ; + sub EBX,0x1000 ; + jae L1 ; + test [ECX],EBX ; + } + } + asm nothrow @nogc + { + mov ECX,EBP ; + sub ECX,ESP ; + sub ECX,[EDX] ; + add [EDX],ESI ; + mov ESP,EAX ; + add EAX,ECX ; + mov EDI,ESP ; + add ESI,ESP ; + shr ECX,2 ; + rep ; + movsd ; + jmp done ; + + Aoverflow: + xor EAX,EAX ; + + done: + pop ESI ; + pop EDI ; + pop EBX ; + ret ; + } + } + else version (D_InlineAsm_X86_64) + { + version (Win64) + { + asm nothrow @nogc + { + naked ; + push RBX ; + push RDI ; + push RSI ; + mov RAX,RCX ; + add RAX,15 ; + and AL,0xF0 ; + test RAX,RAX ; + jnz Abegin ; + mov RAX,16 ; + Abegin: + mov RSI,RAX ; + neg RAX ; + add RAX,RSP ; + jae Aoverflow ; + + // Touch guard pages + mov RCX,RAX ; + mov RBX,RSI ; + L1: + test [RCX+RBX],RBX ; + sub RBX,0x1000 ; + jae L1 ; + test [RCX],RBX ; + + mov RCX,RBP ; + sub RCX,RSP ; + sub RCX,[RDX] ; + add [RDX],RSI ; + mov RSP,RAX ; + add RAX,RCX ; + mov RDI,RSP ; + add RSI,RSP ; + shr RCX,3 ; + rep ; + movsq ; + jmp done ; + + Aoverflow: + xor RAX,RAX ; + + done: + pop RSI ; + pop RDI ; + pop RBX ; + ret ; + } + } + else + { + asm nothrow @nogc + { + naked ; + mov RDX,RCX ; + mov RAX,RDI ; + add RAX,15 ; + and AL,0xF0 ; + test RAX,RAX ; + jnz Abegin ; + mov RAX,16 ; + Abegin: + mov RSI,RAX ; + neg RAX ; + add RAX,RSP ; + jae Aoverflow ; + + mov RCX,RBP ; + sub RCX,RSP ; + sub RCX,[RDX] ; + add [RDX],RSI ; + mov RSP,RAX ; + add RAX,RCX ; + mov RDI,RSP ; + add RSI,RSP ; + shr RCX,3 ; + rep ; + movsq ; + jmp done ; + + Aoverflow: + xor RAX,RAX ; + + done: + ret ; + } + } + } + else + static assert(0, "Unsupported architecture for __alloca"); + } +} diff --git a/src/urt/mem/region.d b/src/urt/mem/region.d index 9a4a228..a657ace 100644 --- a/src/urt/mem/region.d +++ b/src/urt/mem/region.d @@ -6,7 +6,7 @@ import urt.util; static Region* makeRegion(void[] mem) pure nothrow @nogc { assert(mem.length >= Region.sizeof, "Memory block too small"); - Region* region = cast(Region*)mem.ptr.alignUp(Region.alignof); + Region* region = cast(Region*)mem.ptr.align_up(Region.alignof); size_t alignBytes = cast(void*)region - mem.ptr; if (size_t.sizeof > 4 && mem.length > uint.max + alignBytes + Region.sizeof) region.length = uint.max; @@ -21,7 +21,7 @@ struct Region void[] alloc(size_t size, size_t alignment = size_t.sizeof) pure nothrow @nogc { size_t ptr = cast(size_t)&this + Region.sizeof + offset; - size_t alignedPtr = ptr.alignUp(alignment); + size_t alignedPtr = ptr.align_up(alignment); size_t alignBytes = alignedPtr - ptr; if (offset + alignBytes + size > length) return null; diff --git a/src/urt/mem/scratchpad.d b/src/urt/mem/scratchpad.d index 83a3abb..2a20c30 100644 --- a/src/urt/mem/scratchpad.d +++ b/src/urt/mem/scratchpad.d @@ -8,10 +8,10 @@ nothrow @nogc: enum size_t MaxScratchpadSize = 2048; enum size_t NumScratchBuffers = 4; -static assert(MaxScratchpadSize.isPowerOf2, "Scratchpad size must be a power of 2"); +static assert(MaxScratchpadSize.is_power_of_2, "Scratchpad size must be a power of 2"); -void[] allocScratchpad(size_t size = MaxScratchpadSize) +void[] alloc_scratchpad(size_t size = MaxScratchpadSize) { if (size > MaxScratchpadSize) { @@ -19,17 +19,17 @@ void[] allocScratchpad(size_t size = MaxScratchpadSize) return null; } - size = max(size.nextPowerOf2, WindowSize); + size = max(size.next_power_of_2, WindowSize); size_t maskBits = size / WindowSize; size_t mask = (1 << maskBits) - 1; - for (size_t page = 0; page < scratchpadAlloc.length; ++page) + for (size_t page = 0; page < scratchpad_alloc.length; ++page) { for (size_t window = 0; window < 8; window += maskBits) { - if ((scratchpadAlloc[page] & (mask << window)) == 0) + if ((scratchpad_alloc[page] & (mask << window)) == 0) { - scratchpadAlloc[page] |= mask << window; + scratchpad_alloc[page] |= mask << window; return scratchpad[page*MaxScratchpadSize + window*WindowSize .. page*MaxScratchpadSize + window*WindowSize + size]; } } @@ -40,7 +40,7 @@ void[] allocScratchpad(size_t size = MaxScratchpadSize) return null; } -void freeScratchpad(void[] mem) +void free_scratchpad(void[] mem) { size_t page = (cast(size_t)mem.ptr - cast(size_t)scratchpad.ptr) / MaxScratchpadSize; size_t window = (cast(size_t)mem.ptr - cast(size_t)scratchpad.ptr) % MaxScratchpadSize / WindowSize; @@ -48,8 +48,8 @@ void freeScratchpad(void[] mem) size_t maskBits = mem.length / WindowSize; size_t mask = (1 << maskBits) - 1; - assert((scratchpadAlloc[page] & (mask << window)) == (mask << window), "Freeing unallocated scratchpad memory!"); - scratchpadAlloc[page] &= ~(mask << window); + assert((scratchpad_alloc[page] & (mask << window)) == (mask << window), "Freeing unallocated scratchpad memory!"); + scratchpad_alloc[page] &= ~(mask << window); } private: @@ -57,31 +57,31 @@ private: enum WindowSize = MaxScratchpadSize / 8; __gshared ubyte[MaxScratchpadSize*NumScratchBuffers] scratchpad; -__gshared ubyte[NumScratchBuffers] scratchpadAlloc; +__gshared ubyte[NumScratchBuffers] scratchpad_alloc; unittest { - void[] t = allocScratchpad(MaxScratchpadSize); + void[] t = alloc_scratchpad(MaxScratchpadSize); assert(t.length == MaxScratchpadSize); - void[] t2 = allocScratchpad(MaxScratchpadSize / 2); + void[] t2 = alloc_scratchpad(MaxScratchpadSize / 2); assert(t2.length == MaxScratchpadSize / 2); - void[] t3 = allocScratchpad(MaxScratchpadSize / 2); + void[] t3 = alloc_scratchpad(MaxScratchpadSize / 2); assert(t3.length == MaxScratchpadSize / 2); - void[] t4 = allocScratchpad(MaxScratchpadSize / 4); + void[] t4 = alloc_scratchpad(MaxScratchpadSize / 4); assert(t4.length == MaxScratchpadSize / 4); - void[] t5 = allocScratchpad(MaxScratchpadSize / 8); + void[] t5 = alloc_scratchpad(MaxScratchpadSize / 8); assert(t5.length == MaxScratchpadSize / 8); - void[] t6 = allocScratchpad(MaxScratchpadSize / 4); + void[] t6 = alloc_scratchpad(MaxScratchpadSize / 4); assert(t6.length == MaxScratchpadSize / 4); - void[] t7 = allocScratchpad(MaxScratchpadSize / 8); + void[] t7 = alloc_scratchpad(MaxScratchpadSize / 8); assert(t7.length == MaxScratchpadSize / 8); - freeScratchpad(t); - freeScratchpad(t7); - freeScratchpad(t5); - freeScratchpad(t4); - freeScratchpad(t6); - freeScratchpad(t2); - freeScratchpad(t3); + free_scratchpad(t); + free_scratchpad(t7); + free_scratchpad(t5); + free_scratchpad(t4); + free_scratchpad(t6); + free_scratchpad(t2); + free_scratchpad(t3); } diff --git a/src/urt/mem/string.d b/src/urt/mem/string.d index a04f5e9..ff606ca 100644 --- a/src/urt/mem/string.d +++ b/src/urt/mem/string.d @@ -4,61 +4,58 @@ import urt.mem; import urt.string; -// TODO: THIS IS TEMP!! REMOVE ME!! -shared static this() -{ - initStringHeap(ushort.max); -} - - -struct StringCache -{ -} - struct CacheString { +nothrow @nogc: alias toString this; - this(typeof(null)) pure nothrow @nogc + this(typeof(null)) pure { offset = 0; } - string toString() const nothrow @nogc + string toString() const pure { - ushort len = *cast(ushort*)(stringHeap.ptr + offset); - return cast(string)stringHeap[offset + 2 .. offset + 2 + len]; + // HACK: deploy the pure hack! + static char[] pureHack() nothrow @nogc => stringHeap; + string heap = (cast(immutable(char[]) function() pure nothrow @nogc)&pureHack)(); + + ushort len = *cast(ushort*)(heap.ptr + offset); + return heap[offset + 2 .. offset + 2 + len]; } - immutable(char)* ptr() const nothrow @nogc - => cast(immutable(char)*)(stringHeap.ptr + offset + 2); + immutable(char)* ptr() const pure + => toString().ptr; + + size_t length() const pure + => toString().length; - size_t length() const nothrow @nogc - => *cast(ushort*)(stringHeap.ptr + offset); + string opIndex() const pure + => toString(); - bool opCast(T : bool)() const pure nothrow @nogc + bool opCast(T : bool)() const pure => offset != 0; - void opAssign(typeof(null)) pure nothrow @nogc + void opAssign(typeof(null)) pure { offset = 0; } - bool opEquals(const(char)[] rhs) const nothrow @nogc + bool opEquals(const(char)[] rhs) const pure { string s = toString(); - return s.length == rhs.length && (s.ptr == rhs.ptr || s[] == rhs[]); + return s.length == rhs.length && (s.ptr is rhs.ptr || s[] == rhs[]); } - size_t toHash() const nothrow @nogc + size_t toHash() const pure { import urt.hash; static if (size_t.sizeof == 4) - return fnv1aHash(cast(ubyte[])toString()); + return fnv1a(cast(ubyte[])toString()); else - return fnv1aHash64(cast(ubyte[])toString()); + return fnv1a64(cast(ubyte[])toString()); } private: @@ -74,24 +71,23 @@ private: auto __debugStringView() => toString; } -void initStringHeap(uint stringHeapSize) nothrow +void init_string_heap(uint string_heap_size) nothrow @nogc { assert(stringHeapInitialised == false, "String heap already initialised!"); - assert(stringHeapSize <= ushort.max, "String heap too large!"); + assert(string_heap_size <= ushort.max, "String heap too large!"); - stringHeap = new char[stringHeapSize]; + stringHeap = defaultAllocator.allocArray!char(string_heap_size); // write the null string to the start - stringHeapCursor = 0; - stringHeap[stringHeapCursor++] = 0; - stringHeap[stringHeapCursor++] = 0; - numStrings = 1; + stringHeap[0..2] = 0; + stringHeapCursor = 2; stringHeapInitialised = true; } -void deinitStringHeap() nothrow +void deinit_string_heap() nothrow @nogc { + defaultAllocator.freeArray(stringHeap); } uint getStringHeapAllocated() nothrow @nogc @@ -104,44 +100,46 @@ uint getStringHeapRemaining() nothrow @nogc return cast(uint)stringHeap.length - stringHeapCursor; } -CacheString addString(const(char)[] str, bool dedup = true) nothrow @nogc +CacheString addString(const(char)[] str) pure nothrow @nogc { - // null string - if (str.length == 0) - return CacheString(0); + // HACK: even though this mutates global state, the string cache is immutable after it's emplaced + // so, multiple calls with the same source string will always return the same result! + static CacheString impl(const(char)[] str) nothrow @nogc + { + // null string + if (str.length == 0) + return CacheString(0); - assert(str.length < 2^^14, "String longer than max string len (32768 chars)"); + assert(str.length < 2^^14, "String longer than max string len (32768 chars)"); - if (dedup) - { // first we scan to see if it's already in here... - for (ushort i = 1; i < stringHeapCursor;) + for (ushort i = 2; i < stringHeapCursor;) { ushort offset = i; - ushort len = stringHeap[i++]; - if (len >= 128) - len = (len & 0x7F) | ((stringHeap[i++] << 7) & 0x7F); + ushort len = *cast(ushort*)(stringHeap.ptr + i); + i += 2; if (len == str.length && stringHeap[i .. i + len] == str[]) return CacheString(offset); - i += len; + i += len + (len & 1); } - } - if (stringHeapCursor & 1) - stringHeap[stringHeapCursor++] = '\0'; + // add the string to the heap... + assert(stringHeapCursor + str.length < stringHeap.length, "String heap overflow!"); - // add the string to the heap... - assert(stringHeapCursor + str.length < stringHeap.length, "String heap overflow!"); + char[] heap = stringHeap[stringHeapCursor .. $]; + ushort offset = stringHeapCursor; - ushort offset = stringHeapCursor; - str.makeString(stringHeap[stringHeapCursor .. $]); - stringHeapCursor += str.length + 2; - ++numStrings; + *cast(ushort*)heap.ptr = cast(ushort)str.length; + heap[2 .. 2 + str.length] = str[]; + stringHeapCursor += str.length + 2; + if (stringHeapCursor & 1) + stringHeap[stringHeapCursor++] = '\0'; - return CacheString(offset); + return CacheString(offset); + } + return (cast(CacheString function(const(char)[]) pure nothrow @nogc)&impl)(str); } - void* allocWithStringCache(size_t bytes, String[] cachedStrings, const(char[])[] strings) nothrow @nogc { import urt.mem.alloc; @@ -191,7 +189,6 @@ private: __gshared bool stringHeapInitialised = false; __gshared char[] stringHeap = null; __gshared ushort stringHeapCursor = 0; -__gshared uint numStrings = 0; unittest diff --git a/src/urt/mem/temp.d b/src/urt/mem/temp.d index 6f2d28b..5f4eb08 100644 --- a/src/urt/mem/temp.d +++ b/src/urt/mem/temp.d @@ -16,22 +16,18 @@ void[] talloc(size_t size) nothrow @nogc assert(InFormatFunction == false, "It is illegal to use the temp allocator inside string conversion functions. Consider using stack or scratchpad."); } - if (size >= TempMemSize / 2) - { - assert(false, "Requested temp memory size is too large"); - return null; - } + assert(size <= TempMemSize / 2, "Requested temp memory size is too large"); - if (allocOffset + size > TempMemSize) - allocOffset = 0; + if (alloc_offset + size > TempMemSize) + alloc_offset = 0; - void[] mem = tempMem[allocOffset .. allocOffset + size]; - allocOffset += size; + void[] mem = tempMem[alloc_offset .. alloc_offset + size]; + alloc_offset += size; return mem; } -void[] tallocAligned(size_t size, size_t alignment) nothrow @nogc +void[] talloc_aligned(size_t size, size_t alignment) nothrow @nogc { assert(false); } @@ -50,19 +46,19 @@ void[] trealloc(void[] mem, size_t newSize) nothrow @nogc return r; } -void[] treallocAligned(void[] mem, size_t newSize, size_t alignment) nothrow @nogc +void[] trealloc_aligned(void[] mem, size_t newSize, size_t alignment) nothrow @nogc { assert(false); } void[] texpand(void[] mem, size_t newSize) nothrow @nogc { - if (mem.ptr + mem.length != tempMem.ptr + allocOffset) + if (mem.ptr + mem.length != tempMem.ptr + alloc_offset) return null; ptrdiff_t grow = newSize - mem.length; - if (cast(size_t)(allocOffset + grow) > TempMemSize) + if (cast(size_t)(alloc_offset + grow) > TempMemSize) return null; - allocOffset += grow; + alloc_offset += grow; return mem.ptr[0 .. newSize]; } @@ -73,62 +69,108 @@ void tfree(void[] mem) nothrow @nogc char* tstringz(const(char)[] str) nothrow @nogc { - if (str.length > TempMemSize / 2) - return null; - - size_t len = str.length; - if (allocOffset + len + 1 > TempMemSize) - allocOffset = 0; + char* r = cast(char*)talloc(str.length + 1).ptr; + r[0 .. str.length] = str[]; + r[str.length] = '\0'; + return r; +} +char* tstringz(const(wchar)[] str) nothrow @nogc +{ + import urt.string.uni : uni_convert; + char* r = cast(char*)talloc(str.length*3 + 1).ptr; + size_t len = uni_convert(str, r[0 .. str.length*3]); + r[len] = '\0'; + return r; +} - char* r = cast(char*)tempMem.ptr + allocOffset; - r[0 .. len] = str[]; +wchar* twstringz(const(char)[] str) nothrow @nogc +{ + import urt.string.uni : uni_convert; + wchar* r = cast(wchar*)talloc(str.length*2 + 2).ptr; + size_t len = uni_convert(str, r[0 .. str.length]); r[len] = '\0'; - allocOffset += len + 1; + return r; +} +wchar* twstringz(const(wchar)[] str) nothrow @nogc +{ + wchar* r = cast(wchar*)talloc(str.length*2 + 2).ptr; + r[0 .. str.length] = str[]; + r[str.length] = '\0'; return r; } -char[] tstring(T)(auto ref T value) +const(char)[] tstring(T)(auto ref T value) { - import urt.string.format : toString; - ptrdiff_t r = toString(value, cast(char[])tempMem[allocOffset..$]); - if (r < 0) + import urt.string, urt.array; + static if (is(T : const(char)[]) || is(T : const String) || is(T : const MutableString!N, size_t N) || is(T : const Array!char)) + { + pragma(inline, true); + return value[]; + } + else { - allocOffset = 0; - r = toString(value, cast(char[])tempMem[0..TempMemSize / 2]); + import urt.string.format : toString; + ptrdiff_t r = toString(value, cast(char[])tempMem[alloc_offset..$]); if (r < 0) { -// assert(false, "Formatted string is too large for the temp buffer!"); - return null; + alloc_offset = 0; + r = toString(value, cast(char[])tempMem[0..TempMemSize / 2]); + if (r < 0) + { +// assert(false, "Formatted string is too large for the temp buffer!"); + return null; + } } + const(char)[] result = cast(char[])tempMem[alloc_offset .. alloc_offset + r]; + alloc_offset += r; + return result; } - char[] result = cast(char[])tempMem[allocOffset .. allocOffset + r]; - allocOffset += r; - return result; } -char[] tconcat(Args...)(ref Args args) +const(dchar)[] tdstring(T)(auto ref T value) nothrow @nogc { - import urt.string.format : concat; - char[] r = concat(cast(char[])tempMem[allocOffset..$], args); - if (!r) + static if (is(T : const(char)[]) || is(T : const(wchar)[]) || is(T : const(dchar)[])) + alias s = value; + else + char[] s = tstring(value); + import urt.string.uni : uni_convert; + dchar* r = cast(dchar*)talloc(s[].length*4).ptr; + size_t len = uni_convert(s[], r[0 .. s.length]); + return r[0 .. len]; +} + +const(char)[] tconcat(Args...)(ref Args args) +{ + import urt.string, urt.array; + static if (Args.length == 1 && (is(Args[0] : const(char)[]) || is(Args[0] : const String) || is(Args[0] : const MutableString!N, size_t N) || is(Args[0] : const Array!char))) { - allocOffset = 0; - r = concat(cast(char[])tempMem[0..TempMemSize / 2], args); + pragma(inline, true); + return args[0][]; + } + else + { + import urt.string.format : concat; + const(char)[] r = concat(cast(char[])tempMem[alloc_offset..$], args); + if (!r) + { + alloc_offset = 0; + r = concat(cast(char[])tempMem[0..TempMemSize / 2], args); + } + alloc_offset += r.length; + return r; } - allocOffset += r.length; - return r; } char[] tformat(Args...)(const(char)[] fmt, ref Args args) { import urt.string.format : format; - char[] r = format(cast(char[])tempMem[allocOffset..$], fmt, args); + char[] r = format(cast(char[])tempMem[alloc_offset..$], fmt, args); if (!r) { - allocOffset = 0; + alloc_offset = 0; r = format(cast(char[])tempMem[0..TempMemSize / 2], fmt, args); } - allocOffset += r.length; + alloc_offset += r.length; return r; } @@ -170,4 +212,4 @@ private: private: static void[TempMemSize] tempMem; -static ushort allocOffset = 0; +static ushort alloc_offset = 0; diff --git a/src/urt/meta/enuminfo.d b/src/urt/meta/enuminfo.d new file mode 100644 index 0000000..1a1ef5e --- /dev/null +++ b/src/urt/meta/enuminfo.d @@ -0,0 +1,394 @@ +module urt.meta.enuminfo; + +import urt.algorithm : binary_search, qsort; +import urt.traits :EnumType, is_enum, Unqual; +import urt.meta : Iota, STATIC_MAP; +import urt.variant; + +nothrow @nogc: + + +const(E)* enum_from_key(E)(const(char)[] key) pure + if (is_enum!E) + => enum_info!E.value_for(key); + +const(char)[] enum_key_from_value(E)(EnumType!E value) pure + if (is_enum!E) + => enum_info!E.key_for(value); + +const(char)[] enum_key_by_decl_index(E)(size_t value) pure + if (is_enum!E) + => enum_info!E.key_by_decl_index(value); + +struct VoidEnumInfo +{ + import urt.algorithm : binary_search; +nothrow @nogc: + + // keys and values are sorted for binary search + ushort count; + ushort stride; + uint type_hash; + + const(char)[] key_for(const void* value, int function(const void* a, const void* b) pure nothrow @nogc pred) const pure + { + size_t i = binary_search(_values[0 .. count*stride], stride, value, pred); + if (i < count) + return get_key(_lookup_tables[count + i]); + return null; + } + + const(char)[] key_for(const void* value, int delegate(const void* a, const void* b) pure nothrow @nogc pred) const pure + { + size_t i = binary_search(_values[0 .. count*stride], stride, value, pred); + if (i < count) + return get_key(_lookup_tables[count + i]); + return null; + } + + const(char)[] key_by_decl_index(size_t i) const pure + { + assert(i < count, "Declaration index out of range"); + return get_key(_lookup_tables[count*2 + i]); + } + + const(char)[] key_by_sorted_index(size_t i) const pure + { + assert(i < count, "Declaration index out of range"); + return get_key(i); + } + + Variant value_for(const(char)[] key) const pure + { + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); + if (i == count) + return Variant(); + i = _lookup_tables[i]; + return _get_value(_values + i*stride, &this); + } + + bool contains(const(char)[] key) const pure + { + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); + return i < count; + } + +private: + const void* _values; + const ushort* _keys; + const char* _string_buffer; + + // these tables map between indices of keys and values + const ubyte* _lookup_tables; + + const GetFun _get_value; + + this(ubyte count, ushort stride, uint type_hash, inout void* values, inout ushort* keys, inout char* strings, inout ubyte* lookup, GetFun get_value) inout pure + { + this.count = count; + this.stride = stride; + this.type_hash = type_hash; + this._keys = keys; + this._values = values; + this._string_buffer = strings; + this._lookup_tables = lookup; + this._get_value = get_value; + } + + const(char)[] get_key(size_t i) const pure + { + const(char)* s = _string_buffer + _keys[i]; + return s[0 .. s.key_length]; + } +} + +template EnumInfo(E) +{ + static assert (is(E == Unqual!E), "EnumInfo can only be instantiated with unqualified types!"); + + static if (is(E == void)) + alias EnumInfo = VoidEnumInfo; + else + { + struct EnumInfo + { + import urt.algorithm : binary_search; + nothrow @nogc: + + static assert (EnumInfo.sizeof == EnumInfo.sizeof, "Template EnumInfo must not add any members!"); + + static if (is(E T == enum)) + alias V = T; + else + static assert(false, E.string ~ " is not an enum type!"); + + // keys and values are sorted for binary search + union { + VoidEnumInfo _base; + struct { + ubyte[VoidEnumInfo._values.offsetof] _pad; + const E* _values; // shadows the _values in _base with a typed version + } + } + alias _base this; + + inout(VoidEnumInfo*) make_void() inout pure + => &_base; + + this(ubyte count, uint type_hash, inout(E)* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure + { + _base = inout(VoidEnumInfo)(count, E.sizeof, type_hash, values, keys, strings, lookup, cast(GetFun)&get_value!V); + } + + const(E)[] values() const pure + => _values[0 .. count]; + + const(char)[] key_for(V value) const pure + { + size_t i = binary_search(values[0 .. count], value); + if (i < count) + return get_key(_lookup_tables[count + i]); + return null; + } + + const(char)[] key_by_decl_index(size_t i) const pure + => _base.key_by_decl_index(i); + + const(char)[] key_by_sorted_index(size_t i) const pure + => _base.key_by_sorted_index(i); + + const(E)* value_for(const(char)[] key) const pure + { + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); + if (i == count) + return null; + return _values + _lookup_tables[i]; + } + + bool contains(const(char)[] key) const pure + => _base.contains(key); + } + } +} + +template enum_info(E) + if (is(E == enum)) +{ + static assert (is(E == Unqual!E), "EnumInfo can only be instantiated with unqualified types!"); + + enum ubyte num_items = enum_members.length; + static assert(num_items <= ubyte.max, "Too many enum items!"); + + __gshared immutable enum_info = immutable(EnumInfo!E)( + num_items, + fnv1a(cast(ubyte[])E.stringof), + _values.ptr, + _keys.ptr, + _strings.ptr, + _lookup.ptr + ); + +private: + import urt.algorithm : binary_search, compare, qsort; + import urt.hash : fnv1a; + import urt.string.uni : uni_compare; + + // keys and values are sorted for binary search + __gshared immutable E[num_items] _values = [ STATIC_MAP!(GetValue, iota) ]; + + // keys are stored as offsets info the string buffer + __gshared immutable ushort[num_items] _keys = () { + ushort[num_items] key_offsets; + size_t offset = 2; + foreach (i; 0 .. num_items) + { + const(char)[] key = by_key[i].k; + key_offsets[i] = cast(ushort)offset; + offset += 2 + key.length; + if (key.length & 1) + offset += 1; // align to 2 bytes + } + return key_offsets; + }(); + + // build the string buffer + __gshared immutable char[total_strings] _strings = () { + char[total_strings] str_data; + char* ptr = str_data.ptr; + foreach (i; 0 .. num_items) + { + const(char)[] key = by_key[i].k; + version (LittleEndian) + { + *ptr++ = key.length & 0xFF; + *ptr++ = (key.length >> 8) & 0xFF; + } + else + { + *ptr++ = (key.length >> 8) & 0xFF; + *ptr++ = key.length & 0xFF; + } + ptr[0 .. key.length] = key[]; + ptr += key.length; + if (key.length & 1) + *ptr++ = 0; // align to 2 bytes + } + return str_data; + }(); + + // these tables map between indices of keys and values + __gshared immutable ubyte[num_items * 3] _lookup = [ STATIC_MAP!(GetKeyRedirect, iota), + STATIC_MAP!(GetValRedirect, iota), + STATIC_MAP!(GetKeyOrig, iota) ]; + + // a whole bunch of nonsense to build the tables... + struct KI + { + string k; + ubyte i; + } + struct VI + { + E v; + ubyte i; + } + + alias iota = Iota!(enum_members.length); + enum enum_members = __traits(allMembers, E); + enum by_key = (){ KI[num_items] r = [ STATIC_MAP!(MakeKI, iota) ]; r.qsort!((ref a, ref b) => uni_compare(a.k, b.k)); return r; }(); + enum by_value = (){ VI[num_items] r = [ STATIC_MAP!(MakeVI, iota) ]; r.qsort!((ref a, ref b) => compare(a.v, b.v)); return r; }(); + enum inv_key = (){ KI[num_items] bk = by_key; ubyte[num_items] r; foreach (ubyte i, ref ki; bk) r[ki.i] = i; return r; }(); + enum inv_val = (){ VI[num_items] bv = by_value; ubyte[num_items] r; foreach (ubyte i, ref vi; bv) r[vi.i] = i; return r; }(); + + // calculate the total size of the string buffer + enum total_strings = () { + size_t total = 0; + static foreach (k; enum_members) + total += 2 + k.length + (k.length & 1); + return total; + }(); + + enum MakeKI(ushort i) = KI(trim_key!(enum_members[i]), i); + enum MakeVI(ushort i) = VI(__traits(getMember, E, enum_members[i]), i); + enum GetValue(size_t i) = by_value[i].v; + enum GetKeyRedirect(size_t i) = inv_val[by_key[i].i]; + enum GetValRedirect(size_t i) = inv_key[by_value[i].i]; + enum GetKeyOrig(size_t i) = inv_key[i]; +} + +VoidEnumInfo* make_enum_info(T)(const(char)[] name, const(char)[][] keys, T[] values) +{ + import urt.algorithm; + import urt.hash : fnv1a; + import urt.mem.allocator; + import urt.string; + import urt.string.uni; + import urt.util; + + assert(keys.length == values.length, "keys and values must have the same length"); + assert(keys.length <= ubyte.max, "Too many enum items!"); + + size_t count = keys.length; + + struct VI(T) + { + T v; + ubyte i; + } + + // first we'll sort the keys and values for binary searching + // we need to associate their original indices for the lookup tables + auto ksort = tempAllocator().allocArray!(VI!(const(char)[]))(count); + auto vsort = tempAllocator().allocArray!(VI!T)(count); + foreach (i; 0 .. count) + { + ksort[i] = VI!(const(char)[])(keys[i], cast(ubyte)i); + vsort[i] = VI!T(values[i], cast(ubyte)i); + } + ksort.qsort!((ref a, ref b) => uni_compare(a.v, b.v)); + vsort.qsort!((ref a, ref b) => compare(a.v, b.v)); + + // build the reverse lookup tables + ubyte[] inv_k = tempAllocator().allocArray!ubyte(count); + ubyte[] inv_v = tempAllocator().allocArray!ubyte(count); + foreach (i, ref ki; ksort) + inv_k[ki.i] = cast(ubyte)i; + foreach (i, ref vi; vsort) + inv_v[vi.i] = cast(ubyte)i; + + // count the string memory + size_t total_string; + foreach (i; 0 .. count) + total_string += 2 + keys[i].length + (keys[i].length & 1); + + // calculate the total size + size_t total_size = VoidEnumInfo.sizeof + T.sizeof*count; + total_size += (total_size & 1) + ushort.sizeof*count + count*3; + total_size += (total_size & 1) + total_string; + + // allocate a buffer and assign all the sub-buffers + void[] info = defaultAllocator().alloc(total_size); + VoidEnumInfo* result = cast(VoidEnumInfo*)info.ptr; + T* value_ptr = cast(T*)&result[1]; + char* str_data = cast(char*)&value_ptr[count]; + if (cast(size_t)str_data & 1) + *str_data++ = 0; // align to 2 bytes + ushort* key_ptr = cast(ushort*)str_data; + ubyte* lookup = cast(ubyte*)&key_ptr[count]; + str_data = cast(char*)&lookup[count*3]; + if (cast(size_t)str_data & 1) + *str_data++ = 0; // align to 2 bytes + char* str_ptr = str_data + 2; + + // populate the enum info data + foreach (i; 0 .. count) + { + value_ptr[i] = vsort[i].v; + + // write the string data and store the key offset + const(char)[] key = ksort[i].v; + key_ptr[i] = cast(ushort)(str_ptr - str_data); + writeString(str_ptr, key); + if (key.length & 1) + (str_ptr++)[key.length] = 0; // align to 2 bytes + str_ptr += 2 + key.length; + + lookup[i] = inv_v[ksort[i].i]; + lookup[count + i] = inv_k[vsort[i].i]; + lookup[count*2 + i] = inv_k[i]; + } + + // build and return the object + return new(*result) VoidEnumInfo(cast(ubyte)keys.length, cast(ushort)T.sizeof, fnv1a(cast(ubyte[])name), value_ptr, key_ptr, str_data, lookup, cast(GetFun)&get_value!T); +} + + +private: + +alias GetFun = Variant function(const(void)*, const(VoidEnumInfo)*) pure; + +Variant get_value(T)(const(void)* ptr, const(VoidEnumInfo)* info) + => Variant(*cast(T*)ptr, info); + +import urt.string : trim; +enum trim_key(string key) = key.trim!(c => c == '_'); + +ushort key_length(const(char)* key) pure +{ + if (__ctfe) + { + version (LittleEndian) + return key[-2] | cast(ushort)(key[-1] << 8); + else + return key[-1] | cast(ushort)(key[-2] << 8); + } + else + return *cast(ushort*)(key - 2); +} + +int key_compare(ushort a, const(char)[] b, const(char)* strings) pure +{ + import urt.string.uni : uni_compare; + const(char)* s = strings + a; + return uni_compare(s[0 .. s.key_length], b); +} diff --git a/src/urt/meta/nullable.d b/src/urt/meta/nullable.d index 3d981cc..98f29fd 100644 --- a/src/urt/meta/nullable.d +++ b/src/urt/meta/nullable.d @@ -9,8 +9,8 @@ template Nullable(T) { struct Nullable { - enum T NullValue = null; - T value = NullValue; + enum T null_value = null; + T value = null_value; this(T v) { @@ -18,7 +18,7 @@ template Nullable(T) } bool opCast(T : bool)() const - => value !is NullValue; + => value !is null_value; bool opEquals(typeof(null)) const => value is null; @@ -42,16 +42,16 @@ template Nullable(T) } template Nullable(T) - if (isBoolean!T) + if (is_boolean!T) { struct Nullable { - enum ubyte NullValue = 0xFF; - private ubyte _value = NullValue; + enum ubyte null_value = 0xFF; + private ubyte _value = null_value; this(typeof(null)) { - _value = NullValue; + _value = null_value; } this(T v) { @@ -62,27 +62,27 @@ template Nullable(T) => _value == 1; bool opCast(T : bool)() const - => _value != NullValue; + => _value != null_value; bool opEquals(typeof(null)) const - => _value == NullValue; + => _value == null_value; bool opEquals(T v) const => _value == cast(ubyte)v; void opAssign(typeof(null)) { - _value = NullValue; + _value = null_value; } void opAssign(U)(U v) if (is(U : T)) { - assert(v != NullValue); + assert(v != null_value); _value = cast(ubyte)v; } ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - if (value == NullValue) + if (value == null_value) return formatValue(null, buffer, format, formatArgs); else return formatValue(value, buffer, format, formatArgs); @@ -91,16 +91,16 @@ template Nullable(T) } template Nullable(T) - if (isSomeInt!T) + if (is_some_int!T) { struct Nullable { - enum T NullValue = isSignedInt!T ? T.min : T.max; - T value = NullValue; + enum T null_value = is_signed_int!T ? T.min : T.max; + T value = null_value; this(typeof(null)) { - value = NullValue; + value = null_value; } this(T v) { @@ -108,27 +108,27 @@ template Nullable(T) } bool opCast(T : bool)() const - => value != NullValue; + => value != null_value; bool opEquals(typeof(null)) const - => value == NullValue; + => value == null_value; bool opEquals(T v) const - => value != NullValue && value == v; + => value != null_value && value == v; void opAssign(typeof(null)) { - value = NullValue; + value = null_value; } void opAssign(U)(U v) if (is(U : T)) { - assert(v != NullValue); + assert(v != null_value); value = v; } ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - if (value == NullValue) + if (value == null_value) return formatValue(null, buffer, format, formatArgs); else return formatValue(value, buffer, format, formatArgs); @@ -137,16 +137,16 @@ template Nullable(T) } template Nullable(T) - if (isSomeFloat!T) + if (is_some_float!T) { struct Nullable { - enum T NullValue = T.nan; - T value = NullValue; + enum T null_value = T.nan; + T value = null_value; this(typeof(null)) { - value = NullValue; + value = null_value; } this(T v) { @@ -154,16 +154,16 @@ template Nullable(T) } bool opCast(T : bool)() const - => value !is NullValue; + => value !is null_value; bool opEquals(typeof(null)) const - => value is NullValue; + => value is null_value; bool opEquals(T v) const => value == v; // because nan doesn't compare with anything void opAssign(typeof(null)) { - value = NullValue; + value = null_value; } void opAssign(U)(U v) if (is(U : T)) @@ -173,7 +173,7 @@ template Nullable(T) ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - if (value is NullValue) + if (value is null_value) return formatValue(null, buffer, format, formatArgs); else return formatValue(value, buffer, format, formatArgs); @@ -189,42 +189,42 @@ template Nullable(T) struct Nullable { T value = void; - bool isValue = false; + bool is_value = false; this(typeof(null)) { - isValue = false; + is_value = false; } this(T v) { moveEmplace(v, value); - isValue = true; + is_value = true; } ~this() { - if (isValue) + if (is_value) value.destroy(); } bool opCast(T : bool)() const - => isValue; + => is_value; bool opEquals(typeof(null)) const - => !isValue; + => !is_value; bool opEquals(T v) const - => isValue && value == v; + => is_value && value == v; void opAssign(typeof(null)) { - if (isValue) + if (is_value) value.destroy(); - isValue = false; + is_value = false; } void opAssign(U)(U v) if (is(U : T)) { - if (!isValue) + if (!is_value) moveEmplace(v, value); else value = v; @@ -232,7 +232,7 @@ template Nullable(T) ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - if (!isValue) + if (!is_value) return formatValue(null, buffer, format, formatArgs); else return formatValue(value, buffer, format, formatArgs); @@ -246,39 +246,39 @@ template Nullable(T) struct Nullable { T value; - bool isValue; + bool is_value; this(typeof(null)) { - isValue = false; + is_value = false; } this(T v) { value = v; - isValue = true; + is_value = true; } bool opCast(T : bool)() const - => isValue; + => is_value; bool opEquals(typeof(null)) const - => !isValue; + => !is_value; bool opEquals(T v) const - => isValue && value == v; + => is_value && value == v; void opAssign(typeof(null)) { - isValue = false; + is_value = false; } void opAssign(T v) { value = v; - isValue = true; + is_value = true; } ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - if (!isValue) + if (!is_value) return formatValue(null, buffer, format, formatArgs); else return formatValue(value, buffer, format, formatArgs); diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index faa2941..62bf1a2 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -1,90 +1,160 @@ module urt.meta; +import urt.traits : is_callable, is_enum, EnumType, Unqual; + +nothrow @nogc: alias Alias(alias a) = a; alias Alias(T) = T; alias AliasSeq(TList...) = TList; -template intForWidth(size_t width, bool signed = false) +template Iota(ptrdiff_t start, ptrdiff_t end) +{ + static assert(start <= end, "start must be less than or equal to end"); + alias Iota = AliasSeq!(); + static foreach (i; start .. end) + Iota = AliasSeq!(Iota, i); +} +alias Iota(size_t end) = Iota!(0, end); + +auto make_delegate(Fun)(Fun fun) pure + if (is_callable!Fun) +{ + import urt.traits : is_function_pointer, ReturnType, Parameters; + + static if (is_function_pointer!fun) + { + struct Hack + { + static if (is(Fun : R function(Args) pure nothrow @nogc, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) pure nothrow @nogc => (cast(ReturnType!Fun function(Parameters!Fun args) pure nothrow @nogc)&this)(args); + else static if (is(Fun : R function(Args) nothrow @nogc, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) nothrow @nogc => (cast(ReturnType!Fun function(Parameters!Fun args) nothrow @nogc)&this)(args); + else static if (is(Fun : R function(Args) pure @nogc, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) pure @nogc => (cast(ReturnType!Fun function(Parameters!Fun args) pure @nogc)&this)(args); + else static if (is(Fun : R function(Args) pure nothrow, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) pure nothrow => (cast(ReturnType!Fun function(Parameters!Fun args) pure nothrow)&this)(args); + else static if (is(Fun : R function(Args) pure, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) pure => (cast(ReturnType!Fun function(Parameters!Fun args) pure)&this)(args); + else static if (is(Fun : R function(Args) nothrow, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) nothrow => (cast(ReturnType!Fun function(Parameters!Fun args) nothrow)&this)(args); + else static if (is(Fun : R function(Args) @nogc, R, Args...)) + ReturnType!Fun call(Parameters!Fun args) @nogc => (cast(ReturnType!Fun function(Parameters!Fun args) @nogc)&this)(args); + else static if (is(Fun : R function(Args), R, Args...)) + ReturnType!Fun call(Parameters!Fun args) => (cast(ReturnType!Fun function(Parameters!Fun args))&this)(args); + } + Hack hack; + auto dg = &hack.call; + dg.ptr = fun; + return dg; + } + else static if (is(Fun == delegate)) + return fun; + else + static assert(false, "Unsupported type for make_delegate"); +} + +ulong bit_mask(size_t bits) pure +{ + return (1UL << bits) - 1; +} + +template bit_mask(size_t bits, bool signed = false) { - static if (width <= 8 && !signed) - alias intForWidth = ubyte; - else static if (width <= 8 && signed) - alias intForWidth = byte; - else static if (width <= 16 && !signed) - alias intForWidth = ushort; - else static if (width <= 16 && signed) - alias intForWidth = short; - else static if (width <= 32 && !signed) - alias intForWidth = uint; - else static if (width <= 32 && signed) - alias intForWidth = int; - else static if (width <= 64 && !signed) - alias intForWidth = ulong; - else static if (width <= 64 && signed) - alias intForWidth = long; + static assert(bits <= 64, "bit_mask only supports up to 64 bits"); + static if (bits == 64) + enum IntForWidth!(64, signed) bit_mask = ~0UL; + else + enum IntForWidth!(bits, signed) bit_mask = (1UL << bits) - 1; } -template staticMap(alias fun, args...) +template IntForWidth(size_t bits, bool signed = false) { - alias staticMap = AliasSeq!(); + static if (bits <= 8 && !signed) + alias IntForWidth = ubyte; + else static if (bits <= 8 && signed) + alias IntForWidth = byte; + else static if (bits <= 16 && !signed) + alias IntForWidth = ushort; + else static if (bits <= 16 && signed) + alias IntForWidth = short; + else static if (bits <= 32 && !signed) + alias IntForWidth = uint; + else static if (bits <= 32 && signed) + alias IntForWidth = int; + else static if (bits <= 64 && !signed) + alias IntForWidth = ulong; + else static if (bits <= 64 && signed) + alias IntForWidth = long; +} + +alias TypeForOp(string op, U) = typeof(mixin(op ~ "U()")); +alias TypeForOp(string op, A, B) = typeof(mixin("A()" ~ op ~ "B()")); + +template STATIC_MAP(alias fun, args...) +{ + alias STATIC_MAP = AliasSeq!(); static foreach (arg; args) - staticMap = AliasSeq!(staticMap, fun!arg); + STATIC_MAP = AliasSeq!(STATIC_MAP, fun!arg); +} + +template STATIC_UNROLL(alias array) +{ + static if (is(typeof(array) : T[], T)) + { + alias STATIC_UNROLL = AliasSeq!(); + static foreach (i; 0 .. array.length) + STATIC_UNROLL = AliasSeq!(STATIC_UNROLL, array[i]); + } + else + static assert(false, "STATIC_UNROLL requires an array"); } -template staticIndexOf(args...) +template STATIC_FILTER(alias filter, args...) +{ + alias STATIC_FILTER = AliasSeq!(); + static foreach (arg; args) + static if (filter!arg) + STATIC_FILTER = AliasSeq!(STATIC_FILTER, arg); +} + +template static_index_of(args...) if (args.length >= 1) { - enum staticIndexOf = { + enum static_index_of = { static foreach (idx, arg; args[1 .. $]) - static if (isSame!(args[0], arg)) + static if (is_same!(args[0], arg)) // `if (__ctfe)` is redundant here but avoids the "Unreachable code" warning. if (__ctfe) return idx; return -1; }(); } -template InterleaveSeparator(alias sep, Args...) +template INTERLEAVE_SEPARATOR(alias sep, Args...) { - alias InterleaveSeparator = AliasSeq!(); + alias INTERLEAVE_SEPARATOR = AliasSeq!(); static foreach (i, A; Args) static if (i > 0) - InterleaveSeparator = AliasSeq!(InterleaveSeparator, sep, A); + INTERLEAVE_SEPARATOR = AliasSeq!(INTERLEAVE_SEPARATOR, sep, A); else - InterleaveSeparator = AliasSeq!(A); + INTERLEAVE_SEPARATOR = AliasSeq!(A); } -template EnumKeys(E) -{ - static assert(is(E == enum), "EnumKeys only works with enums!"); - __gshared immutable string[EnumStrings.length] EnumKeys = [ EnumStrings ]; - private alias EnumStrings = __traits(allMembers, E); -} - -E enumFromString(E)(const(char)[] key) -if (is(E == enum)) -{ - foreach (i, k; EnumKeys!E) - if (key[] == k[]) - return cast(E)i; - return cast(E)-1; -} - private: -template isSame(alias a, alias b) +template is_same(alias a, alias b) { static if (!is(typeof(&a && &b)) // at least one is an rvalue - && __traits(compiles, { enum isSame = a == b; })) // c-t comparable - enum isSame = a == b; + && __traits(compiles, { enum is_same = a == b; })) // c-t comparable + enum is_same = a == b; else - enum isSame = __traits(isSame, a, b); + enum is_same = __traits(isSame, a, b); } // TODO: remove after https://github.com/dlang/dmd/pull/11320 and https://issues.dlang.org/show_bug.cgi?id=21889 are fixed -template isSame(A, B) +template is_same(A, B) { - enum isSame = is(A == B); + enum is_same = is(A == B); } diff --git a/src/urt/meta/tuple.d b/src/urt/meta/tuple.d index a2fe66f..f29e758 100644 --- a/src/urt/meta/tuple.d +++ b/src/urt/meta/tuple.d @@ -64,20 +64,20 @@ template Tuple(Specs...) // Returns Specs for a subtuple this[from .. to] preserving field // names if any. - alias sliceSpecs(size_t from, size_t to) = staticMap!(expandSpec, fieldSpecs[from .. to]); + alias sliceSpecs(size_t from, size_t to) = STATIC_MAP!(expandSpec, fieldSpecs[from .. to]); struct Tuple { nothrow @nogc: - alias Types = staticMap!(extractType, fieldSpecs); + alias Types = STATIC_MAP!(extractType, fieldSpecs); private alias _Fields = Specs; /** * The names of the `Tuple`'s components. Unnamed fields have empty names. */ - alias fieldNames = staticMap!(extractName, fieldSpecs); + alias fieldNames = STATIC_MAP!(extractName, fieldSpecs); /** * Use `t.expand` for a `Tuple` `t` to expand it into its @@ -369,7 +369,7 @@ template Tuple(Specs...) } import std.range : roundRobin, iota; - alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!( + alias NewTupleT = Tuple!(STATIC_MAP!(GetItem, aliasSeqOf!( roundRobin(iota(nT), iota(nT, 2*nT))))); return *(() @trusted => cast(NewTupleT*)&this)(); } @@ -545,7 +545,7 @@ template Tuple(Specs...) } else { - formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand)); + formattedWrite(sink, fmt.nested, STATIC_MAP!(sharedToString, this.expand)); } } else if (fmt.spec == 's') diff --git a/src/urt/package.d b/src/urt/package.d index 354632b..f875816 100644 --- a/src/urt/package.d +++ b/src/urt/package.d @@ -16,20 +16,295 @@ public import urt.processor; public import urt.meta : Alias, AliasSeq; public import urt.util : min, max, swap; +version (Windows) +{ + private enum crt = __traits(getTargetInfo, "cppRuntimeLibrary"); + static if (crt) + pragma(lib, crt); + pragma(lib, "kernel32"); +} + private: -pragma(crt_constructor) -void crt_bootup() +// ────────────────────────────────────────────────────────────────────── +// C entry point — replaces druntime's rt/dmain2.d +// +// We initialize uRT subsystems, scan the PE .minfo section for +// ModuleInfo records, run module constructors, call the D main +// (_Dmain), then run destructors. +// ────────────────────────────────────────────────────────────────────── + +extern (C) int _Dmain(scope string[] args) @nogc; + +extern (C) int main(int argc, char** argv) nothrow @nogc @trusted { - import urt.time : initClock; - initClock(); + import urt.mem; - import urt.rand; - initRand(); + import urt.mem.string : init_string_heap, deinit_string_heap; + init_string_heap(ushort.max); + + import urt.time : init_clock; + init_clock(); - import urt.dbg : setupAssertHandler; - setupAssertHandler(); + import urt.rand; + init_rand(); import urt.string.string : initStringAllocators; initStringAllocators(); + + // Convert C argv to D string[] (zero-copy, just wrap pointers) + string* args = cast(string*)alloca(string.sizeof * argc); + string[] d_args = args[0 .. argc]; + foreach (i; 0 .. argc) + d_args[i] = cast(string)argv[i][0 .. argv[i].strlen]; + + // Run module constructors + auto modules = get_module_infos(); + run_module_ctors(modules); + + // Run unit tests (in unittest builds) and exit + version (unittest) + { + import urt.internal.stdc : fprintf, stderr, fflush; + import urt.internal.stdc : exit; + + size_t executed, passed; + foreach (m; modules) + { + if (m is null) continue; + if (auto fp = cast(void function() nothrow @nogc) m.unitTest) + { + auto mname2 = m.name; + fprintf(stderr, " running: %.*s ... ", cast(int) mname2.length, mname2.ptr); + fflush(stderr); + ++executed; + if (run_test(fp)) + ++passed; + else + fprintf(stderr, "FAIL\n"); + } + } + + if (executed > 0) + fprintf(stderr, "%d/%d modules passed unittests\n", + cast(int) passed, cast(int) executed); + else + fprintf(stderr, "No unittest functions found!\n"); + + fflush(stderr); + run_module_dtors(modules); + exit(executed > 0 && passed == executed ? 0 : 1); + } + + int result = call_dmain(d_args); + + // Run module destructors in reverse order + run_module_dtors(modules); + + import urt.internal.stdc : fflush, stdout; + fflush(stdout); + + deinit_string_heap(); + + return result; +} + +// Separated from main() because DMD cannot mix alloca() and exception handling. + +bool run_test(void function() nothrow @nogc test) nothrow @nogc @trusted +{ + import urt.internal.stdc : fprintf, stderr; + try + { + test(); + fprintf(stderr, "ok\n"); + return true; + } + catch (Throwable t) + { + auto msg = t.msg; + fprintf(stderr, "%.*s\n", cast(int) msg.length, msg.ptr); + return false; + } +} + +int call_dmain(scope string[] args) nothrow @nogc @trusted +{ + int result; + try + result = _Dmain(args); + catch (Throwable t) + { + import urt.internal.stdc : fprintf, stderr; + auto msg = t.msg; + fprintf(stderr, "Uncaught exception: %.*s\n", cast(int) msg.length, msg.ptr); + result = 1; + } + return result; +} + +// ────────────────────────────────────────────────────────────────────── +// Module constructor/destructor execution +// ────────────────────────────────────────────────────────────────────── + +void run_module_ctors(immutable(ModuleInfo*)[] modules) nothrow @nogc @trusted +{ + alias Fn = void function() nothrow @nogc; + // Order-independent constructors first + foreach (m; modules) + { + if (m is null) + continue; + if (auto fp = cast(Fn) m.ictor) + fp(); + } + // Then regular constructors + foreach (m; modules) + { + if (m is null) + continue; + if (auto fp = cast(Fn) m.ctor) + fp(); + } + // TLS constructors + foreach (m; modules) + { + if (m is null) + continue; + if (auto fp = cast(Fn) m.tlsctor) + fp(); + } +} + +void run_module_dtors(immutable(ModuleInfo*)[] modules) nothrow @nogc @trusted +{ + alias Fn = void function() nothrow @nogc; + // TLS destructors (reverse) + foreach_reverse (m; modules) + { + if (m is null) + continue; + if (auto fp = cast(Fn) m.tlsdtor) + fp(); + } + // Regular destructors (reverse) + foreach_reverse (m; modules) + { + if (m is null) + continue; + if (auto fp = cast(Fn) m.dtor) + fp(); + } +} + +// ────────────────────────────────────────────────────────────────────── +// PE .minfo section scanning — finds compiler-generated ModuleInfo pointers. +// Inline PE parsing to avoid core.sys.windows struct __init dependencies. +// ────────────────────────────────────────────────────────────────────── + +version (Windows) + extern (C) extern __gshared ubyte __ImageBase; + +version (linux) +{ + // Stashed by _d_dso_registry in object.d from ELF .init_array callback (DMD) + extern (C) extern __gshared immutable(ModuleInfo*)* _elf_minfo_beg; + extern (C) extern __gshared immutable(ModuleInfo*)* _elf_minfo_end; + + version (LDC) + { + // LDC emits ModuleInfo pointers into the __minfo ELF section but does + // not generate .init_array calls to _d_dso_registry. The linker + // generates __start___minfo / __stop___minfo boundary symbols for us. + // Declared as the element type (not pointer-to) so &symbol yields the + // section address with the correct type for slicing. + extern (C) extern __gshared immutable(ModuleInfo*) __start___minfo; + extern (C) extern __gshared immutable(ModuleInfo*) __stop___minfo; + } +} + +immutable(ModuleInfo*)[] get_module_infos() nothrow @nogc @trusted +{ + version (Windows) + { + auto section = find_pe_section(cast(void*)&__ImageBase, ".minfo"); + if (!section.length) + return null; + return (cast(immutable(ModuleInfo*)*)section.ptr)[0 .. section.length / (void*).sizeof]; + } + else version (linux) + { + // DMD path: _d_dso_registry stashed the .minfo section boundaries + if (_elf_minfo_beg !is null) + return _elf_minfo_beg[0 .. _elf_minfo_end - _elf_minfo_beg]; + + // LDC path: read __minfo section via linker-generated symbols + version (LDC) + { + if (&__start___minfo !is null && &__stop___minfo !is null) + return (&__start___minfo)[0 .. &__stop___minfo - &__start___minfo]; + } + + return null; + } + else + { + return null; + } +} + +version (Windows) +void[] find_pe_section(void* image_base, string name) nothrow @nogc @trusted +{ + if (name.length > 8) return null; + + auto base = cast(ubyte*) image_base; + + // DOS header: e_magic at offset 0 (2 bytes), e_lfanew at offset 0x3C (4 bytes) + if (base[0] != 0x4D || base[1] != 0x5A) // 'MZ' + return null; + + auto lfanew = *cast(int*)(base + 0x3C); + auto pe = base + lfanew; + + // PE signature check + if (pe[0] != 'P' || pe[1] != 'E' || pe[2] != 0 || pe[3] != 0) + return null; + + // COFF file header starts at pe+4 + // NumberOfSections at offset 2 (2 bytes) + // SizeOfOptionalHeader at offset 16 (2 bytes) + auto file_header = pe + 4; + ushort num_sections = *cast(ushort*)(file_header + 2); + ushort opt_header_size = *cast(ushort*)(file_header + 16); + + // Section headers start after optional header + auto sections = file_header + 20 + opt_header_size; + + // Each IMAGE_SECTION_HEADER is 40 bytes: + // Name[8] at offset 0 + // VirtualSize at offset 8 + // VirtualAddress at offset 12 + foreach (i; 0 .. num_sections) + { + auto sec = sections + i * 40; + auto sec_name = (cast(char*) sec)[0 .. 8]; + + bool match = true; + foreach (j; 0 .. name.length) + { + if (sec_name[j] != name[j]) + { + match = false; + break; + } + } + if (match && (name.length == 8 || sec_name[name.length] == 0)) + { + auto virtual_size = *cast(uint*)(sec + 8); + auto virtual_address = *cast(uint*)(sec + 12); + return (base + virtual_address)[0 .. virtual_size]; + } + } + return null; } diff --git a/src/urt/processor.d b/src/urt/processor.d index 4acc356..f35792d 100644 --- a/src/urt/processor.d +++ b/src/urt/processor.d @@ -1,9 +1,24 @@ module urt.processor; +enum Endian : byte +{ + Native = -1, // specifies the native/working endian + Little = 0, + Big = 1 +} + version (LittleEndian) +{ enum LittleEndian = true; + enum BigEndian = false; + enum Endian proc_endian = Endian.Little; +} else +{ enum LittleEndian = false; + enum BigEndian = true; + enum Endian proc_endian = Endian.Big; +} version (X86_64) { diff --git a/src/urt/rand.d b/src/urt/rand.d index b4b0f1e..c54cd25 100644 --- a/src/urt/rand.d +++ b/src/urt/rand.d @@ -55,7 +55,7 @@ private: rng.state = rng.state * PCG_DEFAULT_MULTIPLIER_64 + rng.inc; } - package void initRand() + package void init_rand() { import urt.time; srand(getTime().ticks, cast(size_t)&globalRand); diff --git a/src/urt/range/package.d b/src/urt/range/package.d index 06de7f0..94af160 100644 --- a/src/urt/range/package.d +++ b/src/urt/range/package.d @@ -8,23 +8,23 @@ template map(fun...) { /** Params: - r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + r = an $(REF_ALTTEXT input range, is_input_range, std,range,primitives) Returns: A range with each fun applied to all the elements. If there is more than one fun, the element type will be `Tuple` containing one element for each fun. */ auto map(Range)(Range r) - if (isInputRange!(Unqual!Range)) + if (is_input_range!(Unqual!Range)) { - import urt.meta : AliasSeq, staticMap; + import urt.meta : AliasSeq, STATIC_MAP; alias RE = ElementType!(Range); static if (fun.length > 1) { import std.functional : adjoin; - import urt.meta : staticIndexOf; + import urt.meta : static_index_of; - alias _funs = staticMap!(unaryFun, fun); + alias _funs = STATIC_MAP!(unaryFun, fun); alias _fun = adjoin!_funs; // Once https://issues.dlang.org/show_bug.cgi?id=5710 is fixed @@ -56,7 +56,7 @@ private struct MapResult(alias fun, Range) alias R = Unqual!Range; R _input; - static if (isBidirectionalRange!R) + static if (is_bidirectional_range!R) { @property auto ref back()() { @@ -101,7 +101,7 @@ private struct MapResult(alias fun, Range) return fun(_input.front); } - static if (isRandomAccessRange!R) + static if (is_random_access_range!R) { static if (is(typeof(Range.init[ulong.max]))) private alias opIndex_t = ulong; @@ -147,7 +147,7 @@ private struct MapResult(alias fun, Range) } } - static if (isForwardRange!R) + static if (is_forward_range!R) { @property auto save() { @@ -160,9 +160,9 @@ private struct MapResult(alias fun, Range) template reduce(fun...) if (fun.length >= 1) { - import urt.meta : staticMap; + import urt.meta : STATIC_MAP; - alias binfuns = staticMap!(binaryFun, fun); + alias binfuns = STATIC_MAP!(binaryFun, fun); static if (fun.length > 1) import urt.meta.tuple : tuple, isTuple; @@ -182,24 +182,21 @@ template reduce(fun...) Returns: the final result of the accumulator applied to the iterable - - Throws: `Exception` if `r` is empty +/ auto reduce(R)(R r) if (isIterable!R) { - import std.exception : enforce; - alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); - alias Args = staticMap!(ReduceSeedType!E, binfuns); + alias E = Select!(is_input_range!R, ElementType!R, ForeachType!R); + alias Args = STATIC_MAP!(ReduceSeedType!E, binfuns); - static if (isInputRange!R) + static if (is_input_range!R) { // no need to throw if range is statically known to be non-empty static if (!__traits(compiles, { static assert(r.length > 0); })) - enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); + assert(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); Args result = r.front; r.popFront(); @@ -245,7 +242,7 @@ template reduce(fun...) private auto reducePreImpl(R, Args...)(R r, ref Args args) { - alias Result = staticMap!(Unqual, Args); + alias Result = STATIC_MAP!(Unqual, Args); static if (is(Result == Args)) alias result = args; else @@ -259,7 +256,7 @@ template reduce(fun...) import std.algorithm.internal : algoFormat; static assert(Args.length == fun.length, algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length)); - alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); + alias E = Select!(is_input_range!R, ElementType!R, ForeachType!R); static if (mustInitialize) bool initialized = false; @@ -279,7 +276,7 @@ template reduce(fun...) static if (mustInitialize) if (initialized == false) { - import core.internal.lifetime : emplaceRef; + import urt.lifetime : emplaceRef; foreach (i, f; binfuns) emplaceRef!(Args[i])(args[i], e); initialized = true; @@ -309,7 +306,7 @@ template fold(fun...) { /** Params: - r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to fold + r = the $(REF_ALTTEXT input range, is_input_range, std,range,primitives) to fold seeds = the initial values of each accumulator (optional), one for each predicate Returns: Either the accumulated result for a single predicate, or a @@ -323,7 +320,7 @@ template fold(fun...) } else { - import std.typecons : tuple; + import urt.meta : tuple; return reduce!fun(tuple(seeds), r); } } diff --git a/src/urt/range/primitives.d b/src/urt/range/primitives.d index b9789cd..f5ddec8 100644 --- a/src/urt/range/primitives.d +++ b/src/urt/range/primitives.d @@ -3,44 +3,44 @@ module urt.range.primitives; import urt.traits; -enum bool isInputRange(R) = +enum bool is_input_range(R) = is(typeof(R.init) == R) && is(typeof((R r) { return r.empty; } (R.init)) == bool) && (is(typeof((return ref R r) => r.front)) || is(typeof(ref (return ref R r) => r.front))) && !is(typeof((R r) { return r.front; } (R.init)) == void) && is(typeof((R r) => r.popFront)); -enum bool isInputRange(R, E) = - .isInputRange!R && isQualifierConvertible!(ElementType!R, E); +enum bool is_input_range(R, E) = + .is_input_range!R && isQualifierConvertible!(ElementType!R, E); -enum bool isForwardRange(R) = isInputRange!R +enum bool is_forward_range(R) = is_input_range!R && is(typeof((R r) { return r.save; } (R.init)) == R); -enum bool isForwardRange(R, E) = - .isForwardRange!R && isQualifierConvertible!(ElementType!R, E); +enum bool is_forward_range(R, E) = + .is_forward_range!R && isQualifierConvertible!(ElementType!R, E); -enum bool isBidirectionalRange(R) = isForwardRange!R +enum bool is_bidirectional_range(R) = is_forward_range!R && is(typeof((R r) => r.popBack)) && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back))) && is(typeof(R.init.back.init) == ElementType!R); -enum bool isBidirectionalRange(R, E) = - .isBidirectionalRange!R && isQualifierConvertible!(ElementType!R, E); +enum bool is_bidirectional_range(R, E) = + .is_bidirectional_range!R && isQualifierConvertible!(ElementType!R, E); -enum bool isRandomAccessRange(R) = - is(typeof(lvalueOf!R[1]) == ElementType!R) +enum bool is_random_access_range(R) = + is(typeof(lvalue_of!R[1]) == ElementType!R) && !(isAutodecodableString!R && !isAggregateType!R) - && isForwardRange!R - && (isBidirectionalRange!R || isInfinite!R) + && is_forward_range!R + && (is_bidirectional_range!R || isInfinite!R) && (hasLength!R || isInfinite!R) - && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1])) - || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R)); -enum bool isRandomAccessRange(R, E) = - .isRandomAccessRange!R && isQualifierConvertible!(ElementType!R, E); + && (isInfinite!R || !is(typeof(lvalue_of!R[$ - 1])) + || is(typeof(lvalue_of!R[$ - 1]) == ElementType!R)); +enum bool is_random_access_range(R, E) = + .is_random_access_range!R && isQualifierConvertible!(ElementType!R, E); // is this in the wrong place? should this be a general traits for arrays and stuff too? template ElementType(R) { - static if (is(typeof(lvalueOf!R.front))) - alias ElementType = typeof(lvalueOf!R.front); + static if (is(typeof(lvalue_of!R.front))) + alias ElementType = typeof(lvalue_of!R.front); else static if (is(R : T[], T)) alias ElementType = T; else diff --git a/src/urt/result.d b/src/urt/result.d index 564b15c..06b16d9 100644 --- a/src/urt/result.d +++ b/src/urt/result.d @@ -3,18 +3,10 @@ module urt.result; nothrow @nogc: -enum InternalCode -{ - Success = 0, - BufferTooSmall, - InvalidParameter, - Unsupported -} - struct Result { nothrow @nogc: - enum Success = Result(); + enum success = Result(); uint systemCode = 0; @@ -27,49 +19,65 @@ nothrow @nogc: => systemCode != 0; } +struct StringResult +{ +nothrow @nogc: + enum success = StringResult(); + + const(char)[] message = null; + + bool opCast(T : bool)() const + => message is null; + + bool succeeded() const + => message is null; + bool failed() const + => message !is null; +} + +// TODO: should we have a way to convert Result to StringResult, so we can format error messages? + version (Windows) { - import core.sys.windows.windows; + import urt.internal.sys.windows; - Result InternalResult(InternalCode code) + enum InternalResult : Result { - switch (code) - { - case InternalCode.Success: - return Result(); - case InternalCode.BufferTooSmall: - return Result(ERROR_INSUFFICIENT_BUFFER); - case InternalCode.InvalidParameter: - return Result(ERROR_INVALID_PARAMETER); - default: - return Result(ERROR_INVALID_FUNCTION); // InternalCode.Unsupported - } + success = Result.success, + buffer_too_small = Result(ERROR_INSUFFICIENT_BUFFER), + invalid_parameter = Result(ERROR_INVALID_PARAMETER), + data_error = Result(ERROR_INVALID_DATA), + unsupported = Result(ERROR_INVALID_FUNCTION), } - Result Win32Result(uint err) + Result win32_result(uint err) => Result(err); + Result getlasterror_result() + => Result(GetLastError()); } else version (Posix) { - import core.stdc.errno; - Result InternalResult(InternalCode code) + import urt.internal.stdc; + + extern (C) private int* __errno_location() nothrow @nogc; + + @property int errno() nothrow @nogc @trusted + { + return *__errno_location(); + } + + enum InternalResult : Result { - switch (code) - { - case InternalCode.Success: - return Result(); - case InternalCode.BufferTooSmall: - return Result(ERANGE); - case InternalCode.InvalidParameter: - return Result(EINVAL); - default: - return Result(ENOTSUP); // InternalCode.Unsupported - } + success = Result.success, + buffer_too_small = Result(ERANGE), + invalid_parameter = Result(EINVAL), + data_error = Result(EILSEQ), + unsupported = Result(ENOTSUP), } - Result PosixResult(int err) + Result posix_result(int err) => Result(err); - Result ErrnoResult() + Result errno_result() => Result(errno); } diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index 33462df..8656a2c 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -1,14 +1,22 @@ module urt.si.quantity; +import urt.meta : TypeForOp; import urt.si.unit; +import urt.traits; nothrow @nogc: -alias VarQuantity = Quantity!(double); +alias VarQuantity = Quantity!double; alias Scalar = Quantity!(double, ScaledUnit()); alias Metres = Quantity!(double, ScaledUnit(Metre)); alias Seconds = Quantity!(double, ScaledUnit(Second)); +alias Volts = Quantity!(double, ScaledUnit(Volt)); +alias Amps = Quantity!(double, ScaledUnit(Ampere)); +alias AmpHours = Quantity!(double, AmpereHour); +alias Watts = Quantity!(double, ScaledUnit(Watt)); +alias Kilowatts = Quantity!(double, Kilowatt); +alias WattHours = Quantity!(double, WattHour); struct Quantity(T, ScaledUnit _unit = ScaledUnit(uint.max)) @@ -20,7 +28,7 @@ nothrow @nogc: enum Dynamic = _unit.pack == uint.max; enum IsCompatible(ScaledUnit U) = _unit.unit == U.unit; - T value; + T value = 0; static if (Dynamic) ScaledUnit unit; @@ -31,11 +39,20 @@ nothrow @nogc: if (is(U : T)) => unit.unit == compatibleWith.unit.unit; - this(T value) pure + static if (Dynamic) { - static if (Dynamic) - this.unit = ScaledUnit(); - this.value = value; + this(T value, ScaledUnit unit = ScaledUnit()) pure + { + this.unit = unit; + this.value = value; + } + } + else + { + this(T value) pure + { + this.value = value; + } } this(U, ScaledUnit _U)(Quantity!(U, _U) b) pure @@ -49,10 +66,10 @@ nothrow @nogc: else { static if (b.Dynamic) - assert(isCompatible(b), "Incompatible unit!"); + assert(isCompatible(b), "Incompatible units!"); else - static assert(IsCompatible!_U, "Incompatible unit: ", unit, " and ", b.unit); - value = adjustScale(b); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); + value = adjust_scale(b); } } @@ -61,13 +78,14 @@ nothrow @nogc: static if (Dynamic) unit = Scalar; else - static assert(unit == Unit(), "Incompatible unit: ", unit, " and Scalar"); + static assert(unit == Unit(), "Incompatible units: ", unit.toString, " and Scalar"); this.value = value; } void opAssign(U, ScaledUnit _U)(Quantity!(U, _U) b) pure - if (is(U : T)) { + static assert(__traits(compiles, value = b.value), "cannot implicitly convert ScaledUnit of type `", U, "` to `", T, "`"); + static if (Dynamic) { unit = b.unit; @@ -76,37 +94,45 @@ nothrow @nogc: else { static if (b.Dynamic) - assert(isCompatible(b), "Incompatible unit!"); + assert(isCompatible(b), "Incompatible units!"); else - static assert(IsCompatible!_U, "Incompatible unit: ", unit, " and ", b.unit); - value = adjustScale(b); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); + value = adjust_scale(b); } } - auto opBinary(string op, U)(U value) const pure - if ((op == "+" || op == "-") && is(U : T)) + auto opUnary(string op)() const pure + if (op == "+" || op == "-") { + alias RT = Quantity!(TypeForOp!(op, T), _unit); static if (Dynamic) - assert(unit == Scalar); + return RT(mixin(op ~ "value"), unit); else - static assert(unit == Unit(), "Incompatible unit: ", unit, " and Scalar"); - return mixin("this.value " ~ op ~ " value"); + return RT(mixin(op ~ "value")); } + auto opBinary(string op, U)(U value) const pure + if ((op == "+" || op == "-") && is(U : T)) + => opBinary!op(Quantity!(U, ScaledUnit())(value)); + auto opBinary(string op, U, ScaledUnit _U)(Quantity!(U, _U) b) const pure if ((op == "+" || op == "-") && is(U : T)) { + // TODO: what unit should be result take? + // for float T, I reckon maybe the MAX exponent? + // for int types... we need to do some special shit to manage overflows! + // HACK: for now, we just scale to the left-hand size... :/ static if (!Dynamic && !b.Dynamic && unit == Unit() && b.unit == Unit()) return mixin("value " ~ op ~ " b.value"); else { static if (Dynamic || b.Dynamic) - assert(isCompatible(b), "Incompatible unit!"); + assert(isCompatible(b), "Incompatible units!"); else - static assert(IsCompatible!_U, "Incompatible unit: ", unit, " and ", b.unit); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); - This r; - r.value = mixin("value " ~ op ~ " adjustScale(b)"); + Quantity!(TypeForOp!(op, T, U), _unit) r; + r.value = mixin("value " ~ op ~ " adjust_scale(b)"); static if (Dynamic) r.unit = unit; return r; @@ -120,11 +146,12 @@ nothrow @nogc: return mixin("this.value " ~ op ~ " value"); else { - This r; - r.value = mixin("this.value " ~ op ~ " value"); + alias RT = TypeForOp!(op, T, U); + RT v = mixin("this.value " ~ op ~ " value"); static if (Dynamic) - r.unit = unit; - return r; + return Quantity!RT(v, unit); + else + return Quantity!(RT, unit)(v); } } @@ -135,33 +162,63 @@ nothrow @nogc: return mixin("value " ~ op ~ " b.value"); else { + // TODO: if the unit product is invalid, then we need to decide a target scaling factor... static if (Dynamic || b.Dynamic) - { - Quantity!T r; - r.unit = mixin("unit " ~ op ~ " b.unit"); - } + const u = mixin("unit " ~ op ~ " b.unit"); else - Quantity!(T, mixin("unit " ~ op ~ " b.unit")) r; + enum u = mixin("unit " ~ op ~ " b.unit"); - // TODO: if the unit product is invalid, then we apply the scaling factor... - // ... but which side should we scale to? probably the left I guess... + alias RT = TypeForOp!(op, T, U); + RT v = mixin("value " ~ op ~ " b.value"); - r.value = mixin("value " ~ op ~ " b.value"); - return r; + static if (Dynamic || b.Dynamic) + return Quantity!RT(v, u); + else + return Quantity!(RT, u)(v); } } - void opOpAssign(string op, U)(U value) pure - if (is(U : T)) + void opOpAssign(string op)(T value) pure { + // TODO: in D; ubyte += int is allowed, so we should cast the result to T this = opBinary!op(value); } void opOpAssign(string op, U, ScaledUnit _U)(Quantity!(U, _U) b) pure { + // TODO: in D; ubyte += int is allowed, so we should cast the result to T this = opBinary!op(b); } + bool opCast(T : bool)() const pure + => value != 0; + + // not clear if this should return the raw value, or the normalised value...? +// T opCast(T)() const pure +// if (is_some_float!T || is_some_int!T) +// { +// assert(unit.pack == 0, "Non-scalar unit can't cast to scalar"); +// assert(false, "TODO: should we be applying the scale to this result?"); +// return cast(T)value; +// } + + T opCast(T)() const pure + if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) + { + static if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) + { + T r; + static if (Dynamic || T.Dynamic) + assert(isCompatible(r), "Incompatible units!"); + else + static assert(IsCompatible!_U, "Incompatible units: ", r.unit.toString, " and ", unit.toString); + r.value = cast(U)r.adjust_scale(this); + static if (T.Dynamic) + r.unit = unit; + return r; + } + } + bool opEquals(U)(U value) const pure if (is(U : T)) { @@ -185,14 +242,14 @@ nothrow @nogc: // can't compare mismatch unit types... i think? static if (Dynamic || rh.Dynamic) - assert(isCompatible(rh), "Incompatible unit!"); + assert(isCompatible(rh), "Incompatible units!"); else - static assert(IsCompatible!_U, "Incompatible unit: ", unit, " and ", rh.unit); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", rh.unit.toString); // TODO: meeting in the middle is only better if the signs are opposite // otherwise we should just scale to the left... static if (Dynamic && rh.Dynamic) - { + {{ // if the scale values are both dynamic, it should be more precise if we meet in the middle... auto lScale = unit.scale(); auto lTrans = unit.offset(); @@ -200,9 +257,9 @@ nothrow @nogc: auto rTrans = rh.unit.offset(); lhs = lhs*lScale + lTrans; rhs = rhs*rScale + rTrans; - } + }} else - rhs = adjustScale(rh); + rhs = adjust_scale(rh); compare: static if (epsilon == 0) @@ -222,41 +279,136 @@ nothrow @nogc: } } -private: - T adjustScale(U, ScaledUnit _U)(Quantity!(U, _U) b) const pure + auto normalise() const pure { static if (Dynamic) { - auto lScale = unit.scale!true(); - auto lTrans = unit.offset!true(); + Quantity!T r; + r.unit = ScaledUnit(unit.unit); } else - { - enum lScale = unit.scale!true(); - enum lTrans = unit.offset!true(); - } - static if (b.Dynamic) - { - auto rScale = b.unit.scale(); - auto rTrans = b.unit.offset(); - } + Quantity!(T, ScaledUnit(unit.unit)) r; + r.value = r.adjust_scale(this); + return r; + } + + Quantity!Ty adjust_scale(Ty = T)(ScaledUnit su) const pure + { + Quantity!Ty r; + r.unit = su; + assert(r.isCompatible(this), "Incompatible units!"); + if (su == unit) + r.value = cast(Ty)this.value; else + r.value = r.adjust_scale(this); + return r; + } + + import urt.string.format : FormatArg; + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const + { + import urt.conv : format_float; + + double v = value; + ScaledUnit u = unit; + + if (u.pack) { - enum rScale = b.unit.scale(); - enum rTrans = b.unit.offset(); + // round upward to the nearest ^3 + if (u.siScale) + { + int x = u.exp; + if (u.unit.pack == 0) + { + if (x == -3) + { + v *= 0.1; + u = ScaledUnit(Unit(), x + 1); + } + else if (x != -2) + { + v *= u.scale(); + u = ScaledUnit(); + } + } + else + { + x = (x + 33) % 3; + if (x != 0) + { + u = ScaledUnit(u.unit, u.exp + (3 - x)); + if (x == 1) + v *= 0.01; + else + v *= 0.1; + } + } + } } - static if (Dynamic || b.Dynamic) + ptrdiff_t l = format_float(v, buffer); + if (l < 0) + return l; + + if (u.pack) { - auto scale = lScale*rScale; - auto trans = lTrans + lScale*rTrans; + ptrdiff_t l2 = u.toString(buffer[l .. $], null, null); + if (l2 < 0) + return l2; + l += l2; } + + return l; + } + + ptrdiff_t fromString(const(char)[] s) + { + return -1; + } + +private: + T adjust_scale(U, ScaledUnit _U)(Quantity!(U, _U) b) const pure + { + static if (!Dynamic && !b.Dynamic && unit == b.unit) + return cast(T)b.value; else { - enum scale = lScale*rScale; - enum trans = lTrans + lScale*rTrans; + if (unit == b.unit) + return cast(T)b.value; + + static if (Dynamic) + { + auto lScale = unit.scale!true(); + auto lTrans = unit.offset!true(); + } + else + { + enum lScale = unit.scale!true(); + enum lTrans = unit.offset!true(); + } + static if (b.Dynamic) + { + auto rScale = b.unit.scale(); + auto rTrans = b.unit.offset(); + } + else + { + enum rScale = b.unit.scale(); + enum rTrans = b.unit.offset(); + } + + static if (Dynamic || b.Dynamic) + { + auto scale = lScale*rScale; + auto trans = lTrans + lScale*rTrans; + } + else + { + enum scale = lScale*rScale; + enum trans = lTrans + lScale*rTrans; + } + return cast(T)(b.value*scale + trans); } - return cast(T)(b.value*scale + trans); } } @@ -345,3 +497,49 @@ unittest assert(DegreesC(100).opEquals!epsilon(DegreesF(212))); assert(DegreesF(100).opEquals!epsilon(DegreesC(37.77777777777777))); } + + +VarQuantity parse_quantity(const(char)[] text, size_t* bytes_taken = null) nothrow +{ + import urt.si.unit; + import urt.conv; + + int e; + uint base; + size_t taken; + long raw_value = text.parse_int_with_exponent_and_base(e, base, &taken); + if (taken == 0) + { + if (bytes_taken) + *bytes_taken = 0; + return VarQuantity(double.nan); + } + + // we parsed a number! + auto r = VarQuantity(e == 0 ? raw_value : raw_value * double(base)^^e); + + if (taken < text.length) + { + // try and parse a unit... + ScaledUnit su; + float pre_scale; + ptrdiff_t unit_taken = su.parse_unit(text[taken .. $], pre_scale, false); + if (unit_taken > 0) + { + taken += unit_taken; + r = VarQuantity(r.value * pre_scale, su); + } + } + if (bytes_taken) + *bytes_taken = taken; + return r; +} + +unittest +{ + import urt.si.unit; + + size_t taken; + assert("10V".parse_quantity(&taken) == Volts(10) && taken == 3); + assert("10.2e+2Wh".parse_quantity(&taken) == WattHours(1020) && taken == 9); +} diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index cf40a9e..a183d98 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -1,5 +1,8 @@ module urt.si.unit; +import urt.array; +import urt.string; + nothrow @nogc: @@ -29,6 +32,9 @@ nothrow @nogc: // +enum ScaledUnit unit(const(char)[] desc) = () { ScaledUnit r; float f; ptrdiff_t e = r.parse_unit(desc, f); assert(e > 0, "Invalid unit"); assert(f == 1, "Unit requires pre-scale"); return r; }(); + + // base units enum Metre = Unit(UnitType.Length); enum Kilogram = Unit(UnitType.Mass); @@ -39,6 +45,9 @@ enum Candela = Unit(UnitType.Luma); enum Radian = Unit(UnitType.Angle); // non-si units +enum Minute = ScaledUnit(Second, ScaleFactor.Minute); +enum Hour = ScaledUnit(Second, ScaleFactor.Hour); +enum Day = ScaledUnit(Second, ScaleFactor.Day); enum Inch = ScaledUnit(Metre, ScaleFactor.Inch); enum Foot = ScaledUnit(Metre, ScaleFactor.Foot); enum Mile = ScaledUnit(Metre, ScaleFactor.Mile); @@ -60,7 +69,9 @@ enum CubicMetre = Metre^^3; enum Litre = ScaledUnit(CubicMetre, -3); enum Gram = ScaledUnit(Kilogram, -3); enum Milligram = ScaledUnit(Kilogram, -6); +enum Nanosecond = ScaledUnit(Second, -9); enum Hertz = Cycle / Second; +//enum Kilohertz = TODO: ONOES! Our system can't encode kilohertz! This is disaster!! enum Newton = Kilogram * Metre / Second^^2; enum Pascal = Newton / Metre^^2; enum PSI = ScaledUnit(Pascal, ScaleFactor.PSI); @@ -96,7 +107,18 @@ enum UnitType : ubyte struct Unit { -nothrow @nogc: +nothrow: + // debug/ctfe helper + string toString() pure + { + char[32] t = void; + ptrdiff_t l = toString(t, null, null); + if (l < 0) + return "Invalid unit"; // OR JUST COULDN'T STRINGIFY! + return t[0..l].idup; + } + +@nogc: uint pack; @@ -225,82 +247,40 @@ nothrow @nogc: this = this.opBinary!op(rh); } - ptrdiff_t toString(char[] buffer) const + import urt.string.format : FormatArg; + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure { - immutable string[24] si = [ -// "da", "h", "k", "10k", "100k", "M", "10M", "100M", "G", "10G", "100G", "T", "10T", "100T", "P", "10P", "100P", "E", "10E", "100E", "Z", "10Z", "100Z", "Y" - "10", "100", "k", "10k", "100k", "M", "10M", "100M", "G", "10G", "100G", "T", "10T", "100T", "P", "10P", "100P", "E", "10E", "100E", "Z", "10Z", "100Z", "Y" - ]; - immutable string[24] si_inv = [ -// "d", "c", "m", "100μ", "10μ", "μ", "100n", "10n", "n", "100p", "10p", "p", "100f", "10f", "f", "100a", "10a", "a", "100z", "10z", "z", "100y", "10y", "y" - "100m", "10m", "m", "100μ", "10μ", "μ", "100n", "10n", "n", "100p", "10p", "p", "100f", "10f", "f", "100a", "10a", "a", "100z", "10z", "z", "100y", "10y", "y" - ]; - - ptrdiff_t len = 0; - - // ⁰¹⁻²³⁴⁵⁶⁷⁸⁹·° - -// if (q(0).exponent == 0) -// { -// // if we have a scaling factor... what do we write? -//// if (sf) ... -// len = 0; -// return len; -// } -// -// auto name = this in unitNames; -// if (name) -// { -// len = name.length; -// buffer[0 .. len] = *name; -// return len; -// } -// -// int f = sf(); -// name = unit() in unitNames; -// -// if ((name || q(1).exponent == 0) && f <= 24) -// { -// if (f > 0) -// { -// ref immutable string[24] prefix = inv ? si_inv : si; -// -// uint scale = f - 1; -// if (q(0).measure == UnitType.Mass) -// scale += 3; -// -// len = prefix[scale].length; -// buffer[0 .. len] = prefix[scale][]; -// } -// -// if (name) -// { -// buffer[len .. len + name.length] = *name; -// len += name.length; -// } -// else -// { -// immutable string[UnitType.max + 1] unit_name = [ "g", "m", "s", "A", "K", "cd", "cy" ]; -// -// string uname = unit_name[q(0).measure]; -// buffer[len .. len + uname.length] = uname[]; -// len += uname.length; -// } -// -// if (q(0).measure != 1) -// { -// buffer[len++] = '^'; -// len += q(0).exponent.toString(buffer[len .. $]); -// } -// -// } -// else -// { -// // multiple terms, or an odd scale factor... -// // TODO... -// assert(false); -// } + assert(false, "TODO"); + } + + ptrdiff_t fromString(const(char)[] s) pure + { + if (s.length == 0) + { + pack = 0; + return 0; + } + + Unit r; + size_t len = s.length; + bool invert; + char sep; + while (const(char)[] unit = s.split!('/', '*')(sep)) + { + int p = unit.take_power(); + if (p == 0) + return -1; // invalid power + if (const Unit* u = unit in unitMap) + r *= (*u) ^^ (invert ? -p : p); + else + { + assert(false, "TODO?"); + } + if (sep == '/') + invert = true; + } + this = r; return len; } @@ -381,7 +361,18 @@ enum ExtendedScaleFactor : ubyte struct ScaledUnit { -nothrow @nogc: +nothrow: + // debug/ctfe helper + string toString() pure + { + char[32] t = void; + ptrdiff_t l = toString(t, null, null); + if (l < 0) + return "Invalid unit"; // OR JUST COULDN'T STRINGIFY! + return t[0..l].idup; + } + +@nogc: uint pack; @@ -432,12 +423,12 @@ nothrow @nogc: return unit == b.unit; } - double scale(bool inv = false)() const pure + double scale(bool invert = false)() const pure { if (siScale) { int e = exp(); - if (inv) + if (invert) e = -e; if (uint(e + 9) < 19) return sciScaleFactor[e + 9]; @@ -445,9 +436,9 @@ nothrow @nogc: } if (isExtended()) - return extScaleFactor[(pack >> 29) ^ (inv << 2)]; + return extScaleFactor[(pack >> 29) ^ (invert << 2)]; - double s = scaleFactor[(pack >> 31) ^ inv][sf()]; + double s = scaleFactor[(pack >> 31) ^ invert][sf()]; for (uint i = ((pack >> 29) & 3); i > 0; --i) s *= s; return s; @@ -574,9 +565,278 @@ nothrow @nogc: bool opEquals(Unit rh) const pure => (pack & 0xFF000000) ? false : unit == rh; + alias parseUnit = parse_unit; // TODO: DELETE ME!!! + ptrdiff_t parse_unit(const(char)[] s, out float pre_scale, bool allow_unit_scale = true) pure + { + import urt.conv : parse_uint_with_exponent; + + pre_scale = 1; + + if (s.length == 0) + { + pack = 0; + return 0; + } + + size_t len = s.length; + if (s[0] == '-') + { + if (s.length == 1) + return -1; + pre_scale = -1; + s = s[1 .. $]; + } + + ScaledUnit r; + bool invert; + char sep; + while (const(char)[] term = s.split!(['/', '*'], false, false)(&sep)) + { + int p = term.take_power(); + if (p == 0) + return -1; // invalid exponent + if (term.length == 0) + return -1; + + size_t offset = 0; + + // parse the scale factor + int e = 0; + if (term[0].is_numeric) + { + if (!allow_unit_scale) + return -1; // no numeric scale factor allowed + ulong sf = term.parse_uint_with_exponent(e, &offset); + pre_scale *= sf; + } + + if (offset == term.length) + r *= ScaledUnit(Unit(), e); + else if (const ScaledUnit* su = term[offset .. $] in noScaleUnitMap) + { + r *= (*su) ^^ (invert ? -p : p); + pre_scale *= 10.0^^e; + } + else + { + // try and parse SI prefix... + switch (term[offset]) + { + case 'Y': e += 24; ++offset; break; + case 'Z': e += 21; ++offset; break; + case 'E': e += 18; ++offset; break; + case 'P': e += 15; ++offset; break; + case 'T': e += 12; ++offset; break; + case 'G': e += 9; ++offset; break; + case 'M': e += 6; ++offset; break; + case 'k': e += 3; ++offset; break; + case 'h': e += 2; ++offset; break; + case 'c': e -= 2; ++offset; break; + case 'u': e -= 6; ++offset; break; + case 'n': e -= 9; ++offset; break; + case 'p': e -= 12; ++offset; break; + case 'f': e -= 15; ++offset; break; + case 'a': e -= 18; ++offset; break; + case 'z': e -= 21; ++offset; break; + case 'y': e -= 24; ++offset; break; + case 'm': + // can confuse with metres... so gotta check... + if (offset + 1 < term.length) + e -= 3, ++offset; + break; + case 'd': + if (offset + 1 < term.length && term[offset + 1] == 'a') + { + e += 1, offset += 2; + break; + } + e -= 1, ++offset; + break; + default: + if (offset + "µ".length < term.length && term[offset .. offset + "µ".length] == "µ") + e -= 6, offset += "µ".length; + break; + } + if (offset == term.length) + return -1; + + term = term[offset .. $]; + if (const Unit* u = term in unitMap) + { + if (term == "kg") + { + // we alrady parsed the 'k', so this string must have been "kkg", which is nonsense + return -1; + } + r *= ScaledUnit((*u) ^^ (invert ? -p : p), e); + } + else if (const ScaledUnit* su = term in scaledUnitMap) + r *= ScaledUnit(su.unit, su.exp + e) ^^ (invert ? -p : p); + else if (const ScaledUnit* su = term in noScaleUnitMapSI) + { + r *= (*su) ^^ (invert ? -p : p); + pre_scale *= 10.0^^e; + } + else + return -1; // string was not taken? + } + + if (sep == '/') + invert = true; + } + this = r; + return len; + } + + import urt.string.format : FormatArg; + ptrdiff_t format_unit(char[] buffer, out float pre_scale, bool allow_unit_scale = true) const pure + { + assert(allow_unit_scale == true, "TODO: support for no-scale formatting (require pre-scale)"); + pre_scale = 1; + + if (!unit.pack) + { + if (siScale && exp == -2) + { + if (buffer.ptr) + { + if (buffer.length == 0) + return -1; + buffer[0] = '%'; + } + return 1; + } + else if (siScale && exp == -3) + { + enum pm_len = "‰".length; + if (buffer.ptr) + { + if (buffer.length < pm_len) + return -1; + buffer[0..pm_len] = "‰"; + } + return pm_len; + } + else + assert(false, "TODO!"); // how (or should?) we encode a scale as a unit type? + } + + size_t len = 0; + if (siScale) + { + int x = exp; + if (x != 0) + { + // for scale factors between SI units, we'll normalise to the next higher unit... + int y = (x + 33) % 3; + if (y != 0) + { + if (y == 1) + { + if (buffer.ptr) + { + if (buffer.length < 2) + return -1; + buffer[0..2] = "10"; + } + --x; + len += 2; + } + else + { + if (buffer.ptr) + { + if (buffer.length < 3) + return -1; + buffer[0..3] = "100"; + } + x -= 2; + len += 3; + } + } + assert(x >= -30, "TODO: handle this very small case"); + + if (x != 0) + { + if (buffer.ptr) + { + if (buffer.length <= len) + return -1; + buffer[len] = "qryzafpnum kMGTPEZYRQ"[x/3 + 10]; + } + ++len; + } + } + + if (const string* name = unit in unitNames) + { + if (buffer.ptr) + { + if (buffer.length < len + name.length) + return -1; + buffer[len .. len + name.length] = *name; + } + len += name.length; + } + else + { + // synth a unit name... + assert(false, "TODO"); + } + } + else + { + if (const string* name = this in scaledUnitNames) + { + if (buffer.ptr) + { + if (buffer.length < len + name.length) + return -1; + buffer[len .. len + name.length] = *name; + } + len += name.length; + } + else + { + // what now? + assert(false, "TODO"); + } + } + return len; + } + + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure + { + float pre_scale; + ptrdiff_t r = format_unit(buffer, pre_scale, true); + if (pre_scale != 1) + return -1; + return r; + } + + ptrdiff_t fromString(const(char)[] s) pure + { + float scale; + ptrdiff_t r = parse_unit(s, scale); + if (scale != 1) + return -1; + return r; + } + size_t toHash() const pure => pack; + auto __debugOverview() + { + debug { + char[] buffer = new char[32]; + ptrdiff_t len = toString(buffer, null, null); + return buffer[0 .. len]; + } + else + return pack; + } + package: this(uint pack) pure { @@ -649,9 +909,9 @@ immutable double[16][2] scaleFactor = [ [ PI/180, // Degrees double.nan // extended... ], [ - 1/60, // Minute - 1/3600, // Hour - 1/86400, // Day + 1/60.0, // Minute + 1/3600.0, // Hour + 1/86400.0, // Day 1/0.0254, // Inch 1/0.3048, // Foot 1/1609.344, // Mile @@ -688,39 +948,41 @@ immutable double[4] tempOffsets = [ (-273.15*9)/5 + 32 // K -> F ]; -immutable string[ScaledUnit] unitNames = [ +immutable string[Unit] unitNames = [ + Metre : "m", + Metre^^2 : "m²", + Metre^^3 : "m³", + Kilogram : "kg", + Second : "s", + Ampere : "A", + Kelvin : "K", + Candela : "cd", + Radian : "rad", - // ⁰¹⁻²³⁴⁵⁶⁷⁸⁹·° + // derived units + Newton : "N", + Pascal : "Pa", + Joule : "J", + Watt : "W", + Coulomb : "C", + Volt : "V", + Ohm : "Ω", + Farad : "F", + Siemens : "S", + Weber : "Wb", + Tesla : "T", + Henry : "H", + Lumen : "lm", + Lux : "lx", +]; - // base units - ScaledUnit() : "", - ScaledUnit(Metre) : "m", - ScaledUnit(Kilogram) : "kg", - ScaledUnit(Second) : "s", - ScaledUnit(Ampere) : "A", - ScaledUnit(Kelvin) : "°K", - ScaledUnit(Candela) : "cd", - ScaledUnit(Radian) : "rad", +immutable string[ScaledUnit] scaledUnitNames = [ + Minute : "min", +// Minute : "mins", + Hour : "hr", +// Hour : "hrs", + Day : "day", - // derived units - ScaledUnit(SquareMetre) : "m²", - ScaledUnit(CubicMetre) : "m³", - ScaledUnit(Newton) : "N", - ScaledUnit(Pascal) : "Pa", - ScaledUnit(Joule) : "J", - ScaledUnit(Watt) : "W", - ScaledUnit(Coulomb) : "C", - ScaledUnit(Volt) : "V", - ScaledUnit(Ohm) : "Ω", - ScaledUnit(Farad) : "F", - ScaledUnit(Siemens) : "S", - ScaledUnit(Weber) : "Wb", - ScaledUnit(Tesla) : "T", - ScaledUnit(Henry) : "H", - ScaledUnit(Lumen) : "lm", - ScaledUnit(Lux) : "lx", - - // scaled units Inch : "in", Foot : "ft", Mile : "mi", @@ -745,3 +1007,143 @@ immutable string[ScaledUnit] unitNames = [ AmpereHour : "Ah", WattHour : "Wh", ]; + +immutable Unit[string] unitMap = [ + // base units + "m" : Metre, + "kg" : Kilogram, + "s" : Second, + "A" : Ampere, + "K" : Kelvin, + "cd" : Candela, + "rad" : Radian, + + // derived units + "N" : Newton, + "Pa" : Pascal, + "J" : Joule, + "W" : Watt, + "C" : Coulomb, + "V" : Volt, + "Ω" : Ohm, + "F" : Farad, + "S" : Siemens, + "Wb" : Weber, + "T" : Tesla, + "H" : Henry, + "lm" : Lumen, + "lx" : Lux, + + // questionable... :/ + "VA" : Watt, + "var" : Watt, +]; + +immutable ScaledUnit[string] noScaleUnitMap = [ + "min" : Minute, + "mins" : Minute, + "hr" : Hour, + "hrs" : Hour, + "day" : Day, + "days" : Day, + "'" : Inch, + "in" : Inch, + "\"" : Foot, + "ft" : Foot, + "mi" : Mile, + "oz" : Ounce, + // TODO: us/uk floz/gallon? + "lb" : Pound, + "°" : Degree, + "deg" : Degree, + "°C" : Celsius, + "°F" : Fahrenheit, + "cy" : Cycle, + "psi" : PSI, + "%" : Percent, + "‰" : Permille, + "‱" : ScaledUnit(Unit(), -4), + "ppm" : ScaledUnit(Unit(), -6), +]; + +// these can have SI prefixes +immutable ScaledUnit[string] scaledUnitMap = [ + "l" : Litre, + "g" : Gram, +]; + +// these can have SI prefixes, but scale must be converted to coefficient +immutable ScaledUnit[string] noScaleUnitMapSI = [ + "Ah" : AmpereHour, + "Wh" : WattHour, + "Hz" : Hertz, + + // questionable... :/ + "VAh" : WattHour, + "varh" : WattHour, +]; + +int take_power(ref const(char)[] s) pure +{ + size_t e = s.findFirst('^'); + if (e < s.length) + { + const(char)[] p = s[e+1..$]; + s = s[0..e]; + if (s.length == 0 || p.length == 0) + return 0; + if (p[0] == '-') + { + if (p.length != 2 || uint(p[2] - '0') > 4) + return 0; + return -(p[2] - '0'); + } + if (p.length != 1 || uint(p[1] - '0') > 4) + return 0; + return p[2] - '0'; + } + else if (s.length > 2) + { + if (s[$-2..$] == "¹") + { + if (s.length > 5 && s[$-5..$-2] == "⁻") + { + s = s[0..$-5]; + return -1; + } + s = s[0..$-2]; + return 1; + } + if (s[$-2..$] == "²") + { + if (s.length > 5 && s[$-5..$-2] == "⁻") + { + s = s[0..$-5]; + return -2; + } + s = s[0..$-2]; + return 2; + } + if (s[$-2..$] == "³") + { + if (s.length > 5 && s[$-5..$-2] == "⁻") + { + s = s[0..$-5]; + return -3; + } + s = s[0..$-2]; + return 3; + } + } + else if (s.length > 3 && s[$-3..$] == "⁴") + { + if (s.length > 6 && s[$-6..$-3] == "⁻") + { + s = s[0..$-6]; + return -4; + } + s = s[0..$-3]; + return 4; + } + return 1; +} diff --git a/src/urt/socket.d b/src/urt/socket.d index 800f72d..ce2f617 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -11,8 +11,8 @@ version (Windows) // TODO: this is in core.sys.windows.winsock2; why do I need it here? pragma(lib, "ws2_32"); - import core.sys.windows.windows; - import core.sys.windows.winsock2 : + import urt.internal.sys.windows; + import urt.internal.sys.windows.winsock2 : _bind = bind, _listen = listen, _connect = connect, _accept = accept, _send = send, _sendto = sendto, _recv = recv, _recvfrom = recvfrom, _shutdown = shutdown; @@ -20,18 +20,20 @@ version (Windows) version = HasIPv6; alias SocketHandle = SOCKET; + + enum IPV6_RECVPKTINFO = 49; + enum IPV6_PKTINFO = 50; } else version (Posix) { - import core.stdc.errno; + import urt.internal.os; // use ImportC to import system C headers... import core.sys.posix.fcntl; import core.sys.posix.poll; import core.sys.posix.unistd : close, gethostname; - import urt.internal.os; // use ImportC to import system C headers... - import core.sys.posix.netinet.in_ : sockaddr_in6; + import core.sys.posix.netinet.in_ : in6_addr, sockaddr_in6; alias _bind = urt.internal.os.bind, _listen = urt.internal.os.listen, _connect = urt.internal.os.connect, - _accept = urt.internal.os.accept, _send = urt.internal.os.send, _sendto = urt.internal.os.sendto, + _accept = urt.internal.os.accept, _send = urt.internal.os.send, _sendto = urt.internal.os.sendto, _sendmsg = urt.internal.os.sendmsg, _recv = urt.internal.os.recv, _recvfrom = urt.internal.os.recvfrom, _shutdown = urt.internal.os.shutdown; alias _poll = core.sys.posix.poll.poll; @@ -57,119 +59,119 @@ nothrow @nogc: enum SocketResult { - Success, - Failure, - WouldBlock, - NoBuffer, - NetworkDown, - ConnectionRefused, - ConnectionReset, - ConnectionAborted, - ConnectionClosed, - Interrupted, - InvalidSocket, - InvalidArgument, + success, + failure, + would_block, + no_buffer, + network_down, + connection_refused, + connection_reset, + connection_aborted, + connection_closed, + interrupted, + invalid_socket, + invalid_argument, } enum SocketType : byte { - Unknown = -1, - Stream = 0, - Datagram, - Raw, + unknown = -1, + stream = 0, + datagram, + raw, } enum Protocol : byte { - Unknown = -1, - TCP = 0, - UDP, - IP, - ICMP, - Raw, + unknown = -1, + tcp = 0, + udp, + ip, + icmp, + raw, } enum SocketShutdownMode : ubyte { - Read, - Write, - ReadWrite + read, + write, + read_write } enum SocketOption : ubyte { // not traditionally a 'socket option', but this is way more convenient - NonBlocking, + non_blocking, // Socket options - KeepAlive, - Linger, - RandomizePort, - SendBufferLength, - RecvBufferLength, - ReuseAddress, - NoSigPipe, - Error, + keep_alive, + linger, + randomize_port, + send_buffer_length, + recv_buffer_length, + reuse_address, + no_sig_pPipe, + error, // IP options - FirstIpOption, - Multicast = FirstIpOption, - MulticastLoopback, - MulticastTTL, + first_ip_option, + multicast = first_ip_option, + multicast_loopback, + multicast_ttl, + ip_pktinfo, // IPv6 options - FirstIpv6Option, + first_ipv6_option, + ipv6_pktinfo = first_ipv6_option, // ICMP options - FirstIcmpOption = FirstIpv6Option, + first_icmp_option, // ICMPv6 options - FirstIcmpv6Option = FirstIcmpOption, + first_icmpv6_option = first_icmp_option, // TCP options - FirstTcpOption = FirstIcmpv6Option, - TCP_KeepIdle = FirstTcpOption, - TCP_KeepIntvl, - TCP_KeepCnt, - TCP_KeepAlive, // Apple: similar to KeepIdle - TCP_NoDelay, - + first_tcp_option = first_icmpv6_option, + tcp_keep_idle = first_tcp_option, + tcp_keep_intvl, + tcp_keep_cnt, + tcp_keep_alive, // Apple: similar to KeepIdle + tcp_no_delay, // UDP options - FirstUdpOption, + first_udp_option, } enum MsgFlags : ubyte { - None = 0, - OOB = 1 << 0, - Peek = 1 << 1, - Confirm = 1 << 2, - NoSig = 1 << 3, + none = 0, + peek = 1 << 0, + confirm = 1 << 1, + no_sig = 1 << 2, //... } enum AddressInfoFlags : ubyte { - None = 0, - Passive = 1 << 0, - CanonName = 1 << 1, - NumericHost = 1 << 2, - NumericServ = 1 << 3, - All = 1 << 4, - AddrConfig = 1 << 5, - V4Mapped = 1 << 6, - FQDN = 1 << 7, + none = 0, + passive = 1 << 0, + canon_name = 1 << 1, + numeric_host = 1 << 2, + numeric_serv = 1 << 3, + all = 1 << 4, + addr_config = 1 << 5, + v4_mapped = 1 << 6, + fqdn = 1 << 7, } enum PollEvents : ubyte { - None = 0, - Read = 1 << 0, - Write = 1 << 1, - Error = 1 << 2, - HangUp = 1 << 3, - Invalid = 1 << 4, + none = 0, + read = 1 << 0, + write = 1 << 1, + error = 1 << 2, + hangup = 1 << 3, + invalid = 1 << 4, } @@ -190,12 +192,13 @@ private: Result create_socket(AddressFamily af, SocketType type, Protocol proto, out Socket socket) { version (HasUnixSocket) {} else - assert(af != AddressFamily.Unix, "Unix sockets not supported on this platform!"); + assert(af != AddressFamily.unix, "Unix sockets not supported on this platform!"); socket.handle = .socket(s_addressFamily[af], s_socketType[type], s_protocol[proto]); if (socket == Socket.invalid) return socket_getlasterror(); - return Result.Success; + + return Result.success; } Result close(Socket socket) @@ -214,7 +217,7 @@ Result close(Socket socket) // s_noSignal.Erase(socket); // } - return Result.Success; + return Result.success; } Result shutdown(Socket socket, SocketShutdownMode how) @@ -224,15 +227,15 @@ Result shutdown(Socket socket, SocketShutdownMode how) { version (Windows) { - case SocketShutdownMode.Read: t = SD_RECEIVE; break; - case SocketShutdownMode.Write: t = SD_SEND; break; - case SocketShutdownMode.ReadWrite: t = SD_BOTH; break; + case SocketShutdownMode.read: t = SD_RECEIVE; break; + case SocketShutdownMode.write: t = SD_SEND; break; + case SocketShutdownMode.read_write: t = SD_BOTH; break; } else version (Posix) { - case SocketShutdownMode.Read: t = SHUT_RD; break; - case SocketShutdownMode.Write: t = SHUT_WR; break; - case SocketShutdownMode.ReadWrite: t = SHUT_RDWR; break; + case SocketShutdownMode.read: t = SHUT_RD; break; + case SocketShutdownMode.write: t = SHUT_WR; break; + case SocketShutdownMode.read_write: t = SHUT_RDWR; break; } default: assert(false, "Invalid `how`"); @@ -240,41 +243,41 @@ Result shutdown(Socket socket, SocketShutdownMode how) if (_shutdown(socket.handle, t) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } Result bind(Socket socket, ref const InetAddress address) { ubyte[512] buffer = void; - size_t addrLen; - sockaddr* sockAddr = make_sockaddr(address, buffer, addrLen); - assert(sockAddr, "Invalid socket address"); + size_t addr_len; + sockaddr* sock_addr = make_sockaddr(address, buffer, addr_len); + assert(sock_addr, "Invalid socket address"); - if (_bind(socket.handle, sockAddr, cast(int)addrLen) < 0) + if (_bind(socket.handle, sock_addr, cast(int)addr_len) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } Result listen(Socket socket, uint backlog = -1) { if (_listen(socket.handle, int(backlog & 0x7FFFFFFF)) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } Result connect(Socket socket, ref const InetAddress address) { ubyte[512] buffer = void; - size_t addrLen; - sockaddr* sockAddr = make_sockaddr(address, buffer, addrLen); - assert(sockAddr, "Invalid socket address"); + size_t addr_len; + sockaddr* sock_addr = make_sockaddr(address, buffer, addr_len); + assert(sock_addr, "Invalid socket address"); - if (_connect(socket.handle, sockAddr, cast(int)addrLen) < 0) + if (_connect(socket.handle, sock_addr, cast(int)addr_len) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } -Result accept(Socket socket, out Socket connection, InetAddress* connectingSocketAddress = null) +Result accept(Socket socket, out Socket connection, InetAddress* remote_address = null, InetAddress* local_address = null) { char[sockaddr_storage.sizeof] buffer = void; sockaddr* addr = cast(sockaddr*)buffer.ptr; @@ -283,58 +286,180 @@ Result accept(Socket socket, out Socket connection, InetAddress* connectingSocke connection.handle = _accept(socket.handle, addr, &size); if (connection == Socket.invalid) return socket_getlasterror(); - else if (connectingSocketAddress) - *connectingSocketAddress = make_InetAddress(addr); - return Result.Success; + if (remote_address) + *remote_address = make_InetAddress(addr); + if (local_address) + { + if (getsockname(connection.handle, addr, &size) < 0) + return socket_getlasterror(); + *local_address = make_InetAddress(addr); + } + // platforms are inconsistent regarding whether accept inherits the listening socket's blocking mode + // for consistentency, we always set blocking on the accepted socket + connection.set_socket_option(SocketOption.non_blocking, false); + return Result.success; +} + +Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, size_t* bytes_sent = null) + => send(socket, flags, bytes_sent, (&message)[0..1]); + +Result send(Socket socket, MsgFlags flags, size_t* bytes_sent, const void[][] buffers...) +{ + version (Windows) + { + uint sent = void; + WSABUF[32] bufs = void; + assert(buffers.length <= bufs.length, "Too many buffers!"); + + uint n = 0; + foreach(buffer; buffers) + { + if (buffer.length == 0) + continue; + assert(buffer.length <= uint.max, "Buffer too large!"); + bufs[n].buf = cast(char*)buffer.ptr; + bufs[n++].len = cast(uint)buffer.length; + } + + int rc = WSASend(socket.handle, bufs.ptr, n, &sent, /+map_message_flags(flags)+/ 0, null, null); // there are no meaningful flags on Windows + if (rc == SOCKET_ERROR) + return socket_getlasterror(); + if (bytes_sent) + *bytes_sent = sent; + return Result.success; + } + else + return sendmsg(socket, null, flags, null, bytes_sent, buffers); } -Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.None, size_t* bytesSent = null) +Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytes_sent = null) { - Result r = Result.Success; + version (Windows) + return sendto(socket, address, bytes_sent, (&message)[0..1]); + else + return sendmsg(socket, address, flags, null, bytes_sent, (&message)[0..1]); +} - ptrdiff_t sent = _send(socket.handle, message.ptr, cast(int)message.length, map_message_flags(flags)); - if (sent < 0) +Result sendto(Socket socket, const InetAddress* address, size_t* bytes_sent, const void[][] buffers...) +{ + version (Windows) { - r = socket_getlasterror(); - sent = 0; + ubyte[sockaddr_storage.sizeof] tmp = void; + size_t addr_len; + sockaddr* sock_addr = null; + if (address) + { + sock_addr = make_sockaddr(*address, tmp, addr_len); + assert(sock_addr, "Invalid socket address"); + } + + uint sent = void; + WSABUF[32] bufs = void; + assert(buffers.length <= bufs.length, "Too many buffers!"); + + uint n = 0; + foreach(buffer; buffers) + { + if (buffer.length == 0) + continue; + assert(buffer.length <= uint.max, "Buffer too large!"); + bufs[n].buf = cast(char*)buffer.ptr; + bufs[n++].len = cast(uint)buffer.length; + } + + int r = WSASendTo(socket.handle, bufs.ptr, n, &sent, /+map_message_flags(flags)+/ 0, sock_addr, cast(int)addr_len, null, null); // there are no meaningful flags on Windows + if (r == SOCKET_ERROR) + return socket_getlasterror(); + if (bytes_sent) + *bytes_sent = sent; + return Result.success; } - if (bytesSent) - *bytesSent = sent; - return r; + else + return sendmsg(socket, address, MsgFlags.none, null, bytes_sent, buffers); } -Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.None, const InetAddress* address = null, size_t* bytesSent = null) +Result sendmsg(Socket socket, const InetAddress* address, MsgFlags flags, const(void)[] control, size_t* bytes_sent, const void[][] buffers) { ubyte[sockaddr_storage.sizeof] tmp = void; - size_t addrLen; - sockaddr* sockAddr = null; + size_t addr_len; + sockaddr* sock_addr = null; if (address) { - sockAddr = make_sockaddr(*address, tmp, addrLen); - assert(sockAddr, "Invalid socket address"); + sock_addr = make_sockaddr(*address, tmp, addr_len); + assert(sock_addr, "Invalid socket address"); } - Result r = Result.Success; - ptrdiff_t sent = _sendto(socket.handle, message.ptr, cast(int)message.length, map_message_flags(flags), sockAddr, cast(int)addrLen); - if (sent < 0) + version (Windows) { - r = socket_getlasterror(); - sent = 0; + uint sent = void; + WSAMSG msg; + WSABUF[32] bufs = void; + assert(buffers.length <= bufs.length, "Too many buffers!"); + + uint n = 0; + foreach(buffer; buffers) + { + if (buffer.length == 0) + continue; + assert(buffer.length <= uint.max, "Buffer too large!"); + bufs[n].buf = cast(char*)buffer.ptr; + bufs[n++].len = cast(uint)buffer.length; + } + + msg.name = sock_addr; + msg.namelen = cast(int)addr_len; + msg.lpBuffers = bufs.ptr; + msg.dwBufferCount = n; + msg.Control.buf = cast(char*)control.ptr; + msg.Control.len = cast(uint)control.length; + msg.dwFlags = 0; + + int rc = WSASendMsg(socket.handle, &msg, /+map_message_flags(flags)+/ 0, &sent, null, null); // there are no meaningful flags on Windows + if (rc == SOCKET_ERROR) + return socket_getlasterror(); } - if (bytesSent) - *bytesSent = sent; - return r; + else + { + msghdr hdr; + iovec[32] iov = void; + assert(buffers.length <= iov.length, "Too many buffers!"); + + size_t n = 0; + foreach(buffer; buffers) + { + if (buffer.length == 0) + continue; + assert(buffer.length <= uint.max, "Buffer too large!"); + iov[n].iov_base = cast(void*)buffer.ptr; + iov[n++].iov_len = buffer.length; + } + + hdr.msg_name = sock_addr; + hdr.msg_namelen = cast(socklen_t)addr_len; + hdr.msg_iov = iov.ptr; + hdr.msg_iovlen = n; + hdr.msg_control = cast(void*)control.ptr; + hdr.msg_controllen = control.length; + hdr.msg_flags = 0; + + ptrdiff_t sent = _sendmsg(socket.handle, &hdr, map_message_flags(flags)); + if (sent < 0) + return socket_getlasterror(); + } + if (bytes_sent) + *bytes_sent = sent; + return Result.success; } -Result recv(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.None, size_t* bytesReceived) +Result recv(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.none, size_t* bytes_received) { - Result r = Result.Success; + Result r = Result.success; ptrdiff_t bytes = _recv(socket.handle, buffer.ptr, cast(int)buffer.length, map_message_flags(flags)); if (bytes > 0) - *bytesReceived = bytes; + *bytes_received = bytes; else { - *bytesReceived = 0; + *bytes_received = 0; if (bytes == 0) { // if we request 0 bytes, we receive 0 bytes, and it doesn't imply end-of-stream @@ -351,52 +476,111 @@ Result recv(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.None, size_t Result error = socket_getlasterror(); // TODO: Do we want a better way to distinguish between receiving a 0-length packet vs no-data (which looks like an error)? // Is a zero-length packet possible to detect in TCP streams? Makes more sense for recvfrom... - SocketResult sr = get_SocketResult(error); - if (sr != SocketResult.WouldBlock) + SocketResult sr = socket_result(error); + if (sr != SocketResult.would_block) r = error; } } return r; } -Result recvfrom(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.None, InetAddress* senderAddress = null, size_t* bytesReceived) +Result recvfrom(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.none, InetAddress* sender_address = null, size_t* bytes_received, InetAddress* local_address = null) { - char[sockaddr_storage.sizeof] addrBuffer = void; - sockaddr* addr = cast(sockaddr*)addrBuffer.ptr; - socklen_t size = addrBuffer.sizeof; + char[sockaddr_storage.sizeof] addr_buffer = void; + sockaddr* addr = cast(sockaddr*)addr_buffer.ptr; - Result r = Result.Success; - ptrdiff_t bytes = _recvfrom(socket.handle, buffer.ptr, cast(int)buffer.length, map_message_flags(flags), addr, &size); - if (bytes >= 0) - *bytesReceived = bytes; + if (local_address) + { + version (Windows) + { + assert(WSARecvMsg, "WSARecvMsg not available!"); + + void[1500] ctrl = void; // HUGE BUFFER! + + WSABUF msg_buf; + msg_buf.buf = cast(char*)buffer.ptr; + msg_buf.len = cast(uint)buffer.length; + + WSAMSG msg; + msg.name = addr; + msg.namelen = addr_buffer.sizeof; + msg.lpBuffers = &msg_buf; + msg.dwBufferCount = 1; + msg.Control.buf = cast(char*)ctrl.ptr; + msg.Control.len = cast(uint)ctrl.length; + msg.dwFlags = 0; + uint bytes; + int r = WSARecvMsg(socket.handle, &msg, &bytes, null, null); + if (r == 0) + *bytes_received = bytes; + else + { + *bytes_received = 0; + goto fail; + } + + // parse the control messages + *local_address = InetAddress(); + for (WSACMSGHDR* c = WSA_CMSG_FIRSTHDR(&msg); c != null; c = WSA_CMSG_NXTHDR(&msg, c)) + { + if (c.cmsg_level == IPPROTO_IP && c.cmsg_type == IP_PKTINFO) + { + IN_PKTINFO* pk = cast(IN_PKTINFO*)WSA_CMSG_DATA(c); + *local_address = InetAddress(make_IPAddr(pk.ipi_addr), 0); // TODO: be nice to populate the listening port... + // pk.ipi_ifindex = receiving interface index + } + if (c.cmsg_level == IPPROTO_IPV6 && c.cmsg_type == IPV6_PKTINFO) + { + IN6_PKTINFO* pk6 = cast(IN6_PKTINFO*)WSA_CMSG_DATA(c); + *local_address = InetAddress(make_IPv6Addr(pk6.ipi6_addr), 0); // TODO: be nice to populate the listening port... + // pk6.ipi6_ifindex = receiving interface index + } + } + } + else + { + assert(false, "TODO: call recvmsg and all that..."); + } + } else { - *bytesReceived = 0; - - Result error = socket_getlasterror(); - SocketResult sockRes = get_SocketResult(error); - if (sockRes != SocketResult.NoBuffer && // buffers full - sockRes != SocketResult.ConnectionRefused && // posix error - sockRes != SocketResult.ConnectionReset) // !!! windows may report this error, but it appears to mean something different on posix - r = error; + socklen_t size = addr_buffer.sizeof; + ptrdiff_t bytes = _recvfrom(socket.handle, buffer.ptr, cast(int)buffer.length, map_message_flags(flags), addr, &size); + if (bytes >= 0) + *bytes_received = bytes; + else + { + *bytes_received = 0; + goto fail; + } } - if (r && senderAddress) - *senderAddress = make_InetAddress(addr); - return r; + + if (sender_address) + *sender_address = make_InetAddress(addr); + return Result.success; + +fail: + Result error = socket_getlasterror(); + SocketResult sockRes = socket_result(error); + if (sockRes != SocketResult.no_buffer && // buffers full + sockRes != SocketResult.connection_refused && // posix error + sockRes != SocketResult.connection_reset) // !!! windows may report this error, but it appears to mean something different on posix + return error; + return Result.success; } Result set_socket_option(Socket socket, SocketOption option, const(void)* optval, size_t optlen) { - Result r = Result.Success; + Result r = Result.success; // check the option appears to be the proper datatype - const OptInfo* optInfo = &s_socketOptions[option]; - assert(optInfo.rtType != OptType.Unsupported, "Socket option is unsupported on this platform!"); - assert(optlen == s_optTypeRtSize[optInfo.rtType], "Socket option has incorrect payload size!"); + const OptInfo* opt_info = &s_socketOptions[option]; + assert(opt_info.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); + assert(optlen == s_optTypeRtSize[opt_info.rt_type], "Socket option has incorrect payload size!"); // special case for non-blocking // this is not strictly a 'socket option', but this rather simplifies our API - if (option == SocketOption.NonBlocking) + if (option == SocketOption.non_blocking) { bool value = *cast(const(bool)*)optval; version (Windows) @@ -420,30 +604,30 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval // LockGuard!SharedMutex lock(s_noSignalMut); // s_noSignal.InsertOrAssign(socket.handle, *cast(const(bool)*)optval); // -// if (optInfo.platformType == OptType.Unsupported) +// if (opt_info.platform_type == OptType.unsupported) // return r; // } // determine the option 'level' OptLevel level = get_optlevel(option); version (HasIPv6) {} else - assert(level != OptLevel.IPv6 && level != OptLevel.ICMPv6, "Platform does not support IPv6!"); + assert(level != OptLevel.ipv6 && level != OptLevel.ICMPv6, "Platform does not support IPv6!"); // platforms don't all agree on option data formats! const(void)* arg = optval; int itmp = void; linger ling = void; - if (optInfo.rtType != optInfo.platformType) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.rtType) + switch (opt_info.rt_type) { // TODO: there are more converstions necessary as options/platforms are added - case OptType.Bool: + case OptType.bool_: { const bool value = *cast(const(bool)*)optval; - switch (optInfo.platformType) + switch (opt_info.platform_type) { - case OptType.Int: + case OptType.int_: itmp = value ? 1 : 0; arg = &itmp; break; @@ -451,20 +635,20 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval } break; } - case OptType.Duration: + case OptType.duration: { const Duration value = *cast(const(Duration)*)optval; - switch (optInfo.platformType) + switch (opt_info.platform_type) { - case OptType.Seconds: + case OptType.seconds: itmp = cast(int)value.as!"seconds"; arg = &itmp; break; - case OptType.Milliseconds: + case OptType.milliseconds: itmp = cast(int)value.as!"msecs"; arg = &itmp; break; - case OptType.Linger: + case OptType.linger: itmp = cast(int)value.as!"seconds"; ling = linger(!!itmp, cast(ushort)itmp); arg = &ling; @@ -479,88 +663,88 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval } // set the option - r.systemCode = setsockopt(socket.handle, s_sockOptLevel[level], optInfo.option, cast(const(char)*)arg, s_optTypePlatformSize[optInfo.platformType]); + r.systemCode = setsockopt(socket.handle, s_sockOptLevel[level], opt_info.option, cast(const(char)*)arg, s_optTypePlatformSize[opt_info.platform_type]); return r; } Result set_socket_option(Socket socket, SocketOption option, bool value) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Bool, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.bool_, "Incorrect value type for option"); return set_socket_option(socket, option, &value, bool.sizeof); } Result set_socket_option(Socket socket, SocketOption option, int value) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Int, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.int_, "Incorrect value type for option"); return set_socket_option(socket, option, &value, int.sizeof); } Result set_socket_option(Socket socket, SocketOption option, Duration value) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Duration, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.duration, "Incorrect value type for option"); return set_socket_option(socket, option, &value, Duration.sizeof); } Result set_socket_option(Socket socket, SocketOption option, IPAddr value) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.INAddress, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.inet_addr, "Incorrect value type for option"); return set_socket_option(socket, option, &value, IPAddr.sizeof); } Result set_socket_option(Socket socket, SocketOption option, ref MulticastGroup value) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.MulticastGroup, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.multicast_group, "Incorrect value type for option"); return set_socket_option(socket, option, &value, MulticastGroup.sizeof); } Result get_socket_option(Socket socket, SocketOption option, void* output, size_t outputlen) { - Result r = Result.Success; + Result r = Result.success; // check the option appears to be the proper datatype - const OptInfo* optInfo = &s_socketOptions[option]; - assert(optInfo.rtType != OptType.Unsupported, "Socket option is unsupported on this platform!"); - assert(outputlen == s_optTypeRtSize[optInfo.rtType], "Socket option has incorrect payload size!"); + const OptInfo* opt_info = &s_socketOptions[option]; + assert(opt_info.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); + assert(outputlen == s_optTypeRtSize[opt_info.rt_type], "Socket option has incorrect payload size!"); - assert(option != SocketOption.NonBlocking, "Socket option NonBlocking cannot be get"); + assert(option != SocketOption.non_blocking, "Socket option NonBlocking cannot be get"); // determine the option 'level' OptLevel level = get_optlevel(option); version (HasIPv6) - assert(level != OptLevel.IPv6 && level != OptLevel.ICMPv6, "Platform does not support IPv6!"); + assert(level != OptLevel.ipv6 && level != OptLevel.icmpv6, "Platform does not support IPv6!"); // platforms don't all agree on option data formats! void* arg = output; int itmp = 0; linger ling = { 0, 0 }; - if (optInfo.rtType != optInfo.platformType) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.platformType) + switch (opt_info.platform_type) { - case OptType.Int: - case OptType.Seconds: - case OptType.Milliseconds: + case OptType.int_: + case OptType.seconds: + case OptType.milliseconds: { arg = &itmp; break; } - case OptType.Linger: + case OptType.linger: { arg = &ling; break; @@ -570,39 +754,39 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ } } - socklen_t writtenLen = s_optTypePlatformSize[optInfo.platformType]; + socklen_t writtenLen = s_optTypePlatformSize[opt_info.platform_type]; // get the option - r.systemCode = getsockopt(socket.handle, s_sockOptLevel[level], optInfo.option, cast(char*)arg, &writtenLen); + r.systemCode = getsockopt(socket.handle, s_sockOptLevel[level], opt_info.option, cast(char*)arg, &writtenLen); - if (optInfo.rtType != optInfo.platformType) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.rtType) + switch (opt_info.rt_type) { // TODO: there are more converstions necessary as options/platforms are added - case OptType.Bool: + case OptType.bool_: { bool* value = cast(bool*)output; - switch (optInfo.platformType) + switch (opt_info.platform_type) { - case OptType.Int: + case OptType.int_: *value = !!itmp; break; default: assert(false, "Unexpected"); } break; } - case OptType.Duration: + case OptType.duration: { Duration* value = cast(Duration*)output; - switch (optInfo.platformType) + switch (opt_info.platform_type) { - case OptType.Seconds: + case OptType.seconds: *value = seconds(itmp); break; - case OptType.Milliseconds: + case OptType.milliseconds: *value = msecs(itmp); break; - case OptType.Linger: + case OptType.linger: *value = seconds(ling.l_linger); break; default: assert(false, "Unexpected"); @@ -614,10 +798,10 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ } } - assert(optInfo.rtType != OptType.INAddress, "TODO: uncomment this block... for some reason, this block causes DMD to do a bad codegen!"); + assert(opt_info.rt_type != OptType.inet_addr, "TODO: uncomment this block... for some reason, this block causes DMD to do a bad codegen!"); /+ // Options expected in network-byte order - switch (optInfo.rtType) + switch (opt_info.rt_type) { case OptType.INAddress: { @@ -634,37 +818,37 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ Result get_socket_option(Socket socket, SocketOption option, out bool output) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Bool, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.bool_, "Incorrect value type for option"); return get_socket_option(socket, option, &output, bool.sizeof); } Result get_socket_option(Socket socket, SocketOption option, out int output) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Int, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.int_, "Incorrect value type for option"); return get_socket_option(socket, option, &output, int.sizeof); } Result get_socket_option(Socket socket, SocketOption option, out Duration output) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.Duration, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.duration, "Incorrect value type for option"); return get_socket_option(socket, option, &output, Duration.sizeof); } Result get_socket_option(Socket socket, SocketOption option, out IPAddr output) { - const OptInfo* optInfo = &s_socketOptions[option]; - if (optInfo.rtType == OptType.Unsupported) - return InternalResult(InternalCode.Unsupported); - assert(optInfo.rtType == OptType.INAddress, "Incorrect value type for option"); + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(opt_info.rt_type == OptType.inet_addr, "Incorrect value type for option"); return get_socket_option(socket, option, &output, IPAddr.sizeof); } @@ -680,27 +864,27 @@ Result set_keepalive(Socket socket, bool enable, Duration keepIdle, Duration kee uint bytesReturned = 0; if (WSAIoctl(socket.handle, SIO_KEEPALIVE_VALS, &alive, alive.sizeof, null, 0, &bytesReturned, null, null) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } else { - Result res = set_socket_option(socket, SocketOption.KeepAlive, enable); - if (!enable || res != Result.Success) + Result res = set_socket_option(socket, SocketOption.keep_alive, enable); + if (!enable || res != Result.success) return res; version (Darwin) { // OSX doesn't support setting keep-alive interval and probe count. - return set_socket_option(socket, SocketOption.TCP_KeepAlive, keepIdle); + return set_socket_option(socket, SocketOption.tcp_keep_alive, keepIdle); } else { - res = set_socket_option(socket, SocketOption.TCP_KeepIdle, keepIdle); - if (res != Result.Success) + res = set_socket_option(socket, SocketOption.tcp_keep_idle, keepIdle); + if (res != Result.success) return res; - res = set_socket_option(socket, SocketOption.TCP_KeepIntvl, keepInterval); - if (res != Result.Success) + res = set_socket_option(socket, SocketOption.tcp_keep_intvl, keepInterval); + if (res != Result.success) return res; - return set_socket_option(socket, SocketOption.TCP_KeepCnt, keepCount); + return set_socket_option(socket, SocketOption.tcp_keep_cnt, keepCount); } } } @@ -716,7 +900,7 @@ Result get_peer_name(Socket socket, out InetAddress name) name = make_InetAddress(addr); else return socket_getlasterror(); - return Result.Success; + return Result.success; } Result get_socket_name(Socket socket, out InetAddress name) @@ -730,7 +914,7 @@ Result get_socket_name(Socket socket, out InetAddress name) name = make_InetAddress(addr); else return socket_getlasterror(); - return Result.Success; + return Result.success; } Result get_hostname(char* name, size_t len) @@ -738,12 +922,12 @@ Result get_hostname(char* name, size_t len) int fail = gethostname(name, cast(int)len); if (fail) return socket_getlasterror(); - return Result.Success; + return Result.success; } Result get_address_info(const(char)[] nodeName, const(char)[] service, AddressInfo* hints, out AddressInfoResolver result) { - import urt.array : findFirst; + import urt.string : findFirst; import urt.mem.temp : tstringz; size_t colon = nodeName.findFirst(':'); @@ -760,9 +944,9 @@ Result get_address_info(const(char)[] nodeName, const(char)[] service, AddressIn // translate hints... tmpHints.ai_flags = map_addrinfo_flags(hints.flags); tmpHints.ai_family = s_addressFamily[hints.family]; - tmpHints.ai_socktype = s_socketType[hints.sockType]; + tmpHints.ai_socktype = s_socketType[hints.sock_type]; tmpHints.ai_protocol = s_protocol[hints.protocol]; - tmpHints.ai_canonname = cast(char*)hints.canonName; // HAX! + tmpHints.ai_canonname = cast(char*)hints.canon_name; // HAX! tmpHints.ai_addrlen = 0; tmpHints.ai_addr = null; tmpHints.ai_next = null; @@ -780,7 +964,7 @@ Result get_address_info(const(char)[] nodeName, const(char)[] service, AddressIn result.m_internal[0] = res; result.m_internal[1] = res; - return Result.Success; + return Result.success; } Result poll(PollFd[] pollFds, Duration timeout, out uint numEvents) @@ -795,8 +979,8 @@ Result poll(PollFd[] pollFds, Duration timeout, out uint numEvents) { fds[i].fd = pollFds[i].socket.handle; fds[i].revents = 0; - fds[i].events = ((pollFds[i].requestEvents & PollEvents.Read) ? POLLRDNORM : 0) | - ((pollFds[i].requestEvents & PollEvents.Write) ? POLLWRNORM : 0); + fds[i].events = ((pollFds[i].request_events & PollEvents.read) ? POLLRDNORM : 0) | + ((pollFds[i].request_events & PollEvents.write) ? POLLWRNORM : 0); } version (Windows) int r = WSAPoll(fds.ptr, cast(uint)pollFds.length, timeout.ticks < 0 ? -1 : cast(int)timeout.as!"msecs"); @@ -812,14 +996,14 @@ Result poll(PollFd[] pollFds, Duration timeout, out uint numEvents) numEvents = r; for (size_t i = 0; i < pollFds.length; ++i) { - pollFds[i].returnEvents = cast(PollEvents)( - ((fds[i].revents & POLLRDNORM) ? PollEvents.Read : 0) | - ((fds[i].revents & POLLWRNORM) ? PollEvents.Write : 0) | - ((fds[i].revents & POLLERR) ? PollEvents.Error : 0) | - ((fds[i].revents & POLLHUP) ? PollEvents.HangUp : 0) | - ((fds[i].revents & POLLNVAL) ? PollEvents.Invalid : 0)); + pollFds[i].return_events = cast(PollEvents)( + ((fds[i].revents & POLLRDNORM) ? PollEvents.read : 0) | + ((fds[i].revents & POLLWRNORM) ? PollEvents.write : 0) | + ((fds[i].revents & POLLERR) ? PollEvents.error : 0) | + ((fds[i].revents & POLLHUP) ? PollEvents.hangup : 0) | + ((fds[i].revents & POLLNVAL) ? PollEvents.invalid : 0)); } - return Result.Success; + return Result.success; } Result poll(ref PollFd pollFd, Duration timeout, out uint numEvents) @@ -831,9 +1015,9 @@ struct AddressInfo { AddressInfoFlags flags; AddressFamily family; - SocketType sockType; + SocketType sock_type; Protocol protocol; - const(char)* canonName; // Note: this memory is valid until the next call to `next_address`, or until `AddressInfoResolver` is destroyed + const(char)* canon_name; // Note: this memory is valid until the next call to `next_address`, or until `AddressInfoResolver` is destroyed InetAddress address; } @@ -878,11 +1062,11 @@ nothrow @nogc: addrinfo* info = cast(addrinfo*)(m_internal[1]); m_internal[1] = info.ai_next; - addressInfo.flags = AddressInfoFlags.None; // info.ai_flags is only used for 'hints' + addressInfo.flags = AddressInfoFlags.none; // info.ai_flags is only used for 'hints' addressInfo.family = map_address_family(info.ai_family); - addressInfo.sockType = cast(int)info.ai_socktype ? map_socket_type(info.ai_socktype) : SocketType.Unknown; + addressInfo.sock_type = cast(int)info.ai_socktype ? map_socket_type(info.ai_socktype) : SocketType.unknown; addressInfo.protocol = map_protocol(info.ai_protocol); - addressInfo.canonName = info.ai_canonname; + addressInfo.canon_name = info.ai_canonname; addressInfo.address = make_InetAddress(info.ai_addr); return true; } @@ -893,9 +1077,9 @@ nothrow @nogc: struct PollFd { Socket socket; - PollEvents requestEvents; - PollEvents returnEvents; - void* userData; + PollEvents request_events; + PollEvents return_events; + void* user_data; } @@ -905,7 +1089,7 @@ Result socket_getlasterror() version (Windows) return Result(WSAGetLastError()); else - return Result(errno); + return errno_result(); } Result get_socket_error(Socket socket) @@ -920,74 +1104,74 @@ Result get_socket_error(Socket socket) // TODO: !!! enum Result ConnectionClosedResult = Result(-12345); -SocketResult get_SocketResult(Result result) +SocketResult socket_result(Result result) { if (result) - return SocketResult.Success; + return SocketResult.success; if (result.systemCode == ConnectionClosedResult.systemCode) - return SocketResult.ConnectionClosed; + return SocketResult.connection_closed; version (Windows) { if (result.systemCode == WSAEWOULDBLOCK) - return SocketResult.WouldBlock; + return SocketResult.would_block; if (result.systemCode == WSAEINPROGRESS) - return SocketResult.WouldBlock; + return SocketResult.would_block; if (result.systemCode == WSAENOBUFS) - return SocketResult.NoBuffer; + return SocketResult.no_buffer; if (result.systemCode == WSAENETDOWN) - return SocketResult.NetworkDown; + return SocketResult.network_down; if (result.systemCode == WSAECONNREFUSED) - return SocketResult.ConnectionRefused; + return SocketResult.connection_refused; if (result.systemCode == WSAECONNRESET) - return SocketResult.ConnectionReset; + return SocketResult.connection_reset; if (result.systemCode == WSAEINTR) - return SocketResult.Interrupted; + return SocketResult.interrupted; if (result.systemCode == WSAENOTSOCK) - return SocketResult.InvalidSocket; + return SocketResult.invalid_socket; if (result.systemCode == WSAEINVAL) - return SocketResult.InvalidArgument; + return SocketResult.invalid_argument; } else version (Posix) { static if (EAGAIN != EWOULDBLOCK) if (result.systemCode == EAGAIN) - return SocketResult.WouldBlock; + return SocketResult.would_block; if (result.systemCode == EWOULDBLOCK) - return SocketResult.WouldBlock; + return SocketResult.would_block; if (result.systemCode == EINPROGRESS) - return SocketResult.WouldBlock; + return SocketResult.would_block; if (result.systemCode == ENOMEM) - return SocketResult.NoBuffer; + return SocketResult.no_buffer; if (result.systemCode == ENETDOWN) - return SocketResult.NetworkDown; + return SocketResult.network_down; if (result.systemCode == ECONNREFUSED) - return SocketResult.ConnectionRefused; + return SocketResult.connection_refused; if (result.systemCode == ECONNRESET) - return SocketResult.ConnectionReset; + return SocketResult.connection_reset; if (result.systemCode == EINTR) - return SocketResult.Interrupted; + return SocketResult.interrupted; if (result.systemCode == EINVAL) - return SocketResult.InvalidArgument; + return SocketResult.invalid_argument; } - return SocketResult.Failure; + return SocketResult.failure; } -sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_t addrLen) +sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_t addr_len) { - sockaddr* sockAddr = cast(sockaddr*)buffer.ptr; + sockaddr* sock_addr = cast(sockaddr*)buffer.ptr; switch (address.family) { - case AddressFamily.IPv4: + case AddressFamily.ipv4: { - addrLen = sockaddr_in.sizeof; + addr_len = sockaddr_in.sizeof; if (buffer.length < sockaddr_in.sizeof) return null; - sockaddr_in* ain = cast(sockaddr_in*)sockAddr; + sockaddr_in* ain = cast(sockaddr_in*)sock_addr; memzero(ain, sockaddr_in.sizeof); - ain.sin_family = s_addressFamily[AddressFamily.IPv4]; + ain.sin_family = s_addressFamily[AddressFamily.ipv4]; version (Windows) { ain.sin_addr.S_un.S_un_b.s_b1 = address._a.ipv4.addr.b[0]; @@ -1002,19 +1186,19 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ storeBigEndian(&ain.sin_port, ushort(address._a.ipv4.port)); break; } - case AddressFamily.IPv6: + case AddressFamily.ipv6: { version (HasIPv6) { - addrLen = sockaddr_in6.sizeof; + addr_len = sockaddr_in6.sizeof; if (buffer.length < sockaddr_in6.sizeof) return null; - sockaddr_in6* ain6 = cast(sockaddr_in6*)sockAddr; + sockaddr_in6* ain6 = cast(sockaddr_in6*)sock_addr; memzero(ain6, sockaddr_in6.sizeof); - ain6.sin6_family = s_addressFamily[AddressFamily.IPv6]; + ain6.sin6_family = s_addressFamily[AddressFamily.ipv6]; storeBigEndian(&ain6.sin6_port, cast(ushort)address._a.ipv6.port); - storeBigEndian(cast(uint*)&ain6.sin6_flowinfo, address._a.ipv6.flowInfo); + storeBigEndian(cast(uint*)&ain6.sin6_flowinfo, address._a.ipv6.flow_info); storeBigEndian(cast(uint*)&ain6.sin6_scope_id, address._a.ipv6.scopeId); for (int a = 0; a < 8; ++a) { @@ -1030,17 +1214,17 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ assert(false, "Platform does not support IPv6!"); break; } - case AddressFamily.Unix: + case AddressFamily.unix: { // version (HasUnixSocket) // { -// addrLen = sockaddr_un.sizeof; +// addr_len = sockaddr_un.sizeof; // if (buffer.length < sockaddr_un.sizeof) // return null; // -// sockaddr_un* aun = cast(sockaddr_un*)sockAddr; +// sockaddr_un* aun = cast(sockaddr_un*)sock_addr; // memzero(aun, sockaddr_un.sizeof); -// aun.sun_family = s_addressFamily[AddressFamily.Unix]; +// aun.sun_family = s_addressFamily[AddressFamily.unix]; // // memcpy(aun.sun_path, address.un.path, UNIX_PATH_LEN); // break; @@ -1050,70 +1234,51 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ } default: { - sockAddr = null; - addrLen = 0; + sock_addr = null; + addr_len = 0; assert(false, "Unsupported address family"); break; } } - return sockAddr; + return sock_addr; } -InetAddress make_InetAddress(const(sockaddr)* sockAddress) +InetAddress make_InetAddress(const(sockaddr)* sock_address) { InetAddress addr; - addr.family = map_address_family(sockAddress.sa_family); + addr.family = map_address_family(sock_address.sa_family); switch (addr.family) { - case AddressFamily.IPv4: + case AddressFamily.ipv4: { - const sockaddr_in* ain = cast(const(sockaddr_in)*)sockAddress; + const sockaddr_in* ain = cast(const(sockaddr_in)*)sock_address; addr._a.ipv4.port = loadBigEndian(&ain.sin_port); - version (Windows) - { - addr._a.ipv4.addr.b[0] = ain.sin_addr.S_un.S_un_b.s_b1; - addr._a.ipv4.addr.b[1] = ain.sin_addr.S_un.S_un_b.s_b2; - addr._a.ipv4.addr.b[2] = ain.sin_addr.S_un.S_un_b.s_b3; - addr._a.ipv4.addr.b[3] = ain.sin_addr.S_un.S_un_b.s_b4; - } - else version (Posix) - addr._a.ipv4.addr.address = ain.sin_addr.s_addr; - else - assert(false, "Not implemented!"); + addr._a.ipv4.addr = make_IPAddr(ain.sin_addr); break; } - case AddressFamily.IPv6: + case AddressFamily.ipv6: { version (HasIPv6) { - const sockaddr_in6* ain6 = cast(const(sockaddr_in6)*)sockAddress; + const sockaddr_in6* ain6 = cast(const(sockaddr_in6)*)sock_address; addr._a.ipv6.port = loadBigEndian(&ain6.sin6_port); - addr._a.ipv6.flowInfo = loadBigEndian(cast(const(uint)*)&ain6.sin6_flowinfo); + addr._a.ipv6.flow_info = loadBigEndian(cast(const(uint)*)&ain6.sin6_flowinfo); addr._a.ipv6.scopeId = loadBigEndian(cast(const(uint)*)&ain6.sin6_scope_id); - - for (int a = 0; a < 8; ++a) - { - version (Windows) - addr._a.ipv6.addr.s[a] = loadBigEndian(&ain6.sin6_addr.in6_u.u6_addr16[a]); - else version (Posix) - addr._a.ipv6.addr.s[a] = loadBigEndian(cast(const(ushort)*)ain6.sin6_addr.s6_addr + a); - else - assert(false, "Not implemented!"); - } + addr._a.ipv6.addr = make_IPv6Addr(ain6.sin6_addr); } else assert(false, "Platform does not support IPv6!"); break; } - case AddressFamily.Unix: + case AddressFamily.unix: { // version (HasUnixSocket) // { -// const sockaddr_un* aun = cast(const(sockaddr_un)*)sockAddress; +// const sockaddr_un* aun = cast(const(sockaddr_un)*)sock_address; // // memcpy(addr.un.path, aun.sun_path, UNIX_PATH_LEN); // if (UNIX_PATH_LEN < UnixPathLen) @@ -1131,33 +1296,64 @@ InetAddress make_InetAddress(const(sockaddr)* sockAddress) return addr; } +IPAddr make_IPAddr(ref const in_addr in4) +{ + IPAddr addr; + version (Windows) + { + addr.b[0] = in4.S_un.S_un_b.s_b1; + addr.b[1] = in4.S_un.S_un_b.s_b2; + addr.b[2] = in4.S_un.S_un_b.s_b3; + addr.b[3] = in4.S_un.S_un_b.s_b4; + } + else version (Posix) + addr.address = in4.s_addr; + else + assert(false, "Not implemented!"); + return addr; +} + +IPv6Addr make_IPv6Addr(ref const in6_addr in6) +{ + IPv6Addr addr; + for (int a = 0; a < 8; ++a) + { + version (Windows) + addr.s[a] = loadBigEndian(&in6.in6_u.u6_addr16[a]); + else version (Posix) + addr.s[a] = loadBigEndian(cast(const(ushort)*)in6.s6_addr + a); + else + assert(false, "Not implemented!"); + } + return addr; +} private: enum OptLevel : ubyte { - Socket, - IP, - IPv6, - ICMP, - ICMPv6, - TCP, - UDP, + socket, + ip, + ipv6, + icmp, + icmpv6, + tcp, + udp, } enum OptType : ubyte { - Unsupported, - Bool, - Int, - Seconds, - Milliseconds, - Duration, - INAddress, // IPAddr + in_addr - //IN6Address, // IPv6Addr + in6_addr - MulticastGroup, // MulticastGroup + ip_mreq - //MulticastGroupIPv6, // MulticastGroupIPv6? + ipv6_mreq - Linger, + unsupported, + bool_, + int_, + seconds, + milliseconds, + duration, + inet_addr, // IPAddr + in_addr + //inet6_addr, // IPv6Addr + in6_addr + multicast_group, // MulticastGroup + ip_mreq + //multicast_group_ipv6, // MulticastGroupIPv6? + ipv6_mreq + linger, // etc... } @@ -1169,8 +1365,8 @@ __gshared immutable ubyte[] s_optTypePlatformSize = [ 0, 0, int.sizeof, int.size struct OptInfo { short option; - OptType rtType; - OptType platformType; + OptType rt_type; + OptType platform_type; } __gshared immutable ushort[AddressFamily.max+1] s_addressFamily = [ @@ -1182,15 +1378,15 @@ __gshared immutable ushort[AddressFamily.max+1] s_addressFamily = [ AddressFamily map_address_family(int addressFamily) { if (addressFamily == AF_INET) - return AddressFamily.IPv4; + return AddressFamily.ipv4; else if (addressFamily == AF_INET6) - return AddressFamily.IPv6; + return AddressFamily.ipv6; else if (addressFamily == AF_UNIX) - return AddressFamily.Unix; + return AddressFamily.unix; else if (addressFamily == AF_UNSPEC) - return AddressFamily.Unspecified; + return AddressFamily.unspecified; assert(false, "Unsupported address family"); - return AddressFamily.Unknown; + return AddressFamily.unknown; } __gshared immutable int[SocketType.max+1] s_socketType = [ @@ -1201,13 +1397,13 @@ __gshared immutable int[SocketType.max+1] s_socketType = [ SocketType map_socket_type(int sockType) { if (sockType == SOCK_STREAM) - return SocketType.Stream; + return SocketType.stream; else if (sockType == SOCK_DGRAM) - return SocketType.Datagram; + return SocketType.datagram; else if (sockType == SOCK_RAW) - return SocketType.Raw; + return SocketType.raw; assert(false, "Unsupported socket type"); - return SocketType.Unknown; + return SocketType.unknown; } __gshared immutable int[Protocol.max+1] s_protocol = [ @@ -1220,17 +1416,17 @@ __gshared immutable int[Protocol.max+1] s_protocol = [ Protocol map_protocol(int protocol) { if (protocol == IPPROTO_TCP) - return Protocol.TCP; + return Protocol.tcp; else if (protocol == IPPROTO_UDP) - return Protocol.UDP; + return Protocol.udp; else if (protocol == IPPROTO_IP) - return Protocol.IP; + return Protocol.ip; else if (protocol == IPPROTO_ICMP) - return Protocol.ICMP; + return Protocol.icmp; else if (protocol == IPPROTO_RAW) - return Protocol.Raw; + return Protocol.raw; assert(false, "Unsupported protocol"); - return Protocol.Unknown; + return Protocol.unknown; } version (linux) @@ -1261,68 +1457,74 @@ else version (Windows) // BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA { __gshared immutable OptInfo[SocketOption.max] s_socketOptions = [ - OptInfo( -1, OptType.Bool, OptType.Bool ), // NonBlocking - OptInfo( SO_KEEPALIVE, OptType.Bool, OptType.Int ), - OptInfo( SO_LINGER, OptType.Duration, OptType.Linger ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), -// OptInfo( SO_RANDOMIZE_PORT, OptType.Bool, OptType.Int ), // TODO: BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA - OptInfo( SO_SNDBUF, OptType.Int, OptType.Int ), - OptInfo( SO_RCVBUF, OptType.Int, OptType.Int ), - OptInfo( SO_REUSEADDR, OptType.Bool, OptType.Int ), - OptInfo( -1, OptType.Bool, OptType.Unsupported ), // NoSignalPipe - OptInfo( SO_ERROR, OptType.Int, OptType.Int ), - OptInfo( IP_ADD_MEMBERSHIP, OptType.MulticastGroup, OptType.MulticastGroup ), - OptInfo( IP_MULTICAST_LOOP, OptType.Bool, OptType.Int ), - OptInfo( IP_MULTICAST_TTL, OptType.Int, OptType.Int ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( TCP_NODELAY, OptType.Bool, OptType.Int ), + OptInfo( -1, OptType.bool_, OptType.bool_ ), // NonBlocking + OptInfo( SO_KEEPALIVE, OptType.bool_, OptType.int_ ), + OptInfo( SO_LINGER, OptType.duration, OptType.linger ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), +// OptInfo( SO_RANDOMIZE_PORT, OptType.bool_, OptType.int_ ), // TODO: BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA + OptInfo( SO_SNDBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_RCVBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_REUSEADDR, OptType.bool_, OptType.int_ ), + OptInfo( -1, OptType.bool_, OptType.unsupported ), // NoSignalPipe + OptInfo( SO_ERROR, OptType.int_, OptType.int_ ), + OptInfo( IP_ADD_MEMBERSHIP, OptType.multicast_group, OptType.multicast_group ), + OptInfo( IP_MULTICAST_LOOP, OptType.bool_, OptType.int_ ), + OptInfo( IP_MULTICAST_TTL, OptType.int_, OptType.int_ ), + OptInfo( IP_PKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( IPV6_RECVPKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( TCP_NODELAY, OptType.bool_, OptType.int_ ), ]; } else version (linux) // BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA { __gshared immutable OptInfo[SocketOption.max] s_socketOptions = [ - OptInfo( -1, OptType.Bool, OptType.Bool ), // NonBlocking - OptInfo( SO_KEEPALIVE, OptType.Bool, OptType.Int ), - OptInfo( SO_LINGER, OptType.Duration, OptType.Linger ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( SO_SNDBUF, OptType.Int, OptType.Int ), - OptInfo( SO_RCVBUF, OptType.Int, OptType.Int ), - OptInfo( SO_REUSEADDR, OptType.Bool, OptType.Int ), - OptInfo( -1, OptType.Bool, OptType.Unsupported ), // NoSignalPipe - OptInfo( SO_ERROR, OptType.Int, OptType.Int ), - OptInfo( IP_ADD_MEMBERSHIP, OptType.MulticastGroup, OptType.MulticastGroup ), - OptInfo( IP_MULTICAST_LOOP, OptType.Bool, OptType.Int ), - OptInfo( IP_MULTICAST_TTL, OptType.Int, OptType.Int ), - OptInfo( TCP_KEEPIDLE, OptType.Duration, OptType.Seconds ), - OptInfo( TCP_KEEPINTVL, OptType.Duration, OptType.Seconds ), - OptInfo( TCP_KEEPCNT, OptType.Int, OptType.Int ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( TCP_NODELAY, OptType.Bool, OptType.Int ), + OptInfo( -1, OptType.bool_, OptType.bool_ ), // NonBlocking + OptInfo( SO_KEEPALIVE, OptType.bool_, OptType.int_ ), + OptInfo( SO_LINGER, OptType.duration, OptType.linger ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( SO_SNDBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_RCVBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_REUSEADDR, OptType.bool_, OptType.int_ ), + OptInfo( -1, OptType.bool_, OptType.unsupported ), // NoSignalPipe + OptInfo( SO_ERROR, OptType.int_, OptType.int_ ), + OptInfo( IP_ADD_MEMBERSHIP, OptType.multicast_group, OptType.multicast_group ), + OptInfo( IP_MULTICAST_LOOP, OptType.bool_, OptType.int_ ), + OptInfo( IP_MULTICAST_TTL, OptType.int_, OptType.int_ ), + OptInfo( IP_PKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( IPV6_RECVPKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( TCP_KEEPIDLE, OptType.duration, OptType.seconds ), + OptInfo( TCP_KEEPINTVL, OptType.duration, OptType.seconds ), + OptInfo( TCP_KEEPCNT, OptType.int_, OptType.int_ ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( TCP_NODELAY, OptType.bool_, OptType.int_ ), ]; } else version (Darwin) { __gshared immutable OptInfo[SocketOption.max] s_socketOptions = [ - OptInfo( -1, OptType.Bool, OptType.Bool ), // NonBlocking - OptInfo( SO_KEEPALIVE, OptType.Bool, OptType.Int ), + OptInfo( -1, OptType.bool_, OptType.bool_ ), // NonBlocking + OptInfo( SO_KEEPALIVE, OptType.bool_, OptType.int_ ), OptInfo( SO_LINGER, OptType.Duration, OptType.Linger ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( SO_SNDBUF, OptType.Int, OptType.Int ), - OptInfo( SO_RCVBUF, OptType.Int, OptType.Int ), - OptInfo( SO_REUSEADDR, OptType.Bool, OptType.Int ), - OptInfo( SO_NOSIGPIPE, OptType.Bool, OptType.Int ), - OptInfo( SO_ERROR, OptType.Int, OptType.Int ), - OptInfo( IP_ADD_MEMBERSHIP, OptType.MulticastGroup, OptType.MulticastGroup ), - OptInfo( IP_MULTICAST_LOOP, OptType.Bool, OptType.Int ), - OptInfo( IP_MULTICAST_TTL, OptType.Int, OptType.Int ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( -1, OptType.Unsupported, OptType.Unsupported ), - OptInfo( TCP_KEEPALIVE, OptType.Duration, OptType.Seconds ), - OptInfo( TCP_NODELAY, OptType.Bool, OptType.Int ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( SO_SNDBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_RCVBUF, OptType.int_, OptType.int_ ), + OptInfo( SO_REUSEADDR, OptType.bool_, OptType.int_ ), + OptInfo( SO_NOSIGPIPE, OptType.bool_, OptType.int_ ), + OptInfo( SO_ERROR, OptType.int_, OptType.int_ ), + OptInfo( IP_ADD_MEMBERSHIP, OptType.multicast_group, OptType.multicast_group ), + OptInfo( IP_MULTICAST_LOOP, OptType.bool_, OptType.int_ ), + OptInfo( IP_MULTICAST_TTL, OptType.int_, OptType.int_ ), + OptInfo( IP_PKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( IPV6_RECVPKTINFO, OptType.bool_, OptType.int_ ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( -1, OptType.unsupported, OptType.unsupported ), + OptInfo( TCP_KEEPALIVE, OptType.duration, OptType.seconds ), + OptInfo( TCP_NODELAY, OptType.bool_, OptType.int_ ), ]; } else @@ -1331,12 +1533,11 @@ else int map_message_flags(MsgFlags flags) { int r = 0; - if (flags & MsgFlags.OOB) r |= MSG_OOB; - if (flags & MsgFlags.Peek) r |= MSG_PEEK; + if (flags & MsgFlags.peek) r |= MSG_PEEK; version (linux) { - if (flags & MsgFlags.Confirm) r |= MSG_CONFIRM; - if (flags & MsgFlags.NoSig) r |= MSG_NOSIGNAL; + if (flags & MsgFlags.confirm) r |= MSG_CONFIRM; + if (flags & MsgFlags.no_sig) r |= MSG_NOSIGNAL; } return r; } @@ -1344,27 +1545,27 @@ int map_message_flags(MsgFlags flags) int map_addrinfo_flags(AddressInfoFlags flags) { int r = 0; - if (flags & AddressInfoFlags.Passive) r |= AI_PASSIVE; - if (flags & AddressInfoFlags.CanonName) r |= AI_CANONNAME; - if (flags & AddressInfoFlags.NumericHost) r |= AI_NUMERICHOST; - if (flags & AddressInfoFlags.NumericServ) r |= AI_NUMERICSERV; - if (flags & AddressInfoFlags.All) r |= AI_ALL; - if (flags & AddressInfoFlags.AddrConfig) r |= AI_ADDRCONFIG; - if (flags & AddressInfoFlags.V4Mapped) r |= AI_V4MAPPED; + if (flags & AddressInfoFlags.passive) r |= AI_PASSIVE; + if (flags & AddressInfoFlags.canon_name) r |= AI_CANONNAME; + if (flags & AddressInfoFlags.numeric_host) r |= AI_NUMERICHOST; + if (flags & AddressInfoFlags.numeric_serv) r |= AI_NUMERICSERV; + if (flags & AddressInfoFlags.all) r |= AI_ALL; + if (flags & AddressInfoFlags.addr_config) r |= AI_ADDRCONFIG; + if (flags & AddressInfoFlags.v4_mapped) r |= AI_V4MAPPED; version (Windows) - if (flags & AddressInfoFlags.FQDN) r |= AI_FQDN; + if (flags & AddressInfoFlags.fqdn) r |= AI_FQDN; return r; } OptLevel get_optlevel(SocketOption opt) { - if (opt < SocketOption.FirstIpOption) return OptLevel.Socket; - else if (opt < SocketOption.FirstIpv6Option) return OptLevel.IP; - else if (opt < SocketOption.FirstIcmpOption) return OptLevel.IPv6; - else if (opt < SocketOption.FirstIcmpv6Option) return OptLevel.ICMP; - else if (opt < SocketOption.FirstTcpOption) return OptLevel.ICMPv6; - else if (opt < SocketOption.FirstUdpOption) return OptLevel.TCP; - else return OptLevel.UDP; + if (opt < SocketOption.first_ip_option) return OptLevel.socket; + else if (opt < SocketOption.first_ipv6_option) return OptLevel.ip; + else if (opt < SocketOption.first_icmp_option) return OptLevel.ipv6; + else if (opt < SocketOption.first_icmpv6_option) return OptLevel.icmp; + else if (opt < SocketOption.first_tcp_option) return OptLevel.icmpv6; + else if (opt < SocketOption.first_udp_option) return OptLevel.tcp; + else return OptLevel.udp; } @@ -1374,8 +1575,35 @@ version (Windows) void crt_bootup() { WSADATA wsaData; - int result = WSAStartup(MAKEWORD(2, 2), &wsaData); + int result = WSAStartup(0x0202, &wsaData); // what if this fails??? + + // this is truly the worst thing I ever wrote!! + enum SIO_GET_EXTENSION_FUNCTION_POINTER = 0xC8000006; + struct GUID { uint Data1; ushort Data2, Data3; ubyte[8] Data4; } + __gshared immutable GUID WSAID_WSASENDMSG = GUID(0xA441E712, 0x754F, 0x43CA, [0x84,0xA7,0x0D,0xEE,0x44,0xCF,0x60,0x6D]); + __gshared immutable GUID WSAID_WSARECVMSG = GUID(0xF689D7C8, 0x6F1F, 0x436B, [0x8A,0x53,0xE5,0x4F,0xE3,0x51,0xC3,0x22]); + + Socket dummy; + uint bytes = 0; + if (!create_socket(AddressFamily.ipv4, SocketType.datagram, Protocol.udp, dummy)) + goto FAIL; + if (WSAIoctl(dummy.handle, SIO_GET_EXTENSION_FUNCTION_POINTER, cast(void*)&WSAID_WSASENDMSG, cast(uint)GUID.sizeof, + &WSASendMsg, cast(uint)WSASendMsgFn.sizeof, &bytes, null, null) != 0) + goto FAIL; + assert(bytes == WSASendMsgFn.sizeof); + if (WSAIoctl(dummy.handle, SIO_GET_EXTENSION_FUNCTION_POINTER, cast(void*)&WSAID_WSARECVMSG, cast(uint)GUID.sizeof, + &WSARecvMsg, cast(uint)WSARecvMsgFn.sizeof, &bytes, null, null) != 0) + goto FAIL; + assert(bytes == WSARecvMsgFn.sizeof); + dummy.close(); + if (!WSASendMsg || !WSARecvMsg) + goto FAIL; + return; + + FAIL: + import urt.log; + writeWarning("Failed to get WSASendMsg/WSARecvMsg function pointers - sendmsg() won't work, recvfrom() won't be able to report the dst address"); } pragma(crt_destructor) @@ -1394,43 +1622,95 @@ version (Windows) { // stuff that's missing from the windows headers... - enum: int { + enum : int + { AI_NUMERICSERV = 0x0008, AI_ALL = 0x0100, AI_V4MAPPED = 0x0800, AI_FQDN = 0x4000, } - struct pollfd + struct ip_mreq + { + in_addr imr_multiaddr; + in_addr imr_interface; + } + + struct WSAMSG { - SOCKET fd; // Socket handle - SHORT events; // Requested events to monitor - SHORT revents; // Returned events indicating status + LPSOCKADDR name; + int namelen; + LPWSABUF lpBuffers; + uint dwBufferCount; + WSABUF Control; + uint dwFlags; } - alias WSAPOLLFD = pollfd; - alias PWSAPOLLFD = pollfd*; - alias LPWSAPOLLFD = pollfd*; - - enum: short { - POLLRDNORM = 0x0100, - POLLRDBAND = 0x0200, - POLLIN = (POLLRDNORM | POLLRDBAND), - POLLPRI = 0x0400, - - POLLWRNORM = 0x0010, - POLLOUT = (POLLWRNORM), - POLLWRBAND = 0x0020, - - POLLERR = 0x0001, - POLLHUP = 0x0002, - POLLNVAL = 0x0004 + alias LPWSAMSG = WSAMSG*; + + struct WSABUF + { + uint len; + char* buf; } + alias LPWSABUF = WSABUF*; - extern(Windows) int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout); + alias WSASendMsgFn = extern(Windows) int function(SOCKET s, LPWSAMSG lpMsg, uint dwFlags, uint* lpNumberOfBytesSent, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + alias WSARecvMsgFn = extern(Windows) int function(SOCKET s, LPWSAMSG lpMsg, uint* lpdwNumberOfBytesRecvd, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + __gshared WSASendMsgFn WSASendMsg; + __gshared WSARecvMsgFn WSARecvMsg; - struct ip_mreq + struct IN_PKTINFO { - in_addr imr_multiaddr; - in_addr imr_interface; + in_addr ipi_addr; + uint ipi_ifindex; + } + struct IN6_PKTINFO + { + in6_addr ipi6_addr; + uint ipi6_ifindex; } + + struct WSACMSGHDR + { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; + } + alias LPWSACMSGHDR = WSACMSGHDR*; + + LPWSACMSGHDR WSA_CMSG_FIRSTHDR(LPWSAMSG msg) + => msg.Control.len >= WSACMSGHDR.sizeof ? cast(LPWSACMSGHDR)msg.Control.buf : null; + + LPWSACMSGHDR WSA_CMSG_NXTHDR(LPWSAMSG msg, LPWSACMSGHDR cmsg) + { + if (!cmsg) + return WSA_CMSG_FIRSTHDR(msg); + if (cast(ubyte*)cmsg + WSA_CMSGHDR_ALIGN(cmsg.cmsg_len) + WSACMSGHDR.sizeof > cast(ubyte*)msg.Control.buf + msg.Control.len) + return null; + return cast(LPWSACMSGHDR)(cast(ubyte*)cmsg + WSA_CMSGHDR_ALIGN(cmsg.cmsg_len)); + } + + void* WSA_CMSG_DATA(LPWSACMSGHDR cmsg) + => cast(ubyte*)cmsg + WSA_CMSGDATA_ALIGN(WSACMSGHDR.sizeof); + + size_t WSA_CMSGHDR_ALIGN(size_t length) + => (length + WSACMSGHDR.alignof-1) & ~(WSACMSGHDR.alignof-1); + + size_t WSA_CMSGDATA_ALIGN(size_t length) + => (length + size_t.alignof-1) & ~(size_t.alignof-1); + + struct WSAOVERLAPPED + { + uint Internal; + uint InternalHigh; + uint Offset; + uint OffsetHigh; + HANDLE hEvent; + } + alias LPWSAOVERLAPPED = WSAOVERLAPPED*; + + alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint dwError, uint cbTransferred, LPWSAOVERLAPPED lpOverlapped, uint dwFlags); + + extern(Windows) int WSASend(SOCKET s, LPWSABUF lpBuffers, uint dwBufferCount, uint* lpNumberOfBytesSent, uint dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + extern(Windows) int WSASendTo(SOCKET s, LPWSABUF lpBuffers, uint dwBufferCount, uint* lpNumberOfBytesSent, uint dwFlags, const(sockaddr)* lpTo, int iTolen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); } diff --git a/src/urt/string/ansi.d b/src/urt/string/ansi.d index eac1150..931d60d 100644 --- a/src/urt/string/ansi.d +++ b/src/urt/string/ansi.d @@ -68,33 +68,33 @@ enum ANSI_RESET = "\x1b[0m"; nothrow @nogc: -size_t parseANSICode(const(char)[] text) +size_t parse_ansi_code(const(char)[] text) { - import urt.string.ascii : isNumeric; + import urt.string.ascii : is_numeric; if (text.length < 3 || text[0] != '\x1b') return 0; if (text[1] != '[' && text[1] != 'O') return 0; size_t i = 2; - for (; i < text.length && (text[i].isNumeric || text[i] == ';'); ++i) + for (; i < text.length && (text[i].is_numeric || text[i] == ';'); ++i) {} if (i == text.length) return 0; return i + 1; } -char[] stripDecoration(char[] text) pure +char[] strip_decoration(char[] text) pure { - return stripDecoration(text, text); + return strip_decoration(text, text); } -char[] stripDecoration(const(char)[] text, char[] buffer) pure +char[] strip_decoration(const(char)[] text, char[] buffer) pure { size_t len = text.length, outLen = 0; char* dst = buffer.ptr; const(char)* src = text.ptr; - bool writeOutput = text.ptr != buffer.ptr; + bool write_output = text.ptr != buffer.ptr; for (size_t i = 0; i < len;) { char c = src[i]; @@ -106,11 +106,11 @@ char[] stripDecoration(const(char)[] text, char[] buffer) pure if (j < len && src[j] == 'm') { i = j + 1; - writeOutput = true; + write_output = true; continue; } } - if (BranchMoreExpensiveThanStore || writeOutput) + if (BranchMoreExpensiveThanStore || write_output) dst[outLen] = c; // skip stores where unnecessary (probably the common case) ++outLen; } diff --git a/src/urt/string/ascii.d b/src/urt/string/ascii.d index 5541d3a..36f814f 100644 --- a/src/urt/string/ascii.d +++ b/src/urt/string/ascii.d @@ -2,14 +2,14 @@ module urt.string.ascii; -char[] toLower(const(char)[] str) pure nothrow +char[] to_lower(const(char)[] str) pure nothrow { - return toLower(str, new char[str.length]); + return to_lower(str, new char[str.length]); } -char[] toUpper(const(char)[] str) pure nothrow +char[] to_upper(const(char)[] str) pure nothrow { - return toUpper(str, new char[str.length]); + return to_upper(str, new char[str.length]); } @@ -17,7 +17,7 @@ nothrow @nogc: // some character category flags... // 1 = alpha, 2 = numeric, 4 = white, 8 = newline, 10 = control, 20 = ???, 40 = url, 80 = hex -__gshared immutable ubyte[128] charDetails = [ +private __gshared immutable ubyte[128] char_details = [ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x14, 0x18, 0x10, 0x10, 0x18, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, @@ -28,20 +28,20 @@ __gshared immutable ubyte[128] charDetails = [ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x40, 0x10 ]; -__gshared immutable char[16] hexDigits = "0123456789ABCDEF"; +__gshared immutable char[16] hex_digits = "0123456789ABCDEF"; -bool isSpace(char c) pure => c < 128 && (charDetails[c] & 4); -bool isNewline(char c) pure => c < 128 && (charDetails[c] & 8); -bool isWhitespace(char c) pure => c < 128 && (charDetails[c] & 0xC); -bool isAlpha(char c) pure => c < 128 && (charDetails[c] & 1); -bool isNumeric(char c) pure => cast(uint)(c - '0') <= 9; -bool isAlphaNumeric(char c) pure => c < 128 && (charDetails[c] & 3); -bool isHex(char c) pure => c < 128 && (charDetails[c] & 0x80); -bool isControlChar(char c) pure => c < 128 && (charDetails[c] & 0x10); -bool isURL(char c) pure => c < 128 && (charDetails[c] & 0x40); +bool is_space(char c) pure => c < 128 && (char_details[c] & 4); +bool is_newline(char c) pure => c < 128 && (char_details[c] & 8); +bool is_whitespace(char c) pure => c < 128 && (char_details[c] & 0xC); +bool is_alpha(char c) pure => c < 128 && (char_details[c] & 1); +bool is_numeric(char c) pure => cast(uint)(c - '0') <= 9; +bool is_alpha_numeric(char c) pure => c < 128 && (char_details[c] & 3); +bool is_hex(char c) pure => c < 128 && (char_details[c] & 0x80); +bool is_control_char(char c) pure => c < 128 && (char_details[c] & 0x10); +bool is_url(char c) pure => c < 128 && (char_details[c] & 0x40); -char toLower(char c) pure +char to_lower(char c) pure { // this is the typical way; which is faster on a weak arch? // if (c >= 'A' && c <= 'Z') @@ -53,7 +53,7 @@ char toLower(char c) pure return c; } -char toUpper(char c) pure +char to_upper(char c) pure { // this is the typical way; which is faster on a weak arch? // if (c >= 'a' && c <= 'z') @@ -65,26 +65,62 @@ char toUpper(char c) pure return c; } -char[] toLower(const(char)[] str, char[] buffer) pure +char[] to_lower(const(char)[] str, char[] buffer) pure { + if (buffer.length < str.length) + return null; foreach (i; 0 .. str.length) - buffer[i] = toLower(str[i]); - return buffer; + buffer[i] = str[i].to_lower; + return buffer[0..str.length]; } -char[] toUpper(const(char)[] str, char[] buffer) pure +char[] to_upper(const(char)[] str, char[] buffer) pure { + if (buffer.length < str.length) + return null; foreach (i; 0 .. str.length) - buffer[i] = toUpper(str[i]); - return buffer; + buffer[i] = str[i].to_upper; + return buffer[0..str.length]; } -char[] toLower(char[] str) pure +char[] to_lower(char[] str) pure { - return toLower(str, str); + return to_lower(str, str); } -char[] toUpper(char[] str) pure +char[] to_upper(char[] str) pure { - return toUpper(str, str); + return to_upper(str, str); } + +ptrdiff_t cmp(bool case_insensitive = false)(const(char)[] a, const(char)[] b) pure +{ + if (a.length != b.length) + return a.length - b.length; + static if (case_insensitive) + { + for (size_t i = 0; i < a.length; ++i) + { + char ca = a[i].to_lower; + char cb = b[i].to_lower; + if (ca != cb) + return ca - cb; + } + } + else + { + for (size_t i = 0; i < a.length; ++i) + if (a[i] != b[i]) + return a[i] - b[i]; + } + return 0; +} + +ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure + => cmp!true(a, b); + +bool eq(const(char)[] a, const(char)[] b) pure + => cmp(a, b) == 0; + +bool ieq(const(char)[] a, const(char)[] b) pure + => cmp!true(a, b) == 0; diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 6016615..f188cb3 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -1,95 +1,111 @@ module urt.string.format; -import urt.conv : parseIntFast; +import urt.conv : parse_int_fast; import urt.string; import urt.traits; import urt.util; public import urt.mem.temp : tformat, tconcat, tstring; - nothrow @nogc: debug { + // format functions are not re-entrant! static bool InFormatFunction = false; } alias StringifyFunc = ptrdiff_t delegate(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) nothrow @nogc; -alias IntifyFunc = ptrdiff_t delegate() nothrow @nogc; -ptrdiff_t toString(T)(auto ref T value, char[] buffer) +ptrdiff_t toString(T)(ref const T value, char[] buffer, const(char)[] format = null, const(FormatArg)[] formatArgs = null) { - import urt.string.format : FormatArg; - debug InFormatFunction = true; - FormatArg a = FormatArg(value); - ptrdiff_t r = a.getString(buffer, null, null); + ptrdiff_t r = get_to_string_func(value)(buffer, null, null); debug InFormatFunction = false; return r; } -char[] concat(Args...)(char[] buffer, auto ref Args args) +alias formatValue = toString; // TODO: remove me? + +char[] concat(Args...)(char[] buffer, ref const Args args) { + alias NormalisedArgs = NormaliseArgs!Args; + enum is_normalised = is(Args == NormalisedArgs); + static if (Args.length == 0) { return buffer.ptr[0 .. 0]; } - else static if ((Args.length == 1 && allAreStrings!Args) || allConstCorrectStrings!Args) + else static if (!is_normalised) + { + pragma(inline, true); + return concat!(NormalisedArgs)(buffer, args); + } + else { - // this implementation handles pure string concatenations - if (!buffer.ptr) + enum n = num_string_args!Args; + + static if (n == Args.length) { + size_t[Args.length] lens = void; size_t length = 0; - static foreach (i, s; args) + static foreach (i; 0 .. Args.length) { - static if (is(typeof(s) : char)) + static if (is(Args[i] == char)) length += 1; else - length += s.length; - } - return (cast(char*)null)[0 .. length]; - } - size_t offset = 0; - static foreach (i, s; args) - { - static if (is(typeof(s) : char)) - { - if (buffer.length < offset + 1) - return null; - buffer.ptr[offset++] = s; + { + lens[i] = args[i].length; + length += lens[i]; + } } - else + if (buffer.ptr) { - if (buffer.length < offset + s.length) + if (length > buffer.length) return null; - buffer.ptr[offset .. offset + s.length] = s.ptr[0 .. s.length]; - offset += s.length; + char* p = buffer.ptr; + static foreach (i; 0 .. Args.length) + { + static if (is(Args[i] == char)) + *p++ = args[i]; + else + { + p[0..lens[i]] = args[i].ptr[0..lens[i]]; + p += lens[i]; + } + } } + return buffer.ptr[0 .. length]; + } + else static if (Args.length == 1) + { + ptrdiff_t r = get_to_string_func(args[0])(buffer, null, null); + if (r < 0) + return null; + return buffer.ptr[0..r]; + } + else static if (n > 2) + { + char[] r = concat(buffer, args[0 .. n]); + if (buffer.ptr && !r.ptr) + return null; + char[] r2 = concat(buffer.ptr ? buffer.ptr[r.length .. buffer.length] : null, args[n .. $]); + if (buffer.ptr && !r2.ptr) + return null; + return buffer.ptr[0 .. r.length + r2.length]; + } + else + { + // this implementation handles all the other kinds of things! + debug if (!__ctfe) InFormatFunction = true; + StringifyFunc[Args.length] arg_funcs = void; + static foreach(i; 0 .. args.length) + arg_funcs[i] = get_to_string_func(args[i]); + char[] r = concat_impl(buffer, arg_funcs); + debug if (!__ctfe) InFormatFunction = false; + return r; } - return buffer.ptr[0 .. offset]; - } - static if (allAreStrings!Args) - { - // TODO: why can't inline this?! -// pragma(inline, true); - - // avoid duplicate instantiations with different attributes... - return concat!(constCorrectedStrings!Args)(buffer, args); - } - else - { - // this implementation handles all the other kinds of things! - - debug if (!__ctfe) InFormatFunction = true; - FormatArg[Args.length] argFuncs; - // TODO: no need to collect int-ify functions in the arg set... - static foreach(i, arg; args) - argFuncs[i] = FormatArg(arg); - char[] r = concatImpl(buffer, argFuncs); - debug if (!__ctfe) InFormatFunction = false; - return r; } } @@ -104,553 +120,656 @@ char[] format(Args...)(char[] buffer, const(char)[] fmt, ref Args args) return r; } -ptrdiff_t formatValue(T)(auto ref T value, char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) -{ - static if (is(typeof(&value.toString) : StringifyFunc)) - return value.toString(buffer, format, formatArgs); - else - return value.defFormat.toString(buffer, format, formatArgs); -} - struct FormatArg { - enum IsAggregate(T) = is(T == struct) || is(T == class); - private this(T)(ref T value) pure nothrow @nogc { - static if (IsAggregate!T && is(typeof(&value.toString) : StringifyFunc)) - toString = &value.toString; - else static if (IsAggregate!T && __traits(compiles, value.toString(buffer, "format", cast(FormatArg[])null) == 0)) - { - // wrap in a delegate that adjusts for format + args... - static assert(false); - } - else static if (IsAggregate!T && __traits(compiles, value.toString(buffer, "format") == 0)) - { - // wrap in a delegate that adjusts for format... - static assert(false); - } - else static if (IsAggregate!T && __traits(compiles, value.toString(buffer) == 0)) - { - // wrap in a delegate... - static assert(false); - } - else static if (IsAggregate!T && __traits(compiles, value.toString((const(char)[]){}) == 0)) - { - // version with a sink function... - // wrap in a delegate that adjusts for format + args... - static assert(false); - } - else - toString = &defFormat(value).toString; + getString = get_to_string_func(value); - static if (is(typeof(&defFormat(value).toInt))) - toInt = &defFormat(value).toInt; - else - toInt = null; + static if (can_default_int!T) + _to_int_fun = &DefInt!T.to_int; } - ptrdiff_t getString(char[] buffer, const(char)[] format, const(FormatArg)[] args) const nothrow @nogc - { - return toString(buffer, format, args); - } + StringifyFunc getString; + ptrdiff_t getLength(const(char)[] format, const(FormatArg)[] args) const nothrow @nogc - { - return toString(null, format, args); - } + => getString(null, format, args); - bool canInt() const nothrow @nogc - { - return toInt != null; - } - ptrdiff_t getInt() const nothrow @nogc + bool canInt() const pure nothrow @nogc + => _to_int_fun !is null; + + ptrdiff_t getInt() const pure nothrow @nogc { - return toInt(); + ptrdiff_t delegate() pure nothrow @nogc to_int; + to_int.ptr = getString.ptr; + to_int.funcptr = _to_int_fun; + return to_int(); } private: - // TODO: we could assert that the delegate pointers match, and only store it once... - StringifyFunc toString; - IntifyFunc toInt; + // only store the function pointer, since 'this' will be common + ptrdiff_t function() pure nothrow @nogc _to_int_fun; } private: -pragma(inline, true) -DefFormat!T* defFormat(T)(ref const(T) value) pure nothrow @nogc +import urt.array; +enum is_some_string(T) = is(T == char) || is(T : const char[]) || is(T : const(String)) || is(T : const(MutableString!N), size_t N) || is(T : const(Array!(char, N)), size_t N); + +template num_string_args(Args...) { - return cast(DefFormat!T*)&value; + static if (Args.length == 0) + enum num_string_args = 0; + else static if (is_some_string!(Args[0])) + enum num_string_args = 1 + num_string_args!(Args[1 .. $]); + else + enum num_string_args = 0; } -ptrdiff_t defToString(T)(ref const(T) value, char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) nothrow @nogc +template NormaliseArgs(Args...) { - return (cast(DefFormat!T*)&value).toString(buffer, format, formatArgs); + import urt.meta : AliasSeq; + alias NormaliseArgs = AliasSeq!(); + static foreach (Arg; Args) + NormaliseArgs = AliasSeq!(NormaliseArgs, NormaliseConst!Arg); } -struct DefFormat(T) +template NormaliseConst(T) { - T value; + static if (is(T == const(U), U) || is(T == immutable(U), U)) + alias NormaliseConst = NormaliseConst!U; + else static if (is(T == immutable(U)[], U) || is(T == U[], U)) + alias NormaliseConst = const(U)[]; + else static if (is(T == U[N], U, size_t N)) + alias NormaliseConst = const(U)[N]; + else + alias NormaliseConst = T; +} + +alias StringifyFuncReduced = ptrdiff_t delegate(char[] buffer, const(char)[] format) nothrow @nogc; +alias StringifyFuncReduced2 = ptrdiff_t delegate(char[] buffer) nothrow @nogc; + +enum can_default_int(T) = is_some_int!T || is(T == bool); - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc +template to_string_overload_index(T) +{ + static if (!is(T == Unqual!T)) // minimise redundant instantiations + enum to_string_overload_index = to_string_overload_index!(Unqual!T); + else + enum to_string_overload_index = () { + static if (__traits(hasMember, T, "toString")) + { + // multiple passes so that we correctly preference the overload with more arguments... + static foreach (i, overload; __traits(getOverloads, T, "toString", true)) + { + static if (is(typeof(&overload) : typeof(StringifyFunc.funcptr))) + return i; + else static if (is(typeof(&overload!()) : typeof(StringifyFunc.funcptr))) + return i; + } + static foreach (i, overload; __traits(getOverloads, T, "toString", true)) + { + static if (is(typeof(&overload) : typeof(StringifyFuncReduced.funcptr))) + return i; + else static if (is(typeof(&overload!()) : typeof(StringifyFuncReduced.funcptr))) + return i; + } + static foreach (i, overload; __traits(getOverloads, T, "toString", true)) + { + static if (is(typeof(&overload) : typeof(StringifyFuncReduced2.funcptr))) + return i; + else static if (is(typeof(&overload!()) : typeof(StringifyFuncReduced2.funcptr))) + return i; + } + } + return -1; + }(); +} + +StringifyFunc get_to_string_func(T)(ref T value) +{ + enum overload_index = to_string_overload_index!T; + + static if (overload_index >= 0) { - static if (is(T == typeof(null))) + alias overload = __traits(getOverloads, T, "toString", true)[overload_index]; + static if (__traits(isTemplate, overload)) + alias to_string = overload!(); + else + alias to_string = overload; + + // TODO: we can't alias the __traits(child) expression, so we need to repeat it everywhere! + + alias method_type = typeof(&__traits(child, value, to_string)); + + static if (is(method_type : StringifyFunc)) + return &__traits(child, value, to_string); + + else static if (is(method_type : StringifyFuncReduced) || is(method_type : StringifyFuncReduced2)) { - if (!buffer.ptr) - return 4; - if (buffer.length < 4) - return -1; - buffer[0 .. 4] = "null"; - return 4; + StringifyFunc d; + d.ptr = &value; + static if (is(method_type : StringifyFuncReduced)) + d.funcptr = &ToStringShim.shim1!T; + else + d.funcptr = &ToStringShim.shim2!T; + return d; } - else static if (is(T == bool)) + + // TODO: do we want to support toString variants with sink instead of buffer? + + return null; + } + else + return &(cast(DefFormat!T*)&value).toString; +} + +struct ToStringShim +{ + ptrdiff_t shim1(T)(char[] buffer, const(char)[] format, const(FormatArg)[]) + { + ref T _this = *cast(T*)&this; + return _this.toString(buffer, format); + } + ptrdiff_t shim2(T)(char[] buffer, const(char)[], const(FormatArg)[]) + { + ref T _this = *cast(T*)&this; + return _this.toString(buffer); + } +} + +struct DefInt(T) +{ + T value; + + ptrdiff_t to_int() const pure nothrow @nogc + { + static if (T.max > ptrdiff_t.max) + debug assert(value <= ptrdiff_t.max); + return cast(ptrdiff_t)value; + } +} + +ptrdiff_t defToString(T)(ref const(T) value, char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) nothrow @nogc + => (cast(DefFormat!T*)&value).toString(buffer, format, formatArgs); + +template DefFormat(T) +{ + static if (is(T == const U, U) || is(T == immutable U, U)) + alias DefFormat = DefFormat!U; + else static if (is(T == const(U)[], U) || is(T == immutable(U)[], U)) + alias DefFormat = DefFormat!(U[]); + else struct DefFormat + { + static assert(!is(T == const), "How did this slip through?"); + + const T value; + + ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc { - size_t len = value ? 4 : 5; - if (!buffer.ptr) + static if (is(T == U[N], U, size_t N)) + return defToString!(U[])(value[], buffer, format, formatArgs); + else static if (is(T : String) || is(T : MutableString!N, size_t N) || is(T : Array!(char, N), size_t N)) + return defToString!(char[])(value[], buffer, format, formatArgs); + else static if (is(T == typeof(null))) + { + if (!buffer.ptr) + return 4; + if (buffer.length < 4) + return -1; + buffer[0 .. 4] = "null"; + return 4; + } + else static if (is(T == bool)) + { + size_t len = value ? 4 : 5; + if (!buffer.ptr) + return len; + if (buffer.length < len) + return -1; + buffer[0 .. len] = value ? "true" : "false"; return len; - if (buffer.length < len) - return -1; - buffer[0 .. len] = value ? "true" : "false"; - return len; - } - else static if (is(T == char) || is(T == wchar) || is(T == dchar)) - { - if (is(T == char) || value <= 0x7F) + } + else static if (is(T == char) || is(T == wchar) || is(T == dchar)) { - if (buffer.ptr) + if (is(T == char) || value <= 0x7F) { - if (buffer.length < 1) - return -1; - buffer[0] = cast(char)value; + if (buffer.ptr) + { + if (buffer.length < 1) + return -1; + buffer[0] = cast(char)value; + } + return 1; } - return 1; - } - else if (value <= 0x7FF) - { - if (buffer.ptr) + else if (value <= 0x7FF) { - if (buffer.length < 2) - return -1; - buffer[0] = cast(char)(0xC0 | (value >> 6)); - buffer[1] = cast(char)(0x80 | (value & 0x3F)); + if (buffer.ptr) + { + if (buffer.length < 2) + return -1; + buffer[0] = cast(char)(0xC0 | (value >> 6)); + buffer[1] = cast(char)(0x80 | (value & 0x3F)); + } + return 2; } - return 2; - } - else if (value <= 0xFFFF) - { - if (buffer.ptr) + else if (value <= 0xFFFF) { - if (buffer.length < 3) - return -1; - buffer[0] = cast(char)(0xE0 | (value >> 12)); - buffer[1] = cast(char)(0x80 | ((value >> 6) & 0x3F)); - buffer[2] = cast(char)(0x80 | (value & 0x3F)); + if (buffer.ptr) + { + if (buffer.length < 3) + return -1; + buffer[0] = cast(char)(0xE0 | (value >> 12)); + buffer[1] = cast(char)(0x80 | ((value >> 6) & 0x3F)); + buffer[2] = cast(char)(0x80 | (value & 0x3F)); + } + return 3; } - return 3; - } - else if (value <= 0x10FFFF) - { - if (buffer.ptr) + else if (value <= 0x10FFFF) { - if (buffer.length < 4) - return -1; - buffer[0] = cast(char)(0xF0 | (value >> 18)); - buffer[1] = cast(char)(0x80 | ((value >> 12) & 0x3F)); - buffer[2] = cast(char)(0x80 | ((value >> 6) & 0x3F)); - buffer[3] = cast(char)(0x80 | (value & 0x3F)); + if (buffer.ptr) + { + if (buffer.length < 4) + return -1; + buffer[0] = cast(char)(0xF0 | (value >> 18)); + buffer[1] = cast(char)(0x80 | ((value >> 12) & 0x3F)); + buffer[2] = cast(char)(0x80 | ((value >> 6) & 0x3F)); + buffer[3] = cast(char)(0x80 | (value & 0x3F)); + } + return 4; + } + else + { + assert(false, "Invalid code point"); + return 0; } - return 4; } - else + else static if (is(T == double) || is(T == float)) { - assert(false, "Invalid code point"); - return 0; - } - } - else static if (is(T == double) || is(T == float)) - { - import urt.conv : formatFloat, formatInt; + import urt.conv : format_float, format_int; - char[16] tmp = void; - if (format.length && format[0] == '*') - { - bool success; - size_t arg = format[1..$].parseIntFast(success); - if (!success || !formatArgs[arg].canInt) - return -2; - size_t width = formatArgs[arg].getInt; - size_t len = width.formatInt(tmp); - format = tmp[0..len]; + char[16] tmp = void; + if (format.length && format[0] == '*') + { + bool success; + size_t arg = format[1..$].parse_int_fast(success); + if (!success || !formatArgs[arg].canInt) + return -2; + size_t width = formatArgs[arg].getInt; + size_t len = width.format_int(tmp); + format = tmp[0..len]; + } + return format_float(value, buffer, format); } - return formatFloat(value, buffer, format); - } - else static if (is(T == ulong) || is(T == long)) - { - import urt.conv : formatInt, formatUint; + else static if (is(T == ulong) || is(T == long)) + { + import urt.conv : format_int, format_uint; - // TODO: what formats are interesting for ints? + // TODO: what formats are interesting for ints? - bool leadingZeroes = false; - bool toLower = false; - bool varLen = false; - ptrdiff_t padding = 0; - uint base = 10; + bool leadingZeroes = false; + bool to_lower = false; + bool varLen = false; + ptrdiff_t padding = 0; + uint base = 10; - static if (is(T == long)) - { - bool showSign = false; - if (format.length && format[0] == '+') + static if (is(T == long)) { - showSign = true; + bool show_sign = false; + if (format.length && format[0] == '+') + { + show_sign = true; + format.popFront; + } + } + if (format.length && format[0] == '0') + { + leadingZeroes = true; format.popFront; } - } - if (format.length && format[0] == '0') - { - leadingZeroes = true; - format.popFront; - } - if (format.length && format[0] == '*') - { - varLen = true; - format.popFront; - } - if (format.length && format[0].isNumeric) - { - bool success; - padding = format.parseIntFast(success); - if (varLen) + if (format.length && format[0] == '*') { - if (padding < 0 || !formatArgs[padding].canInt) - return -2; - padding = formatArgs[padding].getInt; + varLen = true; + format.popFront; + } + if (format.length && format[0].is_numeric) + { + bool success; + padding = format.parse_int_fast(success); + if (varLen) + { + if (padding < 0 || !formatArgs[padding].canInt) + return -2; + padding = formatArgs[padding].getInt; + } + } + if (format.length) + { + char b = format[0] | 0x20; + if (b == 'x') + { + base = 16; + to_lower = format[0] == 'x' && buffer.ptr; + } + else if (b == 'b') + base = 2; + else if (b == 'o') + base = 8; + else if (b == 'd') + base = 10; + format.popFront; + } + + static if (is(T == long)) + ptrdiff_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', show_sign); + else + ptrdiff_t len = format_uint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); + + if (to_lower && len > 0) + { + for (size_t i = 0; i < len; ++i) + if (cast(uint)(buffer.ptr[i] - 'A') < 26) + buffer.ptr[i] |= 0x20; } + + return len; } - if (format.length) + else static if (is(T == ubyte) || is(T == ushort) || is(T == uint)) { - char b = format[0] | 0x20; - if (b == 'x') - { - base = 16; - toLower = format[0] == 'x' && buffer.ptr; - } - else if (b == 'b') - base = 2; - else if (b == 'o') - base = 8; - else if (b == 'd') - base = 10; - format.popFront; + return defToString(ulong(value), buffer, format, formatArgs); } - - static if (is(T == long)) - size_t len = formatInt(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', showSign); - else - size_t len = formatUint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); - - if (toLower && len > 0) + else static if (is(T == byte) || is(T == short) || is(T == int)) { - for (size_t i = 0; i < len; ++i) - if (cast(uint)(buffer.ptr[i] - 'A') < 26) - buffer.ptr[i] |= 0x20; + return defToString(long(value), buffer, format, formatArgs); } - - return len; - } - else static if (is(T == ubyte) || is(T == ushort) || is(T == uint)) - { - return defToString(ulong(value), buffer, format, formatArgs); - } - else static if (is(T == byte) || is(T == short) || is(T == int)) - { - return defToString(long(value), buffer, format, formatArgs); - } - else static if (is(T == const(char)*) || is(T == const(char*))) - { - const char[] t = value[0 .. value.strlen]; - return t.defToString(buffer, format, formatArgs); - } - else static if (is(T : const(U)*, U)) - { - static assert(size_t.sizeof == 4 || size_t.sizeof == 8); - enum Fmt = "0" ~ (size_t.sizeof == 4 ? "8" : "16") ~ "X"; - size_t p = cast(size_t)value; - return p.defToString(buffer, Fmt, null); - } - else static if (is(T == const char[])) - { - bool leftJustify = false; - bool varLen = false; - ptrdiff_t width = value.length; - if (format.length && format[0] == '-') + else static if (is(T == const(char)*)) { - leftJustify = true; - format.popFront; + const char[] t = value[0 .. value.strlen]; + return t.defToString(buffer, format, formatArgs); } - if (format.length && format[0] == '*') + else static if (is(T : const(U)*, U)) { - varLen = true; - format.popFront; + static assert(size_t.sizeof == 4 || size_t.sizeof == 8); + enum Fmt = "0" ~ (size_t.sizeof == 4 ? "8" : "16") ~ "X"; + size_t p = cast(size_t)value; + return p.defToString(buffer, Fmt, null); } - if (varLen && (!format.length || !format[0].isNumeric)) - return -2; - if (format.length && format[0].isNumeric) + else static if (is(T == char[])) { - bool success; - width = format.parseIntFast(success); - if (varLen) + bool leftJustify = false; + bool varLen = false; + ptrdiff_t width = value.length; + if (format.length && format[0] == '-') { - if (width < 0 || !formatArgs[width].canInt) - return -2; - width = formatArgs[width].getInt; + leftJustify = true; + format.popFront; + } + if (format.length && format[0] == '*') + { + varLen = true; + format.popFront; + } + if (varLen && (!format.length || !format[0].is_numeric)) + return -2; + if (format.length && format[0].is_numeric) + { + bool success; + width = format.parse_int_fast(success); + if (varLen) + { + if (width < 0 || !formatArgs[width].canInt) + return -2; + width = formatArgs[width].getInt; + } + if (width < value.length) + width = value.length; } - if (width < value.length) - width = value.length; - } - if (!buffer.ptr) - return width; - if (buffer.length < width) - return -1; + if (!buffer.ptr) + return width; + if (buffer.length < width) + return -1; - // TODO: accept padd string in the formatSpec? + // TODO: accept padd string in the formatSpec? - size_t padding = width - value.length; - size_t pad = 0, len = 0; - if (!leftJustify && padding > 0) - { - pad = buffer.length < padding ? buffer.length : padding; - buffer[0 .. pad] = ' '; - buffer.takeFront(pad); + size_t padding = width - value.length; + size_t pad = 0, len = 0; + if (!leftJustify && padding > 0) + { + pad = buffer.length < padding ? buffer.length : padding; + buffer[0 .. pad] = ' '; + buffer.takeFront(pad); + } + len = buffer.length < value.length ? buffer.length : value.length; + buffer[0 .. len] = value[0 .. len]; + if (padding > 0 && leftJustify) + { + buffer.takeFront(len); + pad = buffer.length < padding ? buffer.length : padding; + buffer[0 .. pad] = ' '; + } + return pad + len; } - len = buffer.length < value.length ? buffer.length : value.length; - buffer[0 .. len] = value[0 .. len]; - if (padding > 0 && leftJustify) +// else static if (is(T == wchar[])) +// { +// } +// else static if (is (T == U[], U : dchar)) +// { +// // TODO: UTF ENCODE... +// } + else static if (is(T == void[])) { - buffer.takeFront(len); - pad = buffer.length < padding ? buffer.length : padding; - buffer[0 .. pad] = ' '; - } - return pad + len; - } - else static if (is(T : const char[])) - { - return defToString!(const char[])(cast(const char[])value[], buffer, format, formatArgs); - } -// else static if (is(T : const(wchar)[])) -// { -// } -// else static if (is (T : const(U)[], U : dchar)) -// { -// // TODO: UTF ENCODE... -// } - else static if (is(T == void[]) || is(T == const(void)[])) - { - if (!value.length) - return 0; + if (!value.length) + return 0; - int grp1 = 1, grp2 = 0; - if (format.length && format[0].isNumeric) - { - bool success; - grp1 = cast(int)format.parseIntFast(success); - if (success && format.length > 0 && format[0] == ':' && - format.length > 1 && format[1].isNumeric) + int grp1 = 1, grp2 = 0; + if (format.length && format[0].is_numeric) { - format.popFront(); - grp2 = cast(int)format.parseIntFast(success); + bool success; + grp1 = cast(int)format.parse_int_fast(success); + if (success && format.length > 0 && format[0] == ':' && + format.length > 1 && format[1].is_numeric) + { + format.popFront(); + grp2 = cast(int)format.parse_int_fast(success); + } + if (!success) + return -2; } - if (!success) - return -2; - } - if (!buffer.ptr) - { - size_t len = value.length*2; - if (grp1) - len += (value.length-1) / grp1; - return len; - } + if (!buffer.ptr) + { + size_t len = value.length*2; + if (grp1) + len += (value.length-1) / grp1; + return len; + } - char[] hex = toHexString(cast(ubyte[])value, buffer, grp1, grp2); - return hex.length; - } - else static if (is(T : const U[], U)) - { - // arrays of other stuff - size_t len = 1; - if (buffer.ptr) - { - if (buffer.length < 1) + char[] hex = toHexString(cast(ubyte[])value, buffer, grp1, grp2); + if (!hex.ptr) return -1; - buffer[0] = '['; + return hex.length; } - - for (size_t i = 0; i < value.length; ++i) + else static if (is(T : const U[], U)) { - if (i > 0) + // arrays of other stuff + size_t len = 1; + if (buffer.ptr) + { + if (buffer.length < 1) + return -1; + buffer[0] = '['; + } + + for (size_t i = 0; i < value.length; ++i) { + if (i > 0) + { + if (buffer.ptr) + { + if (len == buffer.length) + return -1; + buffer[len] = ','; + } + ++len; + } + + FormatArg arg = FormatArg(value[i]); if (buffer.ptr) { - if (len == buffer.length) - return -1; - buffer[len] = ','; + ptrdiff_t argLen = arg.getString(buffer.ptr[len .. buffer.length], format, formatArgs); + if (argLen < 0) + return argLen; + len += argLen; } - ++len; + else + len += arg.getLength(format, formatArgs); } - FormatArg arg = FormatArg(value[i]); if (buffer.ptr) { - ptrdiff_t argLen = arg.getString(buffer.ptr[len .. buffer.length], format, formatArgs); - if (argLen < 0) - return argLen; - len += argLen; + if (len == buffer.length) + return -1; + buffer[len] = ']'; } - else - len += arg.getLength(format, formatArgs); + return ++len; } - - if (buffer.ptr) + else static if (is(T B == enum)) { - if (len == buffer.length) - return -1; - buffer[len] = ']'; - } - return ++len; - } - else static if (is(T B == enum)) - { - // TODO: optimise short enums with a TABLE! + // TODO: optimise short enums with a TABLE! - // TODO: should probably return FQN ??? - string key = null; - val: switch (value) - { - static foreach (i, KeyName; __traits(allMembers, T)) + // TODO: should probably return FQN ??? + string key = null; + val: switch (value) { - static if (!EnumKeyIsDuplicate!(T, i)) + static foreach (i, KeyName; __traits(allMembers, T)) { - case __traits(getMember, T, KeyName): - key = KeyName; - break val; + static if (!EnumKeyIsDuplicate!(T, i)) + { + case __traits(getMember, T, KeyName): + key = KeyName; + break val; + } } - } - default: - if (!buffer.ptr) - return T.stringof.length + 2 + defToString!B(cast(B)value, null, null, null); + default: + if (!buffer.ptr) + return T.stringof.length + 2 + defToString!B(cast(B)value, null, null, null); - if (buffer.length < T.stringof.length + 2) - return -1; - buffer[0 .. T.stringof.length] = T.stringof; - buffer[T.stringof.length] = '('; - ptrdiff_t len = defToString!B(*cast(B*)&value, buffer[T.stringof.length + 1 .. $], null, null); - if (len < 0) + if (buffer.length < T.stringof.length + 2) + return -1; + buffer[0 .. T.stringof.length] = T.stringof; + buffer[T.stringof.length] = '('; + ptrdiff_t len = defToString!B(*cast(B*)&value, buffer[T.stringof.length + 1 .. $], null, null); + if (len < 0) + return len; + len = T.stringof.length + 2 + len; + if (buffer.length < len) + return -1; + buffer[len - 1] = ')'; return len; - len = T.stringof.length + 2 + len; - if (buffer.length < len) - return -1; - buffer[len - 1] = ')'; + } + + size_t len = T.stringof.length + 1 + key.length; + if (!buffer.ptr) return len; - } - size_t len = T.stringof.length + 1 + key.length; - if (!buffer.ptr) + if (buffer.length < len) + return -1; + buffer[0 .. T.stringof.length] = T.stringof; + buffer[T.stringof.length] = '.'; + buffer[T.stringof.length + 1 .. len] = key[]; return len; - - if (buffer.length < len) - return -1; - buffer[0 .. T.stringof.length] = T.stringof; - buffer[T.stringof.length] = '.'; - buffer[T.stringof.length + 1 .. len] = key[]; - return len; - } - else static if (is(T == class)) - { - try + } + else static if (is(T == class)) { - const(char)[] t = (cast()value).toString(); - if (!buffer.ptr) + // HACK: class toString is not @nogc, so we'll just stringify the pointer for right now... + return defToString(cast(void*)value, buffer, format, formatArgs); +/+ + try + { + const(char)[] t = (cast()value).toString(); + if (!buffer.ptr) + return t.length; + if (buffer.length < t.length) + return -1; + buffer[0 .. t.length] = t[]; return t.length; - if (buffer.length < t.length) + } + catch (Exception) return -1; - buffer[0 .. t.length] = t[]; - return t.length; ++/ } - catch (Exception) - return -1; - } - else static if (is(T == const)) - { - return defToString!(Unqual!T)(cast()value, buffer, format, formatArgs); - } - else static if (is(T == struct)) - { - // general structs - if (buffer.ptr) + else static if (is(T == struct)) { - if (buffer.length < T.stringof.length + 2) - return -1; - buffer[0 .. T.stringof.length] = T.stringof; - buffer[T.stringof.length] = '('; - } + static assert(!__traits(hasMember, T, "toString"), "Struct with custom toString not properly selected!"); - size_t len = T.stringof.length + 1; - static foreach (i; 0 .. value.tupleof.length) - {{ - static if (i > 0) + // general structs + if (buffer.ptr) { + if (buffer.length < T.stringof.length + 2) + return -1; + buffer[0 .. T.stringof.length] = T.stringof; + buffer[T.stringof.length] = '('; + } + + size_t len = T.stringof.length + 1; + static foreach (i; 0 .. value.tupleof.length) + {{ + static if (i > 0) + { + if (buffer.ptr) + { + if (len + 2 > buffer.length) + return -1; + buffer[len .. len + 2] = ", "; + } + len += 2; + } + + FormatArg arg = FormatArg(value.tupleof[i]); if (buffer.ptr) { - if (len + 2 > buffer.length) - return -1; - buffer[len .. len + 2] = ", "; + ptrdiff_t argLen = arg.getString(buffer.ptr[len .. buffer.length], null, null); + if (argLen < 0) + return argLen; + len += argLen; } - len += 2; - } + else + len += arg.getLength(null, null); + + }} - FormatArg arg = FormatArg(value.tupleof[i]); if (buffer.ptr) { - ptrdiff_t argLen = arg.getString(buffer.ptr[len .. buffer.length], null, null); - if (argLen < 0) - return argLen; - len += argLen; + if (len == buffer.length) + return -1; + buffer[len] = ')'; } - else - len += arg.getLength(null, null); - - }} - - if (buffer.ptr) + return ++len; + } + else static if (is(T == function)) { - if (len == buffer.length) - return -1; - buffer[len] = ')'; + assert(false, "TODO"); + return 0; } - return ++len; - } - else - static assert(false, "Not implemented for type: ", T.stringof); - } - - static if (isSomeInt!T || is(T == bool)) - { - ptrdiff_t toInt() const pure nothrow @nogc - { - static if (T.max > ptrdiff_t.max) - debug assert(value <= ptrdiff_t.max); - return cast(ptrdiff_t)value; + else static if (is(T == delegate)) + { + assert(false, "TODO"); + return 0; + } + else + static assert(false, "Not implemented for type: ", T.stringof); } } } -char[] concatImpl(char[] buffer, const(FormatArg)[] args) nothrow @nogc +char[] concat_impl(char[] buffer, const(StringifyFunc)[] args) nothrow @nogc { size_t len = 0; foreach (a; args) { - ptrdiff_t r = a.getString(buffer.ptr ? buffer[len..$] : null, null, null); + ptrdiff_t r = a(buffer.ptr ? buffer[len..$] : null, null, null); if (r < 0) return null; len += r; @@ -744,7 +863,7 @@ ptrdiff_t parseFormat(ref const(char)[] format, ref char[] buffer, const(FormatA // get the arg index bool success; - arg = format.parseIntFast(success); + arg = format.parse_int_fast(success); if (!success) { assert(false, "Invalid format string: Number expected!"); @@ -807,7 +926,7 @@ ptrdiff_t parseFormat(ref const(char)[] format, ref char[] buffer, const(FormatA // } bool success; - ptrdiff_t index = formatSpec.parseIntFast(success); + ptrdiff_t index = formatSpec.parse_int_fast(success); // if (varRef) // { // if (arg < 0 || !args[arg].canInt) @@ -861,6 +980,8 @@ ptrdiff_t parseFormat(ref const(char)[] format, ref char[] buffer, const(FormatA if (bytes < 0) return -2; char[] t = formatImpl(buffer, indirectFormat.ptr[0 .. bytes], args); + if (!t.ptr) + return -1; len = t.length; } else @@ -916,40 +1037,6 @@ unittest } - - -// a template that tests is all template args are a char array, or a char -template allAreStrings(Args...) -{ - static if (Args.length == 1) - enum allAreStrings = is(Args[0] : const(char[])) || is(isSomeChar!(Args[0])); - else - enum allAreStrings = (is(Args[0] : const(char[])) || is(isSomeChar!(Args[0]))) && allAreStrings!(Args[1 .. $]); -} - -template allConstCorrectStrings(Args...) -{ - static if (Args.length == 1) - enum allConstCorrectStrings = is(Args[0] == const(char[])) || is(Args[0] == const char); - else - enum allConstCorrectStrings = (is(Args[0] == const(char[])) || is(Args[0] == const char)) && allConstCorrectStrings!(Args[1 .. $]); -} - -template constCorrectedStrings(Args...) -{ - import urt.meta : AliasSeq; - alias constCorrectedStrings = AliasSeq!(); - static foreach (Ty; Args) - { - static if (is(Ty : const(char)[])) - constCorrectedStrings = AliasSeq!(constCorrectedStrings, const(char[])); - else static if (isSomeChar!Ty) - constCorrectedStrings = AliasSeq!(constCorrectedStrings, const(char)); - else - static assert(false, "Argument must be a char array or a char: ", T); - } -} - template EnumKeyIsDuplicate(Enum, size_t item) { alias Elements = __traits(allMembers, Enum); diff --git a/src/urt/string/package.d b/src/urt/string/package.d index c4abc6f..77ec36b 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -1,132 +1,249 @@ module urt.string; +import urt.string.uni; +import urt.traits : is_some_char; + public import urt.string.ascii; public import urt.string.string; public import urt.string.tailstring; // seful string operations defined elsewhere public import urt.array : empty, popFront, popBack, takeFront, takeBack; -public import urt.mem : strlen; - - -enum TempStringBufferLen = 1024; -enum TempStringMaxLen = TempStringBufferLen / 2; +public import urt.mem : strlen, wcslen; +public import urt.mem.temp : tstringz, twstringz; -static char[TempStringBufferLen] s_tempStringBuffer; -static size_t s_tempStringBufferPos = 0; +nothrow @nogc: -char[] allocTempString(size_t len) nothrow @nogc +size_t strlen_s(const(char)[] s) pure { - assert(len <= TempStringMaxLen); + size_t len = 0; + while (len < s.length && s[len] != '\0') + ++len; + return len; +} - if (len <= TempStringBufferLen - s_tempStringBufferPos) +ptrdiff_t cmp(bool case_insensitive = false, T, U)(const(T)[] a, const(U)[] b) pure +{ + static if (case_insensitive) + return uni_compare_i(a, b); + else { - char[] s = s_tempStringBuffer[s_tempStringBufferPos .. s_tempStringBufferPos + len]; - s_tempStringBufferPos += len; - return s; + static if (is(T == U)) + { + if (a.length != b.length) + return a.length - b.length; + } + return uni_compare(a, b); } - s_tempStringBufferPos = len; - return s_tempStringBuffer[0 .. len]; } -char* tstringz(const(char)[] str) nothrow @nogc -{ - char[] buffer = allocTempString(str.length + 1); - buffer[0..str.length] = str[]; - buffer[str.length] = 0; - return buffer.ptr; -} +ptrdiff_t icmp(T, U)(const(T)[] a, const(U)[] b) pure + => cmp!true(a, b); + +bool eq(const(char)[] a, const(char)[] b) pure + => cmp(a, b) == 0; + +bool ieq(const(char)[] a, const(char)[] b) pure + => cmp!true(a, b) == 0; -wchar* twstringz(const(char)[] str) nothrow @nogc +size_t findFirst(bool case_insensitive = false, T, U)(const(T)[] s, const U c) + if (is_some_char!T && is_some_char!U) { - wchar[] buffer = cast(wchar[])allocTempString((str.length + 1) * 2); + static if (is(U == char)) + assert(c <= 0x7F, "Invalid UTF character"); + else static if (is(U == wchar)) + assert(c < 0xD800 || c >= 0xE000, "Invalid UTF character"); - // TODO: actually decode UTF8 into UTF16!! + // TODO: what if `c` is 'ß'? do we find "ss" in case-insensitive mode? + // and if `c` is 's', do we match 'ß'? + + static if (case_insensitive) + const U lc = cast(U)c.uni_case_fold(); + else + alias lc = c; - foreach (i, c; str) - buffer[i] = c; - buffer[str.length] = 0; - return buffer.ptr; + size_t i = 0; + while (i < s.length) + { + static if (U.sizeof <= T.sizeof) + { + enum l = 1; + dchar d = s[i]; + } + else + { + size_t l; + dchar d = next_dchar(s[i..$], l); + } + static if (case_insensitive) + { + static if (is(U == char)) + { + // only fold the ascii characters, since lc is known to be ascii + if (uint(d - 'A') < 26) + d |= 0x20; + } + else + d = d.uni_case_fold(); + } + if (d == lc) + break; + i += l; + } + return i; } -ptrdiff_t cmp(const(char)[] a, const(char)[] b) pure nothrow @nogc +size_t find_first_i(T, U)(const(T)[] s, U c) + if (is_some_char!T && is_some_char!U) + => findFirst!true(s, c); + +size_t findLast(bool case_insensitive = false, T, U)(const(T)[] s, const U c) + if (is_some_char!T && is_some_char!U) { - if (a.length != b.length) - return a.length - b.length; - for (size_t i = 0; i < a.length; ++i) + static assert(case_insensitive == false, "TODO"); + + static if (is(U == char)) + assert(c <= 0x7F, "Invalid unicode character"); + else static if (is(U == wchar)) + assert(c >= 0xD800 && c < 0xE000, "Invalid unicode character"); + + ptrdiff_t last = s.length-1; + while (last >= 0) { - ptrdiff_t diff = a[i] - b[i]; - if (diff) - return diff; + static if (U.sizeof <= T.sizeof) + { + if (s[last] == c) + return cast(size_t)last; + } + else + { + // this is tricky, because we need to seek backwards to the start of surrogate sequences + assert(false, "TODO"); + } } - return 0; + return s.length; } -ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure nothrow @nogc +size_t find_last_i(T, U)(const(T)[] s, U c) + if (is_some_char!T && is_some_char!U) + => findLast!true(s, c); + +size_t findFirst(bool case_insensitive = false, T, U)(const(T)[] s, const(U)[] t) + if (is_some_char!T && is_some_char!U) { - if (a.length != b.length) - return a.length - b.length; - for (size_t i = 0; i < a.length; ++i) + if (t.length == 0) + return 0; + + // fast-path for one-length tokens + size_t l = t.uni_seq_len(); + if (l == t.length) { - ptrdiff_t diff = toLower(a[i]) - toLower(b[i]); - if (diff) - return diff; + dchar c = t.next_dchar(l); + if (c < 0x80) + return findFirst!case_insensitive(s, cast(char)c); + if (c < 0x10000) + return findFirst!case_insensitive(s, cast(wchar)c); + return findFirst!case_insensitive(s, c); } - return 0; + + size_t offset = 0; + while (offset < s.length) + { + + static if (case_insensitive) + int c = uni_compare_i(s[offset .. $], t); + else + int c = uni_compare(s[offset .. $], t); + if (c == int.max || c == 0) + return offset; + if (c == int.min) + return s.length; + offset += s[offset .. $].uni_seq_len(); + } + return s.length; +} + +size_t find_first_i(T, U)(const(T)[] s, const(U)[] t) + if (is_some_char!T && is_some_char!U) + => findFirst!true(s, t); + +size_t findLast(bool case_insensitive = false, T, U)(const(T)[] s, const(U)[] t) + if (is_some_char!T && is_some_char!U) +{ + // this is tricky, because we need to seek backwards to the start of surrogate sequences + assert(false, "TODO"); } -bool ieq(const(char)[] a, const(char)[] b) pure nothrow @nogc - => icmp(a, b) == 0; +size_t find_last_i(T, U)(const(T)[] s, const(U)[] t) + if (is_some_char!T && is_some_char!U) + => findLast!true(s, t); -bool startsWith(const(char)[] s, const(char)[] prefix) pure nothrow @nogc +bool contains(bool case_insensitive = false, T, U)(const(T)[] s, U c, size_t *offset = null) + if (is_some_char!T && is_some_char!U) +{ + size_t i = findFirst!case_insensitive(s, c); + if (i == s.length) + return false; + if (offset) + *offset = i; + return true; +} + +bool contains(bool case_insensitive = false, T, U)(const(T)[] s, const(U)[] t, size_t *offset = null) + if (is_some_char!T && is_some_char!U) +{ + size_t i = findFirst!case_insensitive(s, t); + if (i == s.length) + return false; + if (offset) + *offset = i; + return true; +} + +bool contains_i(T, U)(const(T)[] s, U c, size_t *offset = null) + if (is_some_char!T && is_some_char!U) + => contains!true(s, c, offset); + +bool contains_i(T, U)(const(T)[] s, const(U)[] t, size_t *offset = null) + if (is_some_char!T && is_some_char!U) + => contains!true(s, t, offset); + +bool startsWith(const(char)[] s, const(char)[] prefix) pure { if (s.length < prefix.length) return false; - return s[0 .. prefix.length] == prefix[]; + return cmp(s[0 .. prefix.length], prefix) == 0; } -bool endsWith(const(char)[] s, const(char)[] suffix) pure nothrow @nogc +bool endsWith(const(char)[] s, const(char)[] suffix) pure { if (s.length < suffix.length) return false; - return s[$ - suffix.length .. $] == suffix[]; + return cmp(s[$ - suffix.length .. $], suffix) == 0; } -inout(char)[] trim(bool Front = true, bool Back = true)(inout(char)[] s) pure nothrow @nogc +inout(char)[] trim(alias pred = is_whitespace, bool Front = true, bool Back = true)(inout(char)[] s) pure { size_t first = 0, last = s.length; static if (Front) { - while (first < s.length && isWhitespace(s.ptr[first])) + while (first < s.length && pred(s.ptr[first])) ++first; } static if (Back) { - while (last > first && isWhitespace(s.ptr[last - 1])) + while (last > first && pred(s.ptr[last - 1])) --last; } return s.ptr[first .. last]; } -alias trimFront = trim!(true, false); +alias trimFront(alias pred = is_whitespace) = trim!(pred, true, false); -alias trimBack = trim!(false, true); +alias trimBack(alias pred = is_whitespace) = trim!(pred, false, true); -inout(char)[] trimComment(char Delimiter)(inout(char)[] s) -{ - size_t i = 0; - for (; i < s.length; ++i) - { - if (s[i] == Delimiter) - break; - } - while(i > 0 && (s[i-1] == ' ' || s[i-1] == '\t')) - --i; - return s[0 .. i]; -} - -inout(char)[] takeLine(ref inout(char)[] s) pure nothrow @nogc +inout(char)[] takeLine(ref inout(char)[] s) pure { for (size_t i = 0; i < s.length; ++i) { @@ -148,20 +265,26 @@ inout(char)[] takeLine(ref inout(char)[] s) pure nothrow @nogc return t; } -inout(char)[] split(char Separator, bool HandleQuotes = true)(ref inout(char)[] s) +inout(char)[] split(char[] separators, bool handle_quotes = true, bool do_trim = true)(ref inout(char)[] s, char* separator = null) pure { - static if (HandleQuotes) + static if (handle_quotes) int inQuotes = 0; else enum inQuotes = false; size_t i = 0; - for (; i < s.length; ++i) + loop: for (; i < s.length; ++i) { - if (s[i] == Separator && !inQuotes) - break; - - static if (HandleQuotes) + static foreach (sep; separators) + { + if (s[i] == sep && !inQuotes) + { + if (separator) + *separator = s[i]; + break loop; + } + } + static if (handle_quotes) { if (s[i] == '"' && !(inQuotes & 0x6)) inQuotes = 1 - inQuotes; @@ -171,40 +294,29 @@ inout(char)[] split(char Separator, bool HandleQuotes = true)(ref inout(char)[] inQuotes = 4 - inQuotes; } } - inout(char)[] t = s[0 .. i].trimBack; - s = i < s.length ? s[i+1 .. $].trimFront : null; + static if (do_trim) + { + inout(char)[] t = s[0 .. i].trimBack; + s = i < s.length ? s[i+1 .. $].trimFront : null; + } + else + { + inout(char)[] t = s[0 .. i]; + s = i < s.length ? s[i+1 .. $] : null; + } return t; } -inout(char)[] split(Separator...)(ref inout(char)[] s, out char sep) +alias split(char separator, bool handle_quotes = true, bool do_trim = true) = split!([separator], handle_quotes, do_trim); + +// TODO: deprecate this one... +inout(char)[] split(separators...)(ref inout(char)[] s, out char sep) pure { sep = '\0'; - int inQuotes = 0; - size_t i = 0; - loop: for (; i < s.length; ++i) - { - static foreach (S; Separator) - { - static assert(is(typeof(S) == char), "Only single character separators supported"); - if (s[i] == S && !inQuotes) - { - sep = s[i]; - break loop; - } - } - if (s[i] == '"' && !(inQuotes & 0x6)) - inQuotes = 1 - inQuotes; - else if (s[i] == '\'' && !(inQuotes & 0x5)) - inQuotes = 2 - inQuotes; - else if (s[i] == '`' && !(inQuotes & 0x3)) - inQuotes = 4 - inQuotes; - } - inout(char)[] t = s[0 .. i].trimBack; - s = i < s.length ? s[i+1 .. $].trimFront : null; - return t; + return split!([separators], true, true)(s, &sep); } -char[] unQuote(const(char)[] s, char[] buffer) pure nothrow @nogc +char[] unQuote(const(char)[] s, char[] buffer) pure { // TODO: should this scan and match quotes rather than assuming there are no rogue closing quotes in the middle of the string? if (s.empty) @@ -223,18 +335,18 @@ char[] unQuote(const(char)[] s, char[] buffer) pure nothrow @nogc return buffer; } -char[] unQuote(char[] s) pure nothrow @nogc +char[] unQuote(char[] s) pure { return unQuote(s, s); } -char[] unQuote(const(char)[] s) nothrow @nogc +char[] unQuote(const(char)[] s) { import urt.mem.temp : talloc; return unQuote(s, cast(char[])talloc(s.length)); } -char[] unEscape(inout(char)[] s, char[] buffer) pure nothrow @nogc +char[] unEscape(inout(char)[] s, char[] buffer) pure { if (s.empty) return null; @@ -266,17 +378,17 @@ char[] unEscape(inout(char)[] s, char[] buffer) pure nothrow @nogc return buffer[0..len]; } -char[] unEscape(char[] s) pure nothrow @nogc +char[] unEscape(char[] s) pure { return unEscape(s, s); } -char[] toHexString(const(void[]) data, char[] buffer, uint group = 0, uint secondaryGroup = 0, const(char)[] seps = " -") pure nothrow @nogc +char[] toHexString(const(void[]) data, char[] buffer, uint group = 0, uint secondaryGroup = 0, const(char)[] seps = " -") pure { - import urt.util : isPowerOf2; - assert(group.isPowerOf2); - assert(secondaryGroup.isPowerOf2); + import urt.util : is_power_of_2; + assert(group.is_power_of_2); + assert(secondaryGroup.is_power_of_2); assert((secondaryGroup == 0 && seps.length > 0) || seps.length > 1, "Secondary grouping requires additional separator"); if (data.length == 0) @@ -296,8 +408,8 @@ char[] toHexString(const(void[]) data, char[] buffer, uint group = 0, uint secon size_t offset = 0; for (size_t i = 0; true; ) { - buffer[offset++] = hexDigits[src[i] >> 4]; - buffer[offset++] = hexDigits[src[i] & 0xF]; + buffer[offset++] = hex_digits[src[i] >> 4]; + buffer[offset++] = hex_digits[src[i] & 0xF]; bool sep = (i & mask) == mask; if (++i == data.length) @@ -307,7 +419,7 @@ char[] toHexString(const(void[]) data, char[] buffer, uint group = 0, uint secon } } -char[] toHexString(const(ubyte[]) data, uint group = 0, uint secondaryGroup = 0, const(char)[] seps = " -") nothrow @nogc +char[] toHexString(const(ubyte[]) data, uint group = 0, uint secondaryGroup = 0, const(char)[] seps = " -") { import urt.mem.temp; @@ -319,7 +431,7 @@ char[] toHexString(const(ubyte[]) data, uint group = 0, uint secondaryGroup = 0, unittest { - ubyte[] data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]; + ubyte[8] data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]; assert(data.toHexString(0) == "0123456789ABCDEF"); assert(data.toHexString(1) == "01 23 45 67 89 AB CD EF"); assert(data.toHexString(2) == "0123 4567 89AB CDEF"); @@ -329,17 +441,476 @@ unittest } -bool wildcardMatch(const(char)[] wildcard, const(char)[] value) +// Pattern grammar: +// * - Match zero or more characters +// ? - Match exactly one character +// # - Match exactly one digit (0-9) +// ~T - Token T is optional (matches with or without T) +// \C - Escape character C (literal match) +// Examples: +// "h*o" matches "hello", "ho" +// "h?llo" matches "hello", "hallo" but not "hllo" +// "v#.#" matches "v1.2", "v3.7" but not "va.b" +// "~ab" matches "ab", "b" +// "h\*o" matches "h*o" literally +bool wildcard_match(const(char)[] wildcard, const(char)[] value, bool value_wildcard = false, bool case_insensitive = false) pure { - // TODO: write this function... + const(char)* a = wildcard.ptr, ae = a + wildcard.length, b = value.ptr, be = b + value.length; - // HACK: we just use this for tail wildcards right now... - for (size_t i = 0; i < wildcard.length; ++i) + static inout(char)* skip_wilds(inout(char)* p, const(char)* pe) { - if (wildcard[i] == '*') - return true; - if (wildcard[i] != value[i]) + while (p < pe) + { + if (*p == '*') + ++p; + else if (*p == '~') + { + one_more: + if (++p < pe) + { + if (*p == '~') + goto one_more; + if (*p++ == '\\' && p < pe) + ++p; + } + } + else break; + } + // HACK: skip trailing slash (handle a degenerate case) + if (p == pe - 1 && *p == '\\') + ++p; + return p; + } + + struct BacktrackState + { + const(char)* a, b; + } + BacktrackState[64] backtrack_stack = void; + size_t backtrack_depth = 0; + + char ca_orig = void, cb_orig = void, ca = void, cb = void; + + while (a < ae && b < be) + { + ca_orig = *a, cb_orig = *b; + + // handle wildcards + if (ca_orig == '*') + { + a = skip_wilds(++a, ae); + if (a == ae) // tail star matches everything + return true; + + const(char)[] a_remain = a[0 .. ae - a]; + for (; b <= be; ++b) + { + if (wildcard_match(a_remain, b[0 .. be - b], value_wildcard, case_insensitive)) + return true; + } return false; + } + + // handle optionals + if (ca_orig == '~') + { + while (++a < ae && *a == '~') {} + if (a == ae) + break; + + if (backtrack_depth < backtrack_stack.length) + { + backtrack_stack[backtrack_depth].a = a + (*a == '\\' ? 2 : 1); + backtrack_stack[backtrack_depth].b = b; + ++backtrack_depth; + } + continue; + } + + // handle escape + ca = ca_orig, cb = cb_orig; + if (ca == '\\') + { + if (++a < ae) + ca = *a; + else + break; + } + + // handle value wildcards + if (value_wildcard) + { + if (cb_orig == '*') + { + b = skip_wilds(++b, be); + if (b == be) // tail star matches everything + return true; + + const(char)[] b_remain = b[0 .. be - b]; + for (; a <= ae; ++a) + { + if (wildcard_match(a[0 .. ae - a], b_remain, true, case_insensitive)) + return true; + } + return false; + } + if (cb_orig == '~') + { + while (++b < be && *b == '~') {} + if (b == be) + break; + + if (backtrack_depth < backtrack_stack.length) + { + backtrack_stack[backtrack_depth].a = a; + backtrack_stack[backtrack_depth].b = b + (*b == '\\' ? 2 : 1); + ++backtrack_depth; + } + continue; + } + if (cb == '\\') + { + if (++b < be) + cb = *b; + else + break; + } + if (cb_orig == '#') + { + if (ca.is_numeric || ca_orig == '#') + goto advance; + } + } + + // compare next char + if (ca_orig == '#') + { + if (cb.is_numeric) + goto advance; + } + else if ((case_insensitive ? ca.to_lower == cb.to_lower : ca == cb) || (ca_orig == '?') || (value_wildcard && cb_orig == '?')) + goto advance; + + try_backtrack: + if (backtrack_depth == 0) + return false; + + --backtrack_depth; + a = backtrack_stack[backtrack_depth].a; + b = backtrack_stack[backtrack_depth].b; + continue; + + advance: + ++a, ++b; + } + + // check the strings are equal... + if (a == ae) + { + if (value_wildcard) + b = skip_wilds(b, be); + if (b == be) + return true; } - return wildcard.length == value.length; + else if (b == be && skip_wilds(a, ae) == ae) + return true; + + goto try_backtrack; +} + +bool wildcard_match_i(const(char)[] wildcard, const(char)[] value, bool value_wildcard = false) pure + => wildcard_match(wildcard, value, value_wildcard, true); + + +unittest +{ + // test findFirst + assert("hello".findFirst('e') == 1); + assert("hello".findFirst('a') == 5); + assert("hello".findFirst("e") == 1); + assert("hello".findFirst("ll") == 2); + assert("hello".findFirst("lo") == 3); + assert("hello".findFirst("la") == 5); + assert("hello".findFirst("low") == 5); + assert("héllo".findFirst('é') == 1); + assert("héllo"w.findFirst('é') == 1); + assert("héllo".findFirst("éll") == 1); + assert("héllo".findFirst('a') == 6); + assert("héllo".findFirst("la") == 6); + assert("hello".find_first_i('E') == 1); + assert("HELLO".find_first_i("e") == 1); + assert("hello".find_first_i("LL") == 2); + assert("héllo".find_first_i('É') == 1); + assert("HÉLLO".find_first_i("é") == 1); + assert("HÉLLO".find_first_i("éll") == 1); + + assert("HÉLLO".contains('É')); + assert(!"HÉLLO".contains('A')); + assert("HÉLLO".contains_i("éll")); + + // test wildcard_match + assert(wildcard_match("hello", "hello")); + assert(!wildcard_match("hello", "world")); + assert(wildcard_match("h*o", "hello")); + assert(wildcard_match("h*", "hello")); + assert(wildcard_match("*o", "hello")); + assert(wildcard_match("*", "hello")); + assert(wildcard_match("h?llo", "hello")); + assert(!wildcard_match("h?llo", "hllo")); + assert(wildcard_match("h??lo", "hello")); + assert(!wildcard_match("a*b", "axxxc")); + + // test optional - basic cases + assert(wildcard_match("~ab", "b")); + assert(wildcard_match("~ab", "ab")); + assert(wildcard_match("a~b", "a")); + assert(wildcard_match("a~b", "ab")); + assert(!wildcard_match("~ab", "a")); + assert(!wildcard_match("a~b", "b")); + assert(!wildcard_match("a~b", "ac")); + + // optional - multiple optionals + assert(wildcard_match("~a~b", "")); + assert(wildcard_match("~a~b", "a")); + assert(wildcard_match("~a~b", "b")); + assert(wildcard_match("~a~b", "ab")); + assert(!wildcard_match("~a~b", "ba")); + assert(wildcard_match("~a~b~c", "")); + assert(wildcard_match("~a~b~c", "ac")); + assert(wildcard_match("~a~b~c", "abc")); + + // optional with wildcards + assert(wildcard_match("~a*", "")); + assert(wildcard_match("~a*", "a")); + assert(wildcard_match("~a*", "abc")); + assert(wildcard_match("*~a", "")); + assert(wildcard_match("*~a", "a")); + assert(wildcard_match("*~a", "xya")); + assert(wildcard_match("a~b*c", "ac")); + assert(wildcard_match("a~b*c", "abc")); + assert(wildcard_match("a~b*c", "abxc")); + assert(wildcard_match("a~b*c", "axbc")); + assert(!wildcard_match("a*~bc", "a")); + assert(wildcard_match("a*~bc", "ac")); + assert(wildcard_match("a*~bc", "abc")); + assert(wildcard_match("a*~bc", "axbc")); + + // optional with ? + assert(wildcard_match("~a?", "a")); + assert(wildcard_match("~a?", "x")); + assert(wildcard_match("~a?", "ax")); + assert(wildcard_match("?~a", "x")); + assert(wildcard_match("?~a", "xa")); + assert(wildcard_match("?~a", "a")); + assert(wildcard_match("?~a", "aa")); + + // optional with # + assert(wildcard_match("~a#", "5")); + assert(wildcard_match("~a#", "a5")); + assert(!wildcard_match("~a#", "a")); + assert(!wildcard_match("~a#", "ax")); + assert(wildcard_match("v~#.#", "v.5")); + assert(wildcard_match("v~#.#", "v1.5")); + + // optional with escape + assert(wildcard_match("~\\*", "")); + assert(wildcard_match("~\\*", "*")); + assert(!wildcard_match("~\\*", "x")); + assert(wildcard_match("a~\\*b", "ab")); + assert(wildcard_match("a~\\*b", "a*b")); + assert(!wildcard_match("a~\\*b", "axb")); + + // double optional ~~ + assert(wildcard_match("~~a", "")); + assert(wildcard_match("~~a", "a")); + assert(!wildcard_match("~~a", "aa")); + + // degenerates + assert(wildcard_match("a\\", "a")); + assert(!wildcard_match("a\\", "")); + assert(!wildcard_match("a\\", "x")); + assert(wildcard_match("a~", "a")); + assert(!wildcard_match("a~", "")); + assert(!wildcard_match("a~", "x")); + assert(wildcard_match("a~\\", "a")); + assert(!wildcard_match("a~\\", "")); + assert(!wildcard_match("a~\\", "x")); + + // optional with value_wildcard - basic + assert(wildcard_match("~b", "", true)); + assert(wildcard_match("~b", "b", true)); + assert(wildcard_match("", "~b", true)); + assert(wildcard_match("b", "~b", true)); + assert(wildcard_match("ab", "~ab", true)); + assert(wildcard_match("ab", "~a~b", true)); + assert(wildcard_match("~ab", "ab", true)); + assert(wildcard_match("~ab", "~ab", true)); + assert(wildcard_match("~ab", "~a~b", true)); + assert(wildcard_match("a~b", "~ab", true)); + assert(wildcard_match("a~b", "~a~b", true)); + assert(wildcard_match("~a~b", "~ab", true)); + assert(wildcard_match("~a~b", "~a~b", true)); + + // optional with value_wildcard - with wildcards on rhs + assert(wildcard_match("~a", "*", true)); + assert(wildcard_match("~ab", "*", true)); + assert(wildcard_match("~a~b", "*", true)); + assert(wildcard_match("*", "~a", true)); + assert(wildcard_match("*", "~ab", true)); + assert(wildcard_match("*", "~a~b", true)); + assert(wildcard_match("~a", "?", true)); + assert(wildcard_match("?", "~a", true)); + assert(wildcard_match("~ab", "?", true)); + assert(wildcard_match("~ab", "?b", true)); + assert(wildcard_match("~ab", "a?", true)); + assert(wildcard_match("~ab", "??", true)); + assert(wildcard_match("?", "~ab", true)); + assert(wildcard_match("?b", "~ab", true)); + assert(wildcard_match("a?", "~ab", true)); + assert(wildcard_match("??", "~ab", true)); + assert(wildcard_match("~ab", "?b", true)); + assert(wildcard_match("~abc", "?c", true)); + assert(wildcard_match("~a*", "*", true)); + assert(wildcard_match("*~a", "*", true)); + assert(wildcard_match("*", "~a*", true)); + assert(wildcard_match("*", "*~a", true)); + assert(wildcard_match("ab", "*~c", true)); + assert(wildcard_match("abc", "a?~c", true)); + + // optional on both sides with wildcards + assert(wildcard_match("~a*", "*~b", true)); + assert(wildcard_match("a~b*", "*~cd", true)); + assert(wildcard_match("~ab", "*b", true)); + assert(wildcard_match("~ab", "*ab", true)); + assert(wildcard_match("x~ab", "*ab", true)); + assert(wildcard_match("*b", "~ab", true)); + assert(wildcard_match("*ab", "~ab", true)); + assert(wildcard_match("*ab", "x~ab", true)); + assert(wildcard_match("~a~b", "~c~d", true)); + assert(wildcard_match("~a~b", "~c~da", true)); + assert(wildcard_match("~a~b", "~c~db", true)); + assert(wildcard_match("~a~b", "~c~dab", true)); + assert(wildcard_match("~a~bc", "~c~d", true)); + assert(wildcard_match("~a~bd", "~c~d", true)); + assert(wildcard_match("~a~bcd", "~c~d", true)); + assert(wildcard_match("*a~bc*d", "xxacxxd", true)); + assert(!wildcard_match("*a~bc*de", "xxabcxxd", true)); + assert(!wildcard_match("*a~bc*d", "xxabcxxde", true)); + assert(wildcard_match("*a~bc*d", "~x~x~a~c~x~x~d", true)); + + // case insensitive with optional + assert(wildcard_match_i("~a", "A")); + assert(wildcard_match_i("~aB", "Ab")); + assert(wildcard_match_i("A~b", "aB")); + + // multiple wildcards + assert(wildcard_match("*l*o", "hello")); + assert(wildcard_match("h*l*o", "hello")); + assert(wildcard_match("h*l*", "hello")); + assert(wildcard_match("*e*l*", "hello")); + assert(wildcard_match("*h*e*l*l*o*", "hello")); + + // wildcards with sequences in between + assert(wildcard_match("h*ll*", "hello")); + assert(wildcard_match("*el*", "hello")); + assert(wildcard_match("h*el*o", "hello")); + assert(!wildcard_match("h*el*x", "hello")); + assert(wildcard_match("*lo", "hello")); + assert(!wildcard_match("*lx", "hello")); + + // mixed wildcards and single matches + assert(wildcard_match("h?*o", "hello")); + assert(wildcard_match("h*?o", "hello")); + assert(wildcard_match("?e*o", "hello")); + assert(wildcard_match("h?ll?", "hello")); + assert(!wildcard_match("h?ll?", "hllo")); + + // overlapping wildcards + assert(wildcard_match("**hello", "hello")); + assert(wildcard_match("hello**", "hello")); + assert(wildcard_match("h**o", "hello")); + assert(wildcard_match("*?*", "hello")); + assert(wildcard_match("?*?", "hello")); + assert(!wildcard_match("?*?", "x")); + assert(wildcard_match("?*?", "xx")); + + // escape sequences + assert(wildcard_match("\\*", "*")); + assert(wildcard_match("\\?", "?")); + assert(wildcard_match("\\#", "#")); + assert(wildcard_match("\\\\", "\\")); + assert(!wildcard_match("\\*", "a")); + assert(!wildcard_match("\\?", "a")); + assert(!wildcard_match("\\#", "5")); + assert(wildcard_match("h\\*o", "h*o")); + assert(!wildcard_match("h\\*o", "hello")); + assert(wildcard_match("\\*\\?\\\\", "*?\\")); + assert(wildcard_match("a\\*b*c", "a*bxyzc")); + assert(wildcard_match("*\\**", "hello*world")); + + // edge cases + assert(wildcard_match("", "")); + assert(!wildcard_match("", "a")); + assert(wildcard_match("*", "")); + assert(wildcard_match("**", "")); + assert(!wildcard_match("?", "")); + assert(wildcard_match("a*b*c", "abc")); + assert(wildcard_match("a*b*c", "aXbYc")); + assert(wildcard_match("a*b*c", "aXXbYYc")); + + // value_wildcard tests - bidirectional matching + assert(wildcard_match("hello", "h*o", true)); + assert(wildcard_match("h*o", "hello", true)); + assert(wildcard_match("h?llo", "he?lo", true)); + assert(wildcard_match("h\\*o", "h\\*o", true)); + assert(wildcard_match("test*", "*test", true)); + + // both sides have wildcards + assert(wildcard_match("h*o", "h*o", true)); + assert(wildcard_match("*hello*", "*world*", true)); + assert(wildcard_match("a*b*c", "a*b*c", true)); + assert(wildcard_match("*", "*", true)); + assert(wildcard_match("?", "?", true)); + + // complex interplay - wildcards matching wildcards + assert(wildcard_match("a*c", "a?c", true)); + assert(wildcard_match("a?c", "a*c", true)); + assert(wildcard_match("*abc", "?abc", true)); + assert(wildcard_match("abc*", "abc?", true)); + + // multiple wildcards on both sides + assert(wildcard_match("a*b*c", "a?b?c", true)); + assert(wildcard_match("*a*b*", "?a?b?", true)); + assert(wildcard_match("a**b", "a*b", true)); + assert(wildcard_match("a*b", "a**b", true)); + + // wildcards at different positions + assert(wildcard_match("*test", "test*", true)); + assert(wildcard_match("test*end", "*end", true)); + assert(wildcard_match("*middle*", "start*", true)); + + // edge cases with value_wildcard + assert(wildcard_match("", "", true)); + assert(wildcard_match("*", "", true)); + assert(wildcard_match("", "*", true)); + assert(wildcard_match("**", "*", true)); + assert(wildcard_match("*", "**", true)); + + // digit wildcard tests + assert(wildcard_match("#", "0")); + assert(wildcard_match("#", "5")); + assert(wildcard_match("#", "9")); + assert(!wildcard_match("#", "a")); + assert(!wildcard_match("#", "A")); + assert(!wildcard_match("#", "")); + assert(!wildcard_match("#", "12")); + assert(!wildcard_match("#", "#")); + assert(wildcard_match("#", "#", true)); + assert(!wildcard_match("#", "\\#", true)); + assert(wildcard_match("v#.#", "v1.2")); + assert(!wildcard_match("v#.#", "va.b")); + assert(!wildcard_match("v#.#", "v1.")); + assert(wildcard_match("port-##", "port-42")); + assert(wildcard_match("#*#", "123")); + assert(!wildcard_match("#*#", "abc")); } diff --git a/src/urt/string/string.d b/src/urt/string/string.d index b1281cd..47c7489 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -3,7 +3,7 @@ module urt.string.string; import urt.lifetime : forward, move; import urt.mem; import urt.mem.string : CacheString; -import urt.hash : fnv1aHash, fnv1aHash64; +import urt.hash : fnv1a, fnv1a64; import urt.string.tailstring : TailString; public import urt.array : Alloc_T, Alloc, Reserve_T, Reserve, Concat_T, Concat; @@ -23,8 +23,7 @@ enum StringAlloc : ubyte TempString, // allocates in the temp ring buffer; could be overwritten at any time! // these must be last... (because comparison logic) - StringCache, // writes to the immutable string cache - StringCacheDedup, // writes to the immutable string cache with de-duplication + StringCache, // writes to the immutable string cache with de-duplication } struct StringAllocator @@ -33,6 +32,61 @@ struct StringAllocator void delegate(char* s) nothrow @nogc free; } +struct StringCacheBuilder +{ +nothrow @nogc: + this(char[] buffer) pure + { + assert(buffer.length >= 2 && buffer.length <= ushort.max, "Invalid buffer length"); + buffer[0..2] = 0; + this._buffer = buffer; + this._offset = 2; + } + + ushort add_string(const(char)[] s) pure + { + if (s.length == 0) + return 0; + + assert(s.length <= MaxStringLen, "String too long"); + assert(_offset + s.length + 2 + (s.length & 1) <= _buffer.length, "Not enough space in buffer"); + if (__ctfe) + { + version (LittleEndian) + { + _buffer[_offset + 0] = cast(char)(s.length & 0xFF); + _buffer[_offset + 1] = cast(char)(s.length >> 8); + } + else + { + _buffer[_offset + 0] = cast(char)(s.length >> 8); + _buffer[_offset + 1] = cast(char)(s.length & 0xFF); + } + } + else + *cast(ushort*)(_buffer.ptr + _offset) = cast(ushort)s.length; + + ushort result = cast(ushort)(_offset + 2); + _buffer[result .. result + s.length] = s[]; + _offset = cast(ushort)(result + s.length); + if (_offset & 1) + _buffer[_offset++] = '\0'; + return result; + } + + size_t used() const pure + => _offset; + + size_t remaining() const pure + => _buffer.length - _offset; + + bool full() const pure + => _offset == _buffer.length; + +private: + char[] _buffer; + ushort _offset; +} //enum String StringLit(string s) = s.makeString; template StringLit(const(char)[] lit, bool zeroTerminate = true) @@ -58,12 +112,11 @@ template StringLit(const(char)[] lit, bool zeroTerminate = true) return buffer; }(); pragma(aligned, 2) - private __gshared literal = LiteralData; + private __gshared immutable literal = LiteralData; - enum String StringLit = String(literal.ptr + 2, false); + enum StringLit = immutable(String)(literal.ptr + 2, false); } - String makeString(const(char)[] s) nothrow { if (s.length == 0) @@ -87,11 +140,11 @@ String makeString(const(char)[] s, StringAlloc allocator, void* userData = null) { return String(writeString(cast(char*)tempAllocator().alloc(2 + s.length, 2).ptr + 2, s), false); } - else if (allocator >= StringAlloc.StringCache) + else if (allocator == StringAlloc.StringCache) { import urt.mem.string : CacheString, addString; - CacheString cs = s.addString(allocator == StringAlloc.StringCacheDedup); + CacheString cs = s.addString(); return String(cs.ptr, false); } assert(false, "Invalid string allocator"); @@ -118,12 +171,51 @@ String makeString(const(char)[] s, char[] buffer) nothrow @nogc return String(writeString(buffer.ptr + 2, s), false); } +char* writeString(char* buffer, const(char)[] str) pure nothrow @nogc +{ + // TODO: assume the calling code has confirmed the length is within spec + if (__ctfe) + { + version (LittleEndian) + { + buffer[-2] = cast(char)(str.length & 0xFF); + buffer[-1] = cast(char)(str.length >> 8); + } + else + { + buffer[-2] = cast(char)(str.length >> 8); + buffer[-1] = cast(char)(str.length & 0xFF); + } + } + else + (cast(ushort*)buffer)[-1] = cast(ushort)str.length; + buffer[0 .. str.length] = str[]; + return buffer; +} + +String as_string(const(char)* s) nothrow @nogc + => String(s, false); + +inout(char)[] as_dstring(inout(char)* s) pure nothrow @nogc +{ + debug assert(s !is null); + + if (__ctfe) + { + version (LittleEndian) + ushort len = cast(ushort)(s[-2] | (s[-1] << 8)); + else + ushort len = cast(ushort)(s[-1] | (s[-2] << 8)); + return s[0 .. len]; + } + else + return s[0 .. (cast(ushort*)s)[-1]]; +} struct String { nothrow @nogc: - - alias toString this; + alias This = typeof(this); const(char)* ptr; @@ -132,17 +224,15 @@ nothrow @nogc: this.ptr = null; } - this(ref inout typeof(this) rhs) inout pure + this(ref inout This rhs) inout pure { ptr = rhs.ptr; - if (ptr) + if (!ptr) + return; + if (ushort* rc = ((cast(ushort*)ptr)[-1] >> 15) ? cast(ushort*)ptr - 2 : null) { - ushort* rc = ((cast(ushort*)ptr)[-1] >> 15) ? cast(ushort*)ptr - 2 : null; - if (rc) - { - assert((*rc & 0x3FFF) < 0x3FFF, "Reference count overflow"); - ++*rc; - } + assert((*rc & 0x3FFF) < 0x3FFF, "Reference count overflow"); + ++*rc; } } @@ -188,7 +278,20 @@ nothrow @nogc: // TODO: I made this return ushort, but normally length() returns size_t ushort length() const pure - => ptr ? ((cast(ushort*)ptr)[-1] & 0x7FFF) : 0; + { + if (__ctfe) + { + version (LittleEndian) + return ptr ? cast(ushort)(ptr[-2] | (ptr[-1] << 8)) & 0x7FFF : 0; + else + return ptr ? cast(ushort)((ptr[-1] | (ptr[-2] << 8)) & 0x7FFF) : 0; + } + else + return ptr ? ((cast(ushort*)ptr)[-1] & 0x7FFF) : 0; + } + + bool empty() const pure + => length() == 0; bool opCast(T : bool)() const pure => ptr != null && ((cast(ushort*)ptr)[-1] & 0x7FFF) != 0; @@ -218,7 +321,6 @@ nothrow @nogc: ptr = cs.ptr; } - bool opEquals(const(char)[] rhs) const pure { if (!ptr) @@ -227,6 +329,15 @@ nothrow @nogc: return len == rhs.length && (ptr == rhs.ptr || ptr[0 .. len] == rhs[]); } + int opEquals(ref const String rhs) const pure + => opEquals(rhs[]); + + int opEquals(size_t N)(ref const MutableString!N rhs) const pure + => opEquals(rhs[]); + + int opEquals(size_t N)(ref const Array!N rhs) const pure + => opEquals(rhs[]); + int opCmp(const(char)[] rhs) const pure { import urt.algorithm : compare; @@ -235,14 +346,24 @@ nothrow @nogc: return compare(ptr[0 .. length()], rhs); } + int opCmp(ref const String rhs) const pure + => opCmp(rhs[]); + + int opCmp(size_t N)(ref const MutableString!N rhs) const pure + => opCmp(rhs[]); + + int opCmp(size_t N)(ref const Array!N rhs) const pure + => opCmp(rhs[]); + size_t toHash() const pure { if (!ptr) return 0; + ushort len = (cast(ushort*)ptr)[-1] & 0x7FFF; static if (size_t.sizeof == 4) - return fnv1aHash(cast(ubyte[])ptr[0 .. length]); + return fnv1a(cast(ubyte[])ptr[0 .. len]); else - return fnv1aHash64(cast(ubyte[])ptr[0 .. length]); + return fnv1a64(cast(ubyte[])ptr[0 .. len]); } const(char)[] opIndex() const pure @@ -263,6 +384,9 @@ nothrow @nogc: size_t opDollar() const pure => length(); + bool has_rc() const pure + => ptr && ((cast(ushort*)ptr)[-1] >> 15) != 0; + private: auto __debugOverview() const pure { debug return ptr[0 .. length].debugExcapeString(); else return ptr[0 .. length]; } auto __debugExpanded() const pure => ptr[0 .. length]; @@ -315,7 +439,9 @@ unittest assert(!emptyLit); // opCast!bool // Test makeString (default allocator) - String s1 = makeString("World"); + // TODO: reinstate the GC for debug allocations... +// String s1 = makeString("World"); + String s1 = StringLit!"World"; assert(s1.length == 5); assert(s1 == "World"); @@ -338,6 +464,7 @@ unittest assert(nullStr == ""); assert(!nullStr); + // TODO: reinstate once GC makeString is available // Test assignment and reference counting (basic check) String s3 = s1; // s3 references the same data as s1 assert(s3.ptr == s1.ptr); @@ -347,7 +474,7 @@ unittest assert(s3.length == 5); // Test equality - String s4 = makeString("World"); + String s4 = StringLit!"World"; assert(s3 == s4); // Different allocations, same content assert(s3 != "world"); // Case sensitive assert(s3 != "Worl"); @@ -414,8 +541,6 @@ nothrow @nogc: static assert(Embed == 0, "Not without move semantics!"); - alias toString this; - char* ptr; // TODO: DELETE POSTBLIT! @@ -470,9 +595,9 @@ nothrow @nogc: append(forward!things); } - this(Args...)(Format_T, auto ref Args args) + this(Args...)(Format_T, const(char)[] format, auto ref Args args) { - format(forward!args); + this.format(format, forward!args); } ~this() @@ -487,9 +612,57 @@ nothrow @nogc: ushort length() const pure => ptr ? ((cast(ushort*)ptr)[-1] & 0x7FFF) : 0; + bool empty() const pure + => length() == 0; + bool opCast(T : bool)() const pure => ptr != null && ((cast(ushort*)ptr)[-1] & 0x7FFF) != 0; + bool opEquals(const(char)[] rhs) const pure + { + if (!ptr) + return rhs.length == 0; + ushort len = (cast(ushort*)ptr)[-1] & 0x7FFF; + return len == rhs.length && (ptr == rhs.ptr || ptr[0 .. len] == rhs[]); + } + + int opEquals(ref const String rhs) const pure + => opEquals(rhs[]); + + int opEquals(size_t N)(ref const MutableString!N rhs) const pure + => opEquals(rhs[]); + + int opEquals(size_t N)(ref const Array!N rhs) const pure + => opEquals(rhs[]); + + int opCmp(const(char)[] rhs) const pure + { + import urt.algorithm : compare; + if (!ptr) + return rhs.length == 0 ? 0 : -1; + return compare(ptr[0 .. length], rhs); + } + + int opCmp(ref const String rhs) const pure + => opCmp(rhs[]); + + int opCmp(size_t N)(ref const MutableString!N rhs) const pure + => opCmp(rhs[]); + + int opCmp(size_t N)(ref const Array!N rhs) const pure + => opCmp(rhs[]); + + size_t toHash() const pure + { + if (!ptr) + return 0; + ushort len = (cast(ushort*)ptr)[-1] & 0x7FFF; + static if (size_t.sizeof == 4) + return fnv1a(cast(ubyte[])ptr[0 .. len]); + else + return fnv1a64(cast(ubyte[])ptr[0 .. len]); + } + void opAssign(ref const typeof(this) rh) { opAssign(rh[]); @@ -499,11 +672,6 @@ nothrow @nogc: opAssign(rh[]); } - void opAssign(typeof(null)) - { - clear(); - } - void opAssign(char c) { reserve(1); @@ -601,9 +769,9 @@ nothrow @nogc: return this; } - ref MutableString!Embed appendFormat(Things...)(auto ref Things things) + ref MutableString!Embed append_format(Things...)(const(char)[] format, auto ref Things args) { - insertFormat(length(), forward!things); + insert_format(length(), format, forward!args); return this; } @@ -615,18 +783,18 @@ nothrow @nogc: return this; } - ref MutableString!Embed format(Args...)(auto ref Args args) + ref MutableString!Embed format(Args...)(const(char)[] format, auto ref Args args) { if (ptr) writeLength(0); - insertFormat(0, forward!args); + insert_format(0, format, forward!args); return this; } ref MutableString!Embed insert(Things...)(size_t offset, auto ref Things things) { import urt.string.format : _concat = concat; - import urt.util : max, nextPowerOf2; + import urt.util : max, next_power_of_2; char* oldPtr = ptr; size_t oldLen = length(); @@ -638,7 +806,7 @@ nothrow @nogc: debug assert(newLen <= MaxStringLen, "String too long"); size_t oldAlloc = allocated(); - ptr = newLen <= oldAlloc ? oldPtr : allocStringBuffer(max(16, cast(ushort)newLen + 4).nextPowerOf2 - 4); + ptr = newLen <= oldAlloc ? oldPtr : allocStringBuffer(max(16, cast(ushort)newLen + 4).next_power_of_2 - 4); memmove(ptr + offset + insertLen, oldPtr + offset, oldLen - offset); _concat(ptr[offset .. offset + insertLen], forward!things); writeLength(newLen); @@ -651,24 +819,24 @@ nothrow @nogc: return this; } - ref MutableString!Embed insertFormat(Things...)(size_t offset, auto ref Things things) + ref MutableString!Embed insert_format(Things...)(size_t offset, const(char)[] format, auto ref Things args) { import urt.string.format : _format = format; - import urt.util : max, nextPowerOf2; + import urt.util : max, next_power_of_2; char* oldPtr = ptr; size_t oldLen = length(); - size_t insertLen = _format(null, things).length; + size_t insertLen = _format(null, format, args).length; size_t newLen = oldLen + insertLen; if (newLen == oldLen) return this; debug assert(newLen <= MaxStringLen, "String too long"); size_t oldAlloc = allocated(); - ptr = newLen <= oldAlloc ? oldPtr : allocStringBuffer(max(16, cast(ushort)newLen + 4).nextPowerOf2 - 4); + ptr = newLen <= oldAlloc ? oldPtr : allocStringBuffer(max(16, cast(ushort)newLen + 4).next_power_of_2 - 4); memmove(ptr + offset + insertLen, oldPtr + offset, oldLen - offset); - _format(ptr[offset .. offset + insertLen], forward!things); + _format(ptr[offset .. offset + insertLen], format, forward!args); writeLength(newLen); if (oldPtr && ptr != oldPtr) @@ -792,6 +960,9 @@ unittest assert(s == "Hello, world!\n"); assert(s.length == 14); + s = null; + assert(s.length == 0); + MutableString!0 s_long; s_long.reserve(4567); s_long = "Start"; @@ -824,11 +995,11 @@ unittest m.append(" Text"); assert(m == "X! More Text"); - // appendFormat + // append_format m.clear(); - m.appendFormat("Value: {0}", 123); + m.append_format("Value: {0}", 123); assert(m == "Value: 123"); - m.appendFormat(", String: {0}", "abc"); + m.append_format(", String: {0}", "abc"); assert(m == "Value: 123, String: abc"); // concat @@ -849,13 +1020,13 @@ unittest m.insert(m.length, "!"); // End (same as append) assert(m == "My Super String!"); - // insertFormat + // insert_format m = "Data"; - m.insertFormat(0, "[{0}] ", 1); + m.insert_format(0, "[{0}] ", 1); assert(m == "[1] Data"); - m.insertFormat(4, "\\{{0}\\}", "fmt"); + m.insert_format(4, "\\{{0}\\}", "fmt"); assert(m == "[1] {fmt}Data"); - m.insertFormat(m.length, " End"); + m.insert_format(m.length, " End"); assert(m == "[1] {fmt}Data End"); // erase @@ -933,15 +1104,7 @@ private: __gshared StringAllocator[4] stringAllocators; static assert(stringAllocators.length <= 4, "Only 2 bits reserved to store allocator index"); -char* writeString(char* buffer, const(char)[] str) pure nothrow @nogc -{ - // TODO: assume the calling code has confirmed the length is within spec - (cast(ushort*)buffer)[-1] = cast(ushort)str.length; - buffer[0 .. str.length] = str[]; - return buffer; -} - -package(urt) void initStringAllocators() +package(urt) void initStringAllocators() nothrow @nogc { stringAllocators[StringAlloc.Default].alloc = (ushort bytes, void* userData) { char* buffer = cast(char*)defaultAllocator().alloc(bytes + 4, ushort.alignof).ptr; diff --git a/src/urt/string/tailstring.d b/src/urt/string/tailstring.d index 3664342..0c7b77d 100644 --- a/src/urt/string/tailstring.d +++ b/src/urt/string/tailstring.d @@ -23,7 +23,7 @@ struct TailString(T) else { const(char)* thisptr = cast(const(char)*)&this; - const(char)* sptr = s.ptr - (s.ptr[-1] < 128 ? 1 : 2); + const(char)* sptr = s.ptr - 2; assert(sptr > thisptr && sptr - thisptr <= offset.max, "!!"); offset = cast(ubyte)(sptr - thisptr); } @@ -39,7 +39,7 @@ struct TailString(T) if (offset == 0) return null; const(char)* ptr = (cast(const(char)*)&this) + offset; - return ptr[0] < 128 ? ptr + 1 : ptr + 2; + return ptr + 2; } size_t length() const nothrow @nogc @@ -64,7 +64,7 @@ struct TailString(T) else { const(char)* thisptr = cast(const(char)*)&this; - const(char)* sptr = s.ptr - (s.ptr[-1] < 128 ? 1 : 2); + const(char)* sptr = s.ptr - 2; assert(sptr > thisptr && sptr - thisptr <= offset.max, "!!"); offset = cast(ubyte)(sptr - thisptr); } @@ -81,9 +81,9 @@ struct TailString(T) import urt.hash; static if (size_t.sizeof == 4) - return fnv1aHash(cast(ubyte[])toString()); + return fnv1a(cast(ubyte[])toString()); else - return fnv1aHash64(cast(ubyte[])toString()); + return fnv1a64(cast(ubyte[])toString()); } private: diff --git a/src/urt/string/uni.d b/src/urt/string/uni.d index ee9b551..56297fc 100644 --- a/src/urt/string/uni.d +++ b/src/urt/string/uni.d @@ -1,18 +1,155 @@ module urt.string.uni; -nothrow @nogc: +import urt.string.ascii : to_lower, to_upper; +import urt.traits : is_some_char; +pure nothrow @nogc: -size_t uniConvert(const(char)[] s, wchar[] buffer) + +size_t uni_seq_len(const(char)[] str) +{ + debug assert(str.length > 0); + + const(char)* s = str.ptr; + if (s[0] < 0x80) // 1-byte sequence: 0xxxxxxx + return 1; + else if ((s[0] & 0xE0) == 0xC0) // 2-byte sequence: 110xxxxx 10xxxxxx + return (str.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + else if ((s[0] & 0xF0) == 0xE0) // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx + return (str.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : + (str.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + else if ((s[0] & 0xF8) == 0xF0) // 4-byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + return (str.length >= 4 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) ? 4 : + (str.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : + (str.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + return 1; // Invalid UTF-8 sequence +} + +size_t uni_seq_len(const(wchar)[] str) +{ + debug assert(str.length > 0); + + const(wchar)* s = str.ptr; + if (s[0] >= 0xD800 && s[0] < 0xDC00 && str.length >= 2 && s[1] >= 0xDC00 && s[1] < 0xE000) + return 2; // Surrogate pair: 110110xxxxxxxxxx 110111xxxxxxxxxx + return 1; +} + +pragma(inline, true) +size_t uni_seq_len(const(dchar)[] s) + => 1; + +size_t uni_strlen(C)(const(C)[] s) + if (is_some_char!C) +{ + static if (is(C == dchar)) + { + pragma(inline, true); + return s.length; + } + else + { + size_t count = 0; + while (s.length) + { + size_t l = s.uni_seq_len; + s = s[l .. $]; + ++count; + } + return count; + } +} + +dchar next_dchar(const(char)[] s, out size_t seq_len) +{ + assert(s.length > 0); + + const(char)* p = s.ptr; + if ((*p & 0x80) == 0) // 1-byte sequence: 0xxxxxxx + { + seq_len = 1; + return *p; + } + else if ((*p & 0xE0) == 0xC0) // 2-byte sequence: 110xxxxx 10xxxxxx + { + if (s.length >= 2 && (p[1] & 0xC0) == 0x80) + { + seq_len = 2; + return ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); + } + } + else if ((*p & 0xF0) == 0xE0) // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx + { + if (s.length >= 3 && (p[1] & 0xC0) == 0x80 && (p[2] & 0xC0) == 0x80) + { + seq_len = 3; + return ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); + } + // check for seq_len == 2 error cases + if (s.length >= 2 && (p[1] & 0xC0) == 0x80) + { + seq_len = 2; + return 0xFFFD; + } + } + else if ((*p & 0xF8) == 0xF0) // 4-byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + { + if (s.length >= 4 && (p[1] & 0xC0) == 0x80 && (p[2] & 0xC0) == 0x80 && (p[3] & 0xC0) == 0x80) + { + seq_len = 4; + return ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); + } + // check for seq_len == 2..3 error cases + if (s.length >= 2 && (p[1] & 0xC0) == 0x80) + { + if (s.length == 2 || (p[2] & 0xC0) != 0x80) + seq_len = 2; + else + seq_len = 3; + return 0xFFFD; + } + } + seq_len = 1; + return 0xFFFD; // Invalid UTF-8 sequence +} + +dchar next_dchar(const(wchar)[] s, out size_t seq_len) +{ + assert(s.length > 0); + + const(wchar)* p = s.ptr; + if (p[0] < 0xD800 || p[0] >= 0xE000) + { + seq_len = 1; + return p[0]; + } + if (p[0] < 0xDC00 && s.length >= 2 && p[1] >= 0xDC00 && p[1] < 0xE000) // Surrogate pair: 110110xxxxxxxxxx 110111xxxxxxxxxx + { + seq_len = 2; + return 0x10000 + ((p[0] - 0xD800) << 10) + (p[1] - 0xDC00); + } + seq_len = 1; + return 0xFFFD; // Invalid UTF-16 sequence +} + +pragma(inline, true) +dchar next_dchar(const(dchar)[] s, out size_t seq_len) +{ + assert(s.length > 0); + seq_len = 1; + return s[0]; +} + +size_t uni_convert(const(char)[] s, wchar[] buffer) { const(char)* p = s.ptr; const(char)* pend = p + s.length; wchar* b = buffer.ptr; - wchar* bEnd = buffer.ptr + buffer.length; + wchar* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; // End of output buffer if ((*p & 0x80) == 0) // 1-byte sequence: 0xxxxxxx *b++ = *p++; @@ -32,7 +169,7 @@ size_t uniConvert(const(char)[] s, wchar[] buffer) } else if ((*p & 0xF8) == 0xF0) // 4-byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx { - if (p + 3 >= pend || b + 1 >= bEnd) + if (p + 3 >= pend || b + 1 >= bend) return 0; // Unexpected end of input/output dchar codepoint = ((p[0] & 0x07) << 18) | ((p[1] & 0x3F) << 12) | ((p[2] & 0x3F) << 6) | (p[3] & 0x3F); codepoint -= 0x10000; @@ -47,16 +184,16 @@ size_t uniConvert(const(char)[] s, wchar[] buffer) return b - buffer.ptr; } -size_t uniConvert(const(char)[] s, dchar[] buffer) +size_t uni_convert(const(char)[] s, dchar[] buffer) { const(char)* p = s.ptr; const(char)* pend = p + s.length; dchar* b = buffer.ptr; - dchar* bEnd = buffer.ptr + buffer.length; + dchar* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; if ((*p & 0x80) == 0) // 1-byte sequence: 0xxxxxxx *b++ = *p++; @@ -84,16 +221,16 @@ size_t uniConvert(const(char)[] s, dchar[] buffer) return b - buffer.ptr; } -size_t uniConvert(const(wchar)[] s, char[] buffer) +size_t uni_convert(const(wchar)[] s, char[] buffer) { const(wchar)* p = s.ptr; const(wchar)* pend = p + s.length; char* b = buffer.ptr; - char* bEnd = buffer.ptr + buffer.length; + char* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; // End of output buffer if (p[0] >= 0xD800) { @@ -103,7 +240,7 @@ size_t uniConvert(const(wchar)[] s, char[] buffer) return 0; // Unexpected end of input if (p[0] < 0xDC00) // Surrogate pair: 110110xxxxxxxxxx 110111xxxxxxxxxx { - if (b + 3 >= bEnd) + if (b + 3 >= bend) return 0; // End of output buffer dchar codepoint = 0x10000 + ((p[0] - 0xD800) << 10) + (p[1] - 0xDC00); b[0] = 0xF0 | (codepoint >> 18); @@ -120,7 +257,7 @@ size_t uniConvert(const(wchar)[] s, char[] buffer) *b++ = cast(char)*p++; else if (*p < 0x800) // 2-byte sequence: 110xxxxx 10xxxxxx { - if (b + 1 >= bEnd) + if (b + 1 >= bend) return 0; // End of output buffer b[0] = 0xC0 | cast(char)(*p >> 6); b[1] = 0x80 | (*p++ & 0x3F); @@ -129,7 +266,7 @@ size_t uniConvert(const(wchar)[] s, char[] buffer) else // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx { three_byte_seq: - if (b + 2 >= bEnd) + if (b + 2 >= bend) return 0; // End of output buffer b[0] = 0xE0 | (*p >> 12); b[1] = 0x80 | ((*p >> 6) & 0x3F); @@ -140,24 +277,24 @@ size_t uniConvert(const(wchar)[] s, char[] buffer) return b - buffer.ptr; } -size_t uniConvert(const(wchar)[] s, dchar[] buffer) +size_t uni_convert(const(wchar)[] s, dchar[] buffer) { const(wchar)* p = s.ptr; const(wchar)* pend = p + s.length; dchar* b = buffer.ptr; - dchar* bEnd = buffer.ptr + buffer.length; + dchar* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; // End of output buffer - if (p[0] >= 0xD800 && p[0] < 0xE000) + if ((p[0] >> 11) == 0x1B) { if (p + 1 >= pend) return 0; // Unexpected end of input - if (p[0] < 0xDC00) // Surrogate pair: 110110xxxxxxxxxx 110111xxxxxxxxxx + if (p[0] < 0xDC00 && (p[1] >> 10) == 0x37) // Surrogate pair: 110110xxxxxxxxxx 110111xxxxxxxxxx { - *b++ = 0x10000 + ((p[0] - 0xD800) << 10) + (p[1] - 0xDC00); + *b++ = 0x10000 + ((p[0] & 0x3FF) << 10 | (p[1] & 0x3FF)); p += 2; continue; } @@ -168,22 +305,22 @@ size_t uniConvert(const(wchar)[] s, dchar[] buffer) return b - buffer.ptr; } -size_t uniConvert(const(dchar)[] s, char[] buffer) +size_t uni_convert(const(dchar)[] s, char[] buffer) { const(dchar)* p = s.ptr; const(dchar)* pend = p + s.length; char* b = buffer.ptr; - char* bEnd = buffer.ptr + buffer.length; + char* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; // End of output buffer if (*p < 0x80) // 1-byte sequence: 0xxxxxxx *b++ = cast(char)*p++; else if (*p < 0x800) // 2-byte sequence: 110xxxxx 10xxxxxx { - if (b + 1 >= bEnd) + if (b + 1 >= bend) return 0; // End of output buffer b[0] = 0xC0 | cast(char)(*p >> 6); b[1] = 0x80 | (*p++ & 0x3F); @@ -191,7 +328,7 @@ size_t uniConvert(const(dchar)[] s, char[] buffer) } else if (*p < 0x10000) // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx { - if (b + 2 >= bEnd) + if (b + 2 >= bend) return 0; // End of output buffer b[0] = 0xE0 | cast(char)(*p >> 12); b[1] = 0x80 | ((*p >> 6) & 0x3F); @@ -200,7 +337,7 @@ size_t uniConvert(const(dchar)[] s, char[] buffer) } else if (*p < 0x110000) // 4-byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx { - if (b + 3 >= bEnd) + if (b + 3 >= bend) return 0; // End of output buffer b[0] = 0xF0 | (*p >> 18); b[1] = 0x80 | ((*p >> 12) & 0x3F); @@ -214,22 +351,22 @@ size_t uniConvert(const(dchar)[] s, char[] buffer) return b - buffer.ptr; } -size_t uniConvert(const(dchar)[] s, wchar[] buffer) +size_t uni_convert(const(dchar)[] s, wchar[] buffer) { const(dchar)* p = s.ptr; const(dchar)* pend = p + s.length; wchar* b = buffer.ptr; - wchar* bEnd = buffer.ptr + buffer.length; + wchar* bend = buffer.ptr + buffer.length; while (p < pend) { - if (b >= bEnd) + if (b >= bend) return 0; // End of output buffer if (*p < 0x10000) *b++ = cast(wchar)*p++; else if (*p < 0x110000) { - if (b + 1 >= bEnd) + if (b + 1 >= bend) return 0; // End of output buffer dchar codepoint = *p++ - 0x10000; b[0] = 0xD800 | (codepoint >> 10); @@ -242,10 +379,507 @@ size_t uniConvert(const(dchar)[] s, wchar[] buffer) return b - buffer.ptr; } +char uni_to_lower(char c) + => c.to_lower(); + +dchar uni_to_lower(dchar c) +{ + if (uint(c - 'A') < 26) + return c | 0x20; + + // TODO: this is a deep rabbit-hole! (and the approach might not be perfect) + + if (c < 0xFF) + { + if (c >= 0xC0) // Latin-1 Supplement + return g_to_lower_latin_1.ptr[c - 0xC0]; + } + else if (c <= 0x556) + { + if (c >= 0x370) + { + if (c < 0x400) // Greek and Coptic + return 0x300 | g_to_lower_greek.ptr[c - 0x370]; + else if (c < 0x460) // Cyrillic + { + if (c < 0x410) + return c + 0x50; + else if (c < 0x430) + return c + 0x20; + } + else if (c < 0x530) // Cyrillic Supplement + { + if (c >= 0x482 && c < 0x48A) // exceptions + return c; + return c | 1; + } + else if (c >= 0x531) // Armenian + return c + 0x30; + } + else if (c < 0x180) // Latin Extended-A + return (0x100 | g_to_lower_latin_extended_a.ptr[c - 0xFF]) - 1; + } + else if (c <= 0x1CB0) // Georgian + { + if (c >= 0x1C90) + return c - 0x0BC0; // Mtavruli -> Mkhedruli + else if (c >= 0x10A0 && c <= 0x10C5) + return c + 0x1C60; // Asomtavruli -> Nuskhuri + } + else if (c >= 0x1E00) + { + if (c < 0x1F00) // Latin Extended Additional + { + if (c >= 0x1E96 && c < 0x1EA0) // exceptions + { + if (c == 0x1E9E) // 'ẞ' -> 'ß' + return 0xDF; + return c; + } + return c | 1; + } + else if (c <= 0x1FFC) // Greek Extended + { + if (c < 0x1F70) + return c & ~0x8; + return 0x1F00 | g_to_lower_greek_extended.ptr[c - 0x1F70]; + } + else if (c < 0x2CE4) + { + if (c >= 0x2C80) // Coptic + return c | 1; + } + } + return c; +} + +char uni_to_upper(char c) + => c.to_upper(); + +dchar uni_to_upper(dchar c) +{ + if (uint(c - 'a') < 26) + return c ^ 0x20; + + // TODO: this is a deep rabbit-hole! (and the approach might not be perfect) + + if (c < 0xFF) + { + if (c == 0xDF) // 'ß' -> 'ẞ' + return 0x1E9E; + if (c >= 0xC0) // Latin-1 Supplement + return g_to_upper_latin_1.ptr[c - 0xC0]; + } + else if (c <= 0x586) + { + if (c >= 0x370) + { + if (c < 0x400) // Greek and Coptic + return 0x300 | g_to_upper_greek.ptr[c - 0x370]; + else if (c < 0x460) // Cyrillic + { + if (c >= 0x450) + return c - 0x50; + else if (c >= 0x430) + return c - 0x20; + } + else if (c < 0x530) // Cyrillic Supplement + { + if (c >= 0x482 && c < 0x48A) // exceptions + return c; + return c & ~1; + } + else if (c >= 0x561) // Armenian + return c - 0x30; + } + else if (c < 0x180) // Latin Extended-A + return 0x100 | g_to_upper_latin_extended_a.ptr[c - 0xFF]; + } + else if (c <= 0x10F0) // Georgian + { + if (c >= 0x10D0) + return c + 0x0BC0; // Mkhedruli -> Mtavruli + } + else if (c >= 0x1E00) + { + if (c < 0x1F00) // Latin Extended Additional + { + if (c >= 0x1E96 && c < 0x1EA0) // exceptions + return c; + return c & ~1; + } + else if (c <= 0x1FFC) // Greek Extended + { + if (c < 0x1F70) + return c | 0x8; + return 0x1F00 | g_to_upper_greek_extended.ptr[c - 0x1F70]; + } + else if (c < 0x2CE4) + { + if (c >= 0x2C80) // Coptic + return c & ~1; + } + else if (c <= 0x2D25) // Georgian + { + if(c >= 0x2D00) + return c - 0x1C60; // Nuskhuri -> Asomtavruli + } + } + return c; +} + +char uni_case_fold(char c) + => c.to_lower(); + +dchar uni_case_fold(dchar c) +{ + // case-folding is stronger than to_lower, there may be many misc cases... + if (c >= 0x3C2) // Greek has bonus case-folding... + { + if (c < 0x3FA) + return 0x300 | g_case_fold_greek.ptr[c - 0x3C2]; + } + else if (c == 'ſ') // TODO: pointless? it's in the spec... + return 's'; + return uni_to_lower(c); +} + +int uni_compare(T, U)(const(T)[] s1, const(U)[] s2) + if (is_some_char!T && is_some_char!U) +{ + const(T)* p1 = s1.ptr; + const(T)* p1end = p1 + s1.length; + const(U)* p2 = s2.ptr; + const(U)* p2end = p2 + s2.length; + + // TODO: this is crude and insufficient; doesn't handle compound diacritics, etc (needs a NFKC normalisation step) + + while (true) + { + // return int.min/max in the case that the strings are a sub-string of the other so the caller can detect this case + if (p1 >= p1end) + return p2 < p2end ? int.min : 0; + if (p2 >= p2end) + return int.max; + + dchar a = *p1, b = *p2; + + if (a < 0x80) + { + if (a != b) + return int(a) - int(b); + ++p1; + p2 += b < 0x80 ? 1 : p2[0 .. p2end - p2].uni_seq_len; + } + else if (b < 0x80) + { + if (a != b) + return int(a) - int(b); + p1 += p1[0 .. p1end - p1].uni_seq_len; + ++p2; + } + else + { + size_t al, bl; + a = next_dchar(p1[0 .. p1end - p1], al); + b = next_dchar(p2[0 .. p2end - p2], bl); + if (a != b) + return cast(int)a - cast(int)b; + p1 += al; + p2 += bl; + } + } +} + +int uni_compare_i(T, U)(const(T)[] s1, const(U)[] s2) + if (is_some_char!T && is_some_char!U) +{ + const(T)* p1 = s1.ptr; + const(T)* p1end = p1 + s1.length; + const(U)* p2 = s2.ptr; + const(U)* p2end = p2 + s2.length; + + // TODO: this is crude and insufficient; doesn't handle compound diacritics, etc (needs a NFKC normalisation step) + // that said, it's also overkill for embedded use! + + size_t al, bl; + while (p1 < p1end && p2 < p2end) + { + dchar a = *p1; + dchar b = void; + if (a < 0x80) + { + // ascii fast-path + a = (cast(char)a).to_lower; + b = *p2; + if (uint(b - 'A') < 26) + b |= 0x20; + if (a != b) + { + if (b >= 0x80) + { + // `b` is not ascii; break-out to the slow path... + al = 1; + goto uni_compare_load_b; + } + return cast(int)a - cast(int)b; + } + ++p1; + ++p2; + } + else + { + a = next_dchar(p1[0 .. p1end - p1], al).uni_case_fold; + uni_compare_load_b: + b = next_dchar(p2[0 .. p2end - p2], bl).uni_case_fold; + uni_compare_a_b: + if (a != b) + { + // it is _SO UNFORTUNATE_ that the ONLY special-case letter in all of unicode is german 'ß' (0xDF)!! + if (b == 0xDF) + { + if (a != 's') + return cast(int)a - cast(int)'s'; + if (++p1 == p1end) + return -1; // only one 's', so the a-side is a shorter string + a = next_dchar(p1[0 .. p1end - p1], al).uni_case_fold; + b = 's'; + p2 += bl - 1; + bl = 1; + goto uni_compare_a_b; + } + else if (a == 0xDF) + { + if (b != 's') + return cast(int)'s' - cast(int)b; + if (++p2 == p2end) + return 1; // only one 's', so the b-side is a shorter string + a = 's'; + p1 += al - 1; + al = 1; + goto uni_compare_load_b; + } + return cast(int)a - cast(int)b; + } + p1 += al; + p2 += bl; + } + } + + // return int.min/max in the case that the strings are a sub-string of the other so the caller can detect this case + return (p1 < p1end) ? int.max : (p2 < p2end) ? int.min : 0; +} + + +private: + +// this is a helper to crush character maps into single byte arrays... +ubyte[N] map_chars(size_t N)(ubyte function(wchar) pure nothrow @nogc translate, wchar[N] map) +{ + if (__ctfe) + { + ubyte[N] result; + foreach (i; 0 .. N) + result[i] = translate(map[i]); + return result; + } + else + assert(false, "Not for runtime!"); +} + +// lookup tables for case conversion +__gshared immutable g_to_lower_latin_1 = map_chars(c => cast(ubyte)c, to_lower_latin[0 .. 0x3F]); +__gshared immutable g_to_upper_latin_1 = map_chars(c => cast(ubyte)c, to_upper_latin[0 .. 0x3F]); +__gshared immutable g_to_lower_latin_extended_a = map_chars(c => cast(ubyte)(c + 1), to_lower_latin[0x3F .. 0xC0]); // calculate `(0x100 | table[n]) - 1` at runtime +__gshared immutable g_to_upper_latin_extended_a = map_chars(c => cast(ubyte)c, to_upper_latin[0x3F .. 0xC0]); // calculate `0x100 | table[n]` at runtime +__gshared immutable g_to_lower_greek = map_chars(c => cast(ubyte)c, to_lower_greek); // calculate `0x300 | table[n]` at runtime +__gshared immutable g_to_upper_greek = map_chars(c => cast(ubyte)c, to_upper_greek); // calculate `0x300 | table[n]` at runtime +__gshared immutable g_case_fold_greek = map_chars(c => cast(ubyte)c, case_fold_greek); // calculate `0x300 | table[n]` at runtime +__gshared immutable g_to_lower_greek_extended = map_chars(c => cast(ubyte)c, to_lower_greek_extended); // calculate `0x1F00 | table[n]` at runtime +__gshared immutable g_to_upper_greek_extended = map_chars(c => cast(ubyte)c, to_upper_greek_extended); // calculate `0x1F00 | table[n]` at runtime + +// Latin-1 Supplement and Latin Extended-A +enum wchar[0x180 - 0xC0] to_lower_latin = [ + 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', + 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 0xD7,'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ß', + 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', + 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 0xF7,'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ', + 'ā', 'ā', 'ă', 'ă', 'ą', 'ą', 'ć', 'ć', 'ĉ', 'ĉ', 'ċ', 'ċ', 'č', 'č', 'ď', 'ď', + 'đ', 'đ', 'ē', 'ē', 'ĕ', 'ĕ', 'ė', 'ė', 'ę', 'ę', 'ě', 'ě', 'ĝ', 'ĝ', 'ğ', 'ğ', + 'ġ', 'ġ', 'ģ', 'ģ', 'ĥ', 'ĥ', 'ħ', 'ħ', 'ĩ', 'ĩ', 'ī', 'ī', 'ĭ', 'ĭ', 'į', 'į', + 0x130,0x131,'ij', 'ij', 'ĵ', 'ĵ', 'ķ', 'ķ',0x138,'ĺ', 'ĺ', 'ļ', 'ļ', 'ľ', 'ľ', 'ŀ', + 'ŀ', 'ł', 'ł', 'ń', 'ń', 'ņ', 'ņ', 'ň', 'ň',0x149,'ŋ', 'ŋ', 'ō', 'ō', 'ŏ', 'ŏ', + 'ő', 'ő', 'œ', 'œ', 'ŕ', 'ŕ', 'ŗ', 'ŗ', 'ř', 'ř', 'ś', 'ś', 'ŝ', 'ŝ', 'ş', 'ş', + 'š', 'š', 'ţ', 'ţ', 'ť', 'ť', 'ŧ', 'ŧ', 'ũ', 'ũ', 'ū', 'ū', 'ŭ', 'ŭ', 'ů', 'ů', + 'ű', 'ű', 'ų', 'ų', 'ŵ', 'ŵ', 'ŷ', 'ŷ', 'ÿ', 'ź', 'ź', 'ż', 'ż', 'ž', 'ž', 'ſ' +]; + +enum wchar[0x180 - 0xC0] to_upper_latin = [ + 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', + 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 0xD7,'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ẞ', + 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', + 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 0xF7,'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'Ÿ', + 'Ā', 'Ā', 'Ă', 'Ă', 'Ą', 'Ą', 'Ć', 'Ć', 'Ĉ', 'Ĉ', 'Ċ', 'Ċ', 'Č', 'Č', 'Ď', 'Ď', + 'Đ', 'Đ', 'Ē', 'Ē', 'Ĕ', 'Ĕ', 'Ė', 'Ė', 'Ę', 'Ę', 'Ě', 'Ě', 'Ĝ', 'Ĝ', 'Ğ', 'Ğ', + 'Ġ', 'Ġ', 'Ģ', 'Ģ', 'Ĥ', 'Ĥ', 'Ħ', 'Ħ', 'Ĩ', 'Ĩ', 'Ī', 'Ī', 'Ĭ', 'Ĭ', 'Į', 'Į', + 0x130,0x131,'IJ', 'IJ', 'Ĵ', 'Ĵ', 'Ķ', 'Ķ',0x138,'Ĺ', 'Ĺ', 'Ļ', 'Ļ', 'Ľ', 'Ľ', 'Ŀ', + 'Ŀ', 'Ł', 'Ł', 'Ń', 'Ń', 'Ņ', 'Ņ', 'Ň', 'Ň',0x149,'Ŋ', 'Ŋ', 'Ō', 'Ō', 'Ŏ', 'Ŏ', + 'Ő', 'Ő', 'Œ', 'Œ', 'Ŕ', 'Ŕ', 'Ŗ', 'Ŗ', 'Ř', 'Ř', 'Ś', 'Ś', 'Ŝ', 'Ŝ', 'Ş', 'Ş', + 'Š', 'Š', 'Ţ', 'Ţ', 'Ť', 'Ť', 'Ŧ', 'Ŧ', 'Ũ', 'Ũ', 'Ū', 'Ū', 'Ŭ', 'Ŭ', 'Ů', 'Ů', + 'Ű', 'Ű', 'Ų', 'Ų', 'Ŵ', 'Ŵ', 'Ŷ', 'Ŷ', 'Ÿ', 'Ź', 'Ź', 'Ż', 'Ż', 'Ž', 'Ž', 'S' +]; + +// Greek and Coptic +enum wchar[0x400 - 0x370] to_lower_greek = [ + 'ͱ', 'ͱ', 'ͳ', 'ͳ',0x374,0x375,'ͷ','ͷ',0x378,0x379,0x37A,'ͻ','ͼ','ͽ',0x37E,'ϳ', +0x380,0x381,0x382,0x383,0x384,0x385,'ά',0x387,'έ','ή','ί',0x38B,'ό',0x38D,'ύ', 'ώ', + 0x390,'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', + 'π', 'ρ',0x3A2,'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω', 'ϊ', 'ϋ', 'ά', 'έ', 'ή', 'ί', + 0x3B0,'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', + 'π', 'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω', 'ϊ', 'ϋ', 'ό', 'ύ', 'ώ', 'ϗ', + 'ϐ', 'ϑ', 'υ', 'ύ', 'ϋ', 'ϕ', 'ϖ', 'ϗ', 'ϙ', 'ϙ', 'ϛ', 'ϛ', 'ϝ', 'ϝ', 'ϟ', 'ϟ', + 'ϡ', 'ϡ', 'ϣ', 'ϣ', 'ϥ', 'ϥ', 'ϧ', 'ϧ', 'ϩ', 'ϩ', 'ϫ', 'ϫ', 'ϭ', 'ϭ', 'ϯ', 'ϯ', + 'ϰ', 'ϱ', 'ϲ', 'ϳ', 'θ', 'ϵ',0x3F6,'ϸ', 'ϸ', 'ϲ', 'ϻ', 'ϻ',0x3FC,'ͻ', 'ͼ', 'ͽ' +]; + +enum wchar[0x400 - 0x370] to_upper_greek = [ + 'Ͱ', 'Ͱ', 'Ͳ', 'Ͳ',0x374,0x375,'Ͷ','Ͷ',0x378,0x379,0x37A,'Ͻ','Ͼ','Ͽ',0x37E,'Ϳ', +0x380,0x381,0x382,0x383,0x384,0x385,'Ά',0x387,'Έ','Ή','Ί',0x38B,'Ό',0x38D,'Ύ', 'Ώ', + 0x390,'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', + 'Π', 'Ρ',0x3A2,'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω', 'Ϊ', 'Ϋ', 'Ά', 'Έ', 'Ή', 'Ί', + 0x3B0,'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', + 'Π', 'Ρ', 'Σ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω', 'Ϊ', 'Ϋ', 'Ό', 'Ύ', 'Ώ', 'Ϗ', + 'Β','Θ',0x3D2,0x3D3,0x3D4,'Φ','Π', 'Ϗ', 'Ϙ', 'Ϙ', 'Ϛ', 'Ϛ', 'Ϝ', 'Ϝ', 'Ϟ', 'Ϟ', + 'Ϡ', 'Ϡ', 'Ϣ', 'Ϣ', 'Ϥ', 'Ϥ', 'Ϧ', 'Ϧ', 'Ϩ', 'Ϩ', 'Ϫ', 'Ϫ', 'Ϭ', 'Ϭ', 'Ϯ', 'Ϯ', + 'Κ', 'Ρ', 'Ϲ', 'Ϳ', 'ϴ', 'Ε',0x3F6,'Ϸ', 'Ϸ', 'Ϲ', 'Ϻ', 'Ϻ',0x3FC,'Ͻ', 'Ͼ', 'Ͽ' +]; + +enum wchar[0x3FA - 0x3C2] case_fold_greek = [ + 'σ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω', 'ϊ', 'ϋ', 'ό', 'ύ', 'ώ', 'ϗ', + 'β', 'θ', 'υ', 'ύ', 'ϋ', 'φ', 'π', 'ϗ', 'ϙ', 'ϙ', 'ϛ', 'ϛ', 'ϝ', 'ϝ', 'ϟ', 'ϟ', + 'ϡ', 'ϡ', 'ϣ', 'ϣ', 'ϥ', 'ϥ', 'ϧ', 'ϧ', 'ϩ', 'ϩ', 'ϫ', 'ϫ', 'ϭ', 'ϭ', 'ϯ', 'ϯ', + 'κ', 'ρ', 'σ', 'ϳ', 'θ', 'ε',0x3F6,'ϸ', 'ϸ', 'σ' +]; + +enum wchar[0x1FFD - 0x1F70] to_lower_greek_extended = [ + 'ὰ', 'ά', 'ὲ', 'έ', 'ὴ', 'ή', 'ὶ', 'ί', 'ὸ', 'ό', 'ὺ', 'ύ', 'ὼ', 'ώ', 0x1F7E,0x1F7F, + 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', + 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', + 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', + 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 0x1FB5, 'ᾶ', 'ᾷ', 'ᾰ', 'ᾱ', 'ὰ', 'ά', 'ᾳ', 0x1FBD,0x1FBE,0x1FBF, +0x1FC0,0x1FC1,'ῂ', 'ῃ', 'ῄ', 0x1FC5, 'ῆ', 'ῇ', 'ὲ', 'έ', 'ὴ', 'ή', 'ῃ', 0x1FCD,0x1FCE,0x1FCF, + 'ῐ', 'ῑ', 'ῒ', 'ΐ', 0x1FD4,0x1FD5, 'ῖ', 'ῗ', 'ῐ', 'ῑ', 'ὶ', 'ί',0x1FDC,0x1FDD,0x1FDE,0x1FDF, + 'ῠ', 'ῡ', 'ῢ', 'ΰ', 'ῤ', 'ῥ', 'ῦ', 'ῧ', 'ῠ', 'ῡ', 'ὺ', 'ύ', 'ῥ', 0x1FED,0x1FEE,0x1FEF, +0x1FF0,0x1FF1,'ῲ', 'ῳ', 'ῴ', 0x1FF5, 'ῶ', 'ῷ', 'ὸ', 'ό', 'ὼ', 'ώ', 'ῳ' +]; + +enum wchar[0x1FFD - 0x1F70] to_upper_greek_extended = [ + 'Ὰ', 'Ά', 'Ὲ', 'Έ', 'Ὴ', 'Ή', 'Ὶ', 'Ί', 'Ὸ', 'Ό', 'Ὺ', 'Ύ', 'Ὼ', 'Ώ', 0x1F7E,0x1F7F, + 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', + 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', + 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', + 'Ᾰ', 'Ᾱ', 0x1FB2, 'ᾼ', 0x1FB4,0x1FB5,0x1FB6,0x1FB7, 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 0x1FBD,0x1FBE,0x1FBF, +0x1FC0,0x1FC1,0x1FC2, 'ῌ', 0x1FC4,0x1FC5,0x1FC6,0x1FC7, 'Ὲ', 'Έ', 'Ὴ', 'Ή', 'ῌ', 0x1FCD,0x1FCE,0x1FCF, + 'Ῐ', 'Ῑ', 0x1FD2,0x1FD3,0x1FD4,0x1FD5,0x1FD6,0x1FD7, 'Ῐ', 'Ῑ', 'Ὶ', 'Ί',0x1FDC,0x1FDD,0x1FDE,0x1FDF, + 'Ῠ', 'Ῡ', 0x1FE2,0x1FE3,0x1FE4, 'Ῥ', 0x1FE6,0x1FE7, 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ῥ', 0x1FED,0x1FEE,0x1FEF, +0x1FF0,0x1FF1,0x1FF2, 'ῼ', 0x1FF4,0x1FF5,0x1FF6,0x1FF7, 'Ὸ', 'Ό', 'Ὼ', 'Ώ', 'ῼ' +]; + +// NOTE: Cyrillic is runtime calculable, no tables required! + + unittest { - immutable dstring unicodeTest = + immutable ushort[5] surrogates = [ 0xD800, 0xD800, 0xDC00, 0xD800, 0x0020 ]; + + // test uni_seq_len functions + assert(uni_seq_len("Hello, World!") == 1); + assert(uni_seq_len("ñowai!") == 2); + assert(uni_seq_len("你好") == 3); + assert(uni_seq_len("😊wow!") == 4); + assert(uni_seq_len("\xFFHello") == 1); + assert(uni_seq_len("\xC2") == 1); + assert(uni_seq_len("\xC2Hello") == 1); + assert(uni_seq_len("\xE2") == 1); + assert(uni_seq_len("\xE2Hello") == 1); + assert(uni_seq_len("\xE2\x82") == 2); + assert(uni_seq_len("\xE2\x82Hello") == 2); + assert(uni_seq_len("\xF0") == 1); + assert(uni_seq_len("\xF0Hello") == 1); + assert(uni_seq_len("\xF0\x9F") == 2); + assert(uni_seq_len("\xF0\x9FHello") == 2); + assert(uni_seq_len("\xF0\x9F\x98") == 3); + assert(uni_seq_len("\xF0\x9F\x98Hello") == 3); + assert(uni_seq_len("Hello, World!"w) == 1); + assert(uni_seq_len("ñowai!"w) == 1); + assert(uni_seq_len("你好"w) == 1); + assert(uni_seq_len("😊wow!"w) == 2); + assert(uni_seq_len(cast(wchar[])surrogates[0..1]) == 1); + assert(uni_seq_len(cast(wchar[])surrogates[0..2]) == 1); + assert(uni_seq_len(cast(wchar[])surrogates[2..3]) == 1); + assert(uni_seq_len(cast(wchar[])surrogates[3..5]) == 1); + assert(uni_seq_len("😊wow!"d) == 1); + + // test uni_strlen + assert(uni_strlen("Hello, World!") == 13); + assert(uni_strlen("ñowai!") == 6); + assert(uni_strlen("你好") == 2); + assert(uni_strlen("😊wow!") == 5); + assert(uni_strlen("\xFFHello") == 6); + assert(uni_strlen("\xC2") == 1); + assert(uni_strlen("\xC2Hello") == 6); + assert(uni_strlen("\xE2") == 1); + assert(uni_strlen("\xE2Hello") == 6); + assert(uni_strlen("\xE2\x82") == 1); + assert(uni_strlen("\xE2\x82Hello") == 6); + assert(uni_strlen("\xF0") == 1); + assert(uni_strlen("\xF0Hello") == 6); + assert(uni_strlen("\xF0\x9F") == 1); + assert(uni_strlen("\xF0\x9FHello") == 6); + assert(uni_strlen("\xF0\x9F\x98") == 1); + assert(uni_strlen("\xF0\x9F\x98Hello") == 6); + assert(uni_strlen("Hello, World!"w) == 13); + assert(uni_strlen("ñowai!"w) == 6); + assert(uni_strlen("你好"w) == 2); + assert(uni_strlen("😊wow!"w) == 5); + assert(uni_strlen(cast(wchar[])surrogates[0..1]) == 1); + assert(uni_strlen(cast(wchar[])surrogates[0..2]) == 2); + assert(uni_strlen(cast(wchar[])surrogates[2..3]) == 1); + assert(uni_strlen(cast(wchar[])surrogates[3..5]) == 2); + assert(uni_strlen("😊wow!"d) == 5); + + // test next_dchar functions + size_t sl; + assert(next_dchar("Hello, World!", sl) == 'H' && sl == 1); + assert(next_dchar("ñowai!", sl) == 'ñ' && sl == 2); + assert(next_dchar("你好", sl) == '你' && sl == 3); + assert(next_dchar("😊wow!", sl) == '😊' && sl == 4); + assert(next_dchar("\xFFHello", sl) == '�' && sl == 1); + assert(next_dchar("\xC2", sl) == '�' && sl == 1); + assert(next_dchar("\xC2Hello", sl) == '�' && sl == 1); + assert(next_dchar("\xE2", sl) == '�' && sl == 1); + assert(next_dchar("\xE2Hello", sl) == '�' && sl == 1); + assert(next_dchar("\xE2\x82", sl) == '�' && sl == 2); + assert(next_dchar("\xE2\x82Hello", sl) == '�' && sl == 2); + assert(next_dchar("\xF0", sl) == '�' && sl == 1); + assert(next_dchar("\xF0Hello", sl) == '�' && sl == 1); + assert(next_dchar("\xF0\x9F", sl) == '�' && sl == 2); + assert(next_dchar("\xF0\x9FHello", sl) == '�' && sl == 2); + assert(next_dchar("\xF0\x9F\x98", sl) == '�' && sl == 3); + assert(next_dchar("\xF0\x9F\x98Hello", sl) == '�' && sl == 3); + assert(next_dchar("Hello, World!"w, sl) == 'H' && sl == 1); + assert(next_dchar("ñowai!"w, sl) == 'ñ' && sl == 1); + assert(next_dchar("你好"w, sl) == '你' && sl == 1); + assert(next_dchar("😊wow!"w, sl) == '😊' && sl == 2); + assert(next_dchar(cast(wchar[])surrogates[0..1], sl) == '�' && sl == 1); + assert(next_dchar(cast(wchar[])surrogates[0..2], sl) == '�' && sl == 1); + assert(next_dchar(cast(wchar[])surrogates[2..3], sl) == '�' && sl == 1); + assert(next_dchar(cast(wchar[])surrogates[3..5], sl) == '�' && sl == 1); + assert(next_dchar("😊wow!"d, sl) == '😊' && sl == 1); + + immutable dstring unicode_test = "Basic ASCII: Hello, World!\n" ~ + "Extended Latin: Café, résumé, naïve, jalapeño\n" ~ "BMP Examples: 你好, مرحبا, שלום, 😊, ☂️\n" ~ "Supplementary Planes: 𐍈, 𝒜, 🀄, 🚀\n" ~ "Surrogate Pair Test: 😀👨‍👩‍👧‍👦\n" ~ @@ -257,20 +891,142 @@ unittest "U+E000–U+FFFF Range:  (U+E000), 豈 (U+F900)" ~ "Control Characters: \u0008 \u001B \u0000\n"; - char[1024] utf8Buffer; - wchar[512] utf16Buffer; - dchar[512] utf32Buffer; + char[1024] utf8_buffer; + wchar[512] utf16_buffer; + dchar[512] utf32_buffer; // test all conversions with characters in every significant value range - size_t utf8Len = uniConvert(unicodeTest, utf8Buffer); // D-C - size_t utf16Len = uniConvert(utf8Buffer[0..utf8Len], utf16Buffer); // C-W - size_t utf32Len = uniConvert(utf16Buffer[0..utf16Len], utf32Buffer); // W-D - utf16Len = uniConvert(utf32Buffer[0..utf32Len], utf16Buffer); // D-W - utf8Len = uniConvert(utf16Buffer[0..utf16Len], utf8Buffer); // W-C - utf32Len = uniConvert(utf8Buffer[0..utf8Len], utf32Buffer); // C-D - assert(unicodeTest[] == utf32Buffer[0..utf32Len]); + size_t utf8_len = uni_convert(unicode_test, utf8_buffer); // D-C + size_t utf16_len = uni_convert(utf8_buffer[0..utf8_len], utf16_buffer); // C-W + size_t utf32_len = uni_convert(utf16_buffer[0..utf16_len], utf32_buffer); // W-D + utf16_len = uni_convert(utf32_buffer[0..utf32_len], utf16_buffer); // D-W + utf8_len = uni_convert(utf16_buffer[0..utf16_len], utf8_buffer); // W-C + utf32_len = uni_convert(utf8_buffer[0..utf8_len], utf32_buffer); // C-D + assert(unicode_test[] == utf32_buffer[0..utf32_len]); // TODO: test all the error cases; invalid characters, buffer overflows, truncated inputs, etc... //... -} + // test uni_to_lower and uni_to_upper + assert(uni_to_lower('A') == 'a'); + assert(uni_to_lower('Z') == 'z'); + assert(uni_to_lower('a') == 'a'); + assert(uni_to_lower('z') == 'z'); + assert(uni_to_lower('À') == 'à'); + assert(uni_to_lower('Ý') == 'ý'); + assert(uni_to_lower('Ÿ') == 'ÿ'); + assert(uni_to_lower('ÿ') == 'ÿ'); + assert(uni_to_lower('ß') == 'ß'); + assert(uni_to_lower('ẞ') == 'ß'); + assert(uni_to_lower('Α') == 'α'); + assert(uni_to_lower('Ω') == 'ω'); + assert(uni_to_lower('α') == 'α'); + assert(uni_to_lower('ω') == 'ω'); + assert(uni_to_lower('Ḁ') == 'ḁ'); + assert(uni_to_lower('ḁ') == 'ḁ'); + assert(uni_to_lower('ẝ') == 'ẝ'); + assert(uni_to_lower('😊') == '😊'); + assert(uni_to_upper('a') == 'A'); + assert(uni_to_upper('z') == 'Z'); + assert(uni_to_upper('A') == 'A'); + assert(uni_to_upper('Z') == 'Z'); + assert(uni_to_upper('à') == 'À'); + assert(uni_to_upper('ý') == 'Ý'); + assert(uni_to_upper('ÿ') == 'Ÿ'); + assert(uni_to_upper('Ÿ') == 'Ÿ'); + assert(uni_to_upper('ß') == 'ẞ'); + assert(uni_to_upper('ẞ') == 'ẞ'); + assert(uni_to_upper('α') == 'Α'); + assert(uni_to_upper('ω') == 'Ω'); + assert(uni_to_upper('Α') == 'Α'); + assert(uni_to_upper('Ω') == 'Ω'); + assert(uni_to_upper('ḁ') == 'Ḁ'); + assert(uni_to_upper('Ḁ') == 'Ḁ'); + assert(uni_to_upper('😊') == '😊'); + assert(uni_to_upper('џ') == 'Џ'); + assert(uni_to_upper('Џ') == 'Џ'); + assert(uni_to_upper('д') == 'Д'); + assert(uni_to_upper('Д') == 'Д'); + assert(uni_to_upper('ѻ') == 'Ѻ'); + assert(uni_to_upper('Ѻ') == 'Ѻ'); + assert(uni_to_upper('ԫ') == 'Ԫ'); + assert(uni_to_upper('Ԫ') == 'Ԫ'); + assert(uni_to_upper('ա') == 'Ա'); + assert(uni_to_upper('Ա') == 'Ա'); + + // test uni_compare + assert(uni_compare("Hello", "Hello") == 0); + assert(uni_compare("Hello", "hello") < 0); + assert(uni_compare("hello", "Hello") > 0); + assert(uni_compare("Hello", "Hello, World!") < 0); + assert(uni_compare("Café", "Café") == 0); + assert(uni_compare("Café", "CafÉ") > 0); + assert(uni_compare("CafÉ", "Café") < 0); + assert(uni_compare("Hello, 世界", "Hello, 世界") == 0); + assert(uni_compare("Hello, 世界", "Hello, 世") > 0); + assert(uni_compare("Hello, 世", "Hello, 世界") < 0); + assert(uni_compare("Hello, 😊", "Hello, 😊") == 0); + assert(uni_compare("Hello, 😊", "Hello, 😢") < 0); + assert(uni_compare("😊A", "😊a") < 0); + + // test uni_compare_i + assert(uni_compare_i("Hello", "Hello") == 0); + assert(uni_compare_i("Hello", "hello") == 0); + assert(uni_compare_i("hello", "Hello") == 0); + assert(uni_compare_i("Hello", "Hello, World!") < 0); + assert(uni_compare_i("hello", "HORLD") < 0); + assert(uni_compare_i("Hello, 世界", "hello, 世界") == 0); + assert(uni_compare_i("Hello, 世界", "hello, 世") > 0); + assert(uni_compare_i("Hello, 世", "hello, 世界") < 0); + assert(uni_compare_i("Hello, 😊", "hello, 😊") == 0); + assert(uni_compare_i("Hello, 😊", "hello, 😢") < 0); + assert(uni_compare_i("😊a", "😊B") < 0); + assert(uni_compare_i("AZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ", "azàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ") == 0); // basic latin + latin-1 supplement + assert(uni_compare_i("ŸĀĦĬIJĹŁŇŊŒŠŦŶŹŽ", "ÿāħĭijĺłňŋœšŧŷźž") == 0); // more latin extended-a characters + assert(uni_compare_i("ḀṤẔẠỸỺỼỾ", "ḁṥẕạỹỻỽỿ") == 0); // just the extended latin + + // test various language pangrams! + assert(uni_compare_i("The quick brown fox jumps over the lazy dog", "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG") == 0); // english + assert(uni_compare_i("Sævör grét áðan því úlpan var ónýt", "SÆVÖR GRÉT ÁÐAN ÞVÍ ÚLPAN VAR ÓNÝT") == 0); // icelandic + assert(uni_compare_i("Příliš žluťoučký kůň úpěl ďábelské ódy.", "PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY.") == 0); // czech + assert(uni_compare_i("Zażółć gęślą jaźń.", "ZAŻÓŁĆ GĘŚLĄ JAŹŃ.") == 0); // polish + assert(uni_compare_i("Φλεγματικά χρώματα που εξοβελίζουν ψευδαισθήσεις.", "ΦΛΕΓΜΑΤΙΚΆ ΧΡΏΜΑΤΑ ΠΟΥ ΕΞΟΒΕΛΊΖΟΥΝ ΨΕΥΔΑΙΣΘΉΣΕΙΣ.") == 0); // greek + assert(uni_compare_i("Любя, съешь щипцы, — вздохнёт мэр, — Кайф жгуч!", "ЛЮБЯ, СЪЕШЬ ЩИПЦЫ, — ВЗДОХНЁТ МЭР, — КАЙФ ЖГУЧ!") == 0); // russian + assert(uni_compare_i("Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք։", "ԲԵԼ ԴՂՅԱԿԻ ՁԱԽ ԺԱՄՆ ՕՖ ԱԶԳՈՒԹՅԱՆԸ ՑՊԱՀԱՆՋ ՉՃՇՏԱԾ ՎՆԱՍ ԷՐ ԵՒ ՓԱՌՔ։") == 0); // armenian + assert(uni_compare_i("აბგად ევზეთ იკალ მანო, პაჟა რასტა უფქა ღაყაშ, ჩაცა ძაწა ჭახა ჯაჰო", "ᲐᲑᲒᲐᲓ ᲔᲕᲖᲔᲗ ᲘᲙᲐᲚ ᲛᲐᲜᲝ, ᲞᲐᲟᲐ ᲠᲐᲡᲢᲐ ᲣᲤᲥᲐ ᲦᲐᲧᲐᲨ, ᲩᲐᲪᲐ ᲫᲐᲬᲐ ᲭᲐᲮᲐ ᲯᲐᲰᲝ") == 0); // georgian modern + assert(uni_compare_i("ⴘⴄⴅ ⴟⴓ ⴠⴡⴢ ⴣⴤⴥ ⴇⴍⴚ ⴞⴐⴈ ⴝⴋⴊⴈ.", "ႸႤႥ ႿႳ ჀჁჂ ჃჄჅ ႧႭႺ ႾႰႨ ႽႫႪႨ.") == 0); // georgian ecclesiastical + assert(uni_compare_i("Ⲁⲛⲟⲕ ⲡⲉ ϣⲏⲙ ⲛ̄ⲕⲏⲙⲉ.", "ⲀⲚⲞⲔ ⲠⲈ ϢⲎⲘ Ⲛ̄ⲔⲎⲘⲈ.") == 0); // coptic + + // test the special-cases around german 'ß' (0xDF) and 'ẞ' (0x1E9E) + // check sort order + assert(uni_compare_i("ß", "sr") > 0); + assert(uni_compare_i("ß", "ss") == 0); + assert(uni_compare_i("ß", "st") < 0); + assert(uni_compare_i("sr", "ß") < 0); + assert(uni_compare_i("ss", "ß") == 0); + assert(uni_compare_i("st", "ß") > 0); + // check truncated comparisons + assert(uni_compare_i("ß", "s") > 0); + assert(uni_compare_i("ß", "r") > 0); + assert(uni_compare_i("ß", "t") < 0); + assert(uni_compare_i("s", "ß") < 0); + assert(uni_compare_i("r", "ß") < 0); + assert(uni_compare_i("t", "ß") > 0); + assert(uni_compare_i("ä", "ß") > 0); + assert(uni_compare_i("sß", "ss") > 0); + assert(uni_compare_i("sß", "ß") > 0); + assert(uni_compare_i("sß", "ßß") < 0); + assert(uni_compare_i("ss", "sß") < 0); + assert(uni_compare_i("ß", "sß") < 0); + assert(uni_compare_i("ßß", "sß") > 0); + // check uneven/recursive comparisons + assert(uni_compare_i("ßẞ", "ẞß") == 0); + assert(uni_compare_i("sß", "ẞs") == 0); + assert(uni_compare_i("ẞs", "sß") == 0); + assert(uni_compare_i("ẞß", "sßs") == 0); + assert(uni_compare_i("sẞs", "ẞß") == 0); + assert(uni_compare_i("sẞsß", "ßsßs") == 0); + assert(uni_compare_i("ẞsßs", "sßsß") == 0); + assert(uni_compare_i("ßßßs", "sßßß") == 0); + assert(uni_compare_i("sßßß", "ßßßs") == 0); +} diff --git a/src/urt/system.d b/src/urt/system.d index 84f97a1..585404f 100644 --- a/src/urt/system.d +++ b/src/urt/system.d @@ -26,7 +26,7 @@ void sleep(Duration duration) version (Windows) { - import core.sys.windows.winbase : Sleep; + import urt.internal.sys.windows.winbase : Sleep; Sleep(cast(uint)duration.as!"msecs"); } else @@ -39,17 +39,17 @@ void sleep(Duration duration) struct SystemInfo { - string osName; + string os_name; string processor; - ulong totalMemory; - ulong availableMemory; + ulong total_memory; + ulong available_memory; Duration uptime; } -SystemInfo getSysInfo() +SystemInfo get_sysinfo() { SystemInfo r; - r.osName = Platform; + r.os_name = Platform; r.processor = ProcessorFamily; version (Windows) { @@ -57,8 +57,8 @@ SystemInfo getSysInfo() mem.dwLength = MEMORYSTATUSEX.sizeof; if (GlobalMemoryStatusEx(&mem)) { - r.totalMemory = mem.ullTotalPhys; - r.availableMemory = mem.ullAvailPhys; + r.total_memory = mem.ullTotalPhys; + r.available_memory = mem.ullAvailPhys; } r.uptime = msecs(GetTickCount64()); } @@ -70,8 +70,8 @@ SystemInfo getSysInfo() if (sysinfo(&info) < 0) assert(false, "sysinfo() failed!"); - r.totalMemory = cast(ulong)info.totalram * info.mem_unit; - r.availableMemory = cast(ulong)info.freeram * info.mem_unit; + r.total_memory = cast(ulong)info.totalram * info.mem_unit; + r.available_memory = cast(ulong)info.freeram * info.mem_unit; r.uptime = seconds(info.uptime); } else version (Posix) @@ -83,23 +83,23 @@ SystemInfo getSysInfo() assert(pages >= 0 && page_size >= 0, "sysconf() failed!"); - r.totalMemory = cast(ulong)pages * page_size; - static assert(false, "TODO: need `availableMemory`"); + r.total_memory = cast(ulong)pages * page_size; + static assert(false, "TODO: need `available_memory`"); } return r; } -void setSystemIdleParams(IdleParams params) +void set_system_idle_params(IdleParams params) { version (Windows) { - import core.sys.windows.winbase; + import urt.internal.sys.windows.winbase; enum EXECUTION_STATE ES_SYSTEM_REQUIRED = 0x00000001; enum EXECUTION_STATE ES_DISPLAY_REQUIRED = 0x00000002; enum EXECUTION_STATE ES_CONTINUOUS = 0x80000000; - SetThreadExecutionState(ES_CONTINUOUS | (params.SystemRequired ? ES_SYSTEM_REQUIRED : 0) | (params.DisplayRequired ? ES_DISPLAY_REQUIRED : 0)); + SetThreadExecutionState(ES_CONTINUOUS | ((params & IdleParams.SystemRequired) ? ES_SYSTEM_REQUIRED : 0) | ((params & IdleParams.DisplayRequired) ? ES_DISPLAY_REQUIRED : 0)); } else version (Posix) { @@ -112,11 +112,11 @@ void setSystemIdleParams(IdleParams params) unittest { - SystemInfo info = getSysInfo(); + SystemInfo info = get_sysinfo(); assert(info.uptime > Duration.zero); import urt.io; - writelnf("System info: {0} - {1}, mem: {2}kb ({3}kb)", info.osName, info.processor, info.totalMemory / (1024), info.availableMemory / (1024)); + writelnf("System info: {0} - {1}, mem: {2}kb ({3}kb)", info.os_name, info.processor, info.total_memory / (1024), info.available_memory / (1024)); } @@ -124,7 +124,7 @@ package: version (Windows) { - import core.sys.windows.winbase : GlobalMemoryStatusEx, MEMORYSTATUSEX; + import urt.internal.sys.windows.winbase : GlobalMemoryStatusEx, MEMORYSTATUSEX; extern(Windows) ulong GetTickCount64(); diff --git a/src/urt/time.d b/src/urt/time.d index 646af39..43592d1 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -1,10 +1,10 @@ module urt.time; -import urt.traits : isSomeFloat; +import urt.traits : is_some_float; version (Windows) { - import core.sys.windows.windows; + import urt.internal.sys.windows; extern (Windows) void GetSystemTimePreciseAsFileTime(FILETIME* lpSystemTimeAsFileTime) nothrow @nogc; } @@ -28,7 +28,8 @@ enum Day : ubyte enum Month : ubyte { - January = 1, + Unspecified = 0, + January, February, March, April, @@ -63,10 +64,15 @@ pure nothrow @nogc: T opCast(T)() const if (is(T == Time!c, Clock c) && c != clock) { - static if (clock == Clock.Monotonic && c == Clock.SystemTime) - return SysTime(ticks + ticksSinceBoot); + static if (is(T == Time!c, Clock c) && c != clock) + { + static if (clock == Clock.Monotonic && c == Clock.SystemTime) + return SysTime(ticks + ticks_since_boot); + else + return MonoTime(ticks - ticks_since_boot); + } else - return MonoTime(ticks - ticksSinceBoot); + static assert(false, "constraint out of sync"); } bool opEquals(Time!clock b) const @@ -82,9 +88,9 @@ pure nothrow @nogc: static if (clock != c) { static if (clock == Clock.Monotonic) - t1 += ticksSinceBoot; + t1 += ticks_since_boot; else - t2 += ticksSinceBoot; + t2 += ticks_since_boot; } return Duration(t1 - t2); } @@ -100,14 +106,38 @@ pure nothrow @nogc: import urt.string.format : FormatArg; ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const { - long ms = (ticks != 0 ? appTime(this) : Duration()).as!"msecs"; - if (!buffer.ptr) - return 2 + timeToString(ms, null); - if (buffer.length < 2) - return -1; - buffer[0..2] = "T+"; - ptrdiff_t len = timeToString(ms, buffer[2..$]); - return len < 0 ? len : 2 + len; + static if (clock == Clock.SystemTime) + { + DateTime dt = getDateTime(this); + return dt.toString(buffer, format, formatArgs); + } + else + { + long ns = (ticks != 0 ? appTime(this) : Duration()).as!"nsecs"; + if (!buffer.ptr) + return 2 + timeToString(ns, null); + if (buffer.length < 2) + return -1; + buffer[0..2] = "T+"; + ptrdiff_t len = timeToString(ns, buffer[2..$]); + return len < 0 ? len : 2 + len; + } + } + + ptrdiff_t fromString(const(char)[] s) + { + static if (clock == Clock.SystemTime) + { + DateTime dt; + ptrdiff_t len = dt.fromString(s); + if (len >= 0) + this = getSysTime(dt); + return len; + } + else + { + assert(false, "TODO: ???"); // what is the format we parse? + } } auto __debugOverview() const @@ -137,8 +167,8 @@ pure nothrow @nogc: bool opCast(T : bool)() const => ticks != 0; - T opCast(T)() const if (isSomeFloat!T) - => cast(T)ticks / cast(T)ticksPerSecond; + T opCast(T)() const if (is_some_float!T) + => cast(T)ticks / cast(T)ticks_per_second; bool opEquals(Duration b) const => ticks == b.ticks; @@ -161,21 +191,21 @@ pure nothrow @nogc: long as(string base)() const { static if (base == "nsecs") - return ticks*nsecMultiplier; + return ticks*nsec_multiplier; else static if (base == "usecs") - return ticks*nsecMultiplier / 1_000; + return ticks*nsec_multiplier / 1_000; else static if (base == "msecs") - return ticks*nsecMultiplier / 1_000_000; + return ticks*nsec_multiplier / 1_000_000; else static if (base == "seconds") - return ticks*nsecMultiplier / 1_000_000_000; + return ticks*nsec_multiplier / 1_000_000_000; else static if (base == "minutes") - return ticks*nsecMultiplier / 60_000_000_000; + return ticks*nsec_multiplier / 60_000_000_000; else static if (base == "hours") - return ticks*nsecMultiplier / 3_600_000_000_000; + return ticks*nsec_multiplier / 3_600_000_000_000; else static if (base == "days") - return ticks*nsecMultiplier / 86_400_000_000_000; + return ticks*nsec_multiplier / 86_400_000_000_000; else static if (base == "weeks") - return ticks*nsecMultiplier / 604_800_000_000_000; + return ticks*nsec_multiplier / 604_800_000_000_000; else static assert(false, "Invalid base"); } @@ -183,7 +213,98 @@ pure nothrow @nogc: import urt.string.format : FormatArg; ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const { - return timeToString(as!"msecs", buffer); + return timeToString(as!"nsecs", buffer); + } + + ptrdiff_t fromString(const(char)[] s) + { + import urt.conv : parse_int; + import urt.string.ascii : is_alpha, ieq; + + if (s.length == 0) + return -1; + + size_t offset = 0; + long total_nsecs = 0; + ubyte last_unit = 8; + + while (offset < s.length) + { + // Parse number + size_t len; + long value = s[offset..$].parse_int(&len); + if (len == 0) + return last_unit != 8 ? offset : -1; + offset += len; + + if (offset >= s.length) + return -1; + + // Parse unit + size_t unit_start = offset; + while (offset < s.length && s[offset].is_alpha) + offset++; + + if (offset == unit_start) + return -1; + + const(char)[] unit = s[unit_start..offset]; + + // Convert unit to nanoseconds and check for duplicates + ubyte this_unit; + if (unit.ieq("w")) + { + value *= 604_800_000_000_000; + this_unit = 7; + } + else if (unit.ieq("d")) + { + value *= 86_400_000_000_000; + this_unit = 6; + } + else if (unit.ieq("h")) + { + value *= 3_600_000_000_000; + this_unit = 5; + } + else if (unit.ieq("m")) + { + value *= 60_000_000_000; + this_unit = 4; + } + else if (unit.ieq("s")) + { + value *= 1_000_000_000; + this_unit = 3; + } + else if (unit.ieq("ms")) + { + value *= 1_000_000; + this_unit = 2; + } + else if (unit.ieq("us")) + { + value *= 1_000; + this_unit = 1; + } + else if (unit.ieq("ns")) + this_unit = 0; + else + return -1; + + // Check for ordering, duplicates, and only one of ms/us/ns may be present + if (this_unit >= last_unit || (this_unit < 2 && last_unit < 3)) + return -1; + last_unit = this_unit; + + total_nsecs += value; + } + + if (last_unit == 8) + return -1; + + ticks = total_nsecs / nsec_multiplier; + return offset; } auto __debugOverview() const @@ -276,76 +397,303 @@ pure nothrow @nogc: this = mixin("this " ~ op ~ " rhs;"); } + int opCmp(DateTime dt) const + { + int r = year - dt.year; + if (r != 0) return r; + r = month - dt.month; + if (r != 0) return r; + r = day - dt.day; + if (r != 0) return r; + r = hour - dt.hour; + if (r != 0) return r; + r = minute - dt.minute; + if (r != 0) return r; + r = second - dt.second; + if (r != 0) return r; + return ns - dt.ns; + } + import urt.string.format : FormatArg; ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const { - import urt.conv : formatInt; + import urt.conv : format_int, format_uint; - size_t offset = 0; - uint y = year; - if (year <= 0) + ptrdiff_t len; + if (!buffer.ptr) { - if (buffer.length < 3) - return -1; - y = -year + 1; - buffer[0 .. 3] = "BC "; - offset += 3; + len = 15; // all the fixed chars + len += year.format_int(null); + if (ns) + { + ++len; // the dot + uint nsecs = ns; + uint m = 0; + while (nsecs) + { + ++len; + uint digit = nsecs / digit_multipliers[m]; + nsecs -= digit * digit_multipliers[m++]; + } + } + return len; } - ptrdiff_t len = year.formatInt(buffer[offset..$]); - if (len < 0 || len == buffer.length) + + len = year.format_int(buffer[]); + if (len < 0 || len + 15 > buffer.length) return -1; - offset += len; + size_t offset = len; buffer[offset++] = '-'; - len = month.formatInt(buffer[offset..$]); - if (len < 0 || len == buffer.length) - return -1; - offset += len; + buffer[offset++] = '0' + (month / 10); + buffer[offset++] = '0' + (month % 10); buffer[offset++] = '-'; - len = day.formatInt(buffer[offset..$]); - if (len < 0 || len == buffer.length) + buffer[offset++] = '0' + (day / 10); + buffer[offset++] = '0' + (day % 10); + buffer[offset++] = 'T'; + buffer[offset++] = '0' + (hour / 10); + buffer[offset++] = '0' + (hour % 10); + buffer[offset++] = ':'; + buffer[offset++] = '0' + (minute / 10); + buffer[offset++] = '0' + (minute % 10); + buffer[offset++] = ':'; + buffer[offset++] = '0' + (second / 10); + buffer[offset++] = '0' + (second % 10); + if (ns) + { + if (offset == buffer.length) + return -1; + buffer[offset++] = '.'; + uint nsecs = ns; + uint m = 0; + while (nsecs) + { + if (offset == buffer.length) + return -1; + int digit = nsecs / digit_multipliers[m]; + buffer[offset++] = cast(char)('0' + digit); + nsecs -= digit * digit_multipliers[m++]; + } + } + // TODO: timezone suffix? + return offset; + } + + ptrdiff_t fromString(const(char)[] s) + { + import urt.conv : parse_int, parse_uint; + import urt.string.ascii : ieq, is_numeric, is_whitespace, to_lower; + + month = Month.Unspecified; + day = 0; + hour = 0; + minute = 0; + second = 0; + ns = 0; + + size_t offset = 0; + + // parse year + if (s.length >= 2 && s[0..2].ieq("bc")) + { + offset = 2; + if (s.length >= 3 && s[2] == ' ') + ++offset; + } + size_t len; + long value = s[offset..$].parse_int(&len); + if (len == 0) return -1; + if (offset > 0) + { + if (value <= 0) + return -1; // no year 0, or negative years BC! + value = -value + 1; + } + if (value < short.min || value > short.max) + return -1; + year = cast(short)value; offset += len; - buffer[offset++] = ' '; - len = hour.formatInt(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) + + if (offset == s.length || (s[offset] != '-' && s[offset] != '/')) + return offset; + + // parse month + value = s[++offset..$].parse_int(&len); + if (len == 0) + { + if (s.length < 3) + return -1; + foreach (i; 0..12) + { + if (s[offset..offset+3].ieq(g_month_names[i])) + { + value = i + 1; + len = 3; + goto got_month; + } + } + return -1; + got_month: + } + else if (value < 1 || value > 12) return -1; + month = cast(Month)value; offset += len; - buffer[offset++] = ':'; - len = minute.formatInt(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) + + if (offset == s.length || (s[offset] != '-' && s[offset] != '/')) + return offset; + + // parse day + value = s[++offset..$].parse_int(&len); + if (len == 0 || value < 1 || value > 31) return -1; + day = cast(ubyte)value; offset += len; - buffer[offset++] = ':'; - len = second.formatInt(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) + + if (offset == s.length || (s[offset] != 'T' && s[offset] != ' ')) + return offset; + + // parse hour + value = s[++offset..$].parse_int(&len); + if (len != 2 || value < 0 || value > 23) return -1; + hour = cast(ubyte)value; offset += len; - buffer[offset++] = '.'; - len = (ns / 1_000_000).formatInt(buffer[offset..$], 10, 3, '0'); - if (len < 0) - return len; - return offset + len; + + if (offset == s.length) + return offset; + + if (s[offset] == ':') + { + // parse minute + value = s[++offset..$].parse_int(&len); + if (len != 2 || value < 0 || value > 59) + return -1; + minute = cast(ubyte)value; + offset += len; + + if (offset == s.length) + return offset; + + if (s[offset] == ':') + { + // parse second + value = s[++offset..$].parse_int(&len); + if (len != 2 || value < 0 || value > 59) + return -1; + second = cast(ubyte)value; + offset += len; + + if (offset < s.length && s[offset] == '.') + { + // parse fractions + ++offset; + uint multiplier = 100_000_000; + while (offset < s.length && multiplier > 0 && s[offset].is_numeric) + { + ns += (s[offset++] - '0') * multiplier; + multiplier /= 10; + } + if (multiplier == 100_000_000) + return offset-1; // no number after the dot + } + } + else if (s[offset] == '.') + { + // fraction of minute + assert(false, "TODO"); + } + } + else if (s[offset] == '.') + { + // fraction of hour + assert(false, "TODO"); + } + + if (offset == s.length) + return offset; + + if (s[offset].to_lower == 'z') + { + // TODO: UTC timezone designator... +// assert(false, "TODO: we need to know our local timezone..."); + + return offset + 1; + } + + size_t tz_offset = offset; + if (s[offset] == ' ') + ++tz_offset; + if (s[tz_offset] != '-' && s[tz_offset] != '+') + return offset; + bool tz_neg = s[tz_offset] == '-'; + tz_offset += 1; + + // parse timezone (00:00) + int tz_hr, tz_min; + + value = s[tz_offset..$].parse_uint(&len); + if (len == 0) + return offset; + + if (len == 4) + { + if (value > 2359) + return -1; + tz_min = cast(int)(value % 100); + if (tz_min > 59) + return -1; + tz_hr = cast(int)(value / 100); + tz_offset += 4; + } + else + { + if (len != 2 || value > 59) + return -1; + + tz_hr = cast(int)value; + tz_offset += 2; + + if (tz_offset < s.length && s[tz_offset] == ':') + { + value = s[tz_offset+1..$].parse_uint(&len); + if (len != 0) + { + if (len != 2 || value > 59) + return -1; + tz_min = cast(int)value; + tz_offset += 3; + } + } + } + + if (tz_neg) + tz_hr = -tz_hr; + +// assert(false, "TODO: we need to know our local timezone..."); + + return tz_offset; } } Duration dur(string base)(long value) pure { static if (base == "nsecs") - return Duration(value / nsecMultiplier); + return Duration(value / nsec_multiplier); else static if (base == "usecs") - return Duration(value*1_000 / nsecMultiplier); + return Duration(value*1_000 / nsec_multiplier); else static if (base == "msecs") - return Duration(value*1_000_000 / nsecMultiplier); + return Duration(value*1_000_000 / nsec_multiplier); else static if (base == "seconds") - return Duration(value*1_000_000_000 / nsecMultiplier); + return Duration(value*1_000_000_000 / nsec_multiplier); else static if (base == "minutes") - return Duration(value*60_000_000_000 / nsecMultiplier); + return Duration(value*60_000_000_000 / nsec_multiplier); else static if (base == "hours") - return Duration(value*3_600_000_000_000 / nsecMultiplier); + return Duration(value*3_600_000_000_000 / nsec_multiplier); else static if (base == "days") - return Duration(value*86_400_000_000_000 / nsecMultiplier); + return Duration(value*86_400_000_000_000 / nsec_multiplier); else static if (base == "weeks") - return Duration(value*604_800_000_000_000 / nsecMultiplier); + return Duration(value*604_800_000_000_000 / nsec_multiplier); else static assert(false, "Invalid base"); } @@ -395,31 +743,43 @@ SysTime getSysTime() } } +SysTime getSysTime(DateTime time) pure +{ + version (Windows) + return datetime_to_filetime(time); + else version (Posix) + { + timespec ts = datetime_to_realtime(time); + return SysTime(ts.tv_sec * 1_000_000_000 + ts.tv_nsec); + } + else + static assert(false, "TODO"); +} DateTime getDateTime() { version (Windows) - return fileTimeToDateTime(getSysTime()); + return filetime_to_datetime(getSysTime()); else version (Posix) { timespec ts; clock_gettime(CLOCK_REALTIME, &ts); - return realtimeToDateTime(ts); + return realtime_to_datetime(ts); } else static assert(false, "TODO"); } -DateTime getDateTime(SysTime time) +DateTime getDateTime(SysTime time) pure { version (Windows) - return fileTimeToDateTime(time); + return filetime_to_datetime(time); else version (Posix) { timespec ts; ts.tv_sec = cast(time_t)(time.ticks / 1_000_000_000); ts.tv_nsec = cast(uint)(time.ticks % 1_000_000_000); - return realtimeToDateTime(ts); + return realtime_to_datetime(ts); } else static assert(false, "TODO"); @@ -443,6 +803,16 @@ ulong unixTimeNs(SysTime t) pure static assert(false, "TODO"); } +SysTime from_unix_time_ns(ulong ns) pure +{ + version (Windows) + return SysTime(ns / 100UL + 116444736000000000UL); + else version (Posix) + return SysTime(ns); + else + static assert(false, "TODO"); +} + Duration abs(Duration d) pure => Duration(d.ticks < 0 ? -d.ticks : d.ticks); @@ -451,32 +821,35 @@ private: immutable MonoTime startTime; +__gshared immutable string[12] g_month_names = [ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" ]; +__gshared immutable uint[9] digit_multipliers = [ 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1 ]; + version (Windows) { - immutable uint ticksPerSecond; - immutable uint nsecMultiplier; - immutable ulong ticksSinceBoot; + immutable uint ticks_per_second; + immutable uint nsec_multiplier; + immutable ulong ticks_since_boot; } else version (Posix) { - enum uint ticksPerSecond = 1_000_000_000; - enum uint nsecMultiplier = 1; - immutable ulong ticksSinceBoot; + enum uint ticks_per_second = 1_000_000_000; + enum uint nsec_multiplier = 1; + immutable ulong ticks_since_boot; } -package(urt) void initClock() +package(urt) void init_clock() { cast()startTime = getTime(); version (Windows) { - import core.sys.windows.windows; + import urt.internal.sys.windows; import urt.util : min; LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); - cast()ticksPerSecond = cast(uint)freq.QuadPart; - cast()nsecMultiplier = 1_000_000_000 / ticksPerSecond; + cast()ticks_per_second = cast(uint)freq.QuadPart; + cast()nsec_multiplier = 1_000_000_000 / ticks_per_second; // we want the ftime for QPC 0; which should be the boot time // we'll repeat this 100 times and take the minimum, and we should be within probably nanoseconds of the correct value @@ -488,7 +861,7 @@ package(urt) void initClock() GetSystemTimePreciseAsFileTime(cast(FILETIME*)&ftime); bootTime = min(bootTime, ftime - qpc.QuadPart); } - cast()ticksSinceBoot = bootTime; + cast()ticks_since_boot = bootTime; } else version (Posix) { @@ -503,28 +876,45 @@ package(urt) void initClock() clock_gettime(CLOCK_REALTIME, &rt); bootTime = min(bootTime, rt.tv_sec*1_000_000_000 + rt.tv_nsec - mt.tv_sec*1_000_000_000 - mt.tv_nsec); } - cast()ticksSinceBoot = bootTime; + cast()ticks_since_boot = bootTime; } else static assert(false, "TODO"); } -ptrdiff_t timeToString(long ms, char[] buffer) pure +ptrdiff_t timeToString(long ns, char[] buffer) pure { - import urt.conv : formatInt; + import urt.conv : format_int; - long hr = ms / 3_600_000; + int hr = cast(int)(ns / 3_600_000_000_000); + ns = ns < 0 ? -ns % 3_600_000_000_000 : ns % 3_600_000_000_000; + uint remainder = cast(uint)(ns % 1_000_000_000); if (!buffer.ptr) - return hr.formatInt(null, 10, 2, '0') + 10; + { + size_t tail = 6; + if (remainder) + { + ++tail; + uint m = 0; + do + { + ++tail; + uint digit = cast(uint)(remainder / digit_multipliers[m]); + remainder -= digit * digit_multipliers[m++]; + } + while (remainder); + } + return hr.format_int(null, 10, 2, '0') + tail; + } - ptrdiff_t len = hr.formatInt(buffer, 10, 2, '0'); - if (len < 0 || buffer.length < len + 10) + ptrdiff_t len = hr.format_int(buffer, 10, 2, '0'); + if (len < 0 || buffer.length < len + 6) return -1; - ubyte min = cast(ubyte)(ms / 60_000 % 60); - ubyte sec = cast(ubyte)(ms / 1000 % 60); - ms %= 1000; + uint min_sec = cast(uint)(ns / 1_000_000_000); + uint min = min_sec / 60; + uint sec = min_sec % 60; buffer.ptr[len++] = ':'; buffer.ptr[len++] = cast(char)('0' + (min / 10)); @@ -532,10 +922,22 @@ ptrdiff_t timeToString(long ms, char[] buffer) pure buffer.ptr[len++] = ':'; buffer.ptr[len++] = cast(char)('0' + (sec / 10)); buffer.ptr[len++] = cast(char)('0' + (sec % 10)); - buffer.ptr[len++] = '.'; - buffer.ptr[len++] = cast(char)('0' + (ms / 100)); - buffer.ptr[len++] = cast(char)('0' + ((ms/10) % 10)); - buffer.ptr[len++] = cast(char)('0' + (ms % 10)); + if (remainder) + { + if (buffer.length < len + 2) + return -1; + buffer.ptr[len++] = '.'; + uint i = 0; + while (remainder) + { + if (buffer.length <= len) + return -1; + uint m = digit_multipliers[i++]; + uint digit = cast(uint)(remainder / m); + buffer.ptr[len++] = cast(char)('0' + digit); + remainder -= digit * m; + } + } return len; } @@ -544,22 +946,79 @@ unittest import urt.mem.temp; assert(tconcat(msecs(3_600_000*3 + 60_000*47 + 1000*34 + 123))[] == "03:47:34.123"); - assert(tconcat(msecs(3_600_000*-123))[] == "-123:00:00.000"); - - assert(getTime().toString(null, null, null) == 14); + assert(tconcat(msecs(3_600_000*-123))[] == "-123:00:00"); + assert(tconcat(usecs(3_600_000_000*-123 + 1))[] == "-122:59:59.999999"); + assert(MonoTime().toString(null, null, null) == 10); assert(tconcat(getTime())[0..2] == "T+"); + + // Test Duration.fromString with compound formats + Duration d; + + // Simple single units + assert(d.fromString("5s") == 2 && d.as!"seconds" == 5); + assert(d.fromString("10m") == 3 && d.as!"minutes" == 10); + + // Compound durations + assert(d.fromString("1h30m") == 5 && d.as!"minutes" == 90); + assert(d.fromString("5m30s") == 5 && d.as!"seconds" == 330); + + // Duplicate units should fail + assert(d.fromString("30m30m") == -1); + assert(d.fromString("1h2h") == -1); + assert(d.fromString("5s10s") == -1); + + // Out-of-order units should fail + assert(d.fromString("30s5m") == -1); // s before m + assert(d.fromString("1m2h") == -1); // m before h + assert(d.fromString("5s10ms5m") == -1); // m after s + assert(d.fromString("5s10ms10ns") == -1); // ms and ns (only one sub-second unit allowed) + + // Improper units should fail + assert(d.fromString("30z") == -1); // not a time denomination + + // Correctly ordered units should work + assert(d.fromString("1d2h30m15s") == 10 && d.as!"seconds" == 86_400 + 7_200 + 1_800 + 15); + + // Test DateTime.fromString + DateTime dt; + assert(dt.fromString("2024-06-15T12:34:56") == 19); + assert(dt.fromString("-100/06/15 12:34:56") == 19); + assert(dt.fromString("BC100-AUG-15 12:34:56.789") == 25); + assert(dt.fromString("BC 10000-AUG-15 12:34:56.789123456") == 34); + assert(dt.fromString("1-1-1 01:01:01") == 14); + assert(dt.fromString("1-1-1 01:01:01.") == 14); + assert(dt.fromString("2025") == 4); + assert(dt.fromString("2025-10") == 7); + assert(dt.fromString("2025-01-01") == 10); + assert(dt.fromString("2025-01-01 00") == 13); + assert(dt.fromString("2025-01-01 00:10") == 16); + assert(dt.fromString("2025-01-01 00:10z") == 17); + assert(dt.fromString("2025-01-01 00+00:00") == 19); + assert(dt.fromString("2025-01-01 00-1030") == 18); + assert(dt.fromString("2025-01-01 00+08") == 16); + + assert(dt.fromString("BC -10") == -1); + assert(dt.fromString("2024-0-15 12:34:56") == -1); + assert(dt.fromString("2024-13-15 12:34:56") == -1); + assert(dt.fromString("2024-1-0 12:34:56") == -1); + assert(dt.fromString("2024-1-32 12:34:56") == -1); + assert(dt.fromString("2024-1-1 24:34:56") == -1); + assert(dt.fromString("2024-1-1 01:60:56") == -1); + assert(dt.fromString("2024-1-1 01:01:60") == -1); + assert(dt.fromString("10000-1-1 1:01:01") == -1); } version (Windows) { - DateTime fileTimeToDateTime(SysTime ftime) + DateTime filetime_to_datetime(SysTime ftime) pure { version (BigEndian) static assert(false, "Only works in little endian!"); SYSTEMTIME stime; - FileTimeToSystemTime(cast(FILETIME*)&ftime.ticks, &stime); + alias PureHACK = extern(Windows) BOOL function(const(FILETIME)*, LPSYSTEMTIME) pure nothrow @nogc; + (cast(PureHACK)&FileTimeToSystemTime)(cast(FILETIME*)&ftime.ticks, &stime); DateTime dt; dt.year = stime.wYear; @@ -575,13 +1034,40 @@ version (Windows) return dt; } + + SysTime datetime_to_filetime(ref DateTime dt) pure + { + version (BigEndian) + static assert(false, "Only works in little endian!"); + + SYSTEMTIME stime; + stime.wYear = dt.year; + stime.wMonth = cast(ushort)dt.month; + stime.wDayOfWeek = cast(ushort)dt.wday; + stime.wDay = cast(ushort)dt.day; + stime.wHour = cast(ushort)dt.hour; + stime.wMinute = cast(ushort)dt.minute; + stime.wSecond = cast(ushort)dt.second; + stime.wMilliseconds = cast(ushort)(dt.ns / 1_000_000); + + SysTime ftime; + alias PureHACK = extern(Windows) BOOL function(const(SYSTEMTIME)*, FILETIME*) pure nothrow @nogc; + if (!(cast(PureHACK)&SystemTimeToFileTime)(&stime, cast(FILETIME*)&ftime)) + assert(false, "TODO: WHAT TO DO?"); + + debug assert(ftime.ticks % 10_000_000 == (dt.ns / 1_000_000) * 10_000); + ftime.ticks = ftime.ticks - ftime.ticks % 10_000_000 + dt.ns / 100; + + return ftime; + } } else version (Posix) { - DateTime realtimeToDateTime(timespec ts) + DateTime realtime_to_datetime(timespec ts) pure { tm t; - gmtime_r(&ts.tv_sec, &t); + alias PureHACK = extern(C) tm* function(time_t* timer, tm* buf) pure nothrow @nogc; + (cast(PureHACK)&gmtime_r)(&ts.tv_sec, &t); DateTime dt; dt.year = cast(short)(t.tm_year + 1900); @@ -595,4 +1081,23 @@ else version (Posix) return dt; } + + timespec datetime_to_realtime(ref DateTime time) pure + { + tm t; + t.tm_year = time.year - 1900; + t.tm_mon = cast(int)time.month - 1; + t.tm_mday = time.day; + t.tm_hour = time.hour; + t.tm_min = time.minute; + t.tm_sec = time.second; + + alias PureHACK = extern(C) time_t function(tm* timer) pure nothrow @nogc; + time_t sec = (cast(PureHACK)&mktime)(&t); + + timespec ts; + ts.tv_sec = sec; + ts.tv_nsec = time.ns; + return ts; + } } diff --git a/src/urt/traits.d b/src/urt/traits.d index 30ef675..6f8a761 100644 --- a/src/urt/traits.d +++ b/src/urt/traits.d @@ -3,67 +3,67 @@ module urt.traits; import urt.meta; -enum bool isType(alias X) = is(X); +enum bool is_type(alias X) = is(X); -enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); +enum bool is_boolean(T) = __traits(isUnsigned, T) && is(T : bool); -enum bool isUnsignedInt(T) = is(Unqual!T == ubyte) || is(Unqual!T == ushort) || is(Unqual!T == uint) || is(Unqual!T == ulong); -enum bool isSignedInt(T) = is(Unqual!T == byte) || is(Unqual!T == short) || is(Unqual!T == int) || is(Unqual!T == long); -enum bool isSomeInt(T) = isUnsignedInt!T || isSignedInt!T; -enum bool isUnsignedIntegral(T) = is(Unqual!T == bool) || isUnsignedInt!T || isSomeChar!T; -enum bool isSignedIntegral(T) = isSignedInt!T; -enum bool isIntegral(T) = isUnsignedIntegral!T || isSignedIntegral!T; -enum bool isSomeFloat(T) = is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real); +enum bool is_unsigned_int(T) = is(Unqual!T == ubyte) || is(Unqual!T == ushort) || is(Unqual!T == uint) || is(Unqual!T == ulong); +enum bool is_signed_int(T) = is(Unqual!T == byte) || is(Unqual!T == short) || is(Unqual!T == int) || is(Unqual!T == long); +enum bool is_some_int(T) = is_unsigned_int!T || is_signed_int!T; +enum bool is_unsigned_integral(T) = is_boolean!T || is_unsigned_int!T || is_some_char!T; +enum bool is_signed_integral(T) = is_signed_int!T; +enum bool is_integral(T) = is_unsigned_integral!T || is_signed_integral!T; +enum bool is_some_float(T) = is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real); -enum bool isEnum(T) = is(T == enum); -template enumType(T) - if (isEnum!T) +enum bool is_enum(T) = is(T == enum); +template EnumType(T) + if (is_enum!T) { static if (is(T E == enum)) - alias enumType = E; + alias EnumType = E; else static assert(false, "How this?"); } -template isUnsigned(T) +template is_unsigned(T) { static if (!__traits(isUnsigned, T)) - enum isUnsigned = false; + enum is_unsigned = false; else static if (is(T U == enum)) - enum isUnsigned = isUnsigned!U; + enum is_unsigned = is_unsigned!U; else - enum isUnsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar. + enum is_unsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar. && !is(immutable T == immutable bool) && !is(T == __vector); } -enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) && is(T : real); +enum bool is_signed(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) && is(T : real); -template isSomeChar(T) +template is_some_char(T) { static if (!__traits(isUnsigned, T)) - enum isSomeChar = false; + enum is_some_char = false; else static if (is(T U == enum)) - enum isSomeChar = isSomeChar!U; + enum is_some_char = is_some_char!U; else - enum isSomeChar = !__traits(isZeroInit, T); + enum is_some_char = !__traits(isZeroInit, T); } -enum bool isSomeFunction(alias T) = is(T == return) || is(typeof(T) == return) || is(typeof(&T) == return); -enum bool isFunctionPointer(alias T) = is(typeof(*T) == function); -enum bool isDelegate(alias T) = is(typeof(T) == delegate) || is(T == delegate); +enum bool is_some_function(alias T) = is(T == return) || is(typeof(T) == return) || is(typeof(&T) == return); +enum bool is_function_pointer(alias T) = is(typeof(*T) == function); +enum bool is_delegate(alias T) = is(typeof(T) == delegate) || is(T == delegate); -template isCallable(alias callable) +template is_callable(alias callable) { static if (is(typeof(&callable.opCall) == delegate)) - enum bool isCallable = true; + enum bool is_callable = true; else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) - enum bool isCallable = true; + enum bool is_callable = true; else static if (is(typeof(&callable.opCall!()) TemplateInstanceType)) - enum bool isCallable = isCallable!TemplateInstanceType; + enum bool is_callable = is_callable!TemplateInstanceType; else static if (is(typeof(&callable!()) TemplateInstanceType)) - enum bool isCallable = isCallable!TemplateInstanceType; + enum bool is_callable = is_callable!TemplateInstanceType; else - enum bool isCallable = isSomeFunction!callable; + enum bool is_callable = is_some_function!callable; } @@ -79,7 +79,7 @@ template Unqual(T : const U, U) template Unsigned(T) { - static if (isUnsigned!T) + static if (is_unsigned!T) alias Unsigned = T; else static if (is(T == long)) alias Unsigned = ulong; @@ -113,7 +113,7 @@ template Unsigned(T) template Signed(T) { - static if (isSigned!T) + static if (is_signed!T) alias Unsigned = T; else static if (is(T == ulong)) alias Signed = long; @@ -144,7 +144,7 @@ template Signed(T) } template ReturnType(alias func) - if (isCallable!func) + if (is_callable!func) { static if (is(FunctionTypeOf!func R == return)) alias ReturnType = R; @@ -153,7 +153,7 @@ template ReturnType(alias func) } template Parameters(alias func) - if (isCallable!func) + if (is_callable!func) { static if (is(FunctionTypeOf!func P == function)) alias Parameters = P; @@ -161,26 +161,26 @@ template Parameters(alias func) static assert(0, "argument has no parameters"); } -template ParameterIdentifierTuple(alias func) - if (isCallable!func) +template parameter_identifier_tuple(alias func) + if (is_callable!func) { static if (is(FunctionTypeOf!func PT == __parameters)) { - alias ParameterIdentifierTuple = AliasSeq!(); + alias parameter_identifier_tuple = AliasSeq!(); static foreach (i; 0 .. PT.length) { - static if (!isFunctionPointer!func && !isDelegate!func + static if (!is_function_pointer!func && !is_delegate!func // Unnamed parameters yield CT error. && is(typeof(__traits(identifier, PT[i .. i+1]))) // Filter out unnamed args, which look like (Type) instead of (Type name). && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) { - ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, + parameter_identifier_tuple = AliasSeq!(parameter_identifier_tuple, __traits(identifier, PT[i .. i+1])); } else { - ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, ""); + parameter_identifier_tuple = AliasSeq!(parameter_identifier_tuple, ""); } } } @@ -188,12 +188,12 @@ template ParameterIdentifierTuple(alias func) { static assert(0, func.stringof ~ " is not a function"); // avoid pointless errors - alias ParameterIdentifierTuple = AliasSeq!(); + alias parameter_identifier_tuple = AliasSeq!(); } } template FunctionTypeOf(alias func) - if (isCallable!func) + if (is_callable!func) { static if ((is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate)) alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol @@ -218,27 +218,29 @@ template FunctionTypeOf(alias func) } // is T a primitive/builtin type? -enum isPrimitive(T) = isIntegral!T || isSomeFloat!T || (isEnum!T && isPrimitive!(enumType!T) || - is(T == P*, P) || is(T == S[], S) || (is(T == A[N], A, size_t N) && isPrimitive!A) || +enum is_primitive(T) = is_integral!T || is_some_float!T || (is_enum!T && is_primitive!(EnumType!T) || + is(T == P*, P) || is(T == S[], S) || (is(T == A[N], A, size_t N) && is_primitive!A) || is(T == R function(Args), R, Args...) || is(T == R delegate(Args), R, Args...)); -enum isDefaultConstructible(T) = isPrimitive!T || (is(T == struct) && __traits(compiles, { T t; })); +enum is_trivial(T) = __traits(isPOD, T); -enum isConstructible(T, Args...) = (isPrimitive!T && (Args.length == 0 || (Args.length == 1 && is(Args[0] : T)))) || +enum is_default_constructible(T) = is_primitive!T || (is(T == struct) && __traits(compiles, { T t; })); + +enum is_constructible(T, Args...) = (is_primitive!T && (Args.length == 0 || (Args.length == 1 && is(Args[0] : T)))) || (is(T == struct) && __traits(compiles, (Args args) { T x = T(args); })); // this probably fails if the struct can't be assigned to x... TODO: use placement new? // TODO: we need to know it's not calling an elaborate constructor... -//enum isTriviallyConstructible(T, Args...) = (isPrimitive!T && (Args.length == 0 || (Args.length == 1 && is(Args[0] : T)))) || -// (is(T == struct) && __traits(compiles, (Args args) { auto x = T(args); })); // this probably fails if the struct can't be assigned to x... TODO: use placement new? +//enum is_trivially_constructible(T, Args...) = (is_primitive!T && (Args.length == 0 || (Args.length == 1 && is(Args[0] : T)))) || +// (is(T == struct) && __traits(compiles, (Args args) { auto x = T(args); })); // this probably fails if the struct can't be assigned to x... TODO: use placement new? -//enum isCopyConstructible(T) = isPrimitive!T || (is(T == struct) && __traits(compiles, { T u = lvalueOf!T; })); -//enum isMoveConstructible(T) = isPrimitive!T || (is(T == struct) && __traits(compiles, { T u = rvalueOf!T; })); +//enum is_copy_constructible(T) = is_primitive!T || (is(T == struct) && __traits(compiles, { T u = lvalue_of!T; })); +//enum is_move_constructible(T) = is_primitive!T || (is(T == struct) && __traits(compiles, { T u = rvalue_of!T; })); -enum isTriviallyDefaultConstructible(T) = isDefaultConstructible!T; // dlang doesn't have elaborate default constructors (YET...) -//enum isTriviallyCopyConstructible(T) = isPrimitive!T; // TODO: somehow find out if there is no copy constructor -//enum isTriviallyMoveConstructible(T) = isPrimitive!T || is(T == struct); // TODO: somehow find out if there is no move constructor +enum is_trivially_default_constructible(T) = is_default_constructible!T; // dlang doesn't have elaborate default constructors (YET...) +//enum is_trivially_copy_constructible(T) = is_primitive!T; // TODO: somehow find out if there is no copy constructor +//enum is_trivially_move_constructible(T) = is_primitive!T || is(T == struct); // TODO: somehow find out if there is no move constructor // helpers to test certain expressions private struct __InoutWorkaroundStruct{} -@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init) pure nothrow @nogc; -@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init) pure nothrow @nogc; +@property T rvalue_of(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init) pure nothrow @nogc; +@property ref T lvalue_of(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init) pure nothrow @nogc; diff --git a/src/urt/util.d b/src/urt/util.d index 044f47f..4971ddd 100644 --- a/src/urt/util.d +++ b/src/urt/util.d @@ -30,33 +30,33 @@ pure: auto min(T, U)(auto ref inout T a, auto ref inout U b) { - return a < b ? a : b; + return b < a ? b : a; } auto max(T, U)(auto ref inout T a, auto ref inout U b) { - return a > b ? a : b; + return b > a ? b : a; } template Align(size_t value, size_t alignment = size_t.sizeof) { - static assert(isPowerOf2(alignment), "Alignment must be a power of two: ", alignment); + static assert(is_power_of_2(alignment), "Alignment must be a power of two: ", alignment); enum Align = alignTo(value, alignment); } -enum IsAligned(size_t value) = isAligned(value); -enum IsPowerOf2(size_t value) = isPowerOf2(value); -enum NextPowerOf2(size_t value) = nextPowerOf2(value); +enum IsAligned(size_t value) = is_aligned(value); +enum IsPowerOf2(size_t value) = is_power_of_2(value); +enum NextPowerOf2(size_t value) = next_power_of_2(value); -bool isPowerOf2(T)(T x) - if (isSomeInt!T) +bool is_power_of_2(T)(T x) + if (is_some_int!T) { return (x & (x - 1)) == 0; } -T nextPowerOf2(T)(T x) - if (isSomeInt!T) +T next_power_of_2(T)(T x) + if (is_some_int!T) { x -= 1; x |= x >> 1; @@ -71,43 +71,81 @@ T nextPowerOf2(T)(T x) return cast(T)(x + 1); } -T alignDown(size_t alignment, T)(T value) - if (isSomeInt!T || is(T == U*, U)) +T align_down(size_t alignment, T)(T value) + if (is_some_int!T || is(T == U*, U)) { return cast(T)(cast(size_t)value & ~(alignment - 1)); } -T alignDown(T)(T value, size_t alignment) - if (isSomeInt!T || is(T == U*, U)) +T align_down(T)(T value, size_t alignment) + if (is_some_int!T || is(T == U*, U)) { return cast(T)(cast(size_t)value & ~(alignment - 1)); } -T alignUp(size_t alignment, T)(T value) - if (isSomeInt!T || is(T == U*, U)) +T align_up(size_t alignment, T)(T value) + if (is_some_int!T || is(T == U*, U)) { return cast(T)((cast(size_t)value + (alignment - 1)) & ~(alignment - 1)); } -T alignUp(T)(T value, size_t alignment) - if (isSomeInt!T || is(T == U*, U)) +T align_up(T)(T value, size_t alignment) + if (is_some_int!T || is(T == U*, U)) { return cast(T)((cast(size_t)value + (alignment - 1)) & ~(alignment - 1)); } -bool isAligned(size_t alignment, T)(T value) - if (isSomeInt!T || is(T == U*, U)) +bool is_aligned(size_t alignment, T)(T value) + if (is_some_int!T || is(T == U*, U)) { - static assert(IsPowerOf2!alignment, "Alignment must be a power of two!"); + static assert(IsPowerOf2!alignment, "Alignment must be a power of two"); + static assert(T.sizeof <= size_t.sizeof, "TODO"); return (cast(size_t)value & (alignment - 1)) == 0; } -bool isAligned(T)(T value, size_t alignment) - if (isSomeInt!T || is(T == U*, U)) +bool is_aligned(T)(T value, size_t alignment) + if (is_some_int!T || is(T == U*, U)) { + static assert(T.sizeof <= size_t.sizeof, "TODO"); return (cast(size_t)value & (alignment - 1)) == 0; } +T rol(T)(const T value, const uint count) pure + if (__traits(isIntegral, T) && __traits(isUnsigned, T)) +{ + assert(count < 8 * T.sizeof); + if (count == 0) + return cast(T)value; + return cast(T)((value << count) | (value >> (T.sizeof * 8 - count))); +} + +T ror(T)(const T value, const uint count) pure + if (__traits(isIntegral, T) && __traits(isUnsigned, T)) +{ + assert(count < 8 * T.sizeof); + if (count == 0) + return cast(T)value; + return cast(T)((value >> count) | (value << (T.sizeof * 8 - count))); +} + +T rol(uint count, T)(const T value) pure + if (__traits(isIntegral, T) && __traits(isUnsigned, T)) +{ + static assert(count < 8 * T.sizeof); + static if (count == 0) + return cast(T)value; + return cast(T)((value << count) | (value >> (T.sizeof * 8 - count))); +} + +T ror(uint count, T)(const T value) pure + if (__traits(isIntegral, T) && __traits(isUnsigned, T)) +{ + static assert(count < 8 * T.sizeof); + static if (count == 0) + return cast(T)value; + return cast(T)((value >> count) | (value << (T.sizeof * 8 - count))); +} + /+ ubyte log2(ubyte val) { @@ -140,7 +178,7 @@ ubyte log2(ubyte val) } ubyte log2(T)(T val) - if (isSomeInt!T && T.sizeof > 1) + if (is_some_int!T && T.sizeof > 1) { if (T.sizeof > 4 && val >> 32) { @@ -172,7 +210,7 @@ ubyte log2(T)(T val) +/ ubyte log2(T)(T x) - if (isIntegral!T) + if (is_integral!T) { ubyte result = 0; static if (T.sizeof > 4) @@ -201,7 +239,7 @@ ubyte log2(T)(T x) } ubyte clz(bool nonZero = false, T)(T x) - if (isIntegral!T) + if (is_integral!T) { static if (nonZero) debug assert(x != 0); @@ -270,7 +308,7 @@ ubyte clz(T : bool)(T x) => x ? 7 : 8; ubyte ctz(bool nonZero = false, T)(T x) - if (isIntegral!T) + if (is_integral!T) { static if (nonZero) debug assert(x != 0); @@ -378,7 +416,7 @@ ubyte ctz(T : bool)(T x) => x ? 0 : 8; ubyte popcnt(T)(T x) - if (isIntegral!T) + if (is_integral!T) { if (__ctfe || !IS_LDC_OR_GDC) { @@ -410,9 +448,9 @@ ubyte popcnt(T)(T x) ubyte popcnt(T : bool)(T x) => x ? 1 : 0; -ubyte byteReverse(ubyte v) +ubyte byte_reverse(ubyte v) => v; -ushort byteReverse(ushort v) +ushort byte_reverse(ushort v) { if (__ctfe || !IS_LDC_OR_GDC) return cast(ushort)((v << 8) | (v >> 8)); @@ -421,7 +459,7 @@ ushort byteReverse(ushort v) else assert(false, "Unreachable"); } -uint byteReverse(uint v) +uint byte_reverse(uint v) { if (__ctfe || !IS_LDC_OR_GDC) return cast(uint)((v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24)); @@ -430,7 +468,7 @@ uint byteReverse(uint v) else assert(false, "Unreachable"); } -ulong byteReverse(ulong v) +ulong byte_reverse(ulong v) { if (__ctfe || !IS_LDC_OR_GDC) return cast(ulong)((v << 56) | ((v & 0xFF00) << 40) | ((v & 0xFF0000) << 24) | ((v & 0xFF000000) << 8) | ((v >> 8) & 0xFF000000) | ((v >> 24) & 0xFF0000) | ((v >> 40) & 0xFF00) | (v >> 56)); @@ -439,17 +477,17 @@ ulong byteReverse(ulong v) else assert(false, "Unreachable"); } -pragma(inline, true) T byteReverse(T)(T val) - if (!isIntegral!T) +pragma(inline, true) T byte_reverse(T)(T val) + if (!is_integral!T) { - import urt.meta : intForWidth; - alias U = intForWidth!(T.sizeof*8); - U r = byteReverse(*cast(U*)&val); + import urt.meta : IntForWidth; + alias U = IntForWidth!(T.sizeof*8); + U r = byte_reverse(*cast(U*)&val); return *cast(T*)&r; } -T bitReverse(T)(T x) - if (isSomeInt!T) +T bit_reverse(T)(T x) + if (is_some_int!T) { if (__ctfe || !IS_LDC) { @@ -458,7 +496,7 @@ T bitReverse(T)(T x) // TODO: these may be inferior on platforms where mul is slow... static if (size_t.sizeof == 8) { - // return cast(ubyte)((b*0x0202020202ULL & 0x010884422010ULL) % 1023; // only 3 ops, but uses div! +// return cast(ubyte)((b*0x0202020202ULL & 0x010884422010ULL) % 1023; // only 3 ops, but uses div! return cast(ubyte)(cast(ulong)(x*0x80200802UL & 0x0884422110)*0x0101010101 >> 32); } else @@ -476,7 +514,84 @@ T bitReverse(T)(T x) static if (T.sizeof == 1) return x; else - return byteReverse(x); + return byte_reverse(x); + } + } + else static if (false) // TODO: DMD, x86, 4 or 8 bytes... + { + static if (T.sizeof == 4) + { + asm pure nothrow @nogc { naked; } + + version (D_InlineAsm_X86_64) + { + version (Win64) + asm pure nothrow @nogc { mov EAX, ECX; } + else + asm pure nothrow @nogc { mov EAX, EDI; } + } + + asm pure nothrow @nogc + { + mov EDX, EAX; + shr EAX, 1; + and EDX, 0x5555_5555; + and EAX, 0x5555_5555; + shl EDX, 1; + or EAX, EDX; + mov EDX, EAX; + shr EAX, 2; + and EDX, 0x3333_3333; + and EAX, 0x3333_3333; + shl EDX, 2; + or EAX, EDX; + mov EDX, EAX; + shr EAX, 4; + and EDX, 0x0f0f_0f0f; + and EAX, 0x0f0f_0f0f; + shl EDX, 4; + or EAX, EDX; + bswap EAX; + ret; + } + } + else + { + asm pure nothrow @nogc { naked; } + + version (Win64) + asm pure nothrow @nogc { mov RAX, RCX; } + else + asm pure nothrow @nogc { mov RAX, RDI; } + + asm pure nothrow @nogc + { + mov RDX, RAX; + shr RAX, 1; + mov RCX, 0x5555_5555_5555_5555L; + and RDX, RCX; + and RAX, RCX; + shl RDX, 1; + or RAX, RDX; + + mov RDX, RAX; + shr RAX, 2; + mov RCX, 0x3333_3333_3333_3333L; + and RDX, RCX; + and RAX, RCX; + shl RDX, 2; + or RAX, RDX; + + mov RDX, RAX; + shr RAX, 4; + mov RCX, 0x0f0f_0f0f_0f0f_0f0fL; + and RDX, RCX; + and RAX, RCX; + shl RDX, 4; + or RAX, RDX; + bswap RAX; + ret; + } } } else @@ -531,39 +646,39 @@ unittest assert(x.swap(y) == 10); assert(x.swap(30) == 20); - static assert(isPowerOf2(0) == true); - static assert(isPowerOf2(1) == true); - static assert(isPowerOf2(2) == true); - static assert(isPowerOf2(3) == false); - static assert(isPowerOf2(4) == true); - static assert(isPowerOf2(5) == false); - static assert(isPowerOf2(ulong(uint.max) + 1) == true); - static assert(isPowerOf2(ulong.max) == false); - assert(isPowerOf2(0) == true); - assert(isPowerOf2(1) == true); - assert(isPowerOf2(2) == true); - assert(isPowerOf2(3) == false); - assert(isPowerOf2(4) == true); - assert(isPowerOf2(5) == false); - assert(isPowerOf2(ulong(uint.max) + 1) == true); - assert(isPowerOf2(ulong.max) == false); - - static assert(nextPowerOf2(0) == 0); - static assert(nextPowerOf2(1) == 1); - static assert(nextPowerOf2(2) == 2); - static assert(nextPowerOf2(3) == 4); - static assert(nextPowerOf2(4) == 4); - static assert(nextPowerOf2(5) == 8); - static assert(nextPowerOf2(uint.max) == 0); - static assert(nextPowerOf2(ulong(uint.max)) == ulong(uint.max) + 1); - assert(nextPowerOf2(0) == 0); - assert(nextPowerOf2(1) == 1); - assert(nextPowerOf2(2) == 2); - assert(nextPowerOf2(3) == 4); - assert(nextPowerOf2(4) == 4); - assert(nextPowerOf2(5) == 8); - assert(nextPowerOf2(uint.max) == 0); - assert(nextPowerOf2(ulong(uint.max)) == ulong(uint.max) + 1); + static assert(is_power_of_2(0) == true); + static assert(is_power_of_2(1) == true); + static assert(is_power_of_2(2) == true); + static assert(is_power_of_2(3) == false); + static assert(is_power_of_2(4) == true); + static assert(is_power_of_2(5) == false); + static assert(is_power_of_2(ulong(uint.max) + 1) == true); + static assert(is_power_of_2(ulong.max) == false); + assert(is_power_of_2(0) == true); + assert(is_power_of_2(1) == true); + assert(is_power_of_2(2) == true); + assert(is_power_of_2(3) == false); + assert(is_power_of_2(4) == true); + assert(is_power_of_2(5) == false); + assert(is_power_of_2(ulong(uint.max) + 1) == true); + assert(is_power_of_2(ulong.max) == false); + + static assert(next_power_of_2(0) == 0); + static assert(next_power_of_2(1) == 1); + static assert(next_power_of_2(2) == 2); + static assert(next_power_of_2(3) == 4); + static assert(next_power_of_2(4) == 4); + static assert(next_power_of_2(5) == 8); + static assert(next_power_of_2(uint.max) == 0); + static assert(next_power_of_2(ulong(uint.max)) == ulong(uint.max) + 1); + assert(next_power_of_2(0) == 0); + assert(next_power_of_2(1) == 1); + assert(next_power_of_2(2) == 2); + assert(next_power_of_2(3) == 4); + assert(next_power_of_2(4) == 4); + assert(next_power_of_2(5) == 8); + assert(next_power_of_2(uint.max) == 0); + assert(next_power_of_2(ulong(uint.max)) == ulong(uint.max) + 1); static assert(log2(ubyte(0)) == 0); static assert(log2(ubyte(1)) == 0); @@ -677,50 +792,50 @@ unittest assert(popcnt(true) == 1); assert(popcnt('D') == 2); // 0x44 - static assert(bitReverse(ubyte(0)) == 0); - static assert(bitReverse(ubyte(1)) == 128); - static assert(bitReverse(ubyte(2)) == 64); - static assert(bitReverse(ubyte(3)) == 192); - static assert(bitReverse(ubyte(4)) == 32); - static assert(bitReverse(ubyte(5)) == 160); - static assert(bitReverse(ubyte(6)) == 96); - static assert(bitReverse(ubyte(7)) == 224); - static assert(bitReverse(ubyte(8)) == 16); - static assert(bitReverse(ubyte(255)) == 255); - static assert(bitReverse(ushort(0b1101100010000000)) == 0b0000000100011011); - static assert(bitReverse(uint(0x73810000)) == 0x000081CE); - static assert(bitReverse(ulong(0x7381000000000000)) == 0x00000000000081CE); - assert(bitReverse(ubyte(0)) == 0); - assert(bitReverse(ubyte(1)) == 128); - assert(bitReverse(ubyte(2)) == 64); - assert(bitReverse(ubyte(3)) == 192); - assert(bitReverse(ubyte(4)) == 32); - assert(bitReverse(ubyte(5)) == 160); - assert(bitReverse(ubyte(6)) == 96); - assert(bitReverse(ubyte(7)) == 224); - assert(bitReverse(ubyte(8)) == 16); - assert(bitReverse(ubyte(255)) == 255); - assert(bitReverse(ushort(0b1101100010000000)) == 0b0000000100011011); - assert(bitReverse(uint(0x73810000)) == 0x000081CE); - assert(bitReverse(ulong(0x7381000000000000)) == 0x00000000000081CE); - - static assert(byteReverse(0x12) == 0x12); - static assert(byteReverse(0x1234) == 0x3412); - static assert(byteReverse(0x12345678) == 0x78563412); - static assert(byteReverse(0x123456789ABCDEF0) == 0xF0DEBC9A78563412); - static assert(byteReverse(true) == true); - static assert(byteReverse(char(0x12)) == char(0x12)); - static assert(byteReverse(wchar(0x1234)) == wchar(0x3412)); - static assert(byteReverse(cast(dchar)0x12345678) == cast(dchar)0x78563412); - assert(byteReverse(0x12) == 0x12); - assert(byteReverse(0x1234) == 0x3412); - assert(byteReverse(0x12345678) == 0x78563412); - assert(byteReverse(0x123456789ABCDEF0) == 0xF0DEBC9A78563412); - assert(byteReverse(true) == true); - assert(byteReverse(char(0x12)) == char(0x12)); - assert(byteReverse(wchar(0x1234)) == wchar(0x3412)); - assert(byteReverse(cast(dchar)0x12345678) == cast(dchar)0x78563412); + static assert(bit_reverse(ubyte(0)) == 0); + static assert(bit_reverse(ubyte(1)) == 128); + static assert(bit_reverse(ubyte(2)) == 64); + static assert(bit_reverse(ubyte(3)) == 192); + static assert(bit_reverse(ubyte(4)) == 32); + static assert(bit_reverse(ubyte(5)) == 160); + static assert(bit_reverse(ubyte(6)) == 96); + static assert(bit_reverse(ubyte(7)) == 224); + static assert(bit_reverse(ubyte(8)) == 16); + static assert(bit_reverse(ubyte(255)) == 255); + static assert(bit_reverse(ushort(0b1101100010000000)) == 0b0000000100011011); + static assert(bit_reverse(uint(0x73810000)) == 0x000081CE); + static assert(bit_reverse(ulong(0x7381000000000000)) == 0x00000000000081CE); + assert(bit_reverse(ubyte(0)) == 0); + assert(bit_reverse(ubyte(1)) == 128); + assert(bit_reverse(ubyte(2)) == 64); + assert(bit_reverse(ubyte(3)) == 192); + assert(bit_reverse(ubyte(4)) == 32); + assert(bit_reverse(ubyte(5)) == 160); + assert(bit_reverse(ubyte(6)) == 96); + assert(bit_reverse(ubyte(7)) == 224); + assert(bit_reverse(ubyte(8)) == 16); + assert(bit_reverse(ubyte(255)) == 255); + assert(bit_reverse(ushort(0b1101100010000000)) == 0b0000000100011011); + assert(bit_reverse(uint(0x73810000)) == 0x000081CE); + assert(bit_reverse(ulong(0x7381000000000000)) == 0x00000000000081CE); + + static assert(byte_reverse(0x12) == 0x12); + static assert(byte_reverse(0x1234) == 0x3412); + static assert(byte_reverse(0x12345678) == 0x78563412); + static assert(byte_reverse(0x123456789ABCDEF0) == 0xF0DEBC9A78563412); + static assert(byte_reverse(true) == true); + static assert(byte_reverse(char(0x12)) == char(0x12)); + static assert(byte_reverse(wchar(0x1234)) == wchar(0x3412)); + static assert(byte_reverse(cast(dchar)0x12345678) == cast(dchar)0x78563412); + assert(byte_reverse(0x12) == 0x12); + assert(byte_reverse(0x1234) == 0x3412); + assert(byte_reverse(0x12345678) == 0x78563412); + assert(byte_reverse(0x123456789ABCDEF0) == 0xF0DEBC9A78563412); + assert(byte_reverse(true) == true); + assert(byte_reverse(char(0x12)) == char(0x12)); + assert(byte_reverse(wchar(0x1234)) == wchar(0x3412)); + assert(byte_reverse(cast(dchar)0x12345678) == cast(dchar)0x78563412); float frev; *cast(uint*)&frev = 0x0000803F; - assert(byteReverse(1.0f) is frev); + assert(byte_reverse(1.0f) is frev); } diff --git a/src/urt/variant.d b/src/urt/variant.d index 9f5fe2c..95ce4af 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -1,22 +1,33 @@ module urt.variant; +import urt.algorithm : compare; import urt.array; +import urt.conv; import urt.kvp; import urt.lifetime; import urt.map; +import urt.mem.allocator; +import urt.meta.enuminfo : enum_info, VoidEnumInfo; import urt.si.quantity; -import urt.si.unit : ScaledUnit; +import urt.si.unit : ScaledUnit, Second, Nanosecond; +import urt.string; +import urt.time; import urt.traits; +import urt.util : swap; nothrow @nogc: enum ValidUserType(T) = (is(T == struct) || is(T == class)) && + is(Unqual!T == T) && !is(T == Variant) && !is(T == VariantKVP) && !is(T == Array!U, U) && !is(T : const(char)[]) && - !is(T == Quantity!(T, U), T, alias U); + !is(T == Quantity!(T, U), T, alias U) && + !is(T == Duration) && + !is(T == String) && + !is(T == MutableString!N, size_t N); alias VariantKVP = KVP!(const(char)[], Variant); @@ -31,14 +42,59 @@ nothrow @nogc: if (rh.type == Type.Map || rh.type == Type.Array) { Array!Variant arr = rh.nodeArray; - takeNodeArray(arr); + take_node_array(arr); + flags = rh.flags; + } + else if ((rh.flags & Flags.NeedDestruction) == 0) + { + __pack = rh.__pack; + } + else if (rh.isString) + { + *cast(String*)&value.s = *cast(String*)&rh.value.s; + count = rh.count; + flags = rh.flags; + } + else if (rh.isBuffer) + { + ptr = defaultAllocator.alloc(rh.count).ptr; + ptr[0 .. rh.count] = rh.ptr[0 .. rh.count]; + count = rh.count; flags = rh.flags; } else + assert(false, "What kind of thing is this?"); + } + + this(ref const Variant rh) inout + { + if (rh.type == Type.Map || rh.type == Type.Array) + { + auto arr = Array!Variant(Reserve, rh.nodeArray.length); + foreach (ref i; rh.nodeArray) + arr.pushBack(i); + flags = rh.flags; + } + else if ((rh.flags & Flags.NeedDestruction) == 0) { - assert((rh.flags & Flags.NeedDestruction) == 0); __pack = rh.__pack; } + else if (rh.isString) + { + *cast(String*)&value.s = *cast(String*)&rh.value.s; + count = rh.count; + flags = rh.flags; + } + else if (rh.isBuffer) + { + void* mem = defaultAllocator.alloc(rh.count).ptr; + mem[0 .. rh.count] = rh.ptr[0 .. rh.count]; + ptr = cast(inout(void)*)mem; + count = rh.count; + flags = rh.flags; + } + else + assert(false, "What kind of thing is this?"); } version (EnableMoveSemantics) { @@ -65,85 +121,150 @@ nothrow @nogc: } this(I)(I i) - if (is(I == byte) || is(I == short)) - { - flags = Flags.NumberInt; - value.l = i; - if (i >= 0) - flags |= Flags.UintFlag | Flags.Uint64Flag; - } - this(I)(I i) - if (is(I == ubyte) || is(I == ushort)) + if (is_some_int!I) { - flags = cast(Flags)(Flags.NumberUint | Flags.IntFlag); - value.ul = i; + static if (is_signed_int!I) + value.l = i; + else + value.ul = i; + + static if (is(I == ubyte) || is(I == ushort)) + flags = cast(Flags)(Flags.NumberUint | Flags.IntFlag | Flags.Int64Flag); + else static if (is(I == byte) || is(I == short) || is(I == int)) + { + flags = Flags.NumberInt; + if (i >= 0) + flags |= Flags.UintFlag | Flags.Uint64Flag; + } + else static if (is(I == uint)) + { + flags = Flags.NumberUint; + if (i <= int.max) + flags |= Flags.IntFlag; + } + else static if (is(I == long)) + { + flags = Flags.NumberInt64; + if (i >= 0) + { + flags |= Flags.Uint64Flag; + if (i <= int.max) + flags |= Flags.IntFlag | Flags.UintFlag; + else if (i <= uint.max) + flags |= Flags.UintFlag; + } + else if (i >= int.min) + flags |= Flags.IntFlag; + } + else static if (is(I == ulong)) + { + flags = Flags.NumberUint64; + if (i <= int.max) + flags |= Flags.IntFlag | Flags.UintFlag | Flags.Int64Flag; + else if (i <= uint.max) + flags |= Flags.UintFlag | Flags.Int64Flag; + else if (i <= long.max) + flags |= Flags.Int64Flag; + } } - this(int i) + this(F)(F f) + if (is_some_float!F) { - flags = Flags.NumberInt; - value.l = i; - if (i >= 0) - flags |= Flags.UintFlag | Flags.Uint64Flag; + import urt.math : float_is_integer; + + if (int sign = float_is_integer(f, value.ul)) + { + if (sign < 0) + { + flags = Flags.NumberInt64; + if (value.l >= int.min) + flags |= Flags.IntFlag; + } + else + { + flags = Flags.NumberUint64; + if (value.ul <= int.max) + flags |= Flags.IntFlag | Flags.UintFlag | Flags.Int64Flag; + else if (value.ul <= uint.max) + flags |= Flags.UintFlag | Flags.Int64Flag; + else if (value.ul <= long.max) + flags |= Flags.Int64Flag; + } + } + else + { + static if (is(F == float)) + flags = Flags.NumberFloat; + else + flags = Flags.NumberDouble; + value.d = f; + } } - this(uint i) + this(E)(const E e) + if (is(E == enum)) { - flags = Flags.NumberUint; - value.ul = i; - if (i <= int.max) - flags |= Flags.IntFlag; + static if (is(E T == enum)) + this(T(e), enum_info!E.make_void()); } - this(long i) + this(T)(T value, const(VoidEnumInfo)* e) { - flags = Flags.NumberInt64; - value.l = i; - if (i >= 0) + static assert(!is(T == enum), "T should be a numeric type"); + + this(value); + static if (size_t.sizeof == 8) { - flags |= Flags.Uint64Flag; - if (i <= int.max) - flags |= Flags.IntFlag | Flags.UintFlag; - else if (i <= uint.max) - flags |= Flags.UintFlag; + size_t ptr = cast(size_t)e; + assert((ptr >> 48) == 0, "Uh on! High ptr bits set... we must be in the distant future!"); + count = cast(uint)ptr; + alloc = cast(ushort)(ptr >> 32); } - else if (i >= int.min) - flags |= Flags.IntFlag; + else + count = cast(size_t)e; + flags |= Flags.Enum; } - this(ulong i) + this(U, ScaledUnit _U)(Quantity!(U, _U) q) { - flags = Flags.NumberUint64; - value.ul = i; - if (i <= int.max) - flags |= Flags.IntFlag | Flags.UintFlag | Flags.Int64Flag; - else if (i <= uint.max) - flags |= Flags.UintFlag | Flags.Int64Flag; - else if (i <= long.max) - flags |= Flags.Int64Flag; + this(q.value); + count = q.unit.pack; } - this(float f) + this(Duration dur) { - flags = Flags.NumberFloat; - value.d = f; + this(dur.as!"nsecs"); + count = Nanosecond.pack; } - this(double d) + + this(String s) { - flags = Flags.NumberDouble; - value.d = d; + if (s.empty) + { + flags = Flags.Null; + return; + } + flags = Flags.String; + value.s = s.ptr; + count = s.length; + if (s.has_rc()) + flags |= Flags.NeedDestruction; + s.ptr = null; } - this(U, ScaledUnit _U)(Quantity!(U, _U) q) + this(size_t N)(MutableString!N s) { - this(q.value); - flags |= Flags.IsQuantity; - count = q.unit.pack; + this(String(s.move)); } - this(const(char)[] s) // TODO: (S)(S s) -// if (is(S : const(char)[])) + this(const(char)[] s) { + if (s.length == 0) + { + flags = Flags.Null; + return; + } if (s.length < embed.length) { flags = Flags.ShortString; @@ -152,17 +273,43 @@ nothrow @nogc: return; } flags = Flags.String; - value.s = s.ptr; + flags |= Flags.NeedDestruction; + String t = s.makeString(defaultAllocator); + value.s = t.ptr.swap(null); count = cast(uint)s.length; } + this(T)(T[] buffer) + if (is(Unqual!T == void)) + { + if (buffer.length == 0) + { + flags = Flags.Null; + return; + } + if (buffer.length < embed.length) + { + flags = Flags.ShortBuffer; + embed[0 .. buffer.length] = cast(char[])buffer[]; + embed[$-1] = cast(ubyte)buffer.length; + return; + } + flags = Flags.Buffer; + flags |= Flags.NeedDestruction; + void[] mem = defaultAllocator.alloc(buffer.length); + mem[] = buffer[]; + ptr = mem.ptr; + count = cast(uint)buffer.length; + } + this(Variant[] a) { flags = Flags.Array; nodeArray = a[]; } + this(T)(T[] a) - if (!is(T == Variant) && !is(T == VariantKVP) && !is(T : dchar)) + if (!is(T == Variant) && !is(T == VariantKVP) && !is(T : dchar) && !is(Unqual!T == void)) { flags = Flags.Array; nodeArray.reserve(a.length); @@ -172,7 +319,7 @@ nothrow @nogc: this(Array!Variant a) { - takeNodeArray(a); + take_node_array(a); flags = Flags.Array; } @@ -203,25 +350,40 @@ nothrow @nogc: this(T)(auto ref T thing) if (ValidUserType!T) { - alias dummy = MakeTypeDetails!T; - - flags = Flags.User; - static if (is(T == class)) - { - count = UserTypeId!T; - value.p = cast(void*)&thing; - } - else static if (EmbedUserType!T) - { - flags |= Flags.Embedded; - alloc = UserTypeShortId!T; - emplace(cast(T*)embed.ptr, forward!thing); - } + static if (is(Unqual!T == MonoTime)) + this(cast(SysTime)thing); else { -// flags |= Flags.NeedDestruction; // if T has a destructor... - count = UserTypeId!T; - assert(false, "TODO: alloc for the object..."); + alias dummy = MakeTypeDetails!T; + + flags = Flags.User; + static if (is(T == class)) + { + count = UserTypeId!T; + alloc = type_detail_index!T(); + ptr = cast(void*)thing; + } + else static if (EmbedUserType!T) + { + alloc = UserTypeId!T; + flags |= Flags.Embedded; + + if (TypeDetailsFor!T.destroy) // TODO: we should check the same condition that determined if there is a destruct function... + flags |= Flags.NeedDestruction; + + emplace(cast(T*)embed.ptr, forward!thing); + } + else + { + count = UserTypeId!T; + alloc = type_detail_index!T(); + + if (TypeDetailsFor!T.destroy) // TODO: we should check the same condition that determined if there is a destruct function... + flags |= Flags.NeedDestruction; + + ptr = defaultAllocator().alloc(T.sizeof, T.alignof).ptr; + emplace(cast(T*)ptr, forward!thing); + } } } @@ -230,12 +392,35 @@ nothrow @nogc: destroy!false(); } + void opAssign(ref Variant value) + { + if (&this is &value) + return; // TODO: should this be an assert instead of a graceful handler? + destroy!false(); + new(this) Variant(value); + } + + void opAssign(ref const Variant value) + { + if (&this is &value) + return; // TODO: should this be an assert instead of a graceful handler? + destroy!false(); + new(this) Variant(value); + } + + version (EnableMoveSemantics) { + void opAssign(Variant value) + { + destroy!false(); + new(this) Variant(__rvalue(value)); // TODO: value.move + } + } + // TODO: since this is a catch-all, the error messages will be a bit shit // maybe we can find a way to constrain it to valid inputs? void opAssign(T)(auto ref T value) { - destroy!false(); - emplace(&this, forward!value); + this = Variant(value); } // TODO: do we want Variant to support +=, ~=, etc...? @@ -266,6 +451,228 @@ nothrow @nogc: return nodeArray.pushBack(); } + bool opEquals(ref const Variant rhs) const pure + { + return opCmp(rhs) == 0; + } + bool opEquals(T)(auto ref const T rhs) const + if (!is(T == Variant)) + { + // TODO: handle short-cut array/map comparisons? + static if (is(T == typeof(null))) + return type == Type.Null || ((type == Type.String || type == Type.Array || type == Type.Map) && empty()); + else static if (is(T == bool)) + { + if (!isBool) + return false; // do non-zero numbers evaluate true? what about non-zero strings? etc... + return asBool == rhs; + } + else static if (is_some_int!T || is_some_float!T) + { + if (!isNumber || isQuantity) + return false; + static if (is_some_int!T) if (!canFitInt!T) + return false; + return as!T == rhs; + } + else static if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) + { + if (!isNumber) + return false; + return asQuantity!double() == rhs; + } + else static if (is(T E == enum)) + { + // TODO: should we also do string key comparisons? + return opEquals(cast(E)rhs); + } + else static if (is(T : const(char)[]) || is(T : const String) || is(T : const MutableString!N, size_t N)) + return isString && asString() == rhs[]; + else static if (ValidUserType!T) + return isUser!T && asUser!T == rhs; + else + static assert(false, "TODO: variant comparison with '", T.stringof, "' not supported"); + } + + int opCmp(ref const Variant rhs) const pure + { + const(Variant)* a, b; + bool invert = false; + if (this.type <= rhs.type) + a = &this, b = &rhs; + else + a = &rhs, b = &this, invert = true; + + int r = 0; + final switch (a.type) + { + case Type.Null: + if (b.type == Type.Null) + return 0; + else if ((b.type == Type.Buffer || b.type == Type.Array || b.type == Type.Map) && b.empty()) + return 0; + r = -1; // sort null before other things... + break; + + case Type.True: + case Type.False: + // if both sides are bool + if (b.type <= Type.False) + { + r = a.asBool - b.asBool; + break; + } + // TODO: maybe we don't want to accept bool/number comparison? + goto case; // we will compare bools with numbers... + + case Type.Number: + if (b.type <= Type.Number) + { + static double asDoubleWithBool(ref const Variant v) + => v.isBool() ? double(v.asBool()) : v.asDouble(); + + uint aunit = a.count; + uint bunit = b.count; + if (aunit || bunit) + { + // we can't compare different units + if ((aunit & 0xFFFFFF) != (bunit & 0xFFFFFF)) + { + r = (aunit & 0xFFFFFF) - (bunit & 0xFFFFFF); + break; + } + + // matching units, but we'll only do quantity comparison if there is some scaling + if ((aunit >> 24) != (bunit >> 24)) + { + VarQuantity aq = a.isNumber ? a.asQuantity!double() : VarQuantity(double(a.asBool())); + VarQuantity bq = b.isNumber ? b.asQuantity!double() : VarQuantity(double(b.asBool())); + r = aq.opCmp(bq); + break; + } + } + + if (a.flags & Flags.DoubleFlag || b.flags & Flags.DoubleFlag) + { + // float comparison + // TODO: determine if float/bool comparison seems right? is: -1 < false < 0.9 < true < 1.1? + double af = asDoubleWithBool(*a); + double bf = asDoubleWithBool(*b); + r = af < bf ? -1 : af > bf ? 1 : 0; + break; + } + + // TODO: this could be further optimised by comparing the value range flags... + if ((a.flags & (Flags.Int64Flag | Flags.IsBool)) == 0) + { + ulong aul = a.asUlong(); + if ((b.flags & (Flags.Int64Flag | Flags.IsBool)) == 0) + { + ulong bul = b.asUlong(); + r = aul < bul ? -1 : aul > bul ? 1 : 0; + } + else + r = 1; // a is in ulong range, rhs is not; a is larger... + break; + } + if ((b.flags & (Flags.Int64Flag | Flags.IsBool)) == 0) + { + r = -1; // b is in ulong range, lhs is not; b is larger... + break; + } + + long al = a.isBool() ? a.asBool() : a.asLong(); + long bl = b.isBool() ? b.asBool() : b.asLong(); + r = al < bl ? -1 : al > bl ? 1 : 0; + } + else + r = -1; // sort numbers before other things... + break; + + case Type.Buffer: + if (b.type != Type.Buffer) + { + r = -1; + break; + } + r = compare(cast(const(char)[])a.asBuffer(), cast(const(char)[])b.asBuffer()); + break; + + case Type.Array: + if (b.type != Type.Array) + { + r = -1; + break; + } + r = compare(a.asArray()[], b.asArray()[]); + break; + + case Type.Map: + if (b.type != Type.Map) + { + r = -1; + break; + } + assert(false, "TODO"); + break; + + case Type.User: + uint at = a.userType; + uint bt = b.userType; + if (at != bt) + { + r = at < bt ? -1 : at > bt ? 1 : 0; + break; + } + alias PureHack = ref TypeDetails function(uint index) pure nothrow @nogc; + if (flags & Flags.Embedded) + { + ref const TypeDetails td = (cast(PureHack)&find_type_details)(alloc); + r = td.cmp(a.embed.ptr, b.embed.ptr, 0); + } + else + { + ref const TypeDetails td = (cast(PureHack)&get_type_details)(alloc); + r = td.cmp(a.ptr, b.ptr, 0); + } + break; + } + return invert ? -r : r; + } + int opCmp(T)(auto ref const T rhs) const + if (!is(T == Variant)) + { + // TODO: handle short-cut string, array, map comparisons + static if (is(T == typeof(null))) + return type == Type.Null || ((type == Type.String || type == Type.Array || type == Type.Map) && empty()) ? 0 : 1; + else static if (is(T : const(char)[])) + return isString() ? compare(asString(), rhs) : (type < Type.String ? -1 : 1); + static if (ValidUserType!T) + return compare(asUser!T, rhs); + else + return opCmp(Variant(rhs)); + } + + bool opBinary(string op)(ref const Variant rhs) const pure + if (op == "is") + { + // compare that Variant's are identical, not just equivalent! + assert(false, "TODO"); + } + bool opBinary(string op, T)(auto ref const T rhs) const + if (op == "is") + { + // TODO: handle short-cut array/map comparisons? + static if (is(T == typeof(null))) + return type == Type.Null || ((type == Type.String || type == Type.Array || type == Type.Map) && empty()); + else static if (is(T : const(char)[])) + return isString && asString().ptr is rhs.ptr && length() == rhs.length; + else static if (ValidUserType!T) + return asUser!T is rhs; + else + return opBinary!"is"(Variant(rhs)); + } + bool isNull() const pure => flags == Flags.Null; bool isFalse() const pure @@ -289,55 +696,113 @@ nothrow @nogc: bool isDouble() const pure => (flags & Flags.DoubleFlag) != 0; bool isQuantity() const pure - => (flags & Flags.IsQuantity) != 0; + => isNumber && count != 0 && !is_enum; + bool isDuration() const pure + => isNumber && (count & 0xFFFFFF) == Second.pack; + bool is_enum() const pure + => (flags & Flags.Enum) != 0; + bool isBuffer() const pure + => type == Type.Buffer; bool isString() const pure => (flags & Flags.IsString) != 0; bool isArray() const pure => flags == Flags.Array; bool isObject() const pure => flags == Flags.Map; + bool isUserType() const pure + => (flags & Flags.TypeMask) == Type.User; bool isUser(T)() const pure - if (ValidUserType!T) + if (ValidUserType!(Unqual!T)) { + alias U = Unqual!T; if ((flags & Flags.TypeMask) != Type.User) return false; - static if (EmbedUserType!T) - return alloc == UserTypeShortId!T; + static if (EmbedUserType!U) + return alloc == UserTypeId!U; else - return count == UserTypeId!T; + { + if (count == UserTypeId!U) + return true; + static if (is(T == class)) + { + immutable(TypeDetails)* td = &get_type_details(alloc); + while (td.super_type_id) + { + if (td.super_type_id == UserTypeId!U) + return true; + td = &find_type_details(td.super_type_id); + } + } + return false; + } } + bool canFitInt(I)() const pure + if (is_some_int!I) + { + if (!isNumber || isFloat) + return false; + static if (is(I == ulong)) + return isUlong; + else static if (is(I == long)) + return isLong; + else static if (is(I == uint)) + return isUint; + else static if (is(I == int)) + return isInt; + else static if (is_signed_int!I) + { + if (!isInt) + return false; + int i = asInt(); + return i >= I.min && i <= I.max; + } + else + return isUlong && asUlong <= I.max; + } bool asBool() const pure @property { + if (isNull) + return false; assert(isBool()); return flags == Flags.True; } int asInt() const pure @property { - assert(isInt()); + if (isNull) + return 0; + assert(isInt(), "Value out of range for int"); return cast(int)value.l; } uint asUint() const pure @property { - assert(isUint()); + if (isNull) + return 0; + assert(isUint(), "Value out of range for uint"); return cast(uint)value.ul; } long asLong() const pure @property { - assert(isLong()); + if (isNull) + return 0; + assert(isLong(), "Value out of range for long"); return value.l; } ulong asUlong() const pure @property { - assert(isUlong()); + if (isNull) + return 0; + assert(isUlong(), "Value out of range for ulong"); return value.ul; } double asDouble() const pure @property { - assert(isNumber()); + if (isNull) + return 0; + assert(isNumber); if ((flags & Flags.DoubleFlag) != 0) return value.d; if ((flags & Flags.UintFlag) != 0) @@ -349,33 +814,63 @@ nothrow @nogc: return cast(double)cast(long)value.ul; } + float asFloat() const pure @property + { + if (isNull) + return 0; + assert(isNumber); + if ((flags & Flags.DoubleFlag) != 0) + return value.d; + if ((flags & Flags.UintFlag) != 0) + return cast(float)cast(uint)value.ul; + if ((flags & Flags.IntFlag) != 0) + return cast(float)cast(int)cast(long)value.ul; + if ((flags & Flags.Uint64Flag) != 0) + return cast(float)value.ul; + return cast(float)cast(long)value.ul; + } + Quantity!T asQuantity(T = double)() const pure @property + if (is_some_float!T || is_some_int!T) { - assert(isNumber()); + if (isNull) + return Quantity!T(0); + assert(isNumber); + Quantity!T r; + r.value = as!T; + r.unit.pack = count; + return r; + } - Quantity!double r; - static if (is(T == double)) - r.value = asDouble(); -// else static if (is(T == float)) -// r.value = asFloat(); - else static if (is(T == int)) - r.value = asInt(); - else static if (is(T == uint)) - r.value = asUint(); - else static if (is(T == long)) - r.value = asLong(); - else static if (is(T == ulong)) - r.value = asUlong(); + Duration asDuration() const pure @property + { + assert(isDuration); + alias Nanoseconds = Quantity!(long, Nanosecond); + Nanoseconds ns; + if (size_t.sizeof < 8 && isFloat) // TODO: better way to detect if double is NOT supported in hardware? + ns = cast(Nanoseconds)asQuantity!float(); + else if (isDouble) + ns = cast(Nanoseconds)asQuantity!double(); else - assert(false, "Unsupported quantity type!"); - if (isQuantity()) - r.unit.pack = count; - return r; + ns = asQuantity!long(); + return ns.value.dur!"nsecs"; + } + + const(void)[] asBuffer() const pure + { + if (isNull) + return null; + assert(isBuffer); + if (flags & Flags.Embedded) + return embed[0 .. embed[$-1]]; + return ptr[0 .. count]; } const(char)[] asString() const pure { - assert(isString()); + if (isNull) + return null; + assert(isString); if (flags & Flags.Embedded) return embed[0 .. embed[$-1]]; return value.s[0 .. count]; @@ -397,44 +892,194 @@ nothrow @nogc: } ref inout(T) asUser(T)() inout pure - if (ValidUserType!T && UserTypeReturnByRef!T) + if (ValidUserType!(Unqual!T) && UserTypeReturnByRef!T) { - if (!isUser!T) - assert(false, "Variant is not a " ~ T.stringof); - static assert(!is(T == class), "Should be impossible?"); - static if (EmbedUserType!T) + alias U = Unqual!T; + if (!isUser!U) + assert(false, "Variant is not a " ~ U.stringof); + static assert(!is(U == class), "Should be impossible?"); + static if (EmbedUserType!U) return *cast(inout(T)*)embed.ptr; else return *cast(inout(T)*)ptr; } - inout(T) asUser(T)() inout pure - if (ValidUserType!T && !UserTypeReturnByRef!T) + inout(T) asUser(T)() inout + if (ValidUserType!(Unqual!T) && !UserTypeReturnByRef!T) { - if (!isUser!T) - assert(false, "Variant is not a " ~ T.stringof); - static if (is(T == class)) - return cast(inout(T))ptr; - else static if (EmbedUserType!T) - static assert(false, "TODO: memcpy to a stack local and return that..."); + alias U = Unqual!T; + + // some hacks for builtin time types... + static if (is(U == MonoTime)) + return cast(MonoTime)asUser!SysTime; + else static if (is(T == SysTime)) + { + static assert (EmbedUserType!SysTime && EmbedUserType!DateTime); + if (isUser!SysTime) + return *cast(inout(SysTime)*)embed.ptr; + if (isUser!DateTime) + return getSysTime(*cast(DateTime*)embed.ptr); + assert(false, "Variant is not a timestamp"); + } + else static if (is(T == DateTime)) + { + static assert (EmbedUserType!SysTime && EmbedUserType!DateTime); + if (isUser!DateTime) + return *cast(inout(DateTime)*)embed.ptr; + if (isUser!SysTime) + return getDateTime(*cast(SysTime*)embed.ptr); + assert(false, "Variant is not a timestamp"); + } else - static assert(false, "Should be impossible?"); + { + if (!isUser!U) + assert(false, "Variant is not a " ~ U.stringof); + static if (is(U == class)) + return cast(inout(T))ptr; + else static if (EmbedUserType!U) + { + // TODO: it would be nice to support trivial types and just cast/copy rather than copy_emplace(...) + + // make a copy on the stack and return by value + U r = void; + TypeDetailsFor!U.copy_emplace(cast(U*)embed.ptr, &r, false); + return r; + } + else + static assert(false, "Should be impossible?"); + } + } + + template as(T) + { + static if (!is(T == Unqual!T)) + alias as = as!(Unqual!T); + else static if (is_boolean!T) + alias as = asBool; + else static if (is(T == long)) + alias as = asLong; + else static if (is(T == int)) + alias as = asInt; + else static if (is(T == ulong)) + alias as = asUlong; + else static if (is(T == uint)) + alias as = asUint; + else static if (is_some_int!T) + { + T as() const pure + { + static if (is_signed_int!T) + { + int i = asInt(); + assert(i >= T.min && i <= T.max, "Value out of range for " ~ T.stringof); + } + else + { + uint i = asUint(); + assert(i <= T.max, "Value out of range for " ~ T.stringof); + } + return cast(T)i; + } + } + else static if (is(T == float)) + alias as = asFloat; + else static if (is(T == double)) + alias as = asDouble; + else static if (is(T == real)) + real as() const pure => asDouble; + else static if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) + alias as = asQuantity!U; + else static if (is(T == Duration)) + alias as = asDuration; + else static if (is(const(char)[] : T)) + alias as = asString; + else static if (is(T == String)) + { + String as() const pure + { + if (isNull) + return String(); + assert(isString); + if (flags & Flags.Embedded) + return embed[0 .. embed[$-1]].makeString(defaultAllocator); + return *cast(String*)&value.s; + } + } + else static if (ValidUserType!(Unqual!T)) + alias as = asUser!T; + else + static assert(false, "TODO!"); + } + + Array!Variant take_array() + { + if (flags == Flags.Null) + return Array!Variant(); + assert(isArray()); + Array!Variant r; + swap(r, nodeArray); + flags = Flags.Null; + return r; + } + + String take_string() + { + if (flags == Flags.Null) + return String(); + assert(isString); + if (flags & Flags.Embedded) + return embed[0 .. embed[$-1]].makeString(defaultAllocator); + String s; + s.ptr = value.s; + value.s = null; + flags = Flags.Null; + return s; + } + + ScaledUnit get_unit() const pure + { + assert(isQuantity()); + return *cast(ScaledUnit*)&count; + } + + const(VoidEnumInfo)* get_enum_info() const pure + { + assert(is_enum); + static if (size_t.sizeof == 8) + return cast(VoidEnumInfo*)(count | (size_t(alloc) << 32)); + else + return cast(VoidEnumInfo*)count; } size_t length() const pure { if (flags == Flags.Null) return 0; - else if (isString()) + else if (isBuffer()) return (flags & Flags.Embedded) ? embed[$-1] : count; else if (isArray()) return count; else - assert(false); + assert(false, "Variant does not have `length`"); } bool empty() const pure => isObject() ? count == 0 : length() == 0; + Variant* insert(const(char)[] key, Variant value) + { + if (flags == Flags.Null) + flags = Flags.Map; + else + { + assert(isObject()); + if (getMember(key)) + return null; + } + nodeArray.emplaceBack(key); + move(value, nodeArray.pushBack()); + return &nodeArray.back(); + } + inout(Variant)* getMember(const(char)[] member) inout pure { assert(isObject()); @@ -446,6 +1091,12 @@ nothrow @nogc: return null; } + void set_unit(ScaledUnit unit) + { + assert(isNumber()); + count = unit.pack; + } + // TODO: this seems to interfere with UFCS a lot... // ref inout(Variant) opDispatch(string member)() inout pure // { @@ -479,73 +1130,160 @@ nothrow @nogc: { final switch (type) { - case Variant.Type.Null: - if (!buffer.ptr) - return 4; - if (buffer.length < 4) - return -1; - buffer[0 .. 4] = "null"; - return 4; - - case Variant.Type.False: - if (!buffer.ptr) - return 5; - if (buffer.length < 5) - return -1; - buffer[0 .. 5] = "false"; - return 5; - - case Variant.Type.True: + case Variant.Type.Null: // assume type == 0 + case Variant.Type.True: // assume type == 1 + case Variant.Type.False: // assume type == 2 + __gshared immutable char** values = [ "null", "true", "false" ]; + size_t len = 4 + (type >> 1); if (!buffer.ptr) - return 4; - if (buffer.length < 4) + return len; + if (buffer.length < len) return -1; - buffer[0 .. 4] = "true"; - return 4; + buffer[0 .. len] = values[type][0 .. len]; + return len; case Variant.Type.Number: - import urt.conv; - if (isQuantity()) - assert(false, "TODO: implement quantity formatting for JSON"); + return asQuantity().toString(buffer, format, formatArgs); if (isDouble()) - return asDouble().formatFloat(buffer); + return asDouble().format_float(buffer); // TODO: parse args? - //format + assert(!format, "TODO"); if (flags & Flags.Uint64Flag) - return asUlong().formatUint(buffer); - return asLong().formatInt(buffer); + return asUlong().format_uint(buffer); + return asLong().format_int(buffer); - case Variant.Type.String: - const char[] s = asString(); - if (buffer.ptr) + case Variant.Type.Buffer: + if (isString) + { + const char[] s = asString(); + if (buffer.ptr) + { + // TODO: should we write out quotes? + // a string of a number won't be distinguishable from a number without quotes... + if (buffer.length < s.length) + return -1; + buffer[0 .. s.length] = s[]; + } + return s.length; + } + else { - // TODO: should we write out quotes? - // a string of a number won't be distinguishable from a number without quotes... - if (buffer.length < s.length) - return -1; - buffer[0 .. s.length] = s[]; + import urt.string.format : formatValue; + return formatValue(asBuffer(), buffer, format, formatArgs); } - return s.length; case Variant.Type.Map: case Variant.Type.Array: import urt.format.json; // should we just format this like JSON or something? - return writeJson(this, buffer); + return write_json(this, buffer); case Variant.Type.User: if (flags & Flags.Embedded) - return findTypeDetails(alloc).stringify(embed.ptr, buffer); + return find_type_details(alloc).stringify(cast(void*)embed.ptr, buffer, true, format, formatArgs); + else + return g_type_details[alloc].stringify(cast(void*)ptr, buffer, true, format, formatArgs); + } + } + + ptrdiff_t fromString(const(char)[] s) + { + import urt.string.ascii : is_numeric; + + if (s.empty || s == "null") + { + this = null; + return s.length; + } + if (s == "true") + { + this = true; + return 4; + } + if (s == "false") + { + this = false; + return 5; + } + + if (s[0] == '"') + { + for (size_t i = 1; i < s.length; ++i) + { + if (s[i] == '"') + { + assert(i == s.length - 1, "String must end with a quote"); + this = s[1 .. i]; + return i + 1; + } + } + assert(false, "String has no closing quote"); + } + + if (s[0].is_numeric || ((s[0] == '+' || s[0] == '-') && s.length > 1 && s[1].is_numeric)) + { + size_t taken; + ScaledUnit unit; + int e; + long i = s.parse_int_with_exponent(e, &taken, 10); + if (taken < s.length) + { + size_t t2 = unit.fromString(s[taken .. $]); + if (t2 > 0) + taken += t2; + } + if (taken == s.length) + { + if (e != 0) + this = i * 10.0^^e; + else + this = i; + count = unit.pack; + return taken; + } + } + + align(64) void[256] buffer = void; + this = null; // clear the object since we'll probably use the embed buffer... + foreach (ushort i; 0 .. g_num_type_details) + { + ref immutable TypeDetails td = get_type_details(i); + debug assert(td.alignment <= 64 && td.size <= buffer.sizeof, "Buffer is too small for user type!"); + ptrdiff_t taken = td.stringify(td.embedded ? embed.ptr : buffer.ptr, cast(char[])s, false, null, null); + if (taken > 0) + { + flags = Flags.User; + if (td.destroy) + flags |= Flags.NeedDestruction; + if (td.embedded) + { + flags |= Flags.Embedded; + alloc = cast(ushort)td.type_id; + } else - return findTypeDetails(count).stringify(ptr, buffer); + { + void* object = defaultAllocator().alloc(td.size, td.alignment).ptr; + td.copy_emplace(buffer.ptr, object, true); + if (td.destroy) + td.destroy(buffer.ptr); + ptr = object; + count = td.type_id; + alloc = i; + } + return taken; + } } + + // what is this? + assert(false, "Can't parse variant from string"); } + package: union Value { @@ -586,9 +1324,22 @@ package: Type type() const pure => cast(Type)(flags & Flags.TypeMask); + uint userType() const pure + { + if (flags & Flags.Embedded) + return alloc; // short id + return count; // long id + } + inout(void)* user_ptr() inout pure + { + if (flags & Flags.Embedded) + return embed.ptr; + return ptr; + } + ref inout(Array!Variant) nodeArray() @property inout pure => *cast(inout(Array!Variant)*)&value.n; - void takeNodeArray(ref Array!Variant arr) + void take_node_array(ref Array!Variant arr) { value.n = arr[].ptr; count = cast(uint)arr.length; @@ -598,33 +1349,41 @@ package: void destroy(bool reset = true)() { if (flags & Flags.NeedDestruction) - doDestroy(); + do_destroy(); static if (reset) __pack[] = 0; } - private void doDestroy() + private void do_destroy() { Type t = type(); - if ((t == Type.Map || t == Type.Array) && value.n) + if (isBuffer) + { + if (isString) + *cast(String*)&value.s = null; + else + defaultAllocator().free(ptr[0..count]); + } + else if ((t == Type.Map || t == Type.Array) && value.n) nodeArray.destroy!false(); else if (t == Type.User) { - if (flags & Flags.Embedded) - findTypeDetails(alloc).destroy(embed.ptr); - else - findTypeDetails(count).destroy(ptr); + ref const TypeDetails td = (flags & Flags.Embedded) ? find_type_details(alloc) : g_type_details[alloc]; + if (td.destroy) + td.destroy(user_ptr); + if (!(flags & Flags.Embedded)) + defaultAllocator().free(ptr[0..td.size]); } } enum Type : ushort { Null = 0, - False = 1, - True = 2, + True = 1, + False = 2, Number = 3, - String = 4, + Buffer = 4, Array = 5, Map = 6, User = 7 @@ -641,8 +1400,10 @@ package: NumberUint64 = cast(Flags)Type.Number | Flags.IsNumber | Flags.Uint64Flag, NumberFloat = cast(Flags)Type.Number | Flags.IsNumber | Flags.FloatFlag | Flags.DoubleFlag, NumberDouble = cast(Flags)Type.Number | Flags.IsNumber | Flags.DoubleFlag, - String = cast(Flags)Type.String | Flags.IsString, - ShortString = cast(Flags)Type.String | Flags.IsString | Flags.Embedded, + Buffer = cast(Flags)Type.Buffer, + ShortBuffer = cast(Flags)Type.Buffer | Flags.Embedded, + String = cast(Flags)Type.Buffer | Flags.IsString, + ShortString = cast(Flags)Type.Buffer | Flags.IsString | Flags.Embedded, Array = cast(Flags)Type.Array | Flags.NeedDestruction, Map = cast(Flags)Type.Map | Flags.NeedDestruction, User = cast(Flags)Type.User, @@ -656,7 +1417,7 @@ package: Uint64Flag = 1 << (TypeBits + 6), FloatFlag = 1 << (TypeBits + 7), DoubleFlag = 1 << (TypeBits + 8), - IsQuantity = 1 << (TypeBits + 9), + Enum = 1 << (TypeBits + 9), Embedded = 1 << (TypeBits + 10), NeedDestruction = 1 << (TypeBits + 11), // CopyFlag = 1 << (TypeBits + 12), // maybe we want to know if a thing is a copy, or a reference to an external one? @@ -691,15 +1452,22 @@ unittest private: -import urt.hash : fnv1aHash; +import urt.hash : fnv1a; +import urt.string.format : formatValue, FormatArg; static assert(Variant.sizeof == 16); static assert(Variant.Type.max <= Variant.Flags.TypeMask); -enum uint UserTypeId(T) = fnv1aHash(cast(const(ubyte)[])T.stringof); // maybe this isn't a good enough hash? -enum uint UserTypeShortId(T) = cast(ushort)UserTypeId!T ^ (UserTypeId!T >> 16); +template UserTypeId(T) +{ + enum uint Hash = fnv1a(cast(const(ubyte)[])T.stringof); // maybe this isn't a good enough hash? + static if (!EmbedUserType!T) + enum uint UserTypeId = Hash; + else + enum ushort UserTypeId = cast(ushort)Hash ^ (Hash >> 16); +} enum bool EmbedUserType(T) = is(T == struct) && T.sizeof <= Variant.embed.sizeof - 2 && T.alignof <= Variant.alignof; -enum bool UserTypeReturnByRef(T) = is(T == struct); +enum bool UserTypeReturnByRef(T) = is(T == struct) && !EmbedUserType!T; ptrdiff_t newline(char[] buffer, ref ptrdiff_t offset, int level) { @@ -713,46 +1481,175 @@ ptrdiff_t newline(char[] buffer, ref ptrdiff_t offset, int level) template MakeTypeDetails(T) { + static assert(is(Unqual!T == T), "Only instantiate for mutable types"); + +// pragma(msg, "Add user type: ", T.stringof, EmbedUserType!T ? " - embedded" : ""); + // this is a hack which populates an array of user type details when the program starts // TODO: we can probably NOT do this for class types, and just use RTTI instead... shared static this() { - assert(numTypeDetails < typeDetails.length, "Too many user types!"); - - TypeDetails* ty = &typeDetails[numTypeDetails++]; - static if (EmbedUserType!T) - ty.typeId = UserTypeShortId!T; - else - ty.typeId = UserTypeId!T; - // TODO: I'd like to not generate a destroy function if the data is POD - ty.destroy = (void* val) { - static if (!is(T == class)) - destroy!false(*cast(T*)val); - }; - ty.stringify = (const void* val, char[] buffer) { - import urt.string.format : toString; - return toString(*cast(T*)val, buffer); - }; + assert(g_num_type_details < g_type_details.length, "Too many user types!"); + g_type_details[g_num_type_details++] = TypeDetailsFor!T; } alias MakeTypeDetails = void; } +ushort type_detail_index(T)() pure + if (ValidUserType!T) +{ + ushort count = (cast(ushort function() pure nothrow @nogc)&num_type_details)(); + foreach (ushort i; 0 .. count) + if (get_type_details(i).type_id == UserTypeId!T) + return i; + assert(false, "Why wasn't the type registered?"); +} + struct TypeDetails { - uint typeId; + uint type_id; + uint super_type_id; + ushort size; + ubyte alignment; + bool embedded; + void function(void* src, void* dst, bool move) nothrow @nogc copy_emplace; void function(void* val) nothrow @nogc destroy; - ptrdiff_t function(const void* val, char[] buffer) nothrow @nogc stringify; + ptrdiff_t function(void* val, char[] buffer, bool do_format, const(char)[] format_spec, const(FormatArg)[] format_args) nothrow @nogc stringify; + int function(const void* a, const void* b, int type) pure nothrow @nogc cmp; } -TypeDetails[8] typeDetails; -size_t numTypeDetails = 0; +__gshared TypeDetails[16] g_type_details; +__gshared ushort g_num_type_details = 0; -ref TypeDetails findTypeDetails(uint typeId) +typeof(g_type_details)* type_details() => &g_type_details; +ushort num_type_details() => g_num_type_details; + +ref immutable(TypeDetails) find_type_details(uint type_id) pure { - foreach (i, ref td; typeDetails[0 .. numTypeDetails]) + auto tds = (cast(immutable(typeof(g_type_details)*) function() pure nothrow @nogc)&type_details)(); + ushort count = (cast(ushort function() pure nothrow @nogc)&num_type_details)(); + foreach (i, ref td; (*tds)[0 .. count]) { - if (td.typeId == typeId) + if (td.type_id == type_id) return td; } assert(false, "TypeDetails not found!"); } +ref immutable(TypeDetails) get_type_details(uint index) pure +{ + auto tds = (cast(immutable(typeof(g_type_details)*) function() pure nothrow @nogc)&type_details)(); + debug assert(index < g_num_type_details); + return (*tds)[index]; +} + +public template TypeDetailsFor(T) + if (is(Unqual!T == T) && (is(T == struct) || is(T == class))) +{ + static if (is(T == class) && is(T S == super)) + { + alias Super = Unqual!S; + static if (!is(Super == Object)) + { + alias dummy = MakeTypeDetails!Super; + enum SuperTypeId = UserTypeId!Super; + } + else + enum ushort SuperTypeId = 0; + } + else + enum ushort SuperTypeId = 0; + + static if (!is(T == class)) + { + static void move_emplace_impl(void* src, void* dst, bool move) nothrow @nogc + { + if (move) + moveEmplace(*cast(T*)src, *cast(T*)dst); + else + { + static if (__traits(compiles, { *cast(T*)dst = *cast(const T*)src; })) + *cast(T*)dst = *cast(const T*)src; + else + assert(false, "Can't copy " ~ T.stringof); + } + } + enum move_emplace = &move_emplace_impl; + } + else + enum move_emplace = null; + + static if (!is_trivial!T && is(typeof(destroy!(false, T)))) + { + static void destroy_impl(void* val) nothrow @nogc + { + destroy!false(*cast(T*)val); + } + enum destroy_fun = &destroy_impl; + } + else + enum destroy_fun = null; + + static ptrdiff_t stringify(void* val, char[] buffer, bool do_format, const(char)[] format_spec, const(FormatArg)[] format_args) nothrow @nogc + { + if (do_format) + { + static if (__traits(compiles, { formatValue(*cast(const T*)val, buffer, format_spec, format_args); })) + return formatValue(*cast(const T*)val, buffer, format_spec, format_args); + else + return -1; + } + else + { + static if (is(typeof(parse!T))) + return buffer.parse!T(*cast(T*)val); + else + return -1; + } + } + + int compare(const void* pa, const void* pb, int type) pure nothrow @nogc + { + ref const T a = *cast(const T*)pa; + ref const T b = *cast(const T*)pb; + switch (type) + { + case 0: + static if (is(T == class) || is(T == U*, U) || is(T == V[], V)) + { + if (pa is pb) + return 0; + } + static if (__traits(compiles, { a.opCmp(b); })) + return a.opCmp(b); + else static if (__traits(compiles, { b.opCmp(a); })) + return -b.opCmp(a); + else static if (is(T == class)) + { + ptrdiff_t r = cast(ptrdiff_t)pa - cast(ptrdiff_t)pb; + return r < 0 ? -1 : r > 0 ? 1 : 0; + } + else + assert(false, "No comparison!"); // TODO: hash or stringify the values and order that way? + case 1: + static if (is(T == class) || is(T == U*, U) || is(T == V[], V)) + { + if (pa is pb) + return 1; + } + static if (__traits(compiles, { a.opEquals(b); })) + return a.opEquals(b); + else static if (__traits(compiles, { b.opEquals(a); })) + return b.opEquals(a); + else static if (!is(T == class) && !is(T == U*, U) && !is(T == V[], V)) + return a == b ? 1 : 0; + else + return 0; + case 2: + return pa is pb ? 1 : 0; + default: + assert(false); + } + } + + enum TypeDetailsFor = TypeDetails(UserTypeId!T, SuperTypeId, T.sizeof, T.alignof, EmbedUserType!T, move_emplace, destroy_fun, &stringify, &compare); +} diff --git a/src/urt/zip.d b/src/urt/zip.d index 4bf2521..040a056 100644 --- a/src/urt/zip.d +++ b/src/urt/zip.d @@ -4,164 +4,158 @@ import urt.crc; import urt.endian; import urt.hash; import urt.mem.allocator; +import urt.result; -alias zlib_crc = calculateCRC!(Algorithm.CRC32_ISO_HDLC); +alias zlib_crc = calculate_crc!(Algorithm.crc32_iso_hdlc); nothrow @nogc: // this is a port of tinflate (tiny inflate) -enum error_code : int +enum GzipFlag : ubyte { - OK = 0, /**< Success */ - DATA_ERROR = -3, /**< Input error */ - BUF_ERROR = -5 /**< Not enough room for output */ + ftext = 1, + fhcrc = 2, + fextra = 4, + fname = 8, + fcomment = 16 } -enum gzip_flag : ubyte -{ - FTEXT = 1, - FHCRC = 2, - FEXTRA = 4, - FNAME = 8, - FCOMMENT = 16 -} - -error_code zlib_uncompress(const(void)[] source, void[] dest, out size_t destLen) +Result zlib_uncompress(const(void)[] source, void[] dest, out size_t destLen) { const ubyte* src = cast(const ubyte*)source.ptr; uint sourceLen = cast(uint)source.length; if (sourceLen < 6) - return error_code.DATA_ERROR; + return InternalResult.data_error; ubyte cmf = src[0]; ubyte flg = src[1]; // check checksum if ((256 * cmf + flg) % 31) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check method is deflate if ((cmf & 0x0F) != 8) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check window size is valid if ((cmf >> 4) > 7) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check there is no preset dictionary if (flg & 0x20) - return error_code.DATA_ERROR; + return InternalResult.data_error; - if (uncompress((src + 2)[0 .. sourceLen - 6], dest, destLen) != error_code.OK) - return error_code.DATA_ERROR; + if (!uncompress((src + 2)[0 .. sourceLen - 6], dest, destLen)) + return InternalResult.data_error; if (adler32(dest[0 .. destLen]) != loadBigEndian!uint(cast(uint*)&src[sourceLen - 4])) - return error_code.DATA_ERROR; + return InternalResult.data_error; - return error_code.OK; + return InternalResult.success; } -error_code gzip_uncompressed_length(const(void)[] source, out size_t destLen) +Result gzip_uncompressed_length(const(void)[] source, out size_t destLen) { const ubyte* src = cast(const ubyte*)source.ptr; uint sourceLen = cast(uint)source.length; if (sourceLen < 18) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check id bytes if (src[0] != 0x1F || src[1] != 0x8B) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check method is deflate if (src[2] != 8) - return error_code.DATA_ERROR; + return InternalResult.data_error; ubyte flg = src[3]; // check that reserved bits are zero if (flg & 0xE0) - return error_code.DATA_ERROR; + return InternalResult.data_error; // get decompressed length destLen = loadLittleEndian!uint(cast(uint*)(src + sourceLen - 4)); - return error_code.OK; + return InternalResult.success; } -error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) +Result gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) { const ubyte* src = cast(const ubyte*)source.ptr; uint sourceLen = cast(uint)source.length; if (sourceLen < 18) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check id bytes if (src[0] != 0x1F || src[1] != 0x8B) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check method is deflate if (src[2] != 8) - return error_code.DATA_ERROR; + return InternalResult.data_error; ubyte flg = src[3]; // check that reserved bits are zero if (flg & 0xE0) - return error_code.DATA_ERROR; + return InternalResult.data_error; // skip base header of 10 bytes const(ubyte)* start = src + 10; // skip extra data if present - if (flg & gzip_flag.FEXTRA) + if (flg & GzipFlag.fextra) { uint xlen = loadLittleEndian!ushort(cast(ushort*)start); if (xlen > sourceLen - 12) - return error_code.DATA_ERROR; + return InternalResult.data_error; start += xlen + 2; } // skip file name if present - if (flg & gzip_flag.FNAME) + if (flg & GzipFlag.fname) { do { if (start - src >= sourceLen) - return error_code.DATA_ERROR; + return InternalResult.data_error; } while (*start++); } // skip file comment if present - if (flg & gzip_flag.FCOMMENT) + if (flg & GzipFlag.fcomment) { do { if (start - src >= sourceLen) - return error_code.DATA_ERROR; + return InternalResult.data_error; } while (*start++); } // check header crc if present - if (flg & gzip_flag.FHCRC) + if (flg & GzipFlag.fhcrc) { uint hcrc; if (start - src > sourceLen - 2) - return error_code.DATA_ERROR; + return InternalResult.data_error; hcrc = loadLittleEndian!ushort(cast(ushort*)start); if (hcrc != (zlib_crc(src[0 .. start - src]) & 0x0000FFFF)) - return error_code.DATA_ERROR; + return InternalResult.data_error; start += 2; } @@ -169,25 +163,25 @@ error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen // get decompressed length uint dlen = loadLittleEndian!uint(cast(uint*)(src + sourceLen - 4)); if (dlen > dest.length) - return error_code.BUF_ERROR; + return InternalResult.buffer_too_small; if ((src + sourceLen) - start < 8) - return error_code.DATA_ERROR; + return InternalResult.data_error; - if (uncompress(start[0 .. (src + sourceLen) - start - 8], dest, destLen) != error_code.OK) - return error_code.DATA_ERROR; + if (!uncompress(start[0 .. (src + sourceLen) - start - 8], dest, destLen)) + return InternalResult.data_error; if (destLen != dlen) - return error_code.DATA_ERROR; + return InternalResult.data_error; // check CRC32 checksum if (zlib_crc(dest[0..dlen]) != loadLittleEndian!uint(cast(uint*)(src + sourceLen - 8))) - return error_code.DATA_ERROR; + return InternalResult.data_error; - return error_code.OK; + return InternalResult.success; } -error_code uncompress(const(void)[] source, void[] dest, out size_t destLen) +Result uncompress(const(void)[] source, void[] dest, out size_t destLen) { data d; @@ -211,7 +205,7 @@ error_code uncompress(const(void)[] source, void[] dest, out size_t destLen) uint btype = getbits(&d, 2); // Decompress block - error_code res; + Result res; switch (btype) { case 0: @@ -227,20 +221,20 @@ error_code uncompress(const(void)[] source, void[] dest, out size_t destLen) res = inflate_dynamic_block(&d); break; default: - res = error_code.DATA_ERROR; + res = InternalResult.data_error; break; } - if (res != error_code.OK) + if (!res) return res; } while (!bfinal); if (d.overflow) - return error_code.DATA_ERROR; + return InternalResult.data_error; destLen = d.dest - d.dest_start; - return error_code.OK; + return InternalResult.success; } @@ -606,7 +600,7 @@ void build_fixed_trees(tree *lt, tree *dt) } /* Given an array of code lengths, build a tree */ -error_code build_tree(tree *t, const(ubyte)* lengths, ushort num) +Result build_tree(tree *t, const(ubyte)* lengths, ushort num) { ushort[16] offs = void; uint available; @@ -638,7 +632,7 @@ error_code build_tree(tree *t, const(ubyte)* lengths, ushort num) /* Check length contains no more codes than available */ if (used > available) - return error_code.DATA_ERROR; + return InternalResult.data_error; available = 2 * (available - used); offs[i] = num_codes; @@ -650,7 +644,7 @@ error_code build_tree(tree *t, const(ubyte)* lengths, ushort num) * code that it has length 1 */ if ((num_codes > 1 && available > 0) || (num_codes == 1 && t.counts[1] != 1)) - return error_code.DATA_ERROR; + return InternalResult.data_error; /* Fill in symbols sorted by code */ for (i = 0; i < num; ++i) @@ -669,7 +663,7 @@ error_code build_tree(tree *t, const(ubyte)* lengths, ushort num) t.symbols[1] = cast(ushort)(t.max_sym + 1); } - return error_code.OK; + return InternalResult.success; } /* -- Decode functions -- */ @@ -749,7 +743,7 @@ int decode_symbol(data *d, const tree *t) return t.symbols[base + offs]; } -error_code decode_trees(data *d, tree *lt, tree *dt) +Result decode_trees(data *d, tree *lt, tree *dt) { /* Special ordering of code length codes */ __gshared immutable ubyte[19] clcidx = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; @@ -776,7 +770,7 @@ error_code decode_trees(data *d, tree *lt, tree *dt) * See also: https://github.com/madler/zlib/issues/82 */ if (hlit > 286 || hdist > 30) - return error_code.DATA_ERROR; + return InternalResult.data_error; for (i = 0; i < 19; ++i) lengths[i] = 0; @@ -791,13 +785,13 @@ error_code decode_trees(data *d, tree *lt, tree *dt) } /* Build code length tree (in literal/length tree to save space) */ - error_code res = build_tree(lt, lengths.ptr, 19); - if (res != error_code.OK) + Result res = build_tree(lt, lengths.ptr, 19); + if (res != InternalResult.success) return res; /* Check code length tree is not empty */ if (lt.max_sym == -1) - return error_code.DATA_ERROR; + return InternalResult.data_error; /* Decode code lengths for the dynamic trees */ for (num = 0; num < hlit + hdist; ) @@ -805,14 +799,14 @@ error_code decode_trees(data *d, tree *lt, tree *dt) int sym = decode_symbol(d, lt); if (sym > lt.max_sym) - return error_code.DATA_ERROR; + return InternalResult.data_error; switch (sym) { case 16: /* Copy previous code length 3-6 times (read 2 bits) */ if (num == 0) { - return error_code.DATA_ERROR; + return InternalResult.data_error; } sym = lengths[num - 1]; length = getbits_base(d, 2, 3); @@ -834,7 +828,7 @@ error_code decode_trees(data *d, tree *lt, tree *dt) } if (length > hlit + hdist - num) - return error_code.DATA_ERROR; + return InternalResult.data_error; while (length--) lengths[num++] = cast(ubyte)sym; @@ -842,26 +836,26 @@ error_code decode_trees(data *d, tree *lt, tree *dt) /* Check EOB symbol is present */ if (lengths[256] == 0) - return error_code.DATA_ERROR; + return InternalResult.data_error; /* Build dynamic trees */ res = build_tree(lt, lengths.ptr, hlit); - if (res != error_code.OK) + if (res != InternalResult.success) return res; res = build_tree(dt, lengths.ptr + hlit, hdist); - if (res != error_code.OK) + if (res != InternalResult.success) return res; - return error_code.OK; + return InternalResult.success; } /* -- Block inflate functions -- */ /* Given a stream and two trees, inflate a block of data */ -error_code inflate_block_data(data *d, tree *lt, tree *dt) +Result inflate_block_data(data *d, tree *lt, tree *dt) { /* Extra bits and base tables for length codes */ __gshared immutable ubyte[30] length_bits = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 127 ]; @@ -877,12 +871,12 @@ error_code inflate_block_data(data *d, tree *lt, tree *dt) /* Check for overflow in bit reader */ if (d.overflow) - return error_code.DATA_ERROR; + return InternalResult.data_error; if (sym < 256) { if (d.dest == d.dest_end) - return error_code.BUF_ERROR; + return InternalResult.buffer_too_small; *d.dest++ = cast(ubyte)sym; } else @@ -892,11 +886,11 @@ error_code inflate_block_data(data *d, tree *lt, tree *dt) /* Check for end of block */ if (sym == 256) - return error_code.OK; + return InternalResult.success; /* Check sym is within range and distance tree is not empty */ if (sym > lt.max_sym || sym - 257 > 28 || dt.max_sym == -1) - return error_code.DATA_ERROR; + return InternalResult.data_error; sym -= 257; @@ -907,16 +901,16 @@ error_code inflate_block_data(data *d, tree *lt, tree *dt) /* Check dist is within range */ if (dist > dt.max_sym || dist > 29) - return error_code.DATA_ERROR; + return InternalResult.data_error; /* Possibly get more bits from distance code */ offs = getbits_base(d, dist_bits[dist], dist_base[dist]); if (offs > d.dest - d.dest_start) - return error_code.DATA_ERROR; + return InternalResult.data_error; if (d.dest_end - d.dest < length) - return error_code.BUF_ERROR; + return InternalResult.buffer_too_small; /* Copy match */ for (i = 0; i < length; ++i) @@ -928,10 +922,10 @@ error_code inflate_block_data(data *d, tree *lt, tree *dt) } /* Inflate an uncompressed block of data */ -error_code inflate_uncompressed_block(data *d) +Result inflate_uncompressed_block(data *d) { if (d.source_end - d.source < 4) - return error_code.DATA_ERROR; + return InternalResult.data_error; /* Get length */ uint length = loadLittleEndian!ushort(cast(ushort*)d.source); @@ -941,15 +935,15 @@ error_code inflate_uncompressed_block(data *d) /* Check length */ if (length != (~invlength & 0x0000FFFF)) - return error_code.DATA_ERROR; + return InternalResult.data_error; d.source += 4; if (d.source_end - d.source < length) - return error_code.DATA_ERROR; + return InternalResult.data_error; if (d.dest_end - d.dest < length) - return error_code.BUF_ERROR; + return InternalResult.buffer_too_small; /* Copy block */ while (length--) @@ -959,19 +953,19 @@ error_code inflate_uncompressed_block(data *d) d.tag = 0; d.bitcount = 0; - return error_code.OK; + return InternalResult.success; } -error_code inflate_fixed_block(data *d) +Result inflate_fixed_block(data *d) { build_fixed_trees(&d.ltree, &d.dtree); return inflate_block_data(d, &d.ltree, &d.dtree); } -error_code inflate_dynamic_block(data *d) +Result inflate_dynamic_block(data *d) { - error_code res = decode_trees(d, &d.ltree, &d.dtree); - if (res != error_code.OK) + Result res = decode_trees(d, &d.ltree, &d.dtree); + if (res != InternalResult.success) return res; return inflate_block_data(d, &d.ltree, &d.dtree); }