From 64bb371db70cc68257113e7d84922becd0d7df2e Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 17 Aug 2025 16:57:00 +1000 Subject: [PATCH 01/91] Add main function --- Makefile | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) 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 From 084fa962c9d62892b684ddfbdb9901f6b1c90913 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 17 Aug 2025 17:25:17 +1000 Subject: [PATCH 02/91] Migrate to lower_snake case... --- src/urt/algorithm.d | 10 +- src/urt/conv.d | 134 ++++---- src/urt/crc.d | 44 +-- src/urt/dbg.d | 2 +- src/urt/digest/md5.d | 28 +- src/urt/digest/sha.d | 34 +- src/urt/endian.d | 8 +- src/urt/file.d | 128 ++++---- src/urt/format/json.d | 44 +-- src/urt/hash.d | 8 +- src/urt/inet.d | 38 +-- src/urt/mem/alloc.d | 16 +- src/urt/mem/allocator.d | 6 +- src/urt/mem/scratchpad.d | 44 +-- src/urt/mem/string.d | 4 +- src/urt/mem/temp.d | 48 +-- src/urt/meta/package.d | 54 +-- src/urt/meta/tuple.d | 10 +- src/urt/package.d | 6 +- src/urt/rand.d | 2 +- src/urt/range/package.d | 14 +- src/urt/result.d | 55 ++-- src/urt/socket.d | 632 ++++++++++++++++++------------------ src/urt/string/ansi.d | 14 +- src/urt/string/format.d | 28 +- src/urt/string/string.d | 6 +- src/urt/string/tailstring.d | 4 +- src/urt/system.d | 28 +- src/urt/time.d | 22 +- src/urt/util.d | 8 +- src/urt/variant.d | 12 +- src/urt/zip.d | 156 +++++---- 32 files changed, 812 insertions(+), 835 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 14ecc1f..a742393 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -48,7 +48,7 @@ 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(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs) { T* p = arr.ptr; size_t low = 0; @@ -165,12 +165,12 @@ unittest 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); } diff --git a/src/urt/conv.d b/src/urt/conv.d index 91f39ab..ebce388 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -8,7 +8,7 @@ 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 +long parse_int(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure { size_t i = 0; bool neg = false; @@ -21,13 +21,13 @@ long parseInt(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure i++; } - ulong value = str.ptr[i .. str.length].parseUint(bytesTaken, base); + ulong value = str.ptr[i .. str.length].parse_uint(bytesTaken, base); if (bytesTaken && *bytesTaken != 0) *bytesTaken += i; return neg ? -cast(long)value : cast(long)value; } -long parseIntWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +long parse_int_with_decimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure { size_t i = 0; bool neg = false; @@ -40,13 +40,13 @@ long parseIntWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_t* i++; } - ulong value = str[i .. str.length].parseUintWithDecimal(fixedPointDivisor, bytesTaken, base); + ulong value = str[i .. str.length].parse_uint_with_decimal(fixedPointDivisor, bytesTaken, base); if (bytesTaken && *bytesTaken != 0) *bytesTaken += i; return neg ? -cast(long)value : cast(long)value; } -ulong parseUint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); @@ -69,7 +69,7 @@ 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; @@ -81,7 +81,7 @@ ulong parseUint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pur return value; } -ulong parseUintWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint_with_decimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); @@ -103,7 +103,7 @@ ulong parseUintWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_ goto parse_decimal; } - uint digit = getDigit(c); + uint digit = get_digit(c); if (digit >= base) break; value = value*base + digit; @@ -113,7 +113,7 @@ ulong parseUintWithDecimal(const(char)[] str, out ulong fixedPointDivisor, size_ parse_decimal: for (; s < e; ++s) { - uint digit = getDigit(*s); + uint digit = get_digit(*s); if (digit >= base) { // if i == 1, then the first char was a '.' and the next was not numeric, so this is not a number! @@ -132,11 +132,11 @@ done: return value; } -ulong parseUintWithBase(const(char)[] str, size_t* bytesTaken = null) pure +ulong parse_uint_with_base(const(char)[] str, size_t* bytesTaken = null) pure { const(char)* p = str.ptr; - int base = parseBasePrefix(str); - ulong i = parseUint(str, bytesTaken, base); + int base = parse_base_prefix(str); + ulong i = parse_uint(str, bytesTaken, base); if (bytesTaken && *bytesTaken != 0) *bytesTaken += str.ptr - p; return i; @@ -147,21 +147,21 @@ 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_uint_with_decimal("123.456", divisor, null, 10) == 123456 && divisor == 1000); + assert(parse_int_with_decimal("123.456.789", divisor, &taken, 16) == 1193046 && taken == 7 && divisor == 4096); + assert(parse_int("11001", null, 2) == 25); + assert(parse_int_with_decimal("-AbCdE.f", divisor, null, 16) == -11259375 && divisor == 16); + 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); } -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,25 +210,25 @@ 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 +double parse_float(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure { // TODO: E-notation... size_t taken = void; ulong div = void; - long value = str.parseIntWithDecimal(div, &taken, base); + long value = str.parse_int_with_decimal(div, &taken, base); if (bytesTaken) *bytesTaken = taken; if (taken == 0) @@ -245,15 +245,15 @@ 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("1101.11", &taken, 2), 13.75) && taken == 7); + assert(parse_float("xyz", &taken) is double.nan && taken == 0); } -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 showSign = false) pure { const bool neg = value < 0; showSign |= neg; @@ -263,7 +263,7 @@ ptrdiff_t formatInt(long value, char[] buffer, uint base = 10, uint width = 0, c ulong i = neg ? -value : value; - ptrdiff_t r = formatUint(i, buffer.ptr ? buffer.ptr[(width == 0 ? showSign : 0) .. buffer.length] : null, base, width, fill); + ptrdiff_t r = format_uint(i, buffer.ptr ? buffer.ptr[(width == 0 ? showSign : 0) .. buffer.length] : null, base, width, fill); if (r < 0 || !showSign) return r; @@ -309,7 +309,7 @@ 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; @@ -360,36 +360,36 @@ 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 @@ -424,9 +424,9 @@ template to(T) { long to(const(char)[] str) { - int base = parseBasePrefix(str); + int 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,9 +435,9 @@ template to(T) { double to(const(char)[] str) { - int base = parseBasePrefix(str); + int 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; } @@ -479,7 +479,7 @@ template to(T) private: -uint getDigit(char c) pure +uint get_digit(char c) pure { uint zeroBase = c - '0'; if (zeroBase < 10) @@ -493,7 +493,7 @@ uint getDigit(char c) pure return -1; } -int parseBasePrefix(ref const(char)[] str) pure +int parse_base_prefix(ref const(char)[] str) pure { int base = 10; if (str.length >= 2) @@ -510,7 +510,7 @@ int parseBasePrefix(ref const(char)[] str) pure /+ -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; @@ -530,7 +530,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..74f266b 100644 --- a/src/urt/crc.d +++ b/src/urt/crc.d @@ -1,6 +1,6 @@ module urt.crc; -import urt.meta : intForWidth; +import urt.meta : IntForWidth; import urt.traits : isUnsignedInt; nothrow @nogc: @@ -41,10 +41,10 @@ struct CRCParams } 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!(paramTable[algo].width); // compute a CRC with runtime parameters -T calculateCRC(T = uint)(const void[] data, ref const CRCParams params, ref const T[256] table) pure +T calculate_crc(T = uint)(const void[] data, ref const CRCParams params, ref const T[256] table) pure if (isUnsignedInt!T) { assert(params.width <= T.sizeof*8, "T is too small for the CRC width"); @@ -68,7 +68,7 @@ T calculateCRC(T = uint)(const void[] data, ref const CRCParams params, ref cons } // 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 +T calculate_crc(Algorithm algo, T = CRCType!algo)(const void[] data, T initial = cast(T)paramTable[algo].initial^paramTable[algo].finalXor) pure if (isUnsignedInt!T) { enum CRCParams params = paramTable[algo]; @@ -98,7 +98,7 @@ T calculateCRC(Algorithm algo, T = CRCType!algo)(const void[] data, T initial = } // 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 +T calculate_crc_2(Algorithm algo, T = IntForWidth!(paramTable[algo].width*2))(const void[] data, uint earlyOffset) pure if (isUnsignedInt!T) { enum CRCParams params = paramTable[algo]; @@ -151,7 +151,7 @@ done: } -T[256] generateCRCTable(T)(ref const CRCParams params) pure +T[256] generate_crc_table(T)(ref const CRCParams params) pure if (isUnsignedInt!T) { enum typeWidth = T.sizeof * 8; @@ -189,23 +189,23 @@ 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[]) == paramTable[Algorithm.CRC16_MODBUS].check); + assert(calculate_crc!(Algorithm.CRC16_EZSP)(checkData[]) == paramTable[Algorithm.CRC16_EZSP].check); + assert(calculate_crc!(Algorithm.CRC16_KERMIT)(checkData[]) == paramTable[Algorithm.CRC16_KERMIT].check); + assert(calculate_crc!(Algorithm.CRC16_USB)(checkData[]) == paramTable[Algorithm.CRC16_USB].check); + assert(calculate_crc!(Algorithm.CRC16_XMODEM)(checkData[]) == paramTable[Algorithm.CRC16_XMODEM].check); + assert(calculate_crc!(Algorithm.CRC16_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC16_ISO_HDLC].check); + assert(calculate_crc!(Algorithm.CRC16_DNP)(checkData[]) == paramTable[Algorithm.CRC16_DNP].check); + assert(calculate_crc!(Algorithm.CRC32_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC32_ISO_HDLC].check); + assert(calculate_crc!(Algorithm.CRC32_CASTAGNOLI)(checkData[]) == paramTable[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) == paramTable[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) == paramTable[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) == paramTable[Algorithm.CRC32_ISO_HDLC].check); } @@ -238,5 +238,5 @@ T reflect(T)(T value, ubyte bits) // this minimises the number of table instantiations template CRCTable(uint width, uint poly, bool reflect) { - __gshared immutable CRCTable = generateCRCTable!(intForWidth!width)(CRCParams(width, reflect, poly, 0, 0, 0)); + __gshared immutable CRCTable = generate_crc_table!(IntForWidth!width)(CRCParams(width, reflect, poly, 0, 0, 0)); } diff --git a/src/urt/dbg.d b/src/urt/dbg.d index 8cc13d1..189100d 100644 --- a/src/urt/dbg.d +++ b/src/urt/dbg.d @@ -61,7 +61,7 @@ else private: -package(urt) void setupAssertHandler() +package(urt) void setup_assert_handler() { import core.exception : assertHandler; assertHandler = &urt_assert; diff --git a/src/urt/digest/md5.d b/src/urt/digest/md5.d index ef75d98..ba1c4c7 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,13 +91,13 @@ unittest import urt.encoding; MD5Context ctx; - md5Init(ctx); - auto digest = md5Finalise(ctx); + md5_init(ctx); + auto digest = md5_finalise(ctx); assert(digest == Hex!"d41d8cd98f00b204e9800998ecf8427e"); - md5Init(ctx); - md5Update(ctx, "Hello, World!"); - digest = md5Finalise(ctx); + md5_init(ctx); + md5_update(ctx, "Hello, World!"); + digest = md5_finalise(ctx); assert(digest == Hex!"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..fc64067 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; @@ -115,23 +115,23 @@ unittest import urt.encoding; SHA1Context ctx; - shaInit(ctx); - auto digest = shaFinalise(ctx); + sha_init(ctx); + auto digest = sha_finalise(ctx); assert(digest == Hex!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - shaInit(ctx); - shaUpdate(ctx, "Hello, World!"); - digest = shaFinalise(ctx); + sha_init(ctx); + sha_update(ctx, "Hello, World!"); + digest = sha_finalise(ctx); assert(digest == Hex!"0a0a9f2a6772942557ab5355d76af442f8f65e01"); SHA256Context ctx2; - shaInit(ctx2); - auto digest2 = shaFinalise(ctx2); + sha_init(ctx2); + auto digest2 = sha_finalise(ctx2); assert(digest2 == Hex!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - shaInit(ctx2); - shaUpdate(ctx2, "Hello, World!"); - digest2 = shaFinalise(ctx2); + sha_init(ctx2); + sha_update(ctx2, "Hello, World!"); + digest2 = sha_finalise(ctx2); assert(digest2 == Hex!"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/endian.d b/src/urt/endian.d index e76f671..879b666 100644 --- a/src/urt/endian.d +++ b/src/urt/endian.d @@ -82,8 +82,8 @@ ulong endianToNative(T, bool little)(ref const ubyte[8] bytes) 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)) { - 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; } @@ -206,8 +206,8 @@ 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)) { - import urt.meta : intForWidth; - alias U = intForWidth!(T.sizeof*8); + import urt.meta : IntForWidth; + alias U = IntForWidth!(T.sizeof*8); U r = nativeToEndian!little(*cast(U*)&val); return *cast(T*)&r; } diff --git a/src/urt/file.d b/src/urt/file.d index e7ec4d3..8d3995c 100644 --- a/src/urt/file.d +++ b/src/urt/file.d @@ -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); 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,13 +772,13 @@ 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); if (resLen == 0) { DeleteFileW(tmp.ptr); - return InternalResult(InternalCode.BufferTooSmall); + return InternalResult.buffer_too_small; } buffer = buffer[0 .. resLen]; } @@ -788,25 +789,14 @@ Result get_temp_filename(ref char[] buffer, const(char)[] dstDir, const(char)[] 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..997b9e1 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -13,12 +13,12 @@ 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) { @@ -76,9 +76,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 +99,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 +107,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; @@ -138,14 +138,14 @@ ptrdiff_t writeJson(ref const Variant val, char[] buffer, bool dense = false, ui assert(false, "TODO: implement quantity formatting for JSON"); if (val.isDouble()) - return val.asDouble().formatFloat(buffer); + return val.asDouble().format_float(buffer); // TODO: parse args? //format if (val.isUlong()) - return val.asUlong().formatUint(buffer); - return val.asLong().formatInt(buffer); + return val.asUlong().format_uint(buffer); + return val.asLong().format_int(buffer); case Variant.Type.User: // in order to text-ify a user type, we probably need a hash table of text-ify functions, which @@ -179,7 +179,7 @@ unittest ] }`; - Variant root = parseJson(doc); + Variant root = parse_json(doc); // check the data was parsed correctly... assert(root["nothing"].isNull); @@ -197,18 +197,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 +220,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 +244,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(); @@ -307,7 +307,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 +315,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); @@ -331,7 +331,7 @@ Variant parseNode(ref const(char)[] text) bool neg = text[0] == '-'; size_t taken = void; ulong div = void; - ulong value = text[neg .. $].parseUintWithDecimal(div, &taken, 10); + ulong value = text[neg .. $].parse_uint_with_decimal(div, &taken, 10); assert(taken > 0); text = text[taken + neg .. $]; 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..1c2e019 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -97,11 +97,11 @@ 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 @@ -113,7 +113,7 @@ nothrow @nogc: { if (i > 0) tmp[offset++] = '.'; - offset += b[i].formatInt(tmp[offset..$]); + offset += b[i].format_int(tmp[offset..$]); } if (buffer.ptr && tmp.ptr == stackBuffer.ptr) @@ -129,22 +129,22 @@ nothrow @nogc: { ubyte[4] t; size_t offset = 0, len; - ulong i = s[offset..$].parseInt(&len); + ulong i = s[offset..$].parse_int(&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_int(&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_int(&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_int(&len); offset += len; if (len == 0 || i > 255) return -1; @@ -224,11 +224,11 @@ 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 @@ -275,7 +275,7 @@ nothrow @nogc: tmp[offset++] = ':'; continue; } - offset += s[i].formatInt(tmp[offset..$], 16); + offset += s[i].format_int(tmp[offset..$], 16); ++i; } @@ -360,7 +360,7 @@ nothrow @nogc: size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefixLen.formatInt(tmp[offset..$]); + offset += prefixLen.format_int(tmp[offset..$]); if (buffer.ptr && tmp.ptr == stackBuffer.ptr) { @@ -378,7 +378,7 @@ 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_int(&t); if (t == 0 || plen > 32) return -1; addr = a; @@ -449,7 +449,7 @@ nothrow @nogc: size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefixLen.formatInt(tmp[offset..$]); + offset += prefixLen.format_int(tmp[offset..$]); if (buffer.ptr && tmp.ptr == stackBuffer.ptr) { @@ -467,7 +467,7 @@ 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_int(&t); if (t == 0 || plen > 32) return -1; addr = a; @@ -547,7 +547,7 @@ nothrow @nogc: { offset = _a.ipv4.addr.toString(tmp, null, null); tmp[offset++] = ':'; - offset += _a.ipv4.port.formatInt(tmp[offset..$]); + offset += _a.ipv4.port.format_int(tmp[offset..$]); } else { @@ -555,7 +555,7 @@ 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_int(tmp[offset..$]); } if (buffer.ptr && tmp.ptr == stackBuffer.ptr) @@ -602,7 +602,7 @@ nothrow @nogc: if (s.length > taken && s[taken] == ':') { size_t t; - ulong p = s[++taken..$].parseInt(&t); + ulong p = s[++taken..$].parse_int(&t); if (t == 0 || p > 0xFFFF) return -1; taken += t; diff --git a/src/urt/mem/alloc.d b/src/urt/mem/alloc.d index 7408808..5ec148a 100644 --- a/src/urt/mem/alloc.d +++ b/src/urt/mem/alloc.d @@ -11,7 +11,7 @@ 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; alignment = max(alignment, (void*).sizeof); @@ -60,24 +60,24 @@ void[] realloc(void[] mem, size_t newSize) nothrow @nogc return core.stdc.stdlib.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; alignment = max(alignment, (void*).sizeof); assert(isPowerOf2(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) @@ -107,7 +107,7 @@ void free(void[] mem) nothrow @nogc core.stdc.stdlib.free(mem.ptr); } -void freeAligned(void[] mem) nothrow @nogc +void free_aligned(void[] mem) nothrow @nogc { version (Windows) { @@ -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..2d3f295 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: diff --git a/src/urt/mem/scratchpad.d b/src/urt/mem/scratchpad.d index 83a3abb..dcc681b 100644 --- a/src/urt/mem/scratchpad.d +++ b/src/urt/mem/scratchpad.d @@ -11,7 +11,7 @@ enum size_t NumScratchBuffers = 4; static assert(MaxScratchpadSize.isPowerOf2, "Scratchpad size must be a power of 2"); -void[] allocScratchpad(size_t size = MaxScratchpadSize) +void[] alloc_scratchpad(size_t size = MaxScratchpadSize) { if (size > MaxScratchpadSize) { @@ -23,13 +23,13 @@ void[] allocScratchpad(size_t size = MaxScratchpadSize) 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..b00618a 100644 --- a/src/urt/mem/string.d +++ b/src/urt/mem/string.d @@ -56,9 +56,9 @@ struct CacheString 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/mem/temp.d b/src/urt/mem/temp.d index 6f2d28b..aabe847 100644 --- a/src/urt/mem/temp.d +++ b/src/urt/mem/temp.d @@ -22,16 +22,16 @@ void[] talloc(size_t size) nothrow @nogc return null; } - 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 +50,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]; } @@ -77,23 +77,23 @@ char* tstringz(const(char)[] str) nothrow @nogc return null; size_t len = str.length; - if (allocOffset + len + 1 > TempMemSize) - allocOffset = 0; + if (alloc_offset + len + 1 > TempMemSize) + alloc_offset = 0; - char* r = cast(char*)tempMem.ptr + allocOffset; + char* r = cast(char*)tempMem.ptr + alloc_offset; r[0 .. len] = str[]; r[len] = '\0'; - allocOffset += len + 1; + alloc_offset += len + 1; return r; } char[] tstring(T)(auto ref T value) { import urt.string.format : toString; - ptrdiff_t r = toString(value, cast(char[])tempMem[allocOffset..$]); + ptrdiff_t r = toString(value, cast(char[])tempMem[alloc_offset..$]); if (r < 0) { - allocOffset = 0; + alloc_offset = 0; r = toString(value, cast(char[])tempMem[0..TempMemSize / 2]); if (r < 0) { @@ -101,34 +101,34 @@ char[] tstring(T)(auto ref T value) return null; } } - char[] result = cast(char[])tempMem[allocOffset .. allocOffset + r]; - allocOffset += r; + char[] result = cast(char[])tempMem[alloc_offset .. alloc_offset + r]; + alloc_offset += r; return result; } char[] tconcat(Args...)(ref Args args) { import urt.string.format : concat; - char[] r = concat(cast(char[])tempMem[allocOffset..$], args); + char[] r = concat(cast(char[])tempMem[alloc_offset..$], args); if (!r) { - allocOffset = 0; + alloc_offset = 0; r = concat(cast(char[])tempMem[0..TempMemSize / 2], args); } - allocOffset += r.length; + alloc_offset += 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 +170,4 @@ private: private: static void[TempMemSize] tempMem; -static ushort allocOffset = 0; +static ushort alloc_offset = 0; diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index faa2941..bec2f81 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -6,39 +6,47 @@ alias Alias(T) = T; alias AliasSeq(TList...) = TList; -template intForWidth(size_t width, bool signed = false) +template IntForWidth(size_t width, bool signed = false) { static if (width <= 8 && !signed) - alias intForWidth = ubyte; + alias IntForWidth = ubyte; else static if (width <= 8 && signed) - alias intForWidth = byte; + alias IntForWidth = byte; else static if (width <= 16 && !signed) - alias intForWidth = ushort; + alias IntForWidth = ushort; else static if (width <= 16 && signed) - alias intForWidth = short; + alias IntForWidth = short; else static if (width <= 32 && !signed) - alias intForWidth = uint; + alias IntForWidth = uint; else static if (width <= 32 && signed) - alias intForWidth = int; + alias IntForWidth = int; else static if (width <= 64 && !signed) - alias intForWidth = ulong; + alias IntForWidth = ulong; else static if (width <= 64 && signed) - alias intForWidth = long; + alias IntForWidth = long; } -template staticMap(alias fun, args...) +template STATIC_MAP(alias fun, args...) { - alias staticMap = AliasSeq!(); + alias STATIC_MAP = AliasSeq!(); static foreach (arg; args) - staticMap = AliasSeq!(staticMap, fun!arg); + STATIC_MAP = AliasSeq!(STATIC_MAP, fun!arg); } -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; @@ -63,8 +71,8 @@ template EnumKeys(E) private alias EnumStrings = __traits(allMembers, E); } -E enumFromString(E)(const(char)[] key) -if (is(E == enum)) +E enum_from_string(E)(const(char)[] key) + if (is(E == enum)) { foreach (i, k; EnumKeys!E) if (key[] == k[]) @@ -75,16 +83,16 @@ if (is(E == enum)) 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..6e57129 100644 --- a/src/urt/package.d +++ b/src/urt/package.d @@ -25,10 +25,10 @@ void crt_bootup() initClock(); import urt.rand; - initRand(); + init_rand(); - import urt.dbg : setupAssertHandler; - setupAssertHandler(); + import urt.dbg : setup_assert_handler; + setup_assert_handler(); import urt.string.string : initStringAllocators; initStringAllocators(); 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..4d69c46 100644 --- a/src/urt/range/package.d +++ b/src/urt/range/package.d @@ -16,15 +16,15 @@ template map(fun...) auto map(Range)(Range r) if (isInputRange!(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 @@ -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; @@ -190,7 +190,7 @@ template reduce(fun...) { import std.exception : enforce; alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); - alias Args = staticMap!(ReduceSeedType!E, binfuns); + alias Args = STATIC_MAP!(ReduceSeedType!E, binfuns); static if (isInputRange!R) { @@ -245,7 +245,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 diff --git a/src/urt/result.d b/src/urt/result.d index 564b15c..3769e8b 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; @@ -32,44 +24,35 @@ version (Windows) { import core.sys.windows.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) + + 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/socket.d b/src/urt/socket.d index 800f72d..e61e29c 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -57,119 +57,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, // IPv6 options - FirstIpv6Option, + first_ipv6_option, // ICMP options - FirstIcmpOption = FirstIpv6Option, + first_icmp_option = first_ipv6_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, + oob = 1 << 0, + peek = 1 << 1, + confirm = 1 << 2, + no_sig = 1 << 3, //... } 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, } @@ -195,7 +195,7 @@ Result create_socket(AddressFamily af, SocketType type, Protocol proto, out Sock 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 +214,7 @@ Result close(Socket socket) // s_noSignal.Erase(socket); // } - return Result.Success; + return Result.success; } Result shutdown(Socket socket, SocketShutdownMode how) @@ -224,15 +224,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,7 +240,7 @@ 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) @@ -252,14 +252,14 @@ Result bind(Socket socket, ref const InetAddress address) if (_bind(socket.handle, sockAddr, cast(int)addrLen) < 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) @@ -271,7 +271,7 @@ Result connect(Socket socket, ref const InetAddress address) if (_connect(socket.handle, sockAddr, cast(int)addrLen) < 0) return socket_getlasterror(); - return Result.Success; + return Result.success; } Result accept(Socket socket, out Socket connection, InetAddress* connectingSocketAddress = null) @@ -285,12 +285,12 @@ Result accept(Socket socket, out Socket connection, InetAddress* connectingSocke return socket_getlasterror(); else if (connectingSocketAddress) *connectingSocketAddress = make_InetAddress(addr); - return Result.Success; + return Result.success; } -Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.None, size_t* bytesSent = null) +Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, size_t* bytesSent = null) { - Result r = Result.Success; + Result r = Result.success; ptrdiff_t sent = _send(socket.handle, message.ptr, cast(int)message.length, map_message_flags(flags)); if (sent < 0) @@ -303,7 +303,7 @@ Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.None return r; } -Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.None, const InetAddress* address = null, size_t* bytesSent = null) +Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytesSent = null) { ubyte[sockaddr_storage.sizeof] tmp = void; size_t addrLen; @@ -314,7 +314,7 @@ Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.No assert(sockAddr, "Invalid socket address"); } - Result r = Result.Success; + 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) { @@ -326,9 +326,9 @@ Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.No return r; } -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* bytesReceived) { - 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; @@ -351,21 +351,21 @@ 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* senderAddress = null, size_t* bytesReceived) { char[sockaddr_storage.sizeof] addrBuffer = void; sockaddr* addr = cast(sockaddr*)addrBuffer.ptr; socklen_t size = addrBuffer.sizeof; - Result r = Result.Success; + 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; @@ -374,10 +374,10 @@ Result recvfrom(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.None, In *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 + 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 r = error; } if (r && senderAddress) @@ -387,16 +387,16 @@ Result recvfrom(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.None, In 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!"); + assert(optInfo.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); + assert(optlen == s_optTypeRtSize[optInfo.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,7 +420,7 @@ 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 (optInfo.platform_type == OptType.unsupported) // return r; // } @@ -433,17 +433,17 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval const(void)* arg = optval; int itmp = void; linger ling = void; - if (optInfo.rtType != optInfo.platformType) + if (optInfo.rt_type != optInfo.platform_type) { - switch (optInfo.rtType) + switch (optInfo.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 (optInfo.platform_type) { - case OptType.Int: + case OptType.int_: itmp = value ? 1 : 0; arg = &itmp; break; @@ -451,20 +451,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 (optInfo.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,7 +479,7 @@ 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], optInfo.option, cast(const(char)*)arg, s_optTypePlatformSize[optInfo.platform_type]); return r; } @@ -487,80 +487,80 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval 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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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!"); + assert(optInfo.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); + assert(outputlen == s_optTypeRtSize[optInfo.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 (optInfo.rt_type != optInfo.platform_type) { - switch (optInfo.platformType) + switch (optInfo.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 +570,39 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ } } - socklen_t writtenLen = s_optTypePlatformSize[optInfo.platformType]; + socklen_t writtenLen = s_optTypePlatformSize[optInfo.platform_type]; // get the option r.systemCode = getsockopt(socket.handle, s_sockOptLevel[level], optInfo.option, cast(char*)arg, &writtenLen); - if (optInfo.rtType != optInfo.platformType) + if (optInfo.rt_type != optInfo.platform_type) { - switch (optInfo.rtType) + switch (optInfo.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 (optInfo.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 (optInfo.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 +614,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(optInfo.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 (optInfo.rt_type) { case OptType.INAddress: { @@ -635,36 +635,36 @@ 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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.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"); + if (optInfo.rt_type == OptType.unsupported) + return InternalResult.unsupported; + assert(optInfo.rt_type == OptType.inet_addr, "Incorrect value type for option"); return get_socket_option(socket, option, &output, IPAddr.sizeof); } @@ -680,27 +680,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 +716,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 +730,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,7 +738,7 @@ 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) @@ -760,9 +760,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 +780,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 +795,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 +812,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 +831,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 +878,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 +893,9 @@ nothrow @nogc: struct PollFd { Socket socket; - PollEvents requestEvents; - PollEvents returnEvents; - void* userData; + PollEvents request_events; + PollEvents return_events; + void* user_data; } @@ -920,56 +920,56 @@ 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; } @@ -1136,28 +1136,28 @@ 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 +1169,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 = [ @@ -1201,13 +1201,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 +1220,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 +1261,68 @@ 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( -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( 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( -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 +1331,12 @@ 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.oob) r |= MSG_OOB; + 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 +1344,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; } diff --git a/src/urt/string/ansi.d b/src/urt/string/ansi.d index eac1150..e17b489 100644 --- a/src/urt/string/ansi.d +++ b/src/urt/string/ansi.d @@ -68,7 +68,7 @@ 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; @@ -84,17 +84,17 @@ size_t parseANSICode(const(char)[] text) 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/format.d b/src/urt/string/format.d index 6016615..8175c42 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -1,6 +1,6 @@ module urt.string.format; -import urt.conv : parseIntFast; +import urt.conv : parse_int_fast; import urt.string; import urt.traits; import urt.util; @@ -269,24 +269,24 @@ struct DefFormat(T) } 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); + 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.formatInt(tmp); + size_t len = width.format_int(tmp); format = tmp[0..len]; } - return formatFloat(value, buffer, format); + return format_float(value, buffer, format); } else static if (is(T == ulong) || is(T == long)) { - import urt.conv : formatInt, formatUint; + import urt.conv : format_int, format_uint; // TODO: what formats are interesting for ints? @@ -318,7 +318,7 @@ struct DefFormat(T) if (format.length && format[0].isNumeric) { bool success; - padding = format.parseIntFast(success); + padding = format.parse_int_fast(success); if (varLen) { if (padding < 0 || !formatArgs[padding].canInt) @@ -344,9 +344,9 @@ struct DefFormat(T) } static if (is(T == long)) - size_t len = formatInt(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', showSign); + size_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', showSign); else - size_t len = formatUint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); + size_t len = format_uint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); if (toLower && len > 0) { @@ -397,7 +397,7 @@ struct DefFormat(T) if (format.length && format[0].isNumeric) { bool success; - width = format.parseIntFast(success); + width = format.parse_int_fast(success); if (varLen) { if (width < 0 || !formatArgs[width].canInt) @@ -453,12 +453,12 @@ struct DefFormat(T) if (format.length && format[0].isNumeric) { bool success; - grp1 = cast(int)format.parseIntFast(success); + grp1 = cast(int)format.parse_int_fast(success); if (success && format.length > 0 && format[0] == ':' && format.length > 1 && format[1].isNumeric) { format.popFront(); - grp2 = cast(int)format.parseIntFast(success); + grp2 = cast(int)format.parse_int_fast(success); } if (!success) return -2; @@ -744,7 +744,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 +807,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) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index b1281cd..f2747fa 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; @@ -240,9 +240,9 @@ nothrow @nogc: if (!ptr) return 0; static if (size_t.sizeof == 4) - return fnv1aHash(cast(ubyte[])ptr[0 .. length]); + return fnv1a(cast(ubyte[])ptr[0 .. length]); else - return fnv1aHash64(cast(ubyte[])ptr[0 .. length]); + return fnv1a64(cast(ubyte[])ptr[0 .. length]); } const(char)[] opIndex() const pure diff --git a/src/urt/string/tailstring.d b/src/urt/string/tailstring.d index 3664342..c9d8751 100644 --- a/src/urt/string/tailstring.d +++ b/src/urt/string/tailstring.d @@ -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/system.d b/src/urt/system.d index 84f97a1..2bb58ec 100644 --- a/src/urt/system.d +++ b/src/urt/system.d @@ -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,13 +83,13 @@ 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) { @@ -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)); } diff --git a/src/urt/time.d b/src/urt/time.d index 646af39..33a7693 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -279,7 +279,7 @@ pure nothrow @nogc: 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; size_t offset = 0; uint y = year; @@ -291,37 +291,37 @@ pure nothrow @nogc: buffer[0 .. 3] = "BC "; offset += 3; } - ptrdiff_t len = year.formatInt(buffer[offset..$]); + ptrdiff_t len = year.format_int(buffer[offset..$]); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = '-'; - len = month.formatInt(buffer[offset..$]); + len = month.format_int(buffer[offset..$]); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = '-'; - len = day.formatInt(buffer[offset..$]); + len = day.format_int(buffer[offset..$]); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = ' '; - len = hour.formatInt(buffer[offset..$], 10, 2, '0'); + len = hour.format_int(buffer[offset..$], 10, 2, '0'); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = ':'; - len = minute.formatInt(buffer[offset..$], 10, 2, '0'); + len = minute.format_int(buffer[offset..$], 10, 2, '0'); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = ':'; - len = second.formatInt(buffer[offset..$], 10, 2, '0'); + len = second.format_int(buffer[offset..$], 10, 2, '0'); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = '.'; - len = (ns / 1_000_000).formatInt(buffer[offset..$], 10, 3, '0'); + len = (ns / 1_000_000).format_int(buffer[offset..$], 10, 3, '0'); if (len < 0) return len; return offset + len; @@ -511,14 +511,14 @@ package(urt) void initClock() ptrdiff_t timeToString(long ms, char[] buffer) pure { - import urt.conv : formatInt; + import urt.conv : format_int; long hr = ms / 3_600_000; if (!buffer.ptr) - return hr.formatInt(null, 10, 2, '0') + 10; + return hr.format_int(null, 10, 2, '0') + 10; - ptrdiff_t len = hr.formatInt(buffer, 10, 2, '0'); + ptrdiff_t len = hr.format_int(buffer, 10, 2, '0'); if (len < 0 || buffer.length < len + 10) return -1; diff --git a/src/urt/util.d b/src/urt/util.d index 044f47f..6eb457d 100644 --- a/src/urt/util.d +++ b/src/urt/util.d @@ -98,13 +98,15 @@ T alignUp(T)(T value, size_t alignment) bool isAligned(size_t alignment, T)(T value) if (isSomeInt!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)) { + static assert(T.sizeof <= size_t.sizeof, "TODO"); return (cast(size_t)value & (alignment - 1)) == 0; } @@ -442,8 +444,8 @@ ulong byteReverse(ulong v) pragma(inline, true) T byteReverse(T)(T val) if (!isIntegral!T) { - import urt.meta : intForWidth; - alias U = intForWidth!(T.sizeof*8); + import urt.meta : IntForWidth; + alias U = IntForWidth!(T.sizeof*8); U r = byteReverse(*cast(U*)&val); return *cast(T*)&r; } diff --git a/src/urt/variant.d b/src/urt/variant.d index 9f5fe2c..8567b46 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -510,14 +510,14 @@ nothrow @nogc: assert(false, "TODO: implement quantity formatting for JSON"); if (isDouble()) - return asDouble().formatFloat(buffer); + return asDouble().format_float(buffer); // TODO: parse args? //format 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(); @@ -536,7 +536,7 @@ nothrow @nogc: 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) @@ -691,12 +691,12 @@ unittest private: -import urt.hash : fnv1aHash; +import urt.hash : fnv1a; 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 UserTypeId(T) = fnv1a(cast(const(ubyte)[])T.stringof); // maybe this isn't a good enough hash? enum uint UserTypeShortId(T) = cast(ushort)UserTypeId!T ^ (UserTypeId!T >> 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); diff --git a/src/urt/zip.d b/src/urt/zip.d index 4bf2521..62f4fda 100644 --- a/src/urt/zip.d +++ b/src/urt/zip.d @@ -4,21 +4,15 @@ 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 -{ - OK = 0, /**< Success */ - DATA_ERROR = -3, /**< Input error */ - BUF_ERROR = -5 /**< Not enough room for output */ -} - enum gzip_flag : ubyte { FTEXT = 1, @@ -28,91 +22,91 @@ enum gzip_flag : ubyte 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; @@ -123,7 +117,7 @@ error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen uint xlen = loadLittleEndian!ushort(cast(ushort*)start); if (xlen > sourceLen - 12) - return error_code.DATA_ERROR; + return InternalResult.data_error; start += xlen + 2; } @@ -134,7 +128,7 @@ error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen do { if (start - src >= sourceLen) - return error_code.DATA_ERROR; + return InternalResult.data_error; } while (*start++); } @@ -145,7 +139,7 @@ error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen do { if (start - src >= sourceLen) - return error_code.DATA_ERROR; + return InternalResult.data_error; } while (*start++); } @@ -156,12 +150,12 @@ error_code gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen 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); } From e683e62aefe684b1211d113dfbe0dfb79eedd5d7 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 21 Aug 2025 15:48:17 +1000 Subject: [PATCH 03/91] More snake_case migration... --- src/urt/algorithm.d | 38 +++---- src/urt/async.d | 2 +- src/urt/conv.d | 104 ++++++++--------- src/urt/crc.d | 164 +++++++++++++-------------- src/urt/digest/sha.d | 2 +- src/urt/encoding.d | 28 ++--- src/urt/endian.d | 42 +++---- src/urt/fibre.d | 20 ++-- src/urt/file.d | 4 +- src/urt/format/json.d | 2 +- src/urt/inet.d | 2 +- src/urt/mem/alloc.d | 12 +- src/urt/mem/region.d | 4 +- src/urt/mem/scratchpad.d | 4 +- src/urt/meta/nullable.d | 100 ++++++++--------- src/urt/meta/package.d | 18 +-- src/urt/range/package.d | 18 +-- src/urt/range/primitives.d | 38 +++---- src/urt/string/ansi.d | 4 +- src/urt/string/ascii.d | 50 ++++----- src/urt/string/format.d | 30 ++--- src/urt/string/package.d | 16 +-- src/urt/string/string.d | 8 +- src/urt/string/uni.d | 74 ++++++------ src/urt/time.d | 4 +- src/urt/traits.d | 112 +++++++++---------- src/urt/util.d | 224 ++++++++++++++++++------------------- src/urt/zip.d | 22 ++-- 28 files changed, 573 insertions(+), 573 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index a742393..9191f28 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -1,6 +1,6 @@ module urt.algorithm; -import urt.traits : lvalueOf; +import urt.traits : lvalue_of; import urt.util : swap; version = SmallSize; @@ -10,18 +10,18 @@ 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, lvalue_of!T.opCmp(lvalue_of!U))) return a.opCmp(b); - else static if (__traits(compiles, lvalueOf!U.opCmp(lvalueOf!T))) + else static if (__traits(compiles, lvalue_of!U.opCmp(lvalue_of!T))) return -b.opCmp(a); else static if (is(T : A[], A)) { - import urt.traits : isPrimitive; + import urt.traits : is_primitive; auto ai = a.ptr; auto bi = b.ptr; size_t len = a.length < b.length ? a.length : b.length; - static if (isPrimitive!A) + static if (is_primitive!A) { // compare strings foreach (i; 0 .. len) @@ -48,7 +48,7 @@ auto compare(T, U)(auto ref T a, auto ref U b) return a < b ? -1 : (a > b ? 1 : 0); } -size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs) +size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_args) { T* p = arr.ptr; size_t low = 0; @@ -59,7 +59,7 @@ size_t binary_search(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,7 +67,7 @@ size_t binary_search(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 @@ -76,12 +76,12 @@ size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmpArgs } 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; @@ -93,7 +93,7 @@ void qsort(alias pred = void, T)(T[] arr) version (SmallSize) { static if (is(pred == void)) - static if (__traits(compiles, lvalueOf!T.opCmp(lvalueOf!T))) + static if (__traits(compiles, lvalue_of!T.opCmp(lvalue_of!T))) static int compare(const void* a, const void* b) nothrow @nogc => (*cast(const T*)a).opCmp(*cast(const T*)b); else @@ -181,34 +181,34 @@ 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) nothrow @nogc compare, void function(void* a, void* b) nothrow @nogc swap) { void* p = arr.ptr; - size_t length = arr.length / elementSize; + size_t length = arr.length / element_size; if (length > 1) { size_t pivotIndex = length / 2; - void* pivot = p + pivotIndex*elementSize; + void* pivot = p + pivotIndex*element_size; size_t i = 0; size_t j = length - 1; while (i <= j) { - while (compare(p + i*elementSize, pivot) < 0) i++; - while (compare(p + j*elementSize, pivot) > 0) j--; + while (compare(p + i*element_size, pivot) < 0) i++; + while (compare(p + j*element_size, pivot) > 0) j--; if (i <= j) { - swap(p + i*elementSize, p + j*elementSize); + swap(p + i*element_size, p + j*element_size); i++; j--; } } if (j > 0) - qsort(p[0 .. (j + 1)*elementSize], elementSize, compare, swap); + qsort(p[0 .. (j + 1)*element_size], element_size, compare, swap); if (i < length) - qsort(p[i*elementSize .. length*elementSize], elementSize, compare, swap); + qsort(p[i*element_size .. length*element_size], element_size, compare, swap); } } } diff --git a/src/urt/async.d b/src/urt/async.d index fee575f..5da7e61 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); diff --git a/src/urt/conv.d b/src/urt/conv.d index ebce388..a20b6ee 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -7,8 +7,8 @@ public import urt.string.format : toString; nothrow @nogc: -// on error or not-a-number cases, bytesTaken will contain 0 -long parse_int(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure +// on error or not-a-number cases, bytes_taken will contain 0 +long parse_int(const(char)[] str, size_t* bytes_taken = null, int base = 10) pure { size_t i = 0; bool neg = false; @@ -21,13 +21,13 @@ long parse_int(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure i++; } - ulong value = str.ptr[i .. str.length].parse_uint(bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += i; + ulong value = str.ptr[i .. str.length].parse_uint(bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += i; return neg ? -cast(long)value : cast(long)value; } -long parse_int_with_decimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +long parse_int_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = null, int base = 10) pure { size_t i = 0; bool neg = false; @@ -40,13 +40,13 @@ long parse_int_with_decimal(const(char)[] str, out ulong fixedPointDivisor, size i++; } - ulong value = str[i .. str.length].parse_uint_with_decimal(fixedPointDivisor, bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += i; + ulong value = str[i .. str.length].parse_uint_with_decimal(fixed_point_divisor, bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += i; return neg ? -cast(long)value : cast(long)value; } -ulong parse_uint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint(const(char)[] str, size_t* bytes_taken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); @@ -76,12 +76,12 @@ ulong parse_uint(const(char)[] str, size_t* bytesTaken = null, int base = 10) pu } } - if (bytesTaken) - *bytesTaken = s - str.ptr; + if (bytes_taken) + *bytes_taken = s - str.ptr; return value; } -ulong parse_uint_with_decimal(const(char)[] str, out ulong fixedPointDivisor, size_t* bytesTaken = null, int base = 10) pure +ulong parse_uint_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); @@ -126,19 +126,19 @@ parse_decimal: } done: - fixedPointDivisor = divisor; - if (bytesTaken) - *bytesTaken = s - str.ptr; + fixed_point_divisor = divisor; + if (bytes_taken) + *bytes_taken = s - str.ptr; return value; } -ulong parse_uint_with_base(const(char)[] str, size_t* bytesTaken = null) pure +ulong parse_uint_with_base(const(char)[] str, size_t* bytes_taken = null) pure { const(char)* p = str.ptr; int base = parse_base_prefix(str); - ulong i = parse_uint(str, bytesTaken, base); - if (bytesTaken && *bytesTaken != 0) - *bytesTaken += str.ptr - p; + ulong i = parse_uint(str, bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += str.ptr - p; return i; } @@ -222,15 +222,15 @@ unittest } -// on error or not-a-number, result will be nan and bytesTaken will contain 0 -double parse_float(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, int base = 10) pure { // TODO: E-notation... size_t taken = void; ulong div = void; long value = str.parse_int_with_decimal(div, &taken, base); - if (bytesTaken) - *bytesTaken = taken; + if (bytes_taken) + *bytes_taken = taken; if (taken == 0) return double.nan; return cast(double)value / div; @@ -253,18 +253,18 @@ unittest } -ptrdiff_t format_int(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 = format_uint(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 +286,10 @@ ptrdiff_t format_int(long value, char[] buffer, uint base = 10, uint width = 0, 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; } @@ -316,13 +316,13 @@ ptrdiff_t format_uint(ulong value, char[] buffer, uint base = 10, uint width = 0 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 +334,14 @@ ptrdiff_t format_uint(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 +351,7 @@ ptrdiff_t format_uint(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; @@ -442,12 +442,12 @@ template to(T) 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); @@ -481,15 +481,15 @@ private: 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; + uint zero_base = c - '0'; + if (zero_base < 10) + return zero_base; + uint A_base = c - 'A'; + if (A_base < 26) + return A_base + 10; + uint a_base = c - 'a'; + if (a_base < 26) + return a_base + 10; return -1; } @@ -518,7 +518,7 @@ size_t format_struct(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; } diff --git a/src/urt/crc.d b/src/urt/crc.d index 74f266b..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.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 calculate_crc(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 calculate_crc(T = uint)(const void[] data, ref const CRCParams params, ref con 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 calculate_crc(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 calculate_crc(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 calculate_crc_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 calculate_crc_2(Algorithm algo, T = IntForWidth!(paramTable[algo].width*2))(co } 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] generate_crc_table(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] generate_crc_table(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(calculate_crc!(Algorithm.CRC16_MODBUS)(checkData[]) == paramTable[Algorithm.CRC16_MODBUS].check); - assert(calculate_crc!(Algorithm.CRC16_EZSP)(checkData[]) == paramTable[Algorithm.CRC16_EZSP].check); - assert(calculate_crc!(Algorithm.CRC16_KERMIT)(checkData[]) == paramTable[Algorithm.CRC16_KERMIT].check); - assert(calculate_crc!(Algorithm.CRC16_USB)(checkData[]) == paramTable[Algorithm.CRC16_USB].check); - assert(calculate_crc!(Algorithm.CRC16_XMODEM)(checkData[]) == paramTable[Algorithm.CRC16_XMODEM].check); - assert(calculate_crc!(Algorithm.CRC16_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC16_ISO_HDLC].check); - assert(calculate_crc!(Algorithm.CRC16_DNP)(checkData[]) == paramTable[Algorithm.CRC16_DNP].check); - assert(calculate_crc!(Algorithm.CRC32_ISO_HDLC)(checkData[]) == paramTable[Algorithm.CRC32_ISO_HDLC].check); - assert(calculate_crc!(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 = calculate_crc!(Algorithm.CRC16_MODBUS)(checkData[0 .. 5]); - assert(calculate_crc!(Algorithm.CRC16_MODBUS)(checkData[5 .. 9], crc) == paramTable[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) == paramTable[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) == 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 = generate_crc_table!(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/digest/sha.d b/src/urt/digest/sha.d index fc64067..197b7f4 100644 --- a/src/urt/digest/sha.d +++ b/src/urt/digest/sha.d @@ -105,7 +105,7 @@ ubyte[Context.DigestLen] sha_finalise(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; } diff --git a/src/urt/encoding.d b/src/urt/encoding.d index 36ced1b..54eb70d 100644 --- a/src/urt/encoding.d +++ b/src/urt/encoding.d @@ -7,8 +7,8 @@ enum Hex(const char[] s) = (){ ubyte[s.length / 2] r; ptrdiff_t len = hex_decode 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; }(); -ptrdiff_t base64_encode_length(size_t sourceLength) pure - => (sourceLength + 2) / 3 * 4; +ptrdiff_t base64_encode_length(size_t source_length) pure + => (source_length + 2) / 3 * 4; ptrdiff_t base64_encode(const void[] data, char[] result) pure { @@ -54,8 +54,8 @@ 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; +ptrdiff_t base64_decode_length(size_t source_length) pure +=> source_length / 4 * 3; ptrdiff_t base64_decode(const char[] data, void[] result) pure { @@ -144,7 +144,7 @@ ptrdiff_t hex_encode(const void[] data, char[] result) pure 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 +157,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') @@ -193,12 +193,12 @@ unittest ptrdiff_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 +208,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,8 +226,8 @@ 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]; } } @@ -250,7 +250,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 +268,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; diff --git a/src/urt/endian.d b/src/urt/endian.d index 879b666..bb806d9 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,12 +75,12 @@ 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); @@ -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,7 +204,7 @@ 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); @@ -261,36 +261,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)) { 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)) { 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)) { 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)) { version (LittleEndian) return *src; else - return byteReverse(*src); + return byte_reverse(*src); } diff --git a/src/urt/fibre.d b/src/urt/fibre.d index 574e551..2e736d2 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; @@ -104,7 +104,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 +151,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 +163,7 @@ struct Fibre this.fibreEntry = fibreEntry; this.userData = userData; - isDelegate = false; + is_delegate = false; abortRequested = false; finished = false; aborted = false; @@ -198,7 +198,7 @@ private: YieldHandler yieldHandler; cothread_t fibre; - bool isDelegate; + bool is_delegate; bool abortRequested; bool finished; bool aborted; @@ -570,7 +570,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 +741,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 +804,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 8d3995c..37dfad0 100644 --- a/src/urt/file.d +++ b/src/urt/file.d @@ -195,7 +195,7 @@ Result get_path(ref const File file, ref char[] buffer) if (result == 0 || result > dwPathLen) return getlasterror_result(); - size_t pathLen = tmp[0..result].uniConvert(buffer); + size_t pathLen = tmp[0..result].uni_convert(buffer); if (!pathLen) return InternalResult.buffer_too_small; if (buffer.length >= 4 && buffer[0..4] == `\\?\`) @@ -774,7 +774,7 @@ Result get_temp_filename(ref char[] buffer, const(char)[] dstDir, const(char)[] if (!GetTempFileNameW(dstDir.twstringz, prefix.twstringz, 0, tmp.ptr)) 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); diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 997b9e1..a48faed 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -326,7 +326,7 @@ Variant parse_node(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; diff --git a/src/urt/inet.d b/src/urt/inet.d index 1c2e019..6dccd11 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -284,7 +284,7 @@ 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; } diff --git a/src/urt/mem/alloc.d b/src/urt/mem/alloc.d index 5ec148a..a87c8dd 100644 --- a/src/urt/mem/alloc.d +++ b/src/urt/mem/alloc.d @@ -13,13 +13,13 @@ void[] alloc(size_t size) 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[] alloc_aligned(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]; @@ -62,10 +62,10 @@ void[] realloc(void[] mem, size_t newSize) 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 ? alloc_aligned(newSize, alignment) : null; if (newAlloc !is null && mem !is null) 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 dcc681b..2a20c30 100644 --- a/src/urt/mem/scratchpad.d +++ b/src/urt/mem/scratchpad.d @@ -8,7 +8,7 @@ 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[] alloc_scratchpad(size_t size = MaxScratchpadSize) @@ -19,7 +19,7 @@ void[] alloc_scratchpad(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; 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 bec2f81..e71470a 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -53,28 +53,28 @@ template static_index_of(args...) }(); } -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) +template enum_keys(E) { - static assert(is(E == enum), "EnumKeys only works with enums!"); - __gshared immutable string[EnumStrings.length] EnumKeys = [ EnumStrings ]; - private alias EnumStrings = __traits(allMembers, E); + static assert(is(E == enum), "enum_keys only works with enums!"); + __gshared immutable string[enum_strings.length] enum_keys = [ enum_strings ]; + private alias enum_strings = __traits(allMembers, E); } E enum_from_string(E)(const(char)[] key) if (is(E == enum)) { - foreach (i, k; EnumKeys!E) + foreach (i, k; enum_keys!E) if (key[] == k[]) return cast(E)i; return cast(E)-1; diff --git a/src/urt/range/package.d b/src/urt/range/package.d index 4d69c46..3592b11 100644 --- a/src/urt/range/package.d +++ b/src/urt/range/package.d @@ -8,13 +8,13 @@ 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, STATIC_MAP; @@ -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() { @@ -189,10 +189,10 @@ template reduce(fun...) if (isIterable!R) { import std.exception : enforce; - alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); + 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, @@ -259,7 +259,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; @@ -309,7 +309,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 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/string/ansi.d b/src/urt/string/ansi.d index e17b489..931d60d 100644 --- a/src/urt/string/ansi.d +++ b/src/urt/string/ansi.d @@ -70,14 +70,14 @@ nothrow @nogc: 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; diff --git a/src/urt/string/ascii.d b/src/urt/string/ascii.d index 5541d3a..9edab53 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,26 @@ char toUpper(char c) pure return c; } -char[] toLower(const(char)[] str, char[] buffer) pure +char[] to_lower(const(char)[] str, char[] buffer) pure { foreach (i; 0 .. str.length) - buffer[i] = toLower(str[i]); + buffer[i] = to_lower(str[i]); return buffer; } -char[] toUpper(const(char)[] str, char[] buffer) pure +char[] to_upper(const(char)[] str, char[] buffer) pure { foreach (i; 0 .. str.length) - buffer[i] = toUpper(str[i]); + buffer[i] = to_upper(str[i]); return buffer; } -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); } diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 8175c42..410432e 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -291,17 +291,17 @@ struct DefFormat(T) // TODO: what formats are interesting for ints? bool leadingZeroes = false; - bool toLower = false; + bool to_lower = false; bool varLen = false; ptrdiff_t padding = 0; uint base = 10; static if (is(T == long)) { - bool showSign = false; + bool show_sign = false; if (format.length && format[0] == '+') { - showSign = true; + show_sign = true; format.popFront; } } @@ -315,7 +315,7 @@ struct DefFormat(T) varLen = true; format.popFront; } - if (format.length && format[0].isNumeric) + if (format.length && format[0].is_numeric) { bool success; padding = format.parse_int_fast(success); @@ -332,7 +332,7 @@ struct DefFormat(T) if (b == 'x') { base = 16; - toLower = format[0] == 'x' && buffer.ptr; + to_lower = format[0] == 'x' && buffer.ptr; } else if (b == 'b') base = 2; @@ -344,11 +344,11 @@ struct DefFormat(T) } static if (is(T == long)) - size_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', showSign); + size_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', show_sign); else size_t len = format_uint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); - if (toLower && len > 0) + if (to_lower && len > 0) { for (size_t i = 0; i < len; ++i) if (cast(uint)(buffer.ptr[i] - 'A') < 26) @@ -392,9 +392,9 @@ struct DefFormat(T) varLen = true; format.popFront; } - if (varLen && (!format.length || !format[0].isNumeric)) + if (varLen && (!format.length || !format[0].is_numeric)) return -2; - if (format.length && format[0].isNumeric) + if (format.length && format[0].is_numeric) { bool success; width = format.parse_int_fast(success); @@ -450,12 +450,12 @@ struct DefFormat(T) return 0; int grp1 = 1, grp2 = 0; - if (format.length && format[0].isNumeric) + if (format.length && format[0].is_numeric) { bool success; grp1 = cast(int)format.parse_int_fast(success); if (success && format.length > 0 && format[0] == ':' && - format.length > 1 && format[1].isNumeric) + format.length > 1 && format[1].is_numeric) { format.popFront(); grp2 = cast(int)format.parse_int_fast(success); @@ -634,7 +634,7 @@ struct DefFormat(T) static assert(false, "Not implemented for type: ", T.stringof); } - static if (isSomeInt!T || is(T == bool)) + static if (is_some_int!T || is(T == bool)) { ptrdiff_t toInt() const pure nothrow @nogc { @@ -922,9 +922,9 @@ unittest template allAreStrings(Args...) { static if (Args.length == 1) - enum allAreStrings = is(Args[0] : const(char[])) || is(isSomeChar!(Args[0])); + enum allAreStrings = is(Args[0] : const(char[])) || is(is_some_char!(Args[0])); else - enum allAreStrings = (is(Args[0] : const(char[])) || is(isSomeChar!(Args[0]))) && allAreStrings!(Args[1 .. $]); + enum allAreStrings = (is(Args[0] : const(char[])) || is(is_some_char!(Args[0]))) && allAreStrings!(Args[1 .. $]); } template allConstCorrectStrings(Args...) @@ -943,7 +943,7 @@ template constCorrectedStrings(Args...) { static if (is(Ty : const(char)[])) constCorrectedStrings = AliasSeq!(constCorrectedStrings, const(char[])); - else static if (isSomeChar!Ty) + else static if (is_some_char!Ty) constCorrectedStrings = AliasSeq!(constCorrectedStrings, const(char)); else static assert(false, "Argument must be a char array or a char: ", T); diff --git a/src/urt/string/package.d b/src/urt/string/package.d index c4abc6f..6a103d0 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -69,7 +69,7 @@ ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure nothrow @nogc return a.length - b.length; for (size_t i = 0; i < a.length; ++i) { - ptrdiff_t diff = toLower(a[i]) - toLower(b[i]); + ptrdiff_t diff = to_lower(a[i]) - to_lower(b[i]); if (diff) return diff; } @@ -98,12 +98,12 @@ inout(char)[] trim(bool Front = true, bool Back = true)(inout(char)[] s) pure no size_t first = 0, last = s.length; static if (Front) { - while (first < s.length && isWhitespace(s.ptr[first])) + while (first < s.length && is_whitespace(s.ptr[first])) ++first; } static if (Back) { - while (last > first && isWhitespace(s.ptr[last - 1])) + while (last > first && is_whitespace(s.ptr[last - 1])) --last; } return s.ptr[first .. last]; @@ -274,9 +274,9 @@ char[] unEscape(char[] s) pure nothrow @nogc char[] toHexString(const(void[]) data, char[] buffer, uint group = 0, uint secondaryGroup = 0, const(char)[] seps = " -") pure nothrow @nogc { - 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 +296,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) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index f2747fa..a6d73d4 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -626,7 +626,7 @@ nothrow @nogc: 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 +638,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); @@ -654,7 +654,7 @@ nothrow @nogc: ref MutableString!Embed insertFormat(Things...)(size_t offset, auto ref Things things) { 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(); @@ -666,7 +666,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); _format(ptr[offset .. offset + insertLen], forward!things); writeLength(newLen); diff --git a/src/urt/string/uni.d b/src/urt/string/uni.d index ee9b551..bc3e44c 100644 --- a/src/urt/string/uni.d +++ b/src/urt/string/uni.d @@ -3,16 +3,16 @@ module urt.string.uni; nothrow @nogc: -size_t uniConvert(const(char)[] s, wchar[] buffer) +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 +32,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 +47,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 +84,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 +103,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 +120,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 +129,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,16 +140,16 @@ 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) { @@ -168,22 +168,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 +191,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 +200,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 +214,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); @@ -244,7 +244,7 @@ size_t uniConvert(const(dchar)[] s, wchar[] buffer) unittest { - immutable dstring unicodeTest = + immutable dstring unicode_test = "Basic ASCII: Hello, World!\n" ~ "BMP Examples: 你好, مرحبا, שלום, 😊, ☂️\n" ~ "Supplementary Planes: 𐍈, 𝒜, 🀄, 🚀\n" ~ @@ -257,18 +257,18 @@ 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... //... diff --git a/src/urt/time.d b/src/urt/time.d index 33a7693..901884a 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -1,6 +1,6 @@ module urt.time; -import urt.traits : isSomeFloat; +import urt.traits : is_some_float; version (Windows) { @@ -137,7 +137,7 @@ pure nothrow @nogc: bool opCast(T : bool)() const => ticks != 0; - T opCast(T)() const if (isSomeFloat!T) + T opCast(T)() const if (is_some_float!T) => cast(T)ticks / cast(T)ticksPerSecond; bool opEquals(Duration b) const diff --git a/src/urt/traits.d b/src/urt/traits.d index 30ef675..bfa8073 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(Unqual!T == bool) || 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,27 @@ 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_default_constructible(T) = is_primitive!T || (is(T == struct) && __traits(compiles, { T t; })); -enum isConstructible(T, Args...) = (isPrimitive!T && (Args.length == 0 || (Args.length == 1 && is(Args[0] : 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 6eb457d..fb8fe2d 100644 --- a/src/urt/util.d +++ b/src/urt/util.d @@ -40,23 +40,23 @@ auto max(T, U)(auto ref inout T a, auto ref inout U b) 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,40 +71,40 @@ 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(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; @@ -142,7 +142,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) { @@ -174,7 +174,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) @@ -203,7 +203,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); @@ -272,7 +272,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); @@ -380,7 +380,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) { @@ -412,9 +412,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)); @@ -423,7 +423,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)); @@ -432,7 +432,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)); @@ -441,17 +441,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); + 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) { @@ -478,7 +478,7 @@ T bitReverse(T)(T x) static if (T.sizeof == 1) return x; else - return byteReverse(x); + return byte_reverse(x); } } else @@ -533,39 +533,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); @@ -679,50 +679,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/zip.d b/src/urt/zip.d index 62f4fda..040a056 100644 --- a/src/urt/zip.d +++ b/src/urt/zip.d @@ -6,20 +6,20 @@ import urt.hash; import urt.mem.allocator; import urt.result; -alias zlib_crc = calculate_crc!(Algorithm.CRC32_ISO_HDLC); +alias zlib_crc = calculate_crc!(Algorithm.crc32_iso_hdlc); nothrow @nogc: // this is a port of tinflate (tiny inflate) -enum gzip_flag : ubyte +enum GzipFlag : ubyte { - FTEXT = 1, - FHCRC = 2, - FEXTRA = 4, - FNAME = 8, - FCOMMENT = 16 + ftext = 1, + fhcrc = 2, + fextra = 4, + fname = 8, + fcomment = 16 } Result zlib_uncompress(const(void)[] source, void[] dest, out size_t destLen) @@ -112,7 +112,7 @@ Result gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) 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); @@ -123,7 +123,7 @@ Result gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) } // skip file name if present - if (flg & gzip_flag.FNAME) + if (flg & GzipFlag.fname) { do { @@ -134,7 +134,7 @@ Result gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) } // skip file comment if present - if (flg & gzip_flag.FCOMMENT) + if (flg & GzipFlag.fcomment) { do { @@ -145,7 +145,7 @@ Result gzip_uncompress(const(void)[] source, void[] dest, out size_t destLen) } // check header crc if present - if (flg & gzip_flag.FHCRC) + if (flg & GzipFlag.fhcrc) { uint hcrc; From 8374c4f71de2a16083936ccf613be07c97d8fcfa Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 17 Aug 2025 16:18:37 +1000 Subject: [PATCH 04/91] Improve SI unit/quantity --- src/urt/meta/package.d | 23 +- src/urt/si/quantity.d | 202 ++++++++++++--- src/urt/si/unit.d | 571 +++++++++++++++++++++++++++++++++-------- 3 files changed, 644 insertions(+), 152 deletions(-) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index e71470a..19f10f3 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -6,26 +6,29 @@ alias Alias(T) = T; alias AliasSeq(TList...) = TList; -template IntForWidth(size_t width, bool signed = false) +template IntForWidth(size_t bits, bool signed = false) { - static if (width <= 8 && !signed) + static if (bits <= 8 && !signed) alias IntForWidth = ubyte; - else static if (width <= 8 && signed) + else static if (bits <= 8 && signed) alias IntForWidth = byte; - else static if (width <= 16 && !signed) + else static if (bits <= 16 && !signed) alias IntForWidth = ushort; - else static if (width <= 16 && signed) + else static if (bits <= 16 && signed) alias IntForWidth = short; - else static if (width <= 32 && !signed) + else static if (bits <= 32 && !signed) alias IntForWidth = uint; - else static if (width <= 32 && signed) + else static if (bits <= 32 && signed) alias IntForWidth = int; - else static if (width <= 64 && !signed) + else static if (bits <= 64 && !signed) alias IntForWidth = ulong; - else static if (width <= 64 && signed) + 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!(); @@ -72,7 +75,7 @@ template enum_keys(E) } E enum_from_string(E)(const(char)[] key) - if (is(E == enum)) +if (is(E == enum)) { foreach (i, k; enum_keys!E) if (key[] == k[]) diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index 33462df..8fcecc6 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -1,6 +1,8 @@ module urt.si.quantity; +import urt.meta : TypeForOp; import urt.si.unit; +import urt.traits; nothrow @nogc: @@ -9,6 +11,12 @@ 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,25 +66,26 @@ 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); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); value = adjustScale(b); } } - + void opAssign()(T value) pure { 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,36 +94,44 @@ 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); + static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); value = adjustScale(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; + Quantity!(TypeForOp!(op, T, U), _unit) r; r.value = mixin("value " ~ op ~ " adjustScale(b)"); static if (Dynamic) r.unit = unit; @@ -120,7 +146,7 @@ nothrow @nogc: return mixin("this.value " ~ op ~ " value"); else { - This r; + Quantity!(TypeForOp!(op, T, U), unit) r; r.value = mixin("this.value " ~ op ~ " value"); static if (Dynamic) r.unit = unit; @@ -135,33 +161,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 (isSomeFloat!T || isSomeInt!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.adjustScale(this); + static if (T.Dynamic) + r.unit = unit; + return r; + } + } + bool opEquals(U)(U value) const pure if (is(U : T)) { @@ -185,14 +241,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,7 +256,7 @@ nothrow @nogc: auto rTrans = rh.unit.offset(); lhs = lhs*lScale + lTrans; rhs = rhs*rScale + rTrans; - } + }} else rhs = adjustScale(rh); @@ -222,6 +278,80 @@ nothrow @nogc: } } + auto normalise() const pure + { + static if (Dynamic) + { + Quantity!T r; + r.unit = ScaledUnit(unit.unit); + } + else + Quantity!(T, ScaledUnit(unit.unit)) r; + r.value = r.adjustScale(this); + return r; + } + + ptrdiff_t toString(char[] buffer) const + { + import urt.conv : format_float; + + double v = value; + ScaledUnit u = unit; + + if (u.pack) + { + // 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; + } + } + } + } + + ptrdiff_t l = format_float(v, buffer); + if (l < 0) + return l; + + if (u.pack) + { + ptrdiff_t l2 = u.toString(buffer[l .. $]); + if (l2 < 0) + return l2; + l += l2; + } + + return l; + } + + ptrdiff_t fromString(const(char)[] s) + { + return -1; + } + private: T adjustScale(U, ScaledUnit _U)(Quantity!(U, _U) b) const pure { diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index cf40a9e..1db2668 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: @@ -39,6 +42,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); @@ -61,6 +67,7 @@ enum Litre = ScaledUnit(CubicMetre, -3); enum Gram = ScaledUnit(Kilogram, -3); enum Milligram = ScaledUnit(Kilogram, -6); 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 +103,18 @@ enum UnitType : ubyte struct Unit { -nothrow @nogc: +nothrow: + // debug/ctfe helper + string toString() pure + { + char[32] t; + ptrdiff_t l = toString(t); + if (l < 0) + return "Invalid unit"; // OR JUST COULDN'T STRINGIFY! + return t[0..l].idup; + } + +@nogc: uint pack; @@ -225,82 +243,39 @@ nothrow @nogc: this = this.opBinary!op(rh); } - ptrdiff_t toString(char[] buffer) const + ptrdiff_t toString(char[] buffer) 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.takePower(); + 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 +356,18 @@ enum ExtendedScaleFactor : ubyte struct ScaledUnit { -nothrow @nogc: +nothrow: + // debug/ctfe helper + string toString() pure + { + char[32] t; + ptrdiff_t l = toString(t); + if (l < 0) + return "Invalid unit"; // OR JUST COULDN'T STRINGIFY! + return t[0..l].idup; + } + +@nogc: uint pack; @@ -574,6 +560,244 @@ nothrow @nogc: bool opEquals(Unit rh) const pure => (pack & 0xFF000000) ? false : unit == rh; + ptrdiff_t parseUnit(const(char)[] s, out float preScale) pure + { + preScale = 1; + + if (s.length == 0) + { + pack = 0; + return 0; + } + + size_t len = s.length; + if (s[0] == '-') + { + if (s.length == 1) + return -1; + preScale = -1; + s = s[1 .. $]; + } + + ScaledUnit r; + bool invert; + char sep; + while (const(char)[] term = s.split!('/', '*')(sep)) + { + int p = term.takePower(); + if (p == 0) + return -1; // invalid exponent + + if (const ScaledUnit* su = term in noScaleUnitMap) + r *= (*su) ^^ (invert ? -p : p); + else + { + size_t offset = 0; + + // parse the exponent + int e = 0; + if (term[0].is_numeric) + { + if (term[0] == '0') + { + if (term.length < 2 || term[1] != '.') + return -1; + e = 1; + offset = 2; + while (offset < term.length) + { + if (term[offset] == '1') + break; + if (term[offset] != '0') + return -1; + ++e; + ++offset; + } + ++offset; + e = -e; + } + else if (term[0] == '1') + { + offset = 1; + while (offset < term.length) + { + if (term[offset] != '0') + break; + ++e; + ++offset; + } + } + else + return -1; + } + + if (offset == term.length) + r *= ScaledUnit(Unit(), 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' + 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 noScaleUnitMap) + { + r *= (*su) ^^ (invert ? -p : p); + preScale *= 10^^e; + } + else + return -1; // string was not taken? + } + } + + if (sep == '/') + invert = true; + } + this = r; + return len; + } + + ptrdiff_t toString(char[] buffer) const pure + { + if (!unit.pack) + { + if (siScale && exp == -2) + { + if (buffer.length == 0) + return -1; + buffer[0] = '%'; + return 1; + } + else + assert(false, "TODO!"); + } + + 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.length < 2) + return -1; + --x; + buffer[0..2] = "10"; + len += 2; + } + else + { + if (buffer.length < 3) + return -1; + x -= 2; + buffer[0..3] = "100"; + len += 3; + } + } + assert(x >= -30, "TODO: handle this very small case"); + + if (x != 0) + { + if (buffer.length <= len) + return -1; + buffer[len++] = "qryzafpnum kMGTPEZYRQ"[x/3 + 10]; + } + } + + if (const string* name = unit in unitNames) + { + 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.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 fromString(const(char)[] s) pure + { + float scale; + ptrdiff_t r = parseUnit(s, scale); + if (scale != 1) + return -1; + return r; + } + + size_t toHash() const pure => pack; @@ -688,39 +912,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 +971,136 @@ 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, + "Ah" : AmpereHour, + "Wh" : WattHour, + "cy" : Cycle, + "Hz" : Hertz, + "psi" : PSI, + + // questionable... :/ + "VAh" : WattHour, + "varh" : WattHour, +]; + +immutable ScaledUnit[string] scaledUnitMap = [ + "%" : Percent, + "‰" : Permille, + "l" : Litre, + "g" : Gram, +]; + +int takePower(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; +} From f0fc1600bfff67ec1cab377927829b83336e6a5c Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 19 Aug 2025 20:47:19 +1000 Subject: [PATCH 05/91] Improve variant support for Quantity Also improve user type support... which is kinda unrelated, but ya-know! --- src/urt/conv.d | 88 +++++ src/urt/format/json.d | 23 +- src/urt/inet.d | 10 + src/urt/si/quantity.d | 2 +- src/urt/variant.d | 805 ++++++++++++++++++++++++++++++++++-------- 5 files changed, 758 insertions(+), 170 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index a20b6ee..dbbd3f9 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -253,6 +253,94 @@ unittest } +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 format_int(long value, char[] buffer, uint base = 10, uint width = 0, char fill = ' ', bool show_sign = false) pure { const bool neg = value < 0; diff --git a/src/urt/format/json.d b/src/urt/format/json.d index a48faed..ca2cd26 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -23,28 +23,9 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u 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: diff --git a/src/urt/inet.d b/src/urt/inet.d index 6dccd11..4b41981 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -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; diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index 8fcecc6..c8da35e 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -194,7 +194,7 @@ nothrow @nogc: // not clear if this should return the raw value, or the normalised value...? // T opCast(T)() const pure -// if (isSomeFloat!T || isSomeInt!T) +// 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?"); diff --git a/src/urt/variant.d b/src/urt/variant.d index 8567b46..cc89cf5 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -1,9 +1,12 @@ 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.si.quantity; import urt.si.unit : ScaledUnit; import urt.traits; @@ -65,80 +68,81 @@ nothrow @nogc: } this(I)(I i) - if (is(I == byte) || is(I == short)) + if (is_some_int!I) { - 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)) - { - flags = cast(Flags)(Flags.NumberUint | Flags.IntFlag); - value.ul = i; - } - - this(int i) - { - flags = Flags.NumberInt; - value.l = i; - if (i >= 0) - flags |= Flags.UintFlag | Flags.Uint64Flag; - } - - this(uint i) - { - flags = Flags.NumberUint; - value.ul = i; - if (i <= int.max) - flags |= Flags.IntFlag; - } + static if (is_signed_int!I) + value.l = i; + else + value.ul = i; - this(long i) - { - flags = Flags.NumberInt64; - value.l = i; - if (i >= 0) + 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.Uint64Flag; + flags = Flags.NumberUint; if (i <= int.max) - flags |= Flags.IntFlag | Flags.UintFlag; + 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 |= Flags.UintFlag | Flags.Int64Flag; + else if (i <= long.max) + flags |= Flags.Int64Flag; } - else if (i >= int.min) - flags |= Flags.IntFlag; } - this(ulong i) + this(F)(F f) + if (is_some_float!F) { - 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(float f) - { - flags = Flags.NumberFloat; + static if (is(F == float)) + flags = Flags.NumberFloat; + else + flags = Flags.NumberDouble; value.d = f; } - this(double d) + + this(E)(E e) + if (is(E == enum)) { - flags = Flags.NumberDouble; - value.d = d; + static if (is(E T == enum)) + { + this(T(e)); + // TODO: do we keep a record of the enum keys for stringification? + } } this(U, ScaledUnit _U)(Quantity!(U, _U) q) { this(q.value); - flags |= Flags.IsQuantity; - count = q.unit.pack; + if (q.unit.pack) + { + flags |= Flags.IsQuantity; + count = q.unit.pack; + } } this(const(char)[] s) // TODO: (S)(S s) @@ -209,19 +213,28 @@ nothrow @nogc: static if (is(T == class)) { count = UserTypeId!T; - value.p = cast(void*)&thing; + ptr = cast(void*)thing; } else static if (EmbedUserType!T) { + alloc = UserTypeId!T; flags |= Flags.Embedded; - alloc = UserTypeShortId!T; + + 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 { -// flags |= Flags.NeedDestruction; // if T has a destructor... count = UserTypeId!T; - assert(false, "TODO: alloc for the object..."); + 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 +243,26 @@ 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); + } + 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 +293,230 @@ nothrow @nogc: return nodeArray.pushBack(); } + bool opEquals(T)(ref const Variant rhs) const pure + { + return opCmp(rhs) == 0; + } + bool opEquals(T)(auto ref const T rhs) const + { + // 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) + return false; + static if (is_some_int!T) if (!canFitInt!T) + return false; + if (isQuantity) + return asQuantity!double() == Quantity!T(rhs); + 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)[])) + return isString && asString() == rhs[]; + else static if (ValidUserType!T) + return 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.String || 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(); + + if (a.isQuantity || b.isQuantity) + { + // we can't compare different units + uint aunit = a.isQuantity ? (a.count & 0xFFFFFF) : 0; + uint bunit = b.isQuantity ? (b.count & 0xFFFFFF) : 0; + if (aunit != bunit) + { + r = aunit - bunit; + break; + } + + // matching units, but we'll only do quantity comparison if there is some scaling + ubyte ascale = a.isQuantity ? (a.count >> 24) : 0; + ubyte bscale = b.isQuantity ? (b.count >> 24) : 0; + if (ascale || bscale) + { + Quantity!double aq = a.isQuantity ? a.asQuantity!double() : Quantity!double(asDoubleWithBool(*a)); + Quantity!double bq = b.isQuantity ? b.asQuantity!double() : Quantity!double(asDoubleWithBool(*b)); + r = aq.opCmp(bq); + break; + } + } + + if (a.flags & Flags.FloatFlag || b.flags & Flags.FloatFlag) + { + // 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.String: + if (b.type != Type.String) + { + r = -1; + break; + } + r = compare(a.asString(), b.asString()); + 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 + { + // 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 @@ -302,42 +553,77 @@ nothrow @nogc: if ((flags & Flags.TypeMask) != Type.User) return false; static if (EmbedUserType!T) - return alloc == UserTypeShortId!T; + return alloc == UserTypeId!T; else return count == UserTypeId!T; } + 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 +635,40 @@ 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 || isSomeInt!T) { - assert(isNumber()); - - 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(); - else - assert(false, "Unsupported quantity type!"); - if (isQuantity()) + if (isNull) + return Quantity!T(0); + assert(isNumber); + Quantity!T r; + r.value = as!T; + if (isQuantity) r.unit.pack = count; return r; } 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]; @@ -415,11 +708,72 @@ nothrow @nogc: 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..."); + { + T r = void; + TypeDetailsFor!T.copy_emplace(embed.ptr, &r, false); + return r; + } else static assert(false, "Should be impossible?"); } + auto as(T)() inout pure + if (!ValidUserType!T || !UserTypeReturnByRef!T) + { + static if (is_some_int!T) + { + static if (is_signed_int!T) + { + static if (is(T == long)) + return asLong(); + else + { + int i = asInt(); + static if (!is(T == int)) + assert(i >= T.min && i <= T.max, "Value out of range for " ~ T.stringof); + return cast(T)i; + } + } + else + { + static if (is(T == ulong)) + return asUlong(); + else + { + uint u = asInt(); + static if (!is(T == uint)) + assert(u <= T.max, "Value out of range for " ~ T.stringof); + return cast(T)u; + } + } + } + else static if (is_some_float!T) + { + static if (is(T == float)) + return asFloat(); + else + return asDouble(); + } + else static if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) + { + return asQuantity!U(); + } + else static if (is(T : const(char)[])) + { + static if (is(T == struct)) // for String/MutableString/etc + return T(asString); // TODO: error? shouldn't this NRVO?! + else + return asString; + } + else static if (ValidUserType!T) + return asUser!T; + else + static assert(false, "TODO!"); + } + ref inout(T) as(T)() inout pure + if (ValidUserType!T && UserTypeReturnByRef!T) + => asUser!T; + size_t length() const pure { if (flags == Flags.Null) @@ -429,7 +783,7 @@ nothrow @nogc: else if (isArray()) return count; else - assert(false); + assert(false, "Variant does not have `length`"); } bool empty() const pure @@ -479,41 +833,27 @@ nothrow @nogc: { final switch (type) { - case Variant.Type.Null: + 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] = "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; + 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().format_float(buffer); // TODO: parse args? - //format + assert(!format, "TODO"); if (flags & Flags.Uint64Flag) return asUlong().format_uint(buffer); @@ -540,12 +880,108 @@ nothrow @nogc: 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); else - return findTypeDetails(count).stringify(ptr, buffer); + return type_details[alloc].stringify(cast(void*)ptr, buffer, true); } } + 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) + { + size_t taken; + ScaledUnit unit; + ulong div; + long i = s.parse_int_with_decimal(div, &taken, 10); + if (taken < s.length) + { + size_t t2 = unit.fromString(s[taken .. $]); + if (t2 > 0) + taken += t2; + } + if (taken == s.length) + { + if (div != 1) + this = double(i) / div; + else + this = i; + if (unit.pack) + { + flags |= Flags.IsQuantity; + 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 .. num_type_details) + { + debug assert(type_details[i].alignment <= 64 && type_details[i].size <= buffer.sizeof, "Buffer is too small for user type!"); + ptrdiff_t taken = type_details[i].stringify(type_details[i].embedded ? embed.ptr : buffer.ptr, cast(char[])s, false); + if (taken > 0) + { + flags = Flags.User; + if (type_details[i].destroy) + flags |= Flags.NeedDestruction; + if (type_details[i].embedded) + { + flags |= Flags.Embedded; + alloc = cast(ushort)type_details[i].type_id; + } + else + { + void* object = defaultAllocator().alloc(type_details[i].size, type_details[i].alignment).ptr; + type_details[i].copy_emplace(buffer.ptr, object, true); + if (type_details[i].destroy) + type_details[i].destroy(buffer.ptr); + ptr = object; + count = type_details[i].type_id; + alloc = i; + } + return taken; + } + } + + // what is this? + assert(false, "Can't parse variant from string"); + } + + package: union Value { @@ -586,6 +1022,19 @@ 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)* userPtr() 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) @@ -611,18 +1060,19 @@ package: 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) : type_details[alloc]; + if (td.destroy) + td.destroy(userPtr); + 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, Array = 5, @@ -696,8 +1146,14 @@ import urt.hash : fnv1a; static assert(Variant.sizeof == 16); static assert(Variant.Type.max <= Variant.Flags.TypeMask); -enum uint UserTypeId(T) = fnv1a(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); @@ -717,42 +1173,95 @@ template MakeTypeDetails(T) // 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(num_type_details < type_details.length, "Too many user types!"); + type_details[num_type_details++] = TypeDetailsFor!T; } alias MakeTypeDetails = void; } +ushort type_detail_index(T)() + if (ValidUserType!T) +{ + foreach (i; 0 .. num_type_details) + if (type_details[i].type_id == UserTypeId!T) + return i; + assert(false, "Why wasn't the type registered?"); +} + struct TypeDetails { - uint typeId; + uint 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 format) 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[8] type_details; +__gshared ushort num_type_details = 0; -ref TypeDetails findTypeDetails(uint typeId) +ref TypeDetails find_type_details(uint type_id) { - foreach (i, ref td; typeDetails[0 .. numTypeDetails]) + foreach (i, ref td; type_details[0 .. num_type_details]) { - if (td.typeId == typeId) + if (td.type_id == type_id) return td; } assert(false, "TypeDetails not found!"); } +ref TypeDetails get_type_details(uint index) +{ + debug assert(index < num_type_details); + return type_details[index]; +} + +enum TypeDetailsFor(T) = TypeDetails(UserTypeId!T, + T.sizeof, + T.alignof, + EmbedUserType!T, + // moveEmplace + is(T == class) ? null : (void* src, void* dst, bool move) { + if (move) + moveEmplace(*cast(T*)src, *cast(T*)dst); + else + *cast(T*)dst = *cast(const T*)src; + }, + // destroy + is(T == class) ? null : (void* val) { + destroy!false(*cast(T*)val); + }, + // stringify + (void* val, char[] buffer, bool format) { + import urt.string.format : toString; + if (format) + return toString(*cast(const T*)val, buffer); + else + { + static if (__traits(compiles, { buffer.parse!T(*cast(T*)val); })) + return buffer.parse!T(*cast(T*)val); + else + return -1; + } + }, + // cmp + (const void* pa, const void* pb, int type) { + ref const T a = *cast(const T*)pa; + ref const T b = *cast(const T*)pb; + switch (type) + { + case 0: + static if (__traits(compiles, { a.opCmp(b); })) + return a.opCmp(b); + else + return a < b ? -1 : a > b ? 1 : 0; + case 1: + return a == b ? 1 : 0; + case 2: + return a is b ? 1 : 0; + default: + assert(false); + } + }); From 225132d41428c14baef7e7243f65f645fa197867 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 17 Aug 2025 17:26:03 +1000 Subject: [PATCH 06/91] Added ChaCha stream cypher --- src/urt/crypto/chacha.d | 299 ++++++++++++++++++++++++++++++++++++++++ src/urt/digest/md5.d | 4 +- src/urt/digest/sha.d | 8 +- src/urt/encoding.d | 45 ++++-- 4 files changed, 339 insertions(+), 17 deletions(-) create mode 100644 src/urt/crypto/chacha.d 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/digest/md5.d b/src/urt/digest/md5.d index ba1c4c7..e0a59d6 100644 --- a/src/urt/digest/md5.d +++ b/src/urt/digest/md5.d @@ -93,12 +93,12 @@ unittest MD5Context ctx; md5_init(ctx); auto digest = md5_finalise(ctx); - assert(digest == Hex!"d41d8cd98f00b204e9800998ecf8427e"); + assert(digest == HexDecode!"d41d8cd98f00b204e9800998ecf8427e"); md5_init(ctx); md5_update(ctx, "Hello, World!"); digest = md5_finalise(ctx); - assert(digest == Hex!"65a8e27d8879283831b664bd8b7f0ad4"); + assert(digest == HexDecode!"65a8e27d8879283831b664bd8b7f0ad4"); } diff --git a/src/urt/digest/sha.d b/src/urt/digest/sha.d index 197b7f4..671a6fa 100644 --- a/src/urt/digest/sha.d +++ b/src/urt/digest/sha.d @@ -117,22 +117,22 @@ unittest SHA1Context ctx; sha_init(ctx); auto digest = sha_finalise(ctx); - assert(digest == Hex!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); + assert(digest == HexDecode!"da39a3ee5e6b4b0d3255bfef95601890afd80709"); sha_init(ctx); sha_update(ctx, "Hello, World!"); digest = sha_finalise(ctx); - assert(digest == Hex!"0a0a9f2a6772942557ab5355d76af442f8f65e01"); + assert(digest == HexDecode!"0a0a9f2a6772942557ab5355d76af442f8f65e01"); SHA256Context ctx2; sha_init(ctx2); auto digest2 = sha_finalise(ctx2); - assert(digest2 == Hex!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + assert(digest2 == HexDecode!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); sha_init(ctx2); sha_update(ctx2, "Hello, World!"); digest2 = sha_finalise(ctx2); - assert(digest2 == Hex!"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"); + assert(digest2 == HexDecode!"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"); } diff --git a/src/urt/encoding.d b/src/urt/encoding.d index 54eb70d..43fa85e 100644 --- a/src/urt/encoding.d +++ b/src/urt/encoding.d @@ -3,13 +3,17 @@ 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 source_length) pure => (source_length + 2) / 3 * 4; +ptrdiff_t base64_encode_length(const void[] data) pure + => base64_encode_length(data.length); + ptrdiff_t base64_encode(const void[] data, char[] result) pure { auto src = cast(const(ubyte)[])data; @@ -55,7 +59,10 @@ ptrdiff_t base64_encode(const void[] data, char[] result) pure } ptrdiff_t base64_decode_length(size_t source_length) pure -=> source_length / 4 * 3; + => source_length / 4 * 3; + +ptrdiff_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]); } +ptrdiff_t hex_encode_length(size_t sourceLength) pure + => sourceLength * 2; + +ptrdiff_t hex_encode_length(const void[] data) pure + => data.length * 2; ptrdiff_t hex_encode(const void[] data, char[] result) pure { @@ -142,6 +157,12 @@ ptrdiff_t hex_encode(const void[] data, char[] result) pure return toHexString(data, result).length; } +ptrdiff_t hex_decode_length(size_t sourceLength) pure + => sourceLength / 2; + +ptrdiff_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 : is_hex; @@ -180,14 +201,14 @@ 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]); } @@ -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; From fa63396fa3a486fe40afd455c78fc758e54f099a Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 26 Aug 2025 11:23:00 +1000 Subject: [PATCH 07/91] Remove a couple of dangling phobos references. --- src/urt/lifetime.d | 12 ++++++------ src/urt/math.d | 1 - src/urt/range/package.d | 9 +++------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/urt/lifetime.d b/src/urt/lifetime.d index acb04d1..cb6cf40 100644 --- a/src/urt/lifetime.d +++ b/src/urt/lifetime.d @@ -3,8 +3,6 @@ module urt.lifetime; T* emplace(T)(T* chunk) @safe pure { - import core.internal.lifetime : emplaceRef; - emplaceRef!T(*chunk); return chunk; } @@ -12,8 +10,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; } @@ -73,8 +69,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."); @@ -84,6 +79,11 @@ T* emplace(T, Args...)(void[] chunk, auto ref Args args) } +// HACK: we should port this to our lib... +static import core.internal.lifetime; +alias emplaceRef = core.internal.lifetime.emplaceRef; + + /+ void copyEmplace(S, T)(ref S source, ref T target) @system if (is(immutable S == immutable T)) diff --git a/src/urt/math.d b/src/urt/math.d index 236c867..66217d9 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -2,7 +2,6 @@ module urt.math; import urt.intrinsic; import core.stdc.stdio; // For writeDebugf -import std.format; // For format version (LDC) version = GDC_OR_LDC; version (GNU) version = GDC_OR_LDC; diff --git a/src/urt/range/package.d b/src/urt/range/package.d index 3592b11..94af160 100644 --- a/src/urt/range/package.d +++ b/src/urt/range/package.d @@ -182,13 +182,10 @@ 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!(is_input_range!R, ElementType!R, ForeachType!R); alias Args = STATIC_MAP!(ReduceSeedType!E, binfuns); @@ -199,7 +196,7 @@ template reduce(fun...) { 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(); @@ -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; @@ -323,7 +320,7 @@ template fold(fun...) } else { - import std.typecons : tuple; + import urt.meta : tuple; return reduce!fun(tuple(seeds), r); } } From da335b59da5351da6836dd9dc8a888607953930a Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 2 Sep 2025 13:10:33 +1000 Subject: [PATCH 08/91] Tweak the Promise API --- src/urt/async.d | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/urt/async.d b/src/urt/async.d index 5da7e61..81e0a19 100644 --- a/src/urt/async.d +++ b/src/urt/async.d @@ -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; } @@ -84,15 +84,15 @@ void asyncUpdate() } -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); From c50deb8756eb449657cee93c812331e5732f14de Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 3 Sep 2025 11:53:53 +1000 Subject: [PATCH 09/91] Add StringResult --- src/urt/result.d | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/urt/result.d b/src/urt/result.d index 3769e8b..2462386 100644 --- a/src/urt/result.d +++ b/src/urt/result.d @@ -19,6 +19,24 @@ 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) { From cccff2615a6c227f9af5ebf7234ed6e8fa93b83c Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 3 Sep 2025 17:00:17 +1000 Subject: [PATCH 10/91] Minor improvements to urt.time --- src/urt/time.d | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index 901884a..d1d0e00 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -100,14 +100,22 @@ 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 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; + } } auto __debugOverview() const @@ -410,7 +418,7 @@ DateTime getDateTime() static assert(false, "TODO"); } -DateTime getDateTime(SysTime time) +DateTime getDateTime(SysTime time) pure { version (Windows) return fileTimeToDateTime(time); @@ -553,13 +561,14 @@ unittest version (Windows) { - DateTime fileTimeToDateTime(SysTime ftime) + DateTime fileTimeToDateTime(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; @@ -578,10 +587,11 @@ version (Windows) } else version (Posix) { - DateTime realtimeToDateTime(timespec ts) + DateTime realtimeToDateTime(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); From aa707f58c62f5043634bc77f4c64bbd95c629c5c Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 4 Sep 2025 10:37:44 +1000 Subject: [PATCH 11/91] Improved the compare function a bit --- src/urt/algorithm.d | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 9191f28..1e603fa 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -10,36 +10,44 @@ nothrow @nogc: auto compare(T, U)(auto ref T a, auto ref U b) { - static if (__traits(compiles, lvalue_of!T.opCmp(lvalue_of!U))) + static if (__traits(compiles, a.opCmp(b))) return a.opCmp(b); - else static if (__traits(compiles, lvalue_of!U.opCmp(lvalue_of!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 : 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 (is_primitive!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; From f2311afaac943e95ede4a16ca9cec75f992affd3 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 4 Sep 2025 10:54:17 +1000 Subject: [PATCH 12/91] Made CacheString pure, since the cache is immutable. --- src/urt/mem/string.d | 91 ++++++++++++++++++++++------------------- src/urt/string/string.d | 7 ++-- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/urt/mem/string.d b/src/urt/mem/string.d index b00618a..1cefa4e 100644 --- a/src/urt/mem/string.d +++ b/src/urt/mem/string.d @@ -18,40 +18,48 @@ 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; @@ -79,13 +87,11 @@ void initStringHeap(uint stringHeapSize) nothrow assert(stringHeapInitialised == false, "String heap already initialised!"); assert(stringHeapSize <= ushort.max, "String heap too large!"); - stringHeap = new char[stringHeapSize]; + stringHeap = defaultAllocator.allocArray!char(stringHeapSize); // 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; } @@ -104,44 +110,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 +199,6 @@ private: __gshared bool stringHeapInitialised = false; __gshared char[] stringHeap = null; __gshared ushort stringHeapCursor = 0; -__gshared uint numStrings = 0; unittest diff --git a/src/urt/string/string.d b/src/urt/string/string.d index a6d73d4..b2f7abf 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -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 @@ -87,11 +86,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"); From 72fc423fb18cb0257d62585f2eea51fad4c1de82 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 4 Sep 2025 11:45:59 +1000 Subject: [PATCH 13/91] Improved user variants. - can resolve class variants to base classes - added pure hack for TypeDetails lookup, since the table is immutable after construction - const-correctness fixes - readability improvements --- src/urt/variant.d | 283 +++++++++++++++++++++++++++++++--------------- 1 file changed, 192 insertions(+), 91 deletions(-) diff --git a/src/urt/variant.d b/src/urt/variant.d index cc89cf5..e95a557 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -15,6 +15,7 @@ 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) && @@ -213,6 +214,7 @@ nothrow @nogc: static if (is(T == class)) { count = UserTypeId!T; + alloc = type_detail_index!T(); ptr = cast(void*)thing; } else static if (EmbedUserType!T) @@ -547,15 +549,32 @@ nothrow @nogc: => 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 == UserTypeId!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 @@ -690,27 +709,30 @@ 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) + if (ValidUserType!(Unqual!T) && !UserTypeReturnByRef!T) { - if (!isUser!T) - assert(false, "Variant is not a " ~ T.stringof); - static if (is(T == class)) + alias U = Unqual!T; + 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!T) + else static if (EmbedUserType!U) { - T r = void; - TypeDetailsFor!T.copy_emplace(embed.ptr, &r, false); + // make a copy on the stack and return by value + U r = void; + TypeDetailsFor!U.copy_emplace(embed.ptr, &r, false); return r; } else @@ -718,7 +740,7 @@ nothrow @nogc: } auto as(T)() inout pure - if (!ValidUserType!T || !UserTypeReturnByRef!T) + if (!ValidUserType!(Unqual!T) || !UserTypeReturnByRef!T) { static if (is_some_int!T) { @@ -765,13 +787,13 @@ nothrow @nogc: else return asString; } - else static if (ValidUserType!T) + else static if (ValidUserType!(Unqual!T)) return asUser!T; else static assert(false, "TODO!"); } ref inout(T) as(T)() inout pure - if (ValidUserType!T && UserTypeReturnByRef!T) + if (ValidUserType!(Unqual!T) && UserTypeReturnByRef!T) => asUser!T; size_t length() const pure @@ -882,7 +904,7 @@ nothrow @nogc: if (flags & Flags.Embedded) return find_type_details(alloc).stringify(cast(void*)embed.ptr, buffer, true); else - return type_details[alloc].stringify(cast(void*)ptr, buffer, true); + return g_type_details[alloc].stringify(cast(void*)ptr, buffer, true); } } @@ -949,28 +971,29 @@ nothrow @nogc: align(64) void[256] buffer = void; this = null; // clear the object since we'll probably use the embed buffer... - foreach (ushort i; 0 .. num_type_details) + foreach (ushort i; 0 .. g_num_type_details) { - debug assert(type_details[i].alignment <= 64 && type_details[i].size <= buffer.sizeof, "Buffer is too small for user type!"); - ptrdiff_t taken = type_details[i].stringify(type_details[i].embedded ? embed.ptr : buffer.ptr, cast(char[])s, false); + 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); if (taken > 0) { flags = Flags.User; - if (type_details[i].destroy) + if (td.destroy) flags |= Flags.NeedDestruction; - if (type_details[i].embedded) + if (td.embedded) { flags |= Flags.Embedded; - alloc = cast(ushort)type_details[i].type_id; + alloc = cast(ushort)td.type_id; } else { - void* object = defaultAllocator().alloc(type_details[i].size, type_details[i].alignment).ptr; - type_details[i].copy_emplace(buffer.ptr, object, true); - if (type_details[i].destroy) - type_details[i].destroy(buffer.ptr); + 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 = type_details[i].type_id; + count = td.type_id; alloc = i; } return taken; @@ -1060,7 +1083,7 @@ package: nodeArray.destroy!false(); else if (t == Type.User) { - ref const TypeDetails td = (flags & Flags.Embedded) ? find_type_details(alloc) : type_details[alloc]; + ref const TypeDetails td = (flags & Flags.Embedded) ? find_type_details(alloc) : g_type_details[alloc]; if (td.destroy) td.destroy(userPtr); if (!(flags & Flags.Embedded)) @@ -1169,22 +1192,25 @@ 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"); + // 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(num_type_details < type_details.length, "Too many user types!"); - type_details[num_type_details++] = TypeDetailsFor!T; + 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)() +ushort type_detail_index(T)() pure if (ValidUserType!T) { - foreach (i; 0 .. num_type_details) - if (type_details[i].type_id == UserTypeId!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?"); } @@ -1192,6 +1218,7 @@ ushort type_detail_index(T)() struct TypeDetails { uint type_id; + uint super_type_id; ushort size; ubyte alignment; bool embedded; @@ -1200,68 +1227,142 @@ struct TypeDetails ptrdiff_t function(void* val, char[] buffer, bool format) nothrow @nogc stringify; int function(const void* a, const void* b, int type) pure nothrow @nogc cmp; } -__gshared TypeDetails[8] type_details; -__gshared ushort num_type_details = 0; +__gshared TypeDetails[8] g_type_details; +__gshared ushort g_num_type_details = 0; -ref TypeDetails find_type_details(uint type_id) +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; type_details[0 .. num_type_details]) + 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.type_id == type_id) return td; } assert(false, "TypeDetails not found!"); } -ref TypeDetails get_type_details(uint index) +ref immutable(TypeDetails) get_type_details(uint index) pure { - debug assert(index < num_type_details); - return type_details[index]; + auto tds = (cast(immutable(typeof(g_type_details)*) function() pure nothrow @nogc)&type_details)(); + debug assert(index < g_num_type_details); + return (*tds)[index]; } -enum TypeDetailsFor(T) = TypeDetails(UserTypeId!T, - T.sizeof, - T.alignof, - EmbedUserType!T, - // moveEmplace - is(T == class) ? null : (void* src, void* dst, bool move) { - if (move) - moveEmplace(*cast(T*)src, *cast(T*)dst); - else - *cast(T*)dst = *cast(const T*)src; - }, - // destroy - is(T == class) ? null : (void* val) { - destroy!false(*cast(T*)val); - }, - // stringify - (void* val, char[] buffer, bool format) { - import urt.string.format : toString; - if (format) - return toString(*cast(const T*)val, buffer); - else - { - static if (__traits(compiles, { buffer.parse!T(*cast(T*)val); })) - return buffer.parse!T(*cast(T*)val); - else - return -1; - } - }, - // cmp - (const void* pa, const void* pb, int type) { - ref const T a = *cast(const T*)pa; - ref const T b = *cast(const T*)pb; - switch (type) - { - case 0: - static if (__traits(compiles, { a.opCmp(b); })) - return a.opCmp(b); - else - return a < b ? -1 : a > b ? 1 : 0; - case 1: - return a == b ? 1 : 0; - case 2: - return a is b ? 1 : 0; - default: - assert(false); - } - }); +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(T == class) && 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 format) nothrow @nogc + { + import urt.string.format : toString; + if (format) + { + static if (is(typeof(toString!T))) + return toString(*cast(const T*)val, buffer); + 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 + return a < b ? -1 : a > b ? 1 : 0; + } + 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); +} From d1b1cfea3b369cc5f17ea73b2f7894fada4153ae Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 5 Sep 2025 15:13:24 +1000 Subject: [PATCH 14/91] The body of Array.concat was just completely missing! --- src/urt/array.d | 82 ++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 8d9858c..b83a65a 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -288,7 +288,18 @@ nothrow @nogc: } // manipulation - ref Array!(T, EmbedCount) concat(Things...)(auto ref Things things); + ref Array!(T, EmbedCount) concat(Things...)(auto ref Things things) + { + reserve(_length + things.length); + static foreach (i; 0 .. things.length) + { + static if (is(T == class) || is(T == interface)) + ptr[_length++] = things[i]; + else + emplace!T(&ptr[_length++], forward!(things[i])); + } + return this; + } bool empty() const => _length == 0; @@ -318,26 +329,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 +356,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 +404,7 @@ nothrow @nogc: moveEmplace(ptr[i], ptr[i-1]); } destroy!false(ptr[--_length]); - return copy.move; + return copy; } } @@ -424,19 +414,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; } } From d2da65be3aca1384325b7cddab68adcd39eaee15 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 10 Sep 2025 17:41:25 +1000 Subject: [PATCH 15/91] Fix map range --- src/urt/map.d | 75 +++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/urt/map.d b/src/urt/map.d index 732fc79..97a6f99 100644 --- a/src/urt/map.d +++ b/src/urt/map.d @@ -292,13 +292,13 @@ 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); private: nothrow: @@ -657,9 +657,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 +898,7 @@ unittest assert(map.get(2) is null); } - // Iteration (opApply) + // Iteration (range) { TestAVLTree map; map.insert(3, 30); @@ -908,25 +906,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 +951,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 +972,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); From 760585b07c2c2bbffdb9883844b4ffd8a1cedb49 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 12 Sep 2025 10:01:56 +1000 Subject: [PATCH 16/91] Override accept inheriting blocking from listening socket. --- src/urt/socket.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/urt/socket.d b/src/urt/socket.d index e61e29c..304edee 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -285,6 +285,9 @@ Result accept(Socket socket, out Socket connection, InetAddress* connectingSocke return socket_getlasterror(); else if (connectingSocketAddress) *connectingSocketAddress = 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; } From cc271b9f6a3112bfbf4604908a140301a5048fd8 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 16 Sep 2025 13:57:28 +1000 Subject: [PATCH 17/91] Fixes to tstringz functions, which seemed to have 2 separate implementations! --- src/urt/mem/temp.d | 40 +++++++++++++------- src/urt/string/package.d | 80 +++++++++++----------------------------- 2 files changed, 47 insertions(+), 73 deletions(-) diff --git a/src/urt/mem/temp.d b/src/urt/mem/temp.d index aabe847..7c28cfc 100644 --- a/src/urt/mem/temp.d +++ b/src/urt/mem/temp.d @@ -16,11 +16,7 @@ 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 (alloc_offset + size > TempMemSize) alloc_offset = 0; @@ -73,17 +69,33 @@ 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 (alloc_offset + len + 1 > TempMemSize) - alloc_offset = 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 + alloc_offset; - 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'; - alloc_offset += 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; } diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 6a103d0..4a807e2 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -7,50 +7,12 @@ public import urt.string.tailstring; // seful string operations defined elsewhere public import urt.array : empty, popFront, popBack, takeFront, takeBack; public import urt.mem : strlen; +public import urt.mem.temp : tstringz, twstringz; +nothrow @nogc: -enum TempStringBufferLen = 1024; -enum TempStringMaxLen = TempStringBufferLen / 2; -static char[TempStringBufferLen] s_tempStringBuffer; -static size_t s_tempStringBufferPos = 0; - - -char[] allocTempString(size_t len) nothrow @nogc -{ - assert(len <= TempStringMaxLen); - - if (len <= TempStringBufferLen - s_tempStringBufferPos) - { - char[] s = s_tempStringBuffer[s_tempStringBufferPos .. s_tempStringBufferPos + len]; - s_tempStringBufferPos += len; - return s; - } - 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; -} - -wchar* twstringz(const(char)[] str) nothrow @nogc -{ - wchar[] buffer = cast(wchar[])allocTempString((str.length + 1) * 2); - - // TODO: actually decode UTF8 into UTF16!! - - foreach (i, c; str) - buffer[i] = c; - buffer[str.length] = 0; - return buffer.ptr; -} - -ptrdiff_t cmp(const(char)[] a, const(char)[] b) pure nothrow @nogc +ptrdiff_t cmp(const(char)[] a, const(char)[] b) pure { if (a.length != b.length) return a.length - b.length; @@ -63,7 +25,7 @@ ptrdiff_t cmp(const(char)[] a, const(char)[] b) pure nothrow @nogc return 0; } -ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure nothrow @nogc +ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure { if (a.length != b.length) return a.length - b.length; @@ -76,24 +38,24 @@ ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure nothrow @nogc return 0; } -bool ieq(const(char)[] a, const(char)[] b) pure nothrow @nogc +bool ieq(const(char)[] a, const(char)[] b) pure => icmp(a, b) == 0; -bool startsWith(const(char)[] s, const(char)[] prefix) pure nothrow @nogc +bool startsWith(const(char)[] s, const(char)[] prefix) pure { if (s.length < prefix.length) return false; return s[0 .. prefix.length] == prefix[]; } -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[]; } -inout(char)[] trim(bool Front = true, bool Back = true)(inout(char)[] s) pure nothrow @nogc +inout(char)[] trim(bool Front = true, bool Back = true)(inout(char)[] s) pure { size_t first = 0, last = s.length; static if (Front) @@ -113,7 +75,7 @@ alias trimFront = trim!(true, false); alias trimBack = trim!(false, true); -inout(char)[] trimComment(char Delimiter)(inout(char)[] s) +inout(char)[] trimComment(char Delimiter)(inout(char)[] s) pure { size_t i = 0; for (; i < s.length; ++i) @@ -126,7 +88,7 @@ inout(char)[] trimComment(char Delimiter)(inout(char)[] s) 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,7 +110,7 @@ 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 Separator, bool HandleQuotes = true)(ref inout(char)[] s) pure { static if (HandleQuotes) int inQuotes = 0; @@ -176,7 +138,7 @@ inout(char)[] split(char Separator, bool HandleQuotes = true)(ref inout(char)[] return t; } -inout(char)[] split(Separator...)(ref inout(char)[] s, out char sep) +inout(char)[] split(Separator...)(ref inout(char)[] s, out char sep) pure { sep = '\0'; int inQuotes = 0; @@ -204,7 +166,7 @@ inout(char)[] split(Separator...)(ref inout(char)[] s, out char sep) return t; } -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 +185,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,13 +228,13 @@ 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 : is_power_of_2; assert(group.is_power_of_2); @@ -307,7 +269,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 +281,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,7 +291,7 @@ unittest } -bool wildcardMatch(const(char)[] wildcard, const(char)[] value) +bool wildcardMatch(const(char)[] wildcard, const(char)[] value) pure { // TODO: write this function... From d225b3ac22fc768cd7ef882844bbc613e61ca778 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 15 Sep 2025 19:31:52 +1000 Subject: [PATCH 18/91] Properly detect integer variants fed via float (like numbers from command line). --- src/urt/math.d | 82 ++++++++++++++++++++++++++++++++++++++++++ src/urt/meta/package.d | 15 ++++++++ src/urt/variant.d | 32 ++++++++++++++--- 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/src/urt/math.d b/src/urt/math.d index 66217d9..3c66be8 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -3,6 +3,9 @@ module urt.math; import urt.intrinsic; import core.stdc.stdio; // For writeDebugf +// 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; @@ -63,6 +66,85 @@ extern(C) double acos(double x); } +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); +} pragma(inline, true) bool addc(T = uint)(T a, T b, out T r, bool c_in) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index 19f10f3..599d6c9 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -1,11 +1,26 @@ module urt.meta; +pure nothrow @nogc: alias Alias(alias a) = a; alias Alias(T) = T; alias AliasSeq(TList...) = TList; +ulong bit_mask(size_t bits) +{ + return (1UL << bits) - 1; +} + +template bit_mask(size_t bits, bool signed = false) +{ + 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 IntForWidth(size_t bits, bool signed = false) { static if (bits <= 8 && !signed) diff --git a/src/urt/variant.d b/src/urt/variant.d index e95a557..52c004c 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -119,11 +119,35 @@ nothrow @nogc: this(F)(F f) if (is_some_float!F) { - static if (is(F == float)) - flags = Flags.NumberFloat; + 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 - flags = Flags.NumberDouble; - value.d = f; + { + static if (is(F == float)) + flags = Flags.NumberFloat; + else + flags = Flags.NumberDouble; + value.d = f; + } } this(E)(E e) From c54e6da75611e005f751984bf7e6ab5cd2188646 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 23 Sep 2025 02:18:28 +1000 Subject: [PATCH 19/91] Array was missing slice-assign operators. Added remove-range function. --- src/urt/array.d | 63 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index b83a65a..1ef1377 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -273,6 +273,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); @@ -452,8 +454,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 @@ -461,13 +462,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)); } @@ -504,31 +529,43 @@ 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(i[1] <= _length && i[1] - i[0] == rh.length, "Range error"); + ptr[i[0] .. i[1]] = rh[]; + } + + size_t[2] opSlice(size_t dim : 0)(size_t x, size_t y) const pure { debug assert(y <= _length, "Range error"); - return [cast(uint)x, cast(uint)y]; + return [x, y]; } void opOpAssign(string op : "~", U)(auto ref U el) @@ -631,14 +668,14 @@ private: 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) @@ -659,7 +696,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; From a7690671588b4e19f462b04b6fcb3c0389ef5517 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 25 Sep 2025 09:02:19 +1000 Subject: [PATCH 20/91] Added comparison functions for InetAddress --- src/urt/inet.d | 155 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 3 deletions(-) diff --git a/src/urt/inet.d b/src/urt/inet.d index 4b41981..9f90c55 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -208,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; @@ -428,8 +440,11 @@ nothrow @nogc: IPv6Addr r; int i, j = prefixLen / 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 - (prefixLen % 16))); + while (i < 8) r.s[i++] = 0; + } return r; } @@ -449,6 +464,11 @@ nothrow @nogc: return true; } + IPv6Addr getNetwork(IPv6Addr ip) const pure + => ip & netMask(); + IPv6Addr getLocal(IPv6Addr ip) const pure + => ip & ~netMask(); + size_t toHash() const pure => addr.toHash() ^ prefixLen; @@ -532,11 +552,57 @@ nothrow @nogc: this._a.ipv4.port = port; } - this(IPv6Addr addr, ushort port) + this(IPv6Addr addr, ushort port, int flowInfo = 0, uint scopeId = 0) { family = AddressFamily.IPv6; this._a.ipv6.addr = addr; this._a.ipv6.port = port; + this._a.ipv6.flowInfo = flowInfo; + this._a.ipv6.scopeId = scopeId; + } + + 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.flowInfo == rhs._a.ipv6.flowInfo) + return _a.ipv6.scopeId - rhs._a.ipv6.scopeId; + return _a.ipv6.flowInfo - rhs._a.ipv6.flowInfo; + } + return _a.ipv6.port - rhs._a.ipv6.port; + default: + return 0; + } + return 0; } size_t toHash() const pure @@ -651,8 +717,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(IPSubnet(IPAddr(192, 168, 0, 0), 24).getNetwork(IPAddr(192, 168, 0, 10)) == IPAddr(192, 168, 0, 0)); + assert(IPSubnet(IPAddr(192, 168, 0, 0), 24).getLocal(IPAddr(192, 168, 0, 10)) == 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"); @@ -671,8 +741,13 @@ unittest assert(subnet.fromString("0.0.0.0/0") == 9 && subnet == IPSubnet(IPAddr(0, 0, 0, 0), 0)); 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(IPv6Subnet(IPv6Addr.any, 64).getNetwork(IPv6Addr.loopback) == IPv6Addr.any); + assert(IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).getNetwork(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1)) == IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); + assert(IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).getLocal(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1)) == 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"); @@ -704,4 +779,78 @@ unittest // 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)); + + // 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"); + } + } } From 42c8de0b91dad846bce7cb22fadcf8b82b0ab76a Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 7 Oct 2025 15:35:48 +1000 Subject: [PATCH 21/91] Flesh out unicode implementation, including functions to perform case-insensitive comparison. --- src/urt/array.d | 60 +++- src/urt/mem/temp.d | 12 + src/urt/socket.d | 2 +- src/urt/string/package.d | 230 ++++++++++-- src/urt/string/uni.d | 755 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 1017 insertions(+), 42 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 1ef1377..702c861 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; 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) diff --git a/src/urt/mem/temp.d b/src/urt/mem/temp.d index 7c28cfc..f470dff 100644 --- a/src/urt/mem/temp.d +++ b/src/urt/mem/temp.d @@ -118,6 +118,18 @@ char[] tstring(T)(auto ref T value) return result; } +dchar[] tdstring(T)(auto ref T value) nothrow @nogc +{ + 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]; +} + char[] tconcat(Args...)(ref Args args) { import urt.string.format : concat; diff --git a/src/urt/socket.d b/src/urt/socket.d index 304edee..f27664c 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -746,7 +746,7 @@ Result get_hostname(char* name, size_t len) 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(':'); diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 4a807e2..3210e03 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -1,58 +1,218 @@ 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; +public import urt.mem : strlen, wcslen; public import urt.mem.temp : tstringz, twstringz; nothrow @nogc: -ptrdiff_t cmp(const(char)[] a, const(char)[] b) pure +ptrdiff_t cmp(bool case_insensitive = false, T, U)(const(T)[] a, const(U)[] b) pure { - if (a.length != b.length) - return a.length - b.length; - for (size_t i = 0; i < a.length; ++i) + static if (case_insensitive) + return uni_compare_i(a, b); + else { - ptrdiff_t diff = a[i] - b[i]; - if (diff) - return diff; + static if (is(T == U)) + { + if (a.length != b.length) + return a.length - b.length; + } + return uni_compare(a, b); } - return 0; } -ptrdiff_t icmp(const(char)[] a, const(char)[] b) pure +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; + +size_t findFirst(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 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: 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; + + size_t i = 0; + while (i < s.length) { - ptrdiff_t diff = to_lower(a[i]) - to_lower(b[i]); - if (diff) - return diff; + 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 0; + return i; } -bool ieq(const(char)[] a, const(char)[] b) pure - => icmp(a, b) == 0; +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) +{ + 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) + { + 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 s.length; +} + +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 (t.length == 0) + return 0; + + // fast-path for one-length tokens + size_t l = t.uni_seq_len(); + if (l == t.length) + { + 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); + } + + 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"); +} + +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 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 { 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 @@ -305,3 +465,31 @@ bool wildcardMatch(const(char)[] wildcard, const(char)[] value) pure } return wildcard.length == value.length; } + + +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")); +} diff --git a/src/urt/string/uni.d b/src/urt/string/uni.d index bc3e44c..ce7f7d1 100644 --- a/src/urt/string/uni.d +++ b/src/urt/string/uni.d @@ -1,7 +1,142 @@ 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 uni_seq_len(const(char)[] s) +{ + if (s.length == 0) + return 0; + if (s[0] < 0x80) // 1-byte sequence: 0xxxxxxx + return 1; + else if ((s[0] & 0xE0) == 0xC0) // 2-byte sequence: 110xxxxx 10xxxxxx + return (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + else if ((s[0] & 0xF0) == 0xE0) // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx + return (s.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : + (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + else if ((s[0] & 0xF8) == 0xF0) // 4-byte sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + return (s.length >= 4 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) ? 4 : + (s.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : + (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + return 1; // Invalid UTF-8 sequence +} + +size_t uni_seq_len(const(wchar)[] s) +{ + if (s.length == 0) + return 0; + if (s[0] >= 0xD800 && s[0] < 0xDC00 && s.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) + => s.length > 0; + +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) { @@ -242,10 +377,504 @@ size_t uni_convert(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 (p1 < p1end && p2 < p2end) + { + dchar a = *p1; + if (a < 0x80) + { + dchar b = *p2; + if (a != b) + { + if (b >= 0x80) + { + size_t _; + b = next_dchar(p2[0 .. p2end - p2], _); + } + return cast(int)a - cast(int)b; + } + ++p1; + ++p2; + } + else + { + size_t al, bl; + a = next_dchar(p1[0 .. p1end - p1], al); + dchar b = next_dchar(p2[0 .. p2end - p2], bl); + if (a != b) + return cast(int)a - cast(int)b; + p1 += al; + p2 += al; + } + } + + // 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; +} + +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 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" ~ @@ -272,5 +901,127 @@ unittest // 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); +} From 78124e8a4511921f495e05e75d01ff0ca2934ead Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 14 Oct 2025 10:47:54 +1000 Subject: [PATCH 22/91] Several socket/inet improvements. --- src/urt/inet.d | 94 ++++++++-- src/urt/socket.d | 447 +++++++++++++++++++++++++++++++---------------- 2 files changed, 375 insertions(+), 166 deletions(-) diff --git a/src/urt/inet.d b/src/urt/inet.d index 9f90c55..e4c6d1a 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -35,7 +35,7 @@ enum WellKnownPort : ushort } 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 IPv6Addr IPv6AddrLit(string addr) = () { IPv6Addr a; size_t taken = a.fromString(addr); assert(taken == addr.length, "Not an IPv6 address"); return a; }(); struct IPAddr { @@ -313,9 +313,45 @@ nothrow @nogc: 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_int(&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; } @@ -498,7 +534,7 @@ nothrow @nogc: return -1; size_t t; ulong plen = s[taken..$].parse_int(&t); - if (t == 0 || plen > 32) + if (t == 0 || plen > 128) return -1; addr = a; prefixLen = cast(ubyte)plen; @@ -679,7 +715,7 @@ nothrow @nogc: { size_t t; ulong p = s[++taken..$].parse_int(&t); - if (t == 0 || p > 0xFFFF) + if (t == 0 || p > ushort.max) return -1; taken += t; port = cast(ushort)p; @@ -739,6 +775,9 @@ unittest 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)); + 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)); @@ -755,17 +794,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)); + 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 .. 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)); + 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)); + 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"); @@ -777,8 +839,10 @@ 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 { diff --git a/src/urt/socket.d b/src/urt/socket.d index 304edee..7123163 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -20,6 +20,9 @@ version (Windows) version = HasIPv6; alias SocketHandle = SOCKET; + + enum IPV6_RECVPKTINFO = 49; + enum IPV6_PKTINFO = 50; } else version (Posix) { @@ -28,7 +31,7 @@ else version (Posix) 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, @@ -116,12 +119,14 @@ enum SocketOption : ubyte multicast = first_ip_option, multicast_loopback, multicast_ttl, + ip_pktinfo, // IPv6 options first_ipv6_option, + ipv6_pktinfo = first_ipv6_option, // ICMP options - first_icmp_option = first_ipv6_option, + first_icmp_option, // ICMPv6 options first_icmpv6_option = first_icmp_option, @@ -134,7 +139,6 @@ enum SocketOption : ubyte tcp_keep_alive, // Apple: similar to KeepIdle tcp_no_delay, - // UDP options first_udp_option, } @@ -195,6 +199,7 @@ Result create_socket(AddressFamily af, SocketType type, Protocol proto, out Sock socket.handle = .socket(s_addressFamily[af], s_socketType[type], s_protocol[proto]); if (socket == Socket.invalid) return socket_getlasterror(); + return Result.success; } @@ -247,10 +252,10 @@ 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"); + sockaddr* sock_addr = make_sockaddr(address, buffer, addrLen); + assert(sock_addr, "Invalid socket address"); - if (_bind(socket.handle, sockAddr, cast(int)addrLen) < 0) + if (_bind(socket.handle, sock_addr, cast(int)addrLen) < 0) return socket_getlasterror(); return Result.success; } @@ -266,15 +271,15 @@ 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"); + sockaddr* sock_addr = make_sockaddr(address, buffer, addrLen); + assert(sock_addr, "Invalid socket address"); - if (_connect(socket.handle, sockAddr, cast(int)addrLen) < 0) + if (_connect(socket.handle, sock_addr, cast(int)addrLen) < 0) return socket_getlasterror(); 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,15 +288,21 @@ 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); + 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* bytesSent = null) +Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, size_t* bytes_sent = null) { Result r = Result.success; @@ -301,43 +312,43 @@ Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none r = socket_getlasterror(); sent = 0; } - if (bytesSent) - *bytesSent = sent; + if (bytes_sent) + *bytes_sent = sent; return r; } -Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytesSent = null) +Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytes_sent = null) { ubyte[sockaddr_storage.sizeof] tmp = void; size_t addrLen; - sockaddr* sockAddr = null; + sockaddr* sock_addr = null; if (address) { - sockAddr = make_sockaddr(*address, tmp, addrLen); - assert(sockAddr, "Invalid socket address"); + sock_addr = make_sockaddr(*address, tmp, addrLen); + 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); + ptrdiff_t sent = _sendto(socket.handle, message.ptr, cast(int)message.length, map_message_flags(flags), sock_addr, cast(int)addrLen); if (sent < 0) { r = socket_getlasterror(); sent = 0; } - if (bytesSent) - *bytesSent = sent; + if (bytes_sent) + *bytes_sent = sent; return r; } -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; 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 @@ -362,30 +373,89 @@ Result recv(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.none, size_t 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 = 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 - 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) @@ -393,9 +463,9 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval Result r = Result.success; // check the option appears to be the proper datatype - const OptInfo* optInfo = &s_socketOptions[option]; - assert(optInfo.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); - assert(optlen == s_optTypeRtSize[optInfo.rt_type], "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 @@ -423,7 +493,7 @@ 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.platform_type == OptType.unsupported) +// if (opt_info.platform_type == OptType.unsupported) // return r; // } @@ -436,15 +506,15 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval const(void)* arg = optval; int itmp = void; linger ling = void; - if (optInfo.rt_type != optInfo.platform_type) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.rt_type) + switch (opt_info.rt_type) { // TODO: there are more converstions necessary as options/platforms are added case OptType.bool_: { const bool value = *cast(const(bool)*)optval; - switch (optInfo.platform_type) + switch (opt_info.platform_type) { case OptType.int_: itmp = value ? 1 : 0; @@ -457,7 +527,7 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval case OptType.duration: { const Duration value = *cast(const(Duration)*)optval; - switch (optInfo.platform_type) + switch (opt_info.platform_type) { case OptType.seconds: itmp = cast(int)value.as!"seconds"; @@ -482,53 +552,53 @@ 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.platform_type]); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.bool_, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.int_, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.duration, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.inet_addr, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.multicast_group, "Incorrect value type for option"); + assert(opt_info.rt_type == OptType.multicast_group, "Incorrect value type for option"); return set_socket_option(socket, option, &value, MulticastGroup.sizeof); } @@ -537,9 +607,9 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ Result r = Result.success; // check the option appears to be the proper datatype - const OptInfo* optInfo = &s_socketOptions[option]; - assert(optInfo.rt_type != OptType.unsupported, "Socket option is unsupported on this platform!"); - assert(outputlen == s_optTypeRtSize[optInfo.rt_type], "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.non_blocking, "Socket option NonBlocking cannot be get"); @@ -552,9 +622,9 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ void* arg = output; int itmp = 0; linger ling = { 0, 0 }; - if (optInfo.rt_type != optInfo.platform_type) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.platform_type) + switch (opt_info.platform_type) { case OptType.int_: case OptType.seconds: @@ -573,19 +643,19 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ } } - socklen_t writtenLen = s_optTypePlatformSize[optInfo.platform_type]; + 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.rt_type != optInfo.platform_type) + if (opt_info.rt_type != opt_info.platform_type) { - switch (optInfo.rt_type) + switch (opt_info.rt_type) { // TODO: there are more converstions necessary as options/platforms are added case OptType.bool_: { bool* value = cast(bool*)output; - switch (optInfo.platform_type) + switch (opt_info.platform_type) { case OptType.int_: *value = !!itmp; @@ -597,7 +667,7 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ case OptType.duration: { Duration* value = cast(Duration*)output; - switch (optInfo.platform_type) + switch (opt_info.platform_type) { case OptType.seconds: *value = seconds(itmp); @@ -617,10 +687,10 @@ Result get_socket_option(Socket socket, SocketOption option, void* output, size_ } } - assert(optInfo.rt_type != OptType.inet_addr, "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.rt_type) + switch (opt_info.rt_type) { case OptType.INAddress: { @@ -637,37 +707,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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.bool_, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.int_, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.duration, "Incorrect value type for option"); + 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.rt_type == OptType.unsupported) + const OptInfo* opt_info = &s_socketOptions[option]; + if (opt_info.rt_type == OptType.unsupported) return InternalResult.unsupported; - assert(optInfo.rt_type == OptType.inet_addr, "Incorrect value type for option"); + assert(opt_info.rt_type == OptType.inet_addr, "Incorrect value type for option"); return get_socket_option(socket, option, &output, IPAddr.sizeof); } @@ -978,7 +1048,7 @@ SocketResult socket_result(Result result) sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_t addrLen) { - sockaddr* sockAddr = cast(sockaddr*)buffer.ptr; + sockaddr* sock_addr = cast(sockaddr*)buffer.ptr; switch (address.family) { @@ -988,7 +1058,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ 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]; version (Windows) @@ -1013,7 +1083,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ 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]; storeBigEndian(&ain6.sin6_port, cast(ushort)address._a.ipv6.port); @@ -1041,7 +1111,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ // 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]; // @@ -1053,7 +1123,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ } default: { - sockAddr = null; + sock_addr = null; addrLen = 0; assert(false, "Unsupported address family"); @@ -1061,52 +1131,33 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ } } - 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: { - 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: { 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.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!"); @@ -1116,7 +1167,7 @@ InetAddress make_InetAddress(const(sockaddr)* sockAddress) { // 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) @@ -1134,6 +1185,37 @@ 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: @@ -1277,6 +1359,8 @@ version (Windows) // BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA 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 ), @@ -1299,6 +1383,8 @@ else version (linux) // BS_NETWORK_WINDOWS_VERSION >= _WIN32_WINNT_VISTA 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_ ), @@ -1321,6 +1407,8 @@ else version (Darwin) 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 ), @@ -1379,6 +1467,28 @@ version (Windows) WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &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_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_WSARECVMSG, cast(uint)GUID.sizeof, + &WSARecvMsg, cast(uint)WSARecvMsgFn.sizeof, &bytes, null, null) != 0) + goto FAIL; + assert(bytes == WSARecvMsgFn.sizeof); + dummy.close(); + if (!WSARecvMsg) + goto FAIL; + return; + + FAIL: + import urt.log; + writeWarning("Failed to get WSARecvMsg function pointer - recvfrom() won't be able to report the dst address"); } pragma(crt_destructor) @@ -1397,43 +1507,78 @@ 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 { - SOCKET fd; // Socket handle - SHORT events; // Requested events to monitor - SHORT revents; // Returned events indicating status + in_addr imr_multiaddr; + in_addr imr_interface; } - 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 + + struct WSAMSG + { + LPSOCKADDR name; + int namelen; + LPWSABUF lpBuffers; + uint dwBufferCount; + WSABUF Control; + uint dwFlags; } + alias LPWSAMSG = WSAMSG*; - extern(Windows) int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout); + struct WSABUF + { + uint len; + char* buf; + } + alias LPWSABUF = WSABUF*; - struct ip_mreq + alias WSARecvMsgFn = extern(Windows) int function(SOCKET s, LPWSAMSG lpMsg, uint* lpdwNumberOfBytesRecvd, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + __gshared WSARecvMsgFn WSARecvMsg; + + 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); } From bf85e900f6de66d072e340bcce29b75784758439 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 19 Oct 2025 14:23:38 +1000 Subject: [PATCH 23/91] Renamed IPSubnet to IPNetworkAddress (because it stores a full address!) Also snake_case fixes, and some other minor tweaks. --- src/urt/inet.d | 242 ++++++++++++++++++++++++----------------------- src/urt/socket.d | 38 ++++---- 2 files changed, 143 insertions(+), 137 deletions(-) diff --git a/src/urt/inet.d b/src/urt/inet.d index e4c6d1a..b2d37bb 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 @@ -114,10 +114,10 @@ nothrow @nogc: return fnv1a(b[]); } - 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[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++) { @@ -126,7 +126,7 @@ nothrow @nogc: offset += b[i].format_int(tmp[offset..$]); } - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -190,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 @@ -228,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; @@ -253,12 +253,12 @@ nothrow @nogc: 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++) { @@ -269,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; } } @@ -288,11 +288,11 @@ 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; @@ -365,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; @@ -391,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.format_int(tmp[offset..$]); + offset += prefix_len.format_int(tmp[offset..$]); - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -440,7 +440,7 @@ nothrow @nogc: if (t == 0 || plen > 32) return -1; addr = a; - prefixLen = cast(ubyte)plen; + prefix_len = cast(ubyte)plen; return taken + t; } @@ -453,32 +453,32 @@ 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; if (j < 8) { - r.s[i++] = cast(ushort)(0xFFFF << (16 - (prefixLen % 16))); + r.s[i++] = cast(ushort)(0xFFFF << (16 - (prefix_len % 16))); while (i < 8) r.s[i++] = 0; } return r; @@ -486,38 +486,38 @@ nothrow @nogc: 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 getNetwork(IPv6Addr ip) const pure - => ip & netMask(); - IPv6Addr getLocal(IPv6Addr ip) const pure - => ip & ~netMask(); + 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.format_int(tmp[offset..$]); + offset += prefix_len.format_int(tmp[offset..$]); - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -537,7 +537,7 @@ nothrow @nogc: if (t == 0 || plen > 128) return -1; addr = a; - prefixLen = cast(ubyte)plen; + prefix_len = cast(ubyte)plen; return taken + t; } @@ -569,7 +569,7 @@ nothrow @nogc: { IPv6Addr addr; ushort port; - uint flowInfo; + uint flow_info; uint scopeId; } struct Addr @@ -583,20 +583,26 @@ 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, int flowInfo = 0, uint scopeId = 0) + 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.flowInfo = flowInfo; + 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; @@ -606,9 +612,9 @@ nothrow @nogc: return false; switch (family) { - case AddressFamily.IPv4: + case AddressFamily.ipv4: return _a.ipv4 == rhs._a.ipv4; - case AddressFamily.IPv6: + case AddressFamily.ipv6: return _a.ipv6 == rhs._a.ipv6; default: return true; @@ -621,18 +627,18 @@ nothrow @nogc: return family < rhs.family ? -1 : 1; switch (family) { - case AddressFamily.IPv4: + 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: + 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.flowInfo == rhs._a.ipv6.flowInfo) + if (_a.ipv6.flow_info == rhs._a.ipv6.flow_info) return _a.ipv6.scopeId - rhs._a.ipv6.scopeId; - return _a.ipv6.flowInfo - rhs._a.ipv6.flowInfo; + return _a.ipv6.flow_info - rhs._a.ipv6.flow_info; } return _a.ipv6.port - rhs._a.ipv6.port; default: @@ -643,19 +649,19 @@ nothrow @nogc: 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++] = ':'; @@ -670,7 +676,7 @@ nothrow @nogc: offset += _a.ipv6.port.format_int(tmp[offset..$]); } - if (buffer.ptr && tmp.ptr == stackBuffer.ptr) + if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { if (buffer.length < offset) return -1; @@ -689,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) @@ -723,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; @@ -732,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; @@ -756,9 +762,9 @@ unittest 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(IPSubnet(IPAddr(192, 168, 0, 0), 24).getNetwork(IPAddr(192, 168, 0, 10)) == IPAddr(192, 168, 0, 0)); - assert(IPSubnet(IPAddr(192, 168, 0, 0), 24).getLocal(IPAddr(192, 168, 0, 10)) == IPAddr(0, 0, 0, 10)); + 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"); @@ -769,12 +775,12 @@ 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); @@ -783,10 +789,10 @@ unittest 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(IPv6Subnet(IPv6Addr.any, 64).getNetwork(IPv6Addr.loopback) == IPv6Addr.any); - assert(IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).getNetwork(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1)) == IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)); - assert(IPv6Subnet(IPv6Addr(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).getLocal(IPv6Addr(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1)) == IPv6Addr(0, 0, 0, 1, 0, 0, 0, 1)); + 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"); @@ -819,12 +825,12 @@ unittest assert(addr6.fromString("2001:db8::1/24") == 11 && 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"); + 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"); - 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)); + 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); @@ -895,13 +901,13 @@ unittest // InetAddress sorting tests { InetAddress[10] expected = [ - // IPv4 sorted first + // 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 + // 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), diff --git a/src/urt/socket.d b/src/urt/socket.d index 52bf820..7902c74 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -194,7 +194,7 @@ 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) @@ -500,7 +500,7 @@ Result set_socket_option(Socket socket, SocketOption option, const(void)* optval // 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; @@ -1052,7 +1052,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ switch (address.family) { - case AddressFamily.IPv4: + case AddressFamily.ipv4: { addrLen = sockaddr_in.sizeof; if (buffer.length < sockaddr_in.sizeof) @@ -1060,7 +1060,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ 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]; @@ -1075,7 +1075,7 @@ 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) { @@ -1085,9 +1085,9 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ 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) { @@ -1103,7 +1103,7 @@ 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) // { @@ -1113,7 +1113,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ // // 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; @@ -1140,7 +1140,7 @@ InetAddress make_InetAddress(const(sockaddr)* sock_address) 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)*)sock_address; @@ -1148,14 +1148,14 @@ InetAddress make_InetAddress(const(sockaddr)* sock_address) 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)*)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); addr._a.ipv6.addr = make_IPv6Addr(ain6.sin6_addr); } @@ -1163,7 +1163,7 @@ InetAddress make_InetAddress(const(sockaddr)* sock_address) assert(false, "Platform does not support IPv6!"); break; } - case AddressFamily.Unix: + case AddressFamily.unix: { // version (HasUnixSocket) // { @@ -1267,15 +1267,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 = [ @@ -1475,7 +1475,7 @@ version (Windows) Socket dummy; uint bytes = 0; - if (!create_socket(AddressFamily.IPv4, SocketType.datagram, Protocol.udp, dummy)) + if (!create_socket(AddressFamily.ipv4, SocketType.datagram, Protocol.udp, dummy)) goto FAIL; 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) From e345a97b992e6335c27c212c3b8193b2869b8b72 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 30 Oct 2025 11:09:39 +1000 Subject: [PATCH 24/91] Fix a really dumb bug. --- src/urt/system.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urt/system.d b/src/urt/system.d index 2bb58ec..903ce2a 100644 --- a/src/urt/system.d +++ b/src/urt/system.d @@ -99,7 +99,7 @@ void set_system_idle_params(IdleParams params) 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) { From 970bcaaef41c559d9b54a1f6ee459db76dc5c62a Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 3 Nov 2025 21:52:37 +1000 Subject: [PATCH 25/91] Added ascii strcmp/icmp --- src/urt/string/ascii.d | 44 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/urt/string/ascii.d b/src/urt/string/ascii.d index 9edab53..36f814f 100644 --- a/src/urt/string/ascii.d +++ b/src/urt/string/ascii.d @@ -67,16 +67,20 @@ char to_upper(char c) pure char[] to_lower(const(char)[] str, char[] buffer) pure { + if (buffer.length < str.length) + return null; foreach (i; 0 .. str.length) - buffer[i] = to_lower(str[i]); - return buffer; + buffer[i] = str[i].to_lower; + return buffer[0..str.length]; } char[] to_upper(const(char)[] str, char[] buffer) pure { + if (buffer.length < str.length) + return null; foreach (i; 0 .. str.length) - buffer[i] = to_upper(str[i]); - return buffer; + buffer[i] = str[i].to_upper; + return buffer[0..str.length]; } char[] to_lower(char[] str) pure @@ -88,3 +92,35 @@ char[] to_upper(char[] str) pure { 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; From 5c0fb18a01229ffcc261205548d3a093d15bfb15 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 3 Nov 2025 22:00:13 +1000 Subject: [PATCH 26/91] Added toString/fromString for common time types. Improved precision for stringification down to nanoseconds. Added unit tests. --- src/urt/time.d | 377 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 344 insertions(+), 33 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index d1d0e00..b8dd4be 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -107,17 +107,33 @@ pure nothrow @nogc: } else { - long ms = (ticks != 0 ? appTime(this) : Duration()).as!"msecs"; + long ns = (ticks != 0 ? appTime(this) : Duration()).as!"nsecs"; if (!buffer.ptr) - return 2 + timeToString(ms, null); + return 2 + timeToString(ns, null); if (buffer.length < 2) return -1; buffer[0..2] = "T+"; - ptrdiff_t len = timeToString(ms, buffer[2..$]); + 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 { debug @@ -191,7 +207,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 / nsecMultiplier; + return offset; } auto __debugOverview() const @@ -290,15 +397,6 @@ pure nothrow @nogc: import urt.conv : format_int; size_t offset = 0; - uint y = year; - if (year <= 0) - { - if (buffer.length < 3) - return -1; - y = -year + 1; - buffer[0 .. 3] = "BC "; - offset += 3; - } ptrdiff_t len = year.format_int(buffer[offset..$]); if (len < 0 || len == buffer.length) return -1; @@ -313,7 +411,7 @@ pure nothrow @nogc: if (len < 0 || len == buffer.length) return -1; offset += len; - buffer[offset++] = ' '; + buffer[offset++] = 'T'; len = hour.format_int(buffer[offset..$], 10, 2, '0'); if (len < 0 || len == buffer.length) return -1; @@ -325,15 +423,149 @@ pure nothrow @nogc: offset += len; buffer[offset++] = ':'; len = second.format_int(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) + if (len < 0) return -1; offset += len; - buffer[offset++] = '.'; - len = (ns / 1_000_000).format_int(buffer[offset..$], 10, 3, '0'); - if (len < 0) - return len; + if (ns) + { + if (len == buffer.length) + return -1; + buffer[offset++] = '.'; + uint nsecs = ns; + uint m = 0; + while (nsecs) + { + if (len == buffer.length) + return -1; + int digit = nsecs / digit_multipliers[m]; + buffer[offset++] = cast(char)('0' + digit); + nsecs -= digit * digit_multipliers[m++]; + } + } return offset + len; } + + ptrdiff_t fromString(const(char)[] s) + { + import urt.conv : parse_int; + import urt.string.ascii : ieq, is_numeric, is_whitespace, to_lower; + + if (s.length < 14) + return -1; + size_t offset = 0; + + // parse year + if (s[0..2].ieq("bc")) + { + offset = 2; + if (s[2] == ' ') + ++offset; + } + if (s[offset] == '+') + return -1; + 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; + + if (s[offset] != '-' && s[offset] != '/') + return -1; + + // parse month + value = s[++offset..$].parse_int(&len); + if (len == 0) + { + 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; + + if (s[offset] != '-' && s[offset] != '/') + return -1; + + // parse day + value = s[++offset..$].parse_int(&len); + if (len == 0 || value < 1 || value > 31) + return -1; + day = cast(ubyte)value; + offset += len; + + if (offset >= s.length || (s[offset] != 'T' && s[offset] != ' ')) + return -1; + + // parse hour + value = s[++offset..$].parse_int(&len); + if (len != 2 || value < 0 || value > 23) + return -1; + hour = cast(ubyte)value; + offset += len; + + if (offset >= s.length || s[offset++] != ':') + return -1; + + // 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 || s[offset++] != ':') + return -1; + + // parse second + value = s[offset..$].parse_int(&len); + if (len != 2 || value < 0 || value > 59) + return -1; + second = cast(ubyte)value; + offset += len; + + ns = 0; + 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 -1; // no number after the dot + } + + if (offset < s.length && s[offset].to_lower == 'z') + { + ++offset; + // TODO: UTC timezone designator... + assert(false, "TODO: we need to know our local timezone..."); + } + + return offset; + } } Duration dur(string base)(long value) pure @@ -403,6 +635,10 @@ SysTime getSysTime() } } +SysTime getSysTime(DateTime time) pure +{ + assert(false, "TODO: convert to SysTime..."); +} DateTime getDateTime() { @@ -459,6 +695,9 @@ 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; @@ -517,22 +756,38 @@ package(urt) void initClock() static assert(false, "TODO"); } -ptrdiff_t timeToString(long ms, char[] buffer) pure +ptrdiff_t timeToString(long ns, char[] buffer) pure { import urt.conv : format_int; - long hr = ms / 3_600_000; + long hr = ns / 3_600_000_000_000; if (!buffer.ptr) - return hr.format_int(null, 10, 2, '0') + 10; + { + size_t tail = 6; + ns %= 1_000_000_000; + if (ns) + { + ++tail; + uint m = 0; + do + { + ++tail; + uint digit = cast(uint)(ns / digit_multipliers[m]); + ns -= digit * digit_multipliers[m++]; + } + while (ns); + } + return hr.format_int(null, 10, 2, '0') + tail; + } ptrdiff_t len = hr.format_int(buffer, 10, 2, '0'); - if (len < 0 || buffer.length < len + 10) + 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; + ubyte min = cast(ubyte)(ns / 60_000_000_000 % 60); + ubyte sec = cast(ubyte)(ns / 1_000_000_000 % 60); + ns %= 1_000_000_000; buffer.ptr[len++] = ':'; buffer.ptr[len++] = cast(char)('0' + (min / 10)); @@ -540,10 +795,21 @@ 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 (ns) + { + if (buffer.length < len + 2) + return -1; + buffer.ptr[len++] = '.'; + uint m = 0; + while (ns) + { + if (buffer.length < len + 1) + return -1; + uint digit = cast(uint)(ns / digit_multipliers[m]); + buffer.ptr[len++] = cast(char)('0' + digit); + ns -= digit * digit_multipliers[m++]; + } + } return len; } @@ -552,10 +818,55 @@ 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(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.") == -1); + assert(dt.fromString("2025-01-01") == -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); } From d5527350f783423107967fae2d3c6d8df375b07b Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 3 Nov 2025 22:08:21 +1000 Subject: [PATCH 27/91] Added support for `Duration` to `Variant`, which is a synonym for `Quantity!Nanoseconds`. Since both types are functionally equivalent, they can be interchanged. --- src/urt/si/unit.d | 1 + src/urt/variant.d | 48 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 1db2668..480ab98 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -66,6 +66,7 @@ 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; diff --git a/src/urt/variant.d b/src/urt/variant.d index 52c004c..a086c33 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -8,7 +8,8 @@ import urt.lifetime; import urt.map; import urt.mem.allocator; import urt.si.quantity; -import urt.si.unit : ScaledUnit; +import urt.si.unit : ScaledUnit, Second, Nanosecond; +import urt.time : Duration, dur; import urt.traits; nothrow @nogc: @@ -20,7 +21,8 @@ enum ValidUserType(T) = (is(T == struct) || is(T == class)) && !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); alias VariantKVP = KVP!(const(char)[], Variant); @@ -170,6 +172,13 @@ nothrow @nogc: } } + this(Duration dur) + { + this(dur.as!"nsecs"); + flags |= Flags.IsQuantity; + count = Nanosecond.pack; + } + this(const(char)[] s) // TODO: (S)(S s) // if (is(S : const(char)[])) { @@ -567,6 +576,8 @@ nothrow @nogc: => (flags & Flags.DoubleFlag) != 0; bool isQuantity() const pure => (flags & Flags.IsQuantity) != 0; + bool isDuration() const pure + => isQuantity && (count & 0xFFFFFF) == Second.pack; bool isString() const pure => (flags & Flags.IsString) != 0; bool isArray() const pure @@ -707,6 +718,35 @@ nothrow @nogc: return r; } + 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? + { + Quantity!float q; + q.value = asFloat; + q.unit.pack = count; + ns = cast(Nanoseconds)q; + } + else if (isDouble) + { + Quantity!double q; + q.value = asDouble; + q.unit.pack = count; + ns = cast(Nanoseconds)q; + } + else + { + Quantity!long q; + q.value = asLong; + q.unit.pack = count; + ns = q; + } + return ns.value.dur!"nsecs"; + } + const(char)[] asString() const pure { if (isNull) @@ -804,6 +844,10 @@ nothrow @nogc: { return asQuantity!U(); } + else static if (is(T == Duration)) + { + return asDuration; + } else static if (is(T : const(char)[])) { static if (is(T == struct)) // for String/MutableString/etc From 7b57ce8ca3a14a2d5f9dd70d761064b370bffde6 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 5 Nov 2025 22:16:42 +1000 Subject: [PATCH 28/91] Fixed format for Quantity and related types. --- src/urt/si/quantity.d | 5 +++-- src/urt/si/unit.d | 14 ++++++++------ src/urt/variant.d | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index c8da35e..880166a 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -291,7 +291,8 @@ nothrow @nogc: return r; } - ptrdiff_t toString(char[] buffer) const + import urt.string.format : FormatArg; + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const { import urt.conv : format_float; @@ -338,7 +339,7 @@ nothrow @nogc: if (u.pack) { - ptrdiff_t l2 = u.toString(buffer[l .. $]); + ptrdiff_t l2 = u.toString(buffer[l .. $], null, null); if (l2 < 0) return l2; l += l2; diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 480ab98..1b98a6a 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -108,8 +108,8 @@ nothrow: // debug/ctfe helper string toString() pure { - char[32] t; - ptrdiff_t l = toString(t); + 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; @@ -244,7 +244,8 @@ nothrow: this = this.opBinary!op(rh); } - ptrdiff_t toString(char[] buffer) const pure + import urt.string.format : FormatArg; + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure { assert(false, "TODO"); } @@ -361,8 +362,8 @@ nothrow: // debug/ctfe helper string toString() pure { - char[32] t; - ptrdiff_t l = toString(t); + 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; @@ -706,7 +707,8 @@ nothrow: return len; } - ptrdiff_t toString(char[] buffer) const pure + import urt.string.format : FormatArg; + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure { if (!unit.pack) { diff --git a/src/urt/variant.d b/src/urt/variant.d index a086c33..febad39 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -937,7 +937,7 @@ nothrow @nogc: case Variant.Type.Number: if (isQuantity()) - return asQuantity().toString(buffer);//, format, formatArgs); + return asQuantity().toString(buffer, format, formatArgs); if (isDouble()) return asDouble().format_float(buffer); From 42f5ac166a610175778ccc9e1f52c2d1dfbab8db Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 5 Nov 2025 22:21:03 +1000 Subject: [PATCH 29/91] Add a way to register simple log sinks. TODO: more to come on this... --- src/urt/log.d | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/urt/log.d b/src/urt/log.d index 71af7fd..029718c 100644 --- a/src/urt/log.d +++ b/src/urt/log.d @@ -1,8 +1,11 @@ module urt.log; -import urt.io; +import urt.mem.temp; -enum Level +nothrow @nogc: + + +enum Level : ubyte { Error = 0, Warning, @@ -12,8 +15,19 @@ enum Level immutable string[] levelNames = [ "Error", "Warning", "Info", "Debug" ]; +alias LogSink = void function(Level level, const(char)[] message) nothrow @nogc; + __gshared Level logLevel = Level.Info; +void register_log_sink(LogSink sink) nothrow @nogc +{ + if (g_log_sink_count < g_log_sinks.length) + { + g_log_sinks[g_log_sink_count] = sink; + g_log_sink_count++; + } +} + void writeDebug(T...)(ref T things) { writeLog(Level.Debug, things); } void writeInfo(T...)(ref T things) { writeLog(Level.Info, things); } void writeWarning(T...)(ref T things) { writeLog(Level.Warning, things); } @@ -28,12 +42,26 @@ void writeLog(T...)(Level level, ref T things) { if (level > logLevel) return; - writeln(levelNames[level], ": ", things); + + const(char)[] message = tconcat(levelNames[level], ": ", things); + for (size_t i = 0; i < g_log_sink_count; i++) + g_log_sinks[i](level, message); } void writeLogf(T...)(Level level, const(char)[] format, ref T things) { if (level > logLevel) return; - writelnf("{-2}: {@-1}", things, levelNames[level], format); + + const(char)[] message = tformat("{0}: {@-2}", levelNames[level], things, format); + + for (size_t i = 0; i < g_log_sink_count; i++) + g_log_sinks[i](level, message); } + + +private: + +// HACK: temp until we have a proper registration process... +__gshared LogSink[8] g_log_sinks; +__gshared size_t g_log_sink_count = 0; From 0cf0f6b23f5d6a15f58e2face397f545c17c8eac Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 6 Nov 2025 03:24:50 +1000 Subject: [PATCH 30/91] Fix the DateTime stringify function. It didn't previously report the needed length when supplied a null buffer. Also, the ISO 8601 timestamp format requires padded month-day. Also, fixed a bug! --- src/urt/time.d | 60 +++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index b8dd4be..80accd5 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -394,55 +394,65 @@ pure nothrow @nogc: import urt.string.format : FormatArg; ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const { - import urt.conv : format_int; + import urt.conv : format_int, format_uint; + + ptrdiff_t len; + if (!buffer.ptr) + { + 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; + } size_t offset = 0; - ptrdiff_t len = year.format_int(buffer[offset..$]); + len = year.format_int(buffer[offset..$]); if (len < 0 || len == buffer.length) return -1; offset += len; buffer[offset++] = '-'; - len = month.format_int(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.format_int(buffer[offset..$]); - if (len < 0 || len == buffer.length) - return -1; - offset += len; + buffer[offset++] = '0' + (day / 10); + buffer[offset++] = '0' + (day % 10); buffer[offset++] = 'T'; - len = hour.format_int(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) - return -1; - offset += len; + buffer[offset++] = '0' + (hour / 10); + buffer[offset++] = '0' + (hour % 10); buffer[offset++] = ':'; - len = minute.format_int(buffer[offset..$], 10, 2, '0'); - if (len < 0 || len == buffer.length) - return -1; - offset += len; + buffer[offset++] = '0' + (minute / 10); + buffer[offset++] = '0' + (minute % 10); buffer[offset++] = ':'; - len = second.format_int(buffer[offset..$], 10, 2, '0'); - if (len < 0) - return -1; - offset += len; + buffer[offset++] = '0' + (second / 10); + buffer[offset++] = '0' + (second % 10); if (ns) { - if (len == buffer.length) + if (offset == buffer.length) return -1; buffer[offset++] = '.'; uint nsecs = ns; uint m = 0; while (nsecs) { - if (len == buffer.length) + if (offset == buffer.length) return -1; int digit = nsecs / digit_multipliers[m]; buffer[offset++] = cast(char)('0' + digit); nsecs -= digit * digit_multipliers[m++]; } } - return offset + len; + return offset; } ptrdiff_t fromString(const(char)[] s) From 294a273913b0fe8cfb54da3b9d9e5660e2d40209 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 10 Nov 2025 23:22:10 +1000 Subject: [PATCH 31/91] Improve StringLit interaction with immutable. --- src/urt/string/string.d | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index b2f7abf..4fa8cdb 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -57,12 +57,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) @@ -187,7 +186,17 @@ 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 opCast(T : bool)() const pure => ptr != null && ((cast(ushort*)ptr)[-1] & 0x7FFF) != 0; From 9b6bebcdfb6f9be352f0a0515f87e354b943fb12 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 10 Nov 2025 23:33:13 +1000 Subject: [PATCH 32/91] Added EnumInfo for efficient runtime enum lookup. --- src/urt/algorithm.d | 64 ++++++++--- src/urt/meta/package.d | 235 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 278 insertions(+), 21 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 1e603fa..0484777 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -1,6 +1,7 @@ module urt.algorithm; -import urt.traits : lvalue_of; +import urt.meta : AliasSeq; +import urt.traits : is_some_function, lvalue_of, Parameters, ReturnType, Unqual; import urt.util : swap; version = SmallSize; @@ -56,8 +57,36 @@ auto compare(T, U)(auto ref T a, auto ref U b) return a < b ? -1 : (a > b ? 1 : 0); } +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*))) +{ + const void* p = arr.ptr; + size_t low = 0; + size_t high = arr.length; + 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 == arr.length) + return arr.length; + if (pred(p + low*stride, value) == 0) + return low; + return arr.length; +} + 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; @@ -82,6 +111,8 @@ size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_arg high = mid; } } + if (low == arr.length) + return arr.length; static if (is(pred == void)) { if (p[low] == cmp_args[0]) @@ -96,22 +127,27 @@ size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_arg } -void qsort(alias pred = void, T)(T[] arr) +void qsort(alias pred = void, T)(T[] arr) pure { 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, lvalue_of!T.opCmp(lvalue_of!T))) - 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).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); }); } @@ -121,7 +157,7 @@ void qsort(alias pred = void, T)(T[] arr) if (arr.length > 1) { size_t pivotIndex = arr.length / 2; - T* pivot = &p[pivotIndex]; + T* pivot = p + pivotIndex; size_t i = 0; size_t j = arr.length - 1; @@ -135,8 +171,8 @@ void qsort(alias pred = void, T)(T[] arr) } else { - while (pred(*cast(T*)&p[i], *cast(T*)pivot) < 0) i++; - while (pred(*cast(T*)&p[j], *cast(T*)pivot) > 0) j--; + while (pred(p[i], *pivot) < 0) i++; + while (pred(p[j], *pivot) > 0) j--; } if (i <= j) { @@ -147,9 +183,9 @@ void qsort(alias pred = void, T)(T[] arr) } if (j > 0) - qsort(p[0 .. j + 1]); + qsort!pred(p[0 .. j + 1]); if (i < arr.length) - qsort(p[i .. arr.length]); + qsort!pred(p[i .. arr.length]); } } } @@ -159,7 +195,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; } @@ -167,7 +203,7 @@ 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]); @@ -189,7 +225,7 @@ 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 element_size, 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 / element_size; diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index 599d6c9..b7b1449 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -1,5 +1,7 @@ module urt.meta; +import urt.traits : is_callable, is_enum, EnumType, Unqual; + pure nothrow @nogc: alias Alias(alias a) = a; @@ -7,6 +9,52 @@ alias Alias(T) = T; alias AliasSeq(TList...) = TList; +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) + 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) { return (1UL << bits) - 1; @@ -84,18 +132,191 @@ template INTERLEAVE_SEPARATOR(alias sep, Args...) template enum_keys(E) { - static assert(is(E == enum), "enum_keys only works with enums!"); + static assert(is_enum!E, "enum_keys only works with enums!"); __gshared immutable string[enum_strings.length] enum_keys = [ enum_strings ]; private alias enum_strings = __traits(allMembers, E); } -E enum_from_string(E)(const(char)[] key) -if (is(E == enum)) +const(E)* enum_from_key(E)(const(char)[] key) + if (is_enum!E) + => MakeEnumInfo!E.value_for(key); + +const(char)[] enum_key_from_value(E)(EnumType!E value) + if (is_enum!E) + => MakeEnumInfo!E.key_for(value); + +struct VoidEnumInfo { - foreach (i, k; enum_keys!E) - if (key[] == k[]) - return cast(E)i; - return cast(E)-1; + import urt.algorithm : binary_search; + import urt.string; + + // keys and values are sorted for binary search + const String[] keys; + const void[] values; + uint 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, stride, value, pred); + if (i < values.length) + return keys[_v2k_lookup[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, stride, value, pred); + if (i < values.length) + return keys[_v2k_lookup[i]][]; + return null; + } + + const(void)* value_for(const(char)[] key) const pure + { + size_t i = binary_search(keys, key); + if (i == keys.length) + return null; + i = _k2v_lookup[i]; + return &values[i*stride]; + } + + bool contains(const(char)[] key) const pure + { + size_t i = binary_search(keys, key); + return i < keys.length; + } + +private: + // these tables map between indices of keys and values + const ubyte[] _k2v_lookup; + const ubyte[] _v2k_lookup; +} + +template EnumInfo(E) +{ + alias UE = Unqual!E; + + static if (is(UE == void)) + alias EnumInfo = VoidEnumInfo; + else + { + struct EnumInfo + { + import urt.algorithm : binary_search; + import urt.string; + + static assert (EnumInfo.sizeof == EnumInfo.sizeof, "Template EnumInfo must not add any members!"); + + static if (is(UE T == enum)) + alias V = T; + else + static assert(false, E.string ~ " is not an enum type!"); + + // keys and values are sorted for binary search + const String[] keys; + const UE[] values; + uint stride = E.sizeof; + uint type_hash; + + ref inout(VoidEnumInfo) make_void() inout + => *cast(inout VoidEnumInfo*)&this; + + const(char)[] key_for(V value) const pure + { + size_t i = binary_search(values, value); + if (i < values.length) + return keys[_v2k_lookup[i]][]; + return null; + } + + const(UE)* value_for(const(char)[] key) const pure + { + size_t i = binary_search(keys, key); + if (i == keys.length) + return null; + return &values[_k2v_lookup[i]]; + } + + bool contains(const(char)[] key) const pure + { + size_t i = binary_search(keys, key); + return i < keys.length; + } + + private: + // these tables map between indices of keys and values + const ubyte[] _k2v_lookup; + const ubyte[] _v2k_lookup; + } + + // sanity check the typed one matches the untyped one + static assert(EnumInfo.sizeof == VoidEnumInfo.sizeof); + static assert(EnumInfo.keys.offsetof == VoidEnumInfo.keys.offsetof); + static assert(EnumInfo.values.offsetof == VoidEnumInfo.values.offsetof); + static assert(EnumInfo.stride.offsetof == VoidEnumInfo.stride.offsetof); + static assert(EnumInfo.type_hash.offsetof == VoidEnumInfo.type_hash.offsetof); + static assert(EnumInfo._k2v_lookup.offsetof == VoidEnumInfo._k2v_lookup.offsetof); + static assert(EnumInfo._v2k_lookup.offsetof == VoidEnumInfo._v2k_lookup.offsetof); + } +} + + +template MakeEnumInfo(E) + if (is(Unqual!E == enum)) +{ + alias UE = Unqual!E; + + __gshared immutable MakeEnumInfo = EnumInfo!UE( + _keys[], + _values[], + E.sizeof, + 0, + _k2v_lookup[], + _v2k_lookup[], + ); + +private: + import urt.algorithm : binary_search, compare, qsort; + import urt.string; + import urt.string.uni : uni_compare; + + enum NumItems = __traits(allMembers, E).length; + static assert(NumItems <= ubyte.max, "Too many enum items!"); + + // keys and values are sorted for binary search + __gshared immutable String[NumItems] _keys = [ STATIC_MAP!(GetKey, iota) ]; + __gshared immutable UE[NumItems] _values = [ STATIC_MAP!(GetValue, iota) ]; + + // these tables map between indices of keys and values + __gshared immutable ubyte[NumItems] _k2v_lookup = [ STATIC_MAP!(GetKeyRedirect, iota) ]; + __gshared immutable ubyte[NumItems] _v2k_lookup = [ STATIC_MAP!(GetValRedirect, iota) ]; + + // a whole bunch of nonsense to build the tables... + struct KI + { + string k; + ubyte i; + } + struct VI + { + UE v; + ubyte i; + } + + alias iota = Iota!(enum_members.length); + enum enum_members = __traits(allMembers, E); + enum by_key = (){ KI[NumItems] r = [ STATIC_MAP!(MakeKI, iota) ]; r.qsort!((ref a, ref b) => uni_compare(a.k, b.k)); return r; }(); + enum by_value = (){ VI[NumItems] r = [ STATIC_MAP!(MakeVI, iota) ]; r.qsort!((ref a, ref b) => compare(a.v, b.v)); return r; }(); + enum inv_key = (){ KI[NumItems] bk = by_key; ubyte[NumItems] r; foreach (ubyte i, ref ki; bk) r[ki.i] = i; return r; }(); + enum inv_val = (){ VI[NumItems] bv = by_value; ubyte[NumItems] r; foreach (ubyte i, ref vi; bv) r[vi.i] = i; return r; }(); + + enum MakeKI(ushort i) = KI(enum_members[i], i); + enum MakeVI(ushort i) = VI(__traits(getMember, E, enum_members[i]), i); + enum GetKey(size_t i) = StringLit!(by_key[i].k); + 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]; } From 848e77e0acfe6f4fe45827c86fae369af36ba7e7 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 11 Nov 2025 09:23:58 +1000 Subject: [PATCH 33/91] Remove enum_keys helper in favour of the new enum tools. Also eliminated all the redundant length fields, and just store a single length. --- src/urt/algorithm.d | 11 +++-- src/urt/meta/package.d | 105 +++++++++++++++++++++++------------------ 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 0484777..c070380 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -60,9 +60,12 @@ auto compare(T, U)(auto ref T a, auto ref U b) 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 = arr.length; + size_t high = count; while (low < high) { size_t mid = low + (high - low) / 2; @@ -74,11 +77,11 @@ size_t binary_search(Pred)(const void[] arr, size_t stride, const void* value, a else high = mid; } - if (low == arr.length) - return arr.length; + if (low == count) + return count; if (pred(p + low*stride, value) == 0) return low; - return arr.length; + return count; } size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_args) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index b7b1449..91ca9e6 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -130,13 +130,6 @@ template INTERLEAVE_SEPARATOR(alias sep, Args...) } -template enum_keys(E) -{ - static assert(is_enum!E, "enum_keys only works with enums!"); - __gshared immutable string[enum_strings.length] enum_keys = [ enum_strings ]; - private alias enum_strings = __traits(allMembers, E); -} - const(E)* enum_from_key(E)(const(char)[] key) if (is_enum!E) => MakeEnumInfo!E.value_for(key); @@ -145,52 +138,65 @@ const(char)[] enum_key_from_value(E)(EnumType!E value) if (is_enum!E) => MakeEnumInfo!E.key_for(value); +const(char)[] enum_key_by_decl_index(E)(size_t value) + if (is_enum!E) + => MakeEnumInfo!E.key_by_decl_index(value); + struct VoidEnumInfo { import urt.algorithm : binary_search; import urt.string; +nothrow @nogc: // keys and values are sorted for binary search - const String[] keys; - const void[] values; - uint stride; + const String* keys; + const void* values; + ubyte 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, stride, value, pred); - if (i < values.length) + size_t i = binary_search(values[0 .. count*stride], stride, value, pred); + if (i < count) return keys[_v2k_lookup[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, stride, value, pred); - if (i < values.length) + size_t i = binary_search(values[0 .. count*stride], stride, value, pred); + if (i < count) return keys[_v2k_lookup[i]][]; return null; } + const(char)[] key_by_decl_index(size_t i) const pure + { + assert(i < count, "Declaration index out of range"); + return keys[_decl_lookup[i]][]; + } + const(void)* value_for(const(char)[] key) const pure { - size_t i = binary_search(keys, key); - if (i == keys.length) + size_t i = binary_search(keys[0 .. count], key); + if (i == count) return null; i = _k2v_lookup[i]; - return &values[i*stride]; + return values + i*stride; } bool contains(const(char)[] key) const pure { - size_t i = binary_search(keys, key); - return i < keys.length; + size_t i = binary_search(keys[0 .. count], key); + return i < count; } private: // these tables map between indices of keys and values - const ubyte[] _k2v_lookup; - const ubyte[] _v2k_lookup; + const ubyte* _k2v_lookup; + const ubyte* _v2k_lookup; + const ubyte* _decl_lookup; } template EnumInfo(E) @@ -205,6 +211,7 @@ template EnumInfo(E) { import urt.algorithm : binary_search; import urt.string; + nothrow @nogc: static assert (EnumInfo.sizeof == EnumInfo.sizeof, "Template EnumInfo must not add any members!"); @@ -214,9 +221,10 @@ template EnumInfo(E) static assert(false, E.string ~ " is not an enum type!"); // keys and values are sorted for binary search - const String[] keys; - const UE[] values; - uint stride = E.sizeof; + const String* keys; + const UE* values; + const ubyte count = __traits(allMembers, E).length; + const ushort stride = E.sizeof; uint type_hash; ref inout(VoidEnumInfo) make_void() inout @@ -224,40 +232,43 @@ template EnumInfo(E) const(char)[] key_for(V value) const pure { - size_t i = binary_search(values, value); - if (i < values.length) + size_t i = binary_search(values[0 .. count], value); + if (i < count) return keys[_v2k_lookup[i]][]; return null; } + const(char)[] key_by_decl_index(size_t i) const pure + => make_void().key_by_decl_index(i); + const(UE)* value_for(const(char)[] key) const pure { - size_t i = binary_search(keys, key); - if (i == keys.length) + size_t i = binary_search(keys[0 .. count], key); + if (i == count) return null; - return &values[_k2v_lookup[i]]; + return values + _k2v_lookup[i]; } bool contains(const(char)[] key) const pure - { - size_t i = binary_search(keys, key); - return i < keys.length; - } + => make_void().contains(key); private: // these tables map between indices of keys and values - const ubyte[] _k2v_lookup; - const ubyte[] _v2k_lookup; + const ubyte* _k2v_lookup; + const ubyte* _v2k_lookup; + const ubyte* _decl_lookup; } // sanity check the typed one matches the untyped one static assert(EnumInfo.sizeof == VoidEnumInfo.sizeof); static assert(EnumInfo.keys.offsetof == VoidEnumInfo.keys.offsetof); static assert(EnumInfo.values.offsetof == VoidEnumInfo.values.offsetof); + static assert(EnumInfo.count.offsetof == VoidEnumInfo.count.offsetof); static assert(EnumInfo.stride.offsetof == VoidEnumInfo.stride.offsetof); static assert(EnumInfo.type_hash.offsetof == VoidEnumInfo.type_hash.offsetof); static assert(EnumInfo._k2v_lookup.offsetof == VoidEnumInfo._k2v_lookup.offsetof); static assert(EnumInfo._v2k_lookup.offsetof == VoidEnumInfo._v2k_lookup.offsetof); + static assert(EnumInfo._decl_lookup.offsetof == VoidEnumInfo._decl_lookup.offsetof); } } @@ -267,13 +278,18 @@ template MakeEnumInfo(E) { alias UE = Unqual!E; - __gshared immutable MakeEnumInfo = EnumInfo!UE( - _keys[], - _values[], + enum ubyte NumItems = __traits(allMembers, E).length; + static assert(NumItems <= ubyte.max, "Too many enum items!"); + + __gshared immutable MakeEnumInfo = immutable(EnumInfo!UE)( + _keys.ptr, + _values.ptr, + NumItems, E.sizeof, 0, - _k2v_lookup[], - _v2k_lookup[], + _lookup.ptr, + _lookup.ptr + NumItems, + _lookup.ptr + NumItems*2 ); private: @@ -281,16 +297,14 @@ private: import urt.string; import urt.string.uni : uni_compare; - enum NumItems = __traits(allMembers, E).length; - static assert(NumItems <= ubyte.max, "Too many enum items!"); - // keys and values are sorted for binary search __gshared immutable String[NumItems] _keys = [ STATIC_MAP!(GetKey, iota) ]; __gshared immutable UE[NumItems] _values = [ STATIC_MAP!(GetValue, iota) ]; // these tables map between indices of keys and values - __gshared immutable ubyte[NumItems] _k2v_lookup = [ STATIC_MAP!(GetKeyRedirect, iota) ]; - __gshared immutable ubyte[NumItems] _v2k_lookup = [ STATIC_MAP!(GetValRedirect, iota) ]; + __gshared immutable ubyte[NumItems * 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 @@ -317,6 +331,7 @@ private: 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]; } From fe37a7e8e5b28df2761672d769407a71941e17c9 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 17 Nov 2025 01:18:28 +1000 Subject: [PATCH 34/91] Fix several logging bugs (actually mostly string formatting bugs!) --- src/urt/log.d | 2 +- src/urt/string/format.d | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/urt/log.d b/src/urt/log.d index 029718c..9a08f0a 100644 --- a/src/urt/log.d +++ b/src/urt/log.d @@ -53,7 +53,7 @@ void writeLogf(T...)(Level level, const(char)[] format, ref T things) if (level > logLevel) return; - const(char)[] message = tformat("{0}: {@-2}", levelNames[level], things, format); + const(char)[] message = tformat("{-2}: {@-1}", things, levelNames[level], format); for (size_t i = 0; i < g_log_sink_count; i++) g_log_sinks[i](level, message); diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 410432e..074a32d 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -344,9 +344,9 @@ struct DefFormat(T) } static if (is(T == long)) - size_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', show_sign); + ptrdiff_t len = format_int(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' ', show_sign); else - size_t len = format_uint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); + ptrdiff_t len = format_uint(value, buffer, base, cast(uint)padding, leadingZeroes ? '0' : ' '); if (to_lower && len > 0) { @@ -473,6 +473,8 @@ struct DefFormat(T) } char[] hex = toHexString(cast(ubyte[])value, buffer, grp1, grp2); + if (!hex.ptr) + return -1; return hex.length; } else static if (is(T : const U[], U)) @@ -861,6 +863,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 From d329520278f4876427cc9b0f64a80086154b476d Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 1 Dec 2025 08:58:29 +1000 Subject: [PATCH 35/91] Add support for Variant to store binary data. Made variant strings a flag on binary data. --- src/urt/format/json.d | 25 +++++++++------ src/urt/traits.d | 2 +- src/urt/variant.d | 72 +++++++++++++++++++++++++++++++++---------- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/urt/format/json.d b/src/urt/format/json.d index ca2cd26..01e977e 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -100,17 +100,24 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u return -1; return written; - case Variant.Type.String: - const char[] s = val.asString(); - if (buffer.ptr) + case Variant.Type.Buffer: + if (val.isString) { - if (buffer.length < s.length + 2) - return -1; - buffer[0] = '"'; - buffer[1 .. 1 + s.length] = s[]; - buffer[1 + s.length] = '"'; + const char[] s = val.asString(); + if (buffer.ptr) + { + if (buffer.length < s.length + 2) + return -1; + buffer[0] = '"'; + buffer[1 .. 1 + s.length] = s[]; + buffer[1 + s.length] = '"'; + } + return s.length + 2; + } + else + { + assert(false, "TODO: how are binary buffers represented in json?"); } - return s.length + 2; case Variant.Type.Number: import urt.conv; diff --git a/src/urt/traits.d b/src/urt/traits.d index bfa8073..fb124d9 100644 --- a/src/urt/traits.d +++ b/src/urt/traits.d @@ -10,7 +10,7 @@ enum bool is_boolean(T) = __traits(isUnsigned, T) && is(T : bool); 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(Unqual!T == bool) || is_unsigned_int!T || is_some_char!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); diff --git a/src/urt/variant.d b/src/urt/variant.d index febad39..81af559 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -194,13 +194,29 @@ nothrow @nogc: count = cast(uint)s.length; } + this(T)(T[] buffer) + if (is(Unqual!T == void)) + { + 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; + value.s = cast(char*)buffer.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); @@ -387,7 +403,7 @@ nothrow @nogc: case Type.Null: if (b.type == Type.Null) return 0; - else if ((b.type == Type.String || b.type == Type.Array || b.type == Type.Map) && b.empty()) + 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; @@ -469,13 +485,13 @@ nothrow @nogc: r = -1; // sort numbers before other things... break; - case Type.String: - if (b.type != Type.String) + case Type.Buffer: + if (b.type != Type.Buffer) { r = -1; break; } - r = compare(a.asString(), b.asString()); + r = compare(cast(const(char)[])a.asBuffer(), cast(const(char)[])b.asBuffer()); break; case Type.Array: @@ -578,6 +594,8 @@ nothrow @nogc: => (flags & Flags.IsQuantity) != 0; bool isDuration() const pure => isQuantity && (count & 0xFFFFFF) == Second.pack; + bool isBuffer() const pure + => type == Type.Buffer; bool isString() const pure => (flags & Flags.IsString) != 0; bool isArray() const pure @@ -747,6 +765,16 @@ nothrow @nogc: 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 value.s[0 .. count]; + } + const(char)[] asString() const pure { if (isNull) @@ -949,17 +977,25 @@ nothrow @nogc: 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: @@ -1165,7 +1201,7 @@ package: True = 1, False = 2, Number = 3, - String = 4, + Buffer = 4, Array = 5, Map = 6, User = 7 @@ -1182,8 +1218,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, From 60ddd5e778c77146d4931e692fb860c6ebed3be4 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 1 Dec 2025 09:00:19 +1000 Subject: [PATCH 36/91] Add 2 little helpers: - Bounded strlen_s - SysTime from unit time --- src/urt/string/package.d | 8 ++++++++ src/urt/time.d | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 3210e03..81cbf75 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -15,6 +15,14 @@ public import urt.mem.temp : tstringz, twstringz; nothrow @nogc: +size_t strlen_s(const(char)[] s) pure +{ + size_t len = 0; + while (len < s.length && s[len] != '\0') + ++len; + return len; +} + ptrdiff_t cmp(bool case_insensitive = false, T, U)(const(T)[] a, const(U)[] b) pure { static if (case_insensitive) diff --git a/src/urt/time.d b/src/urt/time.d index 80accd5..7972a1e 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -697,6 +697,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); From 708247852e742132163f933c9ca8b19e0bacb1bc Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 2 Dec 2025 09:55:38 +1000 Subject: [PATCH 37/91] More druntime scrubbing... --- src/urt/internal/lifetime.d | 121 ++++++++++++++++++++++++++++++++++++ src/urt/lifetime.d | 34 +--------- src/urt/math.d | 1 - src/urt/mem/package.d | 20 +++--- 4 files changed, 133 insertions(+), 43 deletions(-) create mode 100644 src/urt/internal/lifetime.d diff --git a/src/urt/internal/lifetime.d b/src/urt/internal/lifetime.d new file mode 100644 index 0000000..314164f --- /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 core.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/lifetime.d b/src/urt/lifetime.d index cb6cf40..586d9db 100644 --- a/src/urt/lifetime.d +++ b/src/urt/lifetime.d @@ -1,5 +1,6 @@ module urt.lifetime; +import urt.internal.lifetime : emplaceRef; // TODO: DESTROY THIS! T* emplace(T)(T* chunk) @safe pure { @@ -78,12 +79,6 @@ T* emplace(T, Args...)(void[] chunk, auto ref Args args) return cast(T*) chunk.ptr; } - -// HACK: we should port this to our lib... -static import core.internal.lifetime; -alias emplaceRef = core.internal.lifetime.emplaceRef; - - /+ void copyEmplace(S, T)(ref S source, ref T target) @system if (is(immutable S == immutable T)) @@ -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; } @@ -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; } diff --git a/src/urt/math.d b/src/urt/math.d index 3c66be8..e3b06b8 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -1,7 +1,6 @@ module urt.math; import urt.intrinsic; -import core.stdc.stdio; // For writeDebugf // for arch where using FPU for int<->float conversions is preferred //version = PreferFPUIntConv; diff --git a/src/urt/mem/package.d b/src/urt/mem/package.d index 795a4e3..9a903c8 100644 --- a/src/urt/mem/package.d +++ b/src/urt/mem/package.d @@ -11,19 +11,19 @@ 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* 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); - 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); } From e55eafde5225a2ff3769d978725948bdcf907c82 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 2 Dec 2025 23:05:34 +1000 Subject: [PATCH 38/91] Json string writer must escape strings --- src/urt/format/json.d | 74 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 01e977e..4b65529 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -104,15 +104,77 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u if (val.isString) { 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; } else { From 4a6088087f4bf6dd8c23bf4798950697927e21cc Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 4 Dec 2025 18:14:53 +1000 Subject: [PATCH 39/91] Variant fixes --- src/urt/endian.d | 10 +++++----- src/urt/variant.d | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/urt/endian.d b/src/urt/endian.d index bb806d9..9c0033c 100644 --- a/src/urt/endian.d +++ b/src/urt/endian.d @@ -96,7 +96,7 @@ 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; @@ -261,7 +261,7 @@ ubyte[T.sizeof] nativeToLittleEndian(T)(auto ref const T data) // load/store from/to memory void storeBigEndian(T)(T* target, const T val) - if (is_some_int!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (BigEndian) *target = val; @@ -269,7 +269,7 @@ void storeBigEndian(T)(T* target, const T val) *target = byte_reverse(val); } void storeLittleEndian(T)(T* target, const T val) - if (is_some_int!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (LittleEndian) *target = val; @@ -277,7 +277,7 @@ void storeLittleEndian(T)(T* target, const T val) *target = byte_reverse(val); } T loadBigEndian(T)(const(T)* src) - if (is_some_int!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (BigEndian) return *src; @@ -285,7 +285,7 @@ T loadBigEndian(T)(const(T)* src) return byte_reverse(*src); } T loadLittleEndian(T)(const(T)* src) - if (is_some_int!T || is(T == float)) + if (is_some_int!T || is(T == float) || is(T == double)) { version (LittleEndian) return *src; diff --git a/src/urt/variant.d b/src/urt/variant.d index 81af559..6fbf197 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -344,11 +344,12 @@ nothrow @nogc: return nodeArray.pushBack(); } - bool opEquals(T)(ref const Variant rhs) const pure + 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))) @@ -536,6 +537,7 @@ nothrow @nogc: 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))) @@ -854,7 +856,7 @@ nothrow @nogc: return asUlong(); else { - uint u = asInt(); + uint u = asUint(); static if (!is(T == uint)) assert(u <= T.max, "Value out of range for " ~ T.stringof); return cast(T)u; @@ -918,6 +920,16 @@ nothrow @nogc: return null; } + void set_unit(ScaledUnit unit) + { + assert(isNumber()); + count = unit.pack; + if (count != 0) + flags |= Flags.IsQuantity; + else + flags &= ~Flags.IsQuantity; + } + // TODO: this seems to interfere with UFCS a lot... // ref inout(Variant) opDispatch(string member)() inout pure // { From c978668e72b8889812de72d95866300d0c518f07 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 12:01:31 +1000 Subject: [PATCH 40/91] Added a function to build a runtime EnumInfo. Substantially compressed the data by: - aggregating buffers - storing keys as a 16-bit index into a single string buffer - removed a bunch of redundant length fields The runtime EnumInfo is a single allocation. --- src/urt/meta/package.d | 309 +++++++++++++++++++++++++++++++---------- 1 file changed, 232 insertions(+), 77 deletions(-) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index 91ca9e6..0a17ea3 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -2,7 +2,7 @@ module urt.meta; import urt.traits : is_callable, is_enum, EnumType, Unqual; -pure nothrow @nogc: +nothrow @nogc: alias Alias(alias a) = a; alias Alias(T) = T; @@ -18,7 +18,7 @@ template Iota(ptrdiff_t start, ptrdiff_t end) } alias Iota(size_t end) = Iota!(0, end); -auto make_delegate(Fun)(Fun fun) +auto make_delegate(Fun)(Fun fun) pure if (is_callable!Fun) { import urt.traits : is_function_pointer, ReturnType, Parameters; @@ -55,7 +55,7 @@ auto make_delegate(Fun)(Fun fun) static assert(false, "Unsupported type for make_delegate"); } -ulong bit_mask(size_t bits) +ulong bit_mask(size_t bits) pure { return (1UL << bits) - 1; } @@ -130,17 +130,17 @@ template INTERLEAVE_SEPARATOR(alias sep, Args...) } -const(E)* enum_from_key(E)(const(char)[] key) +const(E)* enum_from_key(E)(const(char)[] key) pure if (is_enum!E) - => MakeEnumInfo!E.value_for(key); + => enum_info!E.value_for(key); -const(char)[] enum_key_from_value(E)(EnumType!E value) +const(char)[] enum_key_from_value(E)(EnumType!E value) pure if (is_enum!E) - => MakeEnumInfo!E.key_for(value); + => enum_info!E.key_for(value); -const(char)[] enum_key_by_decl_index(E)(size_t value) +const(char)[] enum_key_by_decl_index(E)(size_t value) pure if (is_enum!E) - => MakeEnumInfo!E.key_by_decl_index(value); + => enum_info!E.key_by_decl_index(value); struct VoidEnumInfo { @@ -149,54 +149,71 @@ struct VoidEnumInfo nothrow @nogc: // keys and values are sorted for binary search - const String* keys; - const void* values; - ubyte count; + 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); + size_t i = binary_search(_values[0 .. count*stride], stride, value, pred); if (i < count) - return keys[_v2k_lookup[i]][]; + 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); + size_t i = binary_search(_values[0 .. count*stride], stride, value, pred); if (i < count) - return keys[_v2k_lookup[i]][]; + 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 keys[_decl_lookup[i]][]; + return get_key(_lookup_tables[count*2 + i]); } const(void)* value_for(const(char)[] key) const pure { - size_t i = binary_search(keys[0 .. count], key); + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); if (i == count) return null; - i = _k2v_lookup[i]; - return values + i*stride; + i = _lookup_tables[i]; + return _values + i*stride; } bool contains(const(char)[] key) const pure { - size_t i = binary_search(keys[0 .. count], key); + 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* _k2v_lookup; - const ubyte* _v2k_lookup; - const ubyte* _decl_lookup; + const ubyte* _lookup_tables; + + this(ubyte count, ushort stride, uint type_hash, inout void* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) 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; + } + + 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) @@ -210,7 +227,6 @@ template EnumInfo(E) struct EnumInfo { import urt.algorithm : binary_search; - import urt.string; nothrow @nogc: static assert (EnumInfo.sizeof == EnumInfo.sizeof, "Template EnumInfo must not add any members!"); @@ -221,90 +237,117 @@ template EnumInfo(E) static assert(false, E.string ~ " is not an enum type!"); // keys and values are sorted for binary search - const String* keys; - const UE* values; - const ubyte count = __traits(allMembers, E).length; - const ushort stride = E.sizeof; - uint type_hash; + union { + VoidEnumInfo _base; + const UE* _values; // shadows the _values in _base with a typed version + } + alias _base this; + + inout(VoidEnumInfo*) make_void() inout pure + => &_base; - ref inout(VoidEnumInfo) make_void() inout - => *cast(inout VoidEnumInfo*)&this; + this(ubyte count, uint type_hash, inout UE* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure + { + _base = inout(VoidEnumInfo)(count, UE.sizeof, type_hash, values, keys, strings, lookup); + } + + const(UE)[] 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 keys[_v2k_lookup[i]][]; + return get_key(_lookup_tables[count + i]); return null; } const(char)[] key_by_decl_index(size_t i) const pure - => make_void().key_by_decl_index(i); + => _base.key_by_decl_index(i); const(UE)* value_for(const(char)[] key) const pure { - size_t i = binary_search(keys[0 .. count], key); + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); if (i == count) return null; - return values + _k2v_lookup[i]; + return _values + _lookup_tables[i]; } bool contains(const(char)[] key) const pure - => make_void().contains(key); - - private: - // these tables map between indices of keys and values - const ubyte* _k2v_lookup; - const ubyte* _v2k_lookup; - const ubyte* _decl_lookup; + => _base.contains(key); } - - // sanity check the typed one matches the untyped one - static assert(EnumInfo.sizeof == VoidEnumInfo.sizeof); - static assert(EnumInfo.keys.offsetof == VoidEnumInfo.keys.offsetof); - static assert(EnumInfo.values.offsetof == VoidEnumInfo.values.offsetof); - static assert(EnumInfo.count.offsetof == VoidEnumInfo.count.offsetof); - static assert(EnumInfo.stride.offsetof == VoidEnumInfo.stride.offsetof); - static assert(EnumInfo.type_hash.offsetof == VoidEnumInfo.type_hash.offsetof); - static assert(EnumInfo._k2v_lookup.offsetof == VoidEnumInfo._k2v_lookup.offsetof); - static assert(EnumInfo._v2k_lookup.offsetof == VoidEnumInfo._v2k_lookup.offsetof); - static assert(EnumInfo._decl_lookup.offsetof == VoidEnumInfo._decl_lookup.offsetof); } } - -template MakeEnumInfo(E) +template enum_info(E) if (is(Unqual!E == enum)) { alias UE = Unqual!E; - enum ubyte NumItems = __traits(allMembers, E).length; - static assert(NumItems <= ubyte.max, "Too many enum items!"); + enum ubyte num_items = enum_members.length; + static assert(num_items <= ubyte.max, "Too many enum items!"); - __gshared immutable MakeEnumInfo = immutable(EnumInfo!UE)( - _keys.ptr, + __gshared immutable enum_info = immutable(EnumInfo!UE)( + num_items, + fnv1a(cast(ubyte[])UE.stringof), _values.ptr, - NumItems, - E.sizeof, - 0, - _lookup.ptr, - _lookup.ptr + NumItems, - _lookup.ptr + NumItems*2 + _keys.ptr, + _strings.ptr, + _lookup.ptr ); private: import urt.algorithm : binary_search, compare, qsort; - import urt.string; + import urt.hash : fnv1a; import urt.string.uni : uni_compare; // keys and values are sorted for binary search - __gshared immutable String[NumItems] _keys = [ STATIC_MAP!(GetKey, iota) ]; - __gshared immutable UE[NumItems] _values = [ STATIC_MAP!(GetValue, iota) ]; + __gshared immutable UE[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[NumItems * 3] _lookup = [ STATIC_MAP!(GetKeyRedirect, iota), - STATIC_MAP!(GetValRedirect, iota), - STATIC_MAP!(GetKeyOrig, iota) ]; + __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 @@ -320,20 +363,112 @@ private: alias iota = Iota!(enum_members.length); enum enum_members = __traits(allMembers, E); - enum by_key = (){ KI[NumItems] r = [ STATIC_MAP!(MakeKI, iota) ]; r.qsort!((ref a, ref b) => uni_compare(a.k, b.k)); return r; }(); - enum by_value = (){ VI[NumItems] r = [ STATIC_MAP!(MakeVI, iota) ]; r.qsort!((ref a, ref b) => compare(a.v, b.v)); return r; }(); - enum inv_key = (){ KI[NumItems] bk = by_key; ubyte[NumItems] r; foreach (ubyte i, ref ki; bk) r[ki.i] = i; return r; }(); - enum inv_val = (){ VI[NumItems] bv = by_value; ubyte[NumItems] r; foreach (ubyte i, ref vi; bv) r[vi.i] = i; return r; }(); + 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(enum_members[i], i); enum MakeVI(ushort i) = VI(__traits(getMember, E, enum_members[i]), i); - enum GetKey(size_t i) = StringLit!(by_key[i].k); 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); +} private: @@ -350,3 +485,23 @@ template is_same(A, B) { enum is_same = is(A == B); } + +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); +} From d3a470dd8905b00d5a341afbbc90422dc0b0db14 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 12:03:28 +1000 Subject: [PATCH 41/91] Added a helper for building string caches. - make sure it's CTFE-able. --- src/urt/mem/string.d | 5 --- src/urt/string/string.d | 89 +++++++++++++++++++++++++++++++++---- src/urt/string/tailstring.d | 6 +-- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/urt/mem/string.d b/src/urt/mem/string.d index 1cefa4e..a3adc22 100644 --- a/src/urt/mem/string.d +++ b/src/urt/mem/string.d @@ -11,11 +11,6 @@ shared static this() } -struct StringCache -{ -} - - struct CacheString { nothrow @nogc: diff --git a/src/urt/string/string.d b/src/urt/string/string.d index 4fa8cdb..843c7fd 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -32,6 +32,47 @@ struct StringAllocator void delegate(char* s) nothrow @nogc free; } +struct StringCacheBuilder +{ +nothrow @nogc: + this(char[] buffer) pure + { + assert(buffer.length <= ushort.max, "Buffer too long"); + this._buffer = buffer; + this._offset = 0; + } + + ushort add_string(const(char)[] s) pure + { + assert(s.length <= MaxStringLen, "String too long"); + 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; + } + +private: + char[] _buffer; + ushort _offset; +} //enum String StringLit(string s) = s.makeString; template StringLit(const(char)[] lit, bool zeroTerminate = true) @@ -116,6 +157,46 @@ 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 { @@ -941,14 +1022,6 @@ 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() { stringAllocators[StringAlloc.Default].alloc = (ushort bytes, void* userData) { diff --git a/src/urt/string/tailstring.d b/src/urt/string/tailstring.d index c9d8751..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); } From 7025ea75bf963b0989484ef7cafe95d9e056437c Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 12:03:58 +1000 Subject: [PATCH 42/91] Fix qsort... apparently I buggered it up! --- src/urt/algorithm.d | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index c070380..9995cfb 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -232,30 +232,32 @@ version (SmallSize) { void* p = arr.ptr; size_t length = arr.length / element_size; - if (length > 1) - { - size_t pivotIndex = length / 2; - void* pivot = p + pivotIndex*element_size; + if (length <= 1) + return; - size_t i = 0; - size_t j = length - 1; + size_t pivot_index = length / 2; + size_t last = length - 1; + swap(p + pivot_index*element_size, p + last*element_size); - while (i <= j) + void* pivot = p + last*element_size; + + 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*element_size, pivot) < 0) i++; - while (compare(p + j*element_size, pivot) > 0) j--; - if (i <= j) - { - swap(p + i*element_size, p + j*element_size); - i++; - j--; - } + if (k != partition) + swap(elem, p + partition*element_size); + ++partition; } - - if (j > 0) - qsort(p[0 .. (j + 1)*element_size], element_size, compare, swap); - if (i < length) - qsort(p[i*element_size .. length*element_size], element_size, 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); } } From 0b3c1109ab6948f25e86a8328e49e689f02d8557 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 12:54:51 +1000 Subject: [PATCH 43/91] Added a signed parse_int_with_base --- src/urt/conv.d | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index dbbd3f9..8889082 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -132,11 +132,23 @@ done: return value; } +long parse_int_with_base(const(char)[] str, size_t* bytes_taken = null) pure +{ + const(char)* p = str.ptr; + int base = str.parse_base_prefix(); + if (base == 10) + return str.parse_int(bytes_taken); + ulong i = str.parse_uint(bytes_taken, base); + if (bytes_taken && *bytes_taken != 0) + *bytes_taken += str.ptr - p; + return i; +} + ulong parse_uint_with_base(const(char)[] str, size_t* bytes_taken = null) pure { const(char)* p = str.ptr; - int base = parse_base_prefix(str); - ulong i = parse_uint(str, bytes_taken, base); + int base = str.parse_base_prefix(); + ulong i = str.parse_uint(bytes_taken, base); if (bytes_taken && *bytes_taken != 0) *bytes_taken += str.ptr - p; return i; From 44060913a7449a4f288985830423255a89413f60 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 13:08:17 +1000 Subject: [PATCH 44/91] Added a template to make a ScaledUnit from a string. --- src/urt/si/unit.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 1b98a6a..1ebf7a0 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -32,6 +32,9 @@ nothrow @nogc: // +enum ScaledUnit unit(const(char)[] desc) = () { ScaledUnit r; float f; ptrdiff_t e = r.parseUnit(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); From 38f75e3d4380d93cd81d51487ef266df55b6d9e4 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 7 Dec 2025 12:55:10 +1000 Subject: [PATCH 45/91] Unroll array --- src/urt/meta/package.d | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index 91ca9e6..a28f17a 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -99,6 +99,18 @@ template STATIC_MAP(alias fun, args...) 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 STATIC_FILTER(alias filter, args...) { alias STATIC_FILTER = AliasSeq!(); From 74fc4f11e6e85345c42e019803095f8214cc6b93 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 8 Dec 2025 12:57:56 +1000 Subject: [PATCH 46/91] Fixed an assortment of small bugs. --- src/urt/endian.d | 4 ++-- src/urt/si/quantity.d | 2 +- src/urt/time.d | 24 ++++++++++++++++++++---- src/urt/traits.d | 2 ++ src/urt/variant.d | 20 ++++++++++---------- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/urt/endian.d b/src/urt/endian.d index 9c0033c..a48f872 100644 --- a/src/urt/endian.d +++ b/src/urt/endian.d @@ -100,8 +100,8 @@ T endianToNative(T, bool little)(ref const ubyte[T.sizeof] 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; } } diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index 880166a..df9775f 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -72,7 +72,7 @@ nothrow @nogc: value = adjustScale(b); } } - + void opAssign()(T value) pure { static if (Dynamic) diff --git a/src/urt/time.d b/src/urt/time.d index 7972a1e..2b3036d 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -391,6 +391,23 @@ 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 { @@ -416,11 +433,10 @@ pure nothrow @nogc: return len; } - size_t offset = 0; - len = year.format_int(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++] = '-'; buffer[offset++] = '0' + (month / 10); buffer[offset++] = '0' + (month % 10); diff --git a/src/urt/traits.d b/src/urt/traits.d index fb124d9..6f8a761 100644 --- a/src/urt/traits.d +++ b/src/urt/traits.d @@ -222,6 +222,8 @@ enum is_primitive(T) = is_integral!T || is_some_float!T || (is_enum!T && is_prim 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 is_trivial(T) = __traits(isPOD, 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)))) || diff --git a/src/urt/variant.d b/src/urt/variant.d index 6fbf197..d6aebdd 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -1018,9 +1018,9 @@ nothrow @nogc: case Variant.Type.User: if (flags & Flags.Embedded) - return find_type_details(alloc).stringify(cast(void*)embed.ptr, buffer, true); + 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); + return g_type_details[alloc].stringify(cast(void*)ptr, buffer, true, format, formatArgs); } } @@ -1091,7 +1091,7 @@ nothrow @nogc: { 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); + ptrdiff_t taken = td.stringify(td.embedded ? embed.ptr : buffer.ptr, cast(char[])s, false, null, null); if (taken > 0) { flags = Flags.User; @@ -1283,6 +1283,7 @@ unittest private: import urt.hash : fnv1a; +import urt.string.format : formatValue, FormatArg; static assert(Variant.sizeof == 16); static assert(Variant.Type.max <= Variant.Flags.TypeMask); @@ -1342,7 +1343,7 @@ struct TypeDetails bool embedded; void function(void* src, void* dst, bool move) nothrow @nogc copy_emplace; void function(void* val) nothrow @nogc destroy; - ptrdiff_t function(void* val, char[] buffer, bool format) 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; } __gshared TypeDetails[8] g_type_details; @@ -1405,7 +1406,7 @@ public template TypeDetailsFor(T) else enum move_emplace = null; - static if (!is(T == class) && is(typeof(destroy!(false, T)))) + static if (!is_trivial!T && is(typeof(destroy!(false, T)))) { static void destroy_impl(void* val) nothrow @nogc { @@ -1416,13 +1417,12 @@ public template TypeDetailsFor(T) else enum destroy_fun = null; - static ptrdiff_t stringify(void* val, char[] buffer, bool format) nothrow @nogc + static ptrdiff_t stringify(void* val, char[] buffer, bool do_format, const(char)[] format_spec, const(FormatArg)[] format_args) nothrow @nogc { - import urt.string.format : toString; - if (format) + if (do_format) { - static if (is(typeof(toString!T))) - return toString(*cast(const T*)val, buffer); + 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; } From bb72172fc6ed3c1cfed2403fb912aec9a81d18a2 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 10 Dec 2025 15:19:14 +1000 Subject: [PATCH 47/91] Fix handling of negative times. --- src/urt/time.d | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index 2b3036d..20857dc 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -796,23 +796,24 @@ ptrdiff_t timeToString(long ns, char[] buffer) pure { import urt.conv : format_int; - long hr = ns / 3_600_000_000_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) { size_t tail = 6; - ns %= 1_000_000_000; - if (ns) + if (remainder) { ++tail; uint m = 0; do { ++tail; - uint digit = cast(uint)(ns / digit_multipliers[m]); - ns -= digit * digit_multipliers[m++]; + uint digit = cast(uint)(remainder / digit_multipliers[m]); + remainder -= digit * digit_multipliers[m++]; } - while (ns); + while (remainder); } return hr.format_int(null, 10, 2, '0') + tail; } @@ -821,9 +822,9 @@ ptrdiff_t timeToString(long ns, char[] buffer) pure if (len < 0 || buffer.length < len + 6) return -1; - ubyte min = cast(ubyte)(ns / 60_000_000_000 % 60); - ubyte sec = cast(ubyte)(ns / 1_000_000_000 % 60); - ns %= 1_000_000_000; + 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)); @@ -831,19 +832,20 @@ ptrdiff_t timeToString(long ns, char[] buffer) pure buffer.ptr[len++] = ':'; buffer.ptr[len++] = cast(char)('0' + (sec / 10)); buffer.ptr[len++] = cast(char)('0' + (sec % 10)); - if (ns) + if (remainder) { if (buffer.length < len + 2) return -1; buffer.ptr[len++] = '.'; - uint m = 0; - while (ns) + uint i = 0; + while (remainder) { - if (buffer.length < len + 1) + if (buffer.length <= len) return -1; - uint digit = cast(uint)(ns / digit_multipliers[m]); + uint m = digit_multipliers[i++]; + uint digit = cast(uint)(remainder / m); buffer.ptr[len++] = cast(char)('0' + digit); - ns -= digit * digit_multipliers[m++]; + remainder -= digit * m; } } return len; @@ -855,6 +857,7 @@ unittest 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"); + 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+"); From 5b90efcd9d6babfee8f2f772a82d72fc443bcc13 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 12 Dec 2025 16:31:47 +1000 Subject: [PATCH 48/91] Fix EnumInfo typed values array offset. --- src/urt/meta/package.d | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index 0d25eea..bb6e8d9 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -251,7 +251,10 @@ template EnumInfo(E) // keys and values are sorted for binary search union { VoidEnumInfo _base; - const UE* _values; // shadows the _values in _base with a typed version + struct { + ubyte[VoidEnumInfo._values.offsetof] _pad; + const UE* _values; // shadows the _values in _base with a typed version + } } alias _base this; From b7ca9d6c9670a04c4a6f08d6ab581a9023c623f9 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 12 Dec 2025 19:03:33 +1000 Subject: [PATCH 49/91] Fixed a bug resuming from a fibre multiple times. --- src/urt/async.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urt/async.d b/src/urt/async.d index 81e0a19..557f24a 100644 --- a/src/urt/async.d +++ b/src/urt/async.d @@ -79,7 +79,7 @@ void asyncUpdate() if (!t.event.ready()) continue; } - t.resume(); + t.call.fibre.resume(); } } From a7cc818c5b20148dda194ec97d69ddcc27c1ed65 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 14 Dec 2025 14:02:44 +1000 Subject: [PATCH 50/91] Add toString for Arrays and Maps. - Made it a template; so the code is never generated unless it's called (this should probably be standard practise!) --- src/urt/array.d | 14 +++++++++ src/urt/map.d | 63 +++++++++++++++++++++++++++++++++++++++++ src/urt/string/format.d | 34 ++++++++++++++-------- 3 files changed, 99 insertions(+), 12 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 702c861..6674151 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -685,6 +685,20 @@ 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; uint _length; diff --git a/src/urt/map.d b/src/urt/map.d index 97a6f99..3a624ac 100644 --- a/src/urt/map.d +++ b/src/urt/map.d @@ -300,6 +300,69 @@ struct AVLTree(K, V, alias Pred = DefCmp!K, Allocator = Mallocator) 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: alias Node = AVLTreeNode!(K, V); diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 074a32d..7784113 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -120,6 +120,8 @@ struct FormatArg { static if (IsAggregate!T && is(typeof(&value.toString) : StringifyFunc)) toString = &value.toString; + else 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... @@ -151,22 +153,16 @@ struct FormatArg } ptrdiff_t getString(char[] buffer, const(char)[] format, const(FormatArg)[] args) const nothrow @nogc - { - return toString(buffer, format, args); - } + => toString(buffer, format, args); + 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; - } + => toInt != null; + ptrdiff_t getInt() const nothrow @nogc - { - return toInt(); - } + => toInt(); private: // TODO: we could assert that the delegate pointers match, and only store it once... @@ -569,6 +565,9 @@ struct DefFormat(T) } else static if (is(T == class)) { + // 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(); @@ -581,6 +580,7 @@ struct DefFormat(T) } catch (Exception) return -1; ++/ } else static if (is(T == const)) { @@ -632,6 +632,16 @@ struct DefFormat(T) } return ++len; } + else static if (is(T == function)) + { + assert(false, "TODO"); + return 0; + } + else static if (is(T == delegate)) + { + assert(false, "TODO"); + return 0; + } else static assert(false, "Not implemented for type: ", T.stringof); } From 89eba280556af7dbf6624e8b17b249cdf37e276f Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 14 Dec 2025 14:03:25 +1000 Subject: [PATCH 51/91] Write a log message before calling abort() when a fibre encounters an unrecoverable error. --- src/urt/fibre.d | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/urt/fibre.d b/src/urt/fibre.d index 2e736d2..38a5579 100644 --- a/src/urt/fibre.d +++ b/src/urt/fibre.d @@ -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(); } From f0867935dcbb8e6449b0621dcc9b24c9edfa4cc4 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 30 Dec 2025 21:12:39 +1000 Subject: [PATCH 52/91] Fixed a swap-endian bug in a function that had never been called. --- src/urt/endian.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/urt/endian.d b/src/urt/endian.d index a48f872..2ee3da5 100644 --- a/src/urt/endian.d +++ b/src/urt/endian.d @@ -208,8 +208,7 @@ pragma(inline, true) auto nativeToEndian(bool little, T)(T val) { import urt.meta : IntForWidth; alias U = IntForWidth!(T.sizeof*8); - U r = nativeToEndian!little(*cast(U*)&val); - return *cast(T*)&r; + return nativeToEndian!little(*cast(U*)&val); } ubyte[T.sizeof] nativeToEndian(bool little, T)(auto ref const T data) From aa0d02b34d2adf6bd744739f763cbb76591aa28a Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 14 Dec 2025 20:07:39 +1000 Subject: [PATCH 53/91] Explicit format arg. --- src/urt/string/string.d | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index 843c7fd..1cf61e2 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -559,9 +559,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() @@ -690,9 +690,9 @@ nothrow @nogc: return this; } - ref MutableString!Embed appendFormat(Things...)(auto ref Things things) + ref MutableString!Embed appendFormat(Things...)(const(char)[] format, auto ref Things args) { - insertFormat(length(), forward!things); + insertFormat(length(), format, forward!args); return this; } @@ -704,11 +704,11 @@ 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); + insertFormat(0, format, forward!args); return this; } @@ -740,7 +740,7 @@ nothrow @nogc: return this; } - ref MutableString!Embed insertFormat(Things...)(size_t offset, auto ref Things things) + ref MutableString!Embed insertFormat(Things...)(size_t offset, const(char)[] format, auto ref Things args) { import urt.string.format : _format = format; import urt.util : max, next_power_of_2; @@ -748,7 +748,7 @@ nothrow @nogc: 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; @@ -757,7 +757,7 @@ nothrow @nogc: size_t oldAlloc = allocated(); 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) From 22e16a0722f5a0bfea591eb53484999b995bb4e8 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 31 Dec 2025 20:59:08 +1000 Subject: [PATCH 54/91] Added a function to parse a decimal uint, reporting the exponent where the resulting value is the smallest integral significand. --- src/urt/conv.d | 87 +++++++++++++++++++++++++++++++++++++++++++++++ src/urt/si/unit.d | 37 +++----------------- 2 files changed, 92 insertions(+), 32 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index 8889082..8bd0b79 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -81,6 +81,91 @@ ulong parse_uint(const(char)[] str, size_t* bytes_taken = null, int base = 10) p return value; } +ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* bytes_taken = null, uint base = 10) pure +{ + import urt.util : ctz, is_power_of_2; + + assert(base > 1 && base <= 36, "Invalid base"); + + const(char)* s = str.ptr; + const(char)* e = s + str.length; + + ulong value = 0; + int exp = 0; + uint digits = 0; + uint zero_seq = 0; + + for (; s < e; ++s) + { + char c = *s; + + if (c == '.') + { + if (s == str.ptr) + goto done; + ++s; + if (digits) + digits += zero_seq; + zero_seq = 0; + goto parse_decimal; + } + else if (c == '0') + { + ++zero_seq; + continue; + } + + uint digit = get_digit(c); + if (digit >= base) + break; + + for (uint i = 0; i <= zero_seq; ++i) + value = value * base; + value += digit; + + if (digits) + digits += zero_seq; + digits += 1; + zero_seq = 0; + } + + // number has no decimal point, tail zeroes are positive exp + exp = digits ? zero_seq : 0; + goto done; + +parse_decimal: + for (; s < e; ++s) + { + char c = *s; + + if (c == '0') + { + ++zero_seq; + continue; + } + + uint digit = get_digit(c); + if (digit >= base) + break; + + for (uint i = 0; i <= zero_seq; ++i) + value = value * base; + value += digit; + + if (digits) + digits += zero_seq; + digits += 1; + exp -= 1 + zero_seq; + zero_seq = 0; + } + +done: + exponent = exp; + if (bytes_taken) + *bytes_taken = s - str.ptr; + return value; +} + ulong parse_uint_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); @@ -99,6 +184,8 @@ ulong parse_uint_with_decimal(const(char)[] str, out ulong fixed_point_divisor, if (c == '.') { + if (s == str.ptr) + goto done; ++s; goto parse_decimal; } diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 1ebf7a0..1e2c70e 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -567,6 +567,8 @@ nothrow: ptrdiff_t parseUnit(const(char)[] s, out float preScale) pure { + import urt.conv : parse_uint_with_exponent; + preScale = 1; if (s.length == 0) @@ -599,41 +601,12 @@ nothrow: { size_t offset = 0; - // parse the exponent + // parse the scale factor int e = 0; if (term[0].is_numeric) { - if (term[0] == '0') - { - if (term.length < 2 || term[1] != '.') - return -1; - e = 1; - offset = 2; - while (offset < term.length) - { - if (term[offset] == '1') - break; - if (term[offset] != '0') - return -1; - ++e; - ++offset; - } - ++offset; - e = -e; - } - else if (term[0] == '1') - { - offset = 1; - while (offset < term.length) - { - if (term[offset] != '0') - break; - ++e; - ++offset; - } - } - else - return -1; + ulong sf = term.parse_uint_with_exponent(e, &offset); + preScale *= sf; } if (offset == term.length) From 50d5ba50da76de7f9716a4b978b14863397dffc1 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 31 Dec 2025 23:54:21 +1000 Subject: [PATCH 55/91] Added unittest for parse_uint_with_exponent... fixed a bug. --- src/urt/conv.d | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index 8bd0b79..160eb11 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -104,9 +104,7 @@ ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* byte if (s == str.ptr) goto done; ++s; - if (digits) - digits += zero_seq; - zero_seq = 0; + exp = zero_seq; goto parse_decimal; } else if (c == '0') @@ -119,12 +117,13 @@ ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* byte if (digit >= base) break; - for (uint i = 0; i <= zero_seq; ++i) - value = value * base; - value += digit; - if (digits) + { + for (uint i = 0; i <= zero_seq; ++i) + value = value * base; digits += zero_seq; + } + value += digit; digits += 1; zero_seq = 0; } @@ -148,16 +147,19 @@ parse_decimal: if (digit >= base) break; - for (uint i = 0; i <= zero_seq; ++i) - value = value * base; - value += digit; - 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) + exp = 0; // didn't parse any digits; reset exp to 0 done: exponent = exp; @@ -166,6 +168,19 @@ done: return value; } +unittest +{ + int e; + size_t taken; + 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); +} + ulong parse_uint_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = null, int base = 10) pure { assert(base > 1 && base <= 36, "Invalid base"); From 50f7c18692a72a92e2ff1d3fcd38ae05775ca568 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 7 Jan 2026 15:11:52 +1000 Subject: [PATCH 56/91] Fixed some string formatting bugs and TODOs --- src/urt/encoding.d | 20 ++-- src/urt/format/json.d | 147 ++++++++++++++++------------ src/urt/inet.d | 30 +++--- src/urt/si/unit.d | 69 ++++++++++---- src/urt/string/format.d | 205 +++++++++++++++++++++++++--------------- 5 files changed, 285 insertions(+), 186 deletions(-) diff --git a/src/urt/encoding.d b/src/urt/encoding.d index 43fa85e..9f76aa0 100644 --- a/src/urt/encoding.d +++ b/src/urt/encoding.d @@ -8,10 +8,10 @@ enum HexDecode(string str) = () { ubyte[hex_decode_length(str.length)] 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 source_length) pure +size_t base64_encode_length(size_t source_length) pure => (source_length + 2) / 3 * 4; -ptrdiff_t base64_encode_length(const void[] data) pure +size_t base64_encode_length(const void[] data) pure => base64_encode_length(data.length); ptrdiff_t base64_encode(const void[] data, char[] result) pure @@ -58,10 +58,10 @@ ptrdiff_t base64_encode(const void[] data, char[] result) pure return out_len; } -ptrdiff_t base64_decode_length(size_t source_length) pure +size_t base64_decode_length(size_t source_length) pure => source_length / 4 * 3; -ptrdiff_t base64_decode_length(const char[] data) pure +size_t base64_decode_length(const char[] data) pure => base64_decode_length(data.length); ptrdiff_t base64_decode(const char[] data, void[] result) pure @@ -143,10 +143,10 @@ unittest assert(data[0..10] == decoded[0..10]); } -ptrdiff_t hex_encode_length(size_t sourceLength) pure +size_t hex_encode_length(size_t sourceLength) pure => sourceLength * 2; -ptrdiff_t hex_encode_length(const void[] data) pure +size_t hex_encode_length(const void[] data) pure => data.length * 2; ptrdiff_t hex_encode(const void[] data, char[] result) pure @@ -157,10 +157,10 @@ ptrdiff_t hex_encode(const void[] data, char[] result) pure return toHexString(data, result).length; } -ptrdiff_t hex_decode_length(size_t sourceLength) pure +size_t hex_decode_length(size_t sourceLength) pure => sourceLength / 2; -ptrdiff_t hex_decode_length(const char[] data) pure +size_t hex_decode_length(const char[] data) pure => data.length / 2; ptrdiff_t hex_decode(const char[] data, void[] result) pure @@ -212,7 +212,7 @@ unittest } -ptrdiff_t url_encode_length(const char[] data) pure +size_t url_encode_length(const char[] data) pure { import urt.string.ascii : is_url; @@ -255,7 +255,7 @@ ptrdiff_t url_encode(const char[] data, char[] result) pure 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;) diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 4b65529..9a4e7dd 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -101,85 +101,98 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u return written; case Variant.Type.Buffer: - if (val.isString) + if (!val.isString) { - const char[] s = val.asString(); + import urt.encoding; - if (!buffer.ptr) + // emit raw buffer as base64 + const data = val.asBuffer(); + size_t enc_len = base64_encode_length(data.length); + if (buffer.ptr) { - 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 < 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; + } - if (buffer.length < s.length + 2) - return -1; + const char[] s = val.asString(); - buffer[0] = '"'; - // escape strings - size_t offset = 1; + if (!buffer.ptr) + { + size_t len = 0; 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'; + if (c == '\n' || c == '\r' || c == '\t' || c == '\b' || c == '\f') + len += 2; 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; - } + len += 6; } else if (c == '"' || c == '\\') - sub = 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 + 2) + if (buffer.length < offset + 7) return -1; - buffer[offset++] = c; + buffer[offset .. offset + 4] = "\\u00"; + offset += 4; + buffer[offset++] = hex_digits[c >> 4]; + buffer[offset++] = hex_digits[c & 0xF]; continue; } - - // write escape sequence - if (buffer.length < offset + 3) + } + else if (c == '"' || c == '\\') + sub = c; + else + { + if (buffer.length < offset + 2) return -1; - buffer[offset++] = '\\'; - buffer[offset++] = sub; + buffer[offset++] = c; + continue; } - buffer[offset++] = '"'; - return offset; - } - else - { - assert(false, "TODO: how are binary buffers represented in json?"); + + // write escape sequence + if (buffer.length < offset + 3) + return -1; + buffer[offset++] = '\\'; + buffer[offset++] = sub; } + buffer[offset++] = '"'; + return offset; case Variant.Type.Number: import urt.conv; @@ -198,11 +211,19 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u return val.asLong().format_int(buffer); 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; } } diff --git a/src/urt/inet.d b/src/urt/inet.d index b2d37bb..be1bddc 100644 --- a/src/urt/inet.d +++ b/src/urt/inet.d @@ -114,7 +114,7 @@ nothrow @nogc: return fnv1a(b[]); } - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] format_args) const pure + ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure { char[15] stack_buffer = void; char[] tmp = buffer.length < stack_buffer.sizeof ? stack_buffer : buffer; @@ -123,7 +123,7 @@ nothrow @nogc: { if (i > 0) tmp[offset++] = '.'; - offset += b[i].format_int(tmp[offset..$]); + offset += b[i].format_uint(tmp[offset..$]); } if (buffer.ptr && tmp.ptr == stack_buffer.ptr) @@ -139,22 +139,22 @@ nothrow @nogc: { ubyte[4] t; size_t offset = 0, len; - ulong i = s[offset..$].parse_int(&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..$].parse_int(&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..$].parse_int(&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..$].parse_int(&len); + i = s[offset..$].parse_uint(&len); offset += len; if (len == 0 || i > 255) return -1; @@ -297,7 +297,7 @@ nothrow @nogc: tmp[offset++] = ':'; continue; } - offset += s[i].format_int(tmp[offset..$], 16); + offset += s[i].format_uint(tmp[offset..$], 16); ++i; } @@ -338,7 +338,7 @@ nothrow @nogc: } if (str[offset] == ':') return -1; - ulong i = str[offset..$].parse_int(&len, 16); + ulong i = str[offset..$].parse_uint(&len, 16); if (len == 0) break; if (i > ushort.max || count[0] + count[1] == 8) @@ -418,7 +418,7 @@ nothrow @nogc: size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefix_len.format_int(tmp[offset..$]); + offset += prefix_len.format_uint(tmp[offset..$]); if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { @@ -436,7 +436,7 @@ nothrow @nogc: if (taken < 0 || s.length <= taken + 1 || s[taken++] != '/') return -1; size_t t; - ulong plen = s[taken..$].parse_int(&t); + ulong plen = s[taken..$].parse_uint(&t); if (t == 0 || plen > 32) return -1; addr = a; @@ -515,7 +515,7 @@ nothrow @nogc: size_t offset = addr.toString(tmp, null, null); tmp[offset++] = '/'; - offset += prefix_len.format_int(tmp[offset..$]); + offset += prefix_len.format_uint(tmp[offset..$]); if (buffer.ptr && tmp.ptr == stack_buffer.ptr) { @@ -533,7 +533,7 @@ nothrow @nogc: if (taken < 0 || s.length <= taken + 1 || s[taken++] != '/') return -1; size_t t; - ulong plen = s[taken..$].parse_int(&t); + ulong plen = s[taken..$].parse_uint(&t); if (t == 0 || plen > 128) return -1; addr = a; @@ -665,7 +665,7 @@ nothrow @nogc: { offset = _a.ipv4.addr.toString(tmp, null, null); tmp[offset++] = ':'; - offset += _a.ipv4.port.format_int(tmp[offset..$]); + offset += _a.ipv4.port.format_uint(tmp[offset..$]); } else { @@ -673,7 +673,7 @@ nothrow @nogc: offset = 1 + _a.ipv6.addr.toString(tmp[1 .. $], null, null); tmp[offset++] = ']'; tmp[offset++] = ':'; - offset += _a.ipv6.port.format_int(tmp[offset..$]); + offset += _a.ipv6.port.format_uint(tmp[offset..$]); } if (buffer.ptr && tmp.ptr == stack_buffer.ptr) @@ -720,7 +720,7 @@ nothrow @nogc: if (s.length > taken && s[taken] == ':') { size_t t; - ulong p = s[++taken..$].parse_int(&t); + ulong p = s[++taken..$].parse_uint(&t); if (t == 0 || p > ushort.max) return -1; taken += t; diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 1e2c70e..c0c4ae5 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -690,13 +690,27 @@ nothrow: { if (siScale && exp == -2) { - if (buffer.length == 0) - return -1; - buffer[0] = '%'; + 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!"); + assert(false, "TODO!"); // how (or should?) we encode a scale as a unit type? } size_t len = 0; @@ -711,18 +725,24 @@ nothrow: { if (y == 1) { - if (buffer.length < 2) - return -1; + if (buffer.ptr) + { + if (buffer.length < 2) + return -1; + buffer[0..2] = "10"; + } --x; - buffer[0..2] = "10"; len += 2; } else { - if (buffer.length < 3) - return -1; + if (buffer.ptr) + { + if (buffer.length < 3) + return -1; + buffer[0..3] = "100"; + } x -= 2; - buffer[0..3] = "100"; len += 3; } } @@ -730,17 +750,24 @@ nothrow: if (x != 0) { - if (buffer.length <= len) - return -1; - buffer[len++] = "qryzafpnum kMGTPEZYRQ"[x/3 + 10]; + 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.length < len + name.length) - return -1; - buffer[len .. len + name.length] = *name; + if (buffer.ptr) + { + if (buffer.length < len + name.length) + return -1; + buffer[len .. len + name.length] = *name; + } len += name.length; } else @@ -753,9 +780,12 @@ nothrow: { if (const string* name = this in scaledUnitNames) { - if (buffer.length < len + name.length) - return -1; - buffer[len .. len + name.length] = *name; + if (buffer.ptr) + { + if (buffer.length < len + name.length) + return -1; + buffer[len .. len + name.length] = *name; + } len += name.length; } else @@ -776,7 +806,6 @@ nothrow: return r; } - size_t toHash() const pure => pack; diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 7784113..613a819 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -7,29 +7,27 @@ 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)(auto ref 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; } +alias formatValue = toString; // TODO: remove me? + char[] concat(Args...)(char[] buffer, auto ref Args args) { static if (Args.length == 0) @@ -83,11 +81,10 @@ char[] concat(Args...)(char[] buffer, auto ref Args args) // 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... + StringifyFunc[Args.length] arg_funcs = void; static foreach(i, arg; args) - argFuncs[i] = FormatArg(arg); - char[] r = concatImpl(buffer, argFuncs); + arg_funcs[i] = get_to_string_func(arg); + char[] r = concatImpl(buffer, arg_funcs); debug if (!__ctfe) InFormatFunction = false; return r; } @@ -104,86 +101,146 @@ 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 && 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 - => toString(buffer, format, args); + StringifyFunc getString; ptrdiff_t getLength(const(char)[] format, const(FormatArg)[] args) const nothrow @nogc => getString(null, format, args); - bool canInt() const nothrow @nogc - => toInt != null; + bool canInt() const pure nothrow @nogc + => _to_int_fun !is null; - ptrdiff_t getInt() const nothrow @nogc - => toInt(); + ptrdiff_t getInt() const pure nothrow @nogc + { + 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 +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); + +template to_string_overload_index(T) { - return cast(DefFormat!T*)&value; + 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; + }(); } -ptrdiff_t defToString(T)(ref const(T) value, char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) nothrow @nogc +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(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFunc)) + return &__traits(getOverloads, value, "toString", true)[overload_index]; + + // TODO: if toString is a template, we need to instantiate it and then captuire the function pointer... + + static if (is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFuncReduced))// || +// is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]!()) : StringifyFuncReduced)) + { + StringifyFunc d; + d.ptr = &value; + d.funcptr = &ToStringShim.shim1!T; + return d; + } + + static if (is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFuncReduced2))// || +// is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]!()) : StringifyFuncReduced2)) + { + StringifyFunc d; + d.ptr = &value; + d.funcptr = &ToStringShim.shim2!T; + return d; + } + + // 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) { - return (cast(DefFormat!T*)&value).toString(buffer, format, formatArgs); + 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); + struct DefFormat(T) { T value; @@ -588,6 +645,8 @@ struct DefFormat(T) } else static if (is(T == struct)) { + static assert(!__traits(hasMember, T, "toString"), "Struct with custom toString not properly selected!"); + // general structs if (buffer.ptr) { @@ -645,24 +704,14 @@ struct DefFormat(T) else static assert(false, "Not implemented for type: ", T.stringof); } - - static if (is_some_int!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; - } - } } -char[] concatImpl(char[] buffer, const(FormatArg)[] args) nothrow @nogc +char[] concatImpl(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; From 73cd497905665b60816c162002d33ee3ed01eb68 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 8 Jan 2026 12:48:48 +1000 Subject: [PATCH 57/91] Properly support template toString functions, and also improved/simplified the implementation. --- src/urt/string/format.d | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 613a819..ae564c6 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -181,26 +181,27 @@ StringifyFunc get_to_string_func(T)(ref T value) static if (overload_index >= 0) { - static if (is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFunc)) - return &__traits(getOverloads, value, "toString", true)[overload_index]; + alias overload = __traits(getOverloads, T, "toString", true)[overload_index]; + static if (__traits(isTemplate, overload)) + alias to_string = overload!(); + else + alias to_string = overload; - // TODO: if toString is a template, we need to instantiate it and then captuire the function pointer... + // TODO: we can't alias the __traits(child) expression, so we need to repeat it everywhere! - static if (is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFuncReduced))// || -// is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]!()) : StringifyFuncReduced)) - { - StringifyFunc d; - d.ptr = &value; - d.funcptr = &ToStringShim.shim1!T; - return d; - } + alias method_type = typeof(&__traits(child, value, to_string)); - static if (is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]) : StringifyFuncReduced2))// || -// is(typeof(&__traits(getOverloads, value, "toString", true)[overload_index]!()) : StringifyFuncReduced2)) + static if (is(method_type : StringifyFunc)) + return &__traits(child, value, to_string); + + else static if (is(method_type : StringifyFuncReduced) || is(method_type : StringifyFuncReduced2)) { StringifyFunc d; d.ptr = &value; - d.funcptr = &ToStringShim.shim2!T; + static if (is(method_type : StringifyFuncReduced)) + d.funcptr = &ToStringShim.shim1!T; + else + d.funcptr = &ToStringShim.shim2!T; return d; } From a672ee1ff40628a1b1dabd7ff389380ca1ce8357 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 8 Jan 2026 17:08:40 +1000 Subject: [PATCH 58/91] Fleshed out the wildcard match, and Claude added a shitload of tests! --- src/urt/string/package.d | 165 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 8 deletions(-) diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 81cbf75..ad11e2c 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -459,19 +459,66 @@ unittest } -bool wildcardMatch(const(char)[] wildcard, const(char)[] value) pure +bool wildcard_match(const(char)[] wildcard, const(char)[] value, bool value_wildcard = false) pure { - // TODO: write this function... + const(char)* a = wildcard.ptr, ae = a + wildcard.length, b = value.ptr, be = b + value.length; + const(char)* star_a = null, star_b = null; - // HACK: we just use this for tail wildcards right now... - for (size_t i = 0; i < wildcard.length; ++i) + while (a < ae && b < be) { - if (wildcard[i] == '*') - return true; - if (wildcard[i] != value[i]) + char ca_orig = *a, cb_orig = *b; + char ca = ca_orig, cb = cb_orig; + + // handle escape + if (ca == '\\' && a + 1 < ae) + ca = *++a; + if (value_wildcard && cb == '\\' && b + 1 < be) + cb = *++b; + + // handle wildcards + if (ca_orig == '*') + { + star_a = ++a; + star_b = b; + continue; + } + if (value_wildcard && cb_orig == '*') + { + star_b = ++b; + star_a = a; + continue; + } + + // compare next char + if (ca_orig == '?' || (value_wildcard && cb_orig == '?') || ca == cb) + { + ++a; + ++b; + continue; + } + + // backtrack: expand previous * match + if (!star_a) return false; + a = star_a; + b = ++star_b; } - return wildcard.length == value.length; + + // skip past tail wildcards + while (a < ae && *a == '*') + ++a; + if (value_wildcard) + { + while (b < be && *b == '*') + ++b; + } + + // check for match + if (a == ae && (b == be || star_a !is null)) + return true; + if (value_wildcard && b == be && star_b !is null) + return true; + return false; } @@ -500,4 +547,106 @@ unittest 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")); + + // 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("\\*", "a")); + 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)); } From 3c483a258b1c06d04c479186a445ca8e8e5ae862 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 8 Jan 2026 17:47:49 +1000 Subject: [PATCH 59/91] Added case insensitive option, numeric match, and optional match. --- src/urt/string/package.d | 354 +++++++++++++++++++++++++++++++++++---- 1 file changed, 318 insertions(+), 36 deletions(-) diff --git a/src/urt/string/package.d b/src/urt/string/package.d index ad11e2c..0e39201 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -459,68 +459,186 @@ unittest } -bool wildcard_match(const(char)[] wildcard, const(char)[] value, bool value_wildcard = false) pure +// 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 { const(char)* a = wildcard.ptr, ae = a + wildcard.length, b = value.ptr, be = b + value.length; - const(char)* star_a = null, star_b = null; - while (a < ae && b < be) + static inout(char)* skip_wilds(inout(char)* p, const(char)* pe) + { + 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 { - char ca_orig = *a, cb_orig = *b; - char ca = ca_orig, cb = cb_orig; + const(char)* a, b; + } + BacktrackState[64] backtrack_stack = void; + size_t backtrack_depth = 0; - // handle escape - if (ca == '\\' && a + 1 < ae) - ca = *++a; - if (value_wildcard && cb == '\\' && b + 1 < be) - cb = *++b; + 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 == '*') { - star_a = ++a; - star_b = b; - continue; + 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; } - if (value_wildcard && cb_orig == '*') + + // handle optionals + if (ca_orig == '~') { - star_b = ++b; - star_a = a; + 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 == '?' || (value_wildcard && cb_orig == '?') || ca == cb) + if (ca_orig == '#') { - ++a; - ++b; - continue; + 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; - // backtrack: expand previous * match - if (!star_a) + try_backtrack: + if (backtrack_depth == 0) return false; - a = star_a; - b = ++star_b; + + --backtrack_depth; + a = backtrack_stack[backtrack_depth].a; + b = backtrack_stack[backtrack_depth].b; + continue; + + advance: + ++a, ++b; } - // skip past tail wildcards - while (a < ae && *a == '*') - ++a; - if (value_wildcard) + // check the strings are equal... + if (a == ae) { - while (b < be && *b == '*') - ++b; + if (value_wildcard) + b = skip_wilds(b, be); + if (b == be) + return true; } - - // check for match - if (a == ae && (b == be || star_a !is null)) - return true; - if (value_wildcard && b == be && star_b !is null) + else if (b == be && skip_wilds(a, ae) == ae) return true; - return false; + + 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 { @@ -560,6 +678,149 @@ unittest 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")); @@ -594,8 +855,11 @@ unittest // 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("\\*\\?\\\\", "*?\\")); @@ -649,4 +913,22 @@ unittest 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")); } From 2233eecf97396f04fb31bed1962f2377d64724cb Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 21 Jan 2026 10:08:24 +1000 Subject: [PATCH 60/91] Fixed unit parsing. - Moved the scaling factor outside of the SI unit parsing, since all units can be scaled. - Fixed an issue where some units could incorrectly have SI prefixes added to them. --- src/urt/si/unit.d | 163 ++++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index c0c4ae5..8994aef 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -595,85 +595,85 @@ nothrow: if (p == 0) return -1; // invalid exponent - if (const ScaledUnit* su = term in noScaleUnitMap) + size_t offset = 0; + + // parse the scale factor + int e = 0; + if (term[0].is_numeric) + { + ulong sf = term.parse_uint_with_exponent(e, &offset); + preScale *= sf; + } + + if (offset == term.length) + r *= ScaledUnit(Unit(), e); + else if (const ScaledUnit* su = term[offset .. $] in noScaleUnitMap) + { r *= (*su) ^^ (invert ? -p : p); + preScale *= 10.0^^e; + } else { - size_t offset = 0; - - // parse the scale factor - int e = 0; - if (term[0].is_numeric) + // try and parse SI prefix... + switch (term[offset]) { - ulong sf = term.parse_uint_with_exponent(e, &offset); - preScale *= sf; + 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) - r *= ScaledUnit(Unit(), e); - else + return -1; + + term = term[offset .. $]; + if (const Unit* u = term in unitMap) { - // try and parse SI prefix... - switch (term[offset]) + if (term == "kg") { - 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) + // we alrady parsed the 'k', so this string must have been "kkg", which is nonsense return -1; - - term = term[offset .. $]; - if (const Unit* u = term in unitMap) - { - if (term == "kg") - { - // we alrady parsed the 'k' - 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 noScaleUnitMap) - { - r *= (*su) ^^ (invert ? -p : p); - preScale *= 10^^e; } - else - return -1; // string was not taken? + 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); + preScale *= 10.0^^e; } + else + return -1; // string was not taken? } if (sep == '/') @@ -986,7 +986,7 @@ immutable Unit[string] unitMap = [ "kg" : Kilogram, "s" : Second, "A" : Ampere, - "°K" : Kelvin, + "K" : Kelvin, "cd" : Candela, "rad" : Radian, @@ -1030,24 +1030,31 @@ immutable ScaledUnit[string] noScaleUnitMap = [ "deg" : Degree, "°C" : Celsius, "°F" : Fahrenheit, - "Ah" : AmpereHour, - "Wh" : WattHour, "cy" : Cycle, - "Hz" : Hertz, "psi" : PSI, - - // questionable... :/ - "VAh" : WattHour, - "varh" : WattHour, + "%" : Percent, + "‰" : Permille, + "‱" : ScaledUnit(Unit(), -4), + "ppm" : ScaledUnit(Unit(), -6), ]; +// these can have SI prefixes immutable ScaledUnit[string] scaledUnitMap = [ - "%" : Percent, - "‰" : Permille, "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 takePower(ref const(char)[] s) pure { size_t e = s.findFirst('^'); From 569e27bd85db0b5ea78729a2c192e4dc1aae1935 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 24 Jan 2026 16:21:41 +1000 Subject: [PATCH 61/91] Trim underscore from enum keys, which often exist to disambiguate from keywords. - Tweak `trim` to accept a predicate (default to `is_whitespace`) - Tweak `uni_seq_len` to remove some redundant logic. - Fix `uni_compare` and simplify the implementation a bit. --- src/urt/meta/package.d | 5 +++- src/urt/string/package.d | 10 +++---- src/urt/string/uni.d | 65 +++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index bb6e8d9..c703fc5 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -391,7 +391,7 @@ private: return total; }(); - enum MakeKI(ushort i) = KI(enum_members[i], i); + 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]; @@ -487,6 +487,9 @@ VoidEnumInfo* make_enum_info(T)(const(char)[] name, const(char)[][] keys, T[] va private: +import urt.string : trim; +enum trim_key(string key) = key.trim!(c => c == '_'); + template is_same(alias a, alias b) { static if (!is(typeof(&a && &b)) // at least one is an rvalue diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 0e39201..08025ea 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -223,25 +223,25 @@ bool endsWith(const(char)[] s, const(char)[] suffix) pure return cmp(s[$ - suffix.length .. $], suffix) == 0; } -inout(char)[] trim(bool Front = true, bool Back = true)(inout(char)[] s) pure +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 && is_whitespace(s.ptr[first])) + while (first < s.length && pred(s.ptr[first])) ++first; } static if (Back) { - while (last > first && is_whitespace(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) pure { diff --git a/src/urt/string/uni.d b/src/urt/string/uni.d index ce7f7d1..8447be9 100644 --- a/src/urt/string/uni.d +++ b/src/urt/string/uni.d @@ -6,36 +6,38 @@ import urt.traits : is_some_char; pure nothrow @nogc: -size_t uni_seq_len(const(char)[] s) +size_t uni_seq_len(const(char)[] str) { - if (s.length == 0) - return 0; + 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 (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + return (str.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; else if ((s[0] & 0xF0) == 0xE0) // 3-byte sequence: 1110xxxx 10xxxxxx 10xxxxxx - return (s.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : - (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + 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 (s.length >= 4 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) ? 4 : - (s.length >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) ? 3 : - (s.length >= 2 && (s[1] & 0xC0) == 0x80) ? 2 : 1; + 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)[] s) +size_t uni_seq_len(const(wchar)[] str) { - if (s.length == 0) - return 0; - if (s[0] >= 0xD800 && s[0] < 0xDC00 && s.length >= 2 && s[1] >= 0xDC00 && s[1] < 0xE000) + 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) - => s.length > 0; + => 1; size_t uni_strlen(C)(const(C)[] s) if (is_some_char!C) @@ -552,38 +554,41 @@ int uni_compare(T, U)(const(T)[] s1, const(U)[] s2) // TODO: this is crude and insufficient; doesn't handle compound diacritics, etc (needs a NFKC normalisation step) - while (p1 < p1end && p2 < p2end) + while (true) { - dchar a = *p1; + // 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) { - dchar b = *p2; if (a != b) - { - if (b >= 0x80) - { - size_t _; - b = next_dchar(p2[0 .. p2end - p2], _); - } - return cast(int)a - cast(int)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); - dchar b = next_dchar(p2[0 .. p2end - p2], bl); + b = next_dchar(p2[0 .. p2end - p2], bl); if (a != b) return cast(int)a - cast(int)b; p1 += al; - p2 += 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; } int uni_compare_i(T, U)(const(T)[] s1, const(U)[] s2) From f7de5c1c528a2a141e9a214515498d71a79e7bd4 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 24 Jan 2026 20:58:23 +1000 Subject: [PATCH 62/91] Add a function to adjust a Quantity scale. Minimise error accumulation from floating point precision loss from unnecessary arithmetic. --- src/urt/si/quantity.d | 90 ++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index df9775f..0d5a6b2 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -69,7 +69,7 @@ nothrow @nogc: assert(isCompatible(b), "Incompatible units!"); else static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); - value = adjustScale(b); + value = adjust_scale(b); } } @@ -97,7 +97,7 @@ nothrow @nogc: assert(isCompatible(b), "Incompatible units!"); else static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); - value = adjustScale(b); + value = adjust_scale(b); } } @@ -132,7 +132,7 @@ nothrow @nogc: static assert(IsCompatible!_U, "Incompatible units: ", unit.toString, " and ", b.unit.toString); Quantity!(TypeForOp!(op, T, U), _unit) r; - r.value = mixin("value " ~ op ~ " adjustScale(b)"); + r.value = mixin("value " ~ op ~ " adjust_scale(b)"); static if (Dynamic) r.unit = unit; return r; @@ -211,7 +211,7 @@ nothrow @nogc: assert(isCompatible(r), "Incompatible units!"); else static assert(IsCompatible!_U, "Incompatible units: ", r.unit.toString, " and ", unit.toString); - r.value = cast(U)r.adjustScale(this); + r.value = cast(U)r.adjust_scale(this); static if (T.Dynamic) r.unit = unit; return r; @@ -258,7 +258,7 @@ nothrow @nogc: rhs = rhs*rScale + rTrans; }} else - rhs = adjustScale(rh); + rhs = adjust_scale(rh); compare: static if (epsilon == 0) @@ -287,7 +287,19 @@ nothrow @nogc: } else Quantity!(T, ScaledUnit(unit.unit)) r; - r.value = r.adjustScale(this); + 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; } @@ -354,40 +366,48 @@ nothrow @nogc: } private: - T adjustScale(U, ScaledUnit _U)(Quantity!(U, _U) b) const pure + T adjust_scale(U, ScaledUnit _U)(Quantity!(U, _U) b) const pure { - static if (Dynamic) - { - auto lScale = unit.scale!true(); - auto lTrans = unit.offset!true(); - } + static if (!Dynamic && !b.Dynamic && unit == b.unit) + return cast(T)b.value; 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(); - } + if (unit == b.unit) + return cast(T)b.value; - static if (Dynamic || b.Dynamic) - { - auto scale = lScale*rScale; - auto trans = lTrans + lScale*rTrans; - } - else - { - enum scale = lScale*rScale; - enum trans = lTrans + lScale*rTrans; + 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); } } From 5d854a89cc3d7916a48b7efba527f21c0b98f7fd Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 25 Jan 2026 19:27:04 +1000 Subject: [PATCH 63/91] Add some helpers to StringCacheBuilder --- src/urt/string/string.d | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index 1cf61e2..e3349c1 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -45,6 +45,7 @@ nothrow @nogc: ushort add_string(const(char)[] s) pure { 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) @@ -69,6 +70,15 @@ nothrow @nogc: 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; From 0efbdde6382783953854b7486f2387a7b580aa8e Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 28 Jan 2026 15:22:17 +1000 Subject: [PATCH 64/91] Array!char.concat/append was extended to support the same set of arguments as string concatenation. --- src/urt/array.d | 136 ++++++++++++++++++++++++++++++++++------ src/urt/string/string.d | 22 +++---- 2 files changed, 127 insertions(+), 31 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 6674151..4f61066 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; +import urt.traits : is_some_char, is_primitive, is_trivial; nothrow @nogc: @@ -314,17 +314,120 @@ nothrow @nogc: } // manipulation - ref Array!(T, EmbedCount) concat(Things...)(auto ref Things things) + static if (!is_some_char!T) { - reserve(_length + things.length); - static foreach (i; 0 .. things.length) + alias concat = append; // TODO: REMOVE THIS ALIAS, we phase out the old name... + + ref Array!(T, EmbedCount) append(Things...)(auto ref Things things) { - static if (is(T == class) || is(T == interface)) - ptr[_length++] = things[i]; - else - emplace!T(&ptr[_length++], forward!(things[i])); + 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; } - 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 @@ -592,18 +695,9 @@ nothrow @nogc: return [x, y]; } - void opOpAssign(string op : "~", U)(auto ref U el) - if (is(U : T)) - { - pushBack(forward!el); - } - - 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) @@ -700,6 +794,8 @@ nothrow @nogc: } private: + enum copy_elements = is(T == class) || is(T == interface) || is_primitive!T || is_trivial!T; + T* ptr; uint _length; diff --git a/src/urt/string/string.d b/src/urt/string/string.d index e3349c1..be23b03 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -700,9 +700,9 @@ nothrow @nogc: return this; } - ref MutableString!Embed appendFormat(Things...)(const(char)[] format, auto ref Things args) + ref MutableString!Embed append_format(Things...)(const(char)[] format, auto ref Things args) { - insertFormat(length(), format, forward!args); + insert_format(length(), format, forward!args); return this; } @@ -718,7 +718,7 @@ nothrow @nogc: { if (ptr) writeLength(0); - insertFormat(0, format, forward!args); + insert_format(0, format, forward!args); return this; } @@ -750,7 +750,7 @@ nothrow @nogc: return this; } - ref MutableString!Embed insertFormat(Things...)(size_t offset, const(char)[] format, auto ref Things args) + 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, next_power_of_2; @@ -923,11 +923,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 @@ -948,13 +948,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 From 361a2839f77b8936e8ceebcc45e5186ea06ca55f Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 30 Jan 2026 09:37:07 +1000 Subject: [PATCH 65/91] Fix variant opCmp --- src/urt/variant.d | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/urt/variant.d b/src/urt/variant.d index d6aebdd..8b2a944 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -426,21 +426,19 @@ nothrow @nogc: static double asDoubleWithBool(ref const Variant v) => v.isBool() ? double(v.asBool()) : v.asDouble(); - if (a.isQuantity || b.isQuantity) + uint aunit = a.isQuantity ? a.count : 0; + uint bunit = b.isQuantity ? b.count : 0; + if (aunit || bunit) { // we can't compare different units - uint aunit = a.isQuantity ? (a.count & 0xFFFFFF) : 0; - uint bunit = b.isQuantity ? (b.count & 0xFFFFFF) : 0; - if (aunit != bunit) + if ((aunit & 0xFFFFFF) != (bunit & 0xFFFFFF)) { - r = aunit - bunit; + r = (aunit & 0xFFFFFF) - (bunit & 0xFFFFFF); break; } // matching units, but we'll only do quantity comparison if there is some scaling - ubyte ascale = a.isQuantity ? (a.count >> 24) : 0; - ubyte bscale = b.isQuantity ? (b.count >> 24) : 0; - if (ascale || bscale) + if ((aunit >> 24) != (bunit >> 24)) { Quantity!double aq = a.isQuantity ? a.asQuantity!double() : Quantity!double(asDoubleWithBool(*a)); Quantity!double bq = b.isQuantity ? b.asQuantity!double() : Quantity!double(asDoubleWithBool(*b)); @@ -449,7 +447,7 @@ nothrow @nogc: } } - if (a.flags & Flags.FloatFlag || b.flags & Flags.FloatFlag) + 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? From 2be4ae3169134740b97d5403fc35be830140a19f Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 1 Feb 2026 14:02:41 +1000 Subject: [PATCH 66/91] Improve the DateTime parsing, and supported DateTime -> SysTime conversion. --- src/urt/time.d | 224 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 51 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index 20857dc..7cca87f 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -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 + ticksSinceBoot); + else + return MonoTime(ticks - ticksSinceBoot); + } else - return MonoTime(ticks - ticksSinceBoot); + static assert(false, "constraint out of sync"); } bool opEquals(Time!clock b) const @@ -468,27 +474,31 @@ pure nothrow @nogc: nsecs -= digit * digit_multipliers[m++]; } } + // TODO: timezone suffix? return offset; } ptrdiff_t fromString(const(char)[] s) { - import urt.conv : parse_int; + import urt.conv : parse_int, parse_uint; import urt.string.ascii : ieq, is_numeric, is_whitespace, to_lower; - if (s.length < 14) - return -1; + month = Month.Unspecified; + day = 0; + hour = 0; + minute = 0; + second = 0; + ns = 0; + size_t offset = 0; // parse year - if (s[0..2].ieq("bc")) + if (s.length >= 2 && s[0..2].ieq("bc")) { offset = 2; - if (s[2] == ' ') + if (s.length >= 3 && s[2] == ' ') ++offset; } - if (s[offset] == '+') - return -1; size_t len; long value = s[offset..$].parse_int(&len); if (len == 0) @@ -504,13 +514,15 @@ pure nothrow @nogc: year = cast(short)value; offset += len; - if (s[offset] != '-' && s[offset] != '/') - return -1; + 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])) @@ -528,8 +540,8 @@ pure nothrow @nogc: month = cast(Month)value; offset += len; - if (s[offset] != '-' && s[offset] != '/') - return -1; + if (offset == s.length || (s[offset] != '-' && s[offset] != '/')) + return offset; // parse day value = s[++offset..$].parse_int(&len); @@ -538,8 +550,8 @@ pure nothrow @nogc: day = cast(ubyte)value; offset += len; - if (offset >= s.length || (s[offset] != 'T' && s[offset] != ' ')) - return -1; + if (offset == s.length || (s[offset] != 'T' && s[offset] != ' ')) + return offset; // parse hour value = s[++offset..$].parse_int(&len); @@ -548,49 +560,116 @@ pure nothrow @nogc: hour = cast(ubyte)value; offset += len; - if (offset >= s.length || s[offset++] != ':') - return -1; + if (offset == s.length) + return 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 (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 || s[offset++] != ':') - return -1; + if (offset == s.length) + return 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 (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; - ns = 0; - if (offset < s.length && s[offset] == '.') - { - // parse fractions - ++offset; - uint multiplier = 100_000_000; - while (offset < s.length && multiplier > 0 && s[offset].is_numeric) + 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] == '.') { - ns += (s[offset++] - '0') * multiplier; - multiplier /= 10; + // fraction of minute + assert(false, "TODO"); } - if (multiplier == 100_000_000) - return -1; // no number after the dot + } + else if (s[offset] == '.') + { + // fraction of hour + assert(false, "TODO"); } - if (offset < s.length && s[offset].to_lower == 'z') + if (offset == s.length) + return offset; + + if (s[offset].to_lower == 'z') { - ++offset; // TODO: UTC timezone designator... - assert(false, "TODO: we need to know our local timezone..."); +// assert(false, "TODO: we need to know our local timezone..."); + + return offset + 1; } - return offset; + if (s[offset] != '-' && s[offset] != '+') + return offset; + + size_t tz_offset = 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 (s[offset] == '-') + tz_hr = -tz_hr; + +// assert(false, "TODO: we need to know our local timezone..."); + + return tz_offset; } } @@ -663,7 +742,14 @@ SysTime getSysTime() SysTime getSysTime(DateTime time) pure { - assert(false, "TODO: convert to SysTime..."); + version (Windows) + return dateTimeToFileTime(time); + else version (Posix) + { + assert(false, "TODO"); + } + else + static assert(false, "TODO"); } DateTime getDateTime() @@ -896,8 +982,18 @@ unittest 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.") == -1); - assert(dt.fromString("2025-01-01") == -1); + 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); @@ -934,6 +1030,32 @@ version (Windows) return dt; } + + SysTime dateTimeToFileTime(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) { From 114e944c7a75dffd04cba0cce32413038565cd0d Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 1 Feb 2026 14:03:26 +1000 Subject: [PATCH 67/91] Move function out of urt. --- src/urt/string/package.d | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 08025ea..4fd0e24 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -243,19 +243,6 @@ alias trimFront(alias pred = is_whitespace) = trim!(pred, true, false); alias trimBack(alias pred = is_whitespace) = trim!(pred, false, true); -inout(char)[] trimComment(char Delimiter)(inout(char)[] s) pure -{ - 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 { for (size_t i = 0; i < s.length; ++i) From fe4b7002043120d391c7f04ecf1383f93fc5a0a6 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 1 Feb 2026 14:00:54 +1000 Subject: [PATCH 68/91] Parse e-notation, delete the `with_decimal` ones, normalise, and reorganise these implementations. --- src/urt/conv.d | 280 +++++++++++++++++++++--------------------- src/urt/format/json.d | 33 +++-- src/urt/math.d | 1 + src/urt/variant.d | 8 +- 4 files changed, 158 insertions(+), 164 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index 160eb11..dfdc9f0 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -8,47 +8,52 @@ nothrow @nogc: // on error or not-a-number cases, bytes_taken will contain 0 -long parse_int(const(char)[] str, size_t* bytes_taken = null, int base = 10) pure -{ - size_t i = 0; - bool neg = false; - if (str.length > 0) - { - char c = str.ptr[0]; - neg = c == '-'; - if (neg || c == '+') - i++; - } - - ulong value = str.ptr[i .. str.length].parse_uint(bytes_taken, base); +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 += i; - return neg ? -cast(long)value : cast(long)value; + *bytes_taken += p - s; + return neg ? -long(value) : long(value); } -long parse_int_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = 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].parse_uint_with_decimal(fixed_point_divisor, bytes_taken, base); +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 += i; - return neg ? -cast(long)value : cast(long)value; + *bytes_taken += p - s; + return neg ? -long(value) : long(value); } -ulong parse_uint(const(char)[] str, size_t* bytes_taken = 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; @@ -81,11 +86,19 @@ ulong parse_uint(const(char)[] str, size_t* bytes_taken = null, int base = 10) p return value; } -ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* bytes_taken = null, uint base = 10) pure +ulong parse_uint_with_base(const(char)[] str, size_t* bytes_taken = null) pure { - import urt.util : ctz, is_power_of_2; + 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; +} - assert(base > 1 && base <= 36, "Invalid base"); +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; @@ -129,8 +142,11 @@ ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* byte } // number has no decimal point, tail zeroes are positive exp - exp = digits ? zero_seq : 0; - goto done; + if (!digits) + goto nothing; + + exp = zero_seq; + goto check_exp; parse_decimal: for (; s < e; ++s) @@ -159,120 +175,83 @@ parse_decimal: zero_seq = 0; } if (!digits) - exp = 0; // didn't parse any digits; reset exp to 0 + goto nothing; -done: - exponent = exp; - if (bytes_taken) - *bytes_taken = s - str.ptr; - return value; -} - -unittest -{ - int e; - size_t taken; - 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); -} - -ulong parse_uint_with_decimal(const(char)[] str, out ulong fixed_point_divisor, size_t* bytes_taken = null, int base = 10) pure -{ - assert(base > 1 && base <= 36, "Invalid base"); - - ulong value = 0; - ulong divisor = 1; - - const(char)* s = str.ptr; - const(char)* e = s + str.length; - - // TODO: we could optimise the common base <= 10 case... - - for (; s < e; ++s) +check_exp: + // check for exponent part + if (s < e && (*s == 'e' || *s == 'E')) { - char c = *s; - - if (c == '.') + ++s; + bool exp_neg = false; + if (s < e) { - if (s == str.ptr) - goto done; - ++s; - goto parse_decimal; + char c = *s; + exp_neg = c == '-'; + if (exp_neg || c == '+') + ++s; } - uint digit = get_digit(c); - if (digit >= base) - break; - value = value*base + digit; - } - goto done; - -parse_decimal: - for (; s < e; ++s) - { - uint digit = get_digit(*s); - if (digit >= base) + int exp_value = 0; + const(char)* t = s; + for (; t < e; ++t) { - // 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; - break; + uint digit = *t - '0'; + if (digit > 9) + break; + exp_value = exp_value * 10 + digit; + } + if (t > s) + { + exp += exp_neg ? -exp_value : exp_value; + s = t; } - value = value*base + digit; - divisor *= base; } done: - fixed_point_divisor = divisor; + exponent = exp; if (bytes_taken) *bytes_taken = s - str.ptr; return value; -} -long parse_int_with_base(const(char)[] str, size_t* bytes_taken = null) pure -{ - const(char)* p = str.ptr; - int base = str.parse_base_prefix(); - if (base == 10) - return str.parse_int(bytes_taken); - ulong i = str.parse_uint(bytes_taken, base); - if (bytes_taken && *bytes_taken != 0) - *bytes_taken += str.ptr - p; - return i; +nothing: + exp = 0; + goto done; } -ulong parse_uint_with_base(const(char)[] str, size_t* bytes_taken = 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 = str.parse_base_prefix(); - ulong i = str.parse_uint(bytes_taken, base); - if (bytes_taken && *bytes_taken != 0) - *bytes_taken += 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(parse_uint("123") == 123); assert(parse_int("+123.456") == 123); assert(parse_int("-123.456", null, 10) == -123); - assert(parse_uint_with_decimal("123.456", divisor, null, 10) == 123456 && divisor == 1000); - assert(parse_int_with_decimal("123.456.789", divisor, &taken, 16) == 1193046 && taken == 7 && divisor == 4096); assert(parse_int("11001", null, 2) == 25); - assert(parse_int_with_decimal("-AbCdE.f", divisor, null, 16) == -11259375 && divisor == 16); 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("10e+2".parse_uint_with_exponent(e, &taken, 10) == 1 && e == 3 && taken == 5); + assert("0.01E+2".parse_uint_with_exponent(e, &taken, 10) == 1 && e == 0 && taken == 7); } int parse_int_fast(ref const(char)[] text, out bool success) pure @@ -337,17 +316,25 @@ unittest // 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, int base = 10) pure +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.parse_int_with_decimal(div, &taken, base); + 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(base, e); } unittest @@ -362,6 +349,7 @@ unittest 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); } @@ -626,7 +614,7 @@ template to(T) { long to(const(char)[] str) { - int base = parse_base_prefix(str); + uint base = parse_base_prefix(str); size_t taken; long r = parse_int(str, &taken, base); assert(taken == str.length, "String is not numeric"); @@ -637,7 +625,7 @@ template to(T) { double to(const(char)[] str) { - int base = parse_base_prefix(str); + uint base = parse_base_prefix(str); size_t taken; double r = parse_float(str, &taken, base); assert(taken == str.length, "String is not numeric"); @@ -681,35 +669,43 @@ template to(T) private: +// valid result is 0 .. 35; result is garbage outside that bound uint get_digit(char c) pure { uint zero_base = c - '0'; if (zero_base < 10) return zero_base; - uint A_base = c - 'A'; - if (A_base < 26) - return A_base + 10; - uint a_base = c - 'a'; - if (a_base < 26) - return a_base + 10; - return -1; + uint a_base = (c | 0x20) - 'a'; + return 10 + a_base; } -int parse_base_prefix(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; +} + /+ size_t format_struct(T)(ref T value, char[] buffer) nothrow @nogc diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 9a4e7dd..5866a3c 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -399,31 +399,28 @@ Variant parse_node(ref const(char)[] text) } 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 .. $].parse_uint_with_decimal(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) + // let's work out if value*10^^e is an integer? + bool is_integer = e >= 0; + for (; e > 0; --e) { - double d = cast(double)value; - if (neg) - d = -d; - d /= div; - return Variant(d); - } - else - { - 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/math.d b/src/urt/math.d index e3b06b8..ff63a20 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -63,6 +63,7 @@ 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) diff --git a/src/urt/variant.d b/src/urt/variant.d index 8b2a944..033f072 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -1060,8 +1060,8 @@ nothrow @nogc: { size_t taken; ScaledUnit unit; - ulong div; - long i = s.parse_int_with_decimal(div, &taken, 10); + int e; + long i = s.parse_int_with_exponent(e, &taken, 10); if (taken < s.length) { size_t t2 = unit.fromString(s[taken .. $]); @@ -1070,8 +1070,8 @@ nothrow @nogc: } if (taken == s.length) { - if (div != 1) - this = double(i) / div; + if (e != 0) + this = i * 10.0^^e; else this = i; if (unit.pack) From a913c441e30bb1aa4bb8bfb2a98cbc0511dcf9e6 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 2 Feb 2026 17:12:04 +1000 Subject: [PATCH 69/91] Fix CTFE implementation of qsort! --- src/urt/algorithm.d | 64 +++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/urt/algorithm.d b/src/urt/algorithm.d index 9995cfb..d44fec3 100644 --- a/src/urt/algorithm.d +++ b/src/urt/algorithm.d @@ -132,6 +132,9 @@ size_t binary_search(alias pred = void, T, Cmp...)(T[] arr, auto ref Cmp cmp_arg void qsort(alias pred = void, T)(T[] arr) pure { + if (arr.length <= 1) + return; + version (SmallSize) enum use_small_size_impl = true; else @@ -157,39 +160,44 @@ void qsort(alias pred = void, T)(T[] arr) pure 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(p[i], *pivot) < 0) i++; - while (pred(p[j], *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; + + swap(p[i], p[j]); + ++i; + --j; } - - if (j > 0) - qsort!pred(p[0 .. j + 1]); - if (i < arr.length) - qsort!pred(p[i .. arr.length]); } + + if (j >= 0) + qsort!pred(p[0 .. j + 1]); + if (i < n) + qsort!pred(p[i .. n]); } } From 73eb47134c6a817a302c5477742985d5ebec191d Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 2 Feb 2026 17:11:47 +1000 Subject: [PATCH 70/91] Quantity fix. --- src/urt/si/quantity.d | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index 0d5a6b2..bf9ae9c 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -146,11 +146,12 @@ nothrow @nogc: return mixin("this.value " ~ op ~ " value"); else { - Quantity!(TypeForOp!(op, T, U), unit) 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); } } From 6925ddd0f3ab5e15f7d20a1c92e5f416750ad538 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 7 Feb 2026 02:10:40 +1000 Subject: [PATCH 71/91] Fixed integer division. --- src/urt/si/unit.d | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 8994aef..b9be825 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -423,12 +423,12 @@ nothrow: 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]; @@ -436,9 +436,9 @@ nothrow: } 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; @@ -809,6 +809,17 @@ nothrow: 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 { @@ -881,9 +892,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 From 08df31921c796d6072da3aba7e2db30b6acf7583 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 5 Feb 2026 00:19:47 +1000 Subject: [PATCH 72/91] Reserve null in the string cache. --- src/urt/string/string.d | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/urt/string/string.d b/src/urt/string/string.d index be23b03..ccb06f9 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -37,13 +37,17 @@ struct StringCacheBuilder nothrow @nogc: this(char[] buffer) pure { - assert(buffer.length <= ushort.max, "Buffer too long"); + assert(buffer.length >= 2 && buffer.length <= ushort.max, "Invalid buffer length"); + buffer[0..2] = 0; this._buffer = buffer; - this._offset = 0; + 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) From 14bb0e67a9595c62cc493472bbde9cdd22354685 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 8 Feb 2026 18:37:14 +1000 Subject: [PATCH 73/91] Added parse_quantity which is like parse_float, but with a unit suffix. --- src/urt/conv.d | 92 +++++++++++++++++++++++++++++++--------- src/urt/si/quantity.d | 2 +- src/urt/si/unit.d | 29 +++++++------ src/urt/string/package.d | 61 ++++++++++++-------------- 4 files changed, 117 insertions(+), 67 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index dfdc9f0..f0ab5c1 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -1,6 +1,7 @@ module urt.conv; import urt.meta; +import urt.si.quantity; import urt.string; public import urt.string.format : toString; @@ -107,10 +108,11 @@ ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* byte int exp = 0; uint digits = 0; uint zero_seq = 0; + char c = void; for (; s < e; ++s) { - char c = *s; + c = *s; if (c == '.') { @@ -151,7 +153,7 @@ ulong parse_uint_with_exponent(const(char)[] str, out int exponent, size_t* byte parse_decimal: for (; s < e; ++s) { - char c = *s; + c = *s; if (c == '0') { @@ -179,32 +181,32 @@ parse_decimal: check_exp: // check for exponent part - if (s < e && (*s == 'e' || *s == 'E')) + if (s + 1 < e && ((*s | 0x20) == 'e')) { - ++s; - bool exp_neg = false; - if (s < e) + c = s[1]; + bool exp_neg = c == '-'; + if (exp_neg || c == '+') { - char c = *s; - exp_neg = c == '-'; - if (exp_neg || c == '+') - ++s; + if (s + 2 >= e || !s[2].is_numeric) + goto done; + s += 2; + } + else + { + if (!c.is_numeric) + goto done; + ++s; } int exp_value = 0; - const(char)* t = s; - for (; t < e; ++t) + for (; s < e; ++s) { - uint digit = *t - '0'; + uint digit = *s - '0'; if (digit > 9) break; exp_value = exp_value * 10 + digit; } - if (t > s) - { - exp += exp_neg ? -exp_value : exp_value; - s = t; - } + exp += exp_neg ? -exp_value : exp_value; } done: @@ -250,8 +252,12 @@ unittest 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("10e+2".parse_uint_with_exponent(e, &taken, 10) == 1 && e == 3 && taken == 5); + 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 parse_int_fast(ref const(char)[] text, out bool success) pure @@ -354,6 +360,50 @@ unittest assert(parse_float("xyz", &taken) is double.nan && taken == 0); } +VarQuantity parse_quantity(const(char)[] text, size_t* bytes_taken = null) nothrow +{ + import urt.si.unit; + + 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); +} + ptrdiff_t parse(T)(const char[] text, out T result) { @@ -676,7 +726,7 @@ uint get_digit(char c) pure if (zero_base < 10) return zero_base; uint a_base = (c | 0x20) - 'a'; - return 10 + a_base; + return 10 + (a_base & 0xFF); } uint parse_base_prefix(ref const(char)* str, const(char)* end) pure @@ -703,7 +753,7 @@ uint parse_sign(ref const(char)* str, const(char)* end) pure if (neg > 2 || neg == 1) return 0; ++str; - return neg; + return neg; // neg is 0 (+) or 2 (-) } diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index bf9ae9c..e293ebb 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -7,7 +7,7 @@ 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)); diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index b9be825..7bc059b 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -32,7 +32,7 @@ nothrow @nogc: // -enum ScaledUnit unit(const(char)[] desc) = () { ScaledUnit r; float f; ptrdiff_t e = r.parseUnit(desc, f); assert(e > 0, "Invalid unit"); assert(f == 1, "Unit requires pre-scale"); return r; }(); +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 @@ -267,7 +267,7 @@ nothrow: char sep; while (const(char)[] unit = s.split!('/', '*')(sep)) { - int p = unit.takePower(); + int p = unit.take_power(); if (p == 0) return -1; // invalid power @@ -565,11 +565,12 @@ nothrow: bool opEquals(Unit rh) const pure => (pack & 0xFF000000) ? false : unit == rh; - ptrdiff_t parseUnit(const(char)[] s, out float preScale) pure + 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; - preScale = 1; + pre_scale = 1; if (s.length == 0) { @@ -582,18 +583,20 @@ nothrow: { if (s.length == 1) return -1; - preScale = -1; + pre_scale = -1; s = s[1 .. $]; } ScaledUnit r; bool invert; char sep; - while (const(char)[] term = s.split!('/', '*')(sep)) + while (const(char)[] term = s.split!(['/', '*'], false, false)(&sep)) { - int p = term.takePower(); + int p = term.take_power(); if (p == 0) return -1; // invalid exponent + if (term.length == 0) + return -1; size_t offset = 0; @@ -601,8 +604,10 @@ nothrow: 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); - preScale *= sf; + pre_scale *= sf; } if (offset == term.length) @@ -610,7 +615,7 @@ nothrow: else if (const ScaledUnit* su = term[offset .. $] in noScaleUnitMap) { r *= (*su) ^^ (invert ? -p : p); - preScale *= 10.0^^e; + pre_scale *= 10.0^^e; } else { @@ -670,7 +675,7 @@ nothrow: else if (const ScaledUnit* su = term in noScaleUnitMapSI) { r *= (*su) ^^ (invert ? -p : p); - preScale *= 10.0^^e; + pre_scale *= 10.0^^e; } else return -1; // string was not taken? @@ -800,7 +805,7 @@ nothrow: ptrdiff_t fromString(const(char)[] s) pure { float scale; - ptrdiff_t r = parseUnit(s, scale); + ptrdiff_t r = parse_unit(s, scale); if (scale != 1) return -1; return r; @@ -1066,7 +1071,7 @@ immutable ScaledUnit[string] noScaleUnitMapSI = [ "varh" : WattHour, ]; -int takePower(ref const(char)[] s) pure +int take_power(ref const(char)[] s) pure { size_t e = s.findFirst('^'); if (e < s.length) diff --git a/src/urt/string/package.d b/src/urt/string/package.d index 4fd0e24..77ec36b 100644 --- a/src/urt/string/package.d +++ b/src/urt/string/package.d @@ -265,20 +265,26 @@ inout(char)[] takeLine(ref inout(char)[] s) pure return t; } -inout(char)[] split(char Separator, bool HandleQuotes = true)(ref inout(char)[] s) pure +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; @@ -288,37 +294,26 @@ 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) pure +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 From a19dac19c1602042983b099885a77ac3e51e192d Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 10 Feb 2026 17:00:18 +1000 Subject: [PATCH 74/91] - Improve maps - Add some hacks for unification of timestamp types - Fix compile error for user types with no comparison operator --- src/urt/meta/package.d | 9 +++ src/urt/variant.d | 138 +++++++++++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/src/urt/meta/package.d b/src/urt/meta/package.d index c703fc5..defbd9a 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -187,6 +187,12 @@ nothrow @nogc: 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); + } + const(void)* value_for(const(char)[] key) const pure { size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); @@ -280,6 +286,9 @@ template EnumInfo(E) 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(UE)* value_for(const(char)[] key) const pure { size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); diff --git a/src/urt/variant.d b/src/urt/variant.d index 033f072..0d8a19e 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -9,7 +9,7 @@ import urt.map; import urt.mem.allocator; import urt.si.quantity; import urt.si.unit : ScaledUnit, Second, Nanosecond; -import urt.time : Duration, dur; +import urt.time; import urt.traits; nothrow @nogc: @@ -257,35 +257,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; - alloc = type_detail_index!T(); - ptr = cast(void*)thing; - } - else static if (EmbedUserType!T) + static if (is(Unqual!T == MonoTime)) + this(cast(SysTime)thing); + else { - alloc = UserTypeId!T; - flags |= Flags.Embedded; + alias dummy = MakeTypeDetails!T; - if (TypeDetailsFor!T.destroy) // TODO: we should check the same condition that determined if there is a destruct function... - flags |= Flags.NeedDestruction; + 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; - 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; + + 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; + 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); + ptr = defaultAllocator().alloc(T.sizeof, T.alignof).ptr; + emplace(cast(T*)ptr, forward!thing); + } } } @@ -812,23 +817,50 @@ nothrow @nogc: else return *cast(inout(T)*)ptr; } - inout(T) asUser(T)() inout pure + inout(T) asUser(T)() inout if (ValidUserType!(Unqual!T) && !UserTypeReturnByRef!T) { alias U = Unqual!T; - 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) + + // some hacks for builtin time types... + static if (is(U == MonoTime)) + return cast(MonoTime)asUser!SysTime; + else static if (is(T == SysTime)) { - // make a copy on the stack and return by value - U r = void; - TypeDetailsFor!U.copy_emplace(embed.ptr, &r, false); - return r; + 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?"); + } } auto as(T)() inout pure @@ -907,6 +939,21 @@ nothrow @nogc: 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()); @@ -1295,7 +1342,7 @@ template UserTypeId(T) 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) { @@ -1311,6 +1358,8 @@ 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() @@ -1344,7 +1393,7 @@ struct TypeDetails 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; } -__gshared TypeDetails[8] g_type_details; +__gshared TypeDetails[16] g_type_details; __gshared ushort g_num_type_details = 0; typeof(g_type_details)* type_details() => &g_type_details; @@ -1449,16 +1498,13 @@ public template TypeDetailsFor(T) return a.opCmp(b); else static if (__traits(compiles, { b.opCmp(a); })) return -b.opCmp(a); - else + else static if (is(T == class)) { - 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 - return a < b ? -1 : a > b ? 1 : 0; + 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)) { From 5870e2a2c97bcb8f55b1914052de556f9a5cb2dc Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 14 Feb 2026 13:33:01 +1000 Subject: [PATCH 75/91] Split the EnumInfo code into its own module. --- src/urt/meta/enuminfo.d | 394 ++++++++++++++++++++++++++++++++++++++++ src/urt/meta/package.d | 374 -------------------------------------- 2 files changed, 394 insertions(+), 374 deletions(-) create mode 100644 src/urt/meta/enuminfo.d diff --git a/src/urt/meta/enuminfo.d b/src/urt/meta/enuminfo.d new file mode 100644 index 0000000..75f10c1 --- /dev/null +++ b/src/urt/meta/enuminfo.d @@ -0,0 +1,394 @@ +module urt.meta.enum_; + +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); + } + + bool contains(const(char)[] key) const pure + { + size_t i = binary_search!key_compare(_keys[0 .. count], key, _string_buffer); + return i < count; + } + +private: + alias GetFun = Variant function(const(void)*) pure; + + const void* _values; + const ushort* _keys; + const char* _string_buffer; + + // these tables map between indices of keys and values + const ubyte* _lookup_tables; + + 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) +{ + alias UE = Unqual!E; + + static if (is(UE == 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(UE 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 UE* _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 UE* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure + { + _base = inout(VoidEnumInfo)(count, UE.sizeof, type_hash, values, keys, strings, lookup, cast(VoidEnumInfo.GetFun)&get_value!UE); + } + + const(UE)[] 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(UE)* 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(Unqual!E == enum)) +{ + alias UE = Unqual!E; + + enum ubyte num_items = enum_members.length; + static assert(num_items <= ubyte.max, "Too many enum items!"); + + __gshared immutable enum_info = immutable(EnumInfo!UE)( + num_items, + fnv1a(cast(ubyte[])UE.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 UE[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 + { + UE 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(VoidEnumInfo.GetFun)&get_value!T); +} + + +private: + +Variant get_value(E)(const(void)* ptr) + => Variant(*cast(const(E)*)ptr); + +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/package.d b/src/urt/meta/package.d index defbd9a..62bf1a2 100644 --- a/src/urt/meta/package.d +++ b/src/urt/meta/package.d @@ -142,363 +142,9 @@ template INTERLEAVE_SEPARATOR(alias sep, Args...) } -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; - import urt.string; -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); - } - - const(void)* 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; - i = _lookup_tables[i]; - return _values + i*stride; - } - - 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; - - this(ubyte count, ushort stride, uint type_hash, inout void* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) 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; - } - - 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) -{ - alias UE = Unqual!E; - - static if (is(UE == 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(UE 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 UE* _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 UE* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure - { - _base = inout(VoidEnumInfo)(count, UE.sizeof, type_hash, values, keys, strings, lookup); - } - - const(UE)[] 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(UE)* 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(Unqual!E == enum)) -{ - alias UE = Unqual!E; - - enum ubyte num_items = enum_members.length; - static assert(num_items <= ubyte.max, "Too many enum items!"); - - __gshared immutable enum_info = immutable(EnumInfo!UE)( - num_items, - fnv1a(cast(ubyte[])UE.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 UE[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 - { - UE 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); -} private: -import urt.string : trim; -enum trim_key(string key) = key.trim!(c => c == '_'); - template is_same(alias a, alias b) { static if (!is(typeof(&a && &b)) // at least one is an rvalue @@ -512,23 +158,3 @@ template is_same(A, B) { enum is_same = is(A == B); } - -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); -} From 69f16c16838b7eb5651848df16a34d676291c6b8 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 14 Feb 2026 13:39:58 +1000 Subject: [PATCH 76/91] Improved socket API with functions that can send multiple buffers. --- src/urt/socket.d | 165 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 129 insertions(+), 36 deletions(-) diff --git a/src/urt/socket.d b/src/urt/socket.d index 7902c74..f87ec91 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -34,7 +34,7 @@ else version (Posix) 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; @@ -146,10 +146,9 @@ enum SocketOption : ubyte enum MsgFlags : ubyte { none = 0, - oob = 1 << 0, - peek = 1 << 1, - confirm = 1 << 2, - no_sig = 1 << 3, + peek = 1 << 0, + confirm = 1 << 1, + no_sig = 1 << 2, //... } @@ -251,11 +250,11 @@ Result shutdown(Socket socket, SocketShutdownMode how) Result bind(Socket socket, ref const InetAddress address) { ubyte[512] buffer = void; - size_t addrLen; - sockaddr* sock_addr = make_sockaddr(address, buffer, addrLen); + size_t addr_len; + sockaddr* sock_addr = make_sockaddr(address, buffer, addr_len); assert(sock_addr, "Invalid socket address"); - if (_bind(socket.handle, sock_addr, cast(int)addrLen) < 0) + if (_bind(socket.handle, sock_addr, cast(int)addr_len) < 0) return socket_getlasterror(); return Result.success; } @@ -270,11 +269,11 @@ Result listen(Socket socket, uint backlog = -1) Result connect(Socket socket, ref const InetAddress address) { ubyte[512] buffer = void; - size_t addrLen; - sockaddr* sock_addr = make_sockaddr(address, buffer, addrLen); + size_t addr_len; + sockaddr* sock_addr = make_sockaddr(address, buffer, addr_len); assert(sock_addr, "Invalid socket address"); - if (_connect(socket.handle, sock_addr, cast(int)addrLen) < 0) + if (_connect(socket.handle, sock_addr, cast(int)addr_len) < 0) return socket_getlasterror(); return Result.success; } @@ -303,41 +302,114 @@ Result accept(Socket socket, out Socket connection, InetAddress* remote_address } Result send(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, size_t* bytes_sent = null) -{ - Result r = Result.success; + => send(socket, flags, 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 send(Socket socket, MsgFlags flags, size_t* bytes_sent, const void[][] buffers...) +{ + version (Windows) { - r = socket_getlasterror(); - sent = 0; + 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; } - if (bytes_sent) - *bytes_sent = sent; - return r; + else + return sendmsg(socket, null, flags, null, bytes_sent, buffers); } Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytes_sent = null) + => sendmsg(socket, address, flags, null, bytes_sent, (&message)[0..1]); + +Result sendto(Socket socket, const InetAddress* address, size_t* bytes_sent, const void[][] buffers...) + => sendmsg(socket, address, MsgFlags.none, null, bytes_sent, buffers); + +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; + size_t addr_len; sockaddr* sock_addr = null; if (address) { - sock_addr = make_sockaddr(*address, tmp, addrLen); + 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), sock_addr, cast(int)addrLen); - if (sent < 0) + version (Windows) + { + 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(); + } + else { - r = socket_getlasterror(); - sent = 0; + 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 r; + return Result.success; } Result recv(Socket socket, void[] buffer, MsgFlags flags = MsgFlags.none, size_t* bytes_received) @@ -1046,7 +1118,7 @@ SocketResult socket_result(Result result) } -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* sock_addr = cast(sockaddr*)buffer.ptr; @@ -1054,7 +1126,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ { case AddressFamily.ipv4: { - addrLen = sockaddr_in.sizeof; + addr_len = sockaddr_in.sizeof; if (buffer.length < sockaddr_in.sizeof) return null; @@ -1079,7 +1151,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ { version (HasIPv6) { - addrLen = sockaddr_in6.sizeof; + addr_len = sockaddr_in6.sizeof; if (buffer.length < sockaddr_in6.sizeof) return null; @@ -1107,7 +1179,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ { // version (HasUnixSocket) // { -// addrLen = sockaddr_un.sizeof; +// addr_len = sockaddr_un.sizeof; // if (buffer.length < sockaddr_un.sizeof) // return null; // @@ -1124,7 +1196,7 @@ sockaddr* make_sockaddr(ref const InetAddress address, ubyte[] buffer, out size_ default: { sock_addr = null; - addrLen = 0; + addr_len = 0; assert(false, "Unsupported address family"); break; @@ -1422,7 +1494,6 @@ else int map_message_flags(MsgFlags flags) { int r = 0; - if (flags & MsgFlags.oob) r |= MSG_OOB; if (flags & MsgFlags.peek) r |= MSG_PEEK; version (linux) { @@ -1471,24 +1542,29 @@ version (Windows) // 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 (!WSARecvMsg) + if (!WSASendMsg || !WSARecvMsg) goto FAIL; return; FAIL: import urt.log; - writeWarning("Failed to get WSARecvMsg function pointer - recvfrom() won't be able to report the dst address"); + 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) @@ -1539,7 +1615,9 @@ version (Windows) } alias LPWSABUF = WSABUF*; + 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 IN_PKTINFO @@ -1581,4 +1659,19 @@ version (Windows) 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); } From 578450b214fe773ef4c889956cb1c384446a0969 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sat, 14 Feb 2026 15:31:31 +1000 Subject: [PATCH 77/91] Oops, didn't update the module name! --- src/urt/meta/enuminfo.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urt/meta/enuminfo.d b/src/urt/meta/enuminfo.d index 75f10c1..d69a58a 100644 --- a/src/urt/meta/enuminfo.d +++ b/src/urt/meta/enuminfo.d @@ -1,4 +1,4 @@ -module urt.meta.enum_; +module urt.meta.enuminfo; import urt.algorithm : binary_search, qsort; import urt.traits :EnumType, is_enum, Unqual; From 6fdb2a9f7aa2f67576f7e16b8875684330434b32 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 15 Feb 2026 02:08:25 +1000 Subject: [PATCH 78/91] Move parse_quantity() to urt.quantity. --- src/urt/conv.d | 45 ------------------------------------------ src/urt/si/quantity.d | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index f0ab5c1..3d600ba 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -1,7 +1,6 @@ module urt.conv; import urt.meta; -import urt.si.quantity; import urt.string; public import urt.string.format : toString; @@ -360,50 +359,6 @@ unittest assert(parse_float("xyz", &taken) is double.nan && taken == 0); } -VarQuantity parse_quantity(const(char)[] text, size_t* bytes_taken = null) nothrow -{ - import urt.si.unit; - - 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); -} - ptrdiff_t parse(T)(const char[] text, out T result) { diff --git a/src/urt/si/quantity.d b/src/urt/si/quantity.d index e293ebb..8656a2c 100644 --- a/src/urt/si/quantity.d +++ b/src/urt/si/quantity.d @@ -497,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); +} From 6762313bd84be34207ff0f44294f5236b856f5a8 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Thu, 19 Feb 2026 11:42:48 +1000 Subject: [PATCH 79/91] Removed IsQuantity flag; it's implied for any Number with `count != 0` --- src/urt/variant.d | 62 ++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/urt/variant.d b/src/urt/variant.d index 0d8a19e..faf23cd 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -165,17 +165,12 @@ nothrow @nogc: this(U, ScaledUnit _U)(Quantity!(U, _U) q) { this(q.value); - if (q.unit.pack) - { - flags |= Flags.IsQuantity; - count = q.unit.pack; - } + count = q.unit.pack; } this(Duration dur) { this(dur.as!"nsecs"); - flags |= Flags.IsQuantity; count = Nanosecond.pack; } @@ -367,12 +362,10 @@ nothrow @nogc: } else static if (is_some_int!T || is_some_float!T) { - if (!isNumber) + if (!isNumber || isQuantity) return false; static if (is_some_int!T) if (!canFitInt!T) return false; - if (isQuantity) - return asQuantity!double() == Quantity!T(rhs); return as!T == rhs; } else static if (is(T == Quantity!(U, _U), U, ScaledUnit _U)) @@ -431,8 +424,8 @@ nothrow @nogc: static double asDoubleWithBool(ref const Variant v) => v.isBool() ? double(v.asBool()) : v.asDouble(); - uint aunit = a.isQuantity ? a.count : 0; - uint bunit = b.isQuantity ? b.count : 0; + uint aunit = a.count; + uint bunit = b.count; if (aunit || bunit) { // we can't compare different units @@ -445,8 +438,8 @@ nothrow @nogc: // matching units, but we'll only do quantity comparison if there is some scaling if ((aunit >> 24) != (bunit >> 24)) { - Quantity!double aq = a.isQuantity ? a.asQuantity!double() : Quantity!double(asDoubleWithBool(*a)); - Quantity!double bq = b.isQuantity ? b.asQuantity!double() : Quantity!double(asDoubleWithBool(*b)); + 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; } @@ -596,9 +589,9 @@ nothrow @nogc: bool isDouble() const pure => (flags & Flags.DoubleFlag) != 0; bool isQuantity() const pure - => (flags & Flags.IsQuantity) != 0; + => isNumber && count != 0; bool isDuration() const pure - => isQuantity && (count & 0xFFFFFF) == Second.pack; + => isNumber && (count & 0xFFFFFF) == Second.pack; bool isBuffer() const pure => type == Type.Buffer; bool isString() const pure @@ -729,15 +722,14 @@ nothrow @nogc: } Quantity!T asQuantity(T = double)() const pure @property - if (is_some_float!T || isSomeInt!T) + if (is_some_float!T || is_some_int!T) { if (isNull) return Quantity!T(0); assert(isNumber); Quantity!T r; r.value = as!T; - if (isQuantity) - r.unit.pack = count; + r.unit.pack = count; return r; } @@ -747,26 +739,11 @@ nothrow @nogc: 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? - { - Quantity!float q; - q.value = asFloat; - q.unit.pack = count; - ns = cast(Nanoseconds)q; - } + ns = cast(Nanoseconds)asQuantity!float(); else if (isDouble) - { - Quantity!double q; - q.value = asDouble; - q.unit.pack = count; - ns = cast(Nanoseconds)q; - } + ns = cast(Nanoseconds)asQuantity!double(); else - { - Quantity!long q; - q.value = asLong; - q.unit.pack = count; - ns = q; - } + ns = asQuantity!long(); return ns.value.dur!"nsecs"; } @@ -969,10 +946,6 @@ nothrow @nogc: { assert(isNumber()); count = unit.pack; - if (count != 0) - flags |= Flags.IsQuantity; - else - flags &= ~Flags.IsQuantity; } // TODO: this seems to interfere with UFCS a lot... @@ -1103,7 +1076,7 @@ nothrow @nogc: assert(false, "String has no closing quote"); } - if (s[0].is_numeric) + if (s[0].is_numeric || ((s[0] == '+' || s[0] == '-') && s.length > 1 && s[1].is_numeric)) { size_t taken; ScaledUnit unit; @@ -1121,11 +1094,7 @@ nothrow @nogc: this = i * 10.0^^e; else this = i; - if (unit.pack) - { - flags |= Flags.IsQuantity; - count = unit.pack; - } + count = unit.pack; return taken; } } @@ -1292,7 +1261,6 @@ package: Uint64Flag = 1 << (TypeBits + 6), FloatFlag = 1 << (TypeBits + 7), DoubleFlag = 1 << (TypeBits + 8), - IsQuantity = 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? From da99617779679e5bbcf42aca9ae5e6a30e1fb973 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 18 Feb 2026 16:47:21 +1000 Subject: [PATCH 80/91] Add enum support to Variant --- src/urt/meta/enuminfo.d | 42 ++++++++++++++++++++--------------------- src/urt/variant.d | 35 ++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/urt/meta/enuminfo.d b/src/urt/meta/enuminfo.d index d69a58a..1a1ef5e 100644 --- a/src/urt/meta/enuminfo.d +++ b/src/urt/meta/enuminfo.d @@ -64,7 +64,7 @@ nothrow @nogc: if (i == count) return Variant(); i = _lookup_tables[i]; - return _get_value(_values + i*stride); + return _get_value(_values + i*stride, &this); } bool contains(const(char)[] key) const pure @@ -74,8 +74,6 @@ nothrow @nogc: } private: - alias GetFun = Variant function(const(void)*) pure; - const void* _values; const ushort* _keys; const char* _string_buffer; @@ -83,7 +81,7 @@ private: // these tables map between indices of keys and values const ubyte* _lookup_tables; - GetFun _get_value; + 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 { @@ -106,9 +104,9 @@ private: template EnumInfo(E) { - alias UE = Unqual!E; + static assert (is(E == Unqual!E), "EnumInfo can only be instantiated with unqualified types!"); - static if (is(UE == void)) + static if (is(E == void)) alias EnumInfo = VoidEnumInfo; else { @@ -119,7 +117,7 @@ template EnumInfo(E) static assert (EnumInfo.sizeof == EnumInfo.sizeof, "Template EnumInfo must not add any members!"); - static if (is(UE T == enum)) + static if (is(E T == enum)) alias V = T; else static assert(false, E.string ~ " is not an enum type!"); @@ -129,7 +127,7 @@ template EnumInfo(E) VoidEnumInfo _base; struct { ubyte[VoidEnumInfo._values.offsetof] _pad; - const UE* _values; // shadows the _values in _base with a typed version + const E* _values; // shadows the _values in _base with a typed version } } alias _base this; @@ -137,12 +135,12 @@ template EnumInfo(E) inout(VoidEnumInfo*) make_void() inout pure => &_base; - this(ubyte count, uint type_hash, inout UE* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure + this(ubyte count, uint type_hash, inout(E)* values, inout ushort* keys, inout char* strings, inout ubyte* lookup) inout pure { - _base = inout(VoidEnumInfo)(count, UE.sizeof, type_hash, values, keys, strings, lookup, cast(VoidEnumInfo.GetFun)&get_value!UE); + _base = inout(VoidEnumInfo)(count, E.sizeof, type_hash, values, keys, strings, lookup, cast(GetFun)&get_value!V); } - const(UE)[] values() const pure + const(E)[] values() const pure => _values[0 .. count]; const(char)[] key_for(V value) const pure @@ -159,7 +157,7 @@ template EnumInfo(E) const(char)[] key_by_sorted_index(size_t i) const pure => _base.key_by_sorted_index(i); - const(UE)* value_for(const(char)[] key) const pure + 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) @@ -174,16 +172,16 @@ template EnumInfo(E) } template enum_info(E) - if (is(Unqual!E == enum)) + if (is(E == enum)) { - alias UE = Unqual!E; + 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!UE)( + __gshared immutable enum_info = immutable(EnumInfo!E)( num_items, - fnv1a(cast(ubyte[])UE.stringof), + fnv1a(cast(ubyte[])E.stringof), _values.ptr, _keys.ptr, _strings.ptr, @@ -196,7 +194,7 @@ private: import urt.string.uni : uni_compare; // keys and values are sorted for binary search - __gshared immutable UE[num_items] _values = [ STATIC_MAP!(GetValue, iota) ]; + __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 = () { @@ -251,7 +249,7 @@ private: } struct VI { - UE v; + E v; ubyte i; } @@ -361,14 +359,16 @@ VoidEnumInfo* make_enum_info(T)(const(char)[] name, const(char)[][] keys, T[] va } // 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(VoidEnumInfo.GetFun)&get_value!T); + 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: -Variant get_value(E)(const(void)* ptr) - => Variant(*cast(const(E)*)ptr); +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 == '_'); diff --git a/src/urt/variant.d b/src/urt/variant.d index faf23cd..6834a90 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -7,6 +7,7 @@ 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, Second, Nanosecond; import urt.time; @@ -152,14 +153,28 @@ nothrow @nogc: } } - this(E)(E e) + this(E)(const E e) if (is(E == enum)) { static if (is(E T == enum)) + this(T(e), enum_info!E.make_void()); + } + + this(T)(T value, const(VoidEnumInfo)* e) + { + static assert(!is(T == enum), "T should be a numeric type"); + + this(value); + static if (size_t.sizeof == 8) { - this(T(e)); - // TODO: do we keep a record of the enum keys for stringification? + 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 + count = cast(size_t)e; + flags |= Flags.Enum; } this(U, ScaledUnit _U)(Quantity!(U, _U) q) @@ -589,9 +604,11 @@ nothrow @nogc: bool isDouble() const pure => (flags & Flags.DoubleFlag) != 0; bool isQuantity() const pure - => isNumber && count != 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 @@ -901,6 +918,15 @@ nothrow @nogc: if (ValidUserType!(Unqual!T) && UserTypeReturnByRef!T) => asUser!T; + 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) @@ -1261,6 +1287,7 @@ package: Uint64Flag = 1 << (TypeBits + 6), FloatFlag = 1 << (TypeBits + 7), DoubleFlag = 1 << (TypeBits + 8), + 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? From a804e14a1e0941d776abaa7286d967e97c1c388e Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Sun, 15 Feb 2026 14:36:49 +1000 Subject: [PATCH 81/91] Remove implicit conversion from String -> const(char)[]. - also don't attempt to compare user types if the types doesn't match! --- src/urt/array.d | 5 - src/urt/string/format.d | 753 ++++++++++++++++++++-------------------- src/urt/string/string.d | 105 ++++-- src/urt/variant.d | 206 +++++++---- 4 files changed, 609 insertions(+), 460 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 4f61066..1159e0b 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -878,11 +878,6 @@ struct SharedArray(T) nothrow @nogc: - void opAssign(typeof(null)) - { - clear(); - } - void opAssign(ref typeof(this) val) { clear(); diff --git a/src/urt/string/format.d b/src/urt/string/format.d index ae564c6..c34917d 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -68,7 +68,7 @@ char[] concat(Args...)(char[] buffer, auto ref Args args) } return buffer.ptr[0 .. offset]; } - static if (allAreStrings!Args) + else static if (allAreStrings!Args) { // TODO: why can't inline this?! // pragma(inline, true); @@ -242,468 +242,473 @@ struct DefInt(T) 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); -struct DefFormat(T) +template DefFormat(T) { - T value; - - ptrdiff_t toString(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) const nothrow @nogc + 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 if (is(T == typeof(null))) + 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 { - 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; - 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) + } + 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 : format_float, format_int; + import urt.conv : format_float, format_int; - 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]; + 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 format_float(value, buffer, format); - } - else static if (is(T == ulong) || is(T == long)) - { - import urt.conv : format_int, format_uint; + 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 to_lower = 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 show_sign = false; - if (format.length && format[0] == '+') + static if (is(T == long)) + { + bool show_sign = false; + if (format.length && format[0] == '+') + { + show_sign = true; + format.popFront; + } + } + if (format.length && format[0] == '0') { - show_sign = true; + 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].is_numeric) - { - bool success; - padding = format.parse_int_fast(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) - { - char b = format[0] | 0x20; - if (b == 'x') + if (format.length && format[0].is_numeric) { - base = 16; - to_lower = format[0] == 'x' && buffer.ptr; + 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; } - 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' : ' '); + 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; + } - if (to_lower && len > 0) + return len; + } + else static if (is(T == ubyte) || is(T == ushort) || is(T == uint)) { - for (size_t i = 0; i < len; ++i) - if (cast(uint)(buffer.ptr[i] - 'A') < 26) - buffer.ptr[i] |= 0x20; + return defToString(ulong(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 == byte) || is(T == short) || is(T == int)) { - leftJustify = true; - format.popFront; + return defToString(long(value), buffer, format, formatArgs); } - if (format.length && format[0] == '*') + else static if (is(T == const(char)*)) { - varLen = true; - format.popFront; + const char[] t = value[0 .. value.strlen]; + return t.defToString(buffer, format, formatArgs); } - if (varLen && (!format.length || !format[0].is_numeric)) - return -2; - if (format.length && format[0].is_numeric) + else static if (is(T : const(U)*, U)) { - bool success; - width = format.parse_int_fast(success); - if (varLen) + 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 == char[])) + { + 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].is_numeric) - { - bool success; - grp1 = cast(int)format.parse_int_fast(success); - if (success && format.length > 0 && format[0] == ':' && - format.length > 1 && format[1].is_numeric) + int grp1 = 1, grp2 = 0; + if (format.length && format[0].is_numeric) { - format.popFront(); - grp2 = cast(int)format.parse_int_fast(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); - if (!hex.ptr) - return -1; - 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)) - { - // 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 + } + 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)) - { - static assert(!__traits(hasMember, T, "toString"), "Struct with custom toString not properly selected!"); - - // 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] = '('; } + else static if (is(T == struct)) + { + 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 if (is(T == function)) - { - assert(false, "TODO"); - return 0; - } - else static if (is(T == delegate)) - { - assert(false, "TODO"); - return 0; + else static if (is(T == delegate)) + { + assert(false, "TODO"); + return 0; + } + else + static assert(false, "Not implemented for type: ", T.stringof); } - else - static assert(false, "Not implemented for type: ", T.stringof); } } diff --git a/src/urt/string/string.d b/src/urt/string/string.d index ccb06f9..440c395 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -215,8 +215,7 @@ inout(char)[] as_dstring(inout(char)* s) pure nothrow @nogc struct String { nothrow @nogc: - - alias toString this; + alias This = typeof(this); const(char)* ptr; @@ -225,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; } } @@ -293,6 +290,9 @@ nothrow @nogc: 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; @@ -321,7 +321,6 @@ nothrow @nogc: ptr = cs.ptr; } - bool opEquals(const(char)[] rhs) const pure { if (!ptr) @@ -330,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; @@ -338,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 fnv1a(cast(ubyte[])ptr[0 .. length]); + return fnv1a(cast(ubyte[])ptr[0 .. len]); else - return fnv1a64(cast(ubyte[])ptr[0 .. length]); + return fnv1a64(cast(ubyte[])ptr[0 .. len]); } const(char)[] opIndex() const pure @@ -366,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]; @@ -517,8 +538,6 @@ nothrow @nogc: static assert(Embed == 0, "Not without move semantics!"); - alias toString this; - char* ptr; // TODO: DELETE POSTBLIT! @@ -590,9 +609,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[]); @@ -602,11 +669,6 @@ nothrow @nogc: opAssign(rh[]); } - void opAssign(typeof(null)) - { - clear(); - } - void opAssign(char c) { reserve(1); @@ -895,6 +957,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"; diff --git a/src/urt/variant.d b/src/urt/variant.d index 0d8a19e..88ba0a6 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -9,8 +9,10 @@ import urt.map; import urt.mem.allocator; import urt.si.quantity; import urt.si.unit : ScaledUnit, Second, Nanosecond; +import urt.string; import urt.time; import urt.traits; +import urt.util : swap; nothrow @nogc: @@ -22,7 +24,9 @@ enum ValidUserType(T) = (is(T == struct) || is(T == class)) && !is(T == Array!U, U) && !is(T : const(char)[]) && !is(T == Quantity!(T, U), T, alias U) && - !is(T == Duration); + !is(T == Duration) && + !is(T == String) && + !is(T == MutableString!N, size_t N); alias VariantKVP = KVP!(const(char)[], Variant); @@ -37,14 +41,28 @@ 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 + 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) + { + 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?"); } version (EnableMoveSemantics) { @@ -179,9 +197,33 @@ nothrow @nogc: count = Nanosecond.pack; } - this(const(char)[] s) // TODO: (S)(S s) -// if (is(S : const(char)[])) + this(String s) + { + 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(size_t N)(MutableString!N s) { + this(String(s.move)); + } + + this(const(char)[] s) + { + if (s.length == 0) + { + flags = Flags.Null; + return; + } if (s.length < embed.length) { flags = Flags.ShortString; @@ -190,13 +232,20 @@ 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; @@ -205,7 +254,10 @@ nothrow @nogc: return; } flags = Flags.Buffer; - value.s = cast(char*)buffer.ptr; + flags |= Flags.NeedDestruction; + void[] mem = defaultAllocator.alloc(buffer.length); + mem[] = buffer[]; + ptr = mem.ptr; count = cast(uint)buffer.length; } @@ -226,7 +278,7 @@ nothrow @nogc: this(Array!Variant a) { - takeNodeArray(a); + take_node_array(a); flags = Flags.Array; } @@ -386,10 +438,10 @@ nothrow @nogc: // TODO: should we also do string key comparisons? return opEquals(cast(E)rhs); } - else static if (is(T : const(char)[])) + 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 asUser!T == rhs; + return isUser!T && asUser!T == rhs; else static assert(false, "TODO: variant comparison with '", T.stringof, "' not supported"); } @@ -777,7 +829,7 @@ nothrow @nogc: assert(isBuffer); if (flags & Flags.Embedded) return embed[0 .. embed[$-1]]; - return value.s[0 .. count]; + return ptr[0 .. count]; } const(char)[] asString() const pure @@ -863,72 +915,97 @@ nothrow @nogc: } } - auto as(T)() inout pure - if (!ValidUserType!(Unqual!T) || !UserTypeReturnByRef!T) - { - static if (is_some_int!T) + 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) { - static if (is_signed_int!T) + T as() const pure { - static if (is(T == long)) - return asLong(); - else + static if (is_signed_int!T) { int i = asInt(); - static if (!is(T == int)) - assert(i >= T.min && i <= T.max, "Value out of range for " ~ T.stringof); - return cast(T)i; + assert(i >= T.min && i <= T.max, "Value out of range for " ~ T.stringof); } - } - else - { - static if (is(T == ulong)) - return asUlong(); else { - uint u = asUint(); - static if (!is(T == uint)) - assert(u <= T.max, "Value out of range for " ~ T.stringof); - return cast(T)u; + uint i = asUint(); + assert(i <= T.max, "Value out of range for " ~ T.stringof); } + return cast(T)i; } } - else static if (is_some_float!T) - { - static if (is(T == float)) - return asFloat(); - else - return asDouble(); - } + 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)) - { - return asQuantity!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)) { - return asDuration; - } - else static if (is(T : const(char)[])) - { - static if (is(T == struct)) // for String/MutableString/etc - return T(asString); // TODO: error? shouldn't this NRVO?! - else - return asString; + 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)) - return asUser!T; + alias as = asUser!T; else static assert(false, "TODO!"); } - ref inout(T) as(T)() inout pure - if (ValidUserType!(Unqual!T) && UserTypeReturnByRef!T) - => asUser!T; + + 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; + } 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; @@ -1212,7 +1289,7 @@ package: return alloc; // short id return count; // long id } - inout(void)* userPtr() inout pure + inout(void)* user_ptr() inout pure { if (flags & Flags.Embedded) return embed.ptr; @@ -1221,7 +1298,7 @@ package: 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; @@ -1231,22 +1308,29 @@ 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) { ref const TypeDetails td = (flags & Flags.Embedded) ? find_type_details(alloc) : g_type_details[alloc]; if (td.destroy) - td.destroy(userPtr); + td.destroy(user_ptr); if (!(flags & Flags.Embedded)) defaultAllocator().free(ptr[0..td.size]); } From 2626f4d476bf9cbb14e29a188fc20c6f6caf70a3 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 20 Feb 2026 10:13:27 +1000 Subject: [PATCH 82/91] Fix parse_uint not rejecting digits outside the base for base < 10. --- src/urt/conv.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urt/conv.d b/src/urt/conv.d index 3d600ba..320d57e 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -65,7 +65,7 @@ ulong parse_uint(const(char)[] str, size_t* bytes_taken = null, uint base = 10) for (; s < e; ++s) { uint digit = *s - '0'; - if (digit > 9) + if (digit >= base) break; value = value*base + digit; } From 4711b3cc2944066d96ecc0f23ba1ac0ef9ec237f Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 20 Feb 2026 10:18:04 +1000 Subject: [PATCH 83/91] Accept a single space before the timezone when parsing a DateTime. --- src/urt/time.d | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/urt/time.d b/src/urt/time.d index 7cca87f..e970b75 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -621,10 +621,13 @@ pure nothrow @nogc: return offset + 1; } - if (s[offset] != '-' && s[offset] != '+') + size_t tz_offset = offset; + if (s[offset] == ' ') + ++tz_offset; + if (s[tz_offset] != '-' && s[tz_offset] != '+') return offset; - - size_t tz_offset = offset + 1; + bool tz_neg = s[tz_offset] == '-'; + tz_offset += 1; // parse timezone (00:00) int tz_hr, tz_min; @@ -664,7 +667,7 @@ pure nothrow @nogc: } } - if (s[offset] == '-') + if (tz_neg) tz_hr = -tz_hr; // assert(false, "TODO: we need to know our local timezone..."); From 1ecf69a1f07d202e2574c4d9080032923172f3fc Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Fri, 20 Feb 2026 10:17:36 +1000 Subject: [PATCH 84/91] Windows use WSASendTo because WSASendMsg is actually an extension, and not guaranteed to be available O_o --- src/urt/socket.d | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/urt/socket.d b/src/urt/socket.d index f87ec91..b1f4645 100644 --- a/src/urt/socket.d +++ b/src/urt/socket.d @@ -334,10 +334,50 @@ Result send(Socket socket, MsgFlags flags, size_t* bytes_sent, const void[][] bu } Result sendto(Socket socket, const(void)[] message, MsgFlags flags = MsgFlags.none, const InetAddress* address = null, size_t* bytes_sent = null) - => sendmsg(socket, address, flags, null, bytes_sent, (&message)[0..1]); +{ + version (Windows) + return sendto(socket, address, bytes_sent, (&message)[0..1]); + else + return sendmsg(socket, address, flags, null, bytes_sent, (&message)[0..1]); +} Result sendto(Socket socket, const InetAddress* address, size_t* bytes_sent, const void[][] buffers...) - => sendmsg(socket, address, MsgFlags.none, null, bytes_sent, buffers); +{ + version (Windows) + { + 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; + } + else + return sendmsg(socket, address, MsgFlags.none, null, bytes_sent, buffers); +} Result sendmsg(Socket socket, const InetAddress* address, MsgFlags flags, const(void)[] control, size_t* bytes_sent, const void[][] buffers) { From 5635505bb47e6bee40d2901059ced6057b87055d Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 18 Feb 2026 16:46:15 +1000 Subject: [PATCH 85/91] Renovated concat() a bit --- src/urt/array.d | 7 +- src/urt/string/format.d | 184 ++++++++++++++++++++++------------------ 2 files changed, 107 insertions(+), 84 deletions(-) diff --git a/src/urt/array.d b/src/urt/array.d index 1159e0b..51360f6 100644 --- a/src/urt/array.d +++ b/src/urt/array.d @@ -213,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! @@ -225,7 +229,7 @@ struct Array(T, size_t EmbedCount = 0) this = t[]; } - this(ref typeof(this) val) + this(ref This val) { this(val[]); } @@ -796,7 +800,6 @@ nothrow @nogc: private: enum copy_elements = is(T == class) || is(T == interface) || is_primitive!T || is_trivial!T; - T* ptr; uint _length; static if (EmbedCount > 0) diff --git a/src/urt/string/format.d b/src/urt/string/format.d index c34917d..8747c4c 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -18,7 +18,7 @@ debug alias StringifyFunc = ptrdiff_t delegate(char[] buffer, const(char)[] format, const(FormatArg)[] formatArgs) nothrow @nogc; -ptrdiff_t toString(T)(auto ref T value, char[] buffer, const(char)[] format = null, const(FormatArg)[] formatArgs = null) +ptrdiff_t toString(T)(ref const T value, char[] buffer, const(char)[] format = null, const(FormatArg)[] formatArgs = null) { debug InFormatFunction = true; ptrdiff_t r = get_to_string_func(value)(buffer, null, null); @@ -28,65 +28,83 @@ ptrdiff_t toString(T)(auto ref T value, char[] buffer, const(char)[] format = nu alias formatValue = toString; // TODO: remove me? -char[] concat(Args...)(char[] buffer, auto ref Args args) +char[] concat(Args...)(char[] buffer, ref const Args args) { + alias NormalisedArgs = NormaliseArgs!Args; + static if (Args.length == 1) + enum is_normalised = is(Args[0] == NormalisedArgs); + else + 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) { - // this implementation handles pure string concatenations - if (!buffer.ptr) + pragma(inline, true); + return concat!(NormalisedArgs)(buffer, args); + } + else + { + enum n = num_string_args!Args; + + static if (n == Args.length) { - size_t length = 0; - static foreach (i, s; args) + size_t[Args.length + 1] offsets = void; + size_t[Args.length] lens = void; + offsets[0] = 0; + static foreach (i; 0 .. Args.length) { - static if (is(typeof(s) : char)) - length += 1; + static if (is(Args[i] == char)) + offsets[i + 1] = offsets[i] + 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; + offsets[i + 1] = offsets[i] + lens[i]; + } } - else + if (!buffer.ptr) + return buffer.ptr[0 .. offsets[Args.length]]; + if (offsets[Args.length] > buffer.length) + return null; + static foreach (i; 0 .. Args.length) { - if (buffer.length < offset + s.length) - return null; - buffer.ptr[offset .. offset + s.length] = s.ptr[0 .. s.length]; - offset += s.length; + static if (is(Args[i] == char)) + buffer.ptr[offsets[i]] = args[i]; + else + buffer.ptr[offsets[i] .. offsets[i + 1]] = args[i].ptr[0..lens[i]]; } + return buffer.ptr[0 .. offsets[Args.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]; - } - else 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; - StringifyFunc[Args.length] arg_funcs = void; - static foreach(i, arg; args) - arg_funcs[i] = get_to_string_func(arg); - char[] r = concatImpl(buffer, arg_funcs); - debug if (!__ctfe) InFormatFunction = false; - return r; } } @@ -135,6 +153,42 @@ private: private: +import urt.array; +enum is_some_string(T) = is_some_char!T || 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...) +{ + 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; +} + +template NormaliseArgs(Args...) +{ + import urt.meta : AliasSeq; + static if (Args.length == 0) + alias NormaliseArgs = AliasSeq!(); + else static if (Args.length == 1) + alias NormaliseArgs = NormaliseConst!(Args[0]); + else + alias NormaliseArgs = AliasSeq!(NormaliseConst!(Args[0]), NormaliseArgs!(Args[1 .. $])); +} + +template NormaliseConst(T) +{ + static if (is(T == const(U), U) || is(T == immutable(U), U)) + alias NormaliseConst = NormaliseConst!U; + else static if (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; @@ -712,7 +766,7 @@ template DefFormat(T) } } -char[] concatImpl(char[] buffer, const(StringifyFunc)[] args) nothrow @nogc +char[] concat_impl(char[] buffer, const(StringifyFunc)[] args) nothrow @nogc { size_t len = 0; foreach (a; args) @@ -985,40 +1039,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(is_some_char!(Args[0])); - else - enum allAreStrings = (is(Args[0] : const(char[])) || is(is_some_char!(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 (is_some_char!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); From fc3d8fc18a18c869a90e295ba3367b9db279cc42 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 23 Feb 2026 03:10:14 +1000 Subject: [PATCH 86/91] - Quantity support in json - Better Variant const support --- src/urt/format/json.d | 46 +++++++++++++++++++++++++++++++++++-------- src/urt/si/unit.d | 14 ++++++++++++- src/urt/variant.d | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/urt/format/json.d b/src/urt/format/json.d index 5866a3c..f7455f0 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; @@ -197,18 +198,47 @@ ptrdiff_t write_json(ref const Variant val, char[] buffer, bool dense = false, u 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().format_float(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().format_uint(buffer); - return val.asLong().format_int(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: // for custom types, we'll use the type's regular string format into a json string diff --git a/src/urt/si/unit.d b/src/urt/si/unit.d index 7bc059b..a183d98 100644 --- a/src/urt/si/unit.d +++ b/src/urt/si/unit.d @@ -689,8 +689,11 @@ nothrow: } import urt.string.format : FormatArg; - ptrdiff_t toString(char[] buffer, const(char)[], const(FormatArg)[]) const pure + 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) @@ -802,6 +805,15 @@ nothrow: 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; diff --git a/src/urt/variant.d b/src/urt/variant.d index 0270c7c..95ce4af 100644 --- a/src/urt/variant.d +++ b/src/urt/variant.d @@ -66,6 +66,37 @@ nothrow @nogc: 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) + { + __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) { this(Variant rh) { @@ -368,6 +399,15 @@ nothrow @nogc: 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) { @@ -995,6 +1035,12 @@ nothrow @nogc: return s; } + ScaledUnit get_unit() const pure + { + assert(isQuantity()); + return *cast(ScaledUnit*)&count; + } + const(VoidEnumInfo)* get_enum_info() const pure { assert(is_enum); From c15e6a903ded287bcf272df18bcec8b79bf3c6b3 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 23 Feb 2026 12:33:33 +1000 Subject: [PATCH 87/91] Add Endian enum, which feels nicer than checking bools in various situations. --- src/urt/processor.d | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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) { From 5b72aec26a815db3b466304c383a74a62c31d17f Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 24 Feb 2026 02:49:58 +1000 Subject: [PATCH 88/91] Overhaul the log API! --- src/urt/file.d | 2 +- src/urt/log.d | 247 ++++++++++++++++++++++++++++++++++++++++----- src/urt/mem/temp.d | 58 +++++++---- 3 files changed, 263 insertions(+), 44 deletions(-) diff --git a/src/urt/file.d b/src/urt/file.d index 37dfad0..1000bcf 100644 --- a/src/urt/file.d +++ b/src/urt/file.d @@ -785,7 +785,7 @@ Result get_temp_filename(ref char[] buffer, const(char)[] dstDir, const(char)[] 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) diff --git a/src/urt/log.d b/src/urt/log.d index 9a08f0a..66c6a38 100644 --- a/src/urt/log.d +++ b/src/urt/log.d @@ -1,10 +1,187 @@ module urt.log; -import urt.mem.temp; +import urt.mem.temp : tconcat, tformat; +import urt.time; 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, @@ -13,21 +190,21 @@ enum Level : ubyte Debug } -immutable string[] levelNames = [ "Error", "Warning", "Info", "Debug" ]; +immutable string[] levelNames = ["Error", "Warning", "Info", "Debug"]; -alias LogSink = void function(Level level, const(char)[] message) nothrow @nogc; - -__gshared Level logLevel = Level.Info; - -void register_log_sink(LogSink sink) nothrow @nogc +Severity level_to_severity(Level level) { - if (g_log_sink_count < g_log_sinks.length) + final switch (level) { - g_log_sinks[g_log_sink_count] = sink; - g_log_sink_count++; + 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; + void writeDebug(T...)(ref T things) { writeLog(Level.Debug, things); } void writeInfo(T...)(ref T things) { writeLog(Level.Info, things); } void writeWarning(T...)(ref T things) { writeLog(Level.Warning, things); } @@ -40,28 +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; - - const(char)[] message = tconcat(levelNames[level], ": ", things); - for (size_t i = 0; i < g_log_sink_count; i++) - g_log_sinks[i](level, message); + write_log(sev, null, null, things); } void writeLogf(T...)(Level level, const(char)[] format, ref T things) { - if (level > logLevel) - return; + write_logf(level_to_severity(level), null, null, format, things); +} - const(char)[] message = tformat("{-2}: {@-1}", things, levelNames[level], format); +alias LegacyLogSink = void function(Level level, scope const(char)[] message) nothrow @nogc; - for (size_t i = 0; i < g_log_sink_count; i++) - g_log_sinks[i](level, message); +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: -// HACK: temp until we have a proper registration process... -__gshared LogSink[8] g_log_sinks; -__gshared size_t g_log_sink_count = 0; +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/mem/temp.d b/src/urt/mem/temp.d index f470dff..5f4eb08 100644 --- a/src/urt/mem/temp.d +++ b/src/urt/mem/temp.d @@ -99,26 +99,35 @@ wchar* twstringz(const(wchar)[] str) nothrow @nogc 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[alloc_offset..$]); - 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)) { - alloc_offset = 0; - r = toString(value, cast(char[])tempMem[0..TempMemSize / 2]); + pragma(inline, true); + return value[]; + } + else + { + 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[alloc_offset .. alloc_offset + r]; - alloc_offset += r; - return result; } -dchar[] tdstring(T)(auto ref T value) nothrow @nogc +const(dchar)[] tdstring(T)(auto ref T value) nothrow @nogc { static if (is(T : const(char)[]) || is(T : const(wchar)[]) || is(T : const(dchar)[])) alias s = value; @@ -130,17 +139,26 @@ dchar[] tdstring(T)(auto ref T value) nothrow @nogc return r[0 .. len]; } -char[] tconcat(Args...)(ref Args args) +const(char)[] tconcat(Args...)(ref Args args) { - import urt.string.format : concat; - char[] r = concat(cast(char[])tempMem[alloc_offset..$], args); - if (!r) + 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))) { - alloc_offset = 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; } - alloc_offset += r.length; - return r; } char[] tformat(Args...)(const(char)[] fmt, ref Args args) From 2271e0211a921c2327ebc4afaf75d35948733f45 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Tue, 24 Feb 2026 14:11:39 +1000 Subject: [PATCH 89/91] Fix a bug normalising the argument types in concat. --- src/urt/string/format.d | 50 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 8747c4c..f188cb3 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -31,10 +31,7 @@ alias formatValue = toString; // TODO: remove me? char[] concat(Args...)(char[] buffer, ref const Args args) { alias NormalisedArgs = NormaliseArgs!Args; - static if (Args.length == 1) - enum is_normalised = is(Args[0] == NormalisedArgs); - else - enum is_normalised = is(Args == NormalisedArgs); + enum is_normalised = is(Args == NormalisedArgs); static if (Args.length == 0) { @@ -51,31 +48,35 @@ char[] concat(Args...)(char[] buffer, ref const Args args) static if (n == Args.length) { - size_t[Args.length + 1] offsets = void; size_t[Args.length] lens = void; - offsets[0] = 0; + size_t length = 0; static foreach (i; 0 .. Args.length) { static if (is(Args[i] == char)) - offsets[i + 1] = offsets[i] + 1; + length += 1; else { lens[i] = args[i].length; - offsets[i + 1] = offsets[i] + lens[i]; + length += lens[i]; } } - if (!buffer.ptr) - return buffer.ptr[0 .. offsets[Args.length]]; - if (offsets[Args.length] > buffer.length) - return null; - static foreach (i; 0 .. Args.length) + if (buffer.ptr) { - static if (is(Args[i] == char)) - buffer.ptr[offsets[i]] = args[i]; - else - buffer.ptr[offsets[i] .. offsets[i + 1]] = args[i].ptr[0..lens[i]]; + if (length > buffer.length) + return null; + 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 .. offsets[Args.length]]; + return buffer.ptr[0 .. length]; } else static if (Args.length == 1) { @@ -154,7 +155,7 @@ private: private: import urt.array; -enum is_some_string(T) = is_some_char!T || 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); +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...) { @@ -169,19 +170,16 @@ template num_string_args(Args...) template NormaliseArgs(Args...) { import urt.meta : AliasSeq; - static if (Args.length == 0) - alias NormaliseArgs = AliasSeq!(); - else static if (Args.length == 1) - alias NormaliseArgs = NormaliseConst!(Args[0]); - else - alias NormaliseArgs = AliasSeq!(NormaliseConst!(Args[0]), NormaliseArgs!(Args[1 .. $])); + alias NormaliseArgs = AliasSeq!(); + static foreach (Arg; Args) + NormaliseArgs = AliasSeq!(NormaliseArgs, NormaliseConst!Arg); } template NormaliseConst(T) { static if (is(T == const(U), U) || is(T == immutable(U), U)) alias NormaliseConst = NormaliseConst!U; - else static if (is(T == U[], 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]; From dd8885f170b229c2392ec430fdb2e1abacf920b8 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Wed, 25 Feb 2026 02:42:48 +1000 Subject: [PATCH 90/91] Fix json escape parsing. --- src/urt/format/json.d | 48 +++++++++++++++++++++++++++++++++++------ src/urt/string/format.d | 2 +- src/urt/string/uni.d | 6 +++--- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/urt/format/json.d b/src/urt/format/json.d index f7455f0..5cfd62c 100644 --- a/src/urt/format/json.d +++ b/src/urt/format/json.d @@ -370,20 +370,56 @@ Variant parse_node(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; } diff --git a/src/urt/string/format.d b/src/urt/string/format.d index 8747c4c..e715eff 100644 --- a/src/urt/string/format.d +++ b/src/urt/string/format.d @@ -154,7 +154,7 @@ private: private: import urt.array; -enum is_some_string(T) = is_some_char!T || 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); +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...) { diff --git a/src/urt/string/uni.d b/src/urt/string/uni.d index 8447be9..56297fc 100644 --- a/src/urt/string/uni.d +++ b/src/urt/string/uni.d @@ -288,13 +288,13 @@ size_t uni_convert(const(wchar)[] s, dchar[] buffer) { 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; } From 72521a8e9784c342905eb15e767cf62db4023233 Mon Sep 17 00:00:00 2001 From: Manu Evans Date: Mon, 2 Mar 2026 03:54:51 +1000 Subject: [PATCH 91/91] Stripped druntime for real! --- src/object.d | 1755 +++++++++ src/std/math.d | 8 + src/urt/atomic.d | 107 + src/urt/conv.d | 4 +- src/urt/dbg.d | 43 - src/urt/exception.d | 58 + src/urt/fibre.d | 2 +- src/urt/file.d | 10 +- src/urt/internal/aa.d | 1048 ++++++ src/urt/internal/bitop.d | 283 ++ src/urt/internal/exception.d | 3600 ++++++++++++++++++ src/urt/internal/lifetime.d | 2 +- src/urt/internal/os.c | 8 + src/urt/internal/stdc.c | 19 + src/urt/internal/sys/windows/basetsd.d | 141 + src/urt/internal/sys/windows/basetyps.d | 27 + src/urt/internal/sys/windows/ntdef.d | 85 + src/urt/internal/sys/windows/ntsecapi.d | 796 ++++ src/urt/internal/sys/windows/ntsecpkg.d | 445 +++ src/urt/internal/sys/windows/package.d | 13 + src/urt/internal/sys/windows/schannel.d | 106 + src/urt/internal/sys/windows/sdkddkver.d | 155 + src/urt/internal/sys/windows/security.d | 119 + src/urt/internal/sys/windows/sspi.d | 381 ++ src/urt/internal/sys/windows/subauth.d | 275 ++ src/urt/internal/sys/windows/w32api.d | 138 + src/urt/internal/sys/windows/winbase.d | 2941 +++++++++++++++ src/urt/internal/sys/windows/wincon.d | 316 ++ src/urt/internal/sys/windows/wincrypt.d | 902 +++++ src/urt/internal/sys/windows/windef.d | 151 + src/urt/internal/sys/windows/winerror.d | 2312 ++++++++++++ src/urt/internal/sys/windows/winnt.d | 4321 ++++++++++++++++++++++ src/urt/internal/sys/windows/winsock2.d | 766 ++++ src/urt/internal/sys/windows/winuser.d | 144 + src/urt/internal/traits.d | 1227 ++++++ src/urt/io.d | 84 +- src/urt/lifetime.d | 30 +- src/urt/math.d | 64 +- src/urt/mem/alloc.d | 10 +- src/urt/mem/allocator.d | 5 +- src/urt/mem/package.d | 183 +- src/urt/mem/string.d | 15 +- src/urt/package.d | 289 +- src/urt/result.d | 11 +- src/urt/socket.d | 11 +- src/urt/string/string.d | 9 +- src/urt/system.d | 6 +- src/urt/time.d | 108 +- src/urt/util.d | 119 +- 49 files changed, 23468 insertions(+), 184 deletions(-) create mode 100644 src/object.d create mode 100644 src/std/math.d create mode 100644 src/urt/atomic.d create mode 100644 src/urt/exception.d create mode 100644 src/urt/internal/aa.d create mode 100644 src/urt/internal/bitop.d create mode 100644 src/urt/internal/exception.d create mode 100644 src/urt/internal/stdc.c create mode 100644 src/urt/internal/sys/windows/basetsd.d create mode 100644 src/urt/internal/sys/windows/basetyps.d create mode 100644 src/urt/internal/sys/windows/ntdef.d create mode 100644 src/urt/internal/sys/windows/ntsecapi.d create mode 100644 src/urt/internal/sys/windows/ntsecpkg.d create mode 100644 src/urt/internal/sys/windows/package.d create mode 100644 src/urt/internal/sys/windows/schannel.d create mode 100644 src/urt/internal/sys/windows/sdkddkver.d create mode 100644 src/urt/internal/sys/windows/security.d create mode 100644 src/urt/internal/sys/windows/sspi.d create mode 100644 src/urt/internal/sys/windows/subauth.d create mode 100644 src/urt/internal/sys/windows/w32api.d create mode 100644 src/urt/internal/sys/windows/winbase.d create mode 100644 src/urt/internal/sys/windows/wincon.d create mode 100644 src/urt/internal/sys/windows/wincrypt.d create mode 100644 src/urt/internal/sys/windows/windef.d create mode 100644 src/urt/internal/sys/windows/winerror.d create mode 100644 src/urt/internal/sys/windows/winnt.d create mode 100644 src/urt/internal/sys/windows/winsock2.d create mode 100644 src/urt/internal/sys/windows/winuser.d create mode 100644 src/urt/internal/traits.d 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/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 320d57e..55755a7 100644 --- a/src/urt/conv.d +++ b/src/urt/conv.d @@ -339,7 +339,7 @@ double parse_float(const(char)[] str, size_t* bytes_taken = null, uint base = 10 if (__ctfe) return mantissa * double(base)^^e; else - return mantissa * pow(base, e); + return mantissa * pow(double(base), e); } unittest @@ -589,7 +589,7 @@ ptrdiff_t format_float(double value, char[] buffer, const(char)[] format = null) // 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; diff --git a/src/urt/dbg.d b/src/urt/dbg.d index 189100d..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 setup_assert_handler() -{ - 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/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 38a5579..66253fb 100644 --- a/src/urt/fibre.d +++ b/src/urt/fibre.d @@ -344,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) { diff --git a/src/urt/file.d b/src/urt/file.d index 1000bcf..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; 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 index 314164f..1f9c3a9 100644 --- a/src/urt/internal/lifetime.d +++ b/src/urt/internal/lifetime.d @@ -92,7 +92,7 @@ constructors etc. void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted if (!is(T == const) && !is(T == immutable) && !is(T == inout)) { - import core.internal.traits : hasElaborateAssign; + import urt.internal.traits : hasElaborateAssign; static if (__traits(isZeroInit, T)) { 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 586d9db..d08eb32 100644 --- a/src/urt/lifetime.d +++ b/src/urt/lifetime.d @@ -18,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"); @@ -83,7 +83,7 @@ T* emplace(T, Args...)(void[] chunk, auto ref Args args) 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; })) @@ -95,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); } @@ -198,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)) { @@ -220,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); @@ -237,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; @@ -254,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)) { @@ -346,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) { @@ -378,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])))) { @@ -392,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)) @@ -416,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) @@ -435,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; @@ -511,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; @@ -557,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/math.d b/src/urt/math.d index ff63a20..e92a83c 100644 --- a/src/urt/math.d +++ b/src/urt/math.d @@ -63,7 +63,7 @@ extern(C) double exp(double x); double log(double x); double acos(double x); - double pow(double x, double e); +// double pow(double x, double e); } int float_is_integer(double f, out ulong i) @@ -146,6 +146,68 @@ unittest 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 a87c8dd..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: @@ -57,7 +57,7 @@ void[] alloc_aligned(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[] realloc_aligned(void[] mem, size_t newSize, size_t alignment) nothrow @nogc @@ -104,7 +104,7 @@ 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 free_aligned(void[] mem) nothrow @nogc @@ -114,10 +114,10 @@ void free_aligned(void[] mem) nothrow @nogc 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 diff --git a/src/urt/mem/allocator.d b/src/urt/mem/allocator.d index 2d3f295..f6a54ca 100644 --- a/src/urt/mem/allocator.d +++ b/src/urt/mem/allocator.d @@ -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 9a903c8..ec4e82e 100644 --- a/src/urt/mem/package.d +++ b/src/urt/mem/package.d @@ -1,20 +1,31 @@ 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* 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) pure; int strcmp(const char* s1, const char* s2) pure; @@ -27,3 +38,169 @@ nothrow @nogc: // 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/string.d b/src/urt/mem/string.d index a3adc22..ff606ca 100644 --- a/src/urt/mem/string.d +++ b/src/urt/mem/string.d @@ -4,12 +4,6 @@ import urt.mem; import urt.string; -// TODO: THIS IS TEMP!! REMOVE ME!! -shared static this() -{ - initStringHeap(ushort.max); -} - struct CacheString { @@ -77,12 +71,12 @@ 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 = defaultAllocator.allocArray!char(stringHeapSize); + stringHeap = defaultAllocator.allocArray!char(string_heap_size); // write the null string to the start stringHeap[0..2] = 0; @@ -91,8 +85,9 @@ void initStringHeap(uint stringHeapSize) nothrow stringHeapInitialised = true; } -void deinitStringHeap() nothrow +void deinit_string_heap() nothrow @nogc { + defaultAllocator.freeArray(stringHeap); } uint getStringHeapAllocated() nothrow @nogc diff --git a/src/urt/package.d b/src/urt/package.d index 6e57129..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.mem.string : init_string_heap, deinit_string_heap; + init_string_heap(ushort.max); + + import urt.time : init_clock; + init_clock(); import urt.rand; init_rand(); - import urt.dbg : setup_assert_handler; - setup_assert_handler(); - 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/result.d b/src/urt/result.d index 2462386..06b16d9 100644 --- a/src/urt/result.d +++ b/src/urt/result.d @@ -40,7 +40,7 @@ nothrow @nogc: version (Windows) { - import core.sys.windows.windows; + import urt.internal.sys.windows; enum InternalResult : Result { @@ -58,7 +58,14 @@ version (Windows) } else version (Posix) { - import core.stdc.errno; + import urt.internal.stdc; + + extern (C) private int* __errno_location() nothrow @nogc; + + @property int errno() nothrow @nogc @trusted + { + return *__errno_location(); + } enum InternalResult : Result { diff --git a/src/urt/socket.d b/src/urt/socket.d index b1f4645..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; @@ -26,11 +26,10 @@ version (Windows) } 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_ : in6_addr, sockaddr_in6; alias _bind = urt.internal.os.bind, _listen = urt.internal.os.listen, _connect = urt.internal.os.connect, @@ -1090,7 +1089,7 @@ Result socket_getlasterror() version (Windows) return Result(WSAGetLastError()); else - return Result(errno); + return errno_result(); } Result get_socket_error(Socket socket) @@ -1576,7 +1575,7 @@ 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!! diff --git a/src/urt/string/string.d b/src/urt/string/string.d index 440c395..47c7489 100644 --- a/src/urt/string/string.d +++ b/src/urt/string/string.d @@ -439,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"); @@ -462,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); @@ -471,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"); @@ -1101,7 +1104,7 @@ private: __gshared StringAllocator[4] stringAllocators; static assert(stringAllocators.length <= 4, "Only 2 bits reserved to store allocator index"); -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/system.d b/src/urt/system.d index 903ce2a..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 @@ -93,7 +93,7 @@ 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; @@ -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 e970b75..43592d1 100644 --- a/src/urt/time.d +++ b/src/urt/time.d @@ -4,7 +4,7 @@ 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; } @@ -67,9 +67,9 @@ pure nothrow @nogc: static if (is(T == Time!c, Clock c) && c != clock) { static if (clock == Clock.Monotonic && c == Clock.SystemTime) - return SysTime(ticks + ticksSinceBoot); + return SysTime(ticks + ticks_since_boot); else - return MonoTime(ticks - ticksSinceBoot); + return MonoTime(ticks - ticks_since_boot); } else static assert(false, "constraint out of sync"); @@ -88,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); } @@ -168,7 +168,7 @@ pure nothrow @nogc: => ticks != 0; T opCast(T)() const if (is_some_float!T) - => cast(T)ticks / cast(T)ticksPerSecond; + => cast(T)ticks / cast(T)ticks_per_second; bool opEquals(Duration b) const => ticks == b.ticks; @@ -191,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"); } @@ -303,7 +303,7 @@ pure nothrow @nogc: if (last_unit == 8) return -1; - ticks = total_nsecs / nsecMultiplier; + ticks = total_nsecs / nsec_multiplier; return offset; } @@ -679,21 +679,21 @@ pure nothrow @nogc: 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"); } @@ -746,10 +746,11 @@ SysTime getSysTime() SysTime getSysTime(DateTime time) pure { version (Windows) - return dateTimeToFileTime(time); + return datetime_to_filetime(time); else version (Posix) { - assert(false, "TODO"); + timespec ts = datetime_to_realtime(time); + return SysTime(ts.tv_sec * 1_000_000_000 + ts.tv_nsec); } else static assert(false, "TODO"); @@ -758,12 +759,12 @@ SysTime getSysTime(DateTime time) pure 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"); @@ -772,13 +773,13 @@ DateTime getDateTime() 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"); @@ -825,30 +826,30 @@ __gshared immutable uint[9] digit_multipliers = [ 100_000_000, 10_000_000, 1_000 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 @@ -860,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) { @@ -875,7 +876,7 @@ 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"); @@ -1010,7 +1011,7 @@ unittest version (Windows) { - DateTime fileTimeToDateTime(SysTime ftime) pure + DateTime filetime_to_datetime(SysTime ftime) pure { version (BigEndian) static assert(false, "Only works in little endian!"); @@ -1034,7 +1035,7 @@ version (Windows) return dt; } - SysTime dateTimeToFileTime(DateTime dt) pure + SysTime datetime_to_filetime(ref DateTime dt) pure { version (BigEndian) static assert(false, "Only works in little endian!"); @@ -1062,7 +1063,7 @@ version (Windows) } else version (Posix) { - DateTime realtimeToDateTime(timespec ts) pure + DateTime realtime_to_datetime(timespec ts) pure { tm t; alias PureHACK = extern(C) tm* function(time_t* timer, tm* buf) pure nothrow @nogc; @@ -1080,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/util.d b/src/urt/util.d index fb8fe2d..4971ddd 100644 --- a/src/urt/util.d +++ b/src/urt/util.d @@ -30,12 +30,12 @@ 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) @@ -110,6 +110,42 @@ bool is_aligned(T)(T value, size_t alignment) 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) { @@ -460,7 +496,7 @@ T bit_reverse(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 @@ -481,6 +517,83 @@ T bit_reverse(T)(T 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 { version (LDC)