From 5ab99cf8873b130284336c52551ccede28f6cb2e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 17 Mar 2019 10:56:44 +0100 Subject: Move memory functions into memory.lifecycle - move - moveEmplace - forward - emplace - swap --- .travis.yml | 6 +- appveyor.yml | 1 + dub.json | 6 +- encoding/dub.json | 9 +- encoding/source/tanya/encoding/ascii.d | 501 ----- encoding/source/tanya/encoding/package.d | 17 - encoding/tanya/encoding/ascii.d | 501 +++++ encoding/tanya/encoding/package.d | 17 + memory/dub.json | 18 + memory/tanya/memory/allocator.d | 81 + memory/tanya/memory/lifecycle.d | 984 ++++++++++ memory/tanya/memory/mallocator.d | 233 +++ memory/tanya/memory/mmappool.d | 657 +++++++ memory/tanya/memory/op.d | 437 +++++ memory/tanya/memory/package.d | 132 ++ memory/tanya/memory/smartref.d | 903 +++++++++ memory/tanya/test/assertion.d | 105 + memory/tanya/test/package.d | 18 + memory/tanya/test/stub.d | 373 ++++ meta/dub.json | 9 +- meta/source/tanya/meta/metafunction.d | 1862 ------------------ meta/source/tanya/meta/package.d | 23 - meta/source/tanya/meta/trait.d | 3106 ----------------------------- meta/source/tanya/meta/transform.d | 981 ---------- meta/tanya/meta/metafunction.d | 1862 ++++++++++++++++++ meta/tanya/meta/package.d | 23 + meta/tanya/meta/trait.d | 3158 ++++++++++++++++++++++++++++++ meta/tanya/meta/transform.d | 981 ++++++++++ os/dub.json | 9 +- os/source/tanya/os/error.d | 419 ---- os/source/tanya/os/package.d | 18 - os/tanya/os/error.d | 419 ++++ os/tanya/os/package.d | 18 + source/tanya/algorithm/iteration.d | 2 +- source/tanya/algorithm/mutation.d | 289 +-- source/tanya/container/array.d | 1 - source/tanya/container/entry.d | 2 +- source/tanya/container/list.d | 1 - source/tanya/conv.d | 271 +-- source/tanya/functional.d | 68 +- source/tanya/memory/allocator.d | 81 - source/tanya/memory/lifecycle.d | 360 ---- source/tanya/memory/mallocator.d | 233 --- source/tanya/memory/mmappool.d | 657 ------- source/tanya/memory/op.d | 437 ----- source/tanya/memory/package.d | 203 -- source/tanya/memory/smartref.d | 914 --------- source/tanya/net/ip.d | 1 + source/tanya/range/adapter.d | 2 +- source/tanya/range/primitive.d | 2 +- source/tanya/test/assertion.d | 105 - source/tanya/test/package.d | 18 - source/tanya/test/stub.d | 373 ---- source/tanya/typecons.d | 4 +- sys/dub.json | 9 +- sys/source/tanya/sys/linux/syscall.d | 61 - sys/source/tanya/sys/posix/ioctl.d | 78 - sys/source/tanya/sys/posix/mman.d | 31 - sys/source/tanya/sys/posix/net/if_.d | 27 - sys/source/tanya/sys/posix/socket.d | 152 -- sys/source/tanya/sys/windows/def.d | 66 - sys/source/tanya/sys/windows/ifdef.d | 30 - sys/source/tanya/sys/windows/iphlpapi.d | 39 - sys/source/tanya/sys/windows/package.d | 21 - sys/source/tanya/sys/windows/winbase.d | 55 - sys/source/tanya/sys/windows/winsock2.d | 219 --- sys/tanya/sys/linux/syscall.d | 59 + sys/tanya/sys/posix/ioctl.d | 76 + sys/tanya/sys/posix/mman.d | 29 + sys/tanya/sys/posix/net/if_.d | 25 + sys/tanya/sys/posix/socket.d | 150 ++ sys/tanya/sys/windows/def.d | 64 + sys/tanya/sys/windows/ifdef.d | 28 + sys/tanya/sys/windows/iphlpapi.d | 37 + sys/tanya/sys/windows/package.d | 19 + sys/tanya/sys/windows/winbase.d | 53 + sys/tanya/sys/windows/winsock2.d | 217 ++ 77 files changed, 11741 insertions(+), 11715 deletions(-) delete mode 100644 encoding/source/tanya/encoding/ascii.d delete mode 100644 encoding/source/tanya/encoding/package.d create mode 100644 encoding/tanya/encoding/ascii.d create mode 100644 encoding/tanya/encoding/package.d create mode 100644 memory/dub.json create mode 100644 memory/tanya/memory/allocator.d create mode 100644 memory/tanya/memory/lifecycle.d create mode 100644 memory/tanya/memory/mallocator.d create mode 100644 memory/tanya/memory/mmappool.d create mode 100644 memory/tanya/memory/op.d create mode 100644 memory/tanya/memory/package.d create mode 100644 memory/tanya/memory/smartref.d create mode 100644 memory/tanya/test/assertion.d create mode 100644 memory/tanya/test/package.d create mode 100644 memory/tanya/test/stub.d delete mode 100644 meta/source/tanya/meta/metafunction.d delete mode 100644 meta/source/tanya/meta/package.d delete mode 100644 meta/source/tanya/meta/trait.d delete mode 100644 meta/source/tanya/meta/transform.d create mode 100644 meta/tanya/meta/metafunction.d create mode 100644 meta/tanya/meta/package.d create mode 100644 meta/tanya/meta/trait.d create mode 100644 meta/tanya/meta/transform.d delete mode 100644 os/source/tanya/os/error.d delete mode 100644 os/source/tanya/os/package.d create mode 100644 os/tanya/os/error.d create mode 100644 os/tanya/os/package.d delete mode 100644 source/tanya/memory/allocator.d delete mode 100644 source/tanya/memory/lifecycle.d delete mode 100644 source/tanya/memory/mallocator.d delete mode 100644 source/tanya/memory/mmappool.d delete mode 100644 source/tanya/memory/op.d delete mode 100644 source/tanya/memory/package.d delete mode 100644 source/tanya/memory/smartref.d delete mode 100644 source/tanya/test/assertion.d delete mode 100644 source/tanya/test/package.d delete mode 100644 source/tanya/test/stub.d delete mode 100644 sys/source/tanya/sys/linux/syscall.d delete mode 100644 sys/source/tanya/sys/posix/ioctl.d delete mode 100644 sys/source/tanya/sys/posix/mman.d delete mode 100644 sys/source/tanya/sys/posix/net/if_.d delete mode 100644 sys/source/tanya/sys/posix/socket.d delete mode 100644 sys/source/tanya/sys/windows/def.d delete mode 100644 sys/source/tanya/sys/windows/ifdef.d delete mode 100644 sys/source/tanya/sys/windows/iphlpapi.d delete mode 100644 sys/source/tanya/sys/windows/package.d delete mode 100644 sys/source/tanya/sys/windows/winbase.d delete mode 100644 sys/source/tanya/sys/windows/winsock2.d create mode 100644 sys/tanya/sys/linux/syscall.d create mode 100644 sys/tanya/sys/posix/ioctl.d create mode 100644 sys/tanya/sys/posix/mman.d create mode 100644 sys/tanya/sys/posix/net/if_.d create mode 100644 sys/tanya/sys/posix/socket.d create mode 100644 sys/tanya/sys/windows/def.d create mode 100644 sys/tanya/sys/windows/ifdef.d create mode 100644 sys/tanya/sys/windows/iphlpapi.d create mode 100644 sys/tanya/sys/windows/package.d create mode 100644 sys/tanya/sys/windows/winbase.d create mode 100644 sys/tanya/sys/windows/winsock2.d diff --git a/.travis.yml b/.travis.yml index 3cb4f7a..ee8283f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: include: - name: D-Scanner d: dmd-$LATEST - env: DSCANNER=0.6.0 + env: DSCANNER=0.7.0 os: linux - name: DDoc d: dmd-$LATEST @@ -32,7 +32,7 @@ matrix: allow_failures: - name: D-Scanner d: dmd-$LATEST - env: DSCANNER=0.6.0 + env: DSCANNER=0.7.0 os: linux addons: @@ -53,12 +53,14 @@ script: dub build :sys -b ddox --compiler=$DC; dub build :os -b ddox --compiler=$DC; dub build :encoding -b ddox --compiler=$DC; + dub build :memory -b ddox --compiler=$DC; dub build -b ddox --compiler=$DC; elif [ -z "$DSCANNER" ]; then dub test :meta -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :sys -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :os -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :encoding -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; + dub test :memory -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; else dub fetch dscanner --version=$DSCANNER; diff --git a/appveyor.yml b/appveyor.yml index fd4db00..dc0e5e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,4 +61,5 @@ test_script: - dub test :sys -b unittest --arch=%Darch% --compiler=%DC% - dub test :os -b unittest --arch=%Darch% --compiler=%DC% - dub test :encoding -b unittest --arch=%Darch% --compiler=%DC% + - dub test :memory -b unittest --arch=%Darch% --compiler=%DC% - dub test -b unittest --arch=%Darch% --compiler=%DC% diff --git a/dub.json b/dub.json index 823f97f..3fd1ec6 100644 --- a/dub.json +++ b/dub.json @@ -13,7 +13,8 @@ "tanya:meta": "*", "tanya:sys": "*", "tanya:os": "*", - "tanya:encoding": "*" + "tanya:encoding": "*", + "tanya:memory": "*" }, "dependencies-linux": { @@ -24,7 +25,8 @@ "./meta", "./sys", "./os", - "./encoding" + "./encoding", + "./memory" ], "configurations": [ diff --git a/encoding/dub.json b/encoding/dub.json index 9c50acd..00ef6de 100644 --- a/encoding/dub.json +++ b/encoding/dub.json @@ -5,5 +5,12 @@ "dependencies": { "tanya:meta": "*" - } + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/encoding/source/tanya/encoding/ascii.d b/encoding/source/tanya/encoding/ascii.d deleted file mode 100644 index c664861..0000000 --- a/encoding/source/tanya/encoding/ascii.d +++ /dev/null @@ -1,501 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Functions operating on ASCII characters. - * - * ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation - * $(B I)nterchange. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/encoding/ascii.d, - * tanya/encoding/ascii.d) - */ -module tanya.encoding.ascii; - -import tanya.meta.trait; - -immutable string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f. -immutable string hexDigits = "0123456789ABCDEF"; /// 0..9A..F. -immutable string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f. -immutable string digits = "0123456789"; /// 0..9. -immutable string octalDigits = "01234567"; /// 0..7. - -/// A..Za..z. -immutable string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -immutable string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z. -immutable string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z. - -/** - * Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR), - * Vertical Tab (VT) or Form Feed (FF). - */ -immutable string whitespace = "\t\n\v\f\r "; - -/// Letter case specifier. -enum LetterCase : bool -{ - upper, /// Uppercase. - lower, /// Lowercase. -} - -/** - * Checks for an uppecase alphabetic character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an uppercase alphabetic - * character, $(D_KEYWORD false) otherwise. - */ -bool isUpper(C)(C c) -if (isSomeChar!C) -{ - return (c >= 'A') && (c <= 'Z'); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isUpper('A')); - assert(isUpper('Z')); - assert(isUpper('L')); - assert(!isUpper('a')); - assert(!isUpper('!')); -} - -/** - * Checks for a lowercase alphabetic character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a lowercase alphabetic - * character, $(D_KEYWORD false) otherwise. - */ -bool isLower(C)(C c) -if (isSomeChar!C) -{ - return (c >= 'a') && (c <= 'z'); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isLower('a')); - assert(isLower('z')); - assert(isLower('l')); - assert(!isLower('A')); - assert(!isLower('!')); -} - -/** - * Checks for an alphabetic character (upper- or lowercase). - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character, - * $(D_KEYWORD false) otherwise. - */ -bool isAlpha(C)(C c) -if (isSomeChar!C) -{ - return isUpper(c) || isLower(c); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isAlpha('A')); - assert(isAlpha('Z')); - assert(isAlpha('L')); - assert(isAlpha('a')); - assert(isAlpha('z')); - assert(isAlpha('l')); - assert(!isAlpha('!')); -} - -/** - * Checks for a digit. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a digit, - * $(D_KEYWORD false) otherwise. - */ -bool isDigit(C)(C c) -if (isSomeChar!C) -{ - return (c >= '0') && (c <= '9'); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isDigit('0')); - assert(isDigit('1')); - assert(isDigit('2')); - assert(isDigit('3')); - assert(isDigit('4')); - assert(isDigit('5')); - assert(isDigit('6')); - assert(isDigit('7')); - assert(isDigit('8')); - assert(isDigit('9')); - assert(!isDigit('a')); - assert(!isDigit('!')); -} - -/** - * Checks for an alphabetic character (upper- or lowercase) or a digit. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character or a - * digit, $(D_KEYWORD false) otherwise. - */ -bool isAlphaNum(C)(C c) -if (isSomeChar!C) -{ - return isAlpha(c) || isDigit(c); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isAlphaNum('0')); - assert(isAlphaNum('1')); - assert(isAlphaNum('9')); - assert(isAlphaNum('A')); - assert(isAlphaNum('Z')); - assert(isAlphaNum('L')); - assert(isAlphaNum('a')); - assert(isAlphaNum('z')); - assert(isAlphaNum('l')); - assert(!isAlphaNum('!')); -} - -/** - * Checks for a 7-bit ASCII character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an ASCII character, - * $(D_KEYWORD false) otherwise. - */ -bool isASCII(C)(C c) -if (isSomeChar!C) -{ - return c < 128; -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isASCII('0')); - assert(isASCII('L')); - assert(isASCII('l')); - assert(isASCII('!')); - assert(!isASCII('©')); - assert(!isASCII('§')); - assert(!isASCII(char.init)); // 0xFF - assert(!isASCII(wchar.init)); // 0xFFFF - assert(!isASCII(dchar.init)); // 0xFFFF -} - -/** - * Checks for a control character. - * - * Control characters are non-printable characters. Their ASCII codes are those - * between 0x00 (NUL) and 0x1f (US), and 0x7f (DEL). - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isPrintable), $(D_PSYMBOL isGraphical). - */ -bool isControl(C)(C c) -if (isSomeChar!C) -{ - return (c <= 0x1f) || (c == 0x7f); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isControl('\t')); - assert(isControl('\0')); - assert(isControl('\u007f')); - assert(!isControl(' ')); - assert(!isControl('a')); - assert(!isControl(char.init)); // 0xFF - assert(!isControl(wchar.init)); // 0xFFFF -} - -/** - * Checks for a whitespace character. - * - * Whitespace characters are: - * - * $(UL - * $(LI Whitespace) - * $(LI Horizontal Tab (HT)) - * $(LI Line Feed (LF)) - * $(LI Carriage Return (CR)) - * $(LI Vertical Tab (VT)) - * $(LI Form Feed (FF)) - * ) - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a whitespace character, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL whitespace). - */ -bool isWhite(C)(C c) -if (isSomeChar!C) -{ - return ((c >= 0x09) && (c <= 0x0d)) || (c == 0x20); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isWhite('\t')); - assert(isWhite('\n')); - assert(isWhite('\v')); - assert(isWhite('\f')); - assert(isWhite('\r')); - assert(isWhite(' ')); -} - -/** - * Checks for a graphical character. - * - * Graphical characters are printable characters but whitespace characters. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isControl), $(D_PSYMBOL isWhite). - */ -bool isGraphical(C)(C c) -if (isSomeChar!C) -{ - return (c > 0x20) && (c < 0x7f); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isGraphical('a')); - assert(isGraphical('0')); - assert(!isGraphical('\u007f')); - assert(!isGraphical('§')); - assert(!isGraphical('\n')); - assert(!isGraphical(' ')); -} - -/** - * Checks for a printable character. - * - * This is the opposite of a control character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isControl). - */ -bool isPrintable(C)(C c) -if (isSomeChar!C) -{ - return (c >= 0x20) && (c < 0x7f); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isPrintable('a')); - assert(isPrintable('0')); - assert(!isPrintable('\u007f')); - assert(!isPrintable('§')); - assert(!isPrintable('\n')); - assert(isPrintable(' ')); -} - -/** - * Checks for a hexadecimal digit. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a hexadecimal digit, - * $(D_KEYWORD false) otherwise. - */ -bool isHexDigit(C)(C c) -if (isSomeChar!C) -{ - return ((c >= '0') && (c <= '9')) - || ((c >= 'a') && (c <= 'f')) - || ((c >= 'A') && (c <= 'F')); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isHexDigit('0')); - assert(isHexDigit('1')); - assert(isHexDigit('8')); - assert(isHexDigit('9')); - assert(isHexDigit('A')); - assert(isHexDigit('F')); - assert(!isHexDigit('G')); - assert(isHexDigit('a')); - assert(isHexDigit('f')); - assert(!isHexDigit('g')); -} - -/** - * Checks for an octal character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an octal character, - * $(D_KEYWORD false) otherwise. - */ -bool isOctalDigit(C)(C c) -if (isSomeChar!C) -{ - return (c >= '0') && (c <= '7'); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isOctalDigit('0')); - assert(isOctalDigit('1')); - assert(isOctalDigit('2')); - assert(isOctalDigit('3')); - assert(isOctalDigit('4')); - assert(isOctalDigit('5')); - assert(isOctalDigit('6')); - assert(isOctalDigit('7')); - assert(!isOctalDigit('8')); -} - -/** - * Checks for a octal character. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a octal character, - * $(D_KEYWORD false) otherwise. - */ -bool isPunctuation(C)(C c) -if (isSomeChar!C) -{ - return ((c >= 0x21) && (c <= 0x2f)) - || ((c >= 0x3a) && (c <= 0x40)) - || ((c >= 0x5b) && (c <= 0x60)) - || ((c >= 0x7b) && (c <= 0x7e)); -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isPunctuation('!')); - assert(isPunctuation(':')); - assert(isPunctuation('\\')); - assert(isPunctuation('|')); - assert(!isPunctuation('0')); - assert(!isPunctuation(' ')); -} - -/** - * Converts $(D_PARAM c) to uppercase. - * - * If $(D_PARAM c) is not a lowercase character, $(D_PARAM c) is returned - * unchanged. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c) - * otherwise. - */ -C toUpper(C)(C c) -if (isSomeChar!C) -{ - return isLower(c) ? (cast(C) (c - 32)) : c; -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(toUpper('a') == 'A'); - assert(toUpper('A') == 'A'); - assert(toUpper('!') == '!'); -} - -/** - * Converts $(D_PARAM c) to lowercase. - * - * If $(D_PARAM c) is not an uppercase character, $(D_PARAM c) is returned - * unchanged. - * - * Params: - * C = Some character type. - * c = Some character. - * - * Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c) - * otherwise. - */ -C toLower(C)(C c) -if (isSomeChar!C) -{ - return isUpper(c) ? (cast(C) (c + 32)) : c; -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(toLower('A') == 'a'); - assert(toLower('a') == 'a'); - assert(toLower('!') == '!'); -} diff --git a/encoding/source/tanya/encoding/package.d b/encoding/source/tanya/encoding/package.d deleted file mode 100644 index e9912ee..0000000 --- a/encoding/source/tanya/encoding/package.d +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This package provides tools to work with text encodings. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/encoding/package.d, - * tanya/encoding/package.d) - */ -module tanya.encoding; - -public import tanya.encoding.ascii; diff --git a/encoding/tanya/encoding/ascii.d b/encoding/tanya/encoding/ascii.d new file mode 100644 index 0000000..6498547 --- /dev/null +++ b/encoding/tanya/encoding/ascii.d @@ -0,0 +1,501 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Functions operating on ASCII characters. + * + * ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation + * $(B I)nterchange. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/encoding/tanya/encoding/ascii.d, + * tanya/encoding/ascii.d) + */ +module tanya.encoding.ascii; + +import tanya.meta.trait; + +immutable string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f. +immutable string hexDigits = "0123456789ABCDEF"; /// 0..9A..F. +immutable string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f. +immutable string digits = "0123456789"; /// 0..9. +immutable string octalDigits = "01234567"; /// 0..7. + +/// A..Za..z. +immutable string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +immutable string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z. +immutable string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z. + +/** + * Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR), + * Vertical Tab (VT) or Form Feed (FF). + */ +immutable string whitespace = "\t\n\v\f\r "; + +/// Letter case specifier. +enum LetterCase : bool +{ + upper, /// Uppercase. + lower, /// Lowercase. +} + +/** + * Checks for an uppecase alphabetic character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an uppercase alphabetic + * character, $(D_KEYWORD false) otherwise. + */ +bool isUpper(C)(C c) +if (isSomeChar!C) +{ + return (c >= 'A') && (c <= 'Z'); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isUpper('A')); + assert(isUpper('Z')); + assert(isUpper('L')); + assert(!isUpper('a')); + assert(!isUpper('!')); +} + +/** + * Checks for a lowercase alphabetic character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a lowercase alphabetic + * character, $(D_KEYWORD false) otherwise. + */ +bool isLower(C)(C c) +if (isSomeChar!C) +{ + return (c >= 'a') && (c <= 'z'); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isLower('a')); + assert(isLower('z')); + assert(isLower('l')); + assert(!isLower('A')); + assert(!isLower('!')); +} + +/** + * Checks for an alphabetic character (upper- or lowercase). + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character, + * $(D_KEYWORD false) otherwise. + */ +bool isAlpha(C)(C c) +if (isSomeChar!C) +{ + return isUpper(c) || isLower(c); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isAlpha('A')); + assert(isAlpha('Z')); + assert(isAlpha('L')); + assert(isAlpha('a')); + assert(isAlpha('z')); + assert(isAlpha('l')); + assert(!isAlpha('!')); +} + +/** + * Checks for a digit. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a digit, + * $(D_KEYWORD false) otherwise. + */ +bool isDigit(C)(C c) +if (isSomeChar!C) +{ + return (c >= '0') && (c <= '9'); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isDigit('0')); + assert(isDigit('1')); + assert(isDigit('2')); + assert(isDigit('3')); + assert(isDigit('4')); + assert(isDigit('5')); + assert(isDigit('6')); + assert(isDigit('7')); + assert(isDigit('8')); + assert(isDigit('9')); + assert(!isDigit('a')); + assert(!isDigit('!')); +} + +/** + * Checks for an alphabetic character (upper- or lowercase) or a digit. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character or a + * digit, $(D_KEYWORD false) otherwise. + */ +bool isAlphaNum(C)(C c) +if (isSomeChar!C) +{ + return isAlpha(c) || isDigit(c); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isAlphaNum('0')); + assert(isAlphaNum('1')); + assert(isAlphaNum('9')); + assert(isAlphaNum('A')); + assert(isAlphaNum('Z')); + assert(isAlphaNum('L')); + assert(isAlphaNum('a')); + assert(isAlphaNum('z')); + assert(isAlphaNum('l')); + assert(!isAlphaNum('!')); +} + +/** + * Checks for a 7-bit ASCII character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an ASCII character, + * $(D_KEYWORD false) otherwise. + */ +bool isASCII(C)(C c) +if (isSomeChar!C) +{ + return c < 128; +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isASCII('0')); + assert(isASCII('L')); + assert(isASCII('l')); + assert(isASCII('!')); + assert(!isASCII('©')); + assert(!isASCII('§')); + assert(!isASCII(char.init)); // 0xFF + assert(!isASCII(wchar.init)); // 0xFFFF + assert(!isASCII(dchar.init)); // 0xFFFF +} + +/** + * Checks for a control character. + * + * Control characters are non-printable characters. Their ASCII codes are those + * between 0x00 (NUL) and 0x1f (US), and 0x7f (DEL). + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isPrintable), $(D_PSYMBOL isGraphical). + */ +bool isControl(C)(C c) +if (isSomeChar!C) +{ + return (c <= 0x1f) || (c == 0x7f); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isControl('\t')); + assert(isControl('\0')); + assert(isControl('\u007f')); + assert(!isControl(' ')); + assert(!isControl('a')); + assert(!isControl(char.init)); // 0xFF + assert(!isControl(wchar.init)); // 0xFFFF +} + +/** + * Checks for a whitespace character. + * + * Whitespace characters are: + * + * $(UL + * $(LI Whitespace) + * $(LI Horizontal Tab (HT)) + * $(LI Line Feed (LF)) + * $(LI Carriage Return (CR)) + * $(LI Vertical Tab (VT)) + * $(LI Form Feed (FF)) + * ) + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a whitespace character, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL whitespace). + */ +bool isWhite(C)(C c) +if (isSomeChar!C) +{ + return ((c >= 0x09) && (c <= 0x0d)) || (c == 0x20); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isWhite('\t')); + assert(isWhite('\n')); + assert(isWhite('\v')); + assert(isWhite('\f')); + assert(isWhite('\r')); + assert(isWhite(' ')); +} + +/** + * Checks for a graphical character. + * + * Graphical characters are printable characters but whitespace characters. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isControl), $(D_PSYMBOL isWhite). + */ +bool isGraphical(C)(C c) +if (isSomeChar!C) +{ + return (c > 0x20) && (c < 0x7f); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isGraphical('a')); + assert(isGraphical('0')); + assert(!isGraphical('\u007f')); + assert(!isGraphical('§')); + assert(!isGraphical('\n')); + assert(!isGraphical(' ')); +} + +/** + * Checks for a printable character. + * + * This is the opposite of a control character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isControl). + */ +bool isPrintable(C)(C c) +if (isSomeChar!C) +{ + return (c >= 0x20) && (c < 0x7f); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isPrintable('a')); + assert(isPrintable('0')); + assert(!isPrintable('\u007f')); + assert(!isPrintable('§')); + assert(!isPrintable('\n')); + assert(isPrintable(' ')); +} + +/** + * Checks for a hexadecimal digit. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a hexadecimal digit, + * $(D_KEYWORD false) otherwise. + */ +bool isHexDigit(C)(C c) +if (isSomeChar!C) +{ + return ((c >= '0') && (c <= '9')) + || ((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F')); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isHexDigit('0')); + assert(isHexDigit('1')); + assert(isHexDigit('8')); + assert(isHexDigit('9')); + assert(isHexDigit('A')); + assert(isHexDigit('F')); + assert(!isHexDigit('G')); + assert(isHexDigit('a')); + assert(isHexDigit('f')); + assert(!isHexDigit('g')); +} + +/** + * Checks for an octal character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is an octal character, + * $(D_KEYWORD false) otherwise. + */ +bool isOctalDigit(C)(C c) +if (isSomeChar!C) +{ + return (c >= '0') && (c <= '7'); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isOctalDigit('0')); + assert(isOctalDigit('1')); + assert(isOctalDigit('2')); + assert(isOctalDigit('3')); + assert(isOctalDigit('4')); + assert(isOctalDigit('5')); + assert(isOctalDigit('6')); + assert(isOctalDigit('7')); + assert(!isOctalDigit('8')); +} + +/** + * Checks for a octal character. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM c) is a octal character, + * $(D_KEYWORD false) otherwise. + */ +bool isPunctuation(C)(C c) +if (isSomeChar!C) +{ + return ((c >= 0x21) && (c <= 0x2f)) + || ((c >= 0x3a) && (c <= 0x40)) + || ((c >= 0x5b) && (c <= 0x60)) + || ((c >= 0x7b) && (c <= 0x7e)); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(isPunctuation('!')); + assert(isPunctuation(':')); + assert(isPunctuation('\\')); + assert(isPunctuation('|')); + assert(!isPunctuation('0')); + assert(!isPunctuation(' ')); +} + +/** + * Converts $(D_PARAM c) to uppercase. + * + * If $(D_PARAM c) is not a lowercase character, $(D_PARAM c) is returned + * unchanged. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c) + * otherwise. + */ +C toUpper(C)(C c) +if (isSomeChar!C) +{ + return isLower(c) ? (cast(C) (c - 32)) : c; +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(toUpper('a') == 'A'); + assert(toUpper('A') == 'A'); + assert(toUpper('!') == '!'); +} + +/** + * Converts $(D_PARAM c) to lowercase. + * + * If $(D_PARAM c) is not an uppercase character, $(D_PARAM c) is returned + * unchanged. + * + * Params: + * C = Some character type. + * c = Some character. + * + * Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c) + * otherwise. + */ +C toLower(C)(C c) +if (isSomeChar!C) +{ + return isUpper(c) ? (cast(C) (c + 32)) : c; +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(toLower('A') == 'a'); + assert(toLower('a') == 'a'); + assert(toLower('!') == '!'); +} diff --git a/encoding/tanya/encoding/package.d b/encoding/tanya/encoding/package.d new file mode 100644 index 0000000..9ec74d0 --- /dev/null +++ b/encoding/tanya/encoding/package.d @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This package provides tools to work with text encodings. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/encoding/tanya/encoding/package.d, + * tanya/encoding/package.d) + */ +module tanya.encoding; + +public import tanya.encoding.ascii; diff --git a/memory/dub.json b/memory/dub.json new file mode 100644 index 0000000..7339127 --- /dev/null +++ b/memory/dub.json @@ -0,0 +1,18 @@ +{ + "name": "memory", + "description": "Tools for manual memory management (allocators, smart pointers)", + "targetType": "library", + + "dependencies": { + "tanya:meta": "*", + "tanya:os": "*", + "tanya:sys": "*" + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] +} diff --git a/memory/tanya/memory/allocator.d b/memory/tanya/memory/allocator.d new file mode 100644 index 0000000..e161612 --- /dev/null +++ b/memory/tanya/memory/allocator.d @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This module contains the interface for implementing custom allocators. + * + * Allocators are classes encapsulating memory allocation strategy. This allows + * to decouple memory management from the algorithms and the data. + * + * Copyright: Eugene Wissner 2016-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/allocator.d, + * tanya/memory/allocator.d) + */ +module tanya.memory.allocator; + +/** + * Abstract class implementing a basic allocator. + */ +interface Allocator +{ + /** + * Returns: Alignment offered. + */ + @property uint alignment() const shared pure nothrow @safe @nogc; + + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: Pointer to the new allocated memory. + */ + void[] allocate(size_t size) shared pure nothrow @nogc; + + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) shared pure nothrow @nogc; + + /** + * Increases or decreases the size of a memory block. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: Pointer to the allocated memory. + */ + bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc; + + /** + * Reallocates a memory block in place if possible or returns + * $(D_KEYWORD false). This function cannot be used to allocate or + * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or + * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. + */ + bool reallocateInPlace(ref void[] p, size_t size) + shared pure nothrow @nogc; +} + +package template GetPureInstance(T : Allocator) +{ + alias GetPureInstance = shared(T) function() + pure nothrow @nogc; +} diff --git a/memory/tanya/memory/lifecycle.d b/memory/tanya/memory/lifecycle.d new file mode 100644 index 0000000..35d251b --- /dev/null +++ b/memory/tanya/memory/lifecycle.d @@ -0,0 +1,984 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Lifecycle management functions, types and related exceptions. + * + * Copyright: Eugene Wissner 2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/lifecycle.d, + * tanya/memory/lifecycle.d) + */ +module tanya.memory.lifecycle; + +import tanya.memory : defaultAllocator; +import tanya.memory.allocator; +import tanya.meta.trait; +import tanya.meta.metafunction; +version (unittest) import tanya.test.stub; + +/** + * Error thrown if memory allocation fails. + */ +final class OutOfMemoryError : Error +{ + /** + * Constructs new error. + * + * Params: + * msg = The message for the exception. + * file = The file where the exception occurred. + * line = The line number where the exception occurred. + * next = The previous exception in the chain of exceptions, if any. + */ + this(string msg = "Out of memory", + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null) @nogc nothrow pure @safe + { + super(msg, file, line, next); + } + + /// ditto + this(string msg, + Throwable next, + string file = __FILE__, + size_t line = __LINE__) @nogc nothrow pure @safe + { + super(msg, file, line, next); + } +} + +/** + * Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it. + * + * Params: + * msg = Custom error message. + * + * Throws: $(D_PSYMBOL OutOfMemoryError). + */ +void onOutOfMemoryError(string msg = "Out of memory") +@nogc nothrow pure @trusted +{ + static ubyte[stateSize!OutOfMemoryError] memory; + alias PureType = OutOfMemoryError function(string) @nogc nothrow pure; + throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg); +} + +// From druntime +extern (C) +private void _d_monitordelete(Object h, bool det) @nogc nothrow pure; + +/* + * Internal function used to create, resize or destroy a dynamic array. It + * may throw $(D_PSYMBOL OutOfMemoryError). The new + * allocated part of the array isn't initialized. This function can be trusted + * only in the data structures that can ensure that the array is + * allocated/rellocated/deallocated with the same allocator. + * + * Params: + * T = Element type of the array being created. + * allocator = The allocator used for getting memory. + * array = A reference to the array being changed. + * length = New array length. + * + * Returns: $(D_PARAM array). + */ +package(tanya) T[] resize(T)(shared Allocator allocator, + auto ref T[] array, + const size_t length) @trusted +{ + if (length == 0) + { + if (allocator.deallocate(array)) + { + return null; + } + else + { + onOutOfMemoryError(); + } + } + + void[] buf = array; + if (!allocator.reallocate(buf, length * T.sizeof)) + { + onOutOfMemoryError(); + } + // Casting from void[] is unsafe, but we know we cast to the original type. + array = cast(T[]) buf; + + return array; +} + +@nogc nothrow pure @safe unittest +{ + int[] p; + + p = defaultAllocator.resize(p, 20); + assert(p.length == 20); + + p = defaultAllocator.resize(p, 30); + assert(p.length == 30); + + p = defaultAllocator.resize(p, 10); + assert(p.length == 10); + + p = defaultAllocator.resize(p, 0); + assert(p is null); +} + +/* + * Destroys the object. + * Returns the memory should be freed. + */ +package(tanya.memory) void[] finalize(T)(ref T* p) +{ + if (p is null) + { + return null; + } + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + return (cast(void*) p)[0 .. T.sizeof]; +} + +package(tanya.memory) void[] finalize(T)(ref T p) +if (isPolymorphicType!T) +{ + if (p is null) + { + return null; + } + static if (is(T == interface)) + { + version(Windows) + { + import core.sys.windows.unknwn : IUnknown; + static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in " + ~ __PRETTY_FUNCTION__); + } + auto ob = cast(Object) p; + } + else + { + alias ob = p; + } + auto ptr = cast(void*) ob; + auto support = ptr[0 .. typeid(ob).initializer.length]; + + auto ppv = cast(void**) ptr; + if (!*ppv) + { + return null; + } + auto pc = cast(ClassInfo*) *ppv; + scope (exit) + { + *ppv = null; + } + + auto c = *pc; + do + { + // Assume the destructor is @nogc. Leave it nothrow since the destructor + // shouldn't throw and if it does, it is an error anyway. + if (c.destructor) + { + alias DtorType = void function(Object) pure nothrow @safe @nogc; + (cast(DtorType) c.destructor)(ob); + } + } + while ((c = c.base) !is null); + + if (ppv[1]) // if monitor is not null + { + _d_monitordelete(cast(Object) ptr, true); + } + return support; +} + +package(tanya.memory) void[] finalize(T)(ref T[] p) +{ + destroyAllImpl!(T[], T)(p); + return p; +} + +package(tanya) void destroyAllImpl(R, E)(R p) +{ + static if (hasElaborateDestructor!E) + { + foreach (ref e; p) + { + destroy(e); + } + } +} + +/** + * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). + * It is assumed the respective entities had been allocated with the same + * allocator. + * + * Params: + * T = Type of $(D_PARAM p). + * allocator = Allocator the $(D_PARAM p) was allocated with. + * p = Object or array to be destroyed. + */ +void dispose(T)(shared Allocator allocator, auto ref T p) +{ + () @trusted { allocator.deallocate(finalize(p)); }(); + p = null; +} + +@nogc nothrow pure @system unittest +{ + static struct S + { + ~this() @nogc nothrow pure @safe + { + } + } + auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); + + defaultAllocator.dispose(p); +} + +// Works with interfaces. +@nogc nothrow pure @safe unittest +{ + interface I + { + } + class C : I + { + } + auto c = defaultAllocator.make!C(); + I i = c; + + defaultAllocator.dispose(i); + defaultAllocator.dispose(i); +} + +/** + * Constructs a new class instance of type $(D_PARAM T) using $(D_PARAM args) + * as the parameter list for the constructor of $(D_PARAM T). + * + * Params: + * T = Class type. + * A = Types of the arguments to the constructor of $(D_PARAM T). + * allocator = Allocator. + * args = Constructor arguments of $(D_PARAM T). + * + * Returns: Newly created $(D_PSYMBOL T). + * + * Precondition: $(D_INLINECODE allocator !is null) + */ +T make(T, A...)(shared Allocator allocator, auto ref A args) +if (is(T == class)) +in (allocator !is null) +{ + auto mem = (() @trusted => allocator.allocate(stateSize!T))(); + if (mem is null) + { + onOutOfMemoryError(); + } + scope (failure) + { + () @trusted { allocator.deallocate(mem); }(); + } + + return emplace!T(mem[0 .. stateSize!T], args); +} + +/** + * Constructs a value object of type $(D_PARAM T) using $(D_PARAM args) + * as the parameter list for the constructor of $(D_PARAM T) and returns a + * pointer to the new object. + * + * Params: + * T = Object type. + * A = Types of the arguments to the constructor of $(D_PARAM T). + * allocator = Allocator. + * args = Constructor arguments of $(D_PARAM T). + * + * Returns: Pointer to the created object. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ +T* make(T, A...)(shared Allocator allocator, auto ref A args) +if (!isPolymorphicType!T && !isAssociativeArray!T && !isArray!T) +in (allocator !is null) +{ + auto mem = (() @trusted => allocator.allocate(stateSize!T))(); + if (mem is null) + { + onOutOfMemoryError(); + } + scope (failure) + { + () @trusted { allocator.deallocate(mem); }(); + } + return emplace!T(mem[0 .. stateSize!T], args); +} + +/// +@nogc nothrow pure @safe unittest +{ + int* i = defaultAllocator.make!int(5); + assert(*i == 5); + defaultAllocator.dispose(i); +} + +/** + * Constructs a new array with $(D_PARAM n) elements. + * + * Params: + * T = Array type. + * E = Array element type. + * allocator = Allocator. + * n = Array size. + * + * Returns: Newly created array. + * + * Precondition: $(D_INLINECODE allocator !is null + * && n <= size_t.max / E.sizeof) + */ +T make(T : E[], E)(shared Allocator allocator, size_t n) +in (allocator !is null) +in (n <= size_t.max / E.sizeof) +{ + auto ret = allocator.resize!E(null, n); + + static if (hasElaborateDestructor!E) + { + for (auto range = ret; range.length != 0; range = range[1 .. $]) + { + emplace!E(cast(void[]) range[0 .. 1], E.init); + } + } + else + { + ret[] = E.init; + } + + return ret; +} + +/// +@nogc nothrow pure @safe unittest +{ + int[] i = defaultAllocator.make!(int[])(2); + assert(i.length == 2); + assert(i[0] == int.init && i[1] == int.init); + defaultAllocator.dispose(i); +} + +/** + * Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the + * given arguments. + * + * If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference + * of type $(D_PARAM T), otherwise a pointer to the constructed object is + * returned. + * + * If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer) + * should be an instance of the outer class. + * + * $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If + * $(D_PARAM T) isn't an aggregate type and doesn't have a constructor, + * $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`, + * `Args[0]` should be implicitly convertible to $(D_PARAM T) then. + * + * Params: + * T = Constructed type. + * U = Type of the outer class if $(D_PARAM T) is a nested class. + * Args = Types of the constructor arguments if $(D_PARAM T) has a constructor + * or the type of the initial value. + * outer = Outer class instance if $(D_PARAM T) is a nested class. + * args = Constructor arguments if $(D_PARAM T) has a constructor or the + * initial value. + * + * Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory). + * + * Precondition: `memory.length == stateSize!T`. + * Postcondition: $(D_PARAM memory) and the result point to the same memory. + */ +T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args) +if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U)) +in (memory.length >= stateSize!T) +out (result; memory.ptr is (() @trusted => cast(void*) result)()) +{ + import tanya.memory.op : copy; + + copy(typeid(T).initializer, memory); + + auto result = (() @trusted => cast(T) memory.ptr)(); + result.outer = outer; + + static if (is(typeof(result.__ctor(args)))) + { + result.__ctor(args); + } + + return result; +} + +/// ditto +T emplace(T, Args...)(void[] memory, auto ref Args args) +if (is(T == class) && !isAbstractClass!T && !isInnerClass!T) +in (memory.length == stateSize!T) +out (result; memory.ptr is (() @trusted => cast(void*) result)()) +{ + import tanya.memory.op : copy; + + copy(typeid(T).initializer, memory); + + auto result = (() @trusted => cast(T) memory.ptr)(); + static if (is(typeof(result.__ctor(args)))) + { + result.__ctor(args); + } + return result; +} + +/// +@nogc nothrow pure @safe unittest +{ + class C + { + int i = 5; + class Inner + { + int i; + + this(int param) pure nothrow @safe @nogc + { + this.i = param; + } + } + } + ubyte[stateSize!C] memory1; + ubyte[stateSize!(C.Inner)] memory2; + + auto c = emplace!C(memory1); + assert(c.i == 5); + + auto inner = emplace!(C.Inner)(memory2, c, 8); + assert(c.i == 5); + assert(inner.i == 8); + assert(inner.outer is c); +} + +/// ditto +T* emplace(T, Args...)(void[] memory, auto ref Args args) +if (!isAggregateType!T && (Args.length <= 1)) +in (memory.length >= T.sizeof) +out (result; memory.ptr is result) +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + static if (Args.length == 1) + { + *result = T(args[0]); + } + else + { + *result = T.init; + } + return result; +} + +private void initializeOne(T)(ref void[] memory, ref T* result) @trusted +{ + import tanya.memory.op : copy, fill; + + static if (!hasElaborateAssign!T && isAssignable!T) + { + *result = T.init; + } + else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available. + && __traits(isZeroInit, T)) + { + memory.ptr[0 .. T.sizeof].fill!0; + } + else + { + static immutable T init = T.init; + copy((&init)[0 .. 1], memory); + } +} + +/// ditto +T* emplace(T, Args...)(void[] memory, auto ref Args args) +if (!isPolymorphicType!T && isAggregateType!T) +in (memory.length >= T.sizeof) +out (result; memory.ptr is result) +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + + static if (Args.length == 0) + { + static assert(is(typeof({ static T t; })), + "Default constructor is disabled"); + initializeOne(memory, result); + } + else static if (is(typeof(result.__ctor(args)))) + { + initializeOne(memory, result); + result.__ctor(args); + } + else static if (Args.length == 1 && is(typeof({ T t = args[0]; }))) + { + import tanya.memory.op : copy; + + ((ref arg) @trusted => + copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]); + static if (hasElaborateCopyConstructor!T) + { + result.__postblit(); + } + } + else static if (is(typeof({ T t = T(args); }))) + { + auto init = T(args); + (() @trusted => moveEmplace(init, *result))(); + } + else + { + static assert(false, + "Unable to construct value with the given arguments"); + } + return result; +} + +/// +@nogc nothrow pure @safe unittest +{ + ubyte[4] memory; + + auto i = emplace!int(memory); + static assert(is(typeof(i) == int*)); + assert(*i == 0); + + i = emplace!int(memory, 5); + assert(*i == 5); + + static struct S + { + int i; + @disable this(); + @disable this(this); + this(int i) @nogc nothrow pure @safe + { + this.i = i; + } + } + auto s = emplace!S(memory, 8); + static assert(is(typeof(s) == S*)); + assert(s.i == 8); +} + +// Handles "Cannot access frame pointer" error. +@nogc nothrow pure @safe unittest +{ + struct F + { + ~this() @nogc nothrow pure @safe + { + } + } + static assert(is(typeof(emplace!F((void[]).init)))); +} + +// Can emplace structs without a constructor +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof(emplace!WithDtor(null, WithDtor())))); + static assert(is(typeof(emplace!WithDtor(null)))); +} + +// Doesn't call a destructor on uninitialized elements +@nogc nothrow pure @system unittest +{ + static struct SWithDtor + { + private bool canBeInvoked = false; + ~this() @nogc nothrow pure @safe + { + assert(this.canBeInvoked); + } + } + void[SWithDtor.sizeof] memory = void; + auto actual = emplace!SWithDtor(memory[], SWithDtor(true)); + assert(actual.canBeInvoked); +} + +// Initializes structs if no arguments are given +@nogc nothrow pure @safe unittest +{ + static struct SEntry + { + byte content; + } + ubyte[1] mem = [3]; + + assert(emplace!SEntry(cast(void[]) mem[0 .. 1]).content == 0); +} + +// Postblit is called when emplacing a struct +@nogc nothrow pure @system unittest +{ + static struct S + { + bool called = false; + this(this) @nogc nothrow pure @safe + { + this.called = true; + } + } + S target; + S* sp = ⌖ + + emplace!S(sp[0 .. 1], S()); + assert(target.called); +} + +private void deinitialize(bool zero, T)(ref T value) +{ + static if (is(T == U[S], U, size_t S)) + { + foreach (ref e; value) + { + deinitialize!zero(e); + } + } + else + { + import tanya.memory.op : copy, fill; + + static if (isNested!T) + { + // Don't override the context pointer. + enum size_t size = T.sizeof - (void*).sizeof; + } + else + { + enum size_t size = T.sizeof; + } + static if (zero) + { + fill!0((cast(void*) &value)[0 .. size]); + } + else + { + copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]); + } + } +} + +/** + * Moves $(D_PARAM source) into $(D_PARAM target) assuming that + * $(D_PARAM target) isn't initialized. + * + * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places + * the $(D_PARAM source) into a valid but unspecified state, which means that + * after moving $(D_PARAM source) can be destroyed or assigned a new value, but + * accessing it yields an unspecified value. No postblits or destructors are + * called. If the $(D_PARAM target) should be destroyed before, use + * $(D_PSYMBOL move). + * + * $(D_PARAM source) and $(D_PARAM target) must be different objects. + * + * Params: + * T = Object type. + * source = Source object. + * target = Target object. + * + * See_Also: $(D_PSYMBOL move), + * $(D_PSYMBOL hasElaborateCopyConstructor), + * $(D_PSYMBOL hasElaborateDestructor). + * + * Precondition: `&source !is &target`. + */ +void moveEmplace(T)(ref T source, ref T target) @system +in +{ + assert(&source !is &target, "Source and target must be different"); +} +do +{ + static if (is(T == struct) || isStaticArray!T) + { + import tanya.memory.op : copy; + + copy((&source)[0 .. 1], (&target)[0 .. 1]); + + static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) + { + static if (__VERSION__ >= 2083) // __traits(isZeroInit) available. + { + deinitialize!(__traits(isZeroInit, T))(source); + } + else + { + if (typeid(T).initializer().ptr is null) + { + deinitialize!true(source); + } + else + { + deinitialize!false(source); + } + } + } + } + else + { + target = source; + } +} + +/// +@nogc nothrow pure @system unittest +{ + static struct S + { + int member = 5; + + this(this) @nogc nothrow pure @safe + { + assert(false); + } + } + S source, target = void; + moveEmplace(source, target); + assert(target.member == 5); + + int x1 = 5, x2; + moveEmplace(x1, x2); + assert(x2 == 5); +} + +// Is pure. +@nogc nothrow pure @system unittest +{ + struct S + { + this(this) + { + } + } + S source, target = void; + static assert(is(typeof({ moveEmplace(source, target); }))); +} + +// Moves nested. +@nogc nothrow pure @system unittest +{ + struct Nested + { + void method() @nogc nothrow pure @safe + { + } + } + Nested source, target = void; + moveEmplace(source, target); + assert(source == target); +} + +// Emplaces static arrays. +@nogc nothrow pure @system unittest +{ + static struct S + { + size_t member; + this(size_t i) @nogc nothrow pure @safe + { + this.member = i; + } + ~this() @nogc nothrow pure @safe + { + } + } + S[2] source = [ S(5), S(5) ], target = void; + moveEmplace(source, target); + assert(source[0].member == 0); + assert(target[0].member == 5); + assert(source[1].member == 0); + assert(target[1].member == 5); +} + +/** + * Moves $(D_PARAM source) into $(D_PARAM target) assuming that + * $(D_PARAM target) isn't initialized. + * + * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places + * the $(D_PARAM source) into a valid but unspecified state, which means that + * after moving $(D_PARAM source) can be destroyed or assigned a new value, but + * accessing it yields an unspecified value. $(D_PARAM target) is destroyed before + * the new value is assigned. If $(D_PARAM target) isn't initialized and + * therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used. + * + * If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source + * as rvalue without calling its copy constructor or destructor. + * + * $(D_PARAM source) and $(D_PARAM target) are the same object, + * $(D_PSYMBOL move) does nothing. + * + * Params: + * T = Object type. + * source = Source object. + * target = Target object. + * + * See_Also: $(D_PSYMBOL moveEmplace). + */ +void move(T)(ref T source, ref T target) +{ + if ((() @trusted => &source is &target)()) + { + return; + } + static if (hasElaborateDestructor!T) + { + target.__xdtor(); + } + (() @trusted => moveEmplace(source, target))(); +} + +/// ditto +T move(T)(ref T source) @trusted +{ + static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) + { + T target = void; + moveEmplace(source, target); + return target; + } + else + { + return source; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + int member = 5; + + this(this) @nogc nothrow pure @safe + { + assert(false); + } + } + S source, target = void; + move(source, target); + assert(target.member == 5); + assert(move(target).member == 5); + + int x1 = 5, x2; + move(x1, x2); + assert(x2 == 5); + assert(move(x2) == 5); +} + +// Moves if source is target. +@nogc nothrow pure @safe unittest +{ + int x = 5; + move(x, x); + assert(x == 5); +} + +/** + * Exchanges the values of $(D_PARAM a) and $(D_PARAM b). + * + * $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b) + * without calling its postblits or destructors. + * + * Params: + * a = The first object. + * b = The second object. + */ +void swap(T)(ref T a, ref T b) @trusted +{ + T tmp = void; + moveEmplace(a, tmp); + moveEmplace(b, a); + moveEmplace(tmp, b); +} + +/// +@nogc nothrow pure @safe unittest +{ + int a = 3, b = 5; + swap(a, b); + assert(a == 5); + assert(b == 3); +} + +/** + * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out) + * storage classes. + * + * $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an + * argument list of the same length that can be for example passed to a + * function accepting the arguments of this type. + * + * Params: + * args = Argument list. + * + * Returns: $(D_PARAM args) with their original storage classes. + */ +template forward(args...) +{ + static if (args.length == 0) + { + alias forward = AliasSeq!(); + } + else static if (__traits(isRef, args[0]) || __traits(isOut, args[0])) + { + static if (args.length == 1) + { + alias forward = args[0]; + } + else + { + alias forward = AliasSeq!(args[0], forward!(args[1 .. $])); + } + } + else + { + @property auto forwardOne() + { + return move(args[0]); + } + static if (args.length == 1) + { + alias forward = forwardOne; + } + else + { + alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $])); + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof((int i) { int v = forward!i; }))); + static assert(is(typeof((ref int i) { int v = forward!i; }))); + static assert(is(typeof({ + void f(int i, ref int j, out int k) + { + f(forward!(i, j, k)); + } + }))); +} diff --git a/memory/tanya/memory/mallocator.d b/memory/tanya/memory/mallocator.d new file mode 100644 index 0000000..858ad40 --- /dev/null +++ b/memory/tanya/memory/mallocator.d @@ -0,0 +1,233 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and + * $(D_PSYMBOL free). + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/mallocator.d, + * tanya/memory/mallocator.d) + */ +module tanya.memory.mallocator; + +version (TanyaNative) +{ +} +else: + +import core.stdc.stdlib; +import tanya.memory.allocator; + +/** + * Wrapper for $(D_PSYMBOL malloc)/$(D_PSYMBOL realloc)/$(D_PSYMBOL free) from + * the C standard library. + */ +final class Mallocator : Allocator +{ + private alias MallocType = extern (C) void* function(size_t) + @nogc nothrow pure @system; + private alias FreeType = extern (C) void function(void*) + @nogc nothrow pure @system; + private alias ReallocType = extern (C) void* function(void*, size_t) + @nogc nothrow pure @system; + + /** + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: The pointer to the new allocated memory. + */ + void[] allocate(size_t size) @nogc nothrow pure shared @system + { + if (size == 0) + { + return null; + } + auto p = (cast(MallocType) &malloc)(size + psize); + + return p is null ? null : p[psize .. psize + size]; + } + + /// + @nogc nothrow pure @system unittest + { + auto p = Mallocator.instance.allocate(20); + assert(p.length == 20); + Mallocator.instance.deallocate(p); + + p = Mallocator.instance.allocate(0); + assert(p.length == 0); + } + + /** + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) @nogc nothrow pure shared @system + { + if (p !is null) + { + (cast(FreeType) &free)(p.ptr - psize); + } + return true; + } + + /// + @nogc nothrow pure @system unittest + { + void[] p; + assert(Mallocator.instance.deallocate(p)); + + p = Mallocator.instance.allocate(10); + assert(Mallocator.instance.deallocate(p)); + } + + /** + * Reallocating in place isn't supported. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: $(D_KEYWORD false). + */ + bool reallocateInPlace(ref void[] p, size_t size) + @nogc nothrow pure shared @system + { + cast(void) size; + return false; + } + + /// + @nogc nothrow pure @system unittest + { + void[] p; + assert(!Mallocator.instance.reallocateInPlace(p, 8)); + } + + /** + * Increases or decreases the size of a memory block. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: Whether the reallocation was successful. + */ + bool reallocate(ref void[] p, size_t size) + @nogc nothrow pure shared @system + { + if (size == 0) + { + if (deallocate(p)) + { + p = null; + return true; + } + } + else if (p is null) + { + p = allocate(size); + return p is null ? false : true; + } + else + { + auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize); + + if (r !is null) + { + p = r[psize .. psize + size]; + return true; + } + } + return false; + } + + /// + @nogc nothrow pure @system unittest + { + void[] p; + + assert(Mallocator.instance.reallocate(p, 20)); + assert(p.length == 20); + + assert(Mallocator.instance.reallocate(p, 30)); + assert(p.length == 30); + + assert(Mallocator.instance.reallocate(p, 10)); + assert(p.length == 10); + + assert(Mallocator.instance.reallocate(p, 0)); + assert(p is null); + } + + // Fails with false + @nogc nothrow pure @system unittest + { + void[] p = Mallocator.instance.allocate(20); + void[] oldP = p; + assert(!Mallocator.instance.reallocate(p, size_t.max - Mallocator.psize * 2)); + assert(oldP is p); + Mallocator.instance.deallocate(p); + } + + /** + * Returns: The alignment offered. + */ + @property uint alignment() const @nogc nothrow pure @safe shared + { + return (void*).alignof; + } + + private nothrow @nogc unittest + { + assert(Mallocator.instance.alignment == (void*).alignof); + } + + static private shared(Mallocator) instantiate() @nogc nothrow @system + { + if (instance_ is null) + { + const size = __traits(classInstanceSize, Mallocator) + psize; + void* p = malloc(size); + + if (p !is null) + { + p[psize .. size] = typeid(Mallocator).initializer[]; + instance_ = cast(shared Mallocator) p[psize .. size].ptr; + } + } + return instance_; + } + + /** + * Static allocator instance and initializer. + * + * Returns: The global $(D_PSYMBOL Allocator) instance. + */ + static @property shared(Mallocator) instance() @nogc nothrow pure @system + { + return (cast(GetPureInstance!Mallocator) &instantiate)(); + } + + /// + @nogc nothrow pure @system unittest + { + assert(instance is instance); + } + + private enum ushort psize = 8; + + private shared static Mallocator instance_; +} diff --git a/memory/tanya/memory/mmappool.d b/memory/tanya/memory/mmappool.d new file mode 100644 index 0000000..1bc401b --- /dev/null +++ b/memory/tanya/memory/mmappool.d @@ -0,0 +1,657 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Native allocator. + * + * Copyright: Eugene Wissner 2016-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/mmappool.d, + * tanya/memory/mmappool.d) + */ +module tanya.memory.mmappool; + +version (TanyaNative): + +import mir.linux._asm.unistd; +import tanya.memory.allocator; +import tanya.memory.op; +import tanya.os.error; +import tanya.sys.linux.syscall; +import tanya.sys.posix.mman; + +private void* mapMemory(const size_t length) @nogc nothrow pure @system +{ + auto p = syscall_(0, + length, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0, + NR_mmap); + return p == -ErrorCode.noMemory ? null : cast(void*) p; +} + +private bool unmapMemory(shared void* addr, const size_t length) +@nogc nothrow pure @system +{ + return syscall_(cast(ptrdiff_t) addr, length, NR_munmap) == 0; +} + +/* + * This allocator allocates memory in regions (multiple of 64 KB for example). + * Each region is then splitted in blocks. So it doesn't request the memory + * from the operating system on each call, but only if there are no large + * enough free blocks in the available regions. + * Deallocation works in the same way. Deallocation doesn't immediately + * gives the memory back to the operating system, but marks the appropriate + * block as free and only if all blocks in the region are free, the complete + * region is deallocated. + * + *
+ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * |      |     |         |     |            ||      |     |                  |
+ * |      |prev <-----------    |            ||      |     |                  |
+ * |  R   |  B  |         |  B  |            ||   R  |  B  |                  |
+ * |  E   |  L  |         |  L  |           next  E  |  L  |                  |
+ * |  G   |  O  |  DATA   |  O  |   FREE    --->  G  |  O  |       DATA       |
+ * |  I   |  C  |         |  C  |           <---  I  |  C  |                  |
+ * |  O   |  K  |         |  K  |           prev  O  |  K  |                  |
+ * |  N   |    -----------> next|            ||   N  |     |                  |
+ * |      |     |         |     |            ||      |     |                  |
+ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * 
+ */ +final class MmapPool : Allocator +{ + version (none) + { + @nogc nothrow pure @system invariant + { + for (auto r = &head; *r !is null; r = &((*r).next)) + { + auto block = cast(Block) (cast(void*) *r + RegionEntry.sizeof); + do + { + assert(block.prev is null || block.prev.next is block); + assert(block.next is null || block.next.prev is block); + assert(block.region is *r); + } + while ((block = block.next) !is null); + } + } + } + + /* + * Allocates $(D_PARAM size) bytes of memory. + * + * Params: + * size = Amount of memory to allocate. + * + * Returns: Pointer to the new allocated memory. + */ + void[] allocate(size_t size) @nogc nothrow pure shared @system + { + if (size == 0) + { + return null; + } + const dataSize = addAlignment(size); + if (dataSize < size) + { + return null; + } + + void* data = findBlock(dataSize); + if (data is null) + { + data = initializeRegion(dataSize); + } + + return data is null ? null : data[0 .. size]; + } + + @nogc nothrow pure @system unittest + { + auto p = MmapPool.instance.allocate(20); + assert(p); + MmapPool.instance.deallocate(p); + + p = MmapPool.instance.allocate(0); + assert(p.length == 0); + } + + @nogc nothrow pure @system unittest + { + // allocate() check. + size_t tooMuchMemory = size_t.max + - MmapPool.alignment_ + - BlockEntry.sizeof * 2 + - RegionEntry.sizeof + - pageSize; + assert(MmapPool.instance.allocate(tooMuchMemory) is null); + + assert(MmapPool.instance.allocate(size_t.max) is null); + + // initializeRegion() check. + tooMuchMemory = size_t.max - MmapPool.alignment_; + assert(MmapPool.instance.allocate(tooMuchMemory) is null); + } + + /* + * Search for a block large enough to keep $(D_PARAM size) and split it + * into two blocks if the block is too large. + * + * Params: + * size = Minimum size the block should have (aligned). + * + * Returns: Data the block points to or $(D_KEYWORD null). + */ + private void* findBlock(const ref size_t size) + @nogc nothrow pure shared @system + { + Block block1; + RegionLoop: for (auto r = head; r !is null; r = r.next) + { + block1 = cast(Block) (cast(void*) r + RegionEntry.sizeof); + do + { + if (block1.free && block1.size >= size) + { + break RegionLoop; + } + } + while ((block1 = block1.next) !is null); + } + if (block1 is null) + { + return null; + } + else if (block1.size >= size + alignment_ + BlockEntry.sizeof) + { // Split the block if needed + Block block2 = cast(Block) (cast(void*) block1 + BlockEntry.sizeof + size); + block2.prev = block1; + block2.next = block1.next; + block2.free = true; + block2.size = block1.size - BlockEntry.sizeof - size; + block2.region = block1.region; + + if (block1.next !is null) + { + block1.next.prev = block2; + } + block1.next = block2; + block1.size = size; + } + block1.free = false; + block1.region.blocks = block1.region.blocks + 1; + + return cast(void*) block1 + BlockEntry.sizeof; + } + + // Merge block with the next one. + private void mergeNext(Block block) const @nogc nothrow pure @safe shared + { + block.size = block.size + BlockEntry.sizeof + block.next.size; + if (block.next.next !is null) + { + block.next.next.prev = block; + } + block.next = block.next.next; + } + + /* + * Deallocates a memory block. + * + * Params: + * p = A pointer to the memory block to be freed. + * + * Returns: Whether the deallocation was successful. + */ + bool deallocate(void[] p) @nogc nothrow pure shared @system + { + if (p.ptr is null) + { + return true; + } + + Block block = cast(Block) (p.ptr - BlockEntry.sizeof); + if (block.region.blocks <= 1) + { + if (block.region.prev !is null) + { + block.region.prev.next = block.region.next; + } + else // Replace the list head. It is being deallocated + { + head = block.region.next; + } + if (block.region.next !is null) + { + block.region.next.prev = block.region.prev; + } + return unmapMemory(block.region, block.region.size); + } + // Merge blocks if neigbours are free. + if (block.next !is null && block.next.free) + { + mergeNext(block); + } + if (block.prev !is null && block.prev.free) + { + block.prev.size = block.prev.size + BlockEntry.sizeof + block.size; + if (block.next !is null) + { + block.next.prev = block.prev; + } + block.prev.next = block.next; + } + else + { + block.free = true; + } + block.region.blocks = block.region.blocks - 1; + return true; + } + + @nogc nothrow pure @system unittest + { + auto p = MmapPool.instance.allocate(20); + + assert(MmapPool.instance.deallocate(p)); + } + + /* + * Reallocates a memory block in place if possible or returns + * $(D_KEYWORD false). This function cannot be used to allocate or + * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or + * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. + */ + bool reallocateInPlace(ref void[] p, size_t size) + @nogc nothrow pure shared @system + { + if (p is null || size == 0) + { + return false; + } + if (size <= p.length) + { + // Leave the block as is. + p = p.ptr[0 .. size]; + return true; + } + Block block1 = cast(Block) (p.ptr - BlockEntry.sizeof); + + if (block1.size >= size) + { + // Enough space in the current block. + p = p.ptr[0 .. size]; + return true; + } + const dataSize = addAlignment(size); + const pAlignment = addAlignment(p.length); + assert(pAlignment >= p.length, "Invalid memory chunk length"); + const delta = dataSize - pAlignment; + + if (block1.next is null + || !block1.next.free + || dataSize < size + || block1.next.size + BlockEntry.sizeof < delta) + { + /* - It is the last block in the region + * - The next block isn't free + * - The next block is too small + * - Requested size is too large + */ + return false; + } + if (block1.next.size >= delta + alignment_) + { + // Move size from block2 to block1. + block1.next.size = block1.next.size - delta; + block1.size = block1.size + delta; + + auto block2 = cast(Block) (p.ptr + dataSize); + if (block1.next.next !is null) + { + block1.next.next.prev = block2; + } + copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof], + (cast(void*) block2)[0 .. BlockEntry.sizeof]); + block1.next = block2; + } + else + { + // The next block has enough space, but is too small for further + // allocations. Merge it with the current block. + mergeNext(block1); + } + + p = p.ptr[0 .. size]; + return true; + } + + @nogc nothrow pure @system unittest + { + void[] p; + assert(!MmapPool.instance.reallocateInPlace(p, 5)); + assert(p is null); + + p = MmapPool.instance.allocate(1); + auto orig = p.ptr; + + assert(MmapPool.instance.reallocateInPlace(p, 2)); + assert(p.length == 2); + assert(p.ptr == orig); + + assert(MmapPool.instance.reallocateInPlace(p, 4)); + assert(p.length == 4); + assert(p.ptr == orig); + + assert(MmapPool.instance.reallocateInPlace(p, 2)); + assert(p.length == 2); + assert(p.ptr == orig); + + MmapPool.instance.deallocate(p); + } + + /* + * Increases or decreases the size of a memory block. + * + * Params: + * p = A pointer to the memory block. + * size = Size of the reallocated block. + * + * Returns: Whether the reallocation was successful. + */ + bool reallocate(ref void[] p, size_t size) + @nogc nothrow pure shared @system + { + if (size == 0) + { + if (deallocate(p)) + { + p = null; + return true; + } + return false; + } + else if (reallocateInPlace(p, size)) + { + return true; + } + // Can't reallocate in place, allocate a new block, + // copy and delete the previous one. + void[] reallocP = allocate(size); + if (reallocP is null) + { + return false; + } + if (p !is null) + { + copy(p[0 .. p.length < size ? p.length : size], reallocP); + deallocate(p); + } + p = reallocP; + + return true; + } + + @nogc nothrow pure @system unittest + { + void[] p; + MmapPool.instance.reallocate(p, 10 * int.sizeof); + (cast(int[]) p)[7] = 123; + + assert(p.length == 40); + + MmapPool.instance.reallocate(p, 8 * int.sizeof); + + assert(p.length == 32); + assert((cast(int[]) p)[7] == 123); + + MmapPool.instance.reallocate(p, 20 * int.sizeof); + (cast(int[]) p)[15] = 8; + + assert(p.length == 80); + assert((cast(int[]) p)[15] == 8); + assert((cast(int[]) p)[7] == 123); + + MmapPool.instance.reallocate(p, 8 * int.sizeof); + + assert(p.length == 32); + assert((cast(int[]) p)[7] == 123); + + MmapPool.instance.deallocate(p); + } + + static private shared(MmapPool) instantiate() @nogc nothrow @system + { + if (instance_ is null) + { + const instanceSize = addAlignment(__traits(classInstanceSize, + MmapPool)); + + Region head; // Will become soon our region list head + void* data = initializeRegion(instanceSize, head); + if (data !is null) + { + copy(typeid(MmapPool).initializer, data[0 .. instanceSize]); + instance_ = cast(shared MmapPool) data; + instance_.head = head; + } + } + return instance_; + } + + /* + * Static allocator instance and initializer. + * + * Returns: Global $(D_PSYMBOL MmapPool) instance. + */ + static @property shared(MmapPool) instance() @nogc nothrow pure @system + { + return (cast(GetPureInstance!MmapPool) &instantiate)(); + } + + @nogc nothrow pure @system unittest + { + assert(instance is instance); + } + + /* + * Initializes a region for one element. + * + * Params: + * size = Aligned size of the first data block in the region. + * head = Region list head. + * + * Returns: A pointer to the data. + */ + private static void* initializeRegion(const size_t size, ref Region head) + @nogc nothrow pure @system + { + const regionSize = calculateRegionSize(size); + if (regionSize < size) + { + return null; + } + + void* p = mapMemory(regionSize); + if (p is null) + { + return null; + } + + Region region = cast(Region) p; + region.blocks = 1; + region.size = regionSize; + + // Set the pointer to the head of the region list + if (head !is null) + { + head.prev = region; + } + region.next = head; + region.prev = null; + head = region; + + // Initialize the data block + void* memoryPointer = p + RegionEntry.sizeof; + Block block1 = cast(Block) memoryPointer; + block1.size = size; + block1.free = false; + + // It is what we want to return + void* data = memoryPointer + BlockEntry.sizeof; + + // Free block after data + memoryPointer = data + size; + Block block2 = cast(Block) memoryPointer; + block1.prev = block2.next = null; + block1.next = block2; + block2.prev = block1; + block2.size = regionSize - size - RegionEntry.sizeof - BlockEntry.sizeof * 2; + block2.free = true; + block1.region = block2.region = region; + + return data; + } + + private void* initializeRegion(const size_t size) + @nogc nothrow pure shared @system + { + return initializeRegion(size, this.head); + } + + /* + * Params: + * x = Space to be aligned. + * + * Returns: Aligned size of $(D_PARAM x). + */ + private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe + { + return (x - 1) / alignment_ * alignment_ + alignment_; + } + + /* + * Params: + * x = Required space. + * + * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). + */ + private static size_t calculateRegionSize(ref const size_t x) + @nogc nothrow pure @safe + { + return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2) + / pageSize * pageSize + pageSize; + } + + /* + * Returns: Alignment offered. + */ + @property uint alignment() const @nogc nothrow pure @safe shared + { + return alignment_; + } + + @nogc nothrow pure @system unittest + { + assert(MmapPool.instance.alignment == MmapPool.alignment_); + } + + private enum uint alignment_ = 8; + + private shared static MmapPool instance_; + + // Page size. + enum size_t pageSize = 65536; + + private shared struct RegionEntry + { + Region prev; + Region next; + uint blocks; + size_t size; + } + private alias Region = shared RegionEntry*; + private shared Region head; + + private shared struct BlockEntry + { + Block prev; + Block next; + Region region; + size_t size; + bool free; + } + private alias Block = shared BlockEntry*; +} + +// A lot of allocations/deallocations, but it is the minimum caused a +// segmentation fault because MmapPool reallocateInPlace moves a block wrong. +@nogc nothrow pure @system unittest +{ + auto a = MmapPool.instance.allocate(16); + auto d = MmapPool.instance.allocate(16); + auto b = MmapPool.instance.allocate(16); + auto e = MmapPool.instance.allocate(16); + auto c = MmapPool.instance.allocate(16); + auto f = MmapPool.instance.allocate(16); + + MmapPool.instance.deallocate(a); + MmapPool.instance.deallocate(b); + MmapPool.instance.deallocate(c); + + a = MmapPool.instance.allocate(50); + MmapPool.instance.reallocateInPlace(a, 64); + MmapPool.instance.deallocate(a); + + a = MmapPool.instance.allocate(1); + auto tmp1 = MmapPool.instance.allocate(1); + auto h1 = MmapPool.instance.allocate(1); + auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1); + + auto h2 = MmapPool.instance.allocate(2); + tmp1 = MmapPool.instance.allocate(1); + MmapPool.instance.deallocate(h2); + MmapPool.instance.deallocate(h1); + + h2 = MmapPool.instance.allocate(2); + h1 = MmapPool.instance.allocate(1); + MmapPool.instance.deallocate(h2); + + auto rep = cast(void[]) tmp2; + MmapPool.instance.reallocate(rep, tmp1.length); + tmp2 = cast(ubyte[]) rep; + + MmapPool.instance.reallocate(tmp1, 9); + + rep = cast(void[]) tmp2; + MmapPool.instance.reallocate(rep, tmp1.length); + tmp2 = cast(ubyte[]) rep; + MmapPool.instance.reallocate(tmp1, 17); + + tmp2[$ - 1] = 0; + + MmapPool.instance.deallocate(tmp1); + + b = MmapPool.instance.allocate(16); + + MmapPool.instance.deallocate(h1); + MmapPool.instance.deallocate(a); + MmapPool.instance.deallocate(b); + MmapPool.instance.deallocate(d); + MmapPool.instance.deallocate(e); + MmapPool.instance.deallocate(f); +} diff --git a/memory/tanya/memory/op.d b/memory/tanya/memory/op.d new file mode 100644 index 0000000..d77f3bb --- /dev/null +++ b/memory/tanya/memory/op.d @@ -0,0 +1,437 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Set of operations on memory blocks. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/op.d, + * tanya/memory/op.d) + */ +module tanya.memory.op; + +version (TanyaNative) +{ + extern private void fillMemory(void[], size_t) pure nothrow @system @nogc; + + extern private void copyMemory(const void[], void[]) + pure nothrow @system @nogc; + + extern private void moveMemory(const void[], void[]) + pure nothrow @system @nogc; + + extern private bool equalMemory(const void[], const void[]) + pure nothrow @system @nogc; +} +else +{ + import core.stdc.string; +} + +version (TanyaNative) +{ + @nogc nothrow pure @system unittest + { + ubyte[2] buffer = 1; + fillMemory(buffer[1 .. $], 0); + assert(buffer[0] == 1 && buffer[1] == 0); + } + + @nogc nothrow pure @safe unittest + { + assert(equal(null, null)); + } +} + +private enum alignMask = size_t.sizeof - 1; + +/** + * Copies $(D_PARAM source) into $(D_PARAM target). + * + * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that + * $(D_PARAM source) points ahead of $(D_PARAM target). + * + * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) + * elements. + * + * Params: + * source = Memory to copy from. + * target = Destination memory. + * + * See_Also: $(D_PSYMBOL copyBackward). + * + * Precondition: $(D_INLINECODE source.length <= target.length). + */ +void copy(const void[] source, void[] target) @nogc nothrow pure @trusted +in +{ + assert(source.length <= target.length); + assert(source.length == 0 || source.ptr !is null); + assert(target.length == 0 || target.ptr !is null); +} +do +{ + version (TanyaNative) + { + copyMemory(source, target); + } + else + { + memcpy(target.ptr, source.ptr, source.length); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + ubyte[9] target; + source.copy(target); + assert(equal(source, target)); +} + +@nogc nothrow pure @safe unittest +{ + { + ubyte[0] source, target; + source.copy(target); + } + { + ubyte[1] source = [1]; + ubyte[1] target; + source.copy(target); + assert(target[0] == 1); + } + { + ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8]; + ubyte[8] target; + source.copy(target); + assert(equal(source, target)); + } +} + +/* + * size_t value each of which bytes is set to `Byte`. + */ +private template filledBytes(ubyte Byte, ubyte I = 0) +{ + static if (I == size_t.sizeof) + { + enum size_t filledBytes = Byte; + } + else + { + enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte; + } +} + +/** + * Fills $(D_PARAM memory) with the single byte $(D_PARAM c). + * + * Param: + * c = The value to fill $(D_PARAM memory) with. + * memory = Memory block. + */ +void fill(ubyte c = 0)(void[] memory) @trusted +in +{ + assert(memory.length == 0 || memory.ptr !is null); +} +do +{ + version (TanyaNative) + { + fillMemory(memory, filledBytes!c); + } + else + { + memset(memory.ptr, c, memory.length); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + memory.fill!0(); + foreach (ubyte v; memory) + { + assert(v == 0); + } +} + +/** + * Copies starting from the end of $(D_PARAM source) into the end of + * $(D_PARAM target). + * + * $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the + * order of elements in the $(D_PARAM target) is exactly the same as in the + * $(D_PARAM source). + * + * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that + * $(D_PARAM target) points ahead of $(D_PARAM source). + * + * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) + * elements. + * + * Params: + * source = Memory to copy from. + * target = Destination memory. + * + * See_Also: $(D_PSYMBOL copy). + * + * Precondition: $(D_INLINECODE source.length <= target.length). + */ +void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted +in +{ + assert(source.length <= target.length); + assert(source.length == 0 || source.ptr !is null); + assert(target.length == 0 || target.ptr !is null); +} +do +{ + version (TanyaNative) + { + moveMemory(source, target); + } + else + { + memmove(target.ptr, source.ptr, source.length); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ]; + ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ]; + + copyBackward(mem[0 .. 4], mem[2 .. $]); + assert(equal(expected, mem)); +} + +@nogc nothrow pure @safe unittest +{ + ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]; + ubyte[9] r2; + + copyBackward(r1, r2); + assert(equal(r1, r2)); +} + +/** + * Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if + * any. + * + * Params: + * haystack = Memory block. + * needle = A byte. + * + * Returns: The subrange of $(D_PARAM haystack) whose first element is the + * first occurrence of $(D_PARAM needle). If $(D_PARAM needle) + * couldn't be found, an empty `inout void[]` is returned. + */ +inout(void[]) find(return inout void[] haystack, ubyte needle) +@nogc nothrow pure @trusted +in +{ + assert(haystack.length == 0 || haystack.ptr !is null); +} +do +{ + auto length = haystack.length; + const size_t needleWord = size_t.max * needle; + enum size_t highBits = filledBytes!(0x01, 0); + enum size_t mask = filledBytes!(0x80, 0); + + // Align + auto bytes = cast(inout(ubyte)*) haystack; + while (length > 0 && ((cast(size_t) bytes) & 3) != 0) + { + if (*bytes == needle) + { + return bytes[0 .. length]; + } + ++bytes; + --length; + } + + // Check if some of the words has the needle + auto words = cast(inout(size_t)*) bytes; + while (length >= size_t.sizeof) + { + if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0) + { + break; + } + ++words; + length -= size_t.sizeof; + } + + // Find the exact needle position in the word + bytes = cast(inout(ubyte)*) words; + while (length > 0) + { + if (*bytes == needle) + { + return bytes[0 .. length]; + } + ++bytes; + --length; + } + + return haystack[$ .. $]; +} + +/// +@nogc nothrow pure @safe unittest +{ + const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; + + assert(equal(find(haystack, 'a'), haystack[])); + assert(equal(find(haystack, 'b'), haystack[1 .. $])); + assert(equal(find(haystack, 'c'), haystack[2 .. $])); + assert(equal(find(haystack, 'd'), haystack[3 .. $])); + assert(equal(find(haystack, 'e'), haystack[4 .. $])); + assert(equal(find(haystack, 'f'), haystack[5 .. $])); + assert(equal(find(haystack, 'h'), haystack[8 .. $])); + assert(find(haystack, 'i').length == 0); + + assert(find(null, 'a').length == 0); +} + +/** + * Looks for `\0` in the $(D_PARAM haystack) and returns the part of the + * $(D_PARAM haystack) ahead of it. + * + * Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null + * character. + * + * Params: + * haystack = Memory block. + * + * Returns: The subrange that spans all bytes before the null character or + * $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any. + */ +inout(char[]) findNullTerminated(return inout char[] haystack) +@nogc nothrow pure @trusted +in +{ + assert(haystack.length == 0 || haystack.ptr !is null); +} +do +{ + auto length = haystack.length; + enum size_t highBits = filledBytes!(0x01, 0); + enum size_t mask = filledBytes!(0x80, 0); + + // Align + auto bytes = cast(inout(ubyte)*) haystack; + while (length > 0 && ((cast(size_t) bytes) & 3) != 0) + { + if (*bytes == '\0') + { + return haystack[0 .. haystack.length - length]; + } + ++bytes; + --length; + } + + // Check if some of the words contains 0 + auto words = cast(inout(size_t)*) bytes; + while (length >= size_t.sizeof) + { + if (((*words - highBits) & (~*words) & mask) != 0) + { + break; + } + ++words; + length -= size_t.sizeof; + } + + // Find the exact 0 position in the word + bytes = cast(inout(ubyte)*) words; + while (length > 0) + { + if (*bytes == '\0') + { + return haystack[0 .. haystack.length - length]; + } + ++bytes; + --length; + } + + return null; +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(equal(findNullTerminated("abcdef\0gh"), "abcdef")); + assert(equal(findNullTerminated("\0garbage"), "")); + assert(equal(findNullTerminated("\0"), "")); + assert(equal(findNullTerminated("cstring\0"), "cstring")); + assert(findNullTerminated(null) is null); + assert(findNullTerminated("abcdef") is null); +} + +/** + * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality. + * + * Params: + * r1 = First memory block. + * r2 = Second memory block. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal, + * $(D_KEYWORD false) otherwise. + */ +bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted +in +{ + assert(r1.length == 0 || r1.ptr !is null); + assert(r2.length == 0 || r2.ptr !is null); +} +do +{ + version (TanyaNative) + { + return equalMemory(r1, r2); + } + else + { + return r1.length == r2.length + && memcmp(r1.ptr, r2.ptr, r1.length) == 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(equal("asdf", "asdf")); + assert(!equal("asd", "asdf")); + assert(!equal("asdf", "asd")); + assert(!equal("asdf", "qwer")); +} + +// Compares unanligned memory +@nogc nothrow pure @safe unittest +{ + ubyte[16] r1 = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + ]; + ubyte[16] r2 = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + ]; + + assert(equal(r1, r2)); + assert(equal(r1[1 .. $], r2[1 .. $])); + assert(equal(r1[0 .. $ - 1], r2[0 .. $ - 1])); + assert(equal(r1[0 .. 8], r2[0 .. 8])); +} diff --git a/memory/tanya/memory/package.d b/memory/tanya/memory/package.d new file mode 100644 index 0000000..98aec48 --- /dev/null +++ b/memory/tanya/memory/package.d @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Dynamic memory management. + * + * Copyright: Eugene Wissner 2016-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/package.d, + * tanya/memory/package.d) + */ +module tanya.memory; + +public import tanya.memory.allocator; +public import tanya.memory.lifecycle; +import tanya.meta.trait; +deprecated("Use tanya.meta.trait.stateSize instead") +public import tanya.meta.trait : stateSize; + +/** + * The mixin generates common methods for classes and structs using + * allocators. It provides a protected member, constructor and a read-only property, + * that checks if an allocator was already set and sets it to the default + * one, if not (useful for structs which don't have a default constructor). + */ +mixin template DefaultAllocator() +{ + /// Allocator. + protected shared Allocator allocator_; + + /** + * Params: + * allocator = The allocator should be used. + * + * Precondition: $(D_INLINECODE allocator_ !is null) + */ + this(shared Allocator allocator) @nogc nothrow pure @safe + in (allocator !is null) + { + this.allocator_ = allocator; + } + + /** + * This property checks if the allocator was set in the constructor + * and sets it to the default one, if not. + * + * Returns: Used allocator. + * + * Postcondition: $(D_INLINECODE allocator !is null) + */ + @property shared(Allocator) allocator() @nogc nothrow pure @safe + out (allocator; allocator !is null) + { + if (allocator_ is null) + { + allocator_ = defaultAllocator; + } + return allocator_; + } + + /// ditto + @property shared(Allocator) allocator() const @nogc nothrow pure @trusted + out (allocator; allocator !is null) + { + if (allocator_ is null) + { + return defaultAllocator; + } + return cast(shared Allocator) allocator_; + } +} + +shared Allocator allocator; + +private shared(Allocator) getAllocatorInstance() @nogc nothrow +{ + if (allocator is null) + { + version (TanyaNative) + { + import tanya.memory.mmappool; + defaultAllocator = MmapPool.instance; + } + else + { + import tanya.memory.mallocator; + defaultAllocator = Mallocator.instance; + } + } + return allocator; +} + +/** + * Returns: Default allocator. + * + * Postcondition: $(D_INLINECODE allocator !is null). + */ +@property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted +out (allocator; allocator !is null) +{ + return (cast(GetPureInstance!Allocator) &getAllocatorInstance)(); +} + +/** + * Sets the default allocator. + * + * Params: + * allocator = $(D_PSYMBOL Allocator) instance. + * + * Precondition: $(D_INLINECODE allocator !is null). + */ +@property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe +in (allocator !is null) +{ + .allocator = allocator; +} + +/** + * Params: + * size = Raw size. + * alignment = Alignment. + * + * Returns: Aligned size. + */ +size_t alignedSize(const size_t size, const size_t alignment = 8) +pure nothrow @safe @nogc +{ + return (size - 1) / alignment * alignment + alignment; +} diff --git a/memory/tanya/memory/smartref.d b/memory/tanya/memory/smartref.d new file mode 100644 index 0000000..1fe81f3 --- /dev/null +++ b/memory/tanya/memory/smartref.d @@ -0,0 +1,903 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Smart pointers. + * + * A smart pointer is an object that wraps a raw pointer or a reference + * (class, dynamic array) to manage its lifetime. + * + * This module provides two kinds of lifetime management strategies: + * $(UL + * $(LI Reference counting) + * $(LI Unique ownership) + * ) + * + * Copyright: Eugene Wissner 2016-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/smartref.d, + * tanya/memory/smartref.d) + */ +module tanya.memory.smartref; + +import tanya.memory; +import tanya.meta.trait; +version (unittest) import tanya.test.stub; + +private template Payload(T) +{ + static if (isPolymorphicType!T || isDynamicArray!T) + { + alias Payload = T; + } + else + { + alias Payload = T*; + } +} + +private final class RefCountedStore(T) +{ + T payload; + size_t counter = 1; + + size_t opUnary(string op)() + if (op == "--" || op == "++") + in + { + assert(this.counter > 0); + } + do + { + mixin("return " ~ op ~ "counter;"); + } + + int opCmp(const size_t counter) + { + if (this.counter > counter) + { + return 1; + } + else if (this.counter < counter) + { + return -1; + } + else + { + return 0; + } + } +} + +private void separateDeleter(T)(RefCountedStore!T storage, + shared Allocator allocator) +{ + allocator.dispose(storage.payload); + allocator.dispose(storage); +} + +private void unifiedDeleter(T)(RefCountedStore!T storage, + shared Allocator allocator) +{ + auto ptr1 = finalize(storage); + auto ptr2 = finalize(storage.payload); + allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]); +} + +/** + * Reference-counted object containing a $(D_PARAM T) value as payload. + * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and + * when the reference count goes down to zero, frees the underlying store. + * + * Params: + * T = Type of the reference-counted value. + */ +struct RefCounted(T) +{ + private alias Storage = RefCountedStore!(Payload!T); + + private Storage storage; + private void function(Storage storage, + shared Allocator allocator) @nogc deleter; + + invariant + { + assert(this.storage is null || this.allocator_ !is null); + assert(this.storage is null || this.deleter !is null); + } + + /** + * Takes ownership over $(D_PARAM value), setting the counter to 1. + * $(D_PARAM value) may be a pointer, an object or a dynamic array. + * + * Params: + * value = Value whose ownership is taken over. + * allocator = Allocator used to destroy the $(D_PARAM value) and to + * allocate/deallocate internal storage. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(Payload!T value, shared Allocator allocator = defaultAllocator) + { + this(allocator); + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!(Payload!T); + + this.storage.payload = value; + } + + /// ditto + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + do + { + this.allocator_ = allocator; + } + + /** + * Increases the reference counter by one. + */ + this(this) + { + if (count != 0) + { + ++this.storage; + } + } + + /** + * Decreases the reference counter by one. + * + * If the counter reaches 0, destroys the owned object. + */ + ~this() + { + if (this.storage !is null && !(this.storage > 0 && --this.storage)) + { + deleter(this.storage, allocator); + } + } + + /** + * Takes ownership over $(D_PARAM rhs). Initializes this + * $(D_PSYMBOL RefCounted) if needed. + * + * If it is the last reference of the previously owned object, + * it will be destroyed. + * + * To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). + * + * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will + * be used. If you need a different allocator, create a new + * $(D_PSYMBOL RefCounted) and assign it. + * + * Params: + * rhs = New object. + * + * Returns: $(D_KEYWORD this). + */ + ref typeof(this) opAssign(Payload!T rhs) + { + if (this.storage is null) + { + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!(Payload!T); + } + else if (this.storage > 1) + { + --this.storage; + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!(Payload!T); + } + else + { + finalize(this.storage.payload); + this.storage.payload = Payload!T.init; + } + this.storage.payload = rhs; + return this; + } + + /// ditto + ref typeof(this) opAssign(typeof(null)) + { + if (this.storage is null) + { + return this; + } + else if (this.storage > 1) + { + --this.storage; + } + else + { + deleter(this.storage, allocator); + } + this.storage = null; + + return this; + } + + /// ditto + ref typeof(this) opAssign(typeof(this) rhs) + { + swap(this.allocator_, rhs.allocator_); + swap(this.storage, rhs.storage); + swap(this.deleter, rhs.deleter); + return this; + } + + /** + * Returns: Reference to the owned object. + * + * Precondition: $(D_INLINECODE cound > 0). + */ + inout(Payload!T) get() inout + in + { + assert(count > 0, "Attempted to access an uninitialized reference"); + } + do + { + return this.storage.payload; + } + + version (D_Ddoc) + { + /** + * Dereferences the pointer. It is defined only for pointers, not for + * reference types like classes, that can be accessed directly. + * + * Params: + * op = Operation. + * + * Returns: Reference to the pointed value. + */ + ref inout(T) opUnary(string op)() inout + if (op == "*"); + } + else static if (isPointer!(Payload!T)) + { + ref inout(T) opUnary(string op)() inout + if (op == "*") + { + return *this.storage.payload; + } + } + + /** + * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal + * storage. + */ + @property bool isInitialized() const + { + return this.storage !is null; + } + + /** + * Returns: The number of $(D_PSYMBOL RefCounted) instances that share + * ownership over the same pointer (including $(D_KEYWORD this)). + * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`. + */ + @property size_t count() const + { + return this.storage is null ? 0 : this.storage.counter; + } + + mixin DefaultAllocator; + alias get this; +} + +/// +@nogc @system unittest +{ + auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); + auto val = rc.get(); + + *val = 8; + assert(*rc.storage.payload == 8); + + val = null; + assert(rc.storage.payload !is null); + assert(*rc.storage.payload == 8); + + *rc = 9; + assert(*rc.storage.payload == 9); +} + +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!int(5); + rc = defaultAllocator.make!int(7); + assert(*rc == 7); +} + +@nogc @system unittest +{ + RefCounted!int rc; + assert(!rc.isInitialized); + rc = null; + assert(!rc.isInitialized); +} + +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!int(5); + + void func(RefCounted!int param) @nogc + { + assert(param.count == 2); + param = defaultAllocator.make!int(7); + assert(param.count == 1); + assert(*param == 7); + } + func(rc); + assert(rc.count == 1); + assert(*rc == 5); +} + +@nogc @system unittest +{ + RefCounted!int rc; + + void func(RefCounted!int param) @nogc + { + assert(param.count == 0); + param = defaultAllocator.make!int(7); + assert(param.count == 1); + assert(*param == 7); + } + func(rc); + assert(rc.count == 0); +} + +@nogc @system unittest +{ + RefCounted!int rc1, rc2; + static assert(is(typeof(rc1 = rc2))); +} + +version (unittest) +{ + private class A + { + uint *destroyed; + + this(ref uint destroyed) @nogc + { + this.destroyed = &destroyed; + } + + ~this() @nogc + { + ++(*destroyed); + } + } + + private struct B + { + int prop; + @disable this(); + this(int param1) @nogc + { + prop = param1; + } + } +} + +@nogc @system unittest +{ + uint destroyed; + auto a = defaultAllocator.make!A(destroyed); + + assert(destroyed == 0); + { + auto rc = RefCounted!A(a, defaultAllocator); + assert(rc.count == 1); + + void func(RefCounted!A rc) @nogc @system + { + assert(rc.count == 2); + } + func(rc); + + assert(rc.count == 1); + } + assert(destroyed == 1); + + RefCounted!int rc; + assert(rc.count == 0); + rc = defaultAllocator.make!int(8); + assert(rc.count == 1); +} + +@nogc @system unittest +{ + auto rc = RefCounted!int(defaultAllocator); + assert(!rc.isInitialized); + assert(rc.allocator is defaultAllocator); +} + +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!int(5); + assert(rc.count == 1); + + void func(RefCounted!int rc) @nogc + { + assert(rc.count == 2); + rc = null; + assert(!rc.isInitialized); + assert(rc.count == 0); + } + + assert(rc.count == 1); + func(rc); + assert(rc.count == 1); + + rc = null; + assert(!rc.isInitialized); + assert(rc.count == 0); +} + +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!int(5); + assert(*rc == 5); + + void func(RefCounted!int rc) @nogc + { + assert(rc.count == 2); + rc = defaultAllocator.refCounted!int(4); + assert(*rc == 4); + assert(rc.count == 1); + } + func(rc); + assert(*rc == 5); +} + +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof(RefCounted!int.storage.payload) == int*)); + static assert(is(typeof(RefCounted!A.storage.payload) == A)); + + static assert(is(RefCounted!B)); + static assert(is(RefCounted!A)); +} + +/** + * Constructs a new object of type $(D_PARAM T) and wraps it in a + * $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for + * the constructor of $(D_PARAM T). + * + * This function is more efficient than the using of $(D_PSYMBOL RefCounted) + * directly, since it allocates only ones (the internal storage and the + * object). + * + * Params: + * T = Type of the constructed object. + * A = Types of the arguments to the constructor of $(D_PARAM T). + * allocator = Allocator. + * args = Constructor arguments of $(D_PARAM T). + * + * Returns: Newly created $(D_PSYMBOL RefCounted!T). + * + * Precondition: $(D_INLINECODE allocator !is null) + */ +RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) +if (!is(T == interface) && !isAbstractClass!T + && !isAssociativeArray!T && !isArray!T) +in +{ + assert(allocator !is null); +} +do +{ + auto rc = typeof(return)(allocator); + + const storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); + const size = alignedSize(stateSize!T + storageSize); + + auto mem = (() @trusted => allocator.allocate(size))(); + if (mem is null) + { + onOutOfMemoryError(); + } + scope (failure) + { + () @trusted { allocator.deallocate(mem); }(); + } + rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); + rc.storage.payload = emplace!T(mem[storageSize .. $], args); + + rc.deleter = &unifiedDeleter!(Payload!T); + return rc; +} + +/** + * Constructs a new array with $(D_PARAM size) elements and wraps it in a + * $(D_PSYMBOL RefCounted). + * + * Params: + * T = Array type. + * E = Array element type. + * size = Array size. + * allocator = Allocator. + * + * Returns: Newly created $(D_PSYMBOL RefCounted!T). + * + * Precondition: $(D_INLINECODE allocator !is null + * && size <= size_t.max / E.sizeof) + */ +RefCounted!T refCounted(T : E[], E)(shared Allocator allocator, size_t size) +@trusted +in (allocator !is null) +in (size <= size_t.max / E.sizeof) +{ + return RefCounted!T(allocator.make!T(size), allocator); +} + +/// +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!int(5); + assert(rc.count == 1); + + void func(RefCounted!int param) @nogc + { + if (param.count == 2) + { + func(param); + } + else + { + assert(param.count == 3); + } + } + func(rc); + + assert(rc.count == 1); +} + +@nogc @system unittest +{ + struct E + { + } + auto b = defaultAllocator.refCounted!B(15); + static assert(is(typeof(b.storage.payload) == B*)); + static assert(is(typeof(b.prop) == int)); + static assert(!is(typeof(defaultAllocator.refCounted!B()))); + + static assert(is(typeof(defaultAllocator.refCounted!E()))); + static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); + { + auto rc = defaultAllocator.refCounted!B(3); + assert(rc.get().prop == 3); + } + { + auto rc = defaultAllocator.refCounted!E(); + assert(rc.count); + } +} + +@nogc @system unittest +{ + auto rc = defaultAllocator.refCounted!(int[])(5); + assert(rc.length == 5); +} + +@nogc @system unittest +{ + auto p1 = defaultAllocator.make!int(5); + auto p2 = p1; + auto rc = RefCounted!int(p1, defaultAllocator); + assert(rc.get() is p2); +} + +@nogc @system unittest +{ + size_t destroyed; + { + auto rc = defaultAllocator.refCounted!WithDtor(destroyed); + } + assert(destroyed == 1); +} + +/** + * $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope. + * + * Params: + * T = Value type. + */ +struct Unique(T) +{ + private Payload!T payload; + + invariant + { + assert(payload is null || allocator_ !is null); + } + + /** + * Takes ownership over $(D_PARAM value), setting the counter to 1. + * $(D_PARAM value) may be a pointer, an object or a dynamic array. + * + * Params: + * value = Value whose ownership is taken over. + * allocator = Allocator used to destroy the $(D_PARAM value) and to + * allocate/deallocate internal storage. + * + * Precondition: $(D_INLINECODE allocator !is null) + */ + this(Payload!T value, shared Allocator allocator = defaultAllocator) + { + this(allocator); + this.payload = value; + } + + /// ditto + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + do + { + this.allocator_ = allocator; + } + + /** + * $(D_PSYMBOL Unique) is noncopyable. + */ + @disable this(this); + + /** + * Destroys the owned object. + */ + ~this() + { + allocator.dispose(this.payload); + } + + /** + * Initialized this $(D_PARAM Unique) and takes ownership over + * $(D_PARAM rhs). + * + * To reset $(D_PSYMBOL Unique) assign $(D_KEYWORD null). + * + * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will + * be used. If you need a different allocator, create a new + * $(D_PSYMBOL Unique) and assign it. + * + * Params: + * rhs = New object. + * + * Returns: $(D_KEYWORD this). + */ + ref typeof(this) opAssign(Payload!T rhs) + { + allocator.dispose(this.payload); + this.payload = rhs; + return this; + } + + /// ditto + ref typeof(this) opAssign(typeof(null)) + { + allocator.dispose(this.payload); + return this; + } + + /// ditto + ref typeof(this) opAssign(typeof(this) rhs) + { + swap(this.allocator_, rhs.allocator_); + swap(this.payload, rhs.payload); + + return this; + } + + /// + @nogc nothrow pure @system unittest + { + auto rc = defaultAllocator.unique!int(5); + rc = defaultAllocator.make!int(7); + assert(*rc == 7); + } + + /** + * Returns: Reference to the owned object. + */ + inout(Payload!T) get() inout + { + return this.payload; + } + + version (D_Ddoc) + { + /** + * Dereferences the pointer. It is defined only for pointers, not for + * reference types like classes, that can be accessed directly. + * + * Params: + * op = Operation. + * + * Returns: Reference to the pointed value. + */ + ref inout(T) opUnary(string op)() inout + if (op == "*"); + } + else static if (isPointer!(Payload!T)) + { + ref inout(T) opUnary(string op)() inout + if (op == "*") + { + return *this.payload; + } + } + + /** + * Returns: Whether this $(D_PSYMBOL Unique) holds some value. + */ + @property bool isInitialized() const + { + return this.payload !is null; + } + + /// + @nogc nothrow pure @system unittest + { + Unique!int u; + assert(!u.isInitialized); + } + + /** + * Sets the internal pointer to $(D_KEYWORD). The allocator isn't changed. + * + * Returns: Reference to the owned object. + */ + Payload!T release() + { + auto payload = this.payload; + this.payload = null; + return payload; + } + + /// + @nogc nothrow pure @system unittest + { + auto u = defaultAllocator.unique!int(5); + assert(u.isInitialized); + + auto i = u.release(); + assert(*i == 5); + assert(!u.isInitialized); + } + + mixin DefaultAllocator; + alias get this; +} + +/// +@nogc nothrow pure @system unittest +{ + auto p = defaultAllocator.make!int(5); + auto s = Unique!int(p, defaultAllocator); + assert(*s == 5); +} + +/// +@nogc nothrow @system unittest +{ + static bool destroyed; + + static struct F + { + ~this() @nogc nothrow @safe + { + destroyed = true; + } + } + { + auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator); + } + assert(destroyed); +} + +/** + * Constructs a new object of type $(D_PARAM T) and wraps it in a + * $(D_PSYMBOL Unique) using $(D_PARAM args) as the parameter list for + * the constructor of $(D_PARAM T). + * + * Params: + * T = Type of the constructed object. + * A = Types of the arguments to the constructor of $(D_PARAM T). + * allocator = Allocator. + * args = Constructor arguments of $(D_PARAM T). + * + * Returns: Newly created $(D_PSYMBOL Unique!T). + * + * Precondition: $(D_INLINECODE allocator !is null) + */ +Unique!T unique(T, A...)(shared Allocator allocator, auto ref A args) +if (!is(T == interface) && !isAbstractClass!T + && !isAssociativeArray!T && !isArray!T) +in +{ + assert(allocator !is null); +} +do +{ + auto payload = allocator.make!(T, A)(args); + return Unique!T(payload, allocator); +} + +/** + * Constructs a new array with $(D_PARAM size) elements and wraps it in a + * $(D_PSYMBOL Unique). + * + * Params: + * T = Array type. + * E = Array element type. + * size = Array size. + * allocator = Allocator. + * + * Returns: Newly created $(D_PSYMBOL Unique!T). + * + * Precondition: $(D_INLINECODE allocator !is null + * && size <= size_t.max / E.sizeof) + */ +Unique!T unique(T : E[], E)(shared Allocator allocator, size_t size) +@trusted +in (allocator !is null) +in (size <= size_t.max / E.sizeof) +{ + auto payload = allocator.resize!E(null, size); + return Unique!T(payload, allocator); +} + +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof(defaultAllocator.unique!B(5)))); + static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); +} + +@nogc nothrow pure @system unittest +{ + auto s = defaultAllocator.unique!int(5); + assert(*s == 5); + + s = null; + assert(s is null); +} + +@nogc nothrow pure @system unittest +{ + auto s = defaultAllocator.unique!int(5); + assert(*s == 5); + + s = defaultAllocator.unique!int(4); + assert(*s == 4); +} + +@nogc nothrow pure @system unittest +{ + auto p1 = defaultAllocator.make!int(5); + auto p2 = p1; + + auto rc = Unique!int(p1, defaultAllocator); + assert(rc.get() is p2); +} + +@nogc nothrow pure @system unittest +{ + auto rc = Unique!int(defaultAllocator); + assert(rc.allocator is defaultAllocator); +} diff --git a/memory/tanya/test/assertion.d b/memory/tanya/test/assertion.d new file mode 100644 index 0000000..10105d7 --- /dev/null +++ b/memory/tanya/test/assertion.d @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Additional assertions. + * + * This module provides functions that assert whether a given expression + * satisfies some complex condition, that can't be tested with + * $(D_KEYWORD assert) in a single line. Internally all the functions + * just evaluate the expression and call $(D_KEYWORD assert). + * + * The functions can cause segmentation fault if the module is compiled + * in production mode and the condition fails. + * + * Copyright: Eugene Wissner 2017-2018. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/assertion.d, + * tanya/test/assertion.d) + */ +module tanya.test.assertion; + +import tanya.memory; +import tanya.meta.trait; + +/** + * Asserts whether the function $(D_PARAM expr) throws an exception of type + * $(D_PARAM E). If it does, the exception is catched and properly destroyed. + * If it doesn't, an assertion error is thrown. If the exception doesn't match + * $(D_PARAM E) type, it isn't catched and escapes. + * + * Params: + * E = Expected exception type. + * T = Throwing function type. + * Args = Argument types of the throwing function. + * expr = Throwing function. + * args = Arguments for $(D_PARAM expr). + */ +void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args) +if (isSomeFunction!T) +{ + try + { + cast(void) expr(args); + assert(false, "Expected exception not thrown"); + } + catch (E exception) + { + defaultAllocator.dispose(exception); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // If you want to test that an expression throws, you can wrap it into an + // arrow function. + static struct CtorThrows + { + this(int i) @nogc pure @safe + { + throw defaultAllocator.make!Exception(); + } + } + assertThrown!Exception(() => CtorThrows(8)); +} + +/** + * Asserts that the function $(D_PARAM expr) doesn't throw. + * + * If it does, the thrown exception is catched, properly destroyed and an + * assertion error is thrown instead. + * + * Params: + * T = Tested function type. + * Args = Argument types of $(D_PARAM expr). + * expr = Tested function. + * args = Arguments for $(D_PARAM expr). + */ +void assertNotThrown(T, Args...)(T expr, auto ref Args args) +if (isSomeFunction!T) +{ + try + { + cast(void) expr(args); + } + catch (Exception exception) + { + defaultAllocator.dispose(exception); + assert(false, "Unexpected exception thrown"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // If you want to test that an expression doesn't throw, you can wrap it + // into an arrow function. + static struct S + { + } + assertNotThrown(() => S()); +} diff --git a/memory/tanya/test/package.d b/memory/tanya/test/package.d new file mode 100644 index 0000000..ab6f861 --- /dev/null +++ b/memory/tanya/test/package.d @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test suite for $(D_KEYWORD unittest)-blocks. + * + * Copyright: Eugene Wissner 2017-2018. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/package.d, + * tanya/test/package.d) + */ +module tanya.test; + +public import tanya.test.assertion; +public import tanya.test.stub; diff --git a/memory/tanya/test/stub.d b/memory/tanya/test/stub.d new file mode 100644 index 0000000..e1f8dcb --- /dev/null +++ b/memory/tanya/test/stub.d @@ -0,0 +1,373 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Range and generic type generators. + * + * Copyright: Eugene Wissner 2018. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/stub.d, + * tanya/test/stub.d) + */ +module tanya.test.stub; + +/** + * Attribute signalizing that the generated range should contain the given + * number of elements. + * + * $(D_PSYMBOL Count) should be always specified with some value and not as a + * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), + * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. + * + * $(D_PSYMBOL Count) doesn't generate `.length` property - use + * $(D_PSYMBOL Length) for that. + * + * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, + * $(D_ILNINECODE Count(0)) is assumed. + * + * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). + */ +struct Count +{ + /// Original range length. + size_t count = 0; + + @disable this(); + + /** + * Constructs the attribute with the given length. + * + * Params: + * count = Original range length. + */ + this(size_t count) @nogc nothrow pure @safe + { + this.count = count; + } +} + +/** + * Attribute signalizing that the generated range should be infinite. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). + */ +struct Infinite +{ +} + +/** + * Generates `.length` property for the range. + * + * The length of the range can be specified as a constructor argument, + * otherwise it is 0. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). + */ +struct Length +{ + /// Original range length. + size_t length = 0; +} + +/** + * Attribute signalizing that the generated range should return values by + * reference. + * + * This atribute affects the return values of `.front`, `.back` and `[]`. + */ +struct WithLvalueElements +{ +} + +/** + * Generates an input range. + * + * Params: + * E = Element type. + */ +mixin template InputRangeStub(E = int) +{ + import tanya.meta.metafunction : Alias; + import tanya.meta.trait : evalUDA, getUDAs, hasUDA; + + /* + * Aliases for the attribute lookups to access them faster + */ + private enum bool infinite = hasUDA!(typeof(this), Infinite); + private enum bool withLvalueElements = hasUDA!(typeof(this), + WithLvalueElements); + private alias Count = getUDAs!(typeof(this), .Count); + private alias Length = getUDAs!(typeof(this), .Length); + + static if (Count.length != 0) + { + private enum size_t count = Count[0].count; + + static assert (!infinite, + "Range cannot have count and be infinite at the same time"); + static assert (Length.length == 0, + "Range cannot have count and length at the same time"); + } + else static if (Length.length != 0) + { + private enum size_t count = evalUDA!(Length[0]).length; + + static assert (!infinite, + "Range cannot have length and be infinite at the same time"); + } + else static if (!infinite) + { + private enum size_t count = 0; + } + + /* + * Member generation + */ + static if (infinite) + { + enum bool empty = false; + } + else + { + private size_t length_ = count; + + @property bool empty() const @nogc nothrow pure @safe + { + return this.length_ == 0; + } + } + + static if (withLvalueElements) + { + private E* element; // Pointer to enable range copying in save() + } + + void popFront() @nogc nothrow pure @safe + in (!empty) + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E front() @nogc nothrow pure @safe + in (!empty) + { + return *this.element; + } + } + else + { + E front() @nogc nothrow pure @safe + in (!empty) + { + return E.init; + } + } + + static if (Length.length != 0) + { + size_t length() const @nogc nothrow pure @safe + { + return this.length_; + } + } +} + +/** + * Generates a forward range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template ForwardRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + auto save() @nogc nothrow pure @safe + { + return this; + } +} + +/** + * Generates a bidirectional range. + * + * This mixin includes forward range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template BidirectionalRangeStub(E = int) +{ + mixin ForwardRangeStub!E; + + void popBack() @nogc nothrow pure @safe + in (!empty) + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E back() @nogc nothrow pure @safe + in (!empty) + { + return *this.element; + } + } + else + { + E back() @nogc nothrow pure @safe + in (!empty) + { + return E.init; + } + } +} + +/** + * Generates a random-access range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). + * + * Note that a random-access range also requires $(D_PSYMBOL Length) or + * $(D_PARAM Infinite) by definition. + * + * Params: + * E = Element type. + */ +mixin template RandomAccessRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + static if (withLvalueElements) + { + ref E opIndex(size_t) @nogc nothrow pure @safe + { + return *this.element; + } + } + else + { + E opIndex(size_t) @nogc nothrow pure @safe + { + return E.init; + } + } +} + +/** + * Struct with a disabled postblit constructor. + * + * $(D_PSYMBOL NonCopyable) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + */ +struct NonCopyable +{ + @disable this(this); +} + +/** + * Struct with an elaborate destructor. + * + * $(D_PSYMBOL WithDtor) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + * + * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor) + * accepts an additional `counter` argument, which is incremented by the + * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable, + * so the variable can be investigated after the struct isn't available + * anymore. + */ +struct WithDtor +{ + size_t* counter; + + this(ref size_t counter) @nogc nothrow pure @trusted + { + this.counter = &counter; + } + + ~this() @nogc nothrow pure @safe + { + if (this.counter !is null) + { + ++*this.counter; + } + } +} + +/** + * Struct supporting hashing. + * + * $(D_PSYMBOL Hashable) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + * + * The constructor accepts an additional parameter, which is returned by the + * `toHash()`-function. `0U` is returned if no hash value is given. + */ +struct Hashable +{ + size_t hash; + + size_t toHash() const @nogc nothrow pure @safe + { + return this.hash; + } +} + +/** + * Generates a $(D_KEYWORD struct) with common functionality. + * + * To specify the needed functionality use user-defined attributes on the + * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in. + * + * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable), + * $(D_PSYMBOL WithDtor). + */ +mixin template StructStub() +{ + import tanya.meta.trait : evalUDA, getUDAs, hasUDA; + + static if (hasUDA!(typeof(this), NonCopyable)) + { + @disable this(this); + } + + private alias Hashable = getUDAs!(typeof(this), .Hashable); + static if (Hashable.length > 0) + { + size_t toHash() const @nogc nothrow pure @safe + { + return evalUDA!(Hashable[0]).hash; + } + } + + static if (hasUDA!(typeof(this), WithDtor)) + { + ~this() @nogc nothrow pure @safe + { + } + } +} diff --git a/meta/dub.json b/meta/dub.json index c8d2ba7..663d2fd 100644 --- a/meta/dub.json +++ b/meta/dub.json @@ -1,5 +1,12 @@ { "name": "meta", "description": "Template metaprogramming", - "targetType": "library" + "targetType": "library", + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/meta/source/tanya/meta/metafunction.d b/meta/source/tanya/meta/metafunction.d deleted file mode 100644 index 6da6b47..0000000 --- a/meta/source/tanya/meta/metafunction.d +++ /dev/null @@ -1,1862 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module is suited for computations on template arguments, both types and - * values at compile time. - * - * It contains different algorithms for iterating, searching and modifying - * template arguments. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/metafunction.d, - * tanya/meta/metafunction.d) - */ -module tanya.meta.metafunction; - -import tanya.meta.trait; -import tanya.meta.transform; - -/** - * Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred). - * - * $(D_PARAM Args) should contain at least one element. - * - * $(D_PARAM pred) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * pred = Template predicate. - * Args = Elements for which you want to find the minimum value. - * - * Returns: The minimum. - * - * See_Also: $(D_PSYMBOL isLess). - */ -template Min(alias pred, Args...) -if (Args.length > 0 && __traits(isTemplate, pred)) -{ - static if (Args.length == 1) - { - alias Min = Alias!(Args[0]); - } - else static if (isLess!(pred, Args[1], Args[0])) - { - alias Min = Min!(pred, Args[1], Args[2 .. $]); - } - else - { - alias Min = Min!(pred, Args[0], Args[2 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool cmp(alias T, alias U) = T < U; - static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3); - static assert(Min!(cmp, 8) == 8); -} - -/** - * Finds the maximum value in $(D_PARAM Args) according to $(D_PARAM pred). - * - * $(D_PARAM Args) should contain at least one element. - * - * $(D_PARAM pred) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * pred = Template predicate. - * Args = Elements for which you want to find the maximum value. - * - * Returns: The maximum. - * - * See_Also: $(D_PSYMBOL isLess). - */ -template Max(alias pred, Args...) -if (Args.length > 0 && __traits(isTemplate, pred)) -{ - static if (Args.length == 1) - { - alias Max = Alias!(Args[0]); - } - else static if (isGreater!(pred, Args[1], Args[0])) - { - alias Max = Max!(pred, Args[1], Args[2 .. $]); - } - else - { - alias Max = Max!(pred, Args[0], Args[2 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool cmp(alias T, alias U) = T < U; - static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13); - static assert(Max!(cmp, 8) == 8); -} - -/** - * Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f). - * - * Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the - * $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith) - * produces a sequence: - * - * --- - * f(t1[0], t2[0], ... tk[0]), - * f(t1[1], t2[1], ... tk[1]), - * ... - * f(tk[0], tk[1], ... tk[i]), - * --- - * - * $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs) - * and applies $(D_PARAM f) to them, then it takes the second - * ones and does the same, and so on. - * - * If not all argument tuples have the same length, $(D_PSYMBOL ZipWith) will - * zip only `n` elements from each tuple, where `n` is the length of the - * shortest tuple in the argument list. Remaining elements in the longer tuples - * are just ignored. - * - * Params: - * f = Some template that can be applied to the elements of - * $(D_PARAM Packs). - * Packs = $(D_PSYMBOL Pack) instances. - * - * Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element - * from each of the $(D_PARAM Packs). - */ -template ZipWith(alias f, Packs...) -if (Packs.length > 0 - && __traits(isTemplate, f) - && (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs) - || allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs))) -{ - private template GetIth(size_t i, Args...) - { - static if ((Args.length == 0) || (Args[0].Seq.length <= i)) - { - alias GetIth = AliasSeq!(); - } - else - { - alias GetIth = AliasSeq!(Args[0].Seq[i], GetIth!(i, Args[1 .. $])); - } - } - private template Iterate(size_t i, Args...) - { - alias Pack = GetIth!(i, Args); - - static if (Pack.length < Packs.length) - { - alias Iterate = AliasSeq!(); - } - else - { - alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args)); - } - } - alias ZipWith = Iterate!(0, Packs); -} - -/// -@nogc nothrow pure @safe unittest -{ - alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10)); - static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10)); - - alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5)); - static assert(Result2 == AliasSeq!(1, 4, 2, 5)); - - alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5)); - static assert(Result3.length == 0); -} - -/** - * Holds a typed sequence of template parameters. - * - * Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack - * its template parameters automatically. Consider: - * - * --- - * template A(Args...) - * { - * static assert(Args.length == 4); - * } - * - * alias AInstance = A!(AliasSeq!(int, uint), AliasSeq!(float, double)); - * --- - * - * Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2, - * because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters. - * - * With $(D_PSYMBOL Pack) it is possible to pass distinguishable - * sequences of parameters to a template. So: - * - * --- - * template B(Args...) - * { - * static assert(Args.length == 2); - * } - * - * alias BInstance = B!(Pack!(int, uint), Pack!(float, double)); - * --- - * - * Params: - * Args = Elements of this $(D_PSYMBOL Pack). - * - * See_Also: $(D_PSYMBOL AliasSeq). - */ -struct Pack(Args...) -{ - /// Elements in this tuple as $(D_PSYMBOL AliasSeq). - alias Seq = Args; - - /// The length of the tuple. - enum size_t length = Args.length; - - alias Seq this; -} - -/// -@nogc nothrow pure @safe unittest -{ - alias A = Pack!short; - alias B = Pack!(3, 8, 9); - alias C = Pack!(A, B); - - static assert(C.length == 2); - - static assert(A.length == 1); - static assert(is(A.Seq == AliasSeq!short)); - static assert(B.length == 3); - static assert(B.Seq == AliasSeq!(3, 8, 9)); - - alias D = Pack!(); - static assert(D.length == 0); - static assert(is(D.Seq == AliasSeq!())); -} - -/** - * Unordered sequence of unique aliases. - * - * $(D_PARAM Args) can contain duplicates, but they will be filtered out, so - * $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used - * for determining if two items are equal. - * - * Params: - * Args = Elements of this $(D_PSYMBOL Set). - */ -struct Set(Args...) -{ - /// Elements in this set as $(D_PSYMBOL AliasSeq). - alias Seq = NoDuplicates!Args; - - /// The length of the set. - enum size_t length = Seq.length; - - alias Seq this; -} - -/// -@nogc nothrow pure @safe unittest -{ - alias S1 = Set!(int, 5, 5, int, 4); - static assert(S1.length == 3); -} - -/** - * Produces a $(D_PSYMBOL Set) containing all elements of the given - * $(D_PARAM Sets). - * - * Params: - * Sets = List of $(D_PSYMBOL Set) instances. - * - * Returns: Set-theoretic union of all $(D_PARAM Sets). - * - * See_Also: $(D_PSYMBOL Set). - */ -template Union(Sets...) -if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets)) -{ - private template Impl(Sets...) - { - static if (Sets.length == 0) - { - alias Impl = AliasSeq!(); - } - else - { - alias Impl = AliasSeq!(Sets[0].Seq, Impl!(Sets[1 .. $])); - } - } - alias Union = Set!(Impl!Sets); -} - -/// -@nogc nothrow pure @safe unittest -{ - alias S1 = Set!(2, 5, 8, 4); - alias S2 = Set!(3, 8, 4, 1); - static assert(Union!(S1, S2).Seq == AliasSeq!(2, 5, 8, 4, 3, 1)); -} - -/** - * Produces a $(D_PSYMBOL Set) that containing elements of - * $(D_INLINECODE Sets[0]) that are also elements of all other sets in - * $(D_PARAM Sets). - * - * Params: - * Sets = List of $(D_PSYMBOL Set) instances. - * - * Returns: Set-theoretic intersection of all $(D_PARAM Sets). - * - * See_Also: $(D_PSYMBOL Set). - */ -template Intersection(Sets...) -if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets)) -{ - private template Impl(Args...) - if (Args.length > 0) - { - alias Equal = ApplyLeft!(isEqual, Args[0]); - static if (Args.length == 1) - { - enum bool Impl = true; - } - else static if (!anySatisfy!(Equal, Args[1].Seq)) - { - enum bool Impl = false; - } - else - { - enum bool Impl = Impl!(Args[0], Args[2 .. $]); - } - } - - private enum bool FilterImpl(Args...) = Impl!(Args[0], Sets[1 .. $]); - - static if (Sets.length == 0) - { - alias Intersection = Set!(); - } - else - { - alias Intersection = Set!(Filter!(FilterImpl, Sets[0].Seq)); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias S1 = Set!(2, 5, 8, 4); - alias S2 = Set!(3, 8, 4, 1); - static assert(Intersection!(S1, S2).Seq == AliasSeq!(8, 4)); - - static assert(Intersection!(S1).Seq == AliasSeq!(2, 5, 8, 4)); - static assert(Intersection!().length == 0); -} - -/** - * Produces a $(D_PSYMBOL Set) that contains all elements of - * $(D_PARAM S1) that are not members of $(D_PARAM S2). - * - * Params: - * S1 = A $(D_PSYMBOL Set). - * S2 = A $(D_PSYMBOL Set). - * - * Returns: Set-theoretic difference of two sets $(D_PARAM S1) and - * $(D_PARAM S2). - * - * See_Also: $(D_PSYMBOL Set). - */ -template Difference(alias S1, alias S2) -if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2)) -{ - private template Impl(Args...) - { - alias Equal = ApplyLeft!(isEqual, Args[0]); - enum bool Impl = !anySatisfy!(Equal, S2.Seq); - } - - static if (S1.length == 0) - { - alias Difference = Set!(); - } - else static if (S2.length == 1) - { - alias Difference = S1; - } - else - { - alias Difference = Set!(Filter!(Impl, S1.Seq)); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias S1 = Set!(2, 5, 8, 4); - alias S2 = Set!(3, 8, 4, 1); - static assert(Difference!(S1, S2).Seq == AliasSeq!(2, 5)); - static assert(Difference!(S2, S1).Seq == AliasSeq!(3, 1)); - static assert(Difference!(S1, Set!()).Seq == AliasSeq!(2, 5, 8, 4)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) is less than or equal to - * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than or equal - * to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isLessEqual(alias cmp, Args...) -if (Args.length == 2 && __traits(isTemplate, cmp)) -{ - private enum result = cmp!(Args[1], Args[0]); - static if (is(typeof(result) == bool)) - { - enum bool isLessEqual = !result; - } - else - { - enum bool isLessEqual = result >= 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool boolCmp(T, U) = T.sizeof < U.sizeof; - static assert(isLessEqual!(boolCmp, byte, int)); - static assert(isLessEqual!(boolCmp, uint, int)); - static assert(!isLessEqual!(boolCmp, long, int)); - - enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; - static assert(isLessEqual!(intCmp, byte, int)); - static assert(isLessEqual!(intCmp, uint, int)); - static assert(!isLessEqual!(intCmp, long, int)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) is greater than or equal to - * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than or - * equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isGreaterEqual(alias cmp, Args...) -if (Args.length == 2 && __traits(isTemplate, cmp)) -{ - private enum result = cmp!Args; - static if (is(typeof(result) == bool)) - { - enum bool isGreaterEqual = !result; - } - else - { - enum bool isGreaterEqual = result >= 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool boolCmp(T, U) = T.sizeof < U.sizeof; - static assert(!isGreaterEqual!(boolCmp, byte, int)); - static assert(isGreaterEqual!(boolCmp, uint, int)); - static assert(isGreaterEqual!(boolCmp, long, int)); - - enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; - static assert(!isGreaterEqual!(intCmp, byte, int)); - static assert(isGreaterEqual!(intCmp, uint, int)); - static assert(isGreaterEqual!(intCmp, long, int)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) is less than - * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than - * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isLess(alias cmp, Args...) -if (Args.length == 2 && __traits(isTemplate, cmp)) -{ - private enum result = cmp!Args; - static if (is(typeof(result) == bool)) - { - enum bool isLess = result; - } - else - { - enum bool isLess = result < 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool boolCmp(T, U) = T.sizeof < U.sizeof; - static assert(isLess!(boolCmp, byte, int)); - static assert(!isLess!(boolCmp, uint, int)); - static assert(!isLess!(boolCmp, long, int)); - - enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; - static assert(isLess!(intCmp, byte, int)); - static assert(!isLess!(intCmp, uint, int)); - static assert(!isLess!(intCmp, long, int)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) is greater than - * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE Args[0] < Args[1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE Args[0] < Args[1]), a positive number that - * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) - * ) - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than - * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isGreater(alias cmp, Args...) -if (Args.length == 2 && __traits(isTemplate, cmp)) -{ - private enum result = cmp!Args; - static if (is(typeof(result) == bool)) - { - enum bool isGreater = !result && cmp!(Args[1], Args[0]); - } - else - { - enum bool isGreater = result > 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum bool boolCmp(T, U) = T.sizeof < U.sizeof; - static assert(!isGreater!(boolCmp, byte, int)); - static assert(!isGreater!(boolCmp, uint, int)); - static assert(isGreater!(boolCmp, long, int)); - - enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; - static assert(!isGreater!(intCmp, byte, int)); - static assert(!isGreater!(intCmp, uint, int)); - static assert(isGreater!(intCmp, long, int)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) is equal to $(D_INLINECODE Args[1]). - * - * $(D_PSYMBOL isEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types: - * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are - * considered to be not equal. - * - * If two items cannot be compared (for example comparing a type with a - * number), they are considered not equal. - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is equal to - * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isEqual(Args...) -if (Args.length == 2) -{ - static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) - || (isTypeTuple!Args && is(Args[0] == Args[1])) - || __traits(isSame, Args[0], Args[1])) - { - enum bool isEqual = true; - } - else - { - enum bool isEqual = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isEqual!(int, int)); - static assert(isEqual!(8, 8)); - static assert(!isEqual!(int, const(int))); - static assert(!isEqual!(5, int)); - static assert(!isEqual!(5, 8)); -} - -/** - * Tests whether $(D_INLINECODE Args[0]) isn't equal to - * $(D_INLINECODE Args[1]). - * - * $(D_PSYMBOL isNotEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types: - * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are - * considered to be not equal. - * - * Params: - * Args = Two aliases to compare for equality. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) isn't equal to - * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. - */ -template isNotEqual(Args...) -if (Args.length == 2) -{ - enum bool isNotEqual = !isEqual!Args; -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(!isNotEqual!(int, int)); - static assert(isNotEqual!(5, int)); - static assert(isNotEqual!(5, 8)); -} - -/** - * Instantiates the template $(D_PARAM T) with $(D_PARAM Args). - * - * Params: - * T = Template. - * Args = Template parameters. - * - * Returns: Instantiated template. - */ -alias Instantiate(alias T, Args...) = T!Args; - -/// -@nogc nothrow pure @safe unittest -{ - template Template(T) - { - alias Template = T; - } - alias Seq = AliasSeq!(Template, Template); - - alias Instance1 = Instantiate!(Seq[0], int); - static assert(is(Instance1 == int)); - - alias Instance2 = Instantiate!(Seq[1], float); - static assert(is(Instance2 == float)); -} - -/** - * Creates an alias for $(D_PARAM T). - * - * In contrast to the $(D_KEYWORD alias)-keyword $(D_PSYMBOL Alias) can alias - * any kind of D symbol that can be used as argument to template alias - * parameters. - * - * $(UL - * $(LI Types) - * $(LI Local and global names) - * $(LI Module names) - * $(LI Template names) - * $(LI Template instance names) - * $(LI Literals) - * ) - * - * Params: - * T = A symbol. - * - * Returns: An alias for $(D_PARAM T). - * - * See_Also: $(LINK2 https://dlang.org/spec/template.html#aliasparameters, - * Template Alias Parameters). - */ -alias Alias(alias T) = T; - -/// ditto -alias Alias(T) = T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Alias!int)); - - static assert(is(typeof(Alias!5))); - static assert(is(typeof(Alias!(() {})))); - - int i; - static assert(is(typeof(Alias!i))); -} - -/** - * Holds a sequence of aliases. - * - * $(D_PSYMBOL AliasSeq) can be used to pass multiple parameters to a template - * at once. $(D_PSYMBOL AliasSeq) behaves as it were just $(D_PARAM Args). Note - * that because of this property, if multiple instances of - * $(D_PSYMBOL AliasSeq) are passed to a template, they are not distinguishable - * from each other and act as a single sequence. There is also no way to make - * $(D_PSYMBOL AliasSeq) nested, it always unpacks its elements. - * - * Params: - * Args = Symbol sequence. - * - * Returns: An alias for sequence $(D_PARAM Args). - * - * See_Also: $(D_PSYMBOL Alias). - */ -alias AliasSeq(Args...) = Args; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof({ alias T = AliasSeq!(short, 5); }))); - static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); }))); - static assert(is(typeof({ alias T = AliasSeq!(() {}, short, 5); }))); - static assert(is(typeof({ alias T = AliasSeq!(); }))); - - static assert(AliasSeq!().length == 0); - static assert(AliasSeq!(int, short, 5).length == 3); - - alias A = AliasSeq!(short, float); - alias B = AliasSeq!(ushort, double); - alias C = AliasSeq!(A, B); - static assert(C.length == 4); -} - -/** - * Tests whether all the items of $(D_PARAM L) satisfy the condition - * $(D_PARAM F). - * - * $(D_PARAM F) is a template that accepts one parameter and returns a boolean, - * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called. - * - * Params: - * F = Template predicate. - * L = List of items to test. - * - * Returns: $(D_KEYWORD true) if all the items of $(D_PARAM L) satisfy - * $(D_PARAM F), $(D_KEYWORD false) otherwise. - */ -template allSatisfy(alias F, L...) -if (__traits(isTemplate, F)) -{ - static if (L.length == 0) - { - enum bool allSatisfy = true; - } - else static if (F!(L[0])) - { - enum bool allSatisfy = allSatisfy!(F, L[1 .. $]); - } - else - { - enum bool allSatisfy = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(allSatisfy!(isSigned, int, short, byte, long)); - static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong)); -} - -/** - * Tests whether any of the items of $(D_PARAM L) satisfy the condition - * $(D_PARAM F). - * - * $(D_PARAM F) is a template that accepts one parameter and returns a boolean, - * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called. - * - * Params: - * F = Template predicate. - * L = List of items to test. - * - * Returns: $(D_KEYWORD true) if any of the items of $(D_PARAM L) satisfy - * $(D_PARAM F), $(D_KEYWORD false) otherwise. - */ -template anySatisfy(alias F, L...) -if (__traits(isTemplate, F)) -{ - static if (L.length == 0) - { - enum bool anySatisfy = false; - } - else static if (F!(L[0])) - { - enum bool anySatisfy = true; - } - else - { - enum bool anySatisfy = anySatisfy!(F, L[1 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(anySatisfy!(isSigned, int, short, byte, long)); - static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong)); - static assert(!anySatisfy!(isSigned, uint, ushort, ulong)); -} - -private template indexOf(ptrdiff_t i, Args...) -if (Args.length > 0) -{ - static if (Args.length == 1) - { - enum ptrdiff_t indexOf = -1; - } - else static if (isEqual!(Args[0 .. 2])) - { - enum ptrdiff_t indexOf = i; - } - else - { - enum ptrdiff_t indexOf = indexOf!(i + 1, - AliasSeq!(Args[0], Args[2 .. $])); - } -} - -/** - * Returns the index of the first occurrence of $(D_PARAM T) in $(D_PARAM L). - * `-1` is returned if $(D_PARAM T) is not found. - * - * Params: - * T = The item to search for. - * L = Symbol sequence. - * - * Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L). - */ -template staticIndexOf(T, L...) -{ - enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L)); -} - -/// ditto -template staticIndexOf(alias T, L...) -{ - enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L)); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(staticIndexOf!(int) == -1); - static assert(staticIndexOf!(int, int) == 0); - static assert(staticIndexOf!(int, float, double, int, real) == 2); - static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3); -} - -/** - * Looks for $(D_PARAM T) in $(D_PARAM L) and returns $(D_KEYWORD true) if it - * could be found and $(D_KEYWORD false) otherwise. - * - * Params: - * T = The item to search for. - * L = Symbol sequence. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L), - * $(D_KEYWORD false) otherwise. - */ -template canFind(T, L...) -{ - enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1; -} - -/// ditto -template canFind(alias T, L...) -{ - enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1; -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(!canFind!(int)); - static assert(canFind!(int, int)); - static assert(canFind!(int, float, double, int, real)); - static assert(canFind!(3, () {}, uint, 5, 3)); -} - -/* - * Tests whether $(D_PARAM T) is a template. - * - * $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances, - * since the latter already represent some type. Only not instantiated - * templates, i.e. that accept some template parameters, are considered - * templates. - * - * Params: - * T = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template, - * $(D_KEYWORD false) otherwise. - */ -private enum bool isTemplate(alias T) = __traits(isTemplate, T); - -/// -@nogc nothrow pure @safe unittest -{ - static struct S(T) - { - } - static assert(isTemplate!S); - static assert(!isTemplate!(S!int)); -} - -/** - * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd) - * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on. - * - * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD true). - * - * Params: - * Preds = Template predicates. - * - * Returns: The constructed template. - */ -template templateAnd(Preds...) -if (allSatisfy!(isTemplate, Preds)) -{ - template templateAnd(T...) - { - static if (Preds.length == 0) - { - enum bool templateAnd = true; - } - else static if (Instantiate!(Preds[0], T)) - { - alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); - } - else - { - enum bool templateAnd = false; - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias isMutableInt = templateAnd!(isIntegral, isMutable); - static assert(isMutableInt!int); - static assert(!isMutableInt!(const int)); - static assert(!isMutableInt!float); - - alias alwaysTrue = templateAnd!(); - static assert(alwaysTrue!int); - - alias isIntegral = templateAnd!(.isIntegral); - static assert(isIntegral!int); - static assert(isIntegral!(const int)); - static assert(!isIntegral!float); -} - -/** - * Combines multiple templates with logical OR. So $(D_PSYMBOL templateOr) - * evaluates to $(D_INLINECODE Preds[0] || Preds[1] || Preds[2]) and so on. - * - * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD false). - * - * Params: - * Preds = Template predicates. - * - * Returns: The constructed template. - */ -template templateOr(Preds...) -if (allSatisfy!(isTemplate, Preds)) -{ - template templateOr(T...) - { - static if (Preds.length == 0) - { - enum bool templateOr = false; - } - else static if (Instantiate!(Preds[0], T)) - { - enum bool templateOr = true; - } - else - { - alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias isMutableOrInt = templateOr!(isIntegral, isMutable); - static assert(isMutableOrInt!int); - static assert(isMutableOrInt!(const int)); - static assert(isMutableOrInt!float); - static assert(!isMutableOrInt!(const float)); - - alias alwaysFalse = templateOr!(); - static assert(!alwaysFalse!int); - - alias isIntegral = templateOr!(.isIntegral); - static assert(isIntegral!int); - static assert(isIntegral!(const int)); - static assert(!isIntegral!float); -} - -/** - * Params: - * pred = Template predicate. - * - * Returns: Negated $(D_PARAM pred). - */ -template templateNot(alias pred) -if (__traits(isTemplate, pred)) -{ - enum bool templateNot(T...) = !pred!T; -} - -/// -@nogc nothrow pure @safe unittest -{ - alias isNotIntegral = templateNot!isIntegral; - static assert(!isNotIntegral!int); - static assert(isNotIntegral!(char[])); -} - -/** - * Tests whether $(D_PARAM L) is sorted in ascending order according to - * $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE a[i] < a[i + 1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE a[i] < a[i + 1]), a positive number that - * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.) - * ) - * - * Params: - * cmp = Sorting template predicate. - * L = Elements to be tested. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false) - * if not. - */ -template isSorted(alias cmp, L...) -if (__traits(isTemplate, cmp)) -{ - static if (L.length <= 1) - { - enum bool isSorted = true; - } - else - { - // `L` is sorted if the both halves and the boundary values are sorted. - enum bool isSorted = isLessEqual!(cmp, L[$ / 2 - 1], L[$ / 2]) - && isSorted!(cmp, L[0 .. $ / 2]) - && isSorted!(cmp, L[$ / 2 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum cmp(T, U) = T.sizeof < U.sizeof; - static assert(isSorted!(cmp)); - static assert(isSorted!(cmp, byte)); - static assert(isSorted!(cmp, byte, ubyte, short, uint)); - static assert(!isSorted!(cmp, long, byte, ubyte, short, uint)); -} - -@nogc nothrow pure @safe unittest -{ - enum cmp(int x, int y) = x - y; - static assert(isSorted!(cmp)); - static assert(isSorted!(cmp, 1)); - static assert(isSorted!(cmp, 1, 2, 2)); - static assert(isSorted!(cmp, 1, 2, 2, 4)); - static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); - static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); - static assert(isSorted!(cmp, 32, 32)); -} - -@nogc nothrow pure @safe unittest -{ - enum cmp(int x, int y) = x < y; - static assert(isSorted!(cmp)); - static assert(isSorted!(cmp, 1)); - static assert(isSorted!(cmp, 1, 2, 2)); - static assert(isSorted!(cmp, 1, 2, 2, 4)); - static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); - static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); - static assert(isSorted!(cmp, 32, 32)); -} - -/** - * Params: - * T = A template. - * Args = The first arguments for $(D_PARAM T). - * - * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as its first - * arguments. - */ -template ApplyLeft(alias T, Args...) -{ - alias ApplyLeft(U...) = T!(Args, U); -} - -/// -@nogc nothrow pure @safe unittest -{ - alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral); - static assert(allAreIntegral!(int, uint)); - static assert(!allAreIntegral!(int, float, uint)); -} - -/** - * Params: - * T = A template. - * Args = The last arguments for $(D_PARAM T). - * - * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as itslast - * arguments. - */ -template ApplyRight(alias T, Args...) -{ - alias ApplyRight(U...) = T!(U, Args); -} - -/// -@nogc nothrow pure @safe unittest -{ - alias intIs = ApplyRight!(allSatisfy, int); - static assert(intIs!(isIntegral)); - static assert(!intIs!(isUnsigned)); -} - -/** - * Params: - * n = The number of times to repeat $(D_PARAM L). - * L = The sequence to be repeated. - * - * Returns: $(D_PARAM L) repeated $(D_PARAM n) times. - */ -template Repeat(size_t n, L...) -if (n > 0) -{ - static if (n == 1) - { - alias Repeat = L; - } - else - { - alias Repeat = AliasSeq!(L, Repeat!(n - 1, L)); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int))); - static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int))); - static assert(is(Repeat!(3) == AliasSeq!())); -} - -private template ReplaceOne(L...) -{ - static if (L.length == 2) - { - alias ReplaceOne = AliasSeq!(); - } - else static if (isEqual!(L[0], L[2])) - { - alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]); - } - else - { - alias ReplaceOne = AliasSeq!(L[2], ReplaceOne!(L[0], L[1], L[3 .. $])); - } -} - -/** - * Replaces the first occurrence of $(D_PARAM T) in $(D_PARAM L) with - * $(D_PARAM U). - * - * Params: - * T = The symbol to be replaced. - * U = Replacement. - * L = List of symbols. - * - * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) replaced. - */ -template Replace(T, U, L...) -{ - alias Replace = ReplaceOne!(T, U, L); -} - -/// ditto -template Replace(alias T, U, L...) -{ - alias Replace = ReplaceOne!(T, U, L); -} - -/// ditto -template Replace(T, alias U, L...) -{ - alias Replace = ReplaceOne!(T, U, L); -} - -/// ditto -template Replace(alias T, alias U, L...) -{ - alias Replace = ReplaceOne!(T, U, L); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Replace!(int, uint, int) == AliasSeq!(uint))); - static assert(is(Replace!(int, uint, short, int, int, ushort) - == AliasSeq!(short, uint, int, ushort))); - - static assert(Replace!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 5)); -} - -private template ReplaceAllImpl(L...) -{ - static if (L.length == 2) - { - alias ReplaceAllImpl = AliasSeq!(); - } - else - { - private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]); - static if (isEqual!(L[0], L[2])) - { - alias ReplaceAllImpl = AliasSeq!(L[1], Rest); - } - else - { - alias ReplaceAllImpl = AliasSeq!(L[2], Rest); - } - } -} - -/** - * Replaces all occurrences of $(D_PARAM T) in $(D_PARAM L) with $(D_PARAM U). - * - * Params: - * T = The symbol to be replaced. - * U = Replacement. - * L = List of symbols. - * - * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) replaced. - */ -template ReplaceAll(T, U, L...) -{ - alias ReplaceAll = ReplaceAllImpl!(T, U, L); -} - -/// ditto -template ReplaceAll(alias T, U, L...) -{ - alias ReplaceAll = ReplaceAllImpl!(T, U, L); -} - -/// ditto -template ReplaceAll(T, alias U, L...) -{ - alias ReplaceAll = ReplaceAllImpl!(T, U, L); -} - -/// ditto -template ReplaceAll(alias T, alias U, L...) -{ - alias ReplaceAll = ReplaceAllImpl!(T, U, L); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint))); - static assert(is(ReplaceAll!(int, uint, short, int, int, ushort) - == AliasSeq!(short, uint, uint, ushort))); - - static assert(ReplaceAll!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 8)); -} - -/** - * Params: - * L = List of symbols. - * - * Returns: $(D_PARAM L) with elements in reversed order. - */ -template Reverse(L...) -{ - static if (L.length == 0) - { - alias Reverse = AliasSeq!(); - } - else - { - alias Reverse = AliasSeq!(L[$ - 1], Reverse!(L[0 .. $ - 1])); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte))); -} - -/** - * Applies $(D_PARAM F) to all elements of $(D_PARAM T). - * - * Params: - * F = Template predicate. - * T = List of symbols. - * - * Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them. - */ -template Map(alias F, T...) -if (__traits(isTemplate, F)) -{ - static if (T.length == 0) - { - alias Map = AliasSeq!(); - } - else - { - alias Map = AliasSeq!(F!(T[0]), Map!(F, T[1 .. $])); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Map!(Unqual, const int, immutable short) - == AliasSeq!(int, short))); -} - -/** - * Sorts $(D_PARAM L) in ascending order according to $(D_PARAM cmp). - * - * $(D_PARAM cmp) can evaluate to: - * $(UL - * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means - * $(D_INLINECODE a[i] < a[i + 1]).) - * $(LI $(D_KEYWORD int): a negative number means that - * $(D_INLINECODE a[i] < a[i + 1]), a positive number that - * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.) - * ) - * - * Merge sort is used to sort the arguments. - * - * Params: - * cmp = Sorting template predicate. - * L = Elements to be sorted. - * - * Returns: Elements of $(D_PARAM L) in ascending order. - * - * See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort). - */ -template Sort(alias cmp, L...) -if (__traits(isTemplate, cmp)) -{ - private template merge(size_t A, size_t B) - { - static if (A + B == L.length) - { - alias merge = AliasSeq!(); - } - else static if (B >= Right.length - || (A < Left.length && isLessEqual!(cmp, Left[A], Right[B]))) - { - alias merge = AliasSeq!(Left[A], merge!(A + 1, B)); - } - else - { - alias merge = AliasSeq!(Right[B], merge!(A, B + 1)); - } - } - - static if (L.length <= 1) - { - alias Sort = L; - } - else - { - private alias Left = Sort!(cmp, L[0 .. $ / 2]); - private alias Right = Sort!(cmp, L[$ / 2 .. $]); - alias Sort = merge!(0, 0); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum cmp(T, U) = T.sizeof < U.sizeof; - static assert(is(Sort!(cmp, long, short, byte, int) - == AliasSeq!(byte, short, int, long))); -} - -@nogc nothrow pure @safe unittest -{ - enum cmp(int T, int U) = T - U; - static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14) - == AliasSeq!(2, 5, 9, 10, 12, 14, 17)); -} - -private enum bool DerivedToFrontCmp(A, B) = is(A : B); - -/** - * Returns $(D_PARAM L) sorted in such a way that the most derived types come - * first. - * - * Params: - * L = Type tuple. - * - * Returns: Sorted $(D_PARAM L). - */ -template DerivedToFront(L...) -{ - alias DerivedToFront = Sort!(DerivedToFrontCmp, L); -} - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - } - class B : A - { - } - class C : B - { - } - static assert(is(DerivedToFront!(B, A, C) == AliasSeq!(C, B, A))); -} - -/** - * Returns the type from the type tuple $(D_PARAM L) that is most derived from - * $(D_PARAM T). - * - * Params: - * T = The type to compare to. - * L = Type tuple. - * - * Returns: The type most derived from $(D_PARAM T). - */ -template MostDerived(T, L...) -{ - static if (L.length == 0) - { - alias MostDerived = T; - } - else static if (is(T : L[0])) - { - alias MostDerived = MostDerived!(T, L[1 .. $]); - } - else - { - alias MostDerived = MostDerived!(L[0], L[1 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - } - class B : A - { - } - class C : B - { - } - static assert(is(MostDerived!(A, C, B) == C)); -} - -private template EraseOne(L...) -if (L.length > 0) -{ - static if (L.length == 1) - { - alias EraseOne = AliasSeq!(); - } - else static if (isEqual!(L[0 .. 2])) - { - alias EraseOne = AliasSeq!(L[2 .. $]); - } - else - { - alias EraseOne = AliasSeq!(L[1], EraseOne!(L[0], L[2 .. $])); - } -} - -/** - * Removes the first occurrence of $(D_PARAM T) from the alias sequence - * $(D_PARAL L). - * - * Params: - * T = The item to be removed. - * L = Alias sequence. - * - * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) removed. - */ -template Erase(T, L...) -{ - alias Erase = EraseOne!(T, L); -} - -/// ditto -template Erase(alias T, L...) -{ - alias Erase = EraseOne!(T, L); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint))); - static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint))); -} - -private template EraseAllImpl(L...) -{ - static if (L.length == 1) - { - alias EraseAllImpl = AliasSeq!(); - } - else static if (isEqual!(L[0 .. 2])) - { - alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]); - } - else - { - alias EraseAllImpl = AliasSeq!(L[1], EraseAllImpl!(L[0], L[2 .. $])); - } -} - -/** - * Removes all occurrences of $(D_PARAM T) from the alias sequence $(D_PARAL L). - * - * Params: - * T = The item to be removed. - * L = Alias sequence. - * - * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) removed. - */ -template EraseAll(T, L...) -{ - alias EraseAll = EraseAllImpl!(T, L); -} - -/// ditto -template EraseAll(alias T, L...) -{ - alias EraseAll = EraseAllImpl!(T, L); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint))); - static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint))); - static assert(is(EraseAll!(int, int, int) == AliasSeq!())); -} - -/** - * Returns an alias sequence which contains only items that satisfy the - * condition $(D_PARAM pred). - * - * Params: - * pred = Template predicate. - * L = Alias sequence. - * - * Returns: $(D_PARAM L) filtered so that it contains only items that satisfy - * $(D_PARAM pred). - */ -template Filter(alias pred, L...) -{ - static if (L.length == 0) - { - alias Filter = AliasSeq!(); - } - else static if (pred!(L[0])) - { - alias Filter = AliasSeq!(L[0], Filter!(pred, L[1 .. $])); - } - else - { - alias Filter = Filter!(pred, L[1 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias Given = AliasSeq!(real, int, bool, uint); - static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint))); -} - -/** - * Removes all duplicates from the alias sequence $(D_PARAM L). - * - * Params: - * L = Alias sequence. - * - * Returns: $(D_PARAM L) containing only unique items. - */ -template NoDuplicates(L...) -{ - static if (L.length == 0) - { - alias NoDuplicates = AliasSeq!(); - } - else - { - private alias Rest = NoDuplicates!(EraseAll!(L[0], L[1 .. $])); - alias NoDuplicates = AliasSeq!(L[0], Rest); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias Given = AliasSeq!(int, uint, int, short, short, uint); - static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short))); -} - -/** - * Converts an input range $(D_PARAM range) into an alias sequence. - * - * Params: - * range = Input range. - * - * Returns: Alias sequence with items from $(D_PARAM range). - */ -template aliasSeqOf(alias range) -{ - static if (isArray!(typeof(range))) - { - static if (range.length == 0) - { - alias aliasSeqOf = AliasSeq!(); - } - else - { - alias aliasSeqOf = AliasSeq!(range[0], aliasSeqOf!(range[1 .. $])); - } - } - else - { - ReturnType!(typeof(&range.front))[] toArray(typeof(range) range) - { - typeof(return) result; - foreach (r; range) - { - result ~= r; - } - return result; - } - alias aliasSeqOf = aliasSeqOf!(toArray(range)); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3)); -} - -/** - * Produces a alias sequence consisting of every $(D_PARAM n)th element of - * $(D_PARAM Args), starting with the first. - * - * Params: - * n = Step. - * Args = The items to stride. - * - * Returns: Alias sequence of every $(D_PARAM n)th element of $(D_PARAM Args). - */ -template Stride(size_t n, Args...) -if (n > 0) -{ - static if (Args.length > n) - { - alias Stride = AliasSeq!(Args[0], Stride!(n, Args[n .. $])); - } - else static if (Args.length > 0) - { - alias Stride = AliasSeq!(Args[0]); - } - else - { - alias Stride = AliasSeq!(); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7)); - static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3)); - static assert(Stride!(2, 1, 2) == AliasSeq!(1)); - static assert(Stride!(2, 1) == AliasSeq!(1)); - static assert(Stride!(1, 1, 2, 3) == AliasSeq!(1, 2, 3)); - static assert(is(Stride!3 == AliasSeq!())); -} - -/** - * Aliases itself to $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true), - * to $(D_INLINECODE T[1]) if $(D_KEYWORD false). - * - * Params: - * cond = Template predicate. - * T = Two arguments. - * - * Returns: $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true), - * $(D_INLINECODE T[1]) otherwise. - */ -template Select(bool cond, T...) -if (T.length == 2) -{ - static if (cond) - { - alias Select = T[0]; - } - else - { - alias Select = T[1]; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Select!(true, int, float) == int)); - static assert(is(Select!(false, int, float) == float)); -} - -/** - * Attaces a numeric index to each element from $(D_PARAM Args). - * - * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s) - * consisting of the index of each element and the element itself. - * - * Params: - * start = Enumeration initial value. - * Args = Enumerated sequence. - * - * See_Also: $(D_PSYMBOL Enumerate). - */ -template EnumerateFrom(size_t start, Args...) -{ - static if (Args.length == 0) - { - alias EnumerateFrom = AliasSeq!(); - } - else - { - alias EnumerateFrom = AliasSeq!(Pack!(start, Args[0]), EnumerateFrom!(start + 1, Args[1 .. $])); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(EnumerateFrom!(0, int, uint, bool).length == 3); -} - -/// -@nogc nothrow pure @safe unittest -{ - alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int), - Pack!(cast(size_t) 1, uint)); - static assert(is(EnumerateFrom!(0, int, uint) == Expected)); -} - -/** - * Attaces a numeric index to each element from $(D_PARAM Args). - * - * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s) - * consisting of the index of each element and the element itself. - * - * Params: - * Args = Enumerated sequence. - * - * See_Also: $(D_PSYMBOL EnumerateFrom). - */ -alias Enumerate(Args...) = EnumerateFrom!(0, Args); - -/// -@nogc nothrow pure @safe unittest -{ - alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int), - Pack!(cast(size_t) 1, uint)); - static assert(is(Enumerate!(int, uint) == Expected)); -} diff --git a/meta/source/tanya/meta/package.d b/meta/source/tanya/meta/package.d deleted file mode 100644 index d93e4fa..0000000 --- a/meta/source/tanya/meta/package.d +++ /dev/null @@ -1,23 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Template metaprogramming. - * - * This package contains utilities to acquire type information at compile-time, - * to transform from one type to another. It has also different algorithms for - * iterating, searching and modifying template arguments. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/package.d, - * tanya/meta/package.d) - */ -module tanya.meta; - -public import tanya.meta.metafunction; -public import tanya.meta.trait; -public import tanya.meta.transform; diff --git a/meta/source/tanya/meta/trait.d b/meta/source/tanya/meta/trait.d deleted file mode 100644 index 69b97b9..0000000 --- a/meta/source/tanya/meta/trait.d +++ /dev/null @@ -1,3106 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Type traits. - * - * Templates in this module are used to obtain type information at compile - * time. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/trait.d, - * tanya/meta/trait.d) - */ -module tanya.meta.trait; - -import tanya.meta.metafunction; -import tanya.meta.transform; - -/** - * Determines whether $(D_PARAM T) is a wide string, i.e. consists of - * $(D_KEYWORD dchar). - * - * The character type of the string can be qualified with $(D_KEYWORD const), - * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of - * $(D_KEYWORD shared) in the character type results in returning - * $(D_KEYWORD false). - * The string itself (in contrast to its character type) can have any type - * qualifiers. - * - * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered - * strings. - * - * Params: - * T = A Type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a wide string, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isNarrowString). - */ -enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isWideString!(dchar[])); - static assert(!isWideString!(char[])); - static assert(!isWideString!(wchar[])); - - static assert(isWideString!dstring); - static assert(!isWideString!string); - static assert(!isWideString!wstring); - - static assert(isWideString!(const dstring)); - static assert(!isWideString!(const string)); - static assert(!isWideString!(const wstring)); - - static assert(isWideString!(shared dstring)); - static assert(!isWideString!(shared string)); - static assert(!isWideString!(shared wstring)); - - static assert(isWideString!(const(dchar)[])); - static assert(isWideString!(inout(dchar)[])); - static assert(!isWideString!(shared(const(dchar))[])); - static assert(!isWideString!(shared(dchar)[])); - static assert(!isWideString!(dchar[10])); -} - -/** - * Determines whether $(D_PARAM T) is a complex type. - * - * Complex types are: - * $(UL - * $(LI cfloat) - * $(LI ifloat) - * $(LI cdouble) - * $(LI idouble) - * $(LI creal) - * $(LI ireal) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a complex type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat) - || is(Unqual!(OriginalType!T) == ifloat) - || is(Unqual!(OriginalType!T) == cdouble) - || is(Unqual!(OriginalType!T) == idouble) - || is(Unqual!(OriginalType!T) == creal) - || is(Unqual!(OriginalType!T) == ireal); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isComplex!cfloat); - static assert(isComplex!ifloat); - static assert(isComplex!cdouble); - static assert(isComplex!idouble); - static assert(isComplex!creal); - static assert(isComplex!ireal); - - static assert(!isComplex!float); - static assert(!isComplex!double); - static assert(!isComplex!real); -} - -/* - * Tests whether $(D_PARAM T) is an interface. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface, - * $(D_KEYWORD false) otherwise. - */ -private enum bool isInterface(T) = is(T == interface); - -/** - * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a - * $(D_KEYWORD class) or an $(D_KEYWORD interface). - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a $(D_KEYWORD class) or an - * $(D_KEYWORD interface), $(D_KEYWORD false) otherwise. - */ -enum bool isPolymorphicType(T) = is(T == class) || is(T == interface); - -/// -@nogc nothrow pure @safe unittest -{ - interface I - { - } - static assert(isPolymorphicType!Object); - static assert(isPolymorphicType!I); - static assert(!isPolymorphicType!short); -} - -/** - * Determines whether the type $(D_PARAM T) has a static method - * named $(D_PARAM member). - * - * Params: - * T = Aggregate type. - * member = Symbol name. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM member) is a static method of - * $(D_PARAM T), $(D_KEYWORD false) otherwise. - */ -template hasStaticMember(T, string member) -{ - static if (hasMember!(T, member)) - { - alias Member = Alias!(__traits(getMember, T, member)); - - static if (__traits(isStaticFunction, Member) - || (!isFunction!Member && is(typeof(&Member)))) - { - enum bool hasStaticMember = true; - } - else - { - enum bool hasStaticMember = false; - } - } - else - { - enum bool hasStaticMember = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - int member1; - void member2() - { - } - static int member3; - static void member4() - { - } - static void function() member5; - } - static assert(!hasStaticMember!(S, "member1")); - static assert(!hasStaticMember!(S, "member2")); - static assert(hasStaticMember!(S, "member3")); - static assert(hasStaticMember!(S, "member4")); - static assert(hasStaticMember!(S, "member5")); -} - -/** - * Determines whether $(D_PARAM T) is a floating point type. - * - * Floating point types are: - * $(UL - * $(LI float) - * $(LI double) - * $(LI real) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a floating point type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isFloatingPoint(T) = is(Unqual!(OriginalType!T) == double) - || is(Unqual!(OriginalType!T) == float) - || is(Unqual!(OriginalType!T) == real); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isFloatingPoint!float); - static assert(isFloatingPoint!double); - static assert(isFloatingPoint!real); - static assert(isFloatingPoint!(const float)); - static assert(isFloatingPoint!(shared float)); - static assert(isFloatingPoint!(shared const float)); - static assert(!isFloatingPoint!int); -} - -/** - * Determines whether $(D_PARAM T) is a signed numeric type. - * - * Signed numeric types are: - * $(UL - * $(LI byte) - * $(LI short) - * $(LI int) - * $(LI long) - * $(LI float) - * $(LI double) - * $(LI real) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a signed numeric type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isUnsigned). - */ -enum bool isSigned(T) = is(Unqual!(OriginalType!T) == byte) - || is(Unqual!(OriginalType!T) == short) - || is(Unqual!(OriginalType!T) == int) - || is(Unqual!(OriginalType!T) == long) - || isFloatingPoint!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isSigned!byte); - static assert(isSigned!short); - static assert(isSigned!int); - static assert(isSigned!long); - static assert(isSigned!float); - static assert(isSigned!double); - static assert(isSigned!real); - - static assert(!isSigned!ubyte); - static assert(!isSigned!ushort); - static assert(!isSigned!uint); - static assert(!isSigned!ulong); -} - -/** - * Determines whether $(D_PARAM T) is an unsigned numeric type. - * - * Unsigned numeric types are: - * $(UL - * $(LI ubyte) - * $(LI ushort) - * $(LI uint) - * $(LI ulong) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an unsigned numeric type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isSigned). - */ -enum bool isUnsigned(T) = is(Unqual!(OriginalType!T) == ubyte) - || is(Unqual!(OriginalType!T) == ushort) - || is(Unqual!(OriginalType!T) == uint) - || is(Unqual!(OriginalType!T) == ulong); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isUnsigned!ubyte); - static assert(isUnsigned!ushort); - static assert(isUnsigned!uint); - static assert(isUnsigned!ulong); - - static assert(!isUnsigned!byte); - static assert(!isUnsigned!short); - static assert(!isUnsigned!int); - static assert(!isUnsigned!long); - static assert(!isUnsigned!float); - static assert(!isUnsigned!double); - static assert(!isUnsigned!real); -} - -/** - * Determines whether $(D_PARAM T) is an integral type. - * - * Integral types are: - * $(UL - * $(LI ubyte) - * $(LI ushort) - * $(LI uint) - * $(LI ulong) - * $(LI byte) - * $(LI short) - * $(LI int) - * $(LI long) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an integral type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isIntegral(T) = isUnsigned!T - || is(Unqual!(OriginalType!T) == byte) - || is(Unqual!(OriginalType!T) == short) - || is(Unqual!(OriginalType!T) == int) - || is(Unqual!(OriginalType!T) == long); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isIntegral!ubyte); - static assert(isIntegral!byte); - static assert(!isIntegral!float); -} - -/** - * Determines whether $(D_PARAM T) is a numeric (floating point, integral or - * complex) type. -* - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a numeric type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isIntegral!T), - * $(D_PSYMBOL isFloatingPoint), - * $(D_PSYMBOL isComplex). - */ -enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T; - -/// -@nogc nothrow pure @safe unittest -{ - alias F = float; - static assert(isNumeric!F); - static assert(!isNumeric!bool); - static assert(!isNumeric!char); - static assert(!isNumeric!wchar); -} - -/** - * Determines whether $(D_PARAM T) is a boolean type, i.e. $(D_KEYWORD bool). - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a boolean type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isBoolean!bool); - static assert(isBoolean!(shared const bool)); - static assert(!isBoolean!(ubyte)); - static assert(!isBoolean!(byte)); - - enum E : bool - { - t = true, - f = false, - } - static assert(isBoolean!E); - - static struct S1 - { - bool b; - alias b this; - } - static assert(!isBoolean!S1); - - static struct S2 - { - bool opCast(T : bool)() - { - return true; - } - } - static assert(!isBoolean!S2); -} - -/** - * Determines whether $(D_PARAM T) is a character type. - * - * Character types are: - * - * $(UL - * $(LI char) - * $(LI wchar) - * $(LI dchar) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a character type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char) - || is(Unqual!(OriginalType!T) == wchar) - || is(Unqual!(OriginalType!T) == dchar); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isSomeChar!char); - static assert(isSomeChar!wchar); - static assert(isSomeChar!dchar); - - static assert(!isSomeChar!byte); - static assert(!isSomeChar!ubyte); - static assert(!isSomeChar!short); - static assert(!isSomeChar!ushort); - static assert(!isSomeChar!int); - static assert(!isSomeChar!uint); -} - -/** - * Determines whether $(D_PARAM T) is a scalar type. - * - * Scalar types are numbers, booleans and characters. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a scalar type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isNumeric), - * $(D_PSYMBOL isBoolean), - * $(D_PSYMBOL isSomeChar). - */ -enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isScalarType!int); - static assert(!isScalarType!(int[])); -} - -/** - * Determines whether $(D_PARAM T) is a basic type. - * - * Basic types are scalar types and $(D_KEYWORD void). - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a basic type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isScalarType). - */ -enum bool isBasicType(T) = isScalarType!T || is(T : void); - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - } - class C - { - } - enum E : int - { - i = 0, - } - - static assert(isBasicType!void); - static assert(isBasicType!(shared void)); - static assert(isBasicType!E); - static assert(!isBasicType!(int*)); - static assert(!isBasicType!(void function())); - static assert(!isBasicType!C); -} - -/** - * Determines whether $(D_PARAM T) is a pointer type. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a pointer type, - * $(D_KEYWORD false) otherwise. - */ -template isPointer(T) -{ - static if (is(T U : U*)) - { - enum bool isPointer = !is(Unqual!(OriginalType!T) == typeof(null)); - } - else - { - enum bool isPointer = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isPointer!(bool*)); - static assert(isPointer!(const bool*)); - static assert(isPointer!(const shared bool*)); - static assert(!isPointer!bool); -} - -// typeof(null) is not a pointer. -@nogc nothrow pure @safe unittest -{ - static assert(!isPointer!(typeof(null))); - static assert(!isPointer!(const shared typeof(null))); - - enum typeOfNull : typeof(null) - { - null_ = null, - } - static assert(!isPointer!typeOfNull); -} - -/** - * Determines whether $(D_PARAM T) is an array type (dynamic or static, but - * not an associative one). - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an array type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isAssociativeArray). - */ -template isArray(T) -{ - static if (is(T U : U[])) - { - enum bool isArray = true; - } - else - { - enum bool isArray = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isArray!(bool[])); - static assert(isArray!(const bool[])); - static assert(isArray!(shared bool[])); - static assert(isArray!(bool[8])); - static assert(!isArray!bool); - static assert(!isArray!(bool[string])); -} - -/** - * Determines whether $(D_PARAM T) is a static array type. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a static array type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isArray). - */ -template isStaticArray(T) -{ - static if (is(T U : U[L], size_t L)) - { - enum bool isStaticArray = true; - } - else - { - enum bool isStaticArray = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isStaticArray!(bool[8])); - static assert(isStaticArray!(const bool[8])); - static assert(isStaticArray!(shared bool[8])); - static assert(!isStaticArray!(bool[])); - static assert(!isStaticArray!bool); - static assert(!isStaticArray!(bool[string])); -} - -/** - * Determines whether $(D_PARAM T) is a dynamic array type. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a dynamic array type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isArray). - */ -enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isDynamicArray!(bool[])); - static assert(isDynamicArray!(const bool[])); - static assert(isDynamicArray!(shared bool[])); - static assert(!isDynamicArray!(bool[8])); - static assert(!isDynamicArray!bool); - static assert(!isDynamicArray!(bool[string])); -} - -/** - * Determines whether $(D_PARAM T) is an associative array type. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an associative array type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isArray). - */ -template isAssociativeArray(T) -{ - static if (is(T U : U[L], L)) - { - enum bool isAssociativeArray = true; - } - else - { - enum bool isAssociativeArray = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isAssociativeArray!(bool[string])); - static assert(isAssociativeArray!(const bool[string])); - static assert(isAssociativeArray!(shared const bool[string])); - static assert(!isAssociativeArray!(bool[])); - static assert(!isAssociativeArray!(bool[8])); - static assert(!isAssociativeArray!bool); -} - -/** - * Determines whether $(D_PARAM T) is a built-in type. - * - * Built-in types are all basic types and arrays. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a built-in type, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isBasicType!T), - * $(D_PSYMBOL isArray), - * $(D_PSYMBOL isAssociativeArray). - */ -enum bool isBuiltinType(T) = isBasicType!T - || isArray!T - || isAssociativeArray!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isBuiltinType!int); - static assert(isBuiltinType!(int[])); - static assert(isBuiltinType!(int[int])); - static assert(!isBuiltinType!(int*)); -} - -/** - * Determines whether $(D_PARAM T) is an aggregate type. - * - * Aggregate types are: - * - * $(UL - * $(LI $(D_KEYWORD struct)s) - * $(LI $(D_KEYWORD class)es) - * $(LI $(D_KEYWORD interface)s) - * $(LI $(D_KEYWORD union)s) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an aggregate type, - * $(D_KEYWORD false) otherwise. - */ -enum bool isAggregateType(T) = is(T == struct) - || is(T == class) - || is(T == interface) - || is(T == union); - -/// -@nogc nothrow pure @safe unittest -{ - static struct S; - class C; - interface I; - union U; - enum E; - - static assert(isAggregateType!S); - static assert(isAggregateType!C); - static assert(isAggregateType!I); - static assert(isAggregateType!U); - static assert(!isAggregateType!E); - static assert(!isAggregateType!void); -} - -/** - * Determines whether $(D_PARAM T) is a narrow string, i.e. consists of - * $(D_KEYWORD char) or $(D_KEYWORD wchar). - * - * The character type of the string can be qualified with $(D_KEYWORD const), - * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of - * $(D_KEYWORD shared) in the character type results in returning - * $(D_KEYWORD false). - * The string itself (in contrast to its character type) can have any type - * qualifiers. - * - * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered - * strings. - * - * Params: - * T = A Type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a narrow string, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isWideString). - */ -enum bool isNarrowString(T) = (is(T : const char[]) || is (T : const wchar[])) - && !isStaticArray!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isNarrowString!(char[])); - static assert(isNarrowString!(wchar[])); - static assert(!isNarrowString!(dchar[])); - - static assert(isNarrowString!string); - static assert(isNarrowString!wstring); - static assert(!isNarrowString!dstring); - - static assert(isNarrowString!(const string)); - static assert(isNarrowString!(const wstring)); - static assert(!isNarrowString!(const dstring)); - - static assert(isNarrowString!(shared string)); - static assert(isNarrowString!(shared wstring)); - static assert(!isNarrowString!(shared dstring)); - - static assert(isNarrowString!(const(char)[])); - static assert(isNarrowString!(inout(char)[])); - static assert(!isNarrowString!(shared(const(char))[])); - static assert(!isNarrowString!(shared(char)[])); - static assert(!isNarrowString!(char[10])); -} - -/** - * Determines whether $(D_PARAM T) is a string, i.e. consists of - * $(D_KEYWORD char), $(D_KEYWORD wchar) or $(D_KEYWORD dchar). - * - * The character type of the string can be qualified with $(D_KEYWORD const), - * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of - * $(D_KEYWORD shared) in the character type results in returning - * $(D_KEYWORD false). - * The string itself (in contrast to its character type) can have any type - * qualifiers. - * - * Static character arrays are not considered strings. - * - * Params: - * T = A Type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a string, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isNarrowString), $(D_PSYMBOL isWideString). - */ -enum bool isSomeString(T) = isNarrowString!T || isWideString!T; - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isSomeString!(dchar[])); - static assert(isSomeString!(char[])); - static assert(isSomeString!(wchar[])); - - static assert(isSomeString!dstring); - static assert(isSomeString!string); - static assert(isSomeString!wstring); - - static assert(isSomeString!(const dstring)); - static assert(isSomeString!(const string)); - static assert(isSomeString!(const wstring)); - - static assert(isSomeString!(shared dstring)); - static assert(isSomeString!(shared string)); - static assert(isSomeString!(shared wstring)); - - static assert(isSomeString!(const(char)[])); - static assert(isSomeString!(inout(char)[])); - static assert(!isSomeString!(shared(const(char))[])); - static assert(!isSomeString!(shared(char)[])); - static assert(!isSomeString!(char[10])); -} - -/** - * Returns the minimum value of type $(D_PARAM T). In contrast to - * $(D_INLINECODE T.min) this template works with floating point and complex - * types as well. - * - * Params: - * T = Integral, boolean, floating point, complex or character type. - * - * Returns: The minimum value of $(D_PARAM T). - * - * See_Also: $(D_PSYMBOL isIntegral), - * $(D_PSYMBOL isBoolean), - * $(D_PSYMBOL isSomeChar), - * $(D_PSYMBOL isFloatingPoint), - * $(D_PSYMBOL isComplex). - */ -template mostNegative(T) -{ - static if (isIntegral!T || isBoolean!T || isSomeChar!T) - { - enum T mostNegative = T.min; - } - else static if (isFloatingPoint!T || isComplex!T) - { - enum T mostNegative = -T.max; - } - else - { - static assert(false, T.stringof ~ " doesn't have the minimum value"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(mostNegative!char == char.min); - static assert(mostNegative!wchar == wchar.min); - static assert(mostNegative!dchar == dchar.min); - - static assert(mostNegative!byte == byte.min); - static assert(mostNegative!ubyte == ubyte.min); - static assert(mostNegative!bool == bool.min); - - static assert(mostNegative!float == -float.max); - static assert(mostNegative!double == -double.max); - static assert(mostNegative!real == -real.max); - - static assert(mostNegative!ifloat == -ifloat.max); - static assert(mostNegative!cfloat == -cfloat.max); -} - -/** - * Determines whether the type $(D_PARAM T) is copyable. - * - * Only structs can be not copyable if their postblit constructor or the - * postblit constructor of one of its fields is disabled, i.e. annotated with - * $(D_KEYWORD @disable). - * - * Params: - * T = A type. - * - * Returns: $(D_PARAM true) if $(D_PARAM T) can be copied, - * $(D_PARAM false) otherwise. - */ -enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; })); - -/// -@nogc nothrow pure @safe unittest -{ - static struct S1 - { - } - static struct S2 - { - this(this) - { - } - } - static struct S3 - { - @disable this(this); - } - static struct S4 - { - S3 s; - } - class C - { - } - - static assert(isCopyable!S1); - static assert(isCopyable!S2); - static assert(!isCopyable!S3); - static assert(!isCopyable!S4); - - static assert(isCopyable!C); - static assert(isCopyable!bool); -} - -/** - * Determines whether $(D_PARAM T) is an abstract class. - * - * Abstract class is a class marked as such or a class that has any abstract - * methods or doesn't implement all methods of abstract base classes. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an abstract class, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isAbstractFunction). - */ -enum bool isAbstractClass(T) = __traits(isAbstractClass, T); - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - } - abstract class B - { - } - class C - { - abstract void func(); - } - class D : C - { - } - class E : C - { - override void func() - { - } - } - static assert(!isAbstractClass!A); - static assert(isAbstractClass!B); - static assert(isAbstractClass!C); - static assert(isAbstractClass!D); - static assert(!isAbstractClass!E); -} - -/** - * Checks whether $(D_PARAM T) is a type, same as `is(T)` does. - * - * Params: - * T = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false) - * otherwise. - */ -enum bool isType(alias T) = is(T); - -/// ditto -enum bool isType(T) = true; - -/** - * Determines whether $(D_PARAM Args) contains only types. - * - * Params: - * Args = Alias sequence. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of types, - * $(D_KEYWORD false) otherwise. - */ -enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isTypeTuple!(int, uint, Object)); - static assert(isTypeTuple!()); - static assert(!isTypeTuple!(int, 8, Object)); - static assert(!isTypeTuple!(5, 8, 2)); - - class C - { - } - enum E : bool - { - t, - f, - } - union U - { - } - static struct T() - { - } - - static assert(isTypeTuple!C); - static assert(isTypeTuple!E); - static assert(isTypeTuple!U); - static assert(isTypeTuple!void); - static assert(isTypeTuple!int); - static assert(!isTypeTuple!T); - static assert(isTypeTuple!(T!())); - static assert(!isTypeTuple!5); - static assert(!isTypeTuple!(tanya.meta.trait)); -} - -/** - * Tells whether $(D_PARAM Args) contains only expressions. - * - * An expression is determined by applying $(D_KEYWORD typeof) to an argument: - * - * --- - * static if (is(typeof(Args[i]))) - * { - * // Args[i] is an expression. - * } - * else - * { - * // Args[i] is not an expression. - * } - * --- - * - * Params: - * Args = Alias sequence. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of expressions, - * $(D_KEYWORD false) otherwise. - */ -template isExpressions(Args...) -{ - static if (Args.length == 0) - { - enum bool isExpressions = true; - } - else static if (is(typeof(Args[0]) U)) - { - enum bool isExpressions = !is(U == void) - && isExpressions!(Args[1 .. $]); - } - else - { - enum bool isExpressions = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isExpressions!(5, 8, 2)); - static assert(isExpressions!()); - static assert(!isExpressions!(int, uint, Object)); - static assert(!isExpressions!(int, 8, Object)); - - template T(U) - { - } - static assert(!isExpressions!T); -} - -/** - * Determines whether $(D_PARAM T) is a final class. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a final class, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isFinalFunction). - */ -enum bool isFinalClass(T) = __traits(isFinalClass, T); - -/// -@nogc nothrow pure @safe unittest -{ - final class A - { - } - class B - { - } - - static assert(isFinalClass!A); - static assert(!isFinalClass!B); -} - -/** - * Determines whether $(D_PARAM T) is an abstract method. - * - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is an abstract method, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isAbstractClass). - */ -enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F); - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - void func() - { - } - } - class B - { - abstract void func(); - } - class C : B - { - override void func() - { - } - } - static assert(!isAbstractFunction!(A.func)); - static assert(isAbstractFunction!(B.func)); - static assert(!isAbstractFunction!(C.func)); -} - -/** - * Determines whether $(D_PARAM T) is a final method. - * - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a final method, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isFinalClass). - */ -enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F); - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - void virtualFunc() - { - } - final void finalFunc() - { - } - } - - static assert(isFinalFunction!(A.finalFunc)); - static assert(!isFinalFunction!(A.virtualFunc)); -} - -/** - * Function pointer is a pointer to a function. So a simple function is not - * a function pointer, but getting the address of such function returns a - * function pointer. - * - * A function pointer doesn't save the context pointer, thus cannot have access - * to its outer scope. - * - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function pointer, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, - * Delegates, Function Pointers, and Closures). - */ -template isFunctionPointer(F...) -if (F.length == 1) -{ - static if ((is(typeof(F[0]) T : T*) && is(T == function)) - || (is(F[0] T : T*) && is(T == function))) - { - enum bool isFunctionPointer = true; - } - else - { - enum bool isFunctionPointer = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isFunctionPointer!(void function())); - static assert(!isFunctionPointer!(void delegate())); - - static assert(isFunctionPointer!(() {})); - - void func() - { - } - static void staticFunc() - { - } - interface I - { - @property int prop(); - } - - static assert(!isFunctionPointer!func); - static assert(!isFunctionPointer!staticFunc); - - auto functionPointer = &staticFunc; - auto dg = &func; - - static assert(isFunctionPointer!functionPointer); - static assert(!isFunctionPointer!dg); - - static assert(!isFunctionPointer!(I.prop)); -} - -/** - * Delegate stores the function pointer and function context. - * - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a delegate, - * $(D_KEYWORD false) delegate. - * - * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, - * Delegates, Function Pointers, and Closures). - */ -template isDelegate(F...) -if (F.length == 1) -{ - static if (is(F[0] == delegate) - || is(typeof(F[0]) == delegate)) - { - enum bool isDelegate = true; - } - else - { - enum bool isDelegate = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isDelegate!(void delegate())); - static assert(!isDelegate!(void function())); - - static assert(!isDelegate!(() {})); - - void func() - { - } - static void staticFunc() - { - } - interface I - { - @property int prop(); - } - - static assert(!isDelegate!func); - static assert(!isDelegate!staticFunc); - - auto functionPointer = &staticFunc; - auto dg = &func; - - static assert(!isDelegate!functionPointer); - static assert(isDelegate!dg); - - static assert(!isDelegate!(I.prop)); -} - -/** - * $(D_PSYMBOL isFunction) returns $(D_KEYWORD true) only for plain functions, - * not function pointers or delegates. Use $(D_PSYMBOL isFunctionPointer) or - * $(D_PSYMBOL isDelegate) to detect them or $(D_PSYMBOL isSomeFunction) - * for detecting a function of any type. - * - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, - * Delegates, Function Pointers, and Closures). - */ -template isFunction(F...) -if (F.length == 1) -{ - static if (is(F[0] == function) - || is(typeof(&F[0]) T == delegate) - || (is(typeof(&F[0]) T : T*) && is(T == function))) - { - enum bool isFunction = true; - } - else - { - enum bool isFunction = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(!isFunction!(void function())); - static assert(!isFunction!(() {})); - static assert(!isFunction!(void delegate())); - - void func() - { - } - static void staticFunc() - { - } - interface I - { - @property int prop(); - } - - static assert(isFunction!func); - static assert(isFunction!staticFunc); - - auto functionPointer = &staticFunc; - auto dg = &func; - - static assert(!isFunction!functionPointer); - static assert(!isFunction!dg); - - static assert(isFunction!(I.prop)); -} - -/** - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, function pointer - * or delegate, $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isFunction), - * $(D_PSYMBOL isDelegate), - * $(D_PSYMBOL isFunctionPointer). - */ -template isSomeFunction(F...) -if (F.length == 1) -{ - enum bool isSomeFunction = isFunctionPointer!F - || isFunction!F - || isDelegate!F; -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isSomeFunction!(void function())); - static assert(isSomeFunction!(() {})); - static assert(isSomeFunction!(void delegate())); - - void func() - { - } - static void staticFunc() - { - } - - static assert(isSomeFunction!func); - static assert(isSomeFunction!staticFunc); - - auto functionPointer = &staticFunc; - auto dg = &func; - - static assert(isSomeFunction!functionPointer); - static assert(isSomeFunction!dg); - - static assert(!isSomeFunction!int); -} - -/** - * Params: - * F = A symbol. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM F) is callable, - * $(D_KEYWORD false) otherwise. - */ -template isCallable(F...) -if (F.length == 1) -{ - static if (isSomeFunction!F - || (is(typeof(F[0].opCall)) && isFunction!(F[0].opCall))) - { - enum bool isCallable = true; - } - else - { - enum bool isCallable = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - void opCall() - { - } - } - class C - { - static void opCall() - { - } - } - interface I - { - } - S s; - - static assert(isCallable!s); - static assert(isCallable!C); - static assert(isCallable!S); - static assert(!isCallable!I); -} - -@nogc nothrow pure @safe unittest -{ - static struct S - { - @property int opCall() - { - return 0; - } - } - S s; - static assert(isCallable!S); - static assert(isCallable!s); -} - -/** - * Determines whether $(D_PARAM T) defines a symbol $(D_PARAM member). - * - * Params: - * T = Aggregate type. - * member = Symbol name. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol - * $(D_PARAM member), $(D_KEYWORD false) otherwise. - */ -enum bool hasMember(T, string member) = __traits(hasMember, T, member); - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - int member1; - void member2() - { - } - static int member3; - static void member4() - { - } - } - static assert(hasMember!(S, "member1")); - static assert(hasMember!(S, "member2")); - static assert(hasMember!(S, "member3")); - static assert(hasMember!(S, "member4")); - static assert(!hasMember!(S, "member6")); -} - -/** - * Determines whether $(D_PARAM T) is mutable, i.e. has one of the following - * qualifiers or a combination of them: - * - * $(UL - * $(LI $(D_KEYWORD const)) - * $(LI $(D_KEYWORD immutable)) - * $(LI $(D_KEYWORD const)) - * ) - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) is mutable, - * $(D_KEYWORD false) otherwise. - */ -template isMutable(T) -{ - static if (is(T U == const U) - || is(T U == inout U) - || is(T U == inout const U) - || is(T U == immutable U) - || is(T U == shared const U) - || is(T U == shared inout U) - || is(T U == shared inout const U)) - { - enum bool isMutable = false; - } - else - { - enum bool isMutable = true; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - void method() - { - static assert(isMutable!(typeof(this))); - } - - void method() inout - { - static assert(!isMutable!(typeof(this))); - } - - void immMethod() const - { - static assert(!isMutable!(typeof(this))); - } - void immMethod() immutable - { - static assert(!isMutable!(typeof(this))); - } - } -} - -/** - * Determines whether $(D_PARAM T) is a nested type, i.e. $(D_KEYWORD class), - * $(D_KEYWORD struct) or $(D_KEYWORD union), which internally stores a context - * pointer. - * - * Params: - * T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type. - * - * Returns: $(D_KEYWORD true) if the argument is a nested type which internally - * stores a context pointer, $(D_KEYWORD false) otherwise. - */ -template isNested(T) -if (is(T == class) || is(T == struct) || is(T == union)) -{ - enum bool isNested = __traits(isNested, T); -} - -/// -@nogc pure nothrow @safe unittest -{ - static struct S - { - } - static assert(!isNested!S); - - class C - { - void method() - { - } - } - static assert(isNested!C); -} - -/** - * Determines whether $(D_PARAM T) is a nested function. - * - * Params: - * F = A function. - * - * Returns $(D_KEYWORD true) if the $(D_PARAM T) is a nested function, - * $(D_KEYWORD false) otherwise. - */ -enum bool isNestedFunction(alias F) = __traits(isNested, F); - -/// -@nogc nothrow pure @safe unittest -{ - void func() - { - void nestedFunc() - { - } - static assert(isNestedFunction!nestedFunc); - } -} - -/** - * Determines the type of the callable $(D_PARAM F). - * - * Params: - * F = A function. - * - * Returns: Type of the function $(D_PARAM F). - */ -template FunctionTypeOf(F...) -if (isCallable!F) -{ - static if ((is(typeof(F[0]) T : T*) && is(T == function)) - || (is(F[0] T : T*) && is(T == function)) - || is(F[0] T == delegate) - || is(typeof(F[0]) T == delegate) - || is(F[0] T == function) - || is(typeof(&F[0]) T == delegate) - || (is(typeof(&F[0]) T : T*) && is(T == function))) - { - alias FunctionTypeOf = T; - } - else - { - alias FunctionTypeOf = FunctionTypeOf!(F[0].opCall); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(FunctionTypeOf!(void function()) == function)); - static assert(is(FunctionTypeOf!(() {}) == function)); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(FunctionTypeOf!(void delegate()) == function)); - - static void staticFunc() - { - } - auto functionPointer = &staticFunc; - static assert(is(FunctionTypeOf!staticFunc == function)); - static assert(is(FunctionTypeOf!functionPointer == function)); - - void func() - { - } - auto dg = &func; - static assert(is(FunctionTypeOf!func == function)); - static assert(is(FunctionTypeOf!dg == function)); - - interface I - { - @property int prop(); - } - static assert(is(FunctionTypeOf!(I.prop) == function)); - - static struct S - { - void opCall() - { - } - } - class C - { - static void opCall() - { - } - } - S s; - - static assert(is(FunctionTypeOf!s == function)); - static assert(is(FunctionTypeOf!C == function)); - static assert(is(FunctionTypeOf!S == function)); -} - -@nogc nothrow pure @safe unittest -{ - static struct S2 - { - @property int opCall() - { - return 0; - } - } - S2 s2; - static assert(is(FunctionTypeOf!S2 == function)); - static assert(is(FunctionTypeOf!s2 == function)); -} - -/** - * Determines the return type of the callable $(D_PARAM F). - * - * Params: - * F = A callable object. - * - * Returns: Return type of $(D_PARAM F). - */ -template ReturnType(F...) -if (isCallable!F) -{ - static if (is(FunctionTypeOf!(F[0]) T == return)) - { - alias ReturnType = T; - } - else - { - static assert(false, "Argument is not a callable"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(ReturnType!(int delegate()) == int)); - static assert(is(ReturnType!(bool function()) == bool)); -} - -/** - * Determines the template $(D_PARAM T) is an instance of. - * - * Params: - * T = Template instance. - * - * Returns: Template $(D_PARAM T) is an instance of. - */ -alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base; - -/// -@nogc nothrow pure @safe unittest -{ - static struct S(T) - { - } - static assert(__traits(isSame, TemplateOf!(S!int), S)); - - static void func(T)() - { - } - static assert(__traits(isSame, TemplateOf!(func!int), func)); - - template T(U) - { - } - static assert(__traits(isSame, TemplateOf!(T!int), T)); -} - -/** - * Returns the mangled name of the symbol $(D_PARAM T). - * - * Params: - * T = A symbol. - * - * Returns: Mangled name of $(D_PARAM T). - */ -enum string mangledName(T) = T.mangleof; - -/// -enum string mangledName(alias T) = T.mangleof; - -/** - * Tests whether $(D_PARAM I) is an instance of template $(D_PARAM T). - * - * Params: - * T = Template. - * I = Template instance. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM I) is an instance of $(D_PARAM T), - * $(D_KEYWORD false) otherwise. - */ -enum bool isInstanceOf(alias T, I) = is(I == T!Args, Args...); - -template isInstanceOf(alias T, alias I) -{ - static if (is(typeof(TemplateOf!I))) - { - enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T); - } - else - { - enum bool isInstanceOf = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S(T) - { - } - static assert(isInstanceOf!(S, S!int)); - - static void func(T)(); - static assert(isInstanceOf!(func, func!int)); - - template T(U) - { - } - static assert(isInstanceOf!(T, T!int)); -} - -/** - * Checks whether $(D_PARAM From) is implicitly (without explicit - * $(D_KEYWORD cast)) to $(D_PARAM To). - * - * Params: - * From = Source type. - * To = Conversion target type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM From) is implicitly convertible to - * $(D_PARAM To), $(D_KEYWORD false) if not. - */ -enum bool isImplicitlyConvertible(From, To) = is(From : To); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isImplicitlyConvertible!(const(byte), byte)); - static assert(isImplicitlyConvertible!(byte, char)); - static assert(isImplicitlyConvertible!(byte, short)); - static assert(!isImplicitlyConvertible!(short, byte)); - static assert(isImplicitlyConvertible!(string, const(char)[])); -} - -/** - * Returns a tuple of base classes and interfaces of $(D_PARAM T). - * - * $(D_PSYMBOL BaseTypeTuple) returns only classes and interfaces $(D_PARAM T) - * directly inherits from, but not the base classes and interfaces of its parents. - * - * Params: - * T = Class or interface type. - * - * Returns: A tuple of base classes or interfaces of ($D_PARAM T). - * - * See_Also: $(D_PSYMBOL TransitiveBaseTypeTuple). - */ -template BaseTypeTuple(T) -if (is(T == class) || (is(T == interface))) -{ - static if (is(T Tuple == super)) - { - alias BaseTypeTuple = Tuple; - } - else - { - static assert(false, "Argument isn't a class or interface"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - interface I1 - { - } - interface I2 - { - } - interface I3 : I1, I2 - { - } - interface I4 - { - } - class A : I3, I4 - { - } - static assert(is(BaseTypeTuple!A == AliasSeq!(Object, I3, I4))); - static assert(BaseTypeTuple!Object.length == 0); -} - -/** - * Returns a tuple of all base classes and interfaces of $(D_PARAM T). - * - * $(D_PSYMBOL TransitiveBaseTypeTuple) returns first the parent class, then - * grandparent and so on. The last class is $(D_PSYMBOL Object). Then the interfaces - * follow. - * - * Params: - * T = Class or interface type. - * - * Returns: A tuple of all base classes and interfaces of ($D_PARAM T). - * - * See_Also: $(D_PSYMBOL BaseTypeTuple). - */ -template TransitiveBaseTypeTuple(T) -if (is(T == class) || is(T == interface)) -{ - private template Impl(T...) - { - static if (T.length == 0) - { - alias Impl = AliasSeq!(); - } - else - { - alias Impl = AliasSeq!(BaseTypeTuple!(T[0]), - Map!(ImplCopy, BaseTypeTuple!(T[0]))); - } - } - private alias ImplCopy = Impl; // To avoid recursive template expansion. - private enum bool cmp(A, B) = is(B == interface) && is(A == class); - - alias TransitiveBaseTypeTuple = NoDuplicates!(Sort!(cmp, Impl!T)); -} - -/// -@nogc nothrow pure @safe unittest -{ - interface I1 - { - } - interface I2 : I1 - { - } - class A : I2 - { - } - class B : A, I1 - { - } - class C : B, I2 - { - } - alias Expected = AliasSeq!(B, A, Object, I2, I1); - static assert(is(TransitiveBaseTypeTuple!C == Expected)); - - static assert(is(TransitiveBaseTypeTuple!Object == AliasSeq!())); - static assert(is(TransitiveBaseTypeTuple!I2 == AliasSeq!(I1))); -} - -/** - * Returns all the base classes of $(D_PARAM T), the direct parent class comes - * first, $(D_PSYMBOL Object) ist the last one. - * - * The only type that doesn't have any base class is $(D_PSYMBOL Object). - * - * Params: - * T = Class type. - * - * Returns: Base classes of $(D_PARAM T). - */ -template BaseClassesTuple(T) -if (is(T == class)) -{ - static if (is(T == Object)) - { - alias BaseClassesTuple = AliasSeq!(); - } - else - { - private alias Parents = BaseTypeTuple!T; - alias BaseClassesTuple = AliasSeq!(Parents[0], BaseClassesTuple!(Parents[0])); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - interface I1 - { - } - interface I2 - { - } - class A : I1, I2 - { - } - class B : A, I1 - { - } - class C : B, I2 - { - } - static assert(is(BaseClassesTuple!C == AliasSeq!(B, A, Object))); - static assert(BaseClassesTuple!Object.length == 0); -} - -/** - * Returns all the interfaces $(D_PARAM T) inherits from. - * - * Params: - * T = Class or interface type. - * - * Returns: Interfaces $(D_PARAM T) inherits from. - */ -template InterfacesTuple(T) -if (is(T == class) || is(T == interface)) -{ - alias InterfacesTuple = Filter!(isInterface, TransitiveBaseTypeTuple!T); -} - -/// -@nogc nothrow pure @safe unittest -{ - interface I1 - { - } - interface I2 : I1 - { - } - class A : I2 - { - } - class B : A, I1 - { - } - class C : B, I2 - { - } - static assert(is(InterfacesTuple!C == AliasSeq!(I2, I1))); - - static assert(is(InterfacesTuple!Object == AliasSeq!())); - static assert(is(InterfacesTuple!I1 == AliasSeq!())); -} - -/** - * Tests whether a value of type $(D_PARAM Rhs) can be assigned to a variable - * of type $(D_PARAM Lhs). - * - * If $(D_PARAM Rhs) isn't specified, $(D_PSYMBOL isAssignable) tests whether a - * value of type $(D_PARAM Lhs) can be assigned to a variable of the same type. - * - * $(D_PSYMBOL isAssignable) tells whether $(D_PARAM Rhs) can be assigned by - * value as well by reference. - * - * Params: - * Lhs = Variable type. - * Rhs = Expression type. - * - * Returns: $(D_KEYWORD true) if a value of type $(D_PARAM Rhs) can be assigned - * to a variable of type $(D_PARAM Lhs), $(D_KEYWORD false) otherwise. - */ -template isAssignable(Lhs, Rhs = Lhs) -{ - enum bool isAssignable = is(typeof({ - Lhs lhs = Lhs.init; - Rhs rhs = Rhs.init; - lhs = ((inout ref Rhs) => Rhs.init)(rhs); - })); -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S1 - { - @disable this(); - @disable this(this); - } - static struct S2 - { - void opAssign(S1 s) pure nothrow @safe @nogc - { - } - } - static struct S3 - { - void opAssign(ref S1 s) pure nothrow @safe @nogc - { - } - } - static assert(isAssignable!(S2, S1)); - static assert(!isAssignable!(S3, S1)); - - static assert(isAssignable!(const(char)[], string)); - static assert(!isAssignable!(string, char[])); - - static assert(isAssignable!int); - static assert(!isAssignable!(const int, int)); -} - -/** - * Returns template parameters of $(D_PARAM T). - * - * Params: - * T = Template instance. - * - * Returns: Template parameters of $(D_PARAM T). - */ -alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args; - -/// -@nogc nothrow pure @safe unittest -{ - template T(A, B) - { - } - static assert(is(TemplateArgsOf!(T!(int, uint)) == AliasSeq!(int, uint))); -} - -/** - * Returns a tuple with parameter types of a function. - * - * Params: - * F = A function. - * - * Returns: Tuple with parameter types of a function. - */ -template Parameters(F...) -if (isCallable!F) -{ - static if (is(FunctionTypeOf!F T == function)) - { - alias Parameters = T; - } - else - { - static assert(false, "Function has no parameters"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int func(Object, uint[]); - static assert(is(Parameters!func == AliasSeq!(Object, uint[]))); -} - -/** - * Returns a string array with all parameter names of a function. - * - * If a parameter has no name, an empty string is placed into array. - * - * Params: - * F = A function. - * - * Returns: Function parameter names. - */ -template ParameterIdentifierTuple(F...) -if (isCallable!F) -{ - static if (is(FunctionTypeOf!F Params == __parameters)) - { - string[] Impl() - { - string[] tuple; - - foreach (k, P; Params) - { - static if (is(typeof(__traits(identifier, Params[k .. $])))) - { - tuple ~= __traits(identifier, Params[k .. $]); - } - else - { - tuple ~= ""; - } - } - - return tuple; - } - enum string[] ParameterIdentifierTuple = Impl(); - } - else - { - static assert(false, "Function has no parameters"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int func(ref Object stuff, uint[] = null, scope uint k = 1); - alias P = ParameterIdentifierTuple!func; - static assert(P[0] == "stuff"); - static assert(P[1] == ""); - static assert(P[2] == "k"); -} - -/// Attributes can be attached to a function. -enum FunctionAttribute : uint -{ - none = 0x0000, - pure_ = 0x0001, - nothrow_ = 0x0002, - ref_ = 0x0004, - property = 0x0008, - trusted = 0x0010, - safe = 0x0020, - nogc = 0x0040, - system = 0x0080, - const_ = 0x0100, - immutable_ = 0x0200, - inout_ = 0x0400, - shared_ = 0x0800, - return_ = 0x1000, - scope_ = 0x2000, -} - -/** - * Retrieves the attributes of the function $(D_PARAM F). - * - * The attributes are returned as a bit-mask of - * $(D_PSYMBOL FunctionAttribute) values. - * - * Params: A function. - * - * Returns: Attributes of the function $(D_PARAM F). - * - * See_Also: $(D_PSYMBOL FunctionAttribute). - */ -template functionAttributes(F...) -if (isCallable!F) -{ - uint Impl() - { - uint attrs = FunctionAttribute.none; - foreach (a; __traits(getFunctionAttributes, F[0])) - { - static if (a == "const") - { - attrs |= FunctionAttribute.const_; - } - else static if (a == "immutable") - { - attrs |= FunctionAttribute.immutable_; - } - else static if (a == "inout") - { - attrs |= FunctionAttribute.inout_; - } - else static if (a == "@nogc") - { - attrs |= FunctionAttribute.nogc; - } - else static if (a == "nothrow") - { - attrs |= FunctionAttribute.nothrow_; - } - else static if (a == "@property") - { - attrs |= FunctionAttribute.property; - } - else static if (a == "pure") - { - attrs |= FunctionAttribute.pure_; - } - else static if (a == "ref") - { - attrs |= FunctionAttribute.ref_; - } - else static if (a == "return") - { - attrs |= FunctionAttribute.return_; - } - else static if (a == "@safe") - { - attrs |= FunctionAttribute.safe; - } - else static if (a == "scope") - { - attrs |= FunctionAttribute.scope_; - } - else static if (a == "shared") - { - attrs |= FunctionAttribute.shared_; - } - else static if (a == "@system") - { - attrs |= FunctionAttribute.system; - } - else static if (a == "@trusted") - { - attrs |= FunctionAttribute.trusted; - } - } - return attrs; - } - enum uint functionAttributes = Impl(); -} - -/// -@nogc nothrow pure @safe unittest -{ - @property ref int func1() pure nothrow @safe @nogc shared scope; - static assert((functionAttributes!func1 & FunctionAttribute.pure_) - == FunctionAttribute.pure_); - static assert((functionAttributes!func1 & FunctionAttribute.nothrow_) - == FunctionAttribute.nothrow_); - static assert((functionAttributes!func1 & FunctionAttribute.safe) - == FunctionAttribute.safe); - static assert((functionAttributes!func1 & FunctionAttribute.nogc) - == FunctionAttribute.nogc); - static assert((functionAttributes!func1 & FunctionAttribute.shared_) - == FunctionAttribute.shared_); - static assert((functionAttributes!func1 & FunctionAttribute.ref_) - == FunctionAttribute.ref_); - static assert((functionAttributes!func1 & FunctionAttribute.property) - == FunctionAttribute.property); - static assert((functionAttributes!func1 & FunctionAttribute.scope_) - == FunctionAttribute.scope_); - static assert((functionAttributes!func1 & FunctionAttribute.system) == 0); - static assert((functionAttributes!func1 & FunctionAttribute.trusted) == 0); - static assert((functionAttributes!func1 & FunctionAttribute.return_) == 0); -} - -/** - * Returns a tuple with default values of the parameters to $(D_PARAM F). - * - * If a parameter doesn't have a default value, $(D_KEYWORD void) is returned. - * - * Params: - * F = A function. - * - * Returns: Default values of the parameters to $(D_PARAM F). - */ -template ParameterDefaults(F...) -if (isCallable!F) -{ - static if (is(FunctionTypeOf!F T == __parameters)) - { - private template GetDefault(size_t i) - { - static if (i == T.length) - { - alias GetDefault = AliasSeq!(); - } - else - { - enum getDefault(T[i .. i + 1] name) - { - return name[0]; - } - static if (is(typeof(getDefault()))) - { - alias Default = Alias!(getDefault()); - } - else - { - alias Default = void; - } - alias GetDefault = AliasSeq!(Default, GetDefault!(i + 1)); - } - } - - alias ParameterDefaults = GetDefault!0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - void func1(int k, uint b = 5, int[] = [1, 2]); - alias Defaults = ParameterDefaults!func1; - static assert(is(Defaults[0] == void)); - static assert(Defaults[1 .. 3] == AliasSeq!(5, [1, 2])); -} - -/** - * Determines whether $(D_PARAM T) has an elaborate destructor. - * - * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the - * length greater than`0` can have elaborate destructors, for all other types - * $(D_PSYMBOL hasElaborateDestructor) evaluates to $(D_KEYWORD false). - * - * An elaborate destructor is an explicitly defined destructor or one generated - * by the compiler. The compiler generates a destructor for a - * $(D_KEYWORD struct) if it has members with an elaborate destructor. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate destructor, - * $(D_KEYWORD false) otherwise. - */ -template hasElaborateDestructor(T) -{ - static if (is(T E : E[L], size_t L)) - { - enum bool hasElaborateDestructor = L > 0 && hasElaborateDestructor!E; - } - else - { - enum bool hasElaborateDestructor = is(T == struct) - && hasMember!(T, "__xdtor"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - class C - { - ~this() - { - } - } - static assert(!hasElaborateDestructor!C); - - static struct S - { - ~this() - { - } - } - static struct S1 - { - S s; - } - static struct S2 - { - } - static assert(hasElaborateDestructor!S); // Explicit destructor. - static assert(hasElaborateDestructor!S1); // Compiler-generated destructor. - static assert(!hasElaborateDestructor!S2); // No destructor. - - static assert(hasElaborateDestructor!(S[1])); - static assert(!hasElaborateDestructor!(S[0])); -} - -/** - * Determines whether $(D_PARAM T) has an elaborate postblit constructor. - * - * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the - * length greater than`0` can have elaborate postblit constructors, for all - * other types $(D_PSYMBOL hasElaborateCopyConstructor) evaluates to - * $(D_KEYWORD false). - * - * An elaborate postblit constructor is an explicitly defined postblit - * constructor or one generated by the compiler. The compiler generates a - * postblit constructor for a - * $(D_KEYWORD struct) if it has members with an elaborate postblit - * constructor. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate postblit - * constructor, $(D_KEYWORD false) otherwise. - */ -template hasElaborateCopyConstructor(T) -{ - static if (is(T E : E[L], size_t L)) - { - enum bool hasElaborateCopyConstructor = L > 0 - && hasElaborateCopyConstructor!E; - } - else - { - enum bool hasElaborateCopyConstructor = is(T == struct) - && hasMember!(T, "__xpostblit"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(!hasElaborateCopyConstructor!int); - - static struct S - { - this(this) - { - } - } - static struct S1 - { - S s; - } - static struct S2 - { - } - static assert(hasElaborateCopyConstructor!S); // Explicit destructor. - static assert(hasElaborateCopyConstructor!S1); // Compiler-generated destructor. - static assert(!hasElaborateCopyConstructor!S2); // No destructor. - static assert(hasElaborateCopyConstructor!(S[1])); - static assert(!hasElaborateCopyConstructor!(S[0])); -} - -/** - * Determines whether $(D_PARAM T) has an elaborate assign. - * - * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the - * length greater than`0` can have an elaborate assign, for all - * other types $(D_PSYMBOL hasElaborateAssign) evaluates to $(D_KEYWORD false). - * - * An elaborate assign is defined with $(D_INLINECODE opAssign(typeof(this))) - * or $(D_INLINECODE opAssign(ref typeof(this))). An elaborate assign can be - * generated for a $(D_KEYWORD struct) by the compiler if one of the members of - * this $(D_KEYWORD struct) has an elaborate assign. - * - * Params: - * T = A type. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate assign, - * $(D_KEYWORD false) otherwise. - */ -template hasElaborateAssign(T) -{ - static if (is(T E : E[L], size_t L)) - { - enum bool hasElaborateAssign = L > 0 && hasElaborateAssign!E; - } - else static if (is(T == struct)) - { - private enum bool valueAssign = is(typeof({ T.init.opAssign(T()); })); - enum bool hasElaborateAssign = valueAssign || is(typeof({ - T s; - s.opAssign(s); - })); - } - else - { - enum bool hasElaborateAssign = false; - } -} - -@nogc nothrow pure @safe unittest -{ - static assert(!hasElaborateAssign!int); - - static struct S1 - { - void opAssign(S1) - { - } - } - static struct S2 - { - void opAssign(int) - { - } - } - static struct S3 - { - S1 s; - alias s this; - } - static assert(hasElaborateAssign!S1); - static assert(!hasElaborateAssign!(const S1)); - static assert(hasElaborateAssign!(S1[1])); - static assert(!hasElaborateAssign!(S1[0])); - static assert(!hasElaborateAssign!S2); - static assert(hasElaborateAssign!S3); - - static struct S4 - { - void opAssign(S4) - { - } - @disable this(this); - } - static assert(hasElaborateAssign!S4); -} - -/** - * Returns all members of $(D_KEYWORD enum) $(D_PARAM T). - * - * The members of $(D_PARAM T) are typed as $(D_PARAM T), not as a base type - * of the enum. - * - * $(D_PARAM EnumMembers) returns all members of $(D_PARAM T), also if there - * are some duplicates. - * - * Params: - * T = A $(D_KEYWORD enum). - * - * Returns: All members of $(D_PARAM T). - */ -template EnumMembers(T) -if (is(T == enum)) -{ - private template getEnumMembers(Args...) - { - static if (Args.length == 1) - { - enum T getEnumMembers = __traits(getMember, T, Args[0]); - } - else - { - alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]), - getEnumMembers!(Args[1 .. $])); - } - } - private alias allMembers = AliasSeq!(__traits(allMembers, T)); - static if (allMembers.length == 1) - { - alias EnumMembers = AliasSeq!(__traits(getMember, T, allMembers)); - } - else - { - alias EnumMembers = getEnumMembers!allMembers; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum E : int - { - one, - two, - three, - } - static assert([EnumMembers!E] == [E.one, E.two, E.three]); -} - -// Produces a tuple for an enum with only one member -@nogc nothrow pure @safe unittest -{ - enum E : int - { - one = 0, - } - static assert(EnumMembers!E == AliasSeq!0); -} - -/** - * Different than $(D_INLINECODE T.alignof), which is the same for all class - * types, $(D_PSYMBOL classInstanceOf) determines the alignment of the class - * instance and not of its reference. - * - * Params: - * T = A class. - * - * Returns: Alignment of an instance of the class $(D_PARAM T). - */ -template classInstanceAlignment(T) -if (is(T == class)) -{ - private enum ptrdiff_t pred(U1, U2) = U1.alignof - U2.alignof; - private alias Fields = typeof(T.tupleof); - enum size_t classInstanceAlignment = Max!(pred, T, Fields).alignof; -} - -/// -@nogc nothrow pure @safe unittest -{ - class C1 - { - } - static assert(classInstanceAlignment!C1 == C1.alignof); - - static struct S - { - align(8) - uint s; - - int i; - } - class C2 - { - S s; - } - static assert(classInstanceAlignment!C2 == S.alignof); -} - -/** - * Tests whether $(D_INLINECODE pred(T)) can be used as condition in an - * $(D_KEYWORD if)-statement or a ternary operator. - * - * $(D_PARAM pred) is an optional parameter. By default $(D_PSYMBOL ifTestable) - * tests whether $(D_PARAM T) itself is usable as condition in an - * $(D_KEYWORD if)-statement or a ternary operator, i.e. if it a value of type - * $(D_PARAM T) can be converted to a boolean. - * - * Params: - * T = A type. - * pred = Function with one argument. - * - * Returns: $(D_KEYWORD true) if $(D_INLINECODE pred(T)) can be used as - * condition in an $(D_KEYWORD if)-statement or a ternary operator. - */ -template ifTestable(T, alias pred = a => a) -{ - enum bool ifTestable = is(typeof(pred(T.init) ? true : false)); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(ifTestable!int); - - static struct S1 - { - } - static assert(!ifTestable!S1); - - static struct S2 - { - bool opCast(T : bool)() - { - return true; - } - } - static assert(ifTestable!S2); -} - -/** - * Returns a compile-time tuple of user-defined attributes (UDA) attached to - * $(D_PARAM symbol). - * - * $(D_PARAM symbol) can be: - * - * $(DL - * $(DT Template) - * $(DD The attribute is matched if it is an instance of the template - * $(D_PARAM attr).) - * $(DT Type) - * $(DD The attribute is matched if it its type is $(D_PARAM attr).) - * $(DT Expression) - * $(DD The attribute is matched if it equals to $(D_PARAM attr).) - * ) - * - * If $(D_PARAM attr) isn't given, all user-defined attributes of - * $(D_PARAM symbol) are returned. - * - * Params: - * symbol = A symbol. - * attr = User-defined attribute. - * - * Returns: A tuple of user-defined attributes attached to $(D_PARAM symbol) - * and matching $(D_PARAM attr). - * - * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, - * User Defined Attributes). - */ -template getUDAs(alias symbol, alias attr) -{ - private template FindUDA(T...) - { - static if (T.length == 0) - { - alias FindUDA = AliasSeq!(); - } - else static if ((isTypeTuple!attr && is(TypeOf!(T[0]) == attr)) - || (is(typeof(T[0] == attr)) && (T[0] == attr)) - || isInstanceOf!(attr, TypeOf!(T[0]))) - { - alias FindUDA = AliasSeq!(T[0], FindUDA!(T[1 .. $])); - } - else - { - alias FindUDA = FindUDA!(T[1 .. $]); - } - } - alias getUDAs = FindUDA!(__traits(getAttributes, symbol)); -} - -/// -alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol)); - -/// -@nogc nothrow pure @safe unittest -{ - static struct Attr - { - int i; - } - @Attr int a; - static assert(getUDAs!(a, Attr).length == 1); - - @Attr(8) int b; - static assert(getUDAs!(b, Attr).length == 1); - static assert(getUDAs!(b, Attr)[0].i == 8); - static assert(getUDAs!(b, Attr(8)).length == 1); - static assert(getUDAs!(b, Attr(7)).length == 0); - - @("string", 5) int c; - static assert(getUDAs!(c, "string").length == 1); - static assert(getUDAs!(c, 5).length == 1); - static assert(getUDAs!(c, "String").length == 0); - static assert(getUDAs!(c, 4).length == 0); - - static struct T(U) - { - enum U s = 7; - U i; - } - @T!int @T!int(8) int d; - static assert(getUDAs!(d, T).length == 2); - static assert(getUDAs!(d, T)[0].s == 7); - static assert(getUDAs!(d, T)[1].i == 8); - - @T int e; - static assert(getUDAs!(e, T).length == 0); -} - -/** - * Determines whether $(D_PARAM symbol) has user-defined attribute - * $(D_PARAM attr) attached to it. - * - * Params: - * symbol = A symbol. - * attr = User-defined attribute. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM symbol) has user-defined attribute - * $(D_PARAM attr), $(D_KEYWORD false) otherwise. - * - * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, - * User Defined Attributes). - */ -template hasUDA(alias symbol, alias attr) -{ - enum bool hasUDA = getUDAs!(symbol, attr).length != 0; -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct Attr1 - { - } - static struct Attr2 - { - } - @Attr1 int a; - static assert(hasUDA!(a, Attr1)); - static assert(!hasUDA!(a, Attr2)); -} - -/** - * If $(D_PARAM T) is a type, constructs its default value, otherwise - * $(D_PSYMBOL evalUDA) aliases itself to $(D_PARAM T). - * - * This template is useful when working with UDAs with default parameters, - * i.e. if an attribute can be given as `@Attr` or `@Attr("param")`, - * $(D_PSYMBOL evalUDA) makes `@Attr()` from `@Attr`, but returns - * `@Attr("param")` as is. - * - * $(D_PARAM T) (or its type if it isn't a type already) should have a default - * constructor. - * - * Params: - * T = User Defined Attribute. - */ -alias evalUDA(alias T) = T; - -/// ditto -alias evalUDA(T) = Alias!(T()); - -/// -@nogc nothrow pure @safe unittest -{ - static struct Length - { - size_t length = 8; - } - @Length @Length(0) int i; - alias uda = AliasSeq!(__traits(getAttributes, i)); - - alias attr1 = evalUDA!(uda[0]); - alias attr2 = evalUDA!(uda[1]); - - static assert(is(typeof(attr1) == Length)); - static assert(is(typeof(attr2) == Length)); - - static assert(attr1.length == 8); - static assert(attr2.length == 0); -} - -/** - * Tests whether $(D_PARAM T) is an inner class, i.e. a class nested inside - * another class. - * - * All inner classes get `outer` propery automatically generated, which points - * to its parent class, though it can be explicitly defined to be something - * different. If $(D_PARAM T) does this, $(D_PSYMBOL isInnerClass) - * evaluates to $(D_KEYWORD false). - * - * Params: - * T = Class to be tested. - * - * Returns $(D_KEYWORD true) if $(D_PARAM T) is an inner class, - * $(D_KEYWORD false) otherwise. - */ -template isInnerClass(T) -{ - static if (is(T == class) && is(typeof(T.outer) == class)) - { - enum bool isInnerClass = !canFind!("outer", __traits(allMembers, T)); - } - else - { - enum bool isInnerClass = false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - class A - { - } - class O - { - class I - { - } - class Fake - { - bool outer; - } - } - static assert(!isInnerClass!(O)); - static assert(isInnerClass!(O.I)); - static assert(!isInnerClass!(O.Fake)); -} - -@nogc nothrow pure @safe unittest -{ - class RefCountedStore(T) - { - } - static assert(!isInnerClass!(RefCountedStore!int)); -} - -/** - * Returns the types of all members of $(D_PARAM T). - * - * If $(D_PARAM T) is a $(D_KEYWORD struct) or $(D_KEYWORD union) or - * $(D_KEYWORD class), returns the types of all its fields. It is actually the - * same as `T.tupleof`, but the content pointer for the nested type isn't - * included. - * - * If $(D_PARAM T) is neither a $(D_KEYWORD struct) nor $(D_KEYWORD union) nor - * $(D_KEYWORD class), $(D_PSYMBOL Fields) returns an $(D_PSYMBOL AliasSeq) - * with the single element $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_PARAM T)'s fields. - */ -template Fields(T) -{ - static if ((is(T == struct) || is(T == union)) && isNested!T) - { - // The last element of .tupleof of a nested struct or union is "this", - // the context pointer, type "void*". - alias Fields = typeof(T.tupleof[0 .. $ - 1]); - } - else static if (is(T == class) || is(T == struct) || is(T == union)) - { - alias Fields = typeof(T.tupleof); - } - else - { - alias Fields = AliasSeq!T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - struct Nested - { - int i; - - void func() - { - } - } - static assert(is(Fields!Nested == AliasSeq!int)); - - class C - { - uint u; - } - static assert(is(Fields!C == AliasSeq!uint)); - - static assert(is(Fields!short == AliasSeq!short)); -} - -/** - * Determines whether all $(D_PARAM Types) are the same. - * - * If $(D_PARAM Types) is empty, returns $(D_KEYWORD true). - * - * Params: - * Types = Type sequence. - * - * Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same, - * $(D_KEYWORD false) otherwise. - */ -template allSameType(Types...) -{ - static if (Types.length == 0) - { - enum bool allSameType = true; - } - else - { - private enum bool sameType(T) = is(T == Types[0]); - - enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(allSameType!()); - static assert(allSameType!int); - static assert(allSameType!(int, int, int)); - static assert(!allSameType!(int, uint, int)); - static assert(!allSameType!(int, uint, short)); -} - -/** - * Determines whether values of type $(D_PARAM T) can be compared for equality, - * i.e. using `==` or `!=` binary operators. - * - * Params: - * T = Type to test. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality, - * $(D_KEYWORD false) otherwise. - */ -enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isEqualityComparable!int); -} - -/** - * Determines whether values of type $(D_PARAM T) can be compared for ordering, - * i.e. using `>`, `>=`, `<` or `<=` binary operators. - * - * Params: - * T = Type to test. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering, - * $(D_KEYWORD false) otherwise. - */ -enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(isOrderingComparable!int); -} - -@nogc nothrow pure @safe unittest -{ - static struct DisabledOpEquals - { - @disable bool opEquals(typeof(this)) @nogc nothrow pure @safe; - - int opCmp(typeof(this)) @nogc nothrow pure @safe - { - return 0; - } - } - static assert(!isEqualityComparable!DisabledOpEquals); - static assert(isOrderingComparable!DisabledOpEquals); - - static struct OpEquals - { - bool opEquals(typeof(this)) @nogc nothrow pure @safe - { - return true; - } - } - static assert(isEqualityComparable!OpEquals); - static assert(!isOrderingComparable!OpEquals); -} diff --git a/meta/source/tanya/meta/transform.d b/meta/source/tanya/meta/transform.d deleted file mode 100644 index 5697e69..0000000 --- a/meta/source/tanya/meta/transform.d +++ /dev/null @@ -1,981 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Type transformations. - * - * Templates in this module can be used to modify type qualifiers or transform - * types. They take some type as argument and return a different type after - * perfoming the specified transformation. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/transform.d, - * tanya/meta/transform.d) - */ -module tanya.meta.transform; - -import tanya.meta.metafunction; -import tanya.meta.trait; - -/** - * Removes any type qualifiers from $(D_PARAM T). - * - * Removed qualifiers are: - * $(UL - * $(LI const) - * $(LI immutable) - * $(LI inout) - * $(LI shared) - * ) - * and combinations of these. - * - * If the type $(D_PARAM T) doesn't have any qualifieres, - * $(D_INLINECODE Unqual!T) becomes an alias for $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_PARAM T) without any type qualifiers. - */ -template Unqual(T) -{ - static if (is(T U == const U) - || is(T U == immutable U) - || is(T U == inout U) - || is(T U == inout const U) - || is(T U == shared U) - || is(T U == shared const U) - || is(T U == shared inout U) - || is(T U == shared inout const U)) - { - alias Unqual = U; - } - else - { - alias Unqual = T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Unqual!bool == bool)); - static assert(is(Unqual!(immutable bool) == bool)); - static assert(is(Unqual!(inout bool) == bool)); - static assert(is(Unqual!(inout const bool) == bool)); - static assert(is(Unqual!(shared bool) == bool)); - static assert(is(Unqual!(shared const bool) == bool)); - static assert(is(Unqual!(shared inout const bool) == bool)); -} - -/** - * If $(D_PARAM T) is an $(D_KEYWORD enum), $(D_INLINECODE OriginalType!T) - * evaluates to the most base type of that $(D_KEYWORD enum) and to - * $(D_PARAM T) otherwise. - * - * Params: - * T = A type. - * - * Returns: Base type of the $(D_KEYWORD enum) $(D_PARAM T) or $(D_PARAM T) - * itself. - */ -template OriginalType(T) -{ - static if (is(T U == enum)) - { - alias OriginalType = OriginalType!U; - } - else - { - alias OriginalType = T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - enum E1 : const(int) - { - n = 0, - } - enum E2 : bool - { - t = true, - } - enum E3 : E2 - { - t = E2.t, - } - enum E4 : const(E2) - { - t = E2.t, - } - - static assert(is(OriginalType!E1 == const int)); - static assert(is(OriginalType!E2 == bool)); - static assert(is(OriginalType!E3 == bool)); - static assert(is(OriginalType!E4 == bool)); - static assert(is(OriginalType!(const E4) == bool)); -} - -/** - * Copies constness of $(D_PARAM From) to $(D_PARAM To). - * - * The following type qualifiers affect the constness and hence are copied: - * $(UL - * $(LI const) - * $(LI immutable) - * $(LI inout) - * $(LI inout const) - * ) - * - * Params: - * From = Source type. - * To = Target type. - * - * Returns: $(D_PARAM To) with the constness of $(D_PARAM From). - * - * See_Also: $(D_PSYMBOL CopyTypeQualifiers). - */ -template CopyConstness(From, To) -{ - static if (is(From T == immutable T)) - { - alias CopyConstness = immutable To; - } - else static if (is(From T == const T) || is(From T == shared const T)) - { - alias CopyConstness = const To; - } - else static if (is(From T == inout T) || is(From T == shared inout T)) - { - alias CopyConstness = inout To; - } - else static if (is(From T == inout const T) - || is(From T == shared inout const T)) - { - alias CopyConstness = inout const To; - } - else - { - alias CopyConstness = To; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(CopyConstness!(int, char) == char)); - static assert(is(CopyConstness!(const int, char) == const char)); - static assert(is(CopyConstness!(immutable int, char) == immutable char)); - static assert(is(CopyConstness!(inout int, char) == inout char)); - static assert(is(CopyConstness!(inout const int, char) == inout const char)); - - static assert(is(CopyConstness!(shared int, char) == char)); - static assert(is(CopyConstness!(shared const int, char) == const char)); - static assert(is(CopyConstness!(shared inout int, char) == inout char)); - static assert(is(CopyConstness!(shared inout const int, char) == inout const char)); - - static assert(is(CopyConstness!(const int, shared char) == shared const char)); - static assert(is(CopyConstness!(const int, immutable char) == immutable char)); - static assert(is(CopyConstness!(immutable int, const char) == immutable char)); -} - -/** - * Copies type qualifiers of $(D_PARAM From) to $(D_PARAM To). - * - * Type qualifiers copied are: - * $(UL - * $(LI const) - * $(LI immutable) - * $(LI inout) - * $(LI shared) - * ) - * and combinations of these. - * - * Params: - * From = Source type. - * To = Target type. - * - * Returns: $(D_PARAM To) with the type qualifiers of $(D_PARAM From). - * - * See_Also: $(D_PSYMBOL CopyConstness). - */ -template CopyTypeQualifiers(From, To) -{ - static if (is(From T == immutable T)) - { - alias CopyTypeQualifiers = immutable To; - } - else static if (is(From T == const T)) - { - alias CopyTypeQualifiers = const To; - } - else static if (is(From T == shared T)) - { - alias CopyTypeQualifiers = shared To; - } - else static if (is(From T == shared const T)) - { - alias CopyTypeQualifiers = shared const To; - } - else static if (is(From T == inout T)) - { - alias CopyTypeQualifiers = inout To; - } - else static if (is(From T == shared inout T)) - { - alias CopyTypeQualifiers = shared inout To; - } - else static if (is(From T == inout const T)) - { - alias CopyTypeQualifiers = inout const To; - } - else static if (is(From T == shared inout const T)) - { - alias CopyTypeQualifiers = shared inout const To; - } - else - { - alias CopyTypeQualifiers = To; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(CopyTypeQualifiers!(int, char) == char)); - static assert(is(CopyTypeQualifiers!(const int, char) == const char)); - static assert(is(CopyTypeQualifiers!(immutable int, char) == immutable char)); - static assert(is(CopyTypeQualifiers!(inout int, char) == inout char)); - static assert(is(CopyTypeQualifiers!(inout const int, char) == inout const char)); - - static assert(is(CopyTypeQualifiers!(shared int, char) == shared char)); - static assert(is(CopyTypeQualifiers!(shared const int, char) == shared const char)); - static assert(is(CopyTypeQualifiers!(shared inout int, char) == shared inout char)); - static assert(is(CopyTypeQualifiers!(shared inout const int, char) == shared inout const char)); -} - -/** - * Evaluates to the unsigned counterpart of the integral type $(D_PARAM T) preserving all type qualifiers. - * If $(D_PARAM T) is already unsigned, $(D_INLINECODE Unsigned!T) aliases $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: Unsigned counterpart of $(D_PARAM T). - * - * See_Also: $(D_PSYMBOL isSigned). - */ -template Unsigned(T) -if (isIntegral!T) -{ - alias UnqualedType = Unqual!(OriginalType!T); - static if (is(UnqualedType == byte)) - { - alias Unsigned = CopyTypeQualifiers!(T, ubyte); - } - else static if (is(UnqualedType == short)) - { - alias Unsigned = CopyTypeQualifiers!(T, ushort); - } - else static if (is(UnqualedType == int)) - { - alias Unsigned = CopyTypeQualifiers!(T, uint); - } - else static if (is(UnqualedType == long)) - { - alias Unsigned = CopyTypeQualifiers!(T, ulong); - } - else - { - alias Unsigned = T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Unsigned!byte == ubyte)); - static assert(is(Unsigned!short == ushort)); - static assert(is(Unsigned!int == uint)); - static assert(is(Unsigned!long == ulong)); - - static assert(is(Unsigned!(const byte) == const ubyte)); - static assert(is(Unsigned!(shared byte) == shared ubyte)); - static assert(is(Unsigned!(shared const byte) == shared const ubyte)); - - static assert(!is(Unsigned!float)); - static assert(is(Unsigned!ubyte == ubyte)); -} - -/** - * Evaluates to the signed counterpart of the integral type $(D_PARAM T) preserving all type qualifiers. - * If $(D_PARAM T) is already signed, $(D_INLINECODE Signed!T) aliases $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: Signed counterpart of $(D_PARAM T). - * - * See_Also: $(D_PSYMBOL isUnsigned). - */ -template Signed(T) -if (isIntegral!T) -{ - alias UnqualedType = Unqual!(OriginalType!T); - static if (is(UnqualedType == ubyte)) - { - alias Signed = CopyTypeQualifiers!(T, byte); - } - else static if (is(UnqualedType == ushort)) - { - alias Signed = CopyTypeQualifiers!(T, short); - } - else static if (is(UnqualedType == uint)) - { - alias Signed = CopyTypeQualifiers!(T, int); - } - else static if (is(UnqualedType == ulong)) - { - alias Signed = CopyTypeQualifiers!(T, long); - } - else - { - alias Signed = T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Signed!ubyte == byte)); - static assert(is(Signed!ushort == short)); - static assert(is(Signed!uint == int)); - static assert(is(Signed!ulong == long)); - - static assert(is(Signed!(const ubyte) == const byte)); - static assert(is(Signed!(shared ubyte) == shared byte)); - static assert(is(Signed!(shared const ubyte) == shared const byte)); - - static assert(!is(Signed!float)); - static assert(is(Signed!byte == byte)); -} - -/** - * Retrieves the target type `U` of a pointer `U*`. - * - * Params: - * T = Pointer type. - * - * Returns: Pointer target type. - */ -template PointerTarget(T) -{ - static if (is(T U : U*)) - { - alias PointerTarget = U; - } - else - { - static assert(T.stringof ~ " isn't a pointer type"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(PointerTarget!(bool*) == bool)); - static assert(is(PointerTarget!(const bool*) == const bool)); - static assert(is(PointerTarget!(const shared bool*) == const shared bool)); - static assert(!is(PointerTarget!bool)); -} - -/** - * Params: - * T = The type of the associative array. - * - * Returns: The key type of the associative array $(D_PARAM T). - */ -template KeyType(T) -{ - static if (is(T V : V[K], K)) - { - alias KeyType = K; - } - else - { - static assert(false, T.stringof ~ " isn't an associative array"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(KeyType!(int[string]) == string)); - static assert(!is(KeyType!(int[15]))); -} - -/** - * Params: - * T = The type of the associative array. - * - * Returns: The value type of the associative array $(D_PARAM T). - */ -template ValueType(T) -{ - static if (is(T V : V[K], K)) - { - alias ValueType = V; - } - else - { - static assert(false, T.stringof ~ " isn't an associative array"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(ValueType!(int[string]) == int)); - static assert(!is(ValueType!(int[15]))); -} - -/** - * Params: - * T = Scalar type. - * - * Returns: The type $(D_PARAM T) will promote to. - * - * See_Also: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, - * Integer Promotions). - */ -template Promoted(T) -if (isScalarType!T) -{ - alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Promoted!bool == int)); - static assert(is(Promoted!byte == int)); - static assert(is(Promoted!ubyte == int)); - static assert(is(Promoted!short == int)); - static assert(is(Promoted!ushort == int)); - static assert(is(Promoted!char == int)); - static assert(is(Promoted!wchar == int)); - static assert(is(Promoted!dchar == uint)); - - static assert(is(Promoted!(const bool) == const int)); - static assert(is(Promoted!(shared bool) == shared int)); -} - -/** - * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE inout(T)). - */ -alias InoutOf(T) = inout(T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(InoutOf!int == inout int)); -} - -/** - * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE inout(T)). - */ -alias ConstOf(T) = const(T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(ConstOf!int == const int)); -} - -/** - * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE inout(T)). - */ -alias SharedOf(T) = shared(T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(SharedOf!int == shared int)); -} - -/** - * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE inout(T)). - */ -alias SharedInoutOf(T) = shared(inout T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(SharedInoutOf!int == shared inout int)); -} - -/** - * Adds $(D_KEYWORD shared const) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE shared(const T)). - */ -alias SharedConstOf(T) = shared(const T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(SharedConstOf!int == shared const int)); -} - -/** - * Adds $(D_KEYWORD immutable) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE immutable(T)). - */ -alias ImmutableOf(T) = immutable(T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(ImmutableOf!int == immutable int)); -} - -/** - * Adds $(D_KEYWORD inout const) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE inout(const T)). - */ -alias InoutConstOf(T) = inout(const T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(InoutConstOf!int == inout const int)); -} - -/** - * Adds $(D_KEYWORD shared inout const) qualifier to the type $(D_PARAM T). - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE shared(inout const T)). - */ -alias SharedInoutConstOf(T) = shared(inout const T); - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(SharedInoutConstOf!int == shared inout const int)); -} - -/** - * Returns a template with one argument which applies all qualifiers of - * $(D_PARAM T) on its argument if instantiated. - * - * Params: - * T = A type. - * - * Returns: $(D_INLINECODE shared(inout const T)). - */ -template QualifierOf(T) -{ - static if (is(T U == const U)) - { - alias QualifierOf = ConstOf; - } - else static if (is(T U == immutable U)) - { - alias QualifierOf = ImmutableOf; - } - else static if (is(T U == inout U)) - { - alias QualifierOf = InoutOf; - } - else static if (is(T U == inout const U)) - { - alias QualifierOf = InoutConstOf; - } - else static if (is(T U == shared U)) - { - alias QualifierOf = SharedOf; - } - else static if (is(T U == shared const U)) - { - alias QualifierOf = SharedConstOf; - } - else static if (is(T U == shared inout U)) - { - alias QualifierOf = SharedInoutOf; - } - else static if (is(T U == shared inout const U)) - { - alias QualifierOf = SharedInoutConstOf; - } - else - { - alias QualifierOf(T) = T; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - alias MutableOf = QualifierOf!int; - static assert(is(MutableOf!uint == uint)); - - alias ConstOf = QualifierOf!(const int); - static assert(is(ConstOf!uint == const uint)); - - alias InoutOf = QualifierOf!(inout int); - static assert(is(InoutOf!uint == inout uint)); - - alias InoutConstOf = QualifierOf!(inout const int); - static assert(is(InoutConstOf!uint == inout const uint)); - - alias ImmutableOf = QualifierOf!(immutable int); - static assert(is(ImmutableOf!uint == immutable uint)); - - alias SharedOf = QualifierOf!(shared int); - static assert(is(SharedOf!uint == shared uint)); - - alias SharedConstOf = QualifierOf!(shared const int); - static assert(is(SharedConstOf!uint == shared const uint)); - - alias SharedInoutOf = QualifierOf!(shared inout int); - static assert(is(SharedInoutOf!uint == shared inout uint)); - - alias SharedInoutConstOf = QualifierOf!(shared inout const int); - static assert(is(SharedInoutConstOf!uint == shared inout const uint)); -} - -/** - * Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type, - * $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T). - * - * $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments. - * - * The symbols that don't have a type and aren't types cannot be used as - * arguments to $(D_PSYMBOL TypeOf). - * - * Params: - * T = Expression, type or template. - * - * Returns: The type of $(D_PARAM T). - */ -alias TypeOf(T) = T; - -/// ditto -template TypeOf(alias T) -if (isExpressions!T || __traits(isTemplate, T)) -{ - alias TypeOf = typeof(T); -} - -/// -@nogc nothrow pure @safe unittest -{ - struct S(T) - { - } - static assert(is(TypeOf!S == void)); - static assert(is(TypeOf!int == int)); - static assert(is(TypeOf!true == bool)); - static assert(!is(TypeOf!(tanya.meta))); -} - -// e.g. returns int for int**. -private template FinalPointerTarget(T) -{ - static if (isPointer!T) - { - alias FinalPointerTarget = FinalPointerTarget!(PointerTarget!T); - } - else - { - alias FinalPointerTarget = T; - } -} - -// Returns true if T1 is void* and T2 is some pointer. -private template voidAndPointer(T1, T2) -{ - enum bool voidAndPointer = is(Unqual!(PointerTarget!T1) == void) - && isPointer!T2; -} - -// Type returned by the ternary operator. -private alias TernaryType(T, U) = typeof(true ? T.init : U.init); - -/** - * Determines the type all $(D_PARAM Args) can be implicitly converted to. - * - * $(OL - * $(LI If one of the arguments is $(D_KEYWORD void), the common type is - * $(D_KEYWORD void).) - * $(LI The common type of integers with the same sign is the type with a - * larger size. Signed and unsigned integers don't have a common type. - * Type qualifiers are only preserved if all arguments are the same - * type.) - * $(LI The common type of floating point numbers is the type with more - * precision. Type qualifiers are only preserved if all arguments are - * the same type.) - * $(LI The common type of polymorphic objects is the next, more generic type - * both objects inherit from, e.g. $(D_PSYMBOL Object).) - * $(LI `void*` is concerned as a common type of pointers only if one of the - * arguments is a void pointer.) - * $(LI Other types have a common type only if their pointers have a common - * type. It means that for example $(D_KEYWORD bool) and $(D_KEYWORD int) - don't have a common type. If the types fullfill this condition, the - common type is determined with the ternary operator, i.e. - `typeof(true ? T1.init : T2.init)` is evaluated.) - * ) - * - * If $(D_PARAM Args) don't have a common type, $(D_PSYMBOL CommonType) is - * $(D_KEYWORD void). - * - * Params: - * Args = Type list. - * - * Returns: Common type for $(D_PARAM Args) or $(D_KEYWORD void) if - * $(D_PARAM Args) don't have a common type. - */ -template CommonType(Args...) -if (allSatisfy!(isType, Args)) -{ - static if (Args.length == 0 - || is(Unqual!(Args[0]) == void) - || is(Unqual!(Args[1]) == void)) - { - alias CommonType = void; - } - else static if (Args.length == 1) - { - alias CommonType = Args[0]; - } - else - { - private alias Pair = Args[0 .. 2]; - private enum bool sameSigned = allSatisfy!(isIntegral, Pair) - && isSigned!(Args[0]) == isSigned!(Args[1]); - - static if (is(Args[0] == Args[1])) - { - alias CommonType = CommonType!(Args[0], Args[2 .. $]); - } - else static if (sameSigned || allSatisfy!(isFloatingPoint, Pair)) - { - alias CommonType = CommonType!(Unqual!(Largest!Pair), - Args[2 .. $]); - } - else static if (voidAndPointer!Pair - || voidAndPointer!(Args[1], Args[0])) - { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=15557. - // Determine the qualifiers returned by the ternary operator as if - // both pointers were int*. Then copy the qualifiers to void*. - alias P1 = CopyTypeQualifiers!(FinalPointerTarget!(Args[0]), int)*; - alias P2 = CopyTypeQualifiers!(FinalPointerTarget!(Args[1]), int)*; - static if (is(TernaryType!(P1, P2) U)) - { - alias CommonType = CopyTypeQualifiers!(PointerTarget!U, void)*; - } - else - { - alias CommonType = void; - } - } - else static if ((isPointer!(Args[0]) || isPolymorphicType!(Args[0])) - && is(TernaryType!Pair U)) - { - alias CommonType = CommonType!(U, Args[2 .. $]); - } - else static if (is(TernaryType!(Args[0]*, Args[1]*))) - { - alias CommonType = CommonType!(TernaryType!Pair, Args[2 .. $]); - } - else - { - alias CommonType = void; - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(CommonType!(int, int, int) == int)); - static assert(is(CommonType!(ubyte, ushort, uint) == uint)); - static assert(is(CommonType!(int, uint) == void)); - - static assert(is(CommonType!(int, const int) == int)); - static assert(is(CommonType!(const int, const int) == const int)); - - static assert(is(CommonType!(int[], const(int)[]) == const(int)[])); - static assert(is(CommonType!(string, char[]) == const(char)[])); - - class A - { - } - static assert(is(CommonType!(const A, Object) == const Object)); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(CommonType!(void*, int*) == void*)); - static assert(is(CommonType!(void*, const(int)*) == const(void)*)); - static assert(is(CommonType!(void*, const(void)*) == const(void)*)); - static assert(is(CommonType!(int*, void*) == void*)); - static assert(is(CommonType!(const(int)*, void*) == const(void)*)); - static assert(is(CommonType!(const(void)*, void*) == const(void)*)); - - static assert(is(CommonType!() == void)); - static assert(is(CommonType!(int*, const(int)*) == const(int)*)); - static assert(is(CommonType!(int**, const(int)**) == const(int*)*)); - - static assert(is(CommonType!(float, double) == double)); - static assert(is(CommonType!(float, int) == void)); - - static assert(is(CommonType!(bool, const bool) == bool)); - static assert(is(CommonType!(int, bool) == void)); - static assert(is(CommonType!(int, void) == void)); - static assert(is(CommonType!(Object, void*) == void)); - - class A - { - } - static assert(is(CommonType!(A, Object) == Object)); - static assert(is(CommonType!(const(A)*, Object*) == const(Object)*)); - static assert(is(CommonType!(A, typeof(null)) == A)); - - class B : A - { - } - class C : A - { - } - static assert(is(CommonType!(B, C) == A)); - - static struct S - { - int opCast(T : int)() - { - return 1; - } - } - static assert(is(CommonType!(S, int) == void)); - static assert(is(CommonType!(const S, S) == const S)); -} - -/** - * Finds the type with the smallest size in the $(D_PARAM Args) list. If - * several types have the same type, the leftmost is returned. - * - * Params: - * Args = Type list. - * - * Returns: The smallest type. - * - * See_Also: $(D_PSYMBOL Largest). - */ -template Smallest(Args...) -if (Args.length >= 1) -{ - static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property"); - - static if (Args.length == 1) - { - alias Smallest = Args[0]; - } - else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof) - { - alias Smallest = Smallest!(Args[1 .. $]); - } - else - { - alias Smallest = Args[0]; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Smallest!(int, ushort, uint, short) == ushort)); - static assert(is(Smallest!(short) == short)); - static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5])); - static assert(!is(Smallest!(short, 5))); -} - -/** - * Finds the type with the largest size in the $(D_PARAM Args) list. If several - * types have the same type, the leftmost is returned. - * - * Params: - * Args = Type list. - * - * Returns: The largest type. - * - * See_Also: $(D_PSYMBOL Smallest). - */ -template Largest(Args...) -if (Args.length >= 1) -{ - static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property"); - - static if (Args.length == 1) - { - alias Largest = Args[0]; - } - else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof) - { - alias Largest = Largest!(Args[1 .. $]); - } - else - { - alias Largest = Args[0]; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(Largest!(int, short, uint) == int)); - static assert(is(Largest!(short) == short)); - static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8])); - static assert(!is(Largest!(short, 5))); -} diff --git a/meta/tanya/meta/metafunction.d b/meta/tanya/meta/metafunction.d new file mode 100644 index 0000000..a05b5cf --- /dev/null +++ b/meta/tanya/meta/metafunction.d @@ -0,0 +1,1862 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This module is suited for computations on template arguments, both types and + * values at compile time. + * + * It contains different algorithms for iterating, searching and modifying + * template arguments. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/metafunction.d, + * tanya/meta/metafunction.d) + */ +module tanya.meta.metafunction; + +import tanya.meta.trait; +import tanya.meta.transform; + +/** + * Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred). + * + * $(D_PARAM Args) should contain at least one element. + * + * $(D_PARAM pred) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * pred = Template predicate. + * Args = Elements for which you want to find the minimum value. + * + * Returns: The minimum. + * + * See_Also: $(D_PSYMBOL isLess). + */ +template Min(alias pred, Args...) +if (Args.length > 0 && __traits(isTemplate, pred)) +{ + static if (Args.length == 1) + { + alias Min = Alias!(Args[0]); + } + else static if (isLess!(pred, Args[1], Args[0])) + { + alias Min = Min!(pred, Args[1], Args[2 .. $]); + } + else + { + alias Min = Min!(pred, Args[0], Args[2 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool cmp(alias T, alias U) = T < U; + static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3); + static assert(Min!(cmp, 8) == 8); +} + +/** + * Finds the maximum value in $(D_PARAM Args) according to $(D_PARAM pred). + * + * $(D_PARAM Args) should contain at least one element. + * + * $(D_PARAM pred) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * pred = Template predicate. + * Args = Elements for which you want to find the maximum value. + * + * Returns: The maximum. + * + * See_Also: $(D_PSYMBOL isLess). + */ +template Max(alias pred, Args...) +if (Args.length > 0 && __traits(isTemplate, pred)) +{ + static if (Args.length == 1) + { + alias Max = Alias!(Args[0]); + } + else static if (isGreater!(pred, Args[1], Args[0])) + { + alias Max = Max!(pred, Args[1], Args[2 .. $]); + } + else + { + alias Max = Max!(pred, Args[0], Args[2 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool cmp(alias T, alias U) = T < U; + static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13); + static assert(Max!(cmp, 8) == 8); +} + +/** + * Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f). + * + * Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the + * $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith) + * produces a sequence: + * + * --- + * f(t1[0], t2[0], ... tk[0]), + * f(t1[1], t2[1], ... tk[1]), + * ... + * f(tk[0], tk[1], ... tk[i]), + * --- + * + * $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs) + * and applies $(D_PARAM f) to them, then it takes the second + * ones and does the same, and so on. + * + * If not all argument tuples have the same length, $(D_PSYMBOL ZipWith) will + * zip only `n` elements from each tuple, where `n` is the length of the + * shortest tuple in the argument list. Remaining elements in the longer tuples + * are just ignored. + * + * Params: + * f = Some template that can be applied to the elements of + * $(D_PARAM Packs). + * Packs = $(D_PSYMBOL Pack) instances. + * + * Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element + * from each of the $(D_PARAM Packs). + */ +template ZipWith(alias f, Packs...) +if (Packs.length > 0 + && __traits(isTemplate, f) + && (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs) + || allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs))) +{ + private template GetIth(size_t i, Args...) + { + static if ((Args.length == 0) || (Args[0].Seq.length <= i)) + { + alias GetIth = AliasSeq!(); + } + else + { + alias GetIth = AliasSeq!(Args[0].Seq[i], GetIth!(i, Args[1 .. $])); + } + } + private template Iterate(size_t i, Args...) + { + alias Pack = GetIth!(i, Args); + + static if (Pack.length < Packs.length) + { + alias Iterate = AliasSeq!(); + } + else + { + alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args)); + } + } + alias ZipWith = Iterate!(0, Packs); +} + +/// +@nogc nothrow pure @safe unittest +{ + alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10)); + static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10)); + + alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5)); + static assert(Result2 == AliasSeq!(1, 4, 2, 5)); + + alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5)); + static assert(Result3.length == 0); +} + +/** + * Holds a typed sequence of template parameters. + * + * Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack + * its template parameters automatically. Consider: + * + * --- + * template A(Args...) + * { + * static assert(Args.length == 4); + * } + * + * alias AInstance = A!(AliasSeq!(int, uint), AliasSeq!(float, double)); + * --- + * + * Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2, + * because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters. + * + * With $(D_PSYMBOL Pack) it is possible to pass distinguishable + * sequences of parameters to a template. So: + * + * --- + * template B(Args...) + * { + * static assert(Args.length == 2); + * } + * + * alias BInstance = B!(Pack!(int, uint), Pack!(float, double)); + * --- + * + * Params: + * Args = Elements of this $(D_PSYMBOL Pack). + * + * See_Also: $(D_PSYMBOL AliasSeq). + */ +struct Pack(Args...) +{ + /// Elements in this tuple as $(D_PSYMBOL AliasSeq). + alias Seq = Args; + + /// The length of the tuple. + enum size_t length = Args.length; + + alias Seq this; +} + +/// +@nogc nothrow pure @safe unittest +{ + alias A = Pack!short; + alias B = Pack!(3, 8, 9); + alias C = Pack!(A, B); + + static assert(C.length == 2); + + static assert(A.length == 1); + static assert(is(A.Seq == AliasSeq!short)); + static assert(B.length == 3); + static assert(B.Seq == AliasSeq!(3, 8, 9)); + + alias D = Pack!(); + static assert(D.length == 0); + static assert(is(D.Seq == AliasSeq!())); +} + +/** + * Unordered sequence of unique aliases. + * + * $(D_PARAM Args) can contain duplicates, but they will be filtered out, so + * $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used + * for determining if two items are equal. + * + * Params: + * Args = Elements of this $(D_PSYMBOL Set). + */ +struct Set(Args...) +{ + /// Elements in this set as $(D_PSYMBOL AliasSeq). + alias Seq = NoDuplicates!Args; + + /// The length of the set. + enum size_t length = Seq.length; + + alias Seq this; +} + +/// +@nogc nothrow pure @safe unittest +{ + alias S1 = Set!(int, 5, 5, int, 4); + static assert(S1.length == 3); +} + +/** + * Produces a $(D_PSYMBOL Set) containing all elements of the given + * $(D_PARAM Sets). + * + * Params: + * Sets = List of $(D_PSYMBOL Set) instances. + * + * Returns: Set-theoretic union of all $(D_PARAM Sets). + * + * See_Also: $(D_PSYMBOL Set). + */ +template Union(Sets...) +if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets)) +{ + private template Impl(Sets...) + { + static if (Sets.length == 0) + { + alias Impl = AliasSeq!(); + } + else + { + alias Impl = AliasSeq!(Sets[0].Seq, Impl!(Sets[1 .. $])); + } + } + alias Union = Set!(Impl!Sets); +} + +/// +@nogc nothrow pure @safe unittest +{ + alias S1 = Set!(2, 5, 8, 4); + alias S2 = Set!(3, 8, 4, 1); + static assert(Union!(S1, S2).Seq == AliasSeq!(2, 5, 8, 4, 3, 1)); +} + +/** + * Produces a $(D_PSYMBOL Set) that containing elements of + * $(D_INLINECODE Sets[0]) that are also elements of all other sets in + * $(D_PARAM Sets). + * + * Params: + * Sets = List of $(D_PSYMBOL Set) instances. + * + * Returns: Set-theoretic intersection of all $(D_PARAM Sets). + * + * See_Also: $(D_PSYMBOL Set). + */ +template Intersection(Sets...) +if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets)) +{ + private template Impl(Args...) + if (Args.length > 0) + { + alias Equal = ApplyLeft!(isEqual, Args[0]); + static if (Args.length == 1) + { + enum bool Impl = true; + } + else static if (!anySatisfy!(Equal, Args[1].Seq)) + { + enum bool Impl = false; + } + else + { + enum bool Impl = Impl!(Args[0], Args[2 .. $]); + } + } + + private enum bool FilterImpl(Args...) = Impl!(Args[0], Sets[1 .. $]); + + static if (Sets.length == 0) + { + alias Intersection = Set!(); + } + else + { + alias Intersection = Set!(Filter!(FilterImpl, Sets[0].Seq)); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias S1 = Set!(2, 5, 8, 4); + alias S2 = Set!(3, 8, 4, 1); + static assert(Intersection!(S1, S2).Seq == AliasSeq!(8, 4)); + + static assert(Intersection!(S1).Seq == AliasSeq!(2, 5, 8, 4)); + static assert(Intersection!().length == 0); +} + +/** + * Produces a $(D_PSYMBOL Set) that contains all elements of + * $(D_PARAM S1) that are not members of $(D_PARAM S2). + * + * Params: + * S1 = A $(D_PSYMBOL Set). + * S2 = A $(D_PSYMBOL Set). + * + * Returns: Set-theoretic difference of two sets $(D_PARAM S1) and + * $(D_PARAM S2). + * + * See_Also: $(D_PSYMBOL Set). + */ +template Difference(alias S1, alias S2) +if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2)) +{ + private template Impl(Args...) + { + alias Equal = ApplyLeft!(isEqual, Args[0]); + enum bool Impl = !anySatisfy!(Equal, S2.Seq); + } + + static if (S1.length == 0) + { + alias Difference = Set!(); + } + else static if (S2.length == 1) + { + alias Difference = S1; + } + else + { + alias Difference = Set!(Filter!(Impl, S1.Seq)); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias S1 = Set!(2, 5, 8, 4); + alias S2 = Set!(3, 8, 4, 1); + static assert(Difference!(S1, S2).Seq == AliasSeq!(2, 5)); + static assert(Difference!(S2, S1).Seq == AliasSeq!(3, 1)); + static assert(Difference!(S1, Set!()).Seq == AliasSeq!(2, 5, 8, 4)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) is less than or equal to + * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than or equal + * to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isLessEqual(alias cmp, Args...) +if (Args.length == 2 && __traits(isTemplate, cmp)) +{ + private enum result = cmp!(Args[1], Args[0]); + static if (is(typeof(result) == bool)) + { + enum bool isLessEqual = !result; + } + else + { + enum bool isLessEqual = result >= 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool boolCmp(T, U) = T.sizeof < U.sizeof; + static assert(isLessEqual!(boolCmp, byte, int)); + static assert(isLessEqual!(boolCmp, uint, int)); + static assert(!isLessEqual!(boolCmp, long, int)); + + enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; + static assert(isLessEqual!(intCmp, byte, int)); + static assert(isLessEqual!(intCmp, uint, int)); + static assert(!isLessEqual!(intCmp, long, int)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) is greater than or equal to + * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than or + * equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isGreaterEqual(alias cmp, Args...) +if (Args.length == 2 && __traits(isTemplate, cmp)) +{ + private enum result = cmp!Args; + static if (is(typeof(result) == bool)) + { + enum bool isGreaterEqual = !result; + } + else + { + enum bool isGreaterEqual = result >= 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool boolCmp(T, U) = T.sizeof < U.sizeof; + static assert(!isGreaterEqual!(boolCmp, byte, int)); + static assert(isGreaterEqual!(boolCmp, uint, int)); + static assert(isGreaterEqual!(boolCmp, long, int)); + + enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; + static assert(!isGreaterEqual!(intCmp, byte, int)); + static assert(isGreaterEqual!(intCmp, uint, int)); + static assert(isGreaterEqual!(intCmp, long, int)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) is less than + * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than + * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isLess(alias cmp, Args...) +if (Args.length == 2 && __traits(isTemplate, cmp)) +{ + private enum result = cmp!Args; + static if (is(typeof(result) == bool)) + { + enum bool isLess = result; + } + else + { + enum bool isLess = result < 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool boolCmp(T, U) = T.sizeof < U.sizeof; + static assert(isLess!(boolCmp, byte, int)); + static assert(!isLess!(boolCmp, uint, int)); + static assert(!isLess!(boolCmp, long, int)); + + enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; + static assert(isLess!(intCmp, byte, int)); + static assert(!isLess!(intCmp, uint, int)); + static assert(!isLess!(intCmp, long, int)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) is greater than + * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE Args[0] < Args[1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE Args[0] < Args[1]), a positive number that + * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.) + * ) + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than + * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isGreater(alias cmp, Args...) +if (Args.length == 2 && __traits(isTemplate, cmp)) +{ + private enum result = cmp!Args; + static if (is(typeof(result) == bool)) + { + enum bool isGreater = !result && cmp!(Args[1], Args[0]); + } + else + { + enum bool isGreater = result > 0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum bool boolCmp(T, U) = T.sizeof < U.sizeof; + static assert(!isGreater!(boolCmp, byte, int)); + static assert(!isGreater!(boolCmp, uint, int)); + static assert(isGreater!(boolCmp, long, int)); + + enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof; + static assert(!isGreater!(intCmp, byte, int)); + static assert(!isGreater!(intCmp, uint, int)); + static assert(isGreater!(intCmp, long, int)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) is equal to $(D_INLINECODE Args[1]). + * + * $(D_PSYMBOL isEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types: + * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are + * considered to be not equal. + * + * If two items cannot be compared (for example comparing a type with a + * number), they are considered not equal. + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is equal to + * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isEqual(Args...) +if (Args.length == 2) +{ + static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) + || (isTypeTuple!Args && is(Args[0] == Args[1])) + || __traits(isSame, Args[0], Args[1])) + { + enum bool isEqual = true; + } + else + { + enum bool isEqual = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isEqual!(int, int)); + static assert(isEqual!(8, 8)); + static assert(!isEqual!(int, const(int))); + static assert(!isEqual!(5, int)); + static assert(!isEqual!(5, 8)); +} + +/** + * Tests whether $(D_INLINECODE Args[0]) isn't equal to + * $(D_INLINECODE Args[1]). + * + * $(D_PSYMBOL isNotEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types: + * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are + * considered to be not equal. + * + * Params: + * Args = Two aliases to compare for equality. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) isn't equal to + * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. + */ +template isNotEqual(Args...) +if (Args.length == 2) +{ + enum bool isNotEqual = !isEqual!Args; +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(!isNotEqual!(int, int)); + static assert(isNotEqual!(5, int)); + static assert(isNotEqual!(5, 8)); +} + +/** + * Instantiates the template $(D_PARAM T) with $(D_PARAM Args). + * + * Params: + * T = Template. + * Args = Template parameters. + * + * Returns: Instantiated template. + */ +alias Instantiate(alias T, Args...) = T!Args; + +/// +@nogc nothrow pure @safe unittest +{ + template Template(T) + { + alias Template = T; + } + alias Seq = AliasSeq!(Template, Template); + + alias Instance1 = Instantiate!(Seq[0], int); + static assert(is(Instance1 == int)); + + alias Instance2 = Instantiate!(Seq[1], float); + static assert(is(Instance2 == float)); +} + +/** + * Creates an alias for $(D_PARAM T). + * + * In contrast to the $(D_KEYWORD alias)-keyword $(D_PSYMBOL Alias) can alias + * any kind of D symbol that can be used as argument to template alias + * parameters. + * + * $(UL + * $(LI Types) + * $(LI Local and global names) + * $(LI Module names) + * $(LI Template names) + * $(LI Template instance names) + * $(LI Literals) + * ) + * + * Params: + * T = A symbol. + * + * Returns: An alias for $(D_PARAM T). + * + * See_Also: $(LINK2 https://dlang.org/spec/template.html#aliasparameters, + * Template Alias Parameters). + */ +alias Alias(alias T) = T; + +/// ditto +alias Alias(T) = T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Alias!int)); + + static assert(is(typeof(Alias!5))); + static assert(is(typeof(Alias!(() {})))); + + int i; + static assert(is(typeof(Alias!i))); +} + +/** + * Holds a sequence of aliases. + * + * $(D_PSYMBOL AliasSeq) can be used to pass multiple parameters to a template + * at once. $(D_PSYMBOL AliasSeq) behaves as it were just $(D_PARAM Args). Note + * that because of this property, if multiple instances of + * $(D_PSYMBOL AliasSeq) are passed to a template, they are not distinguishable + * from each other and act as a single sequence. There is also no way to make + * $(D_PSYMBOL AliasSeq) nested, it always unpacks its elements. + * + * Params: + * Args = Symbol sequence. + * + * Returns: An alias for sequence $(D_PARAM Args). + * + * See_Also: $(D_PSYMBOL Alias). + */ +alias AliasSeq(Args...) = Args; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof({ alias T = AliasSeq!(short, 5); }))); + static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); }))); + static assert(is(typeof({ alias T = AliasSeq!(() {}, short, 5); }))); + static assert(is(typeof({ alias T = AliasSeq!(); }))); + + static assert(AliasSeq!().length == 0); + static assert(AliasSeq!(int, short, 5).length == 3); + + alias A = AliasSeq!(short, float); + alias B = AliasSeq!(ushort, double); + alias C = AliasSeq!(A, B); + static assert(C.length == 4); +} + +/** + * Tests whether all the items of $(D_PARAM L) satisfy the condition + * $(D_PARAM F). + * + * $(D_PARAM F) is a template that accepts one parameter and returns a boolean, + * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called. + * + * Params: + * F = Template predicate. + * L = List of items to test. + * + * Returns: $(D_KEYWORD true) if all the items of $(D_PARAM L) satisfy + * $(D_PARAM F), $(D_KEYWORD false) otherwise. + */ +template allSatisfy(alias F, L...) +if (__traits(isTemplate, F)) +{ + static if (L.length == 0) + { + enum bool allSatisfy = true; + } + else static if (F!(L[0])) + { + enum bool allSatisfy = allSatisfy!(F, L[1 .. $]); + } + else + { + enum bool allSatisfy = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(allSatisfy!(isSigned, int, short, byte, long)); + static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong)); +} + +/** + * Tests whether any of the items of $(D_PARAM L) satisfy the condition + * $(D_PARAM F). + * + * $(D_PARAM F) is a template that accepts one parameter and returns a boolean, + * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called. + * + * Params: + * F = Template predicate. + * L = List of items to test. + * + * Returns: $(D_KEYWORD true) if any of the items of $(D_PARAM L) satisfy + * $(D_PARAM F), $(D_KEYWORD false) otherwise. + */ +template anySatisfy(alias F, L...) +if (__traits(isTemplate, F)) +{ + static if (L.length == 0) + { + enum bool anySatisfy = false; + } + else static if (F!(L[0])) + { + enum bool anySatisfy = true; + } + else + { + enum bool anySatisfy = anySatisfy!(F, L[1 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(anySatisfy!(isSigned, int, short, byte, long)); + static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong)); + static assert(!anySatisfy!(isSigned, uint, ushort, ulong)); +} + +private template indexOf(ptrdiff_t i, Args...) +if (Args.length > 0) +{ + static if (Args.length == 1) + { + enum ptrdiff_t indexOf = -1; + } + else static if (isEqual!(Args[0 .. 2])) + { + enum ptrdiff_t indexOf = i; + } + else + { + enum ptrdiff_t indexOf = indexOf!(i + 1, + AliasSeq!(Args[0], Args[2 .. $])); + } +} + +/** + * Returns the index of the first occurrence of $(D_PARAM T) in $(D_PARAM L). + * `-1` is returned if $(D_PARAM T) is not found. + * + * Params: + * T = The item to search for. + * L = Symbol sequence. + * + * Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L). + */ +template staticIndexOf(T, L...) +{ + enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L)); +} + +/// ditto +template staticIndexOf(alias T, L...) +{ + enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L)); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(staticIndexOf!(int) == -1); + static assert(staticIndexOf!(int, int) == 0); + static assert(staticIndexOf!(int, float, double, int, real) == 2); + static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3); +} + +/** + * Looks for $(D_PARAM T) in $(D_PARAM L) and returns $(D_KEYWORD true) if it + * could be found and $(D_KEYWORD false) otherwise. + * + * Params: + * T = The item to search for. + * L = Symbol sequence. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L), + * $(D_KEYWORD false) otherwise. + */ +template canFind(T, L...) +{ + enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1; +} + +/// ditto +template canFind(alias T, L...) +{ + enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1; +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(!canFind!(int)); + static assert(canFind!(int, int)); + static assert(canFind!(int, float, double, int, real)); + static assert(canFind!(3, () {}, uint, 5, 3)); +} + +/* + * Tests whether $(D_PARAM T) is a template. + * + * $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances, + * since the latter already represent some type. Only not instantiated + * templates, i.e. that accept some template parameters, are considered + * templates. + * + * Params: + * T = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template, + * $(D_KEYWORD false) otherwise. + */ +private enum bool isTemplate(alias T) = __traits(isTemplate, T); + +/// +@nogc nothrow pure @safe unittest +{ + static struct S(T) + { + } + static assert(isTemplate!S); + static assert(!isTemplate!(S!int)); +} + +/** + * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd) + * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on. + * + * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD true). + * + * Params: + * Preds = Template predicates. + * + * Returns: The constructed template. + */ +template templateAnd(Preds...) +if (allSatisfy!(isTemplate, Preds)) +{ + template templateAnd(T...) + { + static if (Preds.length == 0) + { + enum bool templateAnd = true; + } + else static if (Instantiate!(Preds[0], T)) + { + alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); + } + else + { + enum bool templateAnd = false; + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias isMutableInt = templateAnd!(isIntegral, isMutable); + static assert(isMutableInt!int); + static assert(!isMutableInt!(const int)); + static assert(!isMutableInt!float); + + alias alwaysTrue = templateAnd!(); + static assert(alwaysTrue!int); + + alias isIntegral = templateAnd!(.isIntegral); + static assert(isIntegral!int); + static assert(isIntegral!(const int)); + static assert(!isIntegral!float); +} + +/** + * Combines multiple templates with logical OR. So $(D_PSYMBOL templateOr) + * evaluates to $(D_INLINECODE Preds[0] || Preds[1] || Preds[2]) and so on. + * + * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD false). + * + * Params: + * Preds = Template predicates. + * + * Returns: The constructed template. + */ +template templateOr(Preds...) +if (allSatisfy!(isTemplate, Preds)) +{ + template templateOr(T...) + { + static if (Preds.length == 0) + { + enum bool templateOr = false; + } + else static if (Instantiate!(Preds[0], T)) + { + enum bool templateOr = true; + } + else + { + alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias isMutableOrInt = templateOr!(isIntegral, isMutable); + static assert(isMutableOrInt!int); + static assert(isMutableOrInt!(const int)); + static assert(isMutableOrInt!float); + static assert(!isMutableOrInt!(const float)); + + alias alwaysFalse = templateOr!(); + static assert(!alwaysFalse!int); + + alias isIntegral = templateOr!(.isIntegral); + static assert(isIntegral!int); + static assert(isIntegral!(const int)); + static assert(!isIntegral!float); +} + +/** + * Params: + * pred = Template predicate. + * + * Returns: Negated $(D_PARAM pred). + */ +template templateNot(alias pred) +if (__traits(isTemplate, pred)) +{ + enum bool templateNot(T...) = !pred!T; +} + +/// +@nogc nothrow pure @safe unittest +{ + alias isNotIntegral = templateNot!isIntegral; + static assert(!isNotIntegral!int); + static assert(isNotIntegral!(char[])); +} + +/** + * Tests whether $(D_PARAM L) is sorted in ascending order according to + * $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE a[i] < a[i + 1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE a[i] < a[i + 1]), a positive number that + * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.) + * ) + * + * Params: + * cmp = Sorting template predicate. + * L = Elements to be tested. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false) + * if not. + */ +template isSorted(alias cmp, L...) +if (__traits(isTemplate, cmp)) +{ + static if (L.length <= 1) + { + enum bool isSorted = true; + } + else + { + // `L` is sorted if the both halves and the boundary values are sorted. + enum bool isSorted = isLessEqual!(cmp, L[$ / 2 - 1], L[$ / 2]) + && isSorted!(cmp, L[0 .. $ / 2]) + && isSorted!(cmp, L[$ / 2 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum cmp(T, U) = T.sizeof < U.sizeof; + static assert(isSorted!(cmp)); + static assert(isSorted!(cmp, byte)); + static assert(isSorted!(cmp, byte, ubyte, short, uint)); + static assert(!isSorted!(cmp, long, byte, ubyte, short, uint)); +} + +@nogc nothrow pure @safe unittest +{ + enum cmp(int x, int y) = x - y; + static assert(isSorted!(cmp)); + static assert(isSorted!(cmp, 1)); + static assert(isSorted!(cmp, 1, 2, 2)); + static assert(isSorted!(cmp, 1, 2, 2, 4)); + static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); + static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); + static assert(isSorted!(cmp, 32, 32)); +} + +@nogc nothrow pure @safe unittest +{ + enum cmp(int x, int y) = x < y; + static assert(isSorted!(cmp)); + static assert(isSorted!(cmp, 1)); + static assert(isSorted!(cmp, 1, 2, 2)); + static assert(isSorted!(cmp, 1, 2, 2, 4)); + static assert(isSorted!(cmp, 1, 2, 2, 4, 8)); + static assert(!isSorted!(cmp, 32, 2, 2, 4, 8)); + static assert(isSorted!(cmp, 32, 32)); +} + +/** + * Params: + * T = A template. + * Args = The first arguments for $(D_PARAM T). + * + * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as its first + * arguments. + */ +template ApplyLeft(alias T, Args...) +{ + alias ApplyLeft(U...) = T!(Args, U); +} + +/// +@nogc nothrow pure @safe unittest +{ + alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral); + static assert(allAreIntegral!(int, uint)); + static assert(!allAreIntegral!(int, float, uint)); +} + +/** + * Params: + * T = A template. + * Args = The last arguments for $(D_PARAM T). + * + * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as itslast + * arguments. + */ +template ApplyRight(alias T, Args...) +{ + alias ApplyRight(U...) = T!(U, Args); +} + +/// +@nogc nothrow pure @safe unittest +{ + alias intIs = ApplyRight!(allSatisfy, int); + static assert(intIs!(isIntegral)); + static assert(!intIs!(isUnsigned)); +} + +/** + * Params: + * n = The number of times to repeat $(D_PARAM L). + * L = The sequence to be repeated. + * + * Returns: $(D_PARAM L) repeated $(D_PARAM n) times. + */ +template Repeat(size_t n, L...) +if (n > 0) +{ + static if (n == 1) + { + alias Repeat = L; + } + else + { + alias Repeat = AliasSeq!(L, Repeat!(n - 1, L)); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int))); + static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int))); + static assert(is(Repeat!(3) == AliasSeq!())); +} + +private template ReplaceOne(L...) +{ + static if (L.length == 2) + { + alias ReplaceOne = AliasSeq!(); + } + else static if (isEqual!(L[0], L[2])) + { + alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]); + } + else + { + alias ReplaceOne = AliasSeq!(L[2], ReplaceOne!(L[0], L[1], L[3 .. $])); + } +} + +/** + * Replaces the first occurrence of $(D_PARAM T) in $(D_PARAM L) with + * $(D_PARAM U). + * + * Params: + * T = The symbol to be replaced. + * U = Replacement. + * L = List of symbols. + * + * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) replaced. + */ +template Replace(T, U, L...) +{ + alias Replace = ReplaceOne!(T, U, L); +} + +/// ditto +template Replace(alias T, U, L...) +{ + alias Replace = ReplaceOne!(T, U, L); +} + +/// ditto +template Replace(T, alias U, L...) +{ + alias Replace = ReplaceOne!(T, U, L); +} + +/// ditto +template Replace(alias T, alias U, L...) +{ + alias Replace = ReplaceOne!(T, U, L); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Replace!(int, uint, int) == AliasSeq!(uint))); + static assert(is(Replace!(int, uint, short, int, int, ushort) + == AliasSeq!(short, uint, int, ushort))); + + static assert(Replace!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 5)); +} + +private template ReplaceAllImpl(L...) +{ + static if (L.length == 2) + { + alias ReplaceAllImpl = AliasSeq!(); + } + else + { + private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]); + static if (isEqual!(L[0], L[2])) + { + alias ReplaceAllImpl = AliasSeq!(L[1], Rest); + } + else + { + alias ReplaceAllImpl = AliasSeq!(L[2], Rest); + } + } +} + +/** + * Replaces all occurrences of $(D_PARAM T) in $(D_PARAM L) with $(D_PARAM U). + * + * Params: + * T = The symbol to be replaced. + * U = Replacement. + * L = List of symbols. + * + * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) replaced. + */ +template ReplaceAll(T, U, L...) +{ + alias ReplaceAll = ReplaceAllImpl!(T, U, L); +} + +/// ditto +template ReplaceAll(alias T, U, L...) +{ + alias ReplaceAll = ReplaceAllImpl!(T, U, L); +} + +/// ditto +template ReplaceAll(T, alias U, L...) +{ + alias ReplaceAll = ReplaceAllImpl!(T, U, L); +} + +/// ditto +template ReplaceAll(alias T, alias U, L...) +{ + alias ReplaceAll = ReplaceAllImpl!(T, U, L); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint))); + static assert(is(ReplaceAll!(int, uint, short, int, int, ushort) + == AliasSeq!(short, uint, uint, ushort))); + + static assert(ReplaceAll!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 8)); +} + +/** + * Params: + * L = List of symbols. + * + * Returns: $(D_PARAM L) with elements in reversed order. + */ +template Reverse(L...) +{ + static if (L.length == 0) + { + alias Reverse = AliasSeq!(); + } + else + { + alias Reverse = AliasSeq!(L[$ - 1], Reverse!(L[0 .. $ - 1])); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte))); +} + +/** + * Applies $(D_PARAM F) to all elements of $(D_PARAM T). + * + * Params: + * F = Template predicate. + * T = List of symbols. + * + * Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them. + */ +template Map(alias F, T...) +if (__traits(isTemplate, F)) +{ + static if (T.length == 0) + { + alias Map = AliasSeq!(); + } + else + { + alias Map = AliasSeq!(F!(T[0]), Map!(F, T[1 .. $])); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Map!(Unqual, const int, immutable short) + == AliasSeq!(int, short))); +} + +/** + * Sorts $(D_PARAM L) in ascending order according to $(D_PARAM cmp). + * + * $(D_PARAM cmp) can evaluate to: + * $(UL + * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means + * $(D_INLINECODE a[i] < a[i + 1]).) + * $(LI $(D_KEYWORD int): a negative number means that + * $(D_INLINECODE a[i] < a[i + 1]), a positive number that + * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.) + * ) + * + * Merge sort is used to sort the arguments. + * + * Params: + * cmp = Sorting template predicate. + * L = Elements to be sorted. + * + * Returns: Elements of $(D_PARAM L) in ascending order. + * + * See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort). + */ +template Sort(alias cmp, L...) +if (__traits(isTemplate, cmp)) +{ + private template merge(size_t A, size_t B) + { + static if (A + B == L.length) + { + alias merge = AliasSeq!(); + } + else static if (B >= Right.length + || (A < Left.length && isLessEqual!(cmp, Left[A], Right[B]))) + { + alias merge = AliasSeq!(Left[A], merge!(A + 1, B)); + } + else + { + alias merge = AliasSeq!(Right[B], merge!(A, B + 1)); + } + } + + static if (L.length <= 1) + { + alias Sort = L; + } + else + { + private alias Left = Sort!(cmp, L[0 .. $ / 2]); + private alias Right = Sort!(cmp, L[$ / 2 .. $]); + alias Sort = merge!(0, 0); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum cmp(T, U) = T.sizeof < U.sizeof; + static assert(is(Sort!(cmp, long, short, byte, int) + == AliasSeq!(byte, short, int, long))); +} + +@nogc nothrow pure @safe unittest +{ + enum cmp(int T, int U) = T - U; + static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14) + == AliasSeq!(2, 5, 9, 10, 12, 14, 17)); +} + +private enum bool DerivedToFrontCmp(A, B) = is(A : B); + +/** + * Returns $(D_PARAM L) sorted in such a way that the most derived types come + * first. + * + * Params: + * L = Type tuple. + * + * Returns: Sorted $(D_PARAM L). + */ +template DerivedToFront(L...) +{ + alias DerivedToFront = Sort!(DerivedToFrontCmp, L); +} + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + } + class B : A + { + } + class C : B + { + } + static assert(is(DerivedToFront!(B, A, C) == AliasSeq!(C, B, A))); +} + +/** + * Returns the type from the type tuple $(D_PARAM L) that is most derived from + * $(D_PARAM T). + * + * Params: + * T = The type to compare to. + * L = Type tuple. + * + * Returns: The type most derived from $(D_PARAM T). + */ +template MostDerived(T, L...) +{ + static if (L.length == 0) + { + alias MostDerived = T; + } + else static if (is(T : L[0])) + { + alias MostDerived = MostDerived!(T, L[1 .. $]); + } + else + { + alias MostDerived = MostDerived!(L[0], L[1 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + } + class B : A + { + } + class C : B + { + } + static assert(is(MostDerived!(A, C, B) == C)); +} + +private template EraseOne(L...) +if (L.length > 0) +{ + static if (L.length == 1) + { + alias EraseOne = AliasSeq!(); + } + else static if (isEqual!(L[0 .. 2])) + { + alias EraseOne = AliasSeq!(L[2 .. $]); + } + else + { + alias EraseOne = AliasSeq!(L[1], EraseOne!(L[0], L[2 .. $])); + } +} + +/** + * Removes the first occurrence of $(D_PARAM T) from the alias sequence + * $(D_PARAL L). + * + * Params: + * T = The item to be removed. + * L = Alias sequence. + * + * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) removed. + */ +template Erase(T, L...) +{ + alias Erase = EraseOne!(T, L); +} + +/// ditto +template Erase(alias T, L...) +{ + alias Erase = EraseOne!(T, L); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint))); + static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint))); +} + +private template EraseAllImpl(L...) +{ + static if (L.length == 1) + { + alias EraseAllImpl = AliasSeq!(); + } + else static if (isEqual!(L[0 .. 2])) + { + alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]); + } + else + { + alias EraseAllImpl = AliasSeq!(L[1], EraseAllImpl!(L[0], L[2 .. $])); + } +} + +/** + * Removes all occurrences of $(D_PARAM T) from the alias sequence $(D_PARAL L). + * + * Params: + * T = The item to be removed. + * L = Alias sequence. + * + * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) removed. + */ +template EraseAll(T, L...) +{ + alias EraseAll = EraseAllImpl!(T, L); +} + +/// ditto +template EraseAll(alias T, L...) +{ + alias EraseAll = EraseAllImpl!(T, L); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint))); + static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint))); + static assert(is(EraseAll!(int, int, int) == AliasSeq!())); +} + +/** + * Returns an alias sequence which contains only items that satisfy the + * condition $(D_PARAM pred). + * + * Params: + * pred = Template predicate. + * L = Alias sequence. + * + * Returns: $(D_PARAM L) filtered so that it contains only items that satisfy + * $(D_PARAM pred). + */ +template Filter(alias pred, L...) +{ + static if (L.length == 0) + { + alias Filter = AliasSeq!(); + } + else static if (pred!(L[0])) + { + alias Filter = AliasSeq!(L[0], Filter!(pred, L[1 .. $])); + } + else + { + alias Filter = Filter!(pred, L[1 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias Given = AliasSeq!(real, int, bool, uint); + static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint))); +} + +/** + * Removes all duplicates from the alias sequence $(D_PARAM L). + * + * Params: + * L = Alias sequence. + * + * Returns: $(D_PARAM L) containing only unique items. + */ +template NoDuplicates(L...) +{ + static if (L.length == 0) + { + alias NoDuplicates = AliasSeq!(); + } + else + { + private alias Rest = NoDuplicates!(EraseAll!(L[0], L[1 .. $])); + alias NoDuplicates = AliasSeq!(L[0], Rest); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias Given = AliasSeq!(int, uint, int, short, short, uint); + static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short))); +} + +/** + * Converts an input range $(D_PARAM range) into an alias sequence. + * + * Params: + * range = Input range. + * + * Returns: Alias sequence with items from $(D_PARAM range). + */ +template aliasSeqOf(alias range) +{ + static if (isArray!(typeof(range))) + { + static if (range.length == 0) + { + alias aliasSeqOf = AliasSeq!(); + } + else + { + alias aliasSeqOf = AliasSeq!(range[0], aliasSeqOf!(range[1 .. $])); + } + } + else + { + ReturnType!(typeof(&range.front))[] toArray(typeof(range) range) + { + typeof(return) result; + foreach (r; range) + { + result ~= r; + } + return result; + } + alias aliasSeqOf = aliasSeqOf!(toArray(range)); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3)); +} + +/** + * Produces a alias sequence consisting of every $(D_PARAM n)th element of + * $(D_PARAM Args), starting with the first. + * + * Params: + * n = Step. + * Args = The items to stride. + * + * Returns: Alias sequence of every $(D_PARAM n)th element of $(D_PARAM Args). + */ +template Stride(size_t n, Args...) +if (n > 0) +{ + static if (Args.length > n) + { + alias Stride = AliasSeq!(Args[0], Stride!(n, Args[n .. $])); + } + else static if (Args.length > 0) + { + alias Stride = AliasSeq!(Args[0]); + } + else + { + alias Stride = AliasSeq!(); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7)); + static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3)); + static assert(Stride!(2, 1, 2) == AliasSeq!(1)); + static assert(Stride!(2, 1) == AliasSeq!(1)); + static assert(Stride!(1, 1, 2, 3) == AliasSeq!(1, 2, 3)); + static assert(is(Stride!3 == AliasSeq!())); +} + +/** + * Aliases itself to $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true), + * to $(D_INLINECODE T[1]) if $(D_KEYWORD false). + * + * Params: + * cond = Template predicate. + * T = Two arguments. + * + * Returns: $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true), + * $(D_INLINECODE T[1]) otherwise. + */ +template Select(bool cond, T...) +if (T.length == 2) +{ + static if (cond) + { + alias Select = T[0]; + } + else + { + alias Select = T[1]; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Select!(true, int, float) == int)); + static assert(is(Select!(false, int, float) == float)); +} + +/** + * Attaces a numeric index to each element from $(D_PARAM Args). + * + * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s) + * consisting of the index of each element and the element itself. + * + * Params: + * start = Enumeration initial value. + * Args = Enumerated sequence. + * + * See_Also: $(D_PSYMBOL Enumerate). + */ +template EnumerateFrom(size_t start, Args...) +{ + static if (Args.length == 0) + { + alias EnumerateFrom = AliasSeq!(); + } + else + { + alias EnumerateFrom = AliasSeq!(Pack!(start, Args[0]), EnumerateFrom!(start + 1, Args[1 .. $])); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(EnumerateFrom!(0, int, uint, bool).length == 3); +} + +/// +@nogc nothrow pure @safe unittest +{ + alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int), + Pack!(cast(size_t) 1, uint)); + static assert(is(EnumerateFrom!(0, int, uint) == Expected)); +} + +/** + * Attaces a numeric index to each element from $(D_PARAM Args). + * + * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s) + * consisting of the index of each element and the element itself. + * + * Params: + * Args = Enumerated sequence. + * + * See_Also: $(D_PSYMBOL EnumerateFrom). + */ +alias Enumerate(Args...) = EnumerateFrom!(0, Args); + +/// +@nogc nothrow pure @safe unittest +{ + alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int), + Pack!(cast(size_t) 1, uint)); + static assert(is(Enumerate!(int, uint) == Expected)); +} diff --git a/meta/tanya/meta/package.d b/meta/tanya/meta/package.d new file mode 100644 index 0000000..f06a166 --- /dev/null +++ b/meta/tanya/meta/package.d @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Template metaprogramming. + * + * This package contains utilities to acquire type information at compile-time, + * to transform from one type to another. It has also different algorithms for + * iterating, searching and modifying template arguments. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/package.d, + * tanya/meta/package.d) + */ +module tanya.meta; + +public import tanya.meta.metafunction; +public import tanya.meta.trait; +public import tanya.meta.transform; diff --git a/meta/tanya/meta/trait.d b/meta/tanya/meta/trait.d new file mode 100644 index 0000000..ebea9ce --- /dev/null +++ b/meta/tanya/meta/trait.d @@ -0,0 +1,3158 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Type traits. + * + * Templates in this module are used to obtain type information at compile + * time. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/trait.d, + * tanya/meta/trait.d) + */ +module tanya.meta.trait; + +import tanya.meta.metafunction; +import tanya.meta.transform; + +/** + * Determines whether $(D_PARAM T) is a wide string, i.e. consists of + * $(D_KEYWORD dchar). + * + * The character type of the string can be qualified with $(D_KEYWORD const), + * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of + * $(D_KEYWORD shared) in the character type results in returning + * $(D_KEYWORD false). + * The string itself (in contrast to its character type) can have any type + * qualifiers. + * + * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered + * strings. + * + * Params: + * T = A Type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a wide string, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isNarrowString). + */ +enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isWideString!(dchar[])); + static assert(!isWideString!(char[])); + static assert(!isWideString!(wchar[])); + + static assert(isWideString!dstring); + static assert(!isWideString!string); + static assert(!isWideString!wstring); + + static assert(isWideString!(const dstring)); + static assert(!isWideString!(const string)); + static assert(!isWideString!(const wstring)); + + static assert(isWideString!(shared dstring)); + static assert(!isWideString!(shared string)); + static assert(!isWideString!(shared wstring)); + + static assert(isWideString!(const(dchar)[])); + static assert(isWideString!(inout(dchar)[])); + static assert(!isWideString!(shared(const(dchar))[])); + static assert(!isWideString!(shared(dchar)[])); + static assert(!isWideString!(dchar[10])); +} + +/** + * Determines whether $(D_PARAM T) is a complex type. + * + * Complex types are: + * $(UL + * $(LI cfloat) + * $(LI ifloat) + * $(LI cdouble) + * $(LI idouble) + * $(LI creal) + * $(LI ireal) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a complex type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat) + || is(Unqual!(OriginalType!T) == ifloat) + || is(Unqual!(OriginalType!T) == cdouble) + || is(Unqual!(OriginalType!T) == idouble) + || is(Unqual!(OriginalType!T) == creal) + || is(Unqual!(OriginalType!T) == ireal); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isComplex!cfloat); + static assert(isComplex!ifloat); + static assert(isComplex!cdouble); + static assert(isComplex!idouble); + static assert(isComplex!creal); + static assert(isComplex!ireal); + + static assert(!isComplex!float); + static assert(!isComplex!double); + static assert(!isComplex!real); +} + +/* + * Tests whether $(D_PARAM T) is an interface. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface, + * $(D_KEYWORD false) otherwise. + */ +private enum bool isInterface(T) = is(T == interface); + +/** + * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a + * $(D_KEYWORD class) or an $(D_KEYWORD interface). + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a $(D_KEYWORD class) or an + * $(D_KEYWORD interface), $(D_KEYWORD false) otherwise. + */ +enum bool isPolymorphicType(T) = is(T == class) || is(T == interface); + +/// +@nogc nothrow pure @safe unittest +{ + interface I + { + } + static assert(isPolymorphicType!Object); + static assert(isPolymorphicType!I); + static assert(!isPolymorphicType!short); +} + +/** + * Determines whether the type $(D_PARAM T) has a static method + * named $(D_PARAM member). + * + * Params: + * T = Aggregate type. + * member = Symbol name. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM member) is a static method of + * $(D_PARAM T), $(D_KEYWORD false) otherwise. + */ +template hasStaticMember(T, string member) +{ + static if (hasMember!(T, member)) + { + alias Member = Alias!(__traits(getMember, T, member)); + + static if (__traits(isStaticFunction, Member) + || (!isFunction!Member && is(typeof(&Member)))) + { + enum bool hasStaticMember = true; + } + else + { + enum bool hasStaticMember = false; + } + } + else + { + enum bool hasStaticMember = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + int member1; + void member2() + { + } + static int member3; + static void member4() + { + } + static void function() member5; + } + static assert(!hasStaticMember!(S, "member1")); + static assert(!hasStaticMember!(S, "member2")); + static assert(hasStaticMember!(S, "member3")); + static assert(hasStaticMember!(S, "member4")); + static assert(hasStaticMember!(S, "member5")); +} + +/** + * Determines whether $(D_PARAM T) is a floating point type. + * + * Floating point types are: + * $(UL + * $(LI float) + * $(LI double) + * $(LI real) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a floating point type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isFloatingPoint(T) = is(Unqual!(OriginalType!T) == double) + || is(Unqual!(OriginalType!T) == float) + || is(Unqual!(OriginalType!T) == real); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isFloatingPoint!float); + static assert(isFloatingPoint!double); + static assert(isFloatingPoint!real); + static assert(isFloatingPoint!(const float)); + static assert(isFloatingPoint!(shared float)); + static assert(isFloatingPoint!(shared const float)); + static assert(!isFloatingPoint!int); +} + +/** + * Determines whether $(D_PARAM T) is a signed numeric type. + * + * Signed numeric types are: + * $(UL + * $(LI byte) + * $(LI short) + * $(LI int) + * $(LI long) + * $(LI float) + * $(LI double) + * $(LI real) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a signed numeric type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isUnsigned). + */ +enum bool isSigned(T) = is(Unqual!(OriginalType!T) == byte) + || is(Unqual!(OriginalType!T) == short) + || is(Unqual!(OriginalType!T) == int) + || is(Unqual!(OriginalType!T) == long) + || isFloatingPoint!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isSigned!byte); + static assert(isSigned!short); + static assert(isSigned!int); + static assert(isSigned!long); + static assert(isSigned!float); + static assert(isSigned!double); + static assert(isSigned!real); + + static assert(!isSigned!ubyte); + static assert(!isSigned!ushort); + static assert(!isSigned!uint); + static assert(!isSigned!ulong); +} + +/** + * Determines whether $(D_PARAM T) is an unsigned numeric type. + * + * Unsigned numeric types are: + * $(UL + * $(LI ubyte) + * $(LI ushort) + * $(LI uint) + * $(LI ulong) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an unsigned numeric type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isSigned). + */ +enum bool isUnsigned(T) = is(Unqual!(OriginalType!T) == ubyte) + || is(Unqual!(OriginalType!T) == ushort) + || is(Unqual!(OriginalType!T) == uint) + || is(Unqual!(OriginalType!T) == ulong); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isUnsigned!ubyte); + static assert(isUnsigned!ushort); + static assert(isUnsigned!uint); + static assert(isUnsigned!ulong); + + static assert(!isUnsigned!byte); + static assert(!isUnsigned!short); + static assert(!isUnsigned!int); + static assert(!isUnsigned!long); + static assert(!isUnsigned!float); + static assert(!isUnsigned!double); + static assert(!isUnsigned!real); +} + +/** + * Determines whether $(D_PARAM T) is an integral type. + * + * Integral types are: + * $(UL + * $(LI ubyte) + * $(LI ushort) + * $(LI uint) + * $(LI ulong) + * $(LI byte) + * $(LI short) + * $(LI int) + * $(LI long) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an integral type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isIntegral(T) = isUnsigned!T + || is(Unqual!(OriginalType!T) == byte) + || is(Unqual!(OriginalType!T) == short) + || is(Unqual!(OriginalType!T) == int) + || is(Unqual!(OriginalType!T) == long); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isIntegral!ubyte); + static assert(isIntegral!byte); + static assert(!isIntegral!float); +} + +/** + * Determines whether $(D_PARAM T) is a numeric (floating point, integral or + * complex) type. +* + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a numeric type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isIntegral!T), + * $(D_PSYMBOL isFloatingPoint), + * $(D_PSYMBOL isComplex). + */ +enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T; + +/// +@nogc nothrow pure @safe unittest +{ + alias F = float; + static assert(isNumeric!F); + static assert(!isNumeric!bool); + static assert(!isNumeric!char); + static assert(!isNumeric!wchar); +} + +/** + * Determines whether $(D_PARAM T) is a boolean type, i.e. $(D_KEYWORD bool). + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a boolean type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isBoolean!bool); + static assert(isBoolean!(shared const bool)); + static assert(!isBoolean!(ubyte)); + static assert(!isBoolean!(byte)); + + enum E : bool + { + t = true, + f = false, + } + static assert(isBoolean!E); + + static struct S1 + { + bool b; + alias b this; + } + static assert(!isBoolean!S1); + + static struct S2 + { + bool opCast(T : bool)() + { + return true; + } + } + static assert(!isBoolean!S2); +} + +/** + * Determines whether $(D_PARAM T) is a character type. + * + * Character types are: + * + * $(UL + * $(LI char) + * $(LI wchar) + * $(LI dchar) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a character type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char) + || is(Unqual!(OriginalType!T) == wchar) + || is(Unqual!(OriginalType!T) == dchar); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isSomeChar!char); + static assert(isSomeChar!wchar); + static assert(isSomeChar!dchar); + + static assert(!isSomeChar!byte); + static assert(!isSomeChar!ubyte); + static assert(!isSomeChar!short); + static assert(!isSomeChar!ushort); + static assert(!isSomeChar!int); + static assert(!isSomeChar!uint); +} + +/** + * Determines whether $(D_PARAM T) is a scalar type. + * + * Scalar types are numbers, booleans and characters. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a scalar type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isNumeric), + * $(D_PSYMBOL isBoolean), + * $(D_PSYMBOL isSomeChar). + */ +enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isScalarType!int); + static assert(!isScalarType!(int[])); +} + +/** + * Determines whether $(D_PARAM T) is a basic type. + * + * Basic types are scalar types and $(D_KEYWORD void). + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a basic type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isScalarType). + */ +enum bool isBasicType(T) = isScalarType!T || is(T : void); + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + } + class C + { + } + enum E : int + { + i = 0, + } + + static assert(isBasicType!void); + static assert(isBasicType!(shared void)); + static assert(isBasicType!E); + static assert(!isBasicType!(int*)); + static assert(!isBasicType!(void function())); + static assert(!isBasicType!C); +} + +/** + * Determines whether $(D_PARAM T) is a pointer type. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a pointer type, + * $(D_KEYWORD false) otherwise. + */ +template isPointer(T) +{ + static if (is(T U : U*)) + { + enum bool isPointer = !is(Unqual!(OriginalType!T) == typeof(null)); + } + else + { + enum bool isPointer = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isPointer!(bool*)); + static assert(isPointer!(const bool*)); + static assert(isPointer!(const shared bool*)); + static assert(!isPointer!bool); +} + +// typeof(null) is not a pointer. +@nogc nothrow pure @safe unittest +{ + static assert(!isPointer!(typeof(null))); + static assert(!isPointer!(const shared typeof(null))); + + enum typeOfNull : typeof(null) + { + null_ = null, + } + static assert(!isPointer!typeOfNull); +} + +/** + * Determines whether $(D_PARAM T) is an array type (dynamic or static, but + * not an associative one). + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an array type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isAssociativeArray). + */ +template isArray(T) +{ + static if (is(T U : U[])) + { + enum bool isArray = true; + } + else + { + enum bool isArray = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isArray!(bool[])); + static assert(isArray!(const bool[])); + static assert(isArray!(shared bool[])); + static assert(isArray!(bool[8])); + static assert(!isArray!bool); + static assert(!isArray!(bool[string])); +} + +/** + * Determines whether $(D_PARAM T) is a static array type. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a static array type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isArray). + */ +template isStaticArray(T) +{ + static if (is(T U : U[L], size_t L)) + { + enum bool isStaticArray = true; + } + else + { + enum bool isStaticArray = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isStaticArray!(bool[8])); + static assert(isStaticArray!(const bool[8])); + static assert(isStaticArray!(shared bool[8])); + static assert(!isStaticArray!(bool[])); + static assert(!isStaticArray!bool); + static assert(!isStaticArray!(bool[string])); +} + +/** + * Determines whether $(D_PARAM T) is a dynamic array type. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a dynamic array type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isArray). + */ +enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isDynamicArray!(bool[])); + static assert(isDynamicArray!(const bool[])); + static assert(isDynamicArray!(shared bool[])); + static assert(!isDynamicArray!(bool[8])); + static assert(!isDynamicArray!bool); + static assert(!isDynamicArray!(bool[string])); +} + +/** + * Determines whether $(D_PARAM T) is an associative array type. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an associative array type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isArray). + */ +template isAssociativeArray(T) +{ + static if (is(T U : U[L], L)) + { + enum bool isAssociativeArray = true; + } + else + { + enum bool isAssociativeArray = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isAssociativeArray!(bool[string])); + static assert(isAssociativeArray!(const bool[string])); + static assert(isAssociativeArray!(shared const bool[string])); + static assert(!isAssociativeArray!(bool[])); + static assert(!isAssociativeArray!(bool[8])); + static assert(!isAssociativeArray!bool); +} + +/** + * Determines whether $(D_PARAM T) is a built-in type. + * + * Built-in types are all basic types and arrays. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a built-in type, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isBasicType!T), + * $(D_PSYMBOL isArray), + * $(D_PSYMBOL isAssociativeArray). + */ +enum bool isBuiltinType(T) = isBasicType!T + || isArray!T + || isAssociativeArray!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isBuiltinType!int); + static assert(isBuiltinType!(int[])); + static assert(isBuiltinType!(int[int])); + static assert(!isBuiltinType!(int*)); +} + +/** + * Determines whether $(D_PARAM T) is an aggregate type. + * + * Aggregate types are: + * + * $(UL + * $(LI $(D_KEYWORD struct)s) + * $(LI $(D_KEYWORD class)es) + * $(LI $(D_KEYWORD interface)s) + * $(LI $(D_KEYWORD union)s) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an aggregate type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isAggregateType(T) = is(T == struct) + || is(T == class) + || is(T == interface) + || is(T == union); + +/// +@nogc nothrow pure @safe unittest +{ + static struct S; + class C; + interface I; + union U; + enum E; + + static assert(isAggregateType!S); + static assert(isAggregateType!C); + static assert(isAggregateType!I); + static assert(isAggregateType!U); + static assert(!isAggregateType!E); + static assert(!isAggregateType!void); +} + +/** + * Determines whether $(D_PARAM T) is a narrow string, i.e. consists of + * $(D_KEYWORD char) or $(D_KEYWORD wchar). + * + * The character type of the string can be qualified with $(D_KEYWORD const), + * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of + * $(D_KEYWORD shared) in the character type results in returning + * $(D_KEYWORD false). + * The string itself (in contrast to its character type) can have any type + * qualifiers. + * + * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered + * strings. + * + * Params: + * T = A Type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a narrow string, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isWideString). + */ +enum bool isNarrowString(T) = (is(T : const char[]) || is (T : const wchar[])) + && !isStaticArray!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isNarrowString!(char[])); + static assert(isNarrowString!(wchar[])); + static assert(!isNarrowString!(dchar[])); + + static assert(isNarrowString!string); + static assert(isNarrowString!wstring); + static assert(!isNarrowString!dstring); + + static assert(isNarrowString!(const string)); + static assert(isNarrowString!(const wstring)); + static assert(!isNarrowString!(const dstring)); + + static assert(isNarrowString!(shared string)); + static assert(isNarrowString!(shared wstring)); + static assert(!isNarrowString!(shared dstring)); + + static assert(isNarrowString!(const(char)[])); + static assert(isNarrowString!(inout(char)[])); + static assert(!isNarrowString!(shared(const(char))[])); + static assert(!isNarrowString!(shared(char)[])); + static assert(!isNarrowString!(char[10])); +} + +/** + * Determines whether $(D_PARAM T) is a string, i.e. consists of + * $(D_KEYWORD char), $(D_KEYWORD wchar) or $(D_KEYWORD dchar). + * + * The character type of the string can be qualified with $(D_KEYWORD const), + * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of + * $(D_KEYWORD shared) in the character type results in returning + * $(D_KEYWORD false). + * The string itself (in contrast to its character type) can have any type + * qualifiers. + * + * Static character arrays are not considered strings. + * + * Params: + * T = A Type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a string, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isNarrowString), $(D_PSYMBOL isWideString). + */ +enum bool isSomeString(T) = isNarrowString!T || isWideString!T; + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isSomeString!(dchar[])); + static assert(isSomeString!(char[])); + static assert(isSomeString!(wchar[])); + + static assert(isSomeString!dstring); + static assert(isSomeString!string); + static assert(isSomeString!wstring); + + static assert(isSomeString!(const dstring)); + static assert(isSomeString!(const string)); + static assert(isSomeString!(const wstring)); + + static assert(isSomeString!(shared dstring)); + static assert(isSomeString!(shared string)); + static assert(isSomeString!(shared wstring)); + + static assert(isSomeString!(const(char)[])); + static assert(isSomeString!(inout(char)[])); + static assert(!isSomeString!(shared(const(char))[])); + static assert(!isSomeString!(shared(char)[])); + static assert(!isSomeString!(char[10])); +} + +/** + * Returns the minimum value of type $(D_PARAM T). In contrast to + * $(D_INLINECODE T.min) this template works with floating point and complex + * types as well. + * + * Params: + * T = Integral, boolean, floating point, complex or character type. + * + * Returns: The minimum value of $(D_PARAM T). + * + * See_Also: $(D_PSYMBOL isIntegral), + * $(D_PSYMBOL isBoolean), + * $(D_PSYMBOL isSomeChar), + * $(D_PSYMBOL isFloatingPoint), + * $(D_PSYMBOL isComplex). + */ +template mostNegative(T) +{ + static if (isIntegral!T || isBoolean!T || isSomeChar!T) + { + enum T mostNegative = T.min; + } + else static if (isFloatingPoint!T || isComplex!T) + { + enum T mostNegative = -T.max; + } + else + { + static assert(false, T.stringof ~ " doesn't have the minimum value"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(mostNegative!char == char.min); + static assert(mostNegative!wchar == wchar.min); + static assert(mostNegative!dchar == dchar.min); + + static assert(mostNegative!byte == byte.min); + static assert(mostNegative!ubyte == ubyte.min); + static assert(mostNegative!bool == bool.min); + + static assert(mostNegative!float == -float.max); + static assert(mostNegative!double == -double.max); + static assert(mostNegative!real == -real.max); + + static assert(mostNegative!ifloat == -ifloat.max); + static assert(mostNegative!cfloat == -cfloat.max); +} + +/** + * Determines whether the type $(D_PARAM T) is copyable. + * + * Only structs can be not copyable if their postblit constructor or the + * postblit constructor of one of its fields is disabled, i.e. annotated with + * $(D_KEYWORD @disable). + * + * Params: + * T = A type. + * + * Returns: $(D_PARAM true) if $(D_PARAM T) can be copied, + * $(D_PARAM false) otherwise. + */ +enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; })); + +/// +@nogc nothrow pure @safe unittest +{ + static struct S1 + { + } + static struct S2 + { + this(this) + { + } + } + static struct S3 + { + @disable this(this); + } + static struct S4 + { + S3 s; + } + class C + { + } + + static assert(isCopyable!S1); + static assert(isCopyable!S2); + static assert(!isCopyable!S3); + static assert(!isCopyable!S4); + + static assert(isCopyable!C); + static assert(isCopyable!bool); +} + +/** + * Determines whether $(D_PARAM T) is an abstract class. + * + * Abstract class is a class marked as such or a class that has any abstract + * methods or doesn't implement all methods of abstract base classes. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an abstract class, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isAbstractFunction). + */ +enum bool isAbstractClass(T) = __traits(isAbstractClass, T); + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + } + abstract class B + { + } + class C + { + abstract void func(); + } + class D : C + { + } + class E : C + { + override void func() + { + } + } + static assert(!isAbstractClass!A); + static assert(isAbstractClass!B); + static assert(isAbstractClass!C); + static assert(isAbstractClass!D); + static assert(!isAbstractClass!E); +} + +/** + * Checks whether $(D_PARAM T) is a type, same as `is(T)` does. + * + * Params: + * T = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false) + * otherwise. + */ +enum bool isType(alias T) = is(T); + +/// ditto +enum bool isType(T) = true; + +/** + * Determines whether $(D_PARAM Args) contains only types. + * + * Params: + * Args = Alias sequence. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of types, + * $(D_KEYWORD false) otherwise. + */ +enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isTypeTuple!(int, uint, Object)); + static assert(isTypeTuple!()); + static assert(!isTypeTuple!(int, 8, Object)); + static assert(!isTypeTuple!(5, 8, 2)); + + class C + { + } + enum E : bool + { + t, + f, + } + union U + { + } + static struct T() + { + } + + static assert(isTypeTuple!C); + static assert(isTypeTuple!E); + static assert(isTypeTuple!U); + static assert(isTypeTuple!void); + static assert(isTypeTuple!int); + static assert(!isTypeTuple!T); + static assert(isTypeTuple!(T!())); + static assert(!isTypeTuple!5); + static assert(!isTypeTuple!(tanya.meta.trait)); +} + +/** + * Tells whether $(D_PARAM Args) contains only expressions. + * + * An expression is determined by applying $(D_KEYWORD typeof) to an argument: + * + * --- + * static if (is(typeof(Args[i]))) + * { + * // Args[i] is an expression. + * } + * else + * { + * // Args[i] is not an expression. + * } + * --- + * + * Params: + * Args = Alias sequence. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of expressions, + * $(D_KEYWORD false) otherwise. + */ +template isExpressions(Args...) +{ + static if (Args.length == 0) + { + enum bool isExpressions = true; + } + else static if (is(typeof(Args[0]) U)) + { + enum bool isExpressions = !is(U == void) + && isExpressions!(Args[1 .. $]); + } + else + { + enum bool isExpressions = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isExpressions!(5, 8, 2)); + static assert(isExpressions!()); + static assert(!isExpressions!(int, uint, Object)); + static assert(!isExpressions!(int, 8, Object)); + + template T(U) + { + } + static assert(!isExpressions!T); +} + +/** + * Determines whether $(D_PARAM T) is a final class. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a final class, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isFinalFunction). + */ +enum bool isFinalClass(T) = __traits(isFinalClass, T); + +/// +@nogc nothrow pure @safe unittest +{ + final class A + { + } + class B + { + } + + static assert(isFinalClass!A); + static assert(!isFinalClass!B); +} + +/** + * Determines whether $(D_PARAM T) is an abstract method. + * + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is an abstract method, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isAbstractClass). + */ +enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F); + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + void func() + { + } + } + class B + { + abstract void func(); + } + class C : B + { + override void func() + { + } + } + static assert(!isAbstractFunction!(A.func)); + static assert(isAbstractFunction!(B.func)); + static assert(!isAbstractFunction!(C.func)); +} + +/** + * Determines whether $(D_PARAM T) is a final method. + * + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a final method, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isFinalClass). + */ +enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F); + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + void virtualFunc() + { + } + final void finalFunc() + { + } + } + + static assert(isFinalFunction!(A.finalFunc)); + static assert(!isFinalFunction!(A.virtualFunc)); +} + +/** + * Function pointer is a pointer to a function. So a simple function is not + * a function pointer, but getting the address of such function returns a + * function pointer. + * + * A function pointer doesn't save the context pointer, thus cannot have access + * to its outer scope. + * + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function pointer, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, + * Delegates, Function Pointers, and Closures). + */ +template isFunctionPointer(F...) +if (F.length == 1) +{ + static if ((is(typeof(F[0]) T : T*) && is(T == function)) + || (is(F[0] T : T*) && is(T == function))) + { + enum bool isFunctionPointer = true; + } + else + { + enum bool isFunctionPointer = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isFunctionPointer!(void function())); + static assert(!isFunctionPointer!(void delegate())); + + static assert(isFunctionPointer!(() {})); + + void func() + { + } + static void staticFunc() + { + } + interface I + { + @property int prop(); + } + + static assert(!isFunctionPointer!func); + static assert(!isFunctionPointer!staticFunc); + + auto functionPointer = &staticFunc; + auto dg = &func; + + static assert(isFunctionPointer!functionPointer); + static assert(!isFunctionPointer!dg); + + static assert(!isFunctionPointer!(I.prop)); +} + +/** + * Delegate stores the function pointer and function context. + * + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a delegate, + * $(D_KEYWORD false) delegate. + * + * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, + * Delegates, Function Pointers, and Closures). + */ +template isDelegate(F...) +if (F.length == 1) +{ + static if (is(F[0] == delegate) + || is(typeof(F[0]) == delegate)) + { + enum bool isDelegate = true; + } + else + { + enum bool isDelegate = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isDelegate!(void delegate())); + static assert(!isDelegate!(void function())); + + static assert(!isDelegate!(() {})); + + void func() + { + } + static void staticFunc() + { + } + interface I + { + @property int prop(); + } + + static assert(!isDelegate!func); + static assert(!isDelegate!staticFunc); + + auto functionPointer = &staticFunc; + auto dg = &func; + + static assert(!isDelegate!functionPointer); + static assert(isDelegate!dg); + + static assert(!isDelegate!(I.prop)); +} + +/** + * $(D_PSYMBOL isFunction) returns $(D_KEYWORD true) only for plain functions, + * not function pointers or delegates. Use $(D_PSYMBOL isFunctionPointer) or + * $(D_PSYMBOL isDelegate) to detect them or $(D_PSYMBOL isSomeFunction) + * for detecting a function of any type. + * + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures, + * Delegates, Function Pointers, and Closures). + */ +template isFunction(F...) +if (F.length == 1) +{ + static if (is(F[0] == function) + || is(typeof(&F[0]) T == delegate) + || (is(typeof(&F[0]) T : T*) && is(T == function))) + { + enum bool isFunction = true; + } + else + { + enum bool isFunction = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(!isFunction!(void function())); + static assert(!isFunction!(() {})); + static assert(!isFunction!(void delegate())); + + void func() + { + } + static void staticFunc() + { + } + interface I + { + @property int prop(); + } + + static assert(isFunction!func); + static assert(isFunction!staticFunc); + + auto functionPointer = &staticFunc; + auto dg = &func; + + static assert(!isFunction!functionPointer); + static assert(!isFunction!dg); + + static assert(isFunction!(I.prop)); +} + +/** + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, function pointer + * or delegate, $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL isFunction), + * $(D_PSYMBOL isDelegate), + * $(D_PSYMBOL isFunctionPointer). + */ +template isSomeFunction(F...) +if (F.length == 1) +{ + enum bool isSomeFunction = isFunctionPointer!F + || isFunction!F + || isDelegate!F; +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isSomeFunction!(void function())); + static assert(isSomeFunction!(() {})); + static assert(isSomeFunction!(void delegate())); + + void func() + { + } + static void staticFunc() + { + } + + static assert(isSomeFunction!func); + static assert(isSomeFunction!staticFunc); + + auto functionPointer = &staticFunc; + auto dg = &func; + + static assert(isSomeFunction!functionPointer); + static assert(isSomeFunction!dg); + + static assert(!isSomeFunction!int); +} + +/** + * Params: + * F = A symbol. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM F) is callable, + * $(D_KEYWORD false) otherwise. + */ +template isCallable(F...) +if (F.length == 1) +{ + static if (isSomeFunction!F + || (is(typeof(F[0].opCall)) && isFunction!(F[0].opCall))) + { + enum bool isCallable = true; + } + else + { + enum bool isCallable = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + void opCall() + { + } + } + class C + { + static void opCall() + { + } + } + interface I + { + } + S s; + + static assert(isCallable!s); + static assert(isCallable!C); + static assert(isCallable!S); + static assert(!isCallable!I); +} + +@nogc nothrow pure @safe unittest +{ + static struct S + { + @property int opCall() + { + return 0; + } + } + S s; + static assert(isCallable!S); + static assert(isCallable!s); +} + +/** + * Determines whether $(D_PARAM T) defines a symbol $(D_PARAM member). + * + * Params: + * T = Aggregate type. + * member = Symbol name. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol + * $(D_PARAM member), $(D_KEYWORD false) otherwise. + */ +enum bool hasMember(T, string member) = __traits(hasMember, T, member); + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + int member1; + void member2() + { + } + static int member3; + static void member4() + { + } + } + static assert(hasMember!(S, "member1")); + static assert(hasMember!(S, "member2")); + static assert(hasMember!(S, "member3")); + static assert(hasMember!(S, "member4")); + static assert(!hasMember!(S, "member6")); +} + +/** + * Determines whether $(D_PARAM T) is mutable, i.e. has one of the following + * qualifiers or a combination of them: + * + * $(UL + * $(LI $(D_KEYWORD const)) + * $(LI $(D_KEYWORD immutable)) + * $(LI $(D_KEYWORD const)) + * ) + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is mutable, + * $(D_KEYWORD false) otherwise. + */ +template isMutable(T) +{ + static if (is(T U == const U) + || is(T U == inout U) + || is(T U == inout const U) + || is(T U == immutable U) + || is(T U == shared const U) + || is(T U == shared inout U) + || is(T U == shared inout const U)) + { + enum bool isMutable = false; + } + else + { + enum bool isMutable = true; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S + { + void method() + { + static assert(isMutable!(typeof(this))); + } + + void method() inout + { + static assert(!isMutable!(typeof(this))); + } + + void immMethod() const + { + static assert(!isMutable!(typeof(this))); + } + void immMethod() immutable + { + static assert(!isMutable!(typeof(this))); + } + } +} + +/** + * Determines whether $(D_PARAM T) is a nested type, i.e. $(D_KEYWORD class), + * $(D_KEYWORD struct) or $(D_KEYWORD union), which internally stores a context + * pointer. + * + * Params: + * T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type. + * + * Returns: $(D_KEYWORD true) if the argument is a nested type which internally + * stores a context pointer, $(D_KEYWORD false) otherwise. + */ +template isNested(T) +if (is(T == class) || is(T == struct) || is(T == union)) +{ + enum bool isNested = __traits(isNested, T); +} + +/// +@nogc pure nothrow @safe unittest +{ + static struct S + { + } + static assert(!isNested!S); + + class C + { + void method() + { + } + } + static assert(isNested!C); +} + +/** + * Determines whether $(D_PARAM T) is a nested function. + * + * Params: + * F = A function. + * + * Returns $(D_KEYWORD true) if the $(D_PARAM T) is a nested function, + * $(D_KEYWORD false) otherwise. + */ +enum bool isNestedFunction(alias F) = __traits(isNested, F); + +/// +@nogc nothrow pure @safe unittest +{ + void func() + { + void nestedFunc() + { + } + static assert(isNestedFunction!nestedFunc); + } +} + +/** + * Determines the type of the callable $(D_PARAM F). + * + * Params: + * F = A function. + * + * Returns: Type of the function $(D_PARAM F). + */ +template FunctionTypeOf(F...) +if (isCallable!F) +{ + static if ((is(typeof(F[0]) T : T*) && is(T == function)) + || (is(F[0] T : T*) && is(T == function)) + || is(F[0] T == delegate) + || is(typeof(F[0]) T == delegate) + || is(F[0] T == function) + || is(typeof(&F[0]) T == delegate) + || (is(typeof(&F[0]) T : T*) && is(T == function))) + { + alias FunctionTypeOf = T; + } + else + { + alias FunctionTypeOf = FunctionTypeOf!(F[0].opCall); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(FunctionTypeOf!(void function()) == function)); + static assert(is(FunctionTypeOf!(() {}) == function)); +} + +@nogc nothrow pure @safe unittest +{ + static assert(is(FunctionTypeOf!(void delegate()) == function)); + + static void staticFunc() + { + } + auto functionPointer = &staticFunc; + static assert(is(FunctionTypeOf!staticFunc == function)); + static assert(is(FunctionTypeOf!functionPointer == function)); + + void func() + { + } + auto dg = &func; + static assert(is(FunctionTypeOf!func == function)); + static assert(is(FunctionTypeOf!dg == function)); + + interface I + { + @property int prop(); + } + static assert(is(FunctionTypeOf!(I.prop) == function)); + + static struct S + { + void opCall() + { + } + } + class C + { + static void opCall() + { + } + } + S s; + + static assert(is(FunctionTypeOf!s == function)); + static assert(is(FunctionTypeOf!C == function)); + static assert(is(FunctionTypeOf!S == function)); +} + +@nogc nothrow pure @safe unittest +{ + static struct S2 + { + @property int opCall() + { + return 0; + } + } + S2 s2; + static assert(is(FunctionTypeOf!S2 == function)); + static assert(is(FunctionTypeOf!s2 == function)); +} + +/** + * Determines the return type of the callable $(D_PARAM F). + * + * Params: + * F = A callable object. + * + * Returns: Return type of $(D_PARAM F). + */ +template ReturnType(F...) +if (isCallable!F) +{ + static if (is(FunctionTypeOf!(F[0]) T == return)) + { + alias ReturnType = T; + } + else + { + static assert(false, "Argument is not a callable"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(ReturnType!(int delegate()) == int)); + static assert(is(ReturnType!(bool function()) == bool)); +} + +/** + * Determines the template $(D_PARAM T) is an instance of. + * + * Params: + * T = Template instance. + * + * Returns: Template $(D_PARAM T) is an instance of. + */ +alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base; + +/// +@nogc nothrow pure @safe unittest +{ + static struct S(T) + { + } + static assert(__traits(isSame, TemplateOf!(S!int), S)); + + static void func(T)() + { + } + static assert(__traits(isSame, TemplateOf!(func!int), func)); + + template T(U) + { + } + static assert(__traits(isSame, TemplateOf!(T!int), T)); +} + +/** + * Returns the mangled name of the symbol $(D_PARAM T). + * + * Params: + * T = A symbol. + * + * Returns: Mangled name of $(D_PARAM T). + */ +enum string mangledName(T) = T.mangleof; + +/// +enum string mangledName(alias T) = T.mangleof; + +/** + * Tests whether $(D_PARAM I) is an instance of template $(D_PARAM T). + * + * Params: + * T = Template. + * I = Template instance. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM I) is an instance of $(D_PARAM T), + * $(D_KEYWORD false) otherwise. + */ +enum bool isInstanceOf(alias T, I) = is(I == T!Args, Args...); + +template isInstanceOf(alias T, alias I) +{ + static if (is(typeof(TemplateOf!I))) + { + enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T); + } + else + { + enum bool isInstanceOf = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S(T) + { + } + static assert(isInstanceOf!(S, S!int)); + + static void func(T)(); + static assert(isInstanceOf!(func, func!int)); + + template T(U) + { + } + static assert(isInstanceOf!(T, T!int)); +} + +/** + * Checks whether $(D_PARAM From) is implicitly (without explicit + * $(D_KEYWORD cast)) to $(D_PARAM To). + * + * Params: + * From = Source type. + * To = Conversion target type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM From) is implicitly convertible to + * $(D_PARAM To), $(D_KEYWORD false) if not. + */ +enum bool isImplicitlyConvertible(From, To) = is(From : To); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isImplicitlyConvertible!(const(byte), byte)); + static assert(isImplicitlyConvertible!(byte, char)); + static assert(isImplicitlyConvertible!(byte, short)); + static assert(!isImplicitlyConvertible!(short, byte)); + static assert(isImplicitlyConvertible!(string, const(char)[])); +} + +/** + * Returns a tuple of base classes and interfaces of $(D_PARAM T). + * + * $(D_PSYMBOL BaseTypeTuple) returns only classes and interfaces $(D_PARAM T) + * directly inherits from, but not the base classes and interfaces of its parents. + * + * Params: + * T = Class or interface type. + * + * Returns: A tuple of base classes or interfaces of ($D_PARAM T). + * + * See_Also: $(D_PSYMBOL TransitiveBaseTypeTuple). + */ +template BaseTypeTuple(T) +if (is(T == class) || (is(T == interface))) +{ + static if (is(T Tuple == super)) + { + alias BaseTypeTuple = Tuple; + } + else + { + static assert(false, "Argument isn't a class or interface"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + interface I1 + { + } + interface I2 + { + } + interface I3 : I1, I2 + { + } + interface I4 + { + } + class A : I3, I4 + { + } + static assert(is(BaseTypeTuple!A == AliasSeq!(Object, I3, I4))); + static assert(BaseTypeTuple!Object.length == 0); +} + +/** + * Returns a tuple of all base classes and interfaces of $(D_PARAM T). + * + * $(D_PSYMBOL TransitiveBaseTypeTuple) returns first the parent class, then + * grandparent and so on. The last class is $(D_PSYMBOL Object). Then the interfaces + * follow. + * + * Params: + * T = Class or interface type. + * + * Returns: A tuple of all base classes and interfaces of ($D_PARAM T). + * + * See_Also: $(D_PSYMBOL BaseTypeTuple). + */ +template TransitiveBaseTypeTuple(T) +if (is(T == class) || is(T == interface)) +{ + private template Impl(T...) + { + static if (T.length == 0) + { + alias Impl = AliasSeq!(); + } + else + { + alias Impl = AliasSeq!(BaseTypeTuple!(T[0]), + Map!(ImplCopy, BaseTypeTuple!(T[0]))); + } + } + private alias ImplCopy = Impl; // To avoid recursive template expansion. + private enum bool cmp(A, B) = is(B == interface) && is(A == class); + + alias TransitiveBaseTypeTuple = NoDuplicates!(Sort!(cmp, Impl!T)); +} + +/// +@nogc nothrow pure @safe unittest +{ + interface I1 + { + } + interface I2 : I1 + { + } + class A : I2 + { + } + class B : A, I1 + { + } + class C : B, I2 + { + } + alias Expected = AliasSeq!(B, A, Object, I2, I1); + static assert(is(TransitiveBaseTypeTuple!C == Expected)); + + static assert(is(TransitiveBaseTypeTuple!Object == AliasSeq!())); + static assert(is(TransitiveBaseTypeTuple!I2 == AliasSeq!(I1))); +} + +/** + * Returns all the base classes of $(D_PARAM T), the direct parent class comes + * first, $(D_PSYMBOL Object) ist the last one. + * + * The only type that doesn't have any base class is $(D_PSYMBOL Object). + * + * Params: + * T = Class type. + * + * Returns: Base classes of $(D_PARAM T). + */ +template BaseClassesTuple(T) +if (is(T == class)) +{ + static if (is(T == Object)) + { + alias BaseClassesTuple = AliasSeq!(); + } + else + { + private alias Parents = BaseTypeTuple!T; + alias BaseClassesTuple = AliasSeq!(Parents[0], BaseClassesTuple!(Parents[0])); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + interface I1 + { + } + interface I2 + { + } + class A : I1, I2 + { + } + class B : A, I1 + { + } + class C : B, I2 + { + } + static assert(is(BaseClassesTuple!C == AliasSeq!(B, A, Object))); + static assert(BaseClassesTuple!Object.length == 0); +} + +/** + * Returns all the interfaces $(D_PARAM T) inherits from. + * + * Params: + * T = Class or interface type. + * + * Returns: Interfaces $(D_PARAM T) inherits from. + */ +template InterfacesTuple(T) +if (is(T == class) || is(T == interface)) +{ + alias InterfacesTuple = Filter!(isInterface, TransitiveBaseTypeTuple!T); +} + +/// +@nogc nothrow pure @safe unittest +{ + interface I1 + { + } + interface I2 : I1 + { + } + class A : I2 + { + } + class B : A, I1 + { + } + class C : B, I2 + { + } + static assert(is(InterfacesTuple!C == AliasSeq!(I2, I1))); + + static assert(is(InterfacesTuple!Object == AliasSeq!())); + static assert(is(InterfacesTuple!I1 == AliasSeq!())); +} + +/** + * Tests whether a value of type $(D_PARAM Rhs) can be assigned to a variable + * of type $(D_PARAM Lhs). + * + * If $(D_PARAM Rhs) isn't specified, $(D_PSYMBOL isAssignable) tests whether a + * value of type $(D_PARAM Lhs) can be assigned to a variable of the same type. + * + * $(D_PSYMBOL isAssignable) tells whether $(D_PARAM Rhs) can be assigned by + * value as well by reference. + * + * Params: + * Lhs = Variable type. + * Rhs = Expression type. + * + * Returns: $(D_KEYWORD true) if a value of type $(D_PARAM Rhs) can be assigned + * to a variable of type $(D_PARAM Lhs), $(D_KEYWORD false) otherwise. + */ +template isAssignable(Lhs, Rhs = Lhs) +{ + enum bool isAssignable = is(typeof({ + Lhs lhs = Lhs.init; + Rhs rhs = Rhs.init; + lhs = ((inout ref Rhs) => Rhs.init)(rhs); + })); +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct S1 + { + @disable this(); + @disable this(this); + } + static struct S2 + { + void opAssign(S1 s) pure nothrow @safe @nogc + { + } + } + static struct S3 + { + void opAssign(ref S1 s) pure nothrow @safe @nogc + { + } + } + static assert(isAssignable!(S2, S1)); + static assert(!isAssignable!(S3, S1)); + + static assert(isAssignable!(const(char)[], string)); + static assert(!isAssignable!(string, char[])); + + static assert(isAssignable!int); + static assert(!isAssignable!(const int, int)); +} + +/** + * Returns template parameters of $(D_PARAM T). + * + * Params: + * T = Template instance. + * + * Returns: Template parameters of $(D_PARAM T). + */ +alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args; + +/// +@nogc nothrow pure @safe unittest +{ + template T(A, B) + { + } + static assert(is(TemplateArgsOf!(T!(int, uint)) == AliasSeq!(int, uint))); +} + +/** + * Returns a tuple with parameter types of a function. + * + * Params: + * F = A function. + * + * Returns: Tuple with parameter types of a function. + */ +template Parameters(F...) +if (isCallable!F) +{ + static if (is(FunctionTypeOf!F T == function)) + { + alias Parameters = T; + } + else + { + static assert(false, "Function has no parameters"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + int func(Object, uint[]); + static assert(is(Parameters!func == AliasSeq!(Object, uint[]))); +} + +/** + * Returns a string array with all parameter names of a function. + * + * If a parameter has no name, an empty string is placed into array. + * + * Params: + * F = A function. + * + * Returns: Function parameter names. + */ +template ParameterIdentifierTuple(F...) +if (isCallable!F) +{ + static if (is(FunctionTypeOf!F Params == __parameters)) + { + string[] Impl() + { + string[] tuple; + + foreach (k, P; Params) + { + static if (is(typeof(__traits(identifier, Params[k .. $])))) + { + tuple ~= __traits(identifier, Params[k .. $]); + } + else + { + tuple ~= ""; + } + } + + return tuple; + } + enum string[] ParameterIdentifierTuple = Impl(); + } + else + { + static assert(false, "Function has no parameters"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + int func(ref Object stuff, uint[] = null, scope uint k = 1); + alias P = ParameterIdentifierTuple!func; + static assert(P[0] == "stuff"); + static assert(P[1] == ""); + static assert(P[2] == "k"); +} + +/// Attributes can be attached to a function. +enum FunctionAttribute : uint +{ + none = 0x0000, + pure_ = 0x0001, + nothrow_ = 0x0002, + ref_ = 0x0004, + property = 0x0008, + trusted = 0x0010, + safe = 0x0020, + nogc = 0x0040, + system = 0x0080, + const_ = 0x0100, + immutable_ = 0x0200, + inout_ = 0x0400, + shared_ = 0x0800, + return_ = 0x1000, + scope_ = 0x2000, +} + +/** + * Retrieves the attributes of the function $(D_PARAM F). + * + * The attributes are returned as a bit-mask of + * $(D_PSYMBOL FunctionAttribute) values. + * + * Params: A function. + * + * Returns: Attributes of the function $(D_PARAM F). + * + * See_Also: $(D_PSYMBOL FunctionAttribute). + */ +template functionAttributes(F...) +if (isCallable!F) +{ + uint Impl() + { + uint attrs = FunctionAttribute.none; + foreach (a; __traits(getFunctionAttributes, F[0])) + { + static if (a == "const") + { + attrs |= FunctionAttribute.const_; + } + else static if (a == "immutable") + { + attrs |= FunctionAttribute.immutable_; + } + else static if (a == "inout") + { + attrs |= FunctionAttribute.inout_; + } + else static if (a == "@nogc") + { + attrs |= FunctionAttribute.nogc; + } + else static if (a == "nothrow") + { + attrs |= FunctionAttribute.nothrow_; + } + else static if (a == "@property") + { + attrs |= FunctionAttribute.property; + } + else static if (a == "pure") + { + attrs |= FunctionAttribute.pure_; + } + else static if (a == "ref") + { + attrs |= FunctionAttribute.ref_; + } + else static if (a == "return") + { + attrs |= FunctionAttribute.return_; + } + else static if (a == "@safe") + { + attrs |= FunctionAttribute.safe; + } + else static if (a == "scope") + { + attrs |= FunctionAttribute.scope_; + } + else static if (a == "shared") + { + attrs |= FunctionAttribute.shared_; + } + else static if (a == "@system") + { + attrs |= FunctionAttribute.system; + } + else static if (a == "@trusted") + { + attrs |= FunctionAttribute.trusted; + } + } + return attrs; + } + enum uint functionAttributes = Impl(); +} + +/// +@nogc nothrow pure @safe unittest +{ + @property ref int func1() pure nothrow @safe @nogc shared scope; + static assert((functionAttributes!func1 & FunctionAttribute.pure_) + == FunctionAttribute.pure_); + static assert((functionAttributes!func1 & FunctionAttribute.nothrow_) + == FunctionAttribute.nothrow_); + static assert((functionAttributes!func1 & FunctionAttribute.safe) + == FunctionAttribute.safe); + static assert((functionAttributes!func1 & FunctionAttribute.nogc) + == FunctionAttribute.nogc); + static assert((functionAttributes!func1 & FunctionAttribute.shared_) + == FunctionAttribute.shared_); + static assert((functionAttributes!func1 & FunctionAttribute.ref_) + == FunctionAttribute.ref_); + static assert((functionAttributes!func1 & FunctionAttribute.property) + == FunctionAttribute.property); + static assert((functionAttributes!func1 & FunctionAttribute.scope_) + == FunctionAttribute.scope_); + static assert((functionAttributes!func1 & FunctionAttribute.system) == 0); + static assert((functionAttributes!func1 & FunctionAttribute.trusted) == 0); + static assert((functionAttributes!func1 & FunctionAttribute.return_) == 0); +} + +/** + * Returns a tuple with default values of the parameters to $(D_PARAM F). + * + * If a parameter doesn't have a default value, $(D_KEYWORD void) is returned. + * + * Params: + * F = A function. + * + * Returns: Default values of the parameters to $(D_PARAM F). + */ +template ParameterDefaults(F...) +if (isCallable!F) +{ + static if (is(FunctionTypeOf!F T == __parameters)) + { + private template GetDefault(size_t i) + { + static if (i == T.length) + { + alias GetDefault = AliasSeq!(); + } + else + { + enum getDefault(T[i .. i + 1] name) + { + return name[0]; + } + static if (is(typeof(getDefault()))) + { + alias Default = Alias!(getDefault()); + } + else + { + alias Default = void; + } + alias GetDefault = AliasSeq!(Default, GetDefault!(i + 1)); + } + } + + alias ParameterDefaults = GetDefault!0; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + void func1(int k, uint b = 5, int[] = [1, 2]); + alias Defaults = ParameterDefaults!func1; + static assert(is(Defaults[0] == void)); + static assert(Defaults[1 .. 3] == AliasSeq!(5, [1, 2])); +} + +/** + * Determines whether $(D_PARAM T) has an elaborate destructor. + * + * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the + * length greater than`0` can have elaborate destructors, for all other types + * $(D_PSYMBOL hasElaborateDestructor) evaluates to $(D_KEYWORD false). + * + * An elaborate destructor is an explicitly defined destructor or one generated + * by the compiler. The compiler generates a destructor for a + * $(D_KEYWORD struct) if it has members with an elaborate destructor. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate destructor, + * $(D_KEYWORD false) otherwise. + */ +template hasElaborateDestructor(T) +{ + static if (is(T E : E[L], size_t L)) + { + enum bool hasElaborateDestructor = L > 0 && hasElaborateDestructor!E; + } + else + { + enum bool hasElaborateDestructor = is(T == struct) + && hasMember!(T, "__xdtor"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + class C + { + ~this() + { + } + } + static assert(!hasElaborateDestructor!C); + + static struct S + { + ~this() + { + } + } + static struct S1 + { + S s; + } + static struct S2 + { + } + static assert(hasElaborateDestructor!S); // Explicit destructor. + static assert(hasElaborateDestructor!S1); // Compiler-generated destructor. + static assert(!hasElaborateDestructor!S2); // No destructor. + + static assert(hasElaborateDestructor!(S[1])); + static assert(!hasElaborateDestructor!(S[0])); +} + +/** + * Determines whether $(D_PARAM T) has an elaborate postblit constructor. + * + * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the + * length greater than`0` can have elaborate postblit constructors, for all + * other types $(D_PSYMBOL hasElaborateCopyConstructor) evaluates to + * $(D_KEYWORD false). + * + * An elaborate postblit constructor is an explicitly defined postblit + * constructor or one generated by the compiler. The compiler generates a + * postblit constructor for a + * $(D_KEYWORD struct) if it has members with an elaborate postblit + * constructor. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate postblit + * constructor, $(D_KEYWORD false) otherwise. + */ +template hasElaborateCopyConstructor(T) +{ + static if (is(T E : E[L], size_t L)) + { + enum bool hasElaborateCopyConstructor = L > 0 + && hasElaborateCopyConstructor!E; + } + else + { + enum bool hasElaborateCopyConstructor = is(T == struct) + && hasMember!(T, "__xpostblit"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(!hasElaborateCopyConstructor!int); + + static struct S + { + this(this) + { + } + } + static struct S1 + { + S s; + } + static struct S2 + { + } + static assert(hasElaborateCopyConstructor!S); // Explicit destructor. + static assert(hasElaborateCopyConstructor!S1); // Compiler-generated destructor. + static assert(!hasElaborateCopyConstructor!S2); // No destructor. + static assert(hasElaborateCopyConstructor!(S[1])); + static assert(!hasElaborateCopyConstructor!(S[0])); +} + +/** + * Determines whether $(D_PARAM T) has an elaborate assign. + * + * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the + * length greater than`0` can have an elaborate assign, for all + * other types $(D_PSYMBOL hasElaborateAssign) evaluates to $(D_KEYWORD false). + * + * An elaborate assign is defined with $(D_INLINECODE opAssign(typeof(this))) + * or $(D_INLINECODE opAssign(ref typeof(this))). An elaborate assign can be + * generated for a $(D_KEYWORD struct) by the compiler if one of the members of + * this $(D_KEYWORD struct) has an elaborate assign. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate assign, + * $(D_KEYWORD false) otherwise. + */ +template hasElaborateAssign(T) +{ + static if (is(T E : E[L], size_t L)) + { + enum bool hasElaborateAssign = L > 0 && hasElaborateAssign!E; + } + else static if (is(T == struct)) + { + private enum bool valueAssign = is(typeof({ T.init.opAssign(T()); })); + enum bool hasElaborateAssign = valueAssign || is(typeof({ + T s; + s.opAssign(s); + })); + } + else + { + enum bool hasElaborateAssign = false; + } +} + +@nogc nothrow pure @safe unittest +{ + static assert(!hasElaborateAssign!int); + + static struct S1 + { + void opAssign(S1) + { + } + } + static struct S2 + { + void opAssign(int) + { + } + } + static struct S3 + { + S1 s; + alias s this; + } + static assert(hasElaborateAssign!S1); + static assert(!hasElaborateAssign!(const S1)); + static assert(hasElaborateAssign!(S1[1])); + static assert(!hasElaborateAssign!(S1[0])); + static assert(!hasElaborateAssign!S2); + static assert(hasElaborateAssign!S3); + + static struct S4 + { + void opAssign(S4) + { + } + @disable this(this); + } + static assert(hasElaborateAssign!S4); +} + +/** + * Returns all members of $(D_KEYWORD enum) $(D_PARAM T). + * + * The members of $(D_PARAM T) are typed as $(D_PARAM T), not as a base type + * of the enum. + * + * $(D_PARAM EnumMembers) returns all members of $(D_PARAM T), also if there + * are some duplicates. + * + * Params: + * T = A $(D_KEYWORD enum). + * + * Returns: All members of $(D_PARAM T). + */ +template EnumMembers(T) +if (is(T == enum)) +{ + private template getEnumMembers(Args...) + { + static if (Args.length == 1) + { + enum T getEnumMembers = __traits(getMember, T, Args[0]); + } + else + { + alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]), + getEnumMembers!(Args[1 .. $])); + } + } + private alias allMembers = AliasSeq!(__traits(allMembers, T)); + static if (allMembers.length == 1) + { + alias EnumMembers = AliasSeq!(__traits(getMember, T, allMembers)); + } + else + { + alias EnumMembers = getEnumMembers!allMembers; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum E : int + { + one, + two, + three, + } + static assert([EnumMembers!E] == [E.one, E.two, E.three]); +} + +// Produces a tuple for an enum with only one member +@nogc nothrow pure @safe unittest +{ + enum E : int + { + one = 0, + } + static assert(EnumMembers!E == AliasSeq!0); +} + +/** + * Different than $(D_INLINECODE T.alignof), which is the same for all class + * types, $(D_PSYMBOL classInstanceOf) determines the alignment of the class + * instance and not of its reference. + * + * Params: + * T = A class. + * + * Returns: Alignment of an instance of the class $(D_PARAM T). + */ +template classInstanceAlignment(T) +if (is(T == class)) +{ + private enum ptrdiff_t pred(U1, U2) = U1.alignof - U2.alignof; + private alias Fields = typeof(T.tupleof); + enum size_t classInstanceAlignment = Max!(pred, T, Fields).alignof; +} + +/// +@nogc nothrow pure @safe unittest +{ + class C1 + { + } + static assert(classInstanceAlignment!C1 == C1.alignof); + + static struct S + { + align(8) + uint s; + + int i; + } + class C2 + { + S s; + } + static assert(classInstanceAlignment!C2 == S.alignof); +} + +/** + * Returns the size in bytes of the state that needs to be allocated to hold an + * object of type $(D_PARAM T). + * + * There is a difference between the `.sizeof`-property and + * $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface. + * `T.sizeof` is constant on the given architecture then and is the same as + * `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and + * interfaces are reference types and `.sizeof` returns the size of the + * reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize) + * returns the size of the instance itself. + * + * The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array + * stores its length and a data pointer. The size of the static arrays is + * calculated differently since they are value types. It is the array length + * multiplied by the element size. + * + * `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym + * for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`. + * + * Params: + * T = Object type. + * + * Returns: Size of an instance of type $(D_PARAM T). + */ +template stateSize(T) +{ + static if (isPolymorphicType!T) + { + enum size_t stateSize = __traits(classInstanceSize, T); + } + else + { + enum size_t stateSize = T.sizeof; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(stateSize!int == 4); + static assert(stateSize!bool == 1); + static assert(stateSize!(int[]) == (size_t.sizeof * 2)); + static assert(stateSize!(short[3]) == 6); + + static struct Empty + { + } + static assert(stateSize!Empty == 1); + static assert(stateSize!void == 1); +} + +/** + * Tests whether $(D_INLINECODE pred(T)) can be used as condition in an + * $(D_KEYWORD if)-statement or a ternary operator. + * + * $(D_PARAM pred) is an optional parameter. By default $(D_PSYMBOL ifTestable) + * tests whether $(D_PARAM T) itself is usable as condition in an + * $(D_KEYWORD if)-statement or a ternary operator, i.e. if it a value of type + * $(D_PARAM T) can be converted to a boolean. + * + * Params: + * T = A type. + * pred = Function with one argument. + * + * Returns: $(D_KEYWORD true) if $(D_INLINECODE pred(T)) can be used as + * condition in an $(D_KEYWORD if)-statement or a ternary operator. + */ +template ifTestable(T, alias pred = a => a) +{ + enum bool ifTestable = is(typeof(pred(T.init) ? true : false)); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(ifTestable!int); + + static struct S1 + { + } + static assert(!ifTestable!S1); + + static struct S2 + { + bool opCast(T : bool)() + { + return true; + } + } + static assert(ifTestable!S2); +} + +/** + * Returns a compile-time tuple of user-defined attributes (UDA) attached to + * $(D_PARAM symbol). + * + * $(D_PARAM symbol) can be: + * + * $(DL + * $(DT Template) + * $(DD The attribute is matched if it is an instance of the template + * $(D_PARAM attr).) + * $(DT Type) + * $(DD The attribute is matched if it its type is $(D_PARAM attr).) + * $(DT Expression) + * $(DD The attribute is matched if it equals to $(D_PARAM attr).) + * ) + * + * If $(D_PARAM attr) isn't given, all user-defined attributes of + * $(D_PARAM symbol) are returned. + * + * Params: + * symbol = A symbol. + * attr = User-defined attribute. + * + * Returns: A tuple of user-defined attributes attached to $(D_PARAM symbol) + * and matching $(D_PARAM attr). + * + * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, + * User Defined Attributes). + */ +template getUDAs(alias symbol, alias attr) +{ + private template FindUDA(T...) + { + static if (T.length == 0) + { + alias FindUDA = AliasSeq!(); + } + else static if ((isTypeTuple!attr && is(TypeOf!(T[0]) == attr)) + || (is(typeof(T[0] == attr)) && (T[0] == attr)) + || isInstanceOf!(attr, TypeOf!(T[0]))) + { + alias FindUDA = AliasSeq!(T[0], FindUDA!(T[1 .. $])); + } + else + { + alias FindUDA = FindUDA!(T[1 .. $]); + } + } + alias getUDAs = FindUDA!(__traits(getAttributes, symbol)); +} + +/// +alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol)); + +/// +@nogc nothrow pure @safe unittest +{ + static struct Attr + { + int i; + } + @Attr int a; + static assert(getUDAs!(a, Attr).length == 1); + + @Attr(8) int b; + static assert(getUDAs!(b, Attr).length == 1); + static assert(getUDAs!(b, Attr)[0].i == 8); + static assert(getUDAs!(b, Attr(8)).length == 1); + static assert(getUDAs!(b, Attr(7)).length == 0); + + @("string", 5) int c; + static assert(getUDAs!(c, "string").length == 1); + static assert(getUDAs!(c, 5).length == 1); + static assert(getUDAs!(c, "String").length == 0); + static assert(getUDAs!(c, 4).length == 0); + + static struct T(U) + { + enum U s = 7; + U i; + } + @T!int @T!int(8) int d; + static assert(getUDAs!(d, T).length == 2); + static assert(getUDAs!(d, T)[0].s == 7); + static assert(getUDAs!(d, T)[1].i == 8); + + @T int e; + static assert(getUDAs!(e, T).length == 0); +} + +/** + * Determines whether $(D_PARAM symbol) has user-defined attribute + * $(D_PARAM attr) attached to it. + * + * Params: + * symbol = A symbol. + * attr = User-defined attribute. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM symbol) has user-defined attribute + * $(D_PARAM attr), $(D_KEYWORD false) otherwise. + * + * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, + * User Defined Attributes). + */ +template hasUDA(alias symbol, alias attr) +{ + enum bool hasUDA = getUDAs!(symbol, attr).length != 0; +} + +/// +@nogc nothrow pure @safe unittest +{ + static struct Attr1 + { + } + static struct Attr2 + { + } + @Attr1 int a; + static assert(hasUDA!(a, Attr1)); + static assert(!hasUDA!(a, Attr2)); +} + +/** + * If $(D_PARAM T) is a type, constructs its default value, otherwise + * $(D_PSYMBOL evalUDA) aliases itself to $(D_PARAM T). + * + * This template is useful when working with UDAs with default parameters, + * i.e. if an attribute can be given as `@Attr` or `@Attr("param")`, + * $(D_PSYMBOL evalUDA) makes `@Attr()` from `@Attr`, but returns + * `@Attr("param")` as is. + * + * $(D_PARAM T) (or its type if it isn't a type already) should have a default + * constructor. + * + * Params: + * T = User Defined Attribute. + */ +alias evalUDA(alias T) = T; + +/// ditto +alias evalUDA(T) = Alias!(T()); + +/// +@nogc nothrow pure @safe unittest +{ + static struct Length + { + size_t length = 8; + } + @Length @Length(0) int i; + alias uda = AliasSeq!(__traits(getAttributes, i)); + + alias attr1 = evalUDA!(uda[0]); + alias attr2 = evalUDA!(uda[1]); + + static assert(is(typeof(attr1) == Length)); + static assert(is(typeof(attr2) == Length)); + + static assert(attr1.length == 8); + static assert(attr2.length == 0); +} + +/** + * Tests whether $(D_PARAM T) is an inner class, i.e. a class nested inside + * another class. + * + * All inner classes get `outer` propery automatically generated, which points + * to its parent class, though it can be explicitly defined to be something + * different. If $(D_PARAM T) does this, $(D_PSYMBOL isInnerClass) + * evaluates to $(D_KEYWORD false). + * + * Params: + * T = Class to be tested. + * + * Returns $(D_KEYWORD true) if $(D_PARAM T) is an inner class, + * $(D_KEYWORD false) otherwise. + */ +template isInnerClass(T) +{ + static if (is(T == class) && is(typeof(T.outer) == class)) + { + enum bool isInnerClass = !canFind!("outer", __traits(allMembers, T)); + } + else + { + enum bool isInnerClass = false; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + class A + { + } + class O + { + class I + { + } + class Fake + { + bool outer; + } + } + static assert(!isInnerClass!(O)); + static assert(isInnerClass!(O.I)); + static assert(!isInnerClass!(O.Fake)); +} + +@nogc nothrow pure @safe unittest +{ + class RefCountedStore(T) + { + } + static assert(!isInnerClass!(RefCountedStore!int)); +} + +/** + * Returns the types of all members of $(D_PARAM T). + * + * If $(D_PARAM T) is a $(D_KEYWORD struct) or $(D_KEYWORD union) or + * $(D_KEYWORD class), returns the types of all its fields. It is actually the + * same as `T.tupleof`, but the content pointer for the nested type isn't + * included. + * + * If $(D_PARAM T) is neither a $(D_KEYWORD struct) nor $(D_KEYWORD union) nor + * $(D_KEYWORD class), $(D_PSYMBOL Fields) returns an $(D_PSYMBOL AliasSeq) + * with the single element $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_PARAM T)'s fields. + */ +template Fields(T) +{ + static if ((is(T == struct) || is(T == union)) && isNested!T) + { + // The last element of .tupleof of a nested struct or union is "this", + // the context pointer, type "void*". + alias Fields = typeof(T.tupleof[0 .. $ - 1]); + } + else static if (is(T == class) || is(T == struct) || is(T == union)) + { + alias Fields = typeof(T.tupleof); + } + else + { + alias Fields = AliasSeq!T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + struct Nested + { + int i; + + void func() + { + } + } + static assert(is(Fields!Nested == AliasSeq!int)); + + class C + { + uint u; + } + static assert(is(Fields!C == AliasSeq!uint)); + + static assert(is(Fields!short == AliasSeq!short)); +} + +/** + * Determines whether all $(D_PARAM Types) are the same. + * + * If $(D_PARAM Types) is empty, returns $(D_KEYWORD true). + * + * Params: + * Types = Type sequence. + * + * Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same, + * $(D_KEYWORD false) otherwise. + */ +template allSameType(Types...) +{ + static if (Types.length == 0) + { + enum bool allSameType = true; + } + else + { + private enum bool sameType(T) = is(T == Types[0]); + + enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(allSameType!()); + static assert(allSameType!int); + static assert(allSameType!(int, int, int)); + static assert(!allSameType!(int, uint, int)); + static assert(!allSameType!(int, uint, short)); +} + +/** + * Determines whether values of type $(D_PARAM T) can be compared for equality, + * i.e. using `==` or `!=` binary operators. + * + * Params: + * T = Type to test. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality, + * $(D_KEYWORD false) otherwise. + */ +enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isEqualityComparable!int); +} + +/** + * Determines whether values of type $(D_PARAM T) can be compared for ordering, + * i.e. using `>`, `>=`, `<` or `<=` binary operators. + * + * Params: + * T = Type to test. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering, + * $(D_KEYWORD false) otherwise. + */ +enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(isOrderingComparable!int); +} + +@nogc nothrow pure @safe unittest +{ + static struct DisabledOpEquals + { + @disable bool opEquals(typeof(this)) @nogc nothrow pure @safe; + + int opCmp(typeof(this)) @nogc nothrow pure @safe + { + return 0; + } + } + static assert(!isEqualityComparable!DisabledOpEquals); + static assert(isOrderingComparable!DisabledOpEquals); + + static struct OpEquals + { + bool opEquals(typeof(this)) @nogc nothrow pure @safe + { + return true; + } + } + static assert(isEqualityComparable!OpEquals); + static assert(!isOrderingComparable!OpEquals); +} diff --git a/meta/tanya/meta/transform.d b/meta/tanya/meta/transform.d new file mode 100644 index 0000000..083e7ee --- /dev/null +++ b/meta/tanya/meta/transform.d @@ -0,0 +1,981 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Type transformations. + * + * Templates in this module can be used to modify type qualifiers or transform + * types. They take some type as argument and return a different type after + * perfoming the specified transformation. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/transform.d, + * tanya/meta/transform.d) + */ +module tanya.meta.transform; + +import tanya.meta.metafunction; +import tanya.meta.trait; + +/** + * Removes any type qualifiers from $(D_PARAM T). + * + * Removed qualifiers are: + * $(UL + * $(LI const) + * $(LI immutable) + * $(LI inout) + * $(LI shared) + * ) + * and combinations of these. + * + * If the type $(D_PARAM T) doesn't have any qualifieres, + * $(D_INLINECODE Unqual!T) becomes an alias for $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_PARAM T) without any type qualifiers. + */ +template Unqual(T) +{ + static if (is(T U == const U) + || is(T U == immutable U) + || is(T U == inout U) + || is(T U == inout const U) + || is(T U == shared U) + || is(T U == shared const U) + || is(T U == shared inout U) + || is(T U == shared inout const U)) + { + alias Unqual = U; + } + else + { + alias Unqual = T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Unqual!bool == bool)); + static assert(is(Unqual!(immutable bool) == bool)); + static assert(is(Unqual!(inout bool) == bool)); + static assert(is(Unqual!(inout const bool) == bool)); + static assert(is(Unqual!(shared bool) == bool)); + static assert(is(Unqual!(shared const bool) == bool)); + static assert(is(Unqual!(shared inout const bool) == bool)); +} + +/** + * If $(D_PARAM T) is an $(D_KEYWORD enum), $(D_INLINECODE OriginalType!T) + * evaluates to the most base type of that $(D_KEYWORD enum) and to + * $(D_PARAM T) otherwise. + * + * Params: + * T = A type. + * + * Returns: Base type of the $(D_KEYWORD enum) $(D_PARAM T) or $(D_PARAM T) + * itself. + */ +template OriginalType(T) +{ + static if (is(T U == enum)) + { + alias OriginalType = OriginalType!U; + } + else + { + alias OriginalType = T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + enum E1 : const(int) + { + n = 0, + } + enum E2 : bool + { + t = true, + } + enum E3 : E2 + { + t = E2.t, + } + enum E4 : const(E2) + { + t = E2.t, + } + + static assert(is(OriginalType!E1 == const int)); + static assert(is(OriginalType!E2 == bool)); + static assert(is(OriginalType!E3 == bool)); + static assert(is(OriginalType!E4 == bool)); + static assert(is(OriginalType!(const E4) == bool)); +} + +/** + * Copies constness of $(D_PARAM From) to $(D_PARAM To). + * + * The following type qualifiers affect the constness and hence are copied: + * $(UL + * $(LI const) + * $(LI immutable) + * $(LI inout) + * $(LI inout const) + * ) + * + * Params: + * From = Source type. + * To = Target type. + * + * Returns: $(D_PARAM To) with the constness of $(D_PARAM From). + * + * See_Also: $(D_PSYMBOL CopyTypeQualifiers). + */ +template CopyConstness(From, To) +{ + static if (is(From T == immutable T)) + { + alias CopyConstness = immutable To; + } + else static if (is(From T == const T) || is(From T == shared const T)) + { + alias CopyConstness = const To; + } + else static if (is(From T == inout T) || is(From T == shared inout T)) + { + alias CopyConstness = inout To; + } + else static if (is(From T == inout const T) + || is(From T == shared inout const T)) + { + alias CopyConstness = inout const To; + } + else + { + alias CopyConstness = To; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(CopyConstness!(int, char) == char)); + static assert(is(CopyConstness!(const int, char) == const char)); + static assert(is(CopyConstness!(immutable int, char) == immutable char)); + static assert(is(CopyConstness!(inout int, char) == inout char)); + static assert(is(CopyConstness!(inout const int, char) == inout const char)); + + static assert(is(CopyConstness!(shared int, char) == char)); + static assert(is(CopyConstness!(shared const int, char) == const char)); + static assert(is(CopyConstness!(shared inout int, char) == inout char)); + static assert(is(CopyConstness!(shared inout const int, char) == inout const char)); + + static assert(is(CopyConstness!(const int, shared char) == shared const char)); + static assert(is(CopyConstness!(const int, immutable char) == immutable char)); + static assert(is(CopyConstness!(immutable int, const char) == immutable char)); +} + +/** + * Copies type qualifiers of $(D_PARAM From) to $(D_PARAM To). + * + * Type qualifiers copied are: + * $(UL + * $(LI const) + * $(LI immutable) + * $(LI inout) + * $(LI shared) + * ) + * and combinations of these. + * + * Params: + * From = Source type. + * To = Target type. + * + * Returns: $(D_PARAM To) with the type qualifiers of $(D_PARAM From). + * + * See_Also: $(D_PSYMBOL CopyConstness). + */ +template CopyTypeQualifiers(From, To) +{ + static if (is(From T == immutable T)) + { + alias CopyTypeQualifiers = immutable To; + } + else static if (is(From T == const T)) + { + alias CopyTypeQualifiers = const To; + } + else static if (is(From T == shared T)) + { + alias CopyTypeQualifiers = shared To; + } + else static if (is(From T == shared const T)) + { + alias CopyTypeQualifiers = shared const To; + } + else static if (is(From T == inout T)) + { + alias CopyTypeQualifiers = inout To; + } + else static if (is(From T == shared inout T)) + { + alias CopyTypeQualifiers = shared inout To; + } + else static if (is(From T == inout const T)) + { + alias CopyTypeQualifiers = inout const To; + } + else static if (is(From T == shared inout const T)) + { + alias CopyTypeQualifiers = shared inout const To; + } + else + { + alias CopyTypeQualifiers = To; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(CopyTypeQualifiers!(int, char) == char)); + static assert(is(CopyTypeQualifiers!(const int, char) == const char)); + static assert(is(CopyTypeQualifiers!(immutable int, char) == immutable char)); + static assert(is(CopyTypeQualifiers!(inout int, char) == inout char)); + static assert(is(CopyTypeQualifiers!(inout const int, char) == inout const char)); + + static assert(is(CopyTypeQualifiers!(shared int, char) == shared char)); + static assert(is(CopyTypeQualifiers!(shared const int, char) == shared const char)); + static assert(is(CopyTypeQualifiers!(shared inout int, char) == shared inout char)); + static assert(is(CopyTypeQualifiers!(shared inout const int, char) == shared inout const char)); +} + +/** + * Evaluates to the unsigned counterpart of the integral type $(D_PARAM T) preserving all type qualifiers. + * If $(D_PARAM T) is already unsigned, $(D_INLINECODE Unsigned!T) aliases $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: Unsigned counterpart of $(D_PARAM T). + * + * See_Also: $(D_PSYMBOL isSigned). + */ +template Unsigned(T) +if (isIntegral!T) +{ + alias UnqualedType = Unqual!(OriginalType!T); + static if (is(UnqualedType == byte)) + { + alias Unsigned = CopyTypeQualifiers!(T, ubyte); + } + else static if (is(UnqualedType == short)) + { + alias Unsigned = CopyTypeQualifiers!(T, ushort); + } + else static if (is(UnqualedType == int)) + { + alias Unsigned = CopyTypeQualifiers!(T, uint); + } + else static if (is(UnqualedType == long)) + { + alias Unsigned = CopyTypeQualifiers!(T, ulong); + } + else + { + alias Unsigned = T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Unsigned!byte == ubyte)); + static assert(is(Unsigned!short == ushort)); + static assert(is(Unsigned!int == uint)); + static assert(is(Unsigned!long == ulong)); + + static assert(is(Unsigned!(const byte) == const ubyte)); + static assert(is(Unsigned!(shared byte) == shared ubyte)); + static assert(is(Unsigned!(shared const byte) == shared const ubyte)); + + static assert(!is(Unsigned!float)); + static assert(is(Unsigned!ubyte == ubyte)); +} + +/** + * Evaluates to the signed counterpart of the integral type $(D_PARAM T) preserving all type qualifiers. + * If $(D_PARAM T) is already signed, $(D_INLINECODE Signed!T) aliases $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: Signed counterpart of $(D_PARAM T). + * + * See_Also: $(D_PSYMBOL isUnsigned). + */ +template Signed(T) +if (isIntegral!T) +{ + alias UnqualedType = Unqual!(OriginalType!T); + static if (is(UnqualedType == ubyte)) + { + alias Signed = CopyTypeQualifiers!(T, byte); + } + else static if (is(UnqualedType == ushort)) + { + alias Signed = CopyTypeQualifiers!(T, short); + } + else static if (is(UnqualedType == uint)) + { + alias Signed = CopyTypeQualifiers!(T, int); + } + else static if (is(UnqualedType == ulong)) + { + alias Signed = CopyTypeQualifiers!(T, long); + } + else + { + alias Signed = T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Signed!ubyte == byte)); + static assert(is(Signed!ushort == short)); + static assert(is(Signed!uint == int)); + static assert(is(Signed!ulong == long)); + + static assert(is(Signed!(const ubyte) == const byte)); + static assert(is(Signed!(shared ubyte) == shared byte)); + static assert(is(Signed!(shared const ubyte) == shared const byte)); + + static assert(!is(Signed!float)); + static assert(is(Signed!byte == byte)); +} + +/** + * Retrieves the target type `U` of a pointer `U*`. + * + * Params: + * T = Pointer type. + * + * Returns: Pointer target type. + */ +template PointerTarget(T) +{ + static if (is(T U : U*)) + { + alias PointerTarget = U; + } + else + { + static assert(T.stringof ~ " isn't a pointer type"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(PointerTarget!(bool*) == bool)); + static assert(is(PointerTarget!(const bool*) == const bool)); + static assert(is(PointerTarget!(const shared bool*) == const shared bool)); + static assert(!is(PointerTarget!bool)); +} + +/** + * Params: + * T = The type of the associative array. + * + * Returns: The key type of the associative array $(D_PARAM T). + */ +template KeyType(T) +{ + static if (is(T V : V[K], K)) + { + alias KeyType = K; + } + else + { + static assert(false, T.stringof ~ " isn't an associative array"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(KeyType!(int[string]) == string)); + static assert(!is(KeyType!(int[15]))); +} + +/** + * Params: + * T = The type of the associative array. + * + * Returns: The value type of the associative array $(D_PARAM T). + */ +template ValueType(T) +{ + static if (is(T V : V[K], K)) + { + alias ValueType = V; + } + else + { + static assert(false, T.stringof ~ " isn't an associative array"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(ValueType!(int[string]) == int)); + static assert(!is(ValueType!(int[15]))); +} + +/** + * Params: + * T = Scalar type. + * + * Returns: The type $(D_PARAM T) will promote to. + * + * See_Also: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, + * Integer Promotions). + */ +template Promoted(T) +if (isScalarType!T) +{ + alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Promoted!bool == int)); + static assert(is(Promoted!byte == int)); + static assert(is(Promoted!ubyte == int)); + static assert(is(Promoted!short == int)); + static assert(is(Promoted!ushort == int)); + static assert(is(Promoted!char == int)); + static assert(is(Promoted!wchar == int)); + static assert(is(Promoted!dchar == uint)); + + static assert(is(Promoted!(const bool) == const int)); + static assert(is(Promoted!(shared bool) == shared int)); +} + +/** + * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE inout(T)). + */ +alias InoutOf(T) = inout(T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(InoutOf!int == inout int)); +} + +/** + * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE inout(T)). + */ +alias ConstOf(T) = const(T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(ConstOf!int == const int)); +} + +/** + * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE inout(T)). + */ +alias SharedOf(T) = shared(T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(SharedOf!int == shared int)); +} + +/** + * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE inout(T)). + */ +alias SharedInoutOf(T) = shared(inout T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(SharedInoutOf!int == shared inout int)); +} + +/** + * Adds $(D_KEYWORD shared const) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE shared(const T)). + */ +alias SharedConstOf(T) = shared(const T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(SharedConstOf!int == shared const int)); +} + +/** + * Adds $(D_KEYWORD immutable) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE immutable(T)). + */ +alias ImmutableOf(T) = immutable(T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(ImmutableOf!int == immutable int)); +} + +/** + * Adds $(D_KEYWORD inout const) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE inout(const T)). + */ +alias InoutConstOf(T) = inout(const T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(InoutConstOf!int == inout const int)); +} + +/** + * Adds $(D_KEYWORD shared inout const) qualifier to the type $(D_PARAM T). + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE shared(inout const T)). + */ +alias SharedInoutConstOf(T) = shared(inout const T); + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(SharedInoutConstOf!int == shared inout const int)); +} + +/** + * Returns a template with one argument which applies all qualifiers of + * $(D_PARAM T) on its argument if instantiated. + * + * Params: + * T = A type. + * + * Returns: $(D_INLINECODE shared(inout const T)). + */ +template QualifierOf(T) +{ + static if (is(T U == const U)) + { + alias QualifierOf = ConstOf; + } + else static if (is(T U == immutable U)) + { + alias QualifierOf = ImmutableOf; + } + else static if (is(T U == inout U)) + { + alias QualifierOf = InoutOf; + } + else static if (is(T U == inout const U)) + { + alias QualifierOf = InoutConstOf; + } + else static if (is(T U == shared U)) + { + alias QualifierOf = SharedOf; + } + else static if (is(T U == shared const U)) + { + alias QualifierOf = SharedConstOf; + } + else static if (is(T U == shared inout U)) + { + alias QualifierOf = SharedInoutOf; + } + else static if (is(T U == shared inout const U)) + { + alias QualifierOf = SharedInoutConstOf; + } + else + { + alias QualifierOf(T) = T; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + alias MutableOf = QualifierOf!int; + static assert(is(MutableOf!uint == uint)); + + alias ConstOf = QualifierOf!(const int); + static assert(is(ConstOf!uint == const uint)); + + alias InoutOf = QualifierOf!(inout int); + static assert(is(InoutOf!uint == inout uint)); + + alias InoutConstOf = QualifierOf!(inout const int); + static assert(is(InoutConstOf!uint == inout const uint)); + + alias ImmutableOf = QualifierOf!(immutable int); + static assert(is(ImmutableOf!uint == immutable uint)); + + alias SharedOf = QualifierOf!(shared int); + static assert(is(SharedOf!uint == shared uint)); + + alias SharedConstOf = QualifierOf!(shared const int); + static assert(is(SharedConstOf!uint == shared const uint)); + + alias SharedInoutOf = QualifierOf!(shared inout int); + static assert(is(SharedInoutOf!uint == shared inout uint)); + + alias SharedInoutConstOf = QualifierOf!(shared inout const int); + static assert(is(SharedInoutConstOf!uint == shared inout const uint)); +} + +/** + * Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type, + * $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T). + * + * $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments. + * + * The symbols that don't have a type and aren't types cannot be used as + * arguments to $(D_PSYMBOL TypeOf). + * + * Params: + * T = Expression, type or template. + * + * Returns: The type of $(D_PARAM T). + */ +alias TypeOf(T) = T; + +/// ditto +template TypeOf(alias T) +if (isExpressions!T || __traits(isTemplate, T)) +{ + alias TypeOf = typeof(T); +} + +/// +@nogc nothrow pure @safe unittest +{ + struct S(T) + { + } + static assert(is(TypeOf!S == void)); + static assert(is(TypeOf!int == int)); + static assert(is(TypeOf!true == bool)); + static assert(!is(TypeOf!(tanya.meta))); +} + +// e.g. returns int for int**. +private template FinalPointerTarget(T) +{ + static if (isPointer!T) + { + alias FinalPointerTarget = FinalPointerTarget!(PointerTarget!T); + } + else + { + alias FinalPointerTarget = T; + } +} + +// Returns true if T1 is void* and T2 is some pointer. +private template voidAndPointer(T1, T2) +{ + enum bool voidAndPointer = is(Unqual!(PointerTarget!T1) == void) + && isPointer!T2; +} + +// Type returned by the ternary operator. +private alias TernaryType(T, U) = typeof(true ? T.init : U.init); + +/** + * Determines the type all $(D_PARAM Args) can be implicitly converted to. + * + * $(OL + * $(LI If one of the arguments is $(D_KEYWORD void), the common type is + * $(D_KEYWORD void).) + * $(LI The common type of integers with the same sign is the type with a + * larger size. Signed and unsigned integers don't have a common type. + * Type qualifiers are only preserved if all arguments are the same + * type.) + * $(LI The common type of floating point numbers is the type with more + * precision. Type qualifiers are only preserved if all arguments are + * the same type.) + * $(LI The common type of polymorphic objects is the next, more generic type + * both objects inherit from, e.g. $(D_PSYMBOL Object).) + * $(LI `void*` is concerned as a common type of pointers only if one of the + * arguments is a void pointer.) + * $(LI Other types have a common type only if their pointers have a common + * type. It means that for example $(D_KEYWORD bool) and $(D_KEYWORD int) + don't have a common type. If the types fullfill this condition, the + common type is determined with the ternary operator, i.e. + `typeof(true ? T1.init : T2.init)` is evaluated.) + * ) + * + * If $(D_PARAM Args) don't have a common type, $(D_PSYMBOL CommonType) is + * $(D_KEYWORD void). + * + * Params: + * Args = Type list. + * + * Returns: Common type for $(D_PARAM Args) or $(D_KEYWORD void) if + * $(D_PARAM Args) don't have a common type. + */ +template CommonType(Args...) +if (allSatisfy!(isType, Args)) +{ + static if (Args.length == 0 + || is(Unqual!(Args[0]) == void) + || is(Unqual!(Args[1]) == void)) + { + alias CommonType = void; + } + else static if (Args.length == 1) + { + alias CommonType = Args[0]; + } + else + { + private alias Pair = Args[0 .. 2]; + private enum bool sameSigned = allSatisfy!(isIntegral, Pair) + && isSigned!(Args[0]) == isSigned!(Args[1]); + + static if (is(Args[0] == Args[1])) + { + alias CommonType = CommonType!(Args[0], Args[2 .. $]); + } + else static if (sameSigned || allSatisfy!(isFloatingPoint, Pair)) + { + alias CommonType = CommonType!(Unqual!(Largest!Pair), + Args[2 .. $]); + } + else static if (voidAndPointer!Pair + || voidAndPointer!(Args[1], Args[0])) + { + // Workaround for https://issues.dlang.org/show_bug.cgi?id=15557. + // Determine the qualifiers returned by the ternary operator as if + // both pointers were int*. Then copy the qualifiers to void*. + alias P1 = CopyTypeQualifiers!(FinalPointerTarget!(Args[0]), int)*; + alias P2 = CopyTypeQualifiers!(FinalPointerTarget!(Args[1]), int)*; + static if (is(TernaryType!(P1, P2) U)) + { + alias CommonType = CopyTypeQualifiers!(PointerTarget!U, void)*; + } + else + { + alias CommonType = void; + } + } + else static if ((isPointer!(Args[0]) || isPolymorphicType!(Args[0])) + && is(TernaryType!Pair U)) + { + alias CommonType = CommonType!(U, Args[2 .. $]); + } + else static if (is(TernaryType!(Args[0]*, Args[1]*))) + { + alias CommonType = CommonType!(TernaryType!Pair, Args[2 .. $]); + } + else + { + alias CommonType = void; + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(CommonType!(int, int, int) == int)); + static assert(is(CommonType!(ubyte, ushort, uint) == uint)); + static assert(is(CommonType!(int, uint) == void)); + + static assert(is(CommonType!(int, const int) == int)); + static assert(is(CommonType!(const int, const int) == const int)); + + static assert(is(CommonType!(int[], const(int)[]) == const(int)[])); + static assert(is(CommonType!(string, char[]) == const(char)[])); + + class A + { + } + static assert(is(CommonType!(const A, Object) == const Object)); +} + +@nogc nothrow pure @safe unittest +{ + static assert(is(CommonType!(void*, int*) == void*)); + static assert(is(CommonType!(void*, const(int)*) == const(void)*)); + static assert(is(CommonType!(void*, const(void)*) == const(void)*)); + static assert(is(CommonType!(int*, void*) == void*)); + static assert(is(CommonType!(const(int)*, void*) == const(void)*)); + static assert(is(CommonType!(const(void)*, void*) == const(void)*)); + + static assert(is(CommonType!() == void)); + static assert(is(CommonType!(int*, const(int)*) == const(int)*)); + static assert(is(CommonType!(int**, const(int)**) == const(int*)*)); + + static assert(is(CommonType!(float, double) == double)); + static assert(is(CommonType!(float, int) == void)); + + static assert(is(CommonType!(bool, const bool) == bool)); + static assert(is(CommonType!(int, bool) == void)); + static assert(is(CommonType!(int, void) == void)); + static assert(is(CommonType!(Object, void*) == void)); + + class A + { + } + static assert(is(CommonType!(A, Object) == Object)); + static assert(is(CommonType!(const(A)*, Object*) == const(Object)*)); + static assert(is(CommonType!(A, typeof(null)) == A)); + + class B : A + { + } + class C : A + { + } + static assert(is(CommonType!(B, C) == A)); + + static struct S + { + int opCast(T : int)() + { + return 1; + } + } + static assert(is(CommonType!(S, int) == void)); + static assert(is(CommonType!(const S, S) == const S)); +} + +/** + * Finds the type with the smallest size in the $(D_PARAM Args) list. If + * several types have the same type, the leftmost is returned. + * + * Params: + * Args = Type list. + * + * Returns: The smallest type. + * + * See_Also: $(D_PSYMBOL Largest). + */ +template Smallest(Args...) +if (Args.length >= 1) +{ + static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property"); + + static if (Args.length == 1) + { + alias Smallest = Args[0]; + } + else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof) + { + alias Smallest = Smallest!(Args[1 .. $]); + } + else + { + alias Smallest = Args[0]; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Smallest!(int, ushort, uint, short) == ushort)); + static assert(is(Smallest!(short) == short)); + static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5])); + static assert(!is(Smallest!(short, 5))); +} + +/** + * Finds the type with the largest size in the $(D_PARAM Args) list. If several + * types have the same type, the leftmost is returned. + * + * Params: + * Args = Type list. + * + * Returns: The largest type. + * + * See_Also: $(D_PSYMBOL Smallest). + */ +template Largest(Args...) +if (Args.length >= 1) +{ + static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property"); + + static if (Args.length == 1) + { + alias Largest = Args[0]; + } + else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof) + { + alias Largest = Largest!(Args[1 .. $]); + } + else + { + alias Largest = Args[0]; + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(Largest!(int, short, uint) == int)); + static assert(is(Largest!(short) == short)); + static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8])); + static assert(!is(Largest!(short, 5))); +} diff --git a/os/dub.json b/os/dub.json index 87df29d..647e24d 100644 --- a/os/dub.json +++ b/os/dub.json @@ -5,5 +5,12 @@ "dependencies": { "tanya:meta": "*" - } + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/os/source/tanya/os/error.d b/os/source/tanya/os/error.d deleted file mode 100644 index b80404a..0000000 --- a/os/source/tanya/os/error.d +++ /dev/null @@ -1,419 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module provides a portable way of using operating system error codes. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/os/error.d, - * tanya/os/error.d) - */ -module tanya.os.error; - -import tanya.meta.trait; - -// Socket API error. -private template SAError(int posix, int wsa = posix) -{ - version (Windows) - { - enum SAError = 10000 + wsa; - } - else - { - alias SAError = posix; - } -} - -// Error for Windows and Posix separately. -private template NativeError(int posix, int win) -{ - version (Windows) - { - alias NativeError = win; - } - else - { - alias NativeError = posix; - } -} - -version (Windows) -{ - private enum eProtocolError = -71; -} -else version (OpenBSD) -{ - private enum eProtocolError = -71; -} -else -{ - private enum eProtocolError = 71; -} - -/** - * System error code. - */ -struct ErrorCode -{ - /** - * Error code numbers. - */ - enum ErrorNo : int - { - /// The operation completed successfully. - success = 0, - - /// Operation not permitted. - noPermission = NativeError!(1, 5), - - /// Interrupted system call. - interrupted = SAError!4, - - /// Bad file descriptor. - badDescriptor = SAError!9, - - /// An operation on a non-blocking socket would block. - wouldBlock = SAError!(11, 35), - - /// Out of memory. - noMemory = NativeError!(12, 14), - - /// Access denied. - accessDenied = SAError!13, - - /// An invalid pointer address detected. - fault = SAError!14, - - /// No such device. - noSuchDevice = NativeError!(19, 20), - - /// An invalid argument was supplied. - invalidArgument = SAError!22, - - /// The limit on the number of open file descriptors. - tooManyDescriptors = NativeError!(23, 331), - - /// The limit on the number of open file descriptors. - noDescriptors = SAError!24, - - /// Broken pipe. - brokenPipe = NativeError!(32, 109), - - /// The name was too long. - nameTooLong = SAError!(36, 63), - - /// A socket operation was attempted on a non-socket. - notSocket = SAError!(88, 38), - - /// Protocol error. - protocolError = eProtocolError, - - /// Message too long. - messageTooLong = SAError!(90, 40), - - /// Wrong protocol type for socket. - wrongProtocolType = SAError!(91, 41), - - /// Protocol not available. - noProtocolOption = SAError!(92, 42), - - /// The protocol is not implemented or has not been configured. - protocolNotSupported = SAError!(93, 43), - - /// The support for the specified socket type does not exist in this - /// address family. - socketNotSupported = SAError!(94, 44), - - /// The address family is no supported by the protocol family. - operationNotSupported = SAError!(95, 45), - - /// Address family specified is not supported. - addressFamilyNotSupported = SAError!(97, 47), - - /// Address already in use. - addressInUse = SAError!(98, 48), - - /// The network is not available. - networkDown = SAError!(100, 50), - - /// No route to host. - networkUnreachable = SAError!(101, 51), - - /// Network dropped connection because of reset. - networkReset = SAError!(102, 52), - - /// The connection has been aborted. - connectionAborted = SAError!(103, 53), - - /// Connection reset by peer. - connectionReset = SAError!(104, 54), - - /// No free buffer space is available for a socket operation. - noBufferSpace = SAError!(105, 55), - - /// Transport endpoint is already connected. - alreadyConnected = SAError!(106, 56), - - /// Transport endpoint is not connected. - notConnected = SAError!(107, 57), - - /// Cannot send after transport endpoint shutdown. - shutdown = SAError!(108, 58), - - /// The connection attempt timed out, or the connected host has failed - /// to respond. - timedOut = SAError!(110, 60), - - /// Connection refused. - connectionRefused = SAError!(111, 61), - - /// Host is down. - hostDown = SAError!(112, 64), - - /// No route to host. - hostUnreachable = SAError!(113, 65), - - /// Operation already in progress. - alreadyStarted = SAError!(114, 37), - - /// Operation now in progress. - inProgress = SAError!(115, 36), - - /// Operation cancelled. - cancelled = SAError!(125, 103), - } - - /** - * Error descriptions. - */ - private enum ErrorStr : string - { - success = "The operation completed successfully", - noPermission = "Operation not permitted", - interrupted = "Interrupted system call", - badDescriptor = "Bad file descriptor", - wouldBlock = "An operation on a non-blocking socket would block", - noMemory = "Out of memory", - accessDenied = "Access denied", - fault = "An invalid pointer address detected", - noSuchDevice = "No such device", - invalidArgument = "An invalid argument was supplied", - tooManyDescriptors = "The limit on the number of open file descriptors", - noDescriptors = "The limit on the number of open file descriptors", - brokenPipe = "Broken pipe", - nameTooLong = "The name was too long", - notSocket = "A socket operation was attempted on a non-socket", - protocolError = "Protocol error", - messageTooLong = "Message too long", - wrongProtocolType = "Wrong protocol type for socket", - noProtocolOption = "Protocol not available", - protocolNotSupported = "The protocol is not implemented or has not been configured", - socketNotSupported = "Socket type not supported", - operationNotSupported = "The address family is no supported by the protocol family", - addressFamilyNotSupported = "Address family specified is not supported", - addressInUse = "Address already in use", - networkDown = "The network is not available", - networkUnreachable = "No route to host", - networkReset = "Network dropped connection because of reset", - connectionAborted = "The connection has been aborted", - connectionReset = "Connection reset by peer", - noBufferSpace = "No free buffer space is available for a socket operation", - alreadyConnected = "Transport endpoint is already connected", - notConnected = "Transport endpoint is not connected", - shutdown = "Cannot send after transport endpoint shutdown", - timedOut = "Operation timed out", - connectionRefused = "Connection refused", - hostDown = "Host is down", - hostUnreachable = "No route to host", - alreadyStarted = "Operation already in progress", - inProgress = "Operation now in progress", - cancelled = "Operation cancelled", - } - - /** - * Constructor. - * - * Params: - * value = Numeric error code. - */ - this(const ErrorNo value) @nogc nothrow pure @safe - { - this.value_ = value; - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec; - assert(ec == ErrorCode.success); - - ec = ErrorCode.fault; - assert(ec == ErrorCode.fault); - } - - /** - * Resets this $(D_PSYMBOL ErrorCode) to default - * ($(D_PSYMBOL ErrorCode.success)). - */ - void reset() @nogc nothrow pure @safe - { - this.value_ = ErrorNo.success; - } - - /// - @nogc nothrow pure @safe unittest - { - auto ec = ErrorCode(ErrorCode.fault); - assert(ec == ErrorCode.fault); - - ec.reset(); - assert(ec == ErrorCode.success); - } - - /** - * Returns: Numeric error code. - */ - ErrorNo opCast(T : ErrorNo)() const - { - return this.value_; - } - - /// ditto - ErrorNo opCast(T : int)() const - { - return this.value_; - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec = ErrorCode.fault; - auto errorNo = cast(ErrorCode.ErrorNo) ec; - - assert(errorNo == ErrorCode.fault); - static assert(is(typeof(cast(int) ec))); - } - - /** - * Assigns another error code or error code number. - * - * Params: - * that = Numeric error code. - * - * Returns: $(D_KEYWORD this). - */ - ref ErrorCode opAssign(const ErrorNo that) @nogc nothrow pure @safe - { - this.value_ = that; - return this; - } - - /// ditto - ref ErrorCode opAssign(const ErrorCode that) @nogc nothrow pure @safe - { - this.value_ = that.value_; - return this; - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec; - assert(ec == ErrorCode.success); - - ec = ErrorCode.fault; - assert(ec == ErrorCode.fault); - } - - /// - @nogc nothrow pure @safe unittest - { - auto ec1 = ErrorCode(ErrorCode.fault); - ErrorCode ec2; - assert(ec2 == ErrorCode.success); - - ec2 = ec1; - assert(ec1 == ec2); - } - - /** - * Equality with another error code or error code number. - * - * Params: - * that = Numeric error code. - * - * Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal. - */ - bool opEquals(const ErrorNo that) const @nogc nothrow pure @safe - { - return this.value_ == that; - } - - /// ditto - bool opEquals(const ErrorCode that) const @nogc nothrow pure @safe - { - return this.value_ == that.value_; - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec1 = ErrorCode.fault; - ErrorCode ec2 = ErrorCode.accessDenied; - - assert(ec1 != ec2); - assert(ec1 != ErrorCode.accessDenied); - assert(ErrorCode.fault != ec2); - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec1 = ErrorCode.fault; - ErrorCode ec2 = ErrorCode.fault; - - assert(ec1 == ec2); - assert(ec1 == ErrorCode.fault); - assert(ErrorCode.fault == ec2); - } - - /** - * Returns string describing the error number. If a description for a - * specific error number is not available, returns $(D_KEYWORD null). - * - * Returns: String describing the error number. - */ - string toString() const @nogc nothrow pure @safe - { - foreach (e; __traits(allMembers, ErrorNo)) - { - if (__traits(getMember, ErrorNo, e) == this.value_) - { - return __traits(getMember, ErrorStr, e); - } - } - return null; - } - - /// - @nogc nothrow pure @safe unittest - { - ErrorCode ec = ErrorCode.fault; - assert(ec.toString() == "An invalid pointer address detected"); - } - - @nogc nothrow pure @safe unittest - { - ErrorCode ec = cast(ErrorCode.ErrorNo) -1; - assert(ec.toString() is null); - } - - private ErrorNo value_ = ErrorNo.success; - - alias ErrorNo this; -} diff --git a/os/source/tanya/os/package.d b/os/source/tanya/os/package.d deleted file mode 100644 index c472c05..0000000 --- a/os/source/tanya/os/package.d +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This package provides platform-independent interfaces to operating system - * functionality. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/os/package.d, - * tanya/os/package.d) - */ -module tanya.os; - -public import tanya.os.error; diff --git a/os/tanya/os/error.d b/os/tanya/os/error.d new file mode 100644 index 0000000..31c0102 --- /dev/null +++ b/os/tanya/os/error.d @@ -0,0 +1,419 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This module provides a portable way of using operating system error codes. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/error.d, + * tanya/os/error.d) + */ +module tanya.os.error; + +import tanya.meta.trait; + +// Socket API error. +private template SAError(int posix, int wsa = posix) +{ + version (Windows) + { + enum SAError = 10000 + wsa; + } + else + { + alias SAError = posix; + } +} + +// Error for Windows and Posix separately. +private template NativeError(int posix, int win) +{ + version (Windows) + { + alias NativeError = win; + } + else + { + alias NativeError = posix; + } +} + +version (Windows) +{ + private enum eProtocolError = -71; +} +else version (OpenBSD) +{ + private enum eProtocolError = -71; +} +else +{ + private enum eProtocolError = 71; +} + +/** + * System error code. + */ +struct ErrorCode +{ + /** + * Error code numbers. + */ + enum ErrorNo : int + { + /// The operation completed successfully. + success = 0, + + /// Operation not permitted. + noPermission = NativeError!(1, 5), + + /// Interrupted system call. + interrupted = SAError!4, + + /// Bad file descriptor. + badDescriptor = SAError!9, + + /// An operation on a non-blocking socket would block. + wouldBlock = SAError!(11, 35), + + /// Out of memory. + noMemory = NativeError!(12, 14), + + /// Access denied. + accessDenied = SAError!13, + + /// An invalid pointer address detected. + fault = SAError!14, + + /// No such device. + noSuchDevice = NativeError!(19, 20), + + /// An invalid argument was supplied. + invalidArgument = SAError!22, + + /// The limit on the number of open file descriptors. + tooManyDescriptors = NativeError!(23, 331), + + /// The limit on the number of open file descriptors. + noDescriptors = SAError!24, + + /// Broken pipe. + brokenPipe = NativeError!(32, 109), + + /// The name was too long. + nameTooLong = SAError!(36, 63), + + /// A socket operation was attempted on a non-socket. + notSocket = SAError!(88, 38), + + /// Protocol error. + protocolError = eProtocolError, + + /// Message too long. + messageTooLong = SAError!(90, 40), + + /// Wrong protocol type for socket. + wrongProtocolType = SAError!(91, 41), + + /// Protocol not available. + noProtocolOption = SAError!(92, 42), + + /// The protocol is not implemented or has not been configured. + protocolNotSupported = SAError!(93, 43), + + /// The support for the specified socket type does not exist in this + /// address family. + socketNotSupported = SAError!(94, 44), + + /// The address family is no supported by the protocol family. + operationNotSupported = SAError!(95, 45), + + /// Address family specified is not supported. + addressFamilyNotSupported = SAError!(97, 47), + + /// Address already in use. + addressInUse = SAError!(98, 48), + + /// The network is not available. + networkDown = SAError!(100, 50), + + /// No route to host. + networkUnreachable = SAError!(101, 51), + + /// Network dropped connection because of reset. + networkReset = SAError!(102, 52), + + /// The connection has been aborted. + connectionAborted = SAError!(103, 53), + + /// Connection reset by peer. + connectionReset = SAError!(104, 54), + + /// No free buffer space is available for a socket operation. + noBufferSpace = SAError!(105, 55), + + /// Transport endpoint is already connected. + alreadyConnected = SAError!(106, 56), + + /// Transport endpoint is not connected. + notConnected = SAError!(107, 57), + + /// Cannot send after transport endpoint shutdown. + shutdown = SAError!(108, 58), + + /// The connection attempt timed out, or the connected host has failed + /// to respond. + timedOut = SAError!(110, 60), + + /// Connection refused. + connectionRefused = SAError!(111, 61), + + /// Host is down. + hostDown = SAError!(112, 64), + + /// No route to host. + hostUnreachable = SAError!(113, 65), + + /// Operation already in progress. + alreadyStarted = SAError!(114, 37), + + /// Operation now in progress. + inProgress = SAError!(115, 36), + + /// Operation cancelled. + cancelled = SAError!(125, 103), + } + + /** + * Error descriptions. + */ + private enum ErrorStr : string + { + success = "The operation completed successfully", + noPermission = "Operation not permitted", + interrupted = "Interrupted system call", + badDescriptor = "Bad file descriptor", + wouldBlock = "An operation on a non-blocking socket would block", + noMemory = "Out of memory", + accessDenied = "Access denied", + fault = "An invalid pointer address detected", + noSuchDevice = "No such device", + invalidArgument = "An invalid argument was supplied", + tooManyDescriptors = "The limit on the number of open file descriptors", + noDescriptors = "The limit on the number of open file descriptors", + brokenPipe = "Broken pipe", + nameTooLong = "The name was too long", + notSocket = "A socket operation was attempted on a non-socket", + protocolError = "Protocol error", + messageTooLong = "Message too long", + wrongProtocolType = "Wrong protocol type for socket", + noProtocolOption = "Protocol not available", + protocolNotSupported = "The protocol is not implemented or has not been configured", + socketNotSupported = "Socket type not supported", + operationNotSupported = "The address family is no supported by the protocol family", + addressFamilyNotSupported = "Address family specified is not supported", + addressInUse = "Address already in use", + networkDown = "The network is not available", + networkUnreachable = "No route to host", + networkReset = "Network dropped connection because of reset", + connectionAborted = "The connection has been aborted", + connectionReset = "Connection reset by peer", + noBufferSpace = "No free buffer space is available for a socket operation", + alreadyConnected = "Transport endpoint is already connected", + notConnected = "Transport endpoint is not connected", + shutdown = "Cannot send after transport endpoint shutdown", + timedOut = "Operation timed out", + connectionRefused = "Connection refused", + hostDown = "Host is down", + hostUnreachable = "No route to host", + alreadyStarted = "Operation already in progress", + inProgress = "Operation now in progress", + cancelled = "Operation cancelled", + } + + /** + * Constructor. + * + * Params: + * value = Numeric error code. + */ + this(const ErrorNo value) @nogc nothrow pure @safe + { + this.value_ = value; + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec; + assert(ec == ErrorCode.success); + + ec = ErrorCode.fault; + assert(ec == ErrorCode.fault); + } + + /** + * Resets this $(D_PSYMBOL ErrorCode) to default + * ($(D_PSYMBOL ErrorCode.success)). + */ + void reset() @nogc nothrow pure @safe + { + this.value_ = ErrorNo.success; + } + + /// + @nogc nothrow pure @safe unittest + { + auto ec = ErrorCode(ErrorCode.fault); + assert(ec == ErrorCode.fault); + + ec.reset(); + assert(ec == ErrorCode.success); + } + + /** + * Returns: Numeric error code. + */ + ErrorNo opCast(T : ErrorNo)() const + { + return this.value_; + } + + /// ditto + ErrorNo opCast(T : int)() const + { + return this.value_; + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec = ErrorCode.fault; + auto errorNo = cast(ErrorCode.ErrorNo) ec; + + assert(errorNo == ErrorCode.fault); + static assert(is(typeof(cast(int) ec))); + } + + /** + * Assigns another error code or error code number. + * + * Params: + * that = Numeric error code. + * + * Returns: $(D_KEYWORD this). + */ + ref ErrorCode opAssign(const ErrorNo that) @nogc nothrow pure @safe + { + this.value_ = that; + return this; + } + + /// ditto + ref ErrorCode opAssign(const ErrorCode that) @nogc nothrow pure @safe + { + this.value_ = that.value_; + return this; + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec; + assert(ec == ErrorCode.success); + + ec = ErrorCode.fault; + assert(ec == ErrorCode.fault); + } + + /// + @nogc nothrow pure @safe unittest + { + auto ec1 = ErrorCode(ErrorCode.fault); + ErrorCode ec2; + assert(ec2 == ErrorCode.success); + + ec2 = ec1; + assert(ec1 == ec2); + } + + /** + * Equality with another error code or error code number. + * + * Params: + * that = Numeric error code. + * + * Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal. + */ + bool opEquals(const ErrorNo that) const @nogc nothrow pure @safe + { + return this.value_ == that; + } + + /// ditto + bool opEquals(const ErrorCode that) const @nogc nothrow pure @safe + { + return this.value_ == that.value_; + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec1 = ErrorCode.fault; + ErrorCode ec2 = ErrorCode.accessDenied; + + assert(ec1 != ec2); + assert(ec1 != ErrorCode.accessDenied); + assert(ErrorCode.fault != ec2); + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec1 = ErrorCode.fault; + ErrorCode ec2 = ErrorCode.fault; + + assert(ec1 == ec2); + assert(ec1 == ErrorCode.fault); + assert(ErrorCode.fault == ec2); + } + + /** + * Returns string describing the error number. If a description for a + * specific error number is not available, returns $(D_KEYWORD null). + * + * Returns: String describing the error number. + */ + string toString() const @nogc nothrow pure @safe + { + foreach (e; __traits(allMembers, ErrorNo)) + { + if (__traits(getMember, ErrorNo, e) == this.value_) + { + return __traits(getMember, ErrorStr, e); + } + } + return null; + } + + /// + @nogc nothrow pure @safe unittest + { + ErrorCode ec = ErrorCode.fault; + assert(ec.toString() == "An invalid pointer address detected"); + } + + @nogc nothrow pure @safe unittest + { + ErrorCode ec = cast(ErrorCode.ErrorNo) -1; + assert(ec.toString() is null); + } + + private ErrorNo value_ = ErrorNo.success; + + alias ErrorNo this; +} diff --git a/os/tanya/os/package.d b/os/tanya/os/package.d new file mode 100644 index 0000000..070ec44 --- /dev/null +++ b/os/tanya/os/package.d @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This package provides platform-independent interfaces to operating system + * functionality. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/package.d, + * tanya/os/package.d) + */ +module tanya.os; + +public import tanya.os.error; diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 7eae9cd..fcfb1c0 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -21,7 +21,7 @@ module tanya.algorithm.iteration; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index f0c156d..ddb2743 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -14,284 +14,21 @@ */ module tanya.algorithm.mutation; -import tanya.conv; static import tanya.memory.op; +static import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; version (unittest) import tanya.test.stub; -private void deinitialize(bool zero, T)(ref T value) -{ - static if (is(T == U[S], U, size_t S)) - { - foreach (ref e; value) - { - deinitialize!zero(e); - } - } - else - { - static if (isNested!T) - { - // Don't override the context pointer. - enum size_t size = T.sizeof - (void*).sizeof; - } - else - { - enum size_t size = T.sizeof; - } - static if (zero) - { - tanya.memory.op.fill!0((cast(void*) &value)[0 .. size]); - } - else - { - tanya.memory.op.copy(typeid(T).initializer()[0 .. size], - (&value)[0 .. 1]); - } - } -} - -/** - * Moves $(D_PARAM source) into $(D_PARAM target) assuming that - * $(D_PARAM target) isn't initialized. - * - * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places - * the $(D_PARAM source) into a valid but unspecified state, which means that - * after moving $(D_PARAM source) can be destroyed or assigned a new value, but - * accessing it yields an unspecified value. No postblits or destructors are - * called. If the $(D_PARAM target) should be destroyed before, use - * $(D_PSYMBOL move). - * - * $(D_PARAM source) and $(D_PARAM target) must be different objects. - * - * Params: - * T = Object type. - * source = Source object. - * target = Target object. - * - * See_Also: $(D_PSYMBOL move), - * $(D_PSYMBOL hasElaborateCopyConstructor), - * $(D_PSYMBOL hasElaborateDestructor). - * - * Precondition: `&source !is &target`. - */ -void moveEmplace(T)(ref T source, ref T target) @system -in -{ - assert(&source !is &target, "Source and target must be different"); -} -do -{ - static if (is(T == struct) || isStaticArray!T) - { - tanya.memory.op.copy((&source)[0 .. 1], (&target)[0 .. 1]); - - static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) - { - static if (__VERSION__ >= 2083) // __traits(isZeroInit) available. - { - deinitialize!(__traits(isZeroInit, T))(source); - } - else - { - if (typeid(T).initializer().ptr is null) - { - deinitialize!true(source); - } - else - { - deinitialize!false(source); - } - } - } - } - else - { - target = source; - } -} - -/// -@nogc nothrow pure @system unittest -{ - static struct S - { - int member = 5; - - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - S source, target = void; - moveEmplace(source, target); - assert(target.member == 5); +deprecated("Use tanya.memory.lifecycle.swap instead") +alias swap = tanya.memory.lifecycle.swap; - int x1 = 5, x2; - moveEmplace(x1, x2); - assert(x2 == 5); -} +deprecated("Use tanya.memory.lifecycle.moveEmplace instead") +alias moveEmplace = tanya.memory.lifecycle.moveEmplace; -// Is pure. -@nogc nothrow pure @system unittest -{ - struct S - { - this(this) - { - } - } - S source, target = void; - static assert(is(typeof({ moveEmplace(source, target); }))); -} - -// Moves nested. -@nogc nothrow pure @system unittest -{ - struct Nested - { - void method() @nogc nothrow pure @safe - { - } - } - Nested source, target = void; - moveEmplace(source, target); - assert(source == target); -} - -// Emplaces static arrays. -@nogc nothrow pure @system unittest -{ - static struct S - { - size_t member; - this(size_t i) @nogc nothrow pure @safe - { - this.member = i; - } - ~this() @nogc nothrow pure @safe - { - } - } - S[2] source = [ S(5), S(5) ], target = void; - moveEmplace(source, target); - assert(source[0].member == 0); - assert(target[0].member == 5); - assert(source[1].member == 0); - assert(target[1].member == 5); -} - -/** - * Moves $(D_PARAM source) into $(D_PARAM target) assuming that - * $(D_PARAM target) isn't initialized. - * - * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places - * the $(D_PARAM source) into a valid but unspecified state, which means that - * after moving $(D_PARAM source) can be destroyed or assigned a new value, but - * accessing it yields an unspecified value. $(D_PARAM target) is destroyed before - * the new value is assigned. If $(D_PARAM target) isn't initialized and - * therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used. - * - * If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source - * as rvalue without calling its copy constructor or destructor. - * - * $(D_PARAM source) and $(D_PARAM target) are the same object, - * $(D_PSYMBOL move) does nothing. - * - * Params: - * T = Object type. - * source = Source object. - * target = Target object. - * - * See_Also: $(D_PSYMBOL moveEmplace). - */ -void move(T)(ref T source, ref T target) -{ - if ((() @trusted => &source is &target)()) - { - return; - } - static if (hasElaborateDestructor!T) - { - target.__xdtor(); - } - (() @trusted => moveEmplace(source, target))(); -} - -/// ditto -T move(T)(ref T source) @trusted -{ - static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) - { - T target = void; - moveEmplace(source, target); - return target; - } - else - { - return source; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct S - { - int member = 5; - - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - S source, target = void; - move(source, target); - assert(target.member == 5); - assert(move(target).member == 5); - - int x1 = 5, x2; - move(x1, x2); - assert(x2 == 5); - assert(move(x2) == 5); -} - -// Moves if source is target. -@nogc nothrow pure @safe unittest -{ - int x = 5; - move(x, x); - assert(x == 5); -} - -/** - * Exchanges the values of $(D_PARAM a) and $(D_PARAM b). - * - * $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b) - * without calling its postblits or destructors. - * - * Params: - * a = The first object. - * b = The second object. - */ -void swap(T)(ref T a, ref T b) @trusted -{ - T tmp = void; - moveEmplace(a, tmp); - moveEmplace(b, a); - moveEmplace(tmp, b); -} - -/// -@nogc nothrow pure @safe unittest -{ - int a = 3, b = 5; - swap(a, b); - assert(a == 5); - assert(b == 3); -} +deprecated("Use tanya.memory.lifecycle.move instead") +alias move = tanya.memory.lifecycle.move; /** * Copies the $(D_PARAM source) range into the $(D_PARAM target) range. @@ -494,7 +231,7 @@ if (isInputRange!Range && hasLvalueElements!Range for (; !range.empty; range.popFront()) { ElementType!Range* p = &range.front; - emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); + tanya.memory.lifecycle.emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); } } else @@ -577,13 +314,7 @@ if (isInputRange!Range && hasLvalueElements!Range) void destroyAll(Range)(Range range) if (isInputRange!Range && hasLvalueElements!Range) { - static if (hasElaborateDestructor!(ElementType!Range)) - { - foreach (ref e; range) - { - destroy(e); - } - } + tanya.memory.lifecycle.destroyAllImpl!(Range, ElementType!Range)(range); } /// @@ -632,7 +363,7 @@ if (isForwardRange!Range && hasSwappableElements!Range) while (!front.empty && !next.empty && !sameHead(front, next)) { - swap(front.front, next.front); + tanya.memory.lifecycle.swap(front.front, next.front); front.popFront(); next.popFront(); diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index 2b3adfa..1c6842f 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -17,7 +17,6 @@ module tanya.container.array; import core.checkedint; import tanya.algorithm.comparison; import tanya.algorithm.mutation; -import tanya.functional; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 1e7e28f..408849a 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -14,9 +14,9 @@ */ module tanya.container.entry; -import tanya.algorithm.mutation; import tanya.container.array; import tanya.memory.allocator; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.typecons; diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 09a5c9f..e6e01d5 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -16,7 +16,6 @@ module tanya.container.list; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; import tanya.container.entry; import tanya.memory; import tanya.meta.trait; diff --git a/source/tanya/conv.d b/source/tanya/conv.d index a78c2bb..f602e01 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -14,15 +14,13 @@ */ module tanya.conv; -import tanya.algorithm.mutation; import tanya.container.string; -import tanya.format; import tanya.memory; -import tanya.memory.op; +deprecated("Use tanya.memory.lifecycle.emplace instead") +public import tanya.memory.lifecycle : emplace; import tanya.meta.trait; import tanya.meta.transform; -import tanya.range.array; -import tanya.range.primitive; +import tanya.range; version (unittest) { @@ -30,269 +28,6 @@ version (unittest) import tanya.test.stub; } -/** - * Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the - * given arguments. - * - * If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference - * of type $(D_PARAM T), otherwise a pointer to the constructed object is - * returned. - * - * If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer) - * should be an instance of the outer class. - * - * $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If - * $(D_PARAM T) isn't an aggregate type and doesn't have a constructor, - * $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`, - * `Args[0]` should be implicitly convertible to $(D_PARAM T) then. - * - * Params: - * T = Constructed type. - * U = Type of the outer class if $(D_PARAM T) is a nested class. - * Args = Types of the constructor arguments if $(D_PARAM T) has a constructor - * or the type of the initial value. - * outer = Outer class instance if $(D_PARAM T) is a nested class. - * args = Constructor arguments if $(D_PARAM T) has a constructor or the - * initial value. - * - * Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory). - * - * Precondition: `memory.length == stateSize!T`. - * Postcondition: $(D_PARAM memory) and the result point to the same memory. - */ -T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args) -if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U)) -in (memory.length >= stateSize!T) -out (result; memory.ptr is (() @trusted => cast(void*) result)()) -{ - copy(typeid(T).initializer, memory); - - auto result = (() @trusted => cast(T) memory.ptr)(); - result.outer = outer; - - static if (is(typeof(result.__ctor(args)))) - { - result.__ctor(args); - } - - return result; -} - -/// ditto -T emplace(T, Args...)(void[] memory, auto ref Args args) -if (is(T == class) && !isAbstractClass!T && !isInnerClass!T) -in (memory.length == stateSize!T) -out (result; memory.ptr is (() @trusted => cast(void*) result)()) -{ - copy(typeid(T).initializer, memory); - - auto result = (() @trusted => cast(T) memory.ptr)(); - static if (is(typeof(result.__ctor(args)))) - { - result.__ctor(args); - } - return result; -} - -/// -@nogc nothrow pure @safe unittest -{ - import tanya.memory : stateSize; - - class C - { - int i = 5; - class Inner - { - int i; - - this(int param) pure nothrow @safe @nogc - { - this.i = param; - } - } - } - ubyte[stateSize!C] memory1; - ubyte[stateSize!(C.Inner)] memory2; - - auto c = emplace!C(memory1); - assert(c.i == 5); - - auto inner = emplace!(C.Inner)(memory2, c, 8); - assert(c.i == 5); - assert(inner.i == 8); - assert(inner.outer is c); -} - -/// ditto -T* emplace(T, Args...)(void[] memory, auto ref Args args) -if (!isAggregateType!T && (Args.length <= 1)) -in (memory.length >= T.sizeof) -out (result; memory.ptr is result) -{ - auto result = (() @trusted => cast(T*) memory.ptr)(); - static if (Args.length == 1) - { - *result = T(args[0]); - } - else - { - *result = T.init; - } - return result; -} - -private void initializeOne(T)(ref void[] memory, ref T* result) @trusted -{ - static if (!hasElaborateAssign!T && isAssignable!T) - { - *result = T.init; - } - else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available. - && __traits(isZeroInit, T)) - { - memory.ptr[0 .. T.sizeof].fill!0; - } - else - { - static immutable T init = T.init; - copy((&init)[0 .. 1], memory); - } -} - -/// ditto -T* emplace(T, Args...)(void[] memory, auto ref Args args) -if (!isPolymorphicType!T && isAggregateType!T) -in (memory.length >= T.sizeof) -out (result; memory.ptr is result) -{ - auto result = (() @trusted => cast(T*) memory.ptr)(); - - static if (Args.length == 0) - { - static assert(is(typeof({ static T t; })), - "Default constructor is disabled"); - initializeOne(memory, result); - } - else static if (is(typeof(result.__ctor(args)))) - { - initializeOne(memory, result); - result.__ctor(args); - } - else static if (Args.length == 1 && is(typeof({ T t = args[0]; }))) - { - ((ref arg) @trusted => - copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]); - static if (hasElaborateCopyConstructor!T) - { - result.__postblit(); - } - } - else static if (is(typeof({ T t = T(args); }))) - { - auto init = T(args); - (() @trusted => moveEmplace(init, *result))(); - } - else - { - static assert(false, - "Unable to construct value with the given arguments"); - } - return result; -} - -/// -@nogc nothrow pure @safe unittest -{ - ubyte[4] memory; - - auto i = emplace!int(memory); - static assert(is(typeof(i) == int*)); - assert(*i == 0); - - i = emplace!int(memory, 5); - assert(*i == 5); - - static struct S - { - int i; - @disable this(); - @disable this(this); - this(int i) @nogc nothrow pure @safe - { - this.i = i; - } - } - auto s = emplace!S(memory, 8); - static assert(is(typeof(s) == S*)); - assert(s.i == 8); -} - -// Handles "Cannot access frame pointer" error. -@nogc nothrow pure @safe unittest -{ - struct F - { - ~this() @nogc nothrow pure @safe - { - } - } - static assert(is(typeof(emplace!F((void[]).init)))); -} - -// Can emplace structs without a constructor -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(emplace!WithDtor(null, WithDtor())))); - static assert(is(typeof(emplace!WithDtor(null)))); -} - -// Doesn't call a destructor on uninitialized elements -@nogc nothrow pure @system unittest -{ - static struct SWithDtor - { - private bool canBeInvoked = false; - ~this() @nogc nothrow pure @safe - { - assert(this.canBeInvoked); - } - } - void[SWithDtor.sizeof] memory = void; - auto actual = emplace!SWithDtor(memory[], SWithDtor(true)); - assert(actual.canBeInvoked); -} - -// Initializes structs if no arguments are given -@nogc nothrow pure @safe unittest -{ - static struct SEntry - { - byte content; - } - ubyte[1] mem = [3]; - - assert(emplace!SEntry(cast(void[]) mem[0 .. 1]).content == 0); -} - -// Postblit is called when emplacing a struct -@nogc nothrow pure @system unittest -{ - static struct S - { - bool called = false; - this(this) @nogc nothrow pure @safe - { - this.called = true; - } - } - S target; - S* sp = ⌖ - - emplace!S(sp[0 .. 1], S()); - assert(target.called); -} - /** * Thrown if a type conversion fails. */ diff --git a/source/tanya/functional.d b/source/tanya/functional.d index f628a51..9a1c3a1 100644 --- a/source/tanya/functional.d +++ b/source/tanya/functional.d @@ -2,77 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/** +/* * Functions that manipulate other functions and their argument lists. * - * Copyright: Eugene Wissner 2018. + * Copyright: Eugene Wissner 2018-2019. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/functional.d, * tanya/functional.d) */ +deprecated("Use tanya.memory.lifecycle instead") module tanya.functional; -import tanya.algorithm.mutation; -import tanya.meta.metafunction; - -/** - * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out) - * storage classes. - * - * $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an - * argument list of the same length that can be for example passed to a - * function accepting the arguments of this type. - * - * Params: - * args = Argument list. - * - * Returns: $(D_PARAM args) with their original storage classes. - */ -template forward(args...) -{ - static if (args.length == 0) - { - alias forward = AliasSeq!(); - } - else static if (__traits(isRef, args[0]) || __traits(isOut, args[0])) - { - static if (args.length == 1) - { - alias forward = args[0]; - } - else - { - alias forward = AliasSeq!(args[0], forward!(args[1 .. $])); - } - } - else - { - @property auto forwardOne() - { - return move(args[0]); - } - static if (args.length == 1) - { - alias forward = forwardOne; - } - else - { - alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $])); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof((int i) { int v = forward!i; }))); - static assert(is(typeof((ref int i) { int v = forward!i; }))); - static assert(is(typeof({ - void f(int i, ref int j, out int k) - { - f(forward!(i, j, k)); - } - }))); -} +public import tanya.memory.lifecycle : forward; diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d deleted file mode 100644 index 3d05a37..0000000 --- a/source/tanya/memory/allocator.d +++ /dev/null @@ -1,81 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * This module contains the interface for implementing custom allocators. - * - * Allocators are classes encapsulating memory allocation strategy. This allows - * to decouple memory management from the algorithms and the data. - * - * Copyright: Eugene Wissner 2016-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/allocator.d, - * tanya/memory/allocator.d) - */ -module tanya.memory.allocator; - -/** - * Abstract class implementing a basic allocator. - */ -interface Allocator -{ - /** - * Returns: Alignment offered. - */ - @property uint alignment() const shared pure nothrow @safe @nogc; - - /** - * Allocates $(D_PARAM size) bytes of memory. - * - * Params: - * size = Amount of memory to allocate. - * - * Returns: Pointer to the new allocated memory. - */ - void[] allocate(size_t size) shared pure nothrow @nogc; - - /** - * Deallocates a memory block. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) shared pure nothrow @nogc; - - /** - * Increases or decreases the size of a memory block. - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: Pointer to the allocated memory. - */ - bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc; - - /** - * Reallocates a memory block in place if possible or returns - * $(D_KEYWORD false). This function cannot be used to allocate or - * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or - * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. - */ - bool reallocateInPlace(ref void[] p, size_t size) - shared pure nothrow @nogc; -} - -package template GetPureInstance(T : Allocator) -{ - alias GetPureInstance = shared(T) function() - pure nothrow @nogc; -} diff --git a/source/tanya/memory/lifecycle.d b/source/tanya/memory/lifecycle.d deleted file mode 100644 index 2221af3..0000000 --- a/source/tanya/memory/lifecycle.d +++ /dev/null @@ -1,360 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Lifecycle management functions, types and related exceptions. - * - * Copyright: Eugene Wissner 2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/init.d, - * tanya/memory/init.d) - */ -module tanya.memory.lifecycle; - -import tanya.algorithm.mutation; -import tanya.conv; -import tanya.memory; -import tanya.meta.trait; -import tanya.range.primitive; - -/** - * Error thrown if memory allocation fails. - */ -final class OutOfMemoryError : Error -{ - /** - * Constructs new error. - * - * Params: - * msg = The message for the exception. - * file = The file where the exception occurred. - * line = The line number where the exception occurred. - * next = The previous exception in the chain of exceptions, if any. - */ - this(string msg = "Out of memory", - string file = __FILE__, - size_t line = __LINE__, - Throwable next = null) @nogc nothrow pure @safe - { - super(msg, file, line, next); - } - - /// ditto - this(string msg, - Throwable next, - string file = __FILE__, - size_t line = __LINE__) @nogc nothrow pure @safe - { - super(msg, file, line, next); - } -} - -/** - * Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it. - * - * Params: - * msg = Custom error message. - * - * Throws: $(D_PSYMBOL OutOfMemoryError). - */ -void onOutOfMemoryError(string msg = "Out of memory") -@nogc nothrow pure @trusted -{ - static ubyte[stateSize!OutOfMemoryError] memory; - alias PureType = OutOfMemoryError function(string) @nogc nothrow pure; - throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg); -} - -// From druntime -extern (C) -private void _d_monitordelete(Object h, bool det) @nogc nothrow pure; - -/* - * Internal function used to create, resize or destroy a dynamic array. It - * may throw $(D_PSYMBOL OutOfMemoryError). The new - * allocated part of the array isn't initialized. This function can be trusted - * only in the data structures that can ensure that the array is - * allocated/rellocated/deallocated with the same allocator. - * - * Params: - * T = Element type of the array being created. - * allocator = The allocator used for getting memory. - * array = A reference to the array being changed. - * length = New array length. - * - * Returns: $(D_PARAM array). - */ -package(tanya) T[] resize(T)(shared Allocator allocator, - auto ref T[] array, - const size_t length) @trusted -{ - if (length == 0) - { - if (allocator.deallocate(array)) - { - return null; - } - else - { - onOutOfMemoryError(); - } - } - - void[] buf = array; - if (!allocator.reallocate(buf, length * T.sizeof)) - { - onOutOfMemoryError(); - } - // Casting from void[] is unsafe, but we know we cast to the original type. - array = cast(T[]) buf; - - return array; -} - -@nogc nothrow pure @safe unittest -{ - int[] p; - - p = defaultAllocator.resize(p, 20); - assert(p.length == 20); - - p = defaultAllocator.resize(p, 30); - assert(p.length == 30); - - p = defaultAllocator.resize(p, 10); - assert(p.length == 10); - - p = defaultAllocator.resize(p, 0); - assert(p is null); -} - -/* - * Destroys the object. - * Returns the memory should be freed. - */ -package(tanya) void[] finalize(T)(ref T* p) -{ - if (p is null) - { - return null; - } - static if (hasElaborateDestructor!T) - { - destroy(*p); - } - return (cast(void*) p)[0 .. T.sizeof]; -} - -package(tanya) void[] finalize(T)(ref T p) -if (isPolymorphicType!T) -{ - if (p is null) - { - return null; - } - static if (is(T == interface)) - { - version(Windows) - { - import core.sys.windows.unknwn : IUnknown; - static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in " - ~ __PRETTY_FUNCTION__); - } - auto ob = cast(Object) p; - } - else - { - alias ob = p; - } - auto ptr = cast(void*) ob; - auto support = ptr[0 .. typeid(ob).initializer.length]; - - auto ppv = cast(void**) ptr; - if (!*ppv) - { - return null; - } - auto pc = cast(ClassInfo*) *ppv; - scope (exit) - { - *ppv = null; - } - - auto c = *pc; - do - { - // Assume the destructor is @nogc. Leave it nothrow since the destructor - // shouldn't throw and if it does, it is an error anyway. - if (c.destructor) - { - alias DtorType = void function(Object) pure nothrow @safe @nogc; - (cast(DtorType) c.destructor)(ob); - } - } - while ((c = c.base) !is null); - - if (ppv[1]) // if monitor is not null - { - _d_monitordelete(cast(Object) ptr, true); - } - return support; -} - -package(tanya) void[] finalize(T)(ref T[] p) -{ - destroyAll(p); - return p; -} - -/** - * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). - * It is assumed the respective entities had been allocated with the same - * allocator. - * - * Params: - * T = Type of $(D_PARAM p). - * allocator = Allocator the $(D_PARAM p) was allocated with. - * p = Object or array to be destroyed. - */ -void dispose(T)(shared Allocator allocator, auto ref T p) -{ - () @trusted { allocator.deallocate(finalize(p)); }(); - p = null; -} - -@nogc nothrow pure @system unittest -{ - static struct S - { - ~this() @nogc nothrow pure @safe - { - } - } - auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); - - defaultAllocator.dispose(p); -} - -// Works with interfaces. -@nogc nothrow pure @safe unittest -{ - interface I - { - } - class C : I - { - } - auto c = defaultAllocator.make!C(); - I i = c; - - defaultAllocator.dispose(i); - defaultAllocator.dispose(i); -} - -/** - * Constructs a new class instance of type $(D_PARAM T) using $(D_PARAM args) - * as the parameter list for the constructor of $(D_PARAM T). - * - * Params: - * T = Class type. - * A = Types of the arguments to the constructor of $(D_PARAM T). - * allocator = Allocator. - * args = Constructor arguments of $(D_PARAM T). - * - * Returns: Newly created $(D_PSYMBOL T). - * - * Precondition: $(D_INLINECODE allocator !is null) - */ -T make(T, A...)(shared Allocator allocator, auto ref A args) -if (is(T == class)) -in (allocator !is null) -{ - auto mem = (() @trusted => allocator.allocate(stateSize!T))(); - if (mem is null) - { - onOutOfMemoryError(); - } - scope (failure) - { - () @trusted { allocator.deallocate(mem); }(); - } - - return emplace!T(mem[0 .. stateSize!T], args); -} - -/** - * Constructs a value object of type $(D_PARAM T) using $(D_PARAM args) - * as the parameter list for the constructor of $(D_PARAM T) and returns a - * pointer to the new object. - * - * Params: - * T = Object type. - * A = Types of the arguments to the constructor of $(D_PARAM T). - * allocator = Allocator. - * args = Constructor arguments of $(D_PARAM T). - * - * Returns: Pointer to the created object. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ -T* make(T, A...)(shared Allocator allocator, auto ref A args) -if (!is(T == interface) - && !is(T == class) - && !isAssociativeArray!T - && !isArray!T) -in (allocator !is null) -{ - auto mem = (() @trusted => allocator.allocate(stateSize!T))(); - if (mem is null) - { - onOutOfMemoryError(); - } - scope (failure) - { - () @trusted { allocator.deallocate(mem); }(); - } - return emplace!T(mem[0 .. stateSize!T], args); -} - -/// -@nogc nothrow pure @safe unittest -{ - int* i = defaultAllocator.make!int(5); - assert(*i == 5); - defaultAllocator.dispose(i); -} - -/** - * Constructs a new array with $(D_PARAM n) elements. - * - * Params: - * T = Array type. - * allocator = Allocator. - * n = Array size. - * - * Returns: Newly created array. - * - * Precondition: $(D_INLINECODE allocator !is null - * && n <= size_t.max / ElementType!T.sizeof) - */ -T make(T)(shared Allocator allocator, const size_t n) -if (isArray!T) -in (allocator !is null) -in (n <= size_t.max / ElementType!T.sizeof) -{ - auto ret = allocator.resize!(ElementType!T)(null, n); - ret.uninitializedFill(ElementType!T.init); - return ret; -} - -/// -@nogc nothrow pure @safe unittest -{ - int[] i = defaultAllocator.make!(int[])(2); - assert(i.length == 2); - assert(i[0] == int.init && i[1] == int.init); - defaultAllocator.dispose(i); -} diff --git a/source/tanya/memory/mallocator.d b/source/tanya/memory/mallocator.d deleted file mode 100644 index fdf7ede..0000000 --- a/source/tanya/memory/mallocator.d +++ /dev/null @@ -1,233 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and - * $(D_PSYMBOL free). - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mallocator.d, - * tanya/memory/mallocator.d) - */ -module tanya.memory.mallocator; - -version (TanyaNative) -{ -} -else: - -import core.stdc.stdlib; -import tanya.memory.allocator; - -/** - * Wrapper for $(D_PSYMBOL malloc)/$(D_PSYMBOL realloc)/$(D_PSYMBOL free) from - * the C standard library. - */ -final class Mallocator : Allocator -{ - private alias MallocType = extern (C) void* function(size_t) - @nogc nothrow pure @system; - private alias FreeType = extern (C) void function(void*) - @nogc nothrow pure @system; - private alias ReallocType = extern (C) void* function(void*, size_t) - @nogc nothrow pure @system; - - /** - * Allocates $(D_PARAM size) bytes of memory. - * - * Params: - * size = Amount of memory to allocate. - * - * Returns: The pointer to the new allocated memory. - */ - void[] allocate(size_t size) @nogc nothrow pure shared @system - { - if (size == 0) - { - return null; - } - auto p = (cast(MallocType) &malloc)(size + psize); - - return p is null ? null : p[psize .. psize + size]; - } - - /// - @nogc nothrow pure @system unittest - { - auto p = Mallocator.instance.allocate(20); - assert(p.length == 20); - Mallocator.instance.deallocate(p); - - p = Mallocator.instance.allocate(0); - assert(p.length == 0); - } - - /** - * Deallocates a memory block. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) @nogc nothrow pure shared @system - { - if (p !is null) - { - (cast(FreeType) &free)(p.ptr - psize); - } - return true; - } - - /// - @nogc nothrow pure @system unittest - { - void[] p; - assert(Mallocator.instance.deallocate(p)); - - p = Mallocator.instance.allocate(10); - assert(Mallocator.instance.deallocate(p)); - } - - /** - * Reallocating in place isn't supported. - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: $(D_KEYWORD false). - */ - bool reallocateInPlace(ref void[] p, size_t size) - @nogc nothrow pure shared @system - { - cast(void) size; - return false; - } - - /// - @nogc nothrow pure @system unittest - { - void[] p; - assert(!Mallocator.instance.reallocateInPlace(p, 8)); - } - - /** - * Increases or decreases the size of a memory block. - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: Whether the reallocation was successful. - */ - bool reallocate(ref void[] p, size_t size) - @nogc nothrow pure shared @system - { - if (size == 0) - { - if (deallocate(p)) - { - p = null; - return true; - } - } - else if (p is null) - { - p = allocate(size); - return p is null ? false : true; - } - else - { - auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize); - - if (r !is null) - { - p = r[psize .. psize + size]; - return true; - } - } - return false; - } - - /// - @nogc nothrow pure @system unittest - { - void[] p; - - assert(Mallocator.instance.reallocate(p, 20)); - assert(p.length == 20); - - assert(Mallocator.instance.reallocate(p, 30)); - assert(p.length == 30); - - assert(Mallocator.instance.reallocate(p, 10)); - assert(p.length == 10); - - assert(Mallocator.instance.reallocate(p, 0)); - assert(p is null); - } - - // Fails with false - @nogc nothrow pure @system unittest - { - void[] p = Mallocator.instance.allocate(20); - void[] oldP = p; - assert(!Mallocator.instance.reallocate(p, size_t.max - Mallocator.psize * 2)); - assert(oldP is p); - Mallocator.instance.deallocate(p); - } - - /** - * Returns: The alignment offered. - */ - @property uint alignment() const @nogc nothrow pure @safe shared - { - return (void*).alignof; - } - - private nothrow @nogc unittest - { - assert(Mallocator.instance.alignment == (void*).alignof); - } - - static private shared(Mallocator) instantiate() @nogc nothrow @system - { - if (instance_ is null) - { - const size = __traits(classInstanceSize, Mallocator) + psize; - void* p = malloc(size); - - if (p !is null) - { - p[psize .. size] = typeid(Mallocator).initializer[]; - instance_ = cast(shared Mallocator) p[psize .. size].ptr; - } - } - return instance_; - } - - /** - * Static allocator instance and initializer. - * - * Returns: The global $(D_PSYMBOL Allocator) instance. - */ - static @property shared(Mallocator) instance() @nogc nothrow pure @system - { - return (cast(GetPureInstance!Mallocator) &instantiate)(); - } - - /// - @nogc nothrow pure @system unittest - { - assert(instance is instance); - } - - private enum ushort psize = 8; - - private shared static Mallocator instance_; -} diff --git a/source/tanya/memory/mmappool.d b/source/tanya/memory/mmappool.d deleted file mode 100644 index 5c42241..0000000 --- a/source/tanya/memory/mmappool.d +++ /dev/null @@ -1,657 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Native allocator. - * - * Copyright: Eugene Wissner 2016-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mmappool.d, - * tanya/memory/mmappool.d) - */ -module tanya.memory.mmappool; - -version (TanyaNative): - -import mir.linux._asm.unistd; -import tanya.memory.allocator; -import tanya.memory.op; -import tanya.os.error; -import tanya.sys.linux.syscall; -import tanya.sys.posix.mman; - -private void* mapMemory(const size_t length) @nogc nothrow pure @system -{ - auto p = syscall_(0, - length, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0, - NR_mmap); - return p == -ErrorCode.noMemory ? null : cast(void*) p; -} - -private bool unmapMemory(shared void* addr, const size_t length) -@nogc nothrow pure @system -{ - return syscall_(cast(ptrdiff_t) addr, length, NR_munmap) == 0; -} - -/* - * This allocator allocates memory in regions (multiple of 64 KB for example). - * Each region is then splitted in blocks. So it doesn't request the memory - * from the operating system on each call, but only if there are no large - * enough free blocks in the available regions. - * Deallocation works in the same way. Deallocation doesn't immediately - * gives the memory back to the operating system, but marks the appropriate - * block as free and only if all blocks in the region are free, the complete - * region is deallocated. - * - *
- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * |      |     |         |     |            ||      |     |                  |
- * |      |prev <-----------    |            ||      |     |                  |
- * |  R   |  B  |         |  B  |            ||   R  |  B  |                  |
- * |  E   |  L  |         |  L  |           next  E  |  L  |                  |
- * |  G   |  O  |  DATA   |  O  |   FREE    --->  G  |  O  |       DATA       |
- * |  I   |  C  |         |  C  |           <---  I  |  C  |                  |
- * |  O   |  K  |         |  K  |           prev  O  |  K  |                  |
- * |  N   |    -----------> next|            ||   N  |     |                  |
- * |      |     |         |     |            ||      |     |                  |
- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * 
- */ -final class MmapPool : Allocator -{ - version (none) - { - @nogc nothrow pure @system invariant - { - for (auto r = &head; *r !is null; r = &((*r).next)) - { - auto block = cast(Block) (cast(void*) *r + RegionEntry.sizeof); - do - { - assert(block.prev is null || block.prev.next is block); - assert(block.next is null || block.next.prev is block); - assert(block.region is *r); - } - while ((block = block.next) !is null); - } - } - } - - /* - * Allocates $(D_PARAM size) bytes of memory. - * - * Params: - * size = Amount of memory to allocate. - * - * Returns: Pointer to the new allocated memory. - */ - void[] allocate(size_t size) @nogc nothrow pure shared @system - { - if (size == 0) - { - return null; - } - const dataSize = addAlignment(size); - if (dataSize < size) - { - return null; - } - - void* data = findBlock(dataSize); - if (data is null) - { - data = initializeRegion(dataSize); - } - - return data is null ? null : data[0 .. size]; - } - - @nogc nothrow pure @system unittest - { - auto p = MmapPool.instance.allocate(20); - assert(p); - MmapPool.instance.deallocate(p); - - p = MmapPool.instance.allocate(0); - assert(p.length == 0); - } - - @nogc nothrow pure @system unittest - { - // allocate() check. - size_t tooMuchMemory = size_t.max - - MmapPool.alignment_ - - BlockEntry.sizeof * 2 - - RegionEntry.sizeof - - pageSize; - assert(MmapPool.instance.allocate(tooMuchMemory) is null); - - assert(MmapPool.instance.allocate(size_t.max) is null); - - // initializeRegion() check. - tooMuchMemory = size_t.max - MmapPool.alignment_; - assert(MmapPool.instance.allocate(tooMuchMemory) is null); - } - - /* - * Search for a block large enough to keep $(D_PARAM size) and split it - * into two blocks if the block is too large. - * - * Params: - * size = Minimum size the block should have (aligned). - * - * Returns: Data the block points to or $(D_KEYWORD null). - */ - private void* findBlock(const ref size_t size) - @nogc nothrow pure shared @system - { - Block block1; - RegionLoop: for (auto r = head; r !is null; r = r.next) - { - block1 = cast(Block) (cast(void*) r + RegionEntry.sizeof); - do - { - if (block1.free && block1.size >= size) - { - break RegionLoop; - } - } - while ((block1 = block1.next) !is null); - } - if (block1 is null) - { - return null; - } - else if (block1.size >= size + alignment_ + BlockEntry.sizeof) - { // Split the block if needed - Block block2 = cast(Block) (cast(void*) block1 + BlockEntry.sizeof + size); - block2.prev = block1; - block2.next = block1.next; - block2.free = true; - block2.size = block1.size - BlockEntry.sizeof - size; - block2.region = block1.region; - - if (block1.next !is null) - { - block1.next.prev = block2; - } - block1.next = block2; - block1.size = size; - } - block1.free = false; - block1.region.blocks = block1.region.blocks + 1; - - return cast(void*) block1 + BlockEntry.sizeof; - } - - // Merge block with the next one. - private void mergeNext(Block block) const @nogc nothrow pure @safe shared - { - block.size = block.size + BlockEntry.sizeof + block.next.size; - if (block.next.next !is null) - { - block.next.next.prev = block; - } - block.next = block.next.next; - } - - /* - * Deallocates a memory block. - * - * Params: - * p = A pointer to the memory block to be freed. - * - * Returns: Whether the deallocation was successful. - */ - bool deallocate(void[] p) @nogc nothrow pure shared @system - { - if (p.ptr is null) - { - return true; - } - - Block block = cast(Block) (p.ptr - BlockEntry.sizeof); - if (block.region.blocks <= 1) - { - if (block.region.prev !is null) - { - block.region.prev.next = block.region.next; - } - else // Replace the list head. It is being deallocated - { - head = block.region.next; - } - if (block.region.next !is null) - { - block.region.next.prev = block.region.prev; - } - return unmapMemory(block.region, block.region.size); - } - // Merge blocks if neigbours are free. - if (block.next !is null && block.next.free) - { - mergeNext(block); - } - if (block.prev !is null && block.prev.free) - { - block.prev.size = block.prev.size + BlockEntry.sizeof + block.size; - if (block.next !is null) - { - block.next.prev = block.prev; - } - block.prev.next = block.next; - } - else - { - block.free = true; - } - block.region.blocks = block.region.blocks - 1; - return true; - } - - @nogc nothrow pure @system unittest - { - auto p = MmapPool.instance.allocate(20); - - assert(MmapPool.instance.deallocate(p)); - } - - /* - * Reallocates a memory block in place if possible or returns - * $(D_KEYWORD false). This function cannot be used to allocate or - * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or - * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. - */ - bool reallocateInPlace(ref void[] p, size_t size) - @nogc nothrow pure shared @system - { - if (p is null || size == 0) - { - return false; - } - if (size <= p.length) - { - // Leave the block as is. - p = p.ptr[0 .. size]; - return true; - } - Block block1 = cast(Block) (p.ptr - BlockEntry.sizeof); - - if (block1.size >= size) - { - // Enough space in the current block. - p = p.ptr[0 .. size]; - return true; - } - const dataSize = addAlignment(size); - const pAlignment = addAlignment(p.length); - assert(pAlignment >= p.length, "Invalid memory chunk length"); - const delta = dataSize - pAlignment; - - if (block1.next is null - || !block1.next.free - || dataSize < size - || block1.next.size + BlockEntry.sizeof < delta) - { - /* - It is the last block in the region - * - The next block isn't free - * - The next block is too small - * - Requested size is too large - */ - return false; - } - if (block1.next.size >= delta + alignment_) - { - // Move size from block2 to block1. - block1.next.size = block1.next.size - delta; - block1.size = block1.size + delta; - - auto block2 = cast(Block) (p.ptr + dataSize); - if (block1.next.next !is null) - { - block1.next.next.prev = block2; - } - copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof], - (cast(void*) block2)[0 .. BlockEntry.sizeof]); - block1.next = block2; - } - else - { - // The next block has enough space, but is too small for further - // allocations. Merge it with the current block. - mergeNext(block1); - } - - p = p.ptr[0 .. size]; - return true; - } - - @nogc nothrow pure @system unittest - { - void[] p; - assert(!MmapPool.instance.reallocateInPlace(p, 5)); - assert(p is null); - - p = MmapPool.instance.allocate(1); - auto orig = p.ptr; - - assert(MmapPool.instance.reallocateInPlace(p, 2)); - assert(p.length == 2); - assert(p.ptr == orig); - - assert(MmapPool.instance.reallocateInPlace(p, 4)); - assert(p.length == 4); - assert(p.ptr == orig); - - assert(MmapPool.instance.reallocateInPlace(p, 2)); - assert(p.length == 2); - assert(p.ptr == orig); - - MmapPool.instance.deallocate(p); - } - - /* - * Increases or decreases the size of a memory block. - * - * Params: - * p = A pointer to the memory block. - * size = Size of the reallocated block. - * - * Returns: Whether the reallocation was successful. - */ - bool reallocate(ref void[] p, size_t size) - @nogc nothrow pure shared @system - { - if (size == 0) - { - if (deallocate(p)) - { - p = null; - return true; - } - return false; - } - else if (reallocateInPlace(p, size)) - { - return true; - } - // Can't reallocate in place, allocate a new block, - // copy and delete the previous one. - void[] reallocP = allocate(size); - if (reallocP is null) - { - return false; - } - if (p !is null) - { - copy(p[0 .. min(p.length, size)], reallocP); - deallocate(p); - } - p = reallocP; - - return true; - } - - @nogc nothrow pure @system unittest - { - void[] p; - MmapPool.instance.reallocate(p, 10 * int.sizeof); - (cast(int[]) p)[7] = 123; - - assert(p.length == 40); - - MmapPool.instance.reallocate(p, 8 * int.sizeof); - - assert(p.length == 32); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.reallocate(p, 20 * int.sizeof); - (cast(int[]) p)[15] = 8; - - assert(p.length == 80); - assert((cast(int[]) p)[15] == 8); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.reallocate(p, 8 * int.sizeof); - - assert(p.length == 32); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.deallocate(p); - } - - static private shared(MmapPool) instantiate() @nogc nothrow @system - { - if (instance_ is null) - { - const instanceSize = addAlignment(__traits(classInstanceSize, - MmapPool)); - - Region head; // Will become soon our region list head - void* data = initializeRegion(instanceSize, head); - if (data !is null) - { - copy(typeid(MmapPool).initializer, data[0 .. instanceSize]); - instance_ = cast(shared MmapPool) data; - instance_.head = head; - } - } - return instance_; - } - - /* - * Static allocator instance and initializer. - * - * Returns: Global $(D_PSYMBOL MmapPool) instance. - */ - static @property shared(MmapPool) instance() @nogc nothrow pure @system - { - return (cast(GetPureInstance!MmapPool) &instantiate)(); - } - - @nogc nothrow pure @system unittest - { - assert(instance is instance); - } - - /* - * Initializes a region for one element. - * - * Params: - * size = Aligned size of the first data block in the region. - * head = Region list head. - * - * Returns: A pointer to the data. - */ - private static void* initializeRegion(const size_t size, ref Region head) - @nogc nothrow pure @system - { - const regionSize = calculateRegionSize(size); - if (regionSize < size) - { - return null; - } - - void* p = mapMemory(regionSize); - if (p is null) - { - return null; - } - - Region region = cast(Region) p; - region.blocks = 1; - region.size = regionSize; - - // Set the pointer to the head of the region list - if (head !is null) - { - head.prev = region; - } - region.next = head; - region.prev = null; - head = region; - - // Initialize the data block - void* memoryPointer = p + RegionEntry.sizeof; - Block block1 = cast(Block) memoryPointer; - block1.size = size; - block1.free = false; - - // It is what we want to return - void* data = memoryPointer + BlockEntry.sizeof; - - // Free block after data - memoryPointer = data + size; - Block block2 = cast(Block) memoryPointer; - block1.prev = block2.next = null; - block1.next = block2; - block2.prev = block1; - block2.size = regionSize - size - RegionEntry.sizeof - BlockEntry.sizeof * 2; - block2.free = true; - block1.region = block2.region = region; - - return data; - } - - private void* initializeRegion(const size_t size) - @nogc nothrow pure shared @system - { - return initializeRegion(size, this.head); - } - - /* - * Params: - * x = Space to be aligned. - * - * Returns: Aligned size of $(D_PARAM x). - */ - private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe - { - return (x - 1) / alignment_ * alignment_ + alignment_; - } - - /* - * Params: - * x = Required space. - * - * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). - */ - private static size_t calculateRegionSize(ref const size_t x) - @nogc nothrow pure @safe - { - return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2) - / pageSize * pageSize + pageSize; - } - - /* - * Returns: Alignment offered. - */ - @property uint alignment() const @nogc nothrow pure @safe shared - { - return alignment_; - } - - @nogc nothrow pure @system unittest - { - assert(MmapPool.instance.alignment == MmapPool.alignment_); - } - - private enum uint alignment_ = 8; - - private shared static MmapPool instance_; - - // Page size. - enum size_t pageSize = 65536; - - private shared struct RegionEntry - { - Region prev; - Region next; - uint blocks; - size_t size; - } - private alias Region = shared RegionEntry*; - private shared Region head; - - private shared struct BlockEntry - { - Block prev; - Block next; - Region region; - size_t size; - bool free; - } - private alias Block = shared BlockEntry*; -} - -// A lot of allocations/deallocations, but it is the minimum caused a -// segmentation fault because MmapPool reallocateInPlace moves a block wrong. -@nogc nothrow pure @system unittest -{ - auto a = MmapPool.instance.allocate(16); - auto d = MmapPool.instance.allocate(16); - auto b = MmapPool.instance.allocate(16); - auto e = MmapPool.instance.allocate(16); - auto c = MmapPool.instance.allocate(16); - auto f = MmapPool.instance.allocate(16); - - MmapPool.instance.deallocate(a); - MmapPool.instance.deallocate(b); - MmapPool.instance.deallocate(c); - - a = MmapPool.instance.allocate(50); - MmapPool.instance.reallocateInPlace(a, 64); - MmapPool.instance.deallocate(a); - - a = MmapPool.instance.allocate(1); - auto tmp1 = MmapPool.instance.allocate(1); - auto h1 = MmapPool.instance.allocate(1); - auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1); - - auto h2 = MmapPool.instance.allocate(2); - tmp1 = MmapPool.instance.allocate(1); - MmapPool.instance.deallocate(h2); - MmapPool.instance.deallocate(h1); - - h2 = MmapPool.instance.allocate(2); - h1 = MmapPool.instance.allocate(1); - MmapPool.instance.deallocate(h2); - - auto rep = cast(void[]) tmp2; - MmapPool.instance.reallocate(rep, tmp1.length); - tmp2 = cast(ubyte[]) rep; - - MmapPool.instance.reallocate(tmp1, 9); - - rep = cast(void[]) tmp2; - MmapPool.instance.reallocate(rep, tmp1.length); - tmp2 = cast(ubyte[]) rep; - MmapPool.instance.reallocate(tmp1, 17); - - tmp2[$ - 1] = 0; - - MmapPool.instance.deallocate(tmp1); - - b = MmapPool.instance.allocate(16); - - MmapPool.instance.deallocate(h1); - MmapPool.instance.deallocate(a); - MmapPool.instance.deallocate(b); - MmapPool.instance.deallocate(d); - MmapPool.instance.deallocate(e); - MmapPool.instance.deallocate(f); -} diff --git a/source/tanya/memory/op.d b/source/tanya/memory/op.d deleted file mode 100644 index 6005329..0000000 --- a/source/tanya/memory/op.d +++ /dev/null @@ -1,437 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Set of operations on memory blocks. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/op.d, - * tanya/memory/op.d) - */ -module tanya.memory.op; - -version (TanyaNative) -{ - extern private void fillMemory(void[], size_t) pure nothrow @system @nogc; - - extern private void copyMemory(const void[], void[]) - pure nothrow @system @nogc; - - extern private void moveMemory(const void[], void[]) - pure nothrow @system @nogc; - - extern private bool equalMemory(const void[], const void[]) - pure nothrow @system @nogc; -} -else -{ - import core.stdc.string; -} - -version (TanyaNative) -{ - @nogc nothrow pure @system unittest - { - ubyte[2] buffer = 1; - fillMemory(buffer[1 .. $], 0); - assert(buffer[0] == 1 && buffer[1] == 0); - } - - @nogc nothrow pure @safe unittest - { - assert(equal(null, null)); - } -} - -private enum alignMask = size_t.sizeof - 1; - -/** - * Copies $(D_PARAM source) into $(D_PARAM target). - * - * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that - * $(D_PARAM source) points ahead of $(D_PARAM target). - * - * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) - * elements. - * - * Params: - * source = Memory to copy from. - * target = Destination memory. - * - * See_Also: $(D_PSYMBOL copyBackward). - * - * Precondition: $(D_INLINECODE source.length <= target.length). - */ -void copy(const void[] source, void[] target) @nogc nothrow pure @trusted -in -{ - assert(source.length <= target.length); - assert(source.length == 0 || source.ptr !is null); - assert(target.length == 0 || target.ptr !is null); -} -do -{ - version (TanyaNative) - { - copyMemory(source, target); - } - else - { - memcpy(target.ptr, source.ptr, source.length); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - ubyte[9] target; - source.copy(target); - assert(equal(source, target)); -} - -@nogc nothrow pure @safe unittest -{ - { - ubyte[0] source, target; - source.copy(target); - } - { - ubyte[1] source = [1]; - ubyte[1] target; - source.copy(target); - assert(target[0] == 1); - } - { - ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8]; - ubyte[8] target; - source.copy(target); - assert(equal(source, target)); - } -} - -/* - * size_t value each of which bytes is set to `Byte`. - */ -private template filledBytes(ubyte Byte, ubyte I = 0) -{ - static if (I == size_t.sizeof) - { - enum size_t filledBytes = Byte; - } - else - { - enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte; - } -} - -/** - * Fills $(D_PARAM memory) with the single byte $(D_PARAM c). - * - * Param: - * c = The value to fill $(D_PARAM memory) with. - * memory = Memory block. - */ -void fill(ubyte c = 0)(void[] memory) @trusted -in -{ - assert(memory.length == 0 || memory.ptr !is null); -} -do -{ - version (TanyaNative) - { - fillMemory(memory, filledBytes!c); - } - else - { - memset(memory.ptr, c, memory.length); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - memory.fill!0(); - foreach (ubyte v; memory) - { - assert(v == 0); - } -} - -/** - * Copies starting from the end of $(D_PARAM source) into the end of - * $(D_PARAM target). - * - * $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the - * order of elements in the $(D_PARAM target) is exactly the same as in the - * $(D_PARAM source). - * - * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that - * $(D_PARAM target) points ahead of $(D_PARAM source). - * - * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length) - * elements. - * - * Params: - * source = Memory to copy from. - * target = Destination memory. - * - * See_Also: $(D_PSYMBOL copy). - * - * Precondition: $(D_INLINECODE source.length <= target.length). - */ -void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted -in -{ - assert(source.length <= target.length); - assert(source.length == 0 || source.ptr !is null); - assert(target.length == 0 || target.ptr !is null); -} -do -{ - version (TanyaNative) - { - moveMemory(source, target); - } - else - { - memmove(target.ptr, source.ptr, source.length); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ]; - ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ]; - - copyBackward(mem[0 .. 4], mem[2 .. $]); - assert(equal(expected, mem)); -} - -@nogc nothrow pure @safe unittest -{ - ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]; - ubyte[9] r2; - - copyBackward(r1, r2); - assert(equal(r1, r2)); -} - -/** - * Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if - * any. - * - * Params: - * haystack = Memory block. - * needle = A byte. - * - * Returns: The subrange of $(D_PARAM haystack) whose first element is the - * first occurrence of $(D_PARAM needle). If $(D_PARAM needle) - * couldn't be found, an empty `inout void[]` is returned. - */ -inout(void[]) find(return inout void[] haystack, ubyte needle) -@nogc nothrow pure @trusted -in -{ - assert(haystack.length == 0 || haystack.ptr !is null); -} -do -{ - auto length = haystack.length; - const size_t needleWord = size_t.max * needle; - enum size_t highBits = filledBytes!(0x01, 0); - enum size_t mask = filledBytes!(0x80, 0); - - // Align - auto bytes = cast(inout(ubyte)*) haystack; - while (length > 0 && ((cast(size_t) bytes) & 3) != 0) - { - if (*bytes == needle) - { - return bytes[0 .. length]; - } - ++bytes; - --length; - } - - // Check if some of the words has the needle - auto words = cast(inout(size_t)*) bytes; - while (length >= size_t.sizeof) - { - if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0) - { - break; - } - ++words; - length -= size_t.sizeof; - } - - // Find the exact needle position in the word - bytes = cast(inout(ubyte)*) words; - while (length > 0) - { - if (*bytes == needle) - { - return bytes[0 .. length]; - } - ++bytes; - --length; - } - - return haystack[$ .. $]; -} - -/// -@nogc nothrow pure @safe unittest -{ - const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; - - assert(equal(find(haystack, 'a'), haystack[])); - assert(equal(find(haystack, 'b'), haystack[1 .. $])); - assert(equal(find(haystack, 'c'), haystack[2 .. $])); - assert(equal(find(haystack, 'd'), haystack[3 .. $])); - assert(equal(find(haystack, 'e'), haystack[4 .. $])); - assert(equal(find(haystack, 'f'), haystack[5 .. $])); - assert(equal(find(haystack, 'h'), haystack[8 .. $])); - assert(find(haystack, 'i').length == 0); - - assert(find(null, 'a').length == 0); -} - -/** - * Looks for `\0` in the $(D_PARAM haystack) and returns the part of the - * $(D_PARAM haystack) ahead of it. - * - * Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null - * character. - * - * Params: - * haystack = Memory block. - * - * Returns: The subrange that spans all bytes before the null character or - * $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any. - */ -inout(char[]) findNullTerminated(return inout char[] haystack) -@nogc nothrow pure @trusted -in -{ - assert(haystack.length == 0 || haystack.ptr !is null); -} -do -{ - auto length = haystack.length; - enum size_t highBits = filledBytes!(0x01, 0); - enum size_t mask = filledBytes!(0x80, 0); - - // Align - auto bytes = cast(inout(ubyte)*) haystack; - while (length > 0 && ((cast(size_t) bytes) & 3) != 0) - { - if (*bytes == '\0') - { - return haystack[0 .. haystack.length - length]; - } - ++bytes; - --length; - } - - // Check if some of the words contains 0 - auto words = cast(inout(size_t)*) bytes; - while (length >= size_t.sizeof) - { - if (((*words - highBits) & (~*words) & mask) != 0) - { - break; - } - ++words; - length -= size_t.sizeof; - } - - // Find the exact 0 position in the word - bytes = cast(inout(ubyte)*) words; - while (length > 0) - { - if (*bytes == '\0') - { - return haystack[0 .. haystack.length - length]; - } - ++bytes; - --length; - } - - return null; -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(equal(findNullTerminated("abcdef\0gh"), "abcdef")); - assert(equal(findNullTerminated("\0garbage"), "")); - assert(equal(findNullTerminated("\0"), "")); - assert(equal(findNullTerminated("cstring\0"), "cstring")); - assert(findNullTerminated(null) is null); - assert(findNullTerminated("abcdef") is null); -} - -/** - * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality. - * - * Params: - * r1 = First memory block. - * r2 = Second memory block. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal, - * $(D_KEYWORD false) otherwise. - */ -bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted -in -{ - assert(r1.length == 0 || r1.ptr !is null); - assert(r2.length == 0 || r2.ptr !is null); -} -do -{ - version (TanyaNative) - { - return equalMemory(r1, r2); - } - else - { - return r1.length == r2.length - && memcmp(r1.ptr, r2.ptr, r1.length) == 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(equal("asdf", "asdf")); - assert(!equal("asd", "asdf")); - assert(!equal("asdf", "asd")); - assert(!equal("asdf", "qwer")); -} - -// Compares unanligned memory -@nogc nothrow pure @safe unittest -{ - ubyte[16] r1 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - ubyte[16] r2 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - - assert(equal(r1, r2)); - assert(equal(r1[1 .. $], r2[1 .. $])); - assert(equal(r1[0 .. $ - 1], r2[0 .. $ - 1])); - assert(equal(r1[0 .. 8], r2[0 .. 8])); -} diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d deleted file mode 100644 index 8526b5a..0000000 --- a/source/tanya/memory/package.d +++ /dev/null @@ -1,203 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Dynamic memory management. - * - * Copyright: Eugene Wissner 2016-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/package.d, - * tanya/memory/package.d) - */ -module tanya.memory; - -import tanya.conv; -public import tanya.memory.allocator; -public import tanya.memory.lifecycle; -import tanya.meta.trait; - -/** - * The mixin generates common methods for classes and structs using - * allocators. It provides a protected member, constructor and a read-only property, - * that checks if an allocator was already set and sets it to the default - * one, if not (useful for structs which don't have a default constructor). - */ -mixin template DefaultAllocator() -{ - /// Allocator. - protected shared Allocator allocator_; - - /** - * Params: - * allocator = The allocator should be used. - * - * Precondition: $(D_INLINECODE allocator_ !is null) - */ - this(shared Allocator allocator) @nogc nothrow pure @safe - in - { - assert(allocator !is null); - } - do - { - this.allocator_ = allocator; - } - - /** - * This property checks if the allocator was set in the constructor - * and sets it to the default one, if not. - * - * Returns: Used allocator. - * - * Postcondition: $(D_INLINECODE allocator !is null) - */ - @property shared(Allocator) allocator() @nogc nothrow pure @safe - out (allocator) - { - assert(allocator !is null); - } - do - { - if (allocator_ is null) - { - allocator_ = defaultAllocator; - } - return allocator_; - } - - /// ditto - @property shared(Allocator) allocator() const @nogc nothrow pure @trusted - out (allocator) - { - assert(allocator !is null); - } - do - { - if (allocator_ is null) - { - return defaultAllocator; - } - return cast(shared Allocator) allocator_; - } -} - -shared Allocator allocator; - -private shared(Allocator) getAllocatorInstance() @nogc nothrow -{ - if (allocator is null) - { - version (TanyaNative) - { - import tanya.memory.mmappool; - defaultAllocator = MmapPool.instance; - } - else - { - import tanya.memory.mallocator; - defaultAllocator = Mallocator.instance; - } - } - return allocator; -} - -/** - * Returns: Default allocator. - * - * Postcondition: $(D_INLINECODE allocator !is null). - */ -@property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted -out (allocator) -{ - assert(allocator !is null); -} -do -{ - return (cast(GetPureInstance!Allocator) &getAllocatorInstance)(); -} - -/** - * Sets the default allocator. - * - * Params: - * allocator = $(D_PSYMBOL Allocator) instance. - * - * Precondition: $(D_INLINECODE allocator !is null). - */ -@property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe -in -{ - assert(allocator !is null); -} -do -{ - .allocator = allocator; -} - -/** - * Returns the size in bytes of the state that needs to be allocated to hold an - * object of type $(D_PARAM T). - * - * There is a difference between the `.sizeof`-property and - * $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface. - * `T.sizeof` is constant on the given architecture then and is the same as - * `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and - * interfaces are reference types and `.sizeof` returns the size of the - * reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize) - * returns the size of the instance itself. - * - * The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array - * stores its length and a data pointer. The size of the static arrays is - * calculated differently since they are value types. It is the array length - * multiplied by the element size. - * - * `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym - * for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`. - * - * Params: - * T = Object type. - * - * Returns: Size of an instance of type $(D_PARAM T). - */ -template stateSize(T) -{ - static if (isPolymorphicType!T) - { - enum size_t stateSize = __traits(classInstanceSize, T); - } - else - { - enum size_t stateSize = T.sizeof; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(stateSize!int == 4); - static assert(stateSize!bool == 1); - static assert(stateSize!(int[]) == (size_t.sizeof * 2)); - static assert(stateSize!(short[3]) == 6); - - static struct Empty - { - } - static assert(stateSize!Empty == 1); - static assert(stateSize!void == 1); -} - -/** - * Params: - * size = Raw size. - * alignment = Alignment. - * - * Returns: Aligned size. - */ -size_t alignedSize(const size_t size, const size_t alignment = 8) -pure nothrow @safe @nogc -{ - return (size - 1) / alignment * alignment + alignment; -} diff --git a/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d deleted file mode 100644 index 90271e7..0000000 --- a/source/tanya/memory/smartref.d +++ /dev/null @@ -1,914 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Smart pointers. - * - * A smart pointer is an object that wraps a raw pointer or a reference - * (class, dynamic array) to manage its lifetime. - * - * This module provides two kinds of lifetime management strategies: - * $(UL - * $(LI Reference counting) - * $(LI Unique ownership) - * ) - * - * Copyright: Eugene Wissner 2016-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/smartref.d, - * tanya/memory/smartref.d) - */ -module tanya.memory.smartref; - -import tanya.algorithm.mutation; -import tanya.conv; -import tanya.memory; -import tanya.meta.trait; -import tanya.range.primitive; -version (unittest) import tanya.test.stub; - -private template Payload(T) -{ - static if (isPolymorphicType!T || isArray!T) - { - alias Payload = T; - } - else - { - alias Payload = T*; - } -} - -private final class RefCountedStore(T) -{ - T payload; - size_t counter = 1; - - size_t opUnary(string op)() - if (op == "--" || op == "++") - in - { - assert(this.counter > 0); - } - do - { - mixin("return " ~ op ~ "counter;"); - } - - int opCmp(const size_t counter) - { - if (this.counter > counter) - { - return 1; - } - else if (this.counter < counter) - { - return -1; - } - else - { - return 0; - } - } -} - -private void separateDeleter(T)(RefCountedStore!T storage, - shared Allocator allocator) -{ - allocator.dispose(storage.payload); - allocator.dispose(storage); -} - -private void unifiedDeleter(T)(RefCountedStore!T storage, - shared Allocator allocator) -{ - auto ptr1 = finalize(storage); - auto ptr2 = finalize(storage.payload); - allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]); -} - -/** - * Reference-counted object containing a $(D_PARAM T) value as payload. - * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and - * when the reference count goes down to zero, frees the underlying store. - * - * Params: - * T = Type of the reference-counted value. - */ -struct RefCounted(T) -{ - private alias Storage = RefCountedStore!(Payload!T); - - private Storage storage; - private void function(Storage storage, - shared Allocator allocator) @nogc deleter; - - invariant - { - assert(this.storage is null || this.allocator_ !is null); - assert(this.storage is null || this.deleter !is null); - } - - /** - * Takes ownership over $(D_PARAM value), setting the counter to 1. - * $(D_PARAM value) may be a pointer, an object or a dynamic array. - * - * Params: - * value = Value whose ownership is taken over. - * allocator = Allocator used to destroy the $(D_PARAM value) and to - * allocate/deallocate internal storage. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(Payload!T value, shared Allocator allocator = defaultAllocator) - { - this(allocator); - this.storage = allocator.make!Storage(); - this.deleter = &separateDeleter!(Payload!T); - - this.storage.payload = value; - } - - /// ditto - this(shared Allocator allocator) - in - { - assert(allocator !is null); - } - do - { - this.allocator_ = allocator; - } - - /** - * Increases the reference counter by one. - */ - this(this) - { - if (count != 0) - { - ++this.storage; - } - } - - /** - * Decreases the reference counter by one. - * - * If the counter reaches 0, destroys the owned object. - */ - ~this() - { - if (this.storage !is null && !(this.storage > 0 && --this.storage)) - { - deleter(this.storage, allocator); - } - } - - /** - * Takes ownership over $(D_PARAM rhs). Initializes this - * $(D_PSYMBOL RefCounted) if needed. - * - * If it is the last reference of the previously owned object, - * it will be destroyed. - * - * To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). - * - * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will - * be used. If you need a different allocator, create a new - * $(D_PSYMBOL RefCounted) and assign it. - * - * Params: - * rhs = New object. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(Payload!T rhs) - { - if (this.storage is null) - { - this.storage = allocator.make!Storage(); - this.deleter = &separateDeleter!(Payload!T); - } - else if (this.storage > 1) - { - --this.storage; - this.storage = allocator.make!Storage(); - this.deleter = &separateDeleter!(Payload!T); - } - else - { - finalize(this.storage.payload); - this.storage.payload = Payload!T.init; - } - this.storage.payload = rhs; - return this; - } - - /// ditto - ref typeof(this) opAssign(typeof(null)) - { - if (this.storage is null) - { - return this; - } - else if (this.storage > 1) - { - --this.storage; - } - else - { - deleter(this.storage, allocator); - } - this.storage = null; - - return this; - } - - /// ditto - ref typeof(this) opAssign(typeof(this) rhs) - { - swap(this.allocator_, rhs.allocator_); - swap(this.storage, rhs.storage); - swap(this.deleter, rhs.deleter); - return this; - } - - /** - * Returns: Reference to the owned object. - * - * Precondition: $(D_INLINECODE cound > 0). - */ - inout(Payload!T) get() inout - in - { - assert(count > 0, "Attempted to access an uninitialized reference"); - } - do - { - return this.storage.payload; - } - - version (D_Ddoc) - { - /** - * Dereferences the pointer. It is defined only for pointers, not for - * reference types like classes, that can be accessed directly. - * - * Params: - * op = Operation. - * - * Returns: Reference to the pointed value. - */ - ref inout(T) opUnary(string op)() inout - if (op == "*"); - } - else static if (isPointer!(Payload!T)) - { - ref inout(T) opUnary(string op)() inout - if (op == "*") - { - return *this.storage.payload; - } - } - - /** - * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal - * storage. - */ - @property bool isInitialized() const - { - return this.storage !is null; - } - - /** - * Returns: The number of $(D_PSYMBOL RefCounted) instances that share - * ownership over the same pointer (including $(D_KEYWORD this)). - * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`. - */ - @property size_t count() const - { - return this.storage is null ? 0 : this.storage.counter; - } - - mixin DefaultAllocator; - alias get this; -} - -/// -@nogc @system unittest -{ - auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); - auto val = rc.get(); - - *val = 8; - assert(*rc.storage.payload == 8); - - val = null; - assert(rc.storage.payload !is null); - assert(*rc.storage.payload == 8); - - *rc = 9; - assert(*rc.storage.payload == 9); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - rc = defaultAllocator.make!int(7); - assert(*rc == 7); -} - -@nogc @system unittest -{ - RefCounted!int rc; - assert(!rc.isInitialized); - rc = null; - assert(!rc.isInitialized); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - - void func(RefCounted!int param) @nogc - { - assert(param.count == 2); - param = defaultAllocator.make!int(7); - assert(param.count == 1); - assert(*param == 7); - } - func(rc); - assert(rc.count == 1); - assert(*rc == 5); -} - -@nogc @system unittest -{ - RefCounted!int rc; - - void func(RefCounted!int param) @nogc - { - assert(param.count == 0); - param = defaultAllocator.make!int(7); - assert(param.count == 1); - assert(*param == 7); - } - func(rc); - assert(rc.count == 0); -} - -@nogc @system unittest -{ - RefCounted!int rc1, rc2; - static assert(is(typeof(rc1 = rc2))); -} - -version (unittest) -{ - private class A - { - uint *destroyed; - - this(ref uint destroyed) @nogc - { - this.destroyed = &destroyed; - } - - ~this() @nogc - { - ++(*destroyed); - } - } - - private struct B - { - int prop; - @disable this(); - this(int param1) @nogc - { - prop = param1; - } - } -} - -@nogc @system unittest -{ - uint destroyed; - auto a = defaultAllocator.make!A(destroyed); - - assert(destroyed == 0); - { - auto rc = RefCounted!A(a, defaultAllocator); - assert(rc.count == 1); - - void func(RefCounted!A rc) @nogc @system - { - assert(rc.count == 2); - } - func(rc); - - assert(rc.count == 1); - } - assert(destroyed == 1); - - RefCounted!int rc; - assert(rc.count == 0); - rc = defaultAllocator.make!int(8); - assert(rc.count == 1); -} - -@nogc @system unittest -{ - auto rc = RefCounted!int(defaultAllocator); - assert(!rc.isInitialized); - assert(rc.allocator is defaultAllocator); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - assert(rc.count == 1); - - void func(RefCounted!int rc) @nogc - { - assert(rc.count == 2); - rc = null; - assert(!rc.isInitialized); - assert(rc.count == 0); - } - - assert(rc.count == 1); - func(rc); - assert(rc.count == 1); - - rc = null; - assert(!rc.isInitialized); - assert(rc.count == 0); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - assert(*rc == 5); - - void func(RefCounted!int rc) @nogc - { - assert(rc.count == 2); - rc = defaultAllocator.refCounted!int(4); - assert(*rc == 4); - assert(rc.count == 1); - } - func(rc); - assert(*rc == 5); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(RefCounted!int.storage.payload) == int*)); - static assert(is(typeof(RefCounted!A.storage.payload) == A)); - - static assert(is(RefCounted!B)); - static assert(is(RefCounted!A)); -} - -/** - * Constructs a new object of type $(D_PARAM T) and wraps it in a - * $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for - * the constructor of $(D_PARAM T). - * - * This function is more efficient than the using of $(D_PSYMBOL RefCounted) - * directly, since it allocates only ones (the internal storage and the - * object). - * - * Params: - * T = Type of the constructed object. - * A = Types of the arguments to the constructor of $(D_PARAM T). - * allocator = Allocator. - * args = Constructor arguments of $(D_PARAM T). - * - * Returns: Newly created $(D_PSYMBOL RefCounted!T). - * - * Precondition: $(D_INLINECODE allocator !is null) - */ -RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) -if (!is(T == interface) && !isAbstractClass!T - && !isAssociativeArray!T && !isArray!T) -in -{ - assert(allocator !is null); -} -do -{ - auto rc = typeof(return)(allocator); - - const storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); - const size = alignedSize(stateSize!T + storageSize); - - auto mem = (() @trusted => allocator.allocate(size))(); - if (mem is null) - { - onOutOfMemoryError(); - } - scope (failure) - { - () @trusted { allocator.deallocate(mem); }(); - } - rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); - rc.storage.payload = emplace!T(mem[storageSize .. $], args); - - rc.deleter = &unifiedDeleter!(Payload!T); - return rc; -} - -/** - * Constructs a new array with $(D_PARAM size) elements and wraps it in a - * $(D_PSYMBOL RefCounted). - * - * Params: - * T = Array type. - * size = Array size. - * allocator = Allocator. - * - * Returns: Newly created $(D_PSYMBOL RefCounted!T). - * - * Precondition: $(D_INLINECODE allocator !is null - * && size <= size_t.max / ElementType!T.sizeof) - */ -RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size) -@trusted -if (isArray!T) -in -{ - assert(allocator !is null); - assert(size <= size_t.max / ElementType!T.sizeof); -} -do -{ - return RefCounted!T(allocator.make!T(size), allocator); -} - -/// -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - assert(rc.count == 1); - - void func(RefCounted!int param) @nogc - { - if (param.count == 2) - { - func(param); - } - else - { - assert(param.count == 3); - } - } - func(rc); - - assert(rc.count == 1); -} - -@nogc @system unittest -{ - struct E - { - } - auto b = defaultAllocator.refCounted!B(15); - static assert(is(typeof(b.storage.payload) == B*)); - static assert(is(typeof(b.prop) == int)); - static assert(!is(typeof(defaultAllocator.refCounted!B()))); - - static assert(is(typeof(defaultAllocator.refCounted!E()))); - static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); - { - auto rc = defaultAllocator.refCounted!B(3); - assert(rc.get().prop == 3); - } - { - auto rc = defaultAllocator.refCounted!E(); - assert(rc.count); - } -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!(int[])(5); - assert(rc.length == 5); -} - -@nogc @system unittest -{ - auto p1 = defaultAllocator.make!int(5); - auto p2 = p1; - auto rc = RefCounted!int(p1, defaultAllocator); - assert(rc.get() is p2); -} - -@nogc @system unittest -{ - size_t destroyed; - { - auto rc = defaultAllocator.refCounted!WithDtor(destroyed); - } - assert(destroyed == 1); -} - -/** - * $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope. - * - * Params: - * T = Value type. - */ -struct Unique(T) -{ - private Payload!T payload; - - invariant - { - assert(payload is null || allocator_ !is null); - } - - /** - * Takes ownership over $(D_PARAM value), setting the counter to 1. - * $(D_PARAM value) may be a pointer, an object or a dynamic array. - * - * Params: - * value = Value whose ownership is taken over. - * allocator = Allocator used to destroy the $(D_PARAM value) and to - * allocate/deallocate internal storage. - * - * Precondition: $(D_INLINECODE allocator !is null) - */ - this(Payload!T value, shared Allocator allocator = defaultAllocator) - { - this(allocator); - this.payload = value; - } - - /// ditto - this(shared Allocator allocator) - in - { - assert(allocator !is null); - } - do - { - this.allocator_ = allocator; - } - - /** - * $(D_PSYMBOL Unique) is noncopyable. - */ - @disable this(this); - - /** - * Destroys the owned object. - */ - ~this() - { - allocator.dispose(this.payload); - } - - /** - * Initialized this $(D_PARAM Unique) and takes ownership over - * $(D_PARAM rhs). - * - * To reset $(D_PSYMBOL Unique) assign $(D_KEYWORD null). - * - * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will - * be used. If you need a different allocator, create a new - * $(D_PSYMBOL Unique) and assign it. - * - * Params: - * rhs = New object. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(Payload!T rhs) - { - allocator.dispose(this.payload); - this.payload = rhs; - return this; - } - - /// ditto - ref typeof(this) opAssign(typeof(null)) - { - allocator.dispose(this.payload); - return this; - } - - /// ditto - ref typeof(this) opAssign(typeof(this) rhs) - { - swap(this.allocator_, rhs.allocator_); - swap(this.payload, rhs.payload); - - return this; - } - - /// - @nogc nothrow pure @system unittest - { - auto rc = defaultAllocator.unique!int(5); - rc = defaultAllocator.make!int(7); - assert(*rc == 7); - } - - /** - * Returns: Reference to the owned object. - */ - inout(Payload!T) get() inout - { - return this.payload; - } - - version (D_Ddoc) - { - /** - * Dereferences the pointer. It is defined only for pointers, not for - * reference types like classes, that can be accessed directly. - * - * Params: - * op = Operation. - * - * Returns: Reference to the pointed value. - */ - ref inout(T) opUnary(string op)() inout - if (op == "*"); - } - else static if (isPointer!(Payload!T)) - { - ref inout(T) opUnary(string op)() inout - if (op == "*") - { - return *this.payload; - } - } - - /** - * Returns: Whether this $(D_PSYMBOL Unique) holds some value. - */ - @property bool isInitialized() const - { - return this.payload !is null; - } - - /// - @nogc nothrow pure @system unittest - { - Unique!int u; - assert(!u.isInitialized); - } - - /** - * Sets the internal pointer to $(D_KEYWORD). The allocator isn't changed. - * - * Returns: Reference to the owned object. - */ - Payload!T release() - { - auto payload = this.payload; - this.payload = null; - return payload; - } - - /// - @nogc nothrow pure @system unittest - { - auto u = defaultAllocator.unique!int(5); - assert(u.isInitialized); - - auto i = u.release(); - assert(*i == 5); - assert(!u.isInitialized); - } - - mixin DefaultAllocator; - alias get this; -} - -/// -@nogc nothrow pure @system unittest -{ - auto p = defaultAllocator.make!int(5); - auto s = Unique!int(p, defaultAllocator); - assert(*s == 5); -} - -/// -@nogc nothrow @system unittest -{ - static bool destroyed; - - static struct F - { - ~this() @nogc nothrow @safe - { - destroyed = true; - } - } - { - auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator); - } - assert(destroyed); -} - -/** - * Constructs a new object of type $(D_PARAM T) and wraps it in a - * $(D_PSYMBOL Unique) using $(D_PARAM args) as the parameter list for - * the constructor of $(D_PARAM T). - * - * Params: - * T = Type of the constructed object. - * A = Types of the arguments to the constructor of $(D_PARAM T). - * allocator = Allocator. - * args = Constructor arguments of $(D_PARAM T). - * - * Returns: Newly created $(D_PSYMBOL Unique!T). - * - * Precondition: $(D_INLINECODE allocator !is null) - */ -Unique!T unique(T, A...)(shared Allocator allocator, auto ref A args) -if (!is(T == interface) && !isAbstractClass!T - && !isAssociativeArray!T && !isArray!T) -in -{ - assert(allocator !is null); -} -do -{ - auto payload = allocator.make!(T, A)(args); - return Unique!T(payload, allocator); -} - -/** - * Constructs a new array with $(D_PARAM size) elements and wraps it in a - * $(D_PSYMBOL Unique). - * - * Params: - * T = Array type. - * size = Array size. - * allocator = Allocator. - * - * Returns: Newly created $(D_PSYMBOL Unique!T). - * - * Precondition: $(D_INLINECODE allocator !is null - * && size <= size_t.max / ElementType!T.sizeof) - */ -Unique!T unique(T)(shared Allocator allocator, const size_t size) -@trusted -if (isArray!T) -in -{ - assert(allocator !is null); - assert(size <= size_t.max / ElementType!T.sizeof); -} -do -{ - auto payload = allocator.resize!(ElementType!T)(null, size); - return Unique!T(payload, allocator); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(defaultAllocator.unique!B(5)))); - static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); -} - -@nogc nothrow pure @system unittest -{ - auto s = defaultAllocator.unique!int(5); - assert(*s == 5); - - s = null; - assert(s is null); -} - -@nogc nothrow pure @system unittest -{ - auto s = defaultAllocator.unique!int(5); - assert(*s == 5); - - s = defaultAllocator.unique!int(4); - assert(*s == 4); -} - -@nogc nothrow pure @system unittest -{ - auto p1 = defaultAllocator.make!int(5); - auto p2 = p1; - - auto rc = Unique!int(p1, defaultAllocator); - assert(rc.get() is p2); -} - -@nogc nothrow pure @system unittest -{ - auto rc = Unique!int(defaultAllocator); - assert(rc.allocator is defaultAllocator); -} diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 2296eac..2afdfe8 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -21,6 +21,7 @@ import tanya.container.string; import tanya.conv; import tanya.encoding.ascii; import tanya.format; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.net.iface; diff --git a/source/tanya/range/adapter.d b/source/tanya/range/adapter.d index 13f4c1e..bc20e10 100644 --- a/source/tanya/range/adapter.d +++ b/source/tanya/range/adapter.d @@ -15,7 +15,7 @@ module tanya.range.adapter; import tanya.algorithm.mutation; -import tanya.functional; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.range; diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index ae604e9..0b192b0 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -15,7 +15,7 @@ module tanya.range.primitive; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range.array; diff --git a/source/tanya/test/assertion.d b/source/tanya/test/assertion.d deleted file mode 100644 index 10105d7..0000000 --- a/source/tanya/test/assertion.d +++ /dev/null @@ -1,105 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Additional assertions. - * - * This module provides functions that assert whether a given expression - * satisfies some complex condition, that can't be tested with - * $(D_KEYWORD assert) in a single line. Internally all the functions - * just evaluate the expression and call $(D_KEYWORD assert). - * - * The functions can cause segmentation fault if the module is compiled - * in production mode and the condition fails. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/assertion.d, - * tanya/test/assertion.d) - */ -module tanya.test.assertion; - -import tanya.memory; -import tanya.meta.trait; - -/** - * Asserts whether the function $(D_PARAM expr) throws an exception of type - * $(D_PARAM E). If it does, the exception is catched and properly destroyed. - * If it doesn't, an assertion error is thrown. If the exception doesn't match - * $(D_PARAM E) type, it isn't catched and escapes. - * - * Params: - * E = Expected exception type. - * T = Throwing function type. - * Args = Argument types of the throwing function. - * expr = Throwing function. - * args = Arguments for $(D_PARAM expr). - */ -void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args) -if (isSomeFunction!T) -{ - try - { - cast(void) expr(args); - assert(false, "Expected exception not thrown"); - } - catch (E exception) - { - defaultAllocator.dispose(exception); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // If you want to test that an expression throws, you can wrap it into an - // arrow function. - static struct CtorThrows - { - this(int i) @nogc pure @safe - { - throw defaultAllocator.make!Exception(); - } - } - assertThrown!Exception(() => CtorThrows(8)); -} - -/** - * Asserts that the function $(D_PARAM expr) doesn't throw. - * - * If it does, the thrown exception is catched, properly destroyed and an - * assertion error is thrown instead. - * - * Params: - * T = Tested function type. - * Args = Argument types of $(D_PARAM expr). - * expr = Tested function. - * args = Arguments for $(D_PARAM expr). - */ -void assertNotThrown(T, Args...)(T expr, auto ref Args args) -if (isSomeFunction!T) -{ - try - { - cast(void) expr(args); - } - catch (Exception exception) - { - defaultAllocator.dispose(exception); - assert(false, "Unexpected exception thrown"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // If you want to test that an expression doesn't throw, you can wrap it - // into an arrow function. - static struct S - { - } - assertNotThrown(() => S()); -} diff --git a/source/tanya/test/package.d b/source/tanya/test/package.d deleted file mode 100644 index ab6f861..0000000 --- a/source/tanya/test/package.d +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Test suite for $(D_KEYWORD unittest)-blocks. - * - * Copyright: Eugene Wissner 2017-2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/package.d, - * tanya/test/package.d) - */ -module tanya.test; - -public import tanya.test.assertion; -public import tanya.test.stub; diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d deleted file mode 100644 index e1f8dcb..0000000 --- a/source/tanya/test/stub.d +++ /dev/null @@ -1,373 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Range and generic type generators. - * - * Copyright: Eugene Wissner 2018. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/stub.d, - * tanya/test/stub.d) - */ -module tanya.test.stub; - -/** - * Attribute signalizing that the generated range should contain the given - * number of elements. - * - * $(D_PSYMBOL Count) should be always specified with some value and not as a - * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), - * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. - * - * $(D_PSYMBOL Count) doesn't generate `.length` property - use - * $(D_PSYMBOL Length) for that. - * - * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, - * $(D_ILNINECODE Count(0)) is assumed. - * - * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). - */ -struct Count -{ - /// Original range length. - size_t count = 0; - - @disable this(); - - /** - * Constructs the attribute with the given length. - * - * Params: - * count = Original range length. - */ - this(size_t count) @nogc nothrow pure @safe - { - this.count = count; - } -} - -/** - * Attribute signalizing that the generated range should be infinite. - * - * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). - */ -struct Infinite -{ -} - -/** - * Generates `.length` property for the range. - * - * The length of the range can be specified as a constructor argument, - * otherwise it is 0. - * - * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). - */ -struct Length -{ - /// Original range length. - size_t length = 0; -} - -/** - * Attribute signalizing that the generated range should return values by - * reference. - * - * This atribute affects the return values of `.front`, `.back` and `[]`. - */ -struct WithLvalueElements -{ -} - -/** - * Generates an input range. - * - * Params: - * E = Element type. - */ -mixin template InputRangeStub(E = int) -{ - import tanya.meta.metafunction : Alias; - import tanya.meta.trait : evalUDA, getUDAs, hasUDA; - - /* - * Aliases for the attribute lookups to access them faster - */ - private enum bool infinite = hasUDA!(typeof(this), Infinite); - private enum bool withLvalueElements = hasUDA!(typeof(this), - WithLvalueElements); - private alias Count = getUDAs!(typeof(this), .Count); - private alias Length = getUDAs!(typeof(this), .Length); - - static if (Count.length != 0) - { - private enum size_t count = Count[0].count; - - static assert (!infinite, - "Range cannot have count and be infinite at the same time"); - static assert (Length.length == 0, - "Range cannot have count and length at the same time"); - } - else static if (Length.length != 0) - { - private enum size_t count = evalUDA!(Length[0]).length; - - static assert (!infinite, - "Range cannot have length and be infinite at the same time"); - } - else static if (!infinite) - { - private enum size_t count = 0; - } - - /* - * Member generation - */ - static if (infinite) - { - enum bool empty = false; - } - else - { - private size_t length_ = count; - - @property bool empty() const @nogc nothrow pure @safe - { - return this.length_ == 0; - } - } - - static if (withLvalueElements) - { - private E* element; // Pointer to enable range copying in save() - } - - void popFront() @nogc nothrow pure @safe - in (!empty) - { - static if (!infinite) - { - --this.length_; - } - } - - static if (withLvalueElements) - { - ref E front() @nogc nothrow pure @safe - in (!empty) - { - return *this.element; - } - } - else - { - E front() @nogc nothrow pure @safe - in (!empty) - { - return E.init; - } - } - - static if (Length.length != 0) - { - size_t length() const @nogc nothrow pure @safe - { - return this.length_; - } - } -} - -/** - * Generates a forward range. - * - * This mixin includes input range primitives as well, but can be combined with - * $(D_PSYMBOL RandomAccessRangeStub). - * - * Params: - * E = Element type. - */ -mixin template ForwardRangeStub(E = int) -{ - static if (!is(typeof(this.InputRangeMixin) == void)) - { - mixin InputRangeStub!E InputRangeMixin; - } - - auto save() @nogc nothrow pure @safe - { - return this; - } -} - -/** - * Generates a bidirectional range. - * - * This mixin includes forward range primitives as well, but can be combined with - * $(D_PSYMBOL RandomAccessRangeStub). - * - * Params: - * E = Element type. - */ -mixin template BidirectionalRangeStub(E = int) -{ - mixin ForwardRangeStub!E; - - void popBack() @nogc nothrow pure @safe - in (!empty) - { - static if (!infinite) - { - --this.length_; - } - } - - static if (withLvalueElements) - { - ref E back() @nogc nothrow pure @safe - in (!empty) - { - return *this.element; - } - } - else - { - E back() @nogc nothrow pure @safe - in (!empty) - { - return E.init; - } - } -} - -/** - * Generates a random-access range. - * - * This mixin includes input range primitives as well, but can be combined with - * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). - * - * Note that a random-access range also requires $(D_PSYMBOL Length) or - * $(D_PARAM Infinite) by definition. - * - * Params: - * E = Element type. - */ -mixin template RandomAccessRangeStub(E = int) -{ - static if (!is(typeof(this.InputRangeMixin) == void)) - { - mixin InputRangeStub!E InputRangeMixin; - } - - static if (withLvalueElements) - { - ref E opIndex(size_t) @nogc nothrow pure @safe - { - return *this.element; - } - } - else - { - E opIndex(size_t) @nogc nothrow pure @safe - { - return E.init; - } - } -} - -/** - * Struct with a disabled postblit constructor. - * - * $(D_PSYMBOL NonCopyable) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - */ -struct NonCopyable -{ - @disable this(this); -} - -/** - * Struct with an elaborate destructor. - * - * $(D_PSYMBOL WithDtor) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - * - * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor) - * accepts an additional `counter` argument, which is incremented by the - * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable, - * so the variable can be investigated after the struct isn't available - * anymore. - */ -struct WithDtor -{ - size_t* counter; - - this(ref size_t counter) @nogc nothrow pure @trusted - { - this.counter = &counter; - } - - ~this() @nogc nothrow pure @safe - { - if (this.counter !is null) - { - ++*this.counter; - } - } -} - -/** - * Struct supporting hashing. - * - * $(D_PSYMBOL Hashable) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - * - * The constructor accepts an additional parameter, which is returned by the - * `toHash()`-function. `0U` is returned if no hash value is given. - */ -struct Hashable -{ - size_t hash; - - size_t toHash() const @nogc nothrow pure @safe - { - return this.hash; - } -} - -/** - * Generates a $(D_KEYWORD struct) with common functionality. - * - * To specify the needed functionality use user-defined attributes on the - * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in. - * - * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable), - * $(D_PSYMBOL WithDtor). - */ -mixin template StructStub() -{ - import tanya.meta.trait : evalUDA, getUDAs, hasUDA; - - static if (hasUDA!(typeof(this), NonCopyable)) - { - @disable this(this); - } - - private alias Hashable = getUDAs!(typeof(this), .Hashable); - static if (Hashable.length > 0) - { - size_t toHash() const @nogc nothrow pure @safe - { - return evalUDA!(Hashable[0]).hash; - } - } - - static if (hasUDA!(typeof(this), WithDtor)) - { - ~this() @nogc nothrow pure @safe - { - } - } -} diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 1407c62..fa09831 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -17,10 +17,8 @@ */ module tanya.typecons; -import tanya.algorithm.mutation; -import tanya.conv; import tanya.format; -import tanya.functional; +import tanya.memory.lifecycle; import tanya.meta.metafunction; import tanya.meta.trait; version (unittest) import tanya.test.stub; diff --git a/sys/dub.json b/sys/dub.json index 8fb6d71..9201582 100644 --- a/sys/dub.json +++ b/sys/dub.json @@ -1,5 +1,12 @@ { "name": "sys", "description": "Low-level operating system bindings and definitions", - "targetType": "library" + "targetType": "library", + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/sys/source/tanya/sys/linux/syscall.d b/sys/source/tanya/sys/linux/syscall.d deleted file mode 100644 index ccc3fee..0000000 --- a/sys/source/tanya/sys/linux/syscall.d +++ /dev/null @@ -1,61 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/linux/syscall.d, - * tanya/sys/linux/syscall.d) - */ -module tanya.sys.linux.syscall; - -version (TanyaNative): - -extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t) -@nogc nothrow @system; - -extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t) -@nogc nothrow @system; - -extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t) -@nogc nothrow @system; - -extern ptrdiff_t syscall(ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t) @nogc nothrow @system; - -// Same syscalls as above but pure. -private template getOverloadMangling(size_t n) -{ - enum string getOverloadMangling = __traits(getOverloads, - tanya.sys.linux.syscall, - "syscall")[n].mangleof; -} - -pragma(mangle, getOverloadMangling!0) -extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t) -@nogc nothrow pure @system; - -pragma(mangle, getOverloadMangling!1) -extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t) -@nogc nothrow pure @system; - -pragma(mangle, getOverloadMangling!2) -extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t) -@nogc nothrow pure @system; - -pragma(mangle, getOverloadMangling!3) -extern ptrdiff_t syscall_(ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t, - ptrdiff_t) @nogc nothrow pure @system; diff --git a/sys/source/tanya/sys/posix/ioctl.d b/sys/source/tanya/sys/posix/ioctl.d deleted file mode 100644 index 86d1465..0000000 --- a/sys/source/tanya/sys/posix/ioctl.d +++ /dev/null @@ -1,78 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/posix/ioctl.d, - * tanya/sys/posix/ioctl.d) - */ -module tanya.sys.posix.ioctl; - -version (TanyaNative): - -enum -{ - SIOCADDRT = 0x890B, // Add routing table entry. - SIOCDELRT = 0x890C, // Delete routing table entry. - SIOCRTMSG = 0x890D, // Call to routing system. - - SIOCGIFNAME = 0x8910, // Get iface name. - SIOCSIFLINK = 0x8911, // Set iface channel. - SIOCGIFCONF = 0x8912, // Get iface list. - SIOCGIFFLAGS = 0x8913, // Get flags. - SIOCSIFFLAGS = 0x8914, // Set flags. - SIOCGIFADDR = 0x8915, // Get PA address. - SIOCSIFADDR = 0x8916, // Set PA address. - SIOCGIFDSTADDR = 0x8917, // Get remote PA address. - SIOCSIFDSTADDR = 0x8918, // Set remote PA address. - SIOCGIFBRDADDR = 0x8919, // Get broadcast PA address. - SIOCSIFBRDADDR = 0x891a, // Set broadcast PA address. - SIOCGIFNETMASK = 0x891b, // Get network PA mask. - SIOCSIFNETMASK = 0x891c, // Set network PA mask. - SIOCGIFMETRIC = 0x891d, // Get metric. - SIOCSIFMETRIC = 0x891e, // Set metric. - SIOCGIFMEM = 0x891f, // Get memory address (BSD). - SIOCSIFMEM = 0x8920, // Set memory address (BSD). - SIOCGIFMTU = 0x8921, // Get MTU size. - SIOCSIFMTU = 0x8922, // Set MTU size. - SIOCSIFNAME = 0x8923, // Set interface name. - SIOCSIFHWADDR = 0x8924, // Set hardware address. - SIOCGIFENCAP = 0x8925, // Get/set encapsulations. - SIOCSIFENCAP = 0x8926, - SIOCGIFHWADDR = 0x8927, // Get hardware address. - SIOCGIFSLAVE = 0x8929, // Driver slaving support. - SIOCSIFSLAVE = 0x8930, - SIOCADDMULTI = 0x8931, // Multicast address lists. - SIOCDELMULTI = 0x8932, - SIOCGIFINDEX = 0x8933, // Name -> if_index mapping. - SIOGIFINDEX = SIOCGIFINDEX, // Misprint compatibility. - SIOCSIFPFLAGS = 0x8934, // Set/get extended flags set. - SIOCGIFPFLAGS = 0x8935, - SIOCDIFADDR = 0x8936, // Delete PA address. - SIOCSIFHWBROADCAST = 0x8937, // Set hardware broadcast address. - SIOCGIFCOUNT = 0x8938, // Get number of devices. - - SIOCGIFBR = 0x8940, // Bridging support. - SIOCSIFBR = 0x8941, // Set bridging options. - - SIOCGIFTXQLEN = 0x8942, // Get the tx queue length. - SIOCSIFTXQLEN = 0x8943, // Set the tx queue length. - - SIOCDARP = 0x8953, // Delete ARP table entry. - SIOCGARP = 0x8954, // Get ARP table entry. - SIOCSARP = 0x8955, // Set ARP table entry. - - SIOCDRARP = 0x8960, // Delete RARP table entry. - SIOCGRARP = 0x8961, // Get RARP table entry. - SIOCSRARP = 0x8962, // Set RARP table entry. - - SIOCGIFMAP = 0x8970, // Get device parameters. - SIOCSIFMAP = 0x8971, // Set device parameters. - - SIOCADDDLCI = 0x8980, // Create new DLCI device. - SIOCDELDLCI = 0x8981, // Delete DLCI device. -} diff --git a/sys/source/tanya/sys/posix/mman.d b/sys/source/tanya/sys/posix/mman.d deleted file mode 100644 index 24a4701..0000000 --- a/sys/source/tanya/sys/posix/mman.d +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/posix/mman.d, - * tanya/sys/posix/mman.d) - */ -module tanya.sys.posix.mman; - -version (TanyaNative): - -enum -{ - PROT_EXEC = 0x4, // Page can be executed. - PROT_NONE = 0x0, // Page cannot be accessed. - PROT_READ = 0x1, // Page can be read. - PROT_WRITE = 0x2, // Page can be written. -} - -enum -{ - MAP_FIXED = 0x10, // Interpret addr exactly. - MAP_PRIVATE = 0x02, // Changes are private. - MAP_SHARED = 0x01, // Share changes. - MAP_ANONYMOUS = 0x20, // Don't use a file. -} diff --git a/sys/source/tanya/sys/posix/net/if_.d b/sys/source/tanya/sys/posix/net/if_.d deleted file mode 100644 index 1cb5b43..0000000 --- a/sys/source/tanya/sys/posix/net/if_.d +++ /dev/null @@ -1,27 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/posix/net/if_.d, - * tanya/sys/posix/net/if_.d) - */ -module tanya.sys.posix.net.if_; - -version (TanyaNative): - -enum size_t IF_NAMESIZE = 16; - -struct ifreq -{ - char[IF_NAMESIZE] ifr_name; - - union - { - int ifr_ifindex; - } -} diff --git a/sys/source/tanya/sys/posix/socket.d b/sys/source/tanya/sys/posix/socket.d deleted file mode 100644 index 8924a8d..0000000 --- a/sys/source/tanya/sys/posix/socket.d +++ /dev/null @@ -1,152 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/posix/socket.d, - * tanya/sys/posix/socket.d) - */ -module tanya.sys.posix.socket; - -version (TanyaNative): - -/* - * Protocol families. - */ -enum -{ - PF_UNSPEC = 0, // Unspecified. - PF_LOCAL = 1, // Local to host (pipes and file-domain). - PF_UNIX = PF_LOCAL, // POSIX name for PF_LOCAL. - PF_FILE = PF_LOCAL, // Another non-standard name for PF_LOCAL. - PF_INET = 2, // IP protocol family. - PF_AX25 = 3, // Amateur Radio AX.25. - PF_IPX = 4, // Novell Internet Protocol. - PF_APPLETALK = 5, // Appletalk DDP. - PF_NETROM = 6, // Amateur radio NetROM. - PF_BRIDGE = 7, // Multiprotocol bridge. - PF_ATMPVC = 8, // ATM PVCs. - PF_X25 = 9, // Reserved for X.25 project. - PF_INET6 = 10, // IP version 6. - PF_ROSE = 11, // Amateur Radio X.25 PLP. - PF_DECnet = 12, // Reserved for DECnet project. - PF_NETBEUI = 13, // Reserved for 802.2LLC project. - PF_SECURITY = 14, // Security callback pseudo AF. - PF_KEY = 15, // PF_KEY key management API. - PF_NETLINK = 16, // Kernel user interface device. - PF_ROUTE = PF_NETLINK, // Alias to emulate 4.4BSD. - PF_PACKET = 17, // Packet family. - PF_ASH = 18, // Ash. - PF_ECONET = 19, // Acorn Econet. - PF_ATMSVC = 20, // ATM SVCs. - PF_RDS = 21, // RDS sockets. - PF_SNA = 22, // Linux SNA Project. - PF_IRDA = 23, // IRDA sockets. - PF_PPPOX = 24, // PPPoX sockets. - PF_WANPIPE = 25, // Wanpipe API sockets. - PF_LLC = 26, // Linux LLC. - PF_IB = 27, // Native InfiniBand address. - PF_MPLS = 28, // MPLS. - PF_CAN = 29, // Controller Area Network. - PF_TIPC = 30, // TIPC sockets. - PF_BLUETOOTH = 31, // Bluetooth sockets. - PF_IUCV = 32, // IUCV sockets. - PF_RXRPC = 33, // RxRPC sockets. - PF_ISDN = 34, // mISDN sockets. - PF_PHONET = 35, // Phonet sockets. - PF_IEEE802154 = 36, // IEEE 802.15.4 sockets. - PF_CAIF = 37, // CAIF sockets. - PF_ALG = 38, // Algorithm sockets. - PF_NFC = 39, // NFC sockets. - PF_VSOCK = 40, // vSockets. - PF_MAX = 41, // For now. -} - -/* - * Address families. - */ -enum -{ - AF_UNSPEC = PF_UNSPEC, - AF_LOCAL = PF_LOCAL, - AF_UNIX = PF_UNIX, - AF_FILE = PF_FILE, - AF_INET = PF_INET, - AF_AX25 = PF_AX25, - AF_IPX = PF_IPX, - AF_APPLETALK = PF_APPLETALK, - AF_NETROM = PF_NETROM, - AF_BRIDGE = PF_BRIDGE, - AF_ATMPVC = PF_ATMPVC, - AF_X25 = PF_X25, - AF_INET6 = PF_INET6, - AF_ROSE = PF_ROSE, - AF_DECnet = PF_DECnet, - AF_NETBEUI = PF_NETBEUI, - AF_SECURITY = PF_SECURITY, - AF_KEY = PF_KEY, - AF_NETLINK = PF_NETLINK, - AF_ROUTE = PF_ROUTE, - AF_PACKET = PF_PACKET, - AF_ASH = PF_ASH, - AF_ECONET = PF_ECONET, - AF_ATMSVC = PF_ATMSVC, - AF_RDS = PF_RDS, - AF_SNA = PF_SNA, - AF_IRDA = PF_IRDA, - AF_PPPOX = PF_PPPOX, - AF_WANPIPE = PF_WANPIPE, - AF_LLC = PF_LLC, - AF_IB = PF_IB, - AF_MPLS = PF_MPLS, - AF_CAN = PF_CAN, - AF_TIPC = PF_TIPC, - AF_BLUETOOTH = PF_BLUETOOTH, - AF_IUCV = PF_IUCV, - AF_RXRPC = PF_RXRPC, - AF_ISDN = PF_ISDN, - AF_PHONET = PF_PHONET, - AF_IEEE802154 = PF_IEEE802154, - AF_CAIF = PF_CAIF, - AF_ALG = PF_ALG, - AF_NFC = PF_NFC, - AF_VSOCK = PF_VSOCK, - AF_MAX = PF_MAX, -} - -/* - * Types of sockets. - */ -enum -{ - // Sequenced, reliable, connection-based byte streams. - SOCK_STREAM = 1, - // Connectionless, unreliable datagrams of fixed maximum length. - SOCK_DGRAM = 2, - // Raw protocol interface. - SOCK_RAW = 3, - // Reliably-delivered messages. - SOCK_RDM = 4, - // Sequenced, reliable, connection-based, datagrams of fixed maximum - // length. - SOCK_SEQPACKET = 5, - // Datagram Congestion Control Protocol. - SOCK_DCCP = 6, - // Linux specific way of getting packets at the dev level. For writing rarp - // and other similar things on the user level. - SOCK_PACKET = 10, -} - -/* - * Flags to be ORed into the type parameter of socket and socketpair and used - * for the flags parameter of paccept. - */ -enum -{ - SOCK_CLOEXEC = 0x80000, // Atomically set close-on-exec flag for the new descriptor(s). - SOCK_NONBLOCK = 0x800, // Atomically mark descriptor(s) as non-blocking. -} diff --git a/sys/source/tanya/sys/windows/def.d b/sys/source/tanya/sys/windows/def.d deleted file mode 100644 index 84b4864..0000000 --- a/sys/source/tanya/sys/windows/def.d +++ /dev/null @@ -1,66 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Base type definitions and aliases. - * - * This module doesn't provide aliases for all types used by Windows, but only - * for types that can vary on different platforms. For example there is no - * need to define `INT32` alias for D, since $(D_KEYWORD int) is always a - * 32-bit signed integer. But `int` and its Windows alias `INT` is not the - * same on all platforms in C, so its size can be something differen than - * 32 bit, therefore an $(D_PSYMBOL INT) alias is available in this module. - * $(D_PARAM TCHAR) can be a $(D_KEYWORD char) if Unicode isn't supported or - * $(D_KEYWORD wchar) if Unicode is supported, so $(D_PSYMBOL TCHAR) is - * defined here. - * Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/def.d, - * tanya/sys/windows/def.d) - */ -module tanya.sys.windows.def; - -version (Windows): - -alias BYTE = ubyte; -alias TBYTE = wchar; // If Unicode, otherwise char. -alias CHAR = char; // Signed or unsigned char. -alias WCHAR = wchar; -alias TCHAR = wchar; // If Unicode, otherwise char. -alias SHORT = short; -alias USHORT = ushort; -alias WORD = ushort; -alias INT = int; -alias UINT = uint; -alias LONG = int; -alias ULONG = uint; -alias DWORD = uint; -alias LONGLONG = long; // Or double. -alias ULONGLONG = ulong; // Or double. -alias DWORDLONG = ulong; -alias FLOAT = float; -alias BOOL = int; -alias BOOLEAN = BYTE; - -alias HANDLE = void*; -enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) -1; - -enum TRUE = 1; -enum FALSE = 0; - -alias PSTR = CHAR*; -alias PWSTR = WCHAR*; -alias PTSTR = TCHAR*; - -align(1) struct GUID -{ - uint Data1; - ushort Data2; - ushort Data3; - char[8] Data4; -} diff --git a/sys/source/tanya/sys/windows/ifdef.d b/sys/source/tanya/sys/windows/ifdef.d deleted file mode 100644 index f091427..0000000 --- a/sys/source/tanya/sys/windows/ifdef.d +++ /dev/null @@ -1,30 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/ifdef.d, - * tanya/sys/windows/ifdef.d) - */ -module tanya.sys.windows.ifdef; - -version (Windows): - -import tanya.sys.windows.def; - -union NET_LUID_LH -{ - ulong Value; - ulong Info; -} - -alias NET_LUID = NET_LUID_LH; -alias IF_LUID = NET_LUID_LH; - -alias NET_IFINDEX = ULONG; - -enum size_t IF_MAX_STRING_SIZE = 256; diff --git a/sys/source/tanya/sys/windows/iphlpapi.d b/sys/source/tanya/sys/windows/iphlpapi.d deleted file mode 100644 index 37d0f16..0000000 --- a/sys/source/tanya/sys/windows/iphlpapi.d +++ /dev/null @@ -1,39 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Copyright: Eugene Wissner 2018-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/iphlpapi.d, - * tanya/sys/windows/iphlpapi.d) - */ -module tanya.sys.windows.iphlpapi; - -version (Windows): - -import tanya.sys.windows.def; -import tanya.sys.windows.ifdef; - -extern(Windows) -DWORD ConvertInterfaceNameToLuidA(const(CHAR)* InterfaceName, - NET_LUID* InterfaceLuid) -@nogc nothrow @system; - -extern(Windows) -DWORD ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid, - NET_IFINDEX* InterfaceIndex) -@nogc nothrow @system; - -extern(Windows) -DWORD ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex, - NET_LUID* InterfaceLuid) -@nogc nothrow @system; - -extern(Windows) -DWORD ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid, - PSTR InterfaceName, - size_t Length) -@nogc nothrow @system; diff --git a/sys/source/tanya/sys/windows/package.d b/sys/source/tanya/sys/windows/package.d deleted file mode 100644 index 5e9b396..0000000 --- a/sys/source/tanya/sys/windows/package.d +++ /dev/null @@ -1,21 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/package.d, - * tanya/sys/windows/package.d) - */ -module tanya.sys.windows; - -version (Windows): - -public import tanya.sys.windows.def; -public import tanya.sys.windows.ifdef; -public import tanya.sys.windows.iphlpapi; -public import tanya.sys.windows.winbase; -public import tanya.sys.windows.winsock2; diff --git a/sys/source/tanya/sys/windows/winbase.d b/sys/source/tanya/sys/windows/winbase.d deleted file mode 100644 index 407b6d2..0000000 --- a/sys/source/tanya/sys/windows/winbase.d +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Definitions from winbase.h. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/winbase.d, - * tanya/sys/windows/winbase.d) - */ -module tanya.sys.windows.winbase; - -version (Windows): - -public import tanya.sys.windows.def; - -struct OVERLAPPED -{ - size_t Internal; - size_t InternalHigh; - union - { - struct - { - DWORD Offset; - DWORD OffsetHigh; - } - void* Pointer; - } - HANDLE hEvent; -} - -extern(Windows) -HANDLE CreateIoCompletionPort(HANDLE FileHandle, - HANDLE ExistingCompletionPort, - size_t CompletionKey, - DWORD NumberOfConcurrentThreads) -nothrow @system @nogc; - -extern(Windows) -BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, - DWORD* lpNumberOfBytes, - size_t* lpCompletionKey, - OVERLAPPED** lpOverlapped, - DWORD dwMilliseconds) nothrow @system @nogc; - -extern(Windows) -BOOL GetOverlappedResult(HANDLE hFile, - OVERLAPPED* lpOverlapped, - DWORD* lpNumberOfBytesTransferred, - BOOL bWait) nothrow @system @nogc; diff --git a/sys/source/tanya/sys/windows/winsock2.d b/sys/source/tanya/sys/windows/winsock2.d deleted file mode 100644 index 931e76b..0000000 --- a/sys/source/tanya/sys/windows/winsock2.d +++ /dev/null @@ -1,219 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * Definitions from winsock2.h, ws2def.h and MSWSock.h. - * - * Copyright: Eugene Wissner 2017-2019. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/winsock2.d, - * tanya/sys/windows/winsock2.d) - */ -module tanya.sys.windows.winsock2; - -version (Windows): - -public import tanya.sys.windows.def; -public import tanya.sys.windows.winbase; - -alias SOCKET = size_t; -enum SOCKET INVALID_SOCKET = ~0; -enum SOCKET_ERROR = -1; - -enum -{ - IOC_UNIX = 0x00000000, - IOC_WS2 = 0x08000000, - IOC_PROTOCOL = 0x10000000, - IOC_VOID = 0x20000000, // No parameters. - IOC_OUT = 0x40000000, // Copy parameters back. - IOC_IN = 0x80000000, // Copy parameters into. - IOC_VENDOR = 0x18000000, - IOC_WSK = (IOC_WS2 | 0x07000000), // _WIN32_WINNT >= 0x0600. - IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back. -} - -template _WSAIO(int x, int y) -{ - enum _WSAIO = IOC_VOID | x | y; -} -template _WSAIOR(int x, int y) -{ - enum _WSAIOR = IOC_OUT | x | y; -} -template _WSAIOW(int x, int y) -{ - enum _WSAIOW = IOC_IN | x | y; -} -template _WSAIORW(int x, int y) -{ - enum _WSAIORW = IOC_INOUT | x | y; -} - -alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); -alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); -alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); -alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); -alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); -alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); -alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); -alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); -alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); -alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); -alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); -alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); -alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); -alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); -alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); -alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); -alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); -alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); -alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); - -alias GROUP = uint; - -enum -{ - WSA_FLAG_OVERLAPPED = 0x01, - WSA_FLAG_MULTIPOINT_C_ROOT = 0x02, - WSA_FLAG_MULTIPOINT_C_LEAF = 0x04, - WSA_FLAG_MULTIPOINT_D_ROOT = 0x08, - WSA_FLAG_MULTIPOINT_D_LEAF = 0x10, - WSA_FLAG_ACCESS_SYSTEM_SECURITY = 0x40, - WSA_FLAG_NO_HANDLE_INHERIT = 0x80, - WSA_FLAG_REGISTERED_IO = 0x100, -} - -enum MAX_PROTOCOL_CHAIN = 7; -enum BASE_PROTOCOL = 1; -enum LAYERED_PROTOCOL = 0; -enum WSAPROTOCOL_LEN = 255; - -struct WSAPROTOCOLCHAIN -{ - int ChainLen; - DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; -} - -struct WSABUF -{ - ULONG len; - CHAR* buf; -} - -struct WSAPROTOCOL_INFO -{ - DWORD dwServiceFlags1; - DWORD dwServiceFlags2; - DWORD dwServiceFlags3; - DWORD dwServiceFlags4; - DWORD dwProviderFlags; - GUID ProviderId; - DWORD dwCatalogEntryId; - WSAPROTOCOLCHAIN ProtocolChain; - int iVersion; - int iAddressFamily; - int iMaxSockAddr; - int iMinSockAddr; - int iSocketType; - int iProtocol; - int iProtocolMaxOffset; - int iNetworkByteOrder; - int iSecurityScheme; - DWORD dwMessageSize; - DWORD dwProviderReserved; - TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; -} - -const GUID WSAID_GETACCEPTEXSOCKADDRS = { - 0xb5367df2, 0xcbac, 0x11cf, - [0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92], -}; - -const GUID WSAID_ACCEPTEX = { - 0xb5367df1, 0xcbac, 0x11cf, - [0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92], -}; - -alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(DWORD dwError, - DWORD cbTransferred, - OVERLAPPED* lpOverlapped, - DWORD dwFlags) nothrow @nogc; - -extern(Windows) -SOCKET WSASocket(int af, - int type, - int protocol, - WSAPROTOCOL_INFO* lpProtocolInfo, - GROUP g, - DWORD dwFlags) nothrow @system @nogc; - -extern(Windows) -int WSARecv(SOCKET s, - WSABUF* lpBuffers, - DWORD dwBufferCount, - DWORD* lpNumberOfBytesRecvd, - DWORD* lpFlags, - OVERLAPPED* lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) -nothrow @system @nogc; - -extern(Windows) -int WSASend(SOCKET s, - WSABUF* lpBuffers, - DWORD dwBufferCount, - DWORD* lpNumberOfBytesRecvd, - DWORD lpFlags, - OVERLAPPED* lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) -nothrow @system @nogc; - -extern(Windows) -int WSAIoctl(SOCKET s, - uint dwIoControlCode, - void* lpvInBuffer, - uint cbInBuffer, - void* lpvOutBuffer, - uint cbOutBuffer, - uint* lpcbBytesReturned, - OVERLAPPED* lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) -nothrow @system @nogc; - -alias ADDRESS_FAMILY = USHORT; - -struct SOCKADDR -{ - ADDRESS_FAMILY sa_family; // Address family. - CHAR[14] sa_data; // Up to 14 bytes of direct address. -} - -alias LPFN_GETACCEPTEXSOCKADDRS = void function(void*, - DWORD, - DWORD, - DWORD, - SOCKADDR**, - INT*, - SOCKADDR**, - INT*) nothrow @nogc; - -alias LPFN_ACCEPTEX = extern(Windows) BOOL function(SOCKET, - SOCKET, - void*, - DWORD, - DWORD, - DWORD, - DWORD*, - OVERLAPPED*) @nogc nothrow; - -enum -{ - SO_MAXDG = 0x7009, - SO_MAXPATHDG = 0x700A, - SO_UPDATE_ACCEPT_CONTEXT = 0x700B, - SO_CONNECT_TIME = 0x700C, - SO_UPDATE_CONNECT_CONTEXT = 0x7010, -} diff --git a/sys/tanya/sys/linux/syscall.d b/sys/tanya/sys/linux/syscall.d new file mode 100644 index 0000000..5c90ea5 --- /dev/null +++ b/sys/tanya/sys/linux/syscall.d @@ -0,0 +1,59 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.linux.syscall; + +version (TanyaNative): + +extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t) +@nogc nothrow @system; + +extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t) +@nogc nothrow @system; + +extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t) +@nogc nothrow @system; + +extern ptrdiff_t syscall(ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t) @nogc nothrow @system; + +// Same syscalls as above but pure. +private template getOverloadMangling(size_t n) +{ + enum string getOverloadMangling = __traits(getOverloads, + tanya.sys.linux.syscall, + "syscall")[n].mangleof; +} + +pragma(mangle, getOverloadMangling!0) +extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t) +@nogc nothrow pure @system; + +pragma(mangle, getOverloadMangling!1) +extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t) +@nogc nothrow pure @system; + +pragma(mangle, getOverloadMangling!2) +extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t) +@nogc nothrow pure @system; + +pragma(mangle, getOverloadMangling!3) +extern ptrdiff_t syscall_(ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t, + ptrdiff_t) @nogc nothrow pure @system; diff --git a/sys/tanya/sys/posix/ioctl.d b/sys/tanya/sys/posix/ioctl.d new file mode 100644 index 0000000..c206c89 --- /dev/null +++ b/sys/tanya/sys/posix/ioctl.d @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.posix.ioctl; + +version (TanyaNative): + +enum +{ + SIOCADDRT = 0x890B, // Add routing table entry. + SIOCDELRT = 0x890C, // Delete routing table entry. + SIOCRTMSG = 0x890D, // Call to routing system. + + SIOCGIFNAME = 0x8910, // Get iface name. + SIOCSIFLINK = 0x8911, // Set iface channel. + SIOCGIFCONF = 0x8912, // Get iface list. + SIOCGIFFLAGS = 0x8913, // Get flags. + SIOCSIFFLAGS = 0x8914, // Set flags. + SIOCGIFADDR = 0x8915, // Get PA address. + SIOCSIFADDR = 0x8916, // Set PA address. + SIOCGIFDSTADDR = 0x8917, // Get remote PA address. + SIOCSIFDSTADDR = 0x8918, // Set remote PA address. + SIOCGIFBRDADDR = 0x8919, // Get broadcast PA address. + SIOCSIFBRDADDR = 0x891a, // Set broadcast PA address. + SIOCGIFNETMASK = 0x891b, // Get network PA mask. + SIOCSIFNETMASK = 0x891c, // Set network PA mask. + SIOCGIFMETRIC = 0x891d, // Get metric. + SIOCSIFMETRIC = 0x891e, // Set metric. + SIOCGIFMEM = 0x891f, // Get memory address (BSD). + SIOCSIFMEM = 0x8920, // Set memory address (BSD). + SIOCGIFMTU = 0x8921, // Get MTU size. + SIOCSIFMTU = 0x8922, // Set MTU size. + SIOCSIFNAME = 0x8923, // Set interface name. + SIOCSIFHWADDR = 0x8924, // Set hardware address. + SIOCGIFENCAP = 0x8925, // Get/set encapsulations. + SIOCSIFENCAP = 0x8926, + SIOCGIFHWADDR = 0x8927, // Get hardware address. + SIOCGIFSLAVE = 0x8929, // Driver slaving support. + SIOCSIFSLAVE = 0x8930, + SIOCADDMULTI = 0x8931, // Multicast address lists. + SIOCDELMULTI = 0x8932, + SIOCGIFINDEX = 0x8933, // Name -> if_index mapping. + SIOGIFINDEX = SIOCGIFINDEX, // Misprint compatibility. + SIOCSIFPFLAGS = 0x8934, // Set/get extended flags set. + SIOCGIFPFLAGS = 0x8935, + SIOCDIFADDR = 0x8936, // Delete PA address. + SIOCSIFHWBROADCAST = 0x8937, // Set hardware broadcast address. + SIOCGIFCOUNT = 0x8938, // Get number of devices. + + SIOCGIFBR = 0x8940, // Bridging support. + SIOCSIFBR = 0x8941, // Set bridging options. + + SIOCGIFTXQLEN = 0x8942, // Get the tx queue length. + SIOCSIFTXQLEN = 0x8943, // Set the tx queue length. + + SIOCDARP = 0x8953, // Delete ARP table entry. + SIOCGARP = 0x8954, // Get ARP table entry. + SIOCSARP = 0x8955, // Set ARP table entry. + + SIOCDRARP = 0x8960, // Delete RARP table entry. + SIOCGRARP = 0x8961, // Get RARP table entry. + SIOCSRARP = 0x8962, // Set RARP table entry. + + SIOCGIFMAP = 0x8970, // Get device parameters. + SIOCSIFMAP = 0x8971, // Set device parameters. + + SIOCADDDLCI = 0x8980, // Create new DLCI device. + SIOCDELDLCI = 0x8981, // Delete DLCI device. +} diff --git a/sys/tanya/sys/posix/mman.d b/sys/tanya/sys/posix/mman.d new file mode 100644 index 0000000..21266ea --- /dev/null +++ b/sys/tanya/sys/posix/mman.d @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.posix.mman; + +version (TanyaNative): + +enum +{ + PROT_EXEC = 0x4, // Page can be executed. + PROT_NONE = 0x0, // Page cannot be accessed. + PROT_READ = 0x1, // Page can be read. + PROT_WRITE = 0x2, // Page can be written. +} + +enum +{ + MAP_FIXED = 0x10, // Interpret addr exactly. + MAP_PRIVATE = 0x02, // Changes are private. + MAP_SHARED = 0x01, // Share changes. + MAP_ANONYMOUS = 0x20, // Don't use a file. +} diff --git a/sys/tanya/sys/posix/net/if_.d b/sys/tanya/sys/posix/net/if_.d new file mode 100644 index 0000000..c9e3b86 --- /dev/null +++ b/sys/tanya/sys/posix/net/if_.d @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.posix.net.if_; + +version (TanyaNative): + +enum size_t IF_NAMESIZE = 16; + +struct ifreq +{ + char[IF_NAMESIZE] ifr_name; + + union + { + int ifr_ifindex; + } +} diff --git a/sys/tanya/sys/posix/socket.d b/sys/tanya/sys/posix/socket.d new file mode 100644 index 0000000..4f01a66 --- /dev/null +++ b/sys/tanya/sys/posix/socket.d @@ -0,0 +1,150 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.posix.socket; + +version (TanyaNative): + +/* + * Protocol families. + */ +enum +{ + PF_UNSPEC = 0, // Unspecified. + PF_LOCAL = 1, // Local to host (pipes and file-domain). + PF_UNIX = PF_LOCAL, // POSIX name for PF_LOCAL. + PF_FILE = PF_LOCAL, // Another non-standard name for PF_LOCAL. + PF_INET = 2, // IP protocol family. + PF_AX25 = 3, // Amateur Radio AX.25. + PF_IPX = 4, // Novell Internet Protocol. + PF_APPLETALK = 5, // Appletalk DDP. + PF_NETROM = 6, // Amateur radio NetROM. + PF_BRIDGE = 7, // Multiprotocol bridge. + PF_ATMPVC = 8, // ATM PVCs. + PF_X25 = 9, // Reserved for X.25 project. + PF_INET6 = 10, // IP version 6. + PF_ROSE = 11, // Amateur Radio X.25 PLP. + PF_DECnet = 12, // Reserved for DECnet project. + PF_NETBEUI = 13, // Reserved for 802.2LLC project. + PF_SECURITY = 14, // Security callback pseudo AF. + PF_KEY = 15, // PF_KEY key management API. + PF_NETLINK = 16, // Kernel user interface device. + PF_ROUTE = PF_NETLINK, // Alias to emulate 4.4BSD. + PF_PACKET = 17, // Packet family. + PF_ASH = 18, // Ash. + PF_ECONET = 19, // Acorn Econet. + PF_ATMSVC = 20, // ATM SVCs. + PF_RDS = 21, // RDS sockets. + PF_SNA = 22, // Linux SNA Project. + PF_IRDA = 23, // IRDA sockets. + PF_PPPOX = 24, // PPPoX sockets. + PF_WANPIPE = 25, // Wanpipe API sockets. + PF_LLC = 26, // Linux LLC. + PF_IB = 27, // Native InfiniBand address. + PF_MPLS = 28, // MPLS. + PF_CAN = 29, // Controller Area Network. + PF_TIPC = 30, // TIPC sockets. + PF_BLUETOOTH = 31, // Bluetooth sockets. + PF_IUCV = 32, // IUCV sockets. + PF_RXRPC = 33, // RxRPC sockets. + PF_ISDN = 34, // mISDN sockets. + PF_PHONET = 35, // Phonet sockets. + PF_IEEE802154 = 36, // IEEE 802.15.4 sockets. + PF_CAIF = 37, // CAIF sockets. + PF_ALG = 38, // Algorithm sockets. + PF_NFC = 39, // NFC sockets. + PF_VSOCK = 40, // vSockets. + PF_MAX = 41, // For now. +} + +/* + * Address families. + */ +enum +{ + AF_UNSPEC = PF_UNSPEC, + AF_LOCAL = PF_LOCAL, + AF_UNIX = PF_UNIX, + AF_FILE = PF_FILE, + AF_INET = PF_INET, + AF_AX25 = PF_AX25, + AF_IPX = PF_IPX, + AF_APPLETALK = PF_APPLETALK, + AF_NETROM = PF_NETROM, + AF_BRIDGE = PF_BRIDGE, + AF_ATMPVC = PF_ATMPVC, + AF_X25 = PF_X25, + AF_INET6 = PF_INET6, + AF_ROSE = PF_ROSE, + AF_DECnet = PF_DECnet, + AF_NETBEUI = PF_NETBEUI, + AF_SECURITY = PF_SECURITY, + AF_KEY = PF_KEY, + AF_NETLINK = PF_NETLINK, + AF_ROUTE = PF_ROUTE, + AF_PACKET = PF_PACKET, + AF_ASH = PF_ASH, + AF_ECONET = PF_ECONET, + AF_ATMSVC = PF_ATMSVC, + AF_RDS = PF_RDS, + AF_SNA = PF_SNA, + AF_IRDA = PF_IRDA, + AF_PPPOX = PF_PPPOX, + AF_WANPIPE = PF_WANPIPE, + AF_LLC = PF_LLC, + AF_IB = PF_IB, + AF_MPLS = PF_MPLS, + AF_CAN = PF_CAN, + AF_TIPC = PF_TIPC, + AF_BLUETOOTH = PF_BLUETOOTH, + AF_IUCV = PF_IUCV, + AF_RXRPC = PF_RXRPC, + AF_ISDN = PF_ISDN, + AF_PHONET = PF_PHONET, + AF_IEEE802154 = PF_IEEE802154, + AF_CAIF = PF_CAIF, + AF_ALG = PF_ALG, + AF_NFC = PF_NFC, + AF_VSOCK = PF_VSOCK, + AF_MAX = PF_MAX, +} + +/* + * Types of sockets. + */ +enum +{ + // Sequenced, reliable, connection-based byte streams. + SOCK_STREAM = 1, + // Connectionless, unreliable datagrams of fixed maximum length. + SOCK_DGRAM = 2, + // Raw protocol interface. + SOCK_RAW = 3, + // Reliably-delivered messages. + SOCK_RDM = 4, + // Sequenced, reliable, connection-based, datagrams of fixed maximum + // length. + SOCK_SEQPACKET = 5, + // Datagram Congestion Control Protocol. + SOCK_DCCP = 6, + // Linux specific way of getting packets at the dev level. For writing rarp + // and other similar things on the user level. + SOCK_PACKET = 10, +} + +/* + * Flags to be ORed into the type parameter of socket and socketpair and used + * for the flags parameter of paccept. + */ +enum +{ + SOCK_CLOEXEC = 0x80000, // Atomically set close-on-exec flag for the new descriptor(s). + SOCK_NONBLOCK = 0x800, // Atomically mark descriptor(s) as non-blocking. +} diff --git a/sys/tanya/sys/windows/def.d b/sys/tanya/sys/windows/def.d new file mode 100644 index 0000000..5b7ab59 --- /dev/null +++ b/sys/tanya/sys/windows/def.d @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Base type definitions and aliases. + * + * This module doesn't provide aliases for all types used by Windows, but only + * for types that can vary on different platforms. For example there is no + * need to define `INT32` alias for D, since $(D_KEYWORD int) is always a + * 32-bit signed integer. But `int` and its Windows alias `INT` is not the + * same on all platforms in C, so its size can be something differen than + * 32 bit, therefore an $(D_PSYMBOL INT) alias is available in this module. + * $(D_PARAM TCHAR) can be a $(D_KEYWORD char) if Unicode isn't supported or + * $(D_KEYWORD wchar) if Unicode is supported, so $(D_PSYMBOL TCHAR) is + * defined here. + * Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows.def; + +version (Windows): + +alias BYTE = ubyte; +alias TBYTE = wchar; // If Unicode, otherwise char. +alias CHAR = char; // Signed or unsigned char. +alias WCHAR = wchar; +alias TCHAR = wchar; // If Unicode, otherwise char. +alias SHORT = short; +alias USHORT = ushort; +alias WORD = ushort; +alias INT = int; +alias UINT = uint; +alias LONG = int; +alias ULONG = uint; +alias DWORD = uint; +alias LONGLONG = long; // Or double. +alias ULONGLONG = ulong; // Or double. +alias DWORDLONG = ulong; +alias FLOAT = float; +alias BOOL = int; +alias BOOLEAN = BYTE; + +alias HANDLE = void*; +enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) -1; + +enum TRUE = 1; +enum FALSE = 0; + +alias PSTR = CHAR*; +alias PWSTR = WCHAR*; +alias PTSTR = TCHAR*; + +align(1) struct GUID +{ + uint Data1; + ushort Data2; + ushort Data3; + char[8] Data4; +} diff --git a/sys/tanya/sys/windows/ifdef.d b/sys/tanya/sys/windows/ifdef.d new file mode 100644 index 0000000..084b5ca --- /dev/null +++ b/sys/tanya/sys/windows/ifdef.d @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows.ifdef; + +version (Windows): + +import tanya.sys.windows.def; + +union NET_LUID_LH +{ + ulong Value; + ulong Info; +} + +alias NET_LUID = NET_LUID_LH; +alias IF_LUID = NET_LUID_LH; + +alias NET_IFINDEX = ULONG; + +enum size_t IF_MAX_STRING_SIZE = 256; diff --git a/sys/tanya/sys/windows/iphlpapi.d b/sys/tanya/sys/windows/iphlpapi.d new file mode 100644 index 0000000..450b265 --- /dev/null +++ b/sys/tanya/sys/windows/iphlpapi.d @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Copyright: Eugene Wissner 2018-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows.iphlpapi; + +version (Windows): + +import tanya.sys.windows.def; +import tanya.sys.windows.ifdef; + +extern(Windows) +DWORD ConvertInterfaceNameToLuidA(const(CHAR)* InterfaceName, + NET_LUID* InterfaceLuid) +@nogc nothrow @system; + +extern(Windows) +DWORD ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid, + NET_IFINDEX* InterfaceIndex) +@nogc nothrow @system; + +extern(Windows) +DWORD ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex, + NET_LUID* InterfaceLuid) +@nogc nothrow @system; + +extern(Windows) +DWORD ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid, + PSTR InterfaceName, + size_t Length) +@nogc nothrow @system; diff --git a/sys/tanya/sys/windows/package.d b/sys/tanya/sys/windows/package.d new file mode 100644 index 0000000..ef03e29 --- /dev/null +++ b/sys/tanya/sys/windows/package.d @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows; + +version (Windows): + +public import tanya.sys.windows.def; +public import tanya.sys.windows.ifdef; +public import tanya.sys.windows.iphlpapi; +public import tanya.sys.windows.winbase; +public import tanya.sys.windows.winsock2; diff --git a/sys/tanya/sys/windows/winbase.d b/sys/tanya/sys/windows/winbase.d new file mode 100644 index 0000000..ba977eb --- /dev/null +++ b/sys/tanya/sys/windows/winbase.d @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Definitions from winbase.h. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows.winbase; + +version (Windows): + +public import tanya.sys.windows.def; + +struct OVERLAPPED +{ + size_t Internal; + size_t InternalHigh; + union + { + struct + { + DWORD Offset; + DWORD OffsetHigh; + } + void* Pointer; + } + HANDLE hEvent; +} + +extern(Windows) +HANDLE CreateIoCompletionPort(HANDLE FileHandle, + HANDLE ExistingCompletionPort, + size_t CompletionKey, + DWORD NumberOfConcurrentThreads) +nothrow @system @nogc; + +extern(Windows) +BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, + DWORD* lpNumberOfBytes, + size_t* lpCompletionKey, + OVERLAPPED** lpOverlapped, + DWORD dwMilliseconds) nothrow @system @nogc; + +extern(Windows) +BOOL GetOverlappedResult(HANDLE hFile, + OVERLAPPED* lpOverlapped, + DWORD* lpNumberOfBytesTransferred, + BOOL bWait) nothrow @system @nogc; diff --git a/sys/tanya/sys/windows/winsock2.d b/sys/tanya/sys/windows/winsock2.d new file mode 100644 index 0000000..1c6a452 --- /dev/null +++ b/sys/tanya/sys/windows/winsock2.d @@ -0,0 +1,217 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Definitions from winsock2.h, ws2def.h and MSWSock.h. + * + * Copyright: Eugene Wissner 2017-2019. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + */ +module tanya.sys.windows.winsock2; + +version (Windows): + +public import tanya.sys.windows.def; +public import tanya.sys.windows.winbase; + +alias SOCKET = size_t; +enum SOCKET INVALID_SOCKET = ~0; +enum SOCKET_ERROR = -1; + +enum +{ + IOC_UNIX = 0x00000000, + IOC_WS2 = 0x08000000, + IOC_PROTOCOL = 0x10000000, + IOC_VOID = 0x20000000, // No parameters. + IOC_OUT = 0x40000000, // Copy parameters back. + IOC_IN = 0x80000000, // Copy parameters into. + IOC_VENDOR = 0x18000000, + IOC_WSK = (IOC_WS2 | 0x07000000), // _WIN32_WINNT >= 0x0600. + IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back. +} + +template _WSAIO(int x, int y) +{ + enum _WSAIO = IOC_VOID | x | y; +} +template _WSAIOR(int x, int y) +{ + enum _WSAIOR = IOC_OUT | x | y; +} +template _WSAIOW(int x, int y) +{ + enum _WSAIOW = IOC_IN | x | y; +} +template _WSAIORW(int x, int y) +{ + enum _WSAIORW = IOC_INOUT | x | y; +} + +alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); +alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); +alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); +alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4); +alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); +alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); +alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); +alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); +alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); +alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); +alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); +alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); +alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); +alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); +alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); +alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); +alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); +alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); +alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); + +alias GROUP = uint; + +enum +{ + WSA_FLAG_OVERLAPPED = 0x01, + WSA_FLAG_MULTIPOINT_C_ROOT = 0x02, + WSA_FLAG_MULTIPOINT_C_LEAF = 0x04, + WSA_FLAG_MULTIPOINT_D_ROOT = 0x08, + WSA_FLAG_MULTIPOINT_D_LEAF = 0x10, + WSA_FLAG_ACCESS_SYSTEM_SECURITY = 0x40, + WSA_FLAG_NO_HANDLE_INHERIT = 0x80, + WSA_FLAG_REGISTERED_IO = 0x100, +} + +enum MAX_PROTOCOL_CHAIN = 7; +enum BASE_PROTOCOL = 1; +enum LAYERED_PROTOCOL = 0; +enum WSAPROTOCOL_LEN = 255; + +struct WSAPROTOCOLCHAIN +{ + int ChainLen; + DWORD[MAX_PROTOCOL_CHAIN] ChainEntries; +} + +struct WSABUF +{ + ULONG len; + CHAR* buf; +} + +struct WSAPROTOCOL_INFO +{ + DWORD dwServiceFlags1; + DWORD dwServiceFlags2; + DWORD dwServiceFlags3; + DWORD dwServiceFlags4; + DWORD dwProviderFlags; + GUID ProviderId; + DWORD dwCatalogEntryId; + WSAPROTOCOLCHAIN ProtocolChain; + int iVersion; + int iAddressFamily; + int iMaxSockAddr; + int iMinSockAddr; + int iSocketType; + int iProtocol; + int iProtocolMaxOffset; + int iNetworkByteOrder; + int iSecurityScheme; + DWORD dwMessageSize; + DWORD dwProviderReserved; + TCHAR[WSAPROTOCOL_LEN + 1] szProtocol; +} + +const GUID WSAID_GETACCEPTEXSOCKADDRS = { + 0xb5367df2, 0xcbac, 0x11cf, + [0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92], +}; + +const GUID WSAID_ACCEPTEX = { + 0xb5367df1, 0xcbac, 0x11cf, + [0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92], +}; + +alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(DWORD dwError, + DWORD cbTransferred, + OVERLAPPED* lpOverlapped, + DWORD dwFlags) nothrow @nogc; + +extern(Windows) +SOCKET WSASocket(int af, + int type, + int protocol, + WSAPROTOCOL_INFO* lpProtocolInfo, + GROUP g, + DWORD dwFlags) nothrow @system @nogc; + +extern(Windows) +int WSARecv(SOCKET s, + WSABUF* lpBuffers, + DWORD dwBufferCount, + DWORD* lpNumberOfBytesRecvd, + DWORD* lpFlags, + OVERLAPPED* lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +nothrow @system @nogc; + +extern(Windows) +int WSASend(SOCKET s, + WSABUF* lpBuffers, + DWORD dwBufferCount, + DWORD* lpNumberOfBytesRecvd, + DWORD lpFlags, + OVERLAPPED* lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +nothrow @system @nogc; + +extern(Windows) +int WSAIoctl(SOCKET s, + uint dwIoControlCode, + void* lpvInBuffer, + uint cbInBuffer, + void* lpvOutBuffer, + uint cbOutBuffer, + uint* lpcbBytesReturned, + OVERLAPPED* lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +nothrow @system @nogc; + +alias ADDRESS_FAMILY = USHORT; + +struct SOCKADDR +{ + ADDRESS_FAMILY sa_family; // Address family. + CHAR[14] sa_data; // Up to 14 bytes of direct address. +} + +alias LPFN_GETACCEPTEXSOCKADDRS = void function(void*, + DWORD, + DWORD, + DWORD, + SOCKADDR**, + INT*, + SOCKADDR**, + INT*) nothrow @nogc; + +alias LPFN_ACCEPTEX = extern(Windows) BOOL function(SOCKET, + SOCKET, + void*, + DWORD, + DWORD, + DWORD, + DWORD*, + OVERLAPPED*) @nogc nothrow; + +enum +{ + SO_MAXDG = 0x7009, + SO_MAXPATHDG = 0x700A, + SO_UPDATE_ACCEPT_CONTEXT = 0x700B, + SO_CONNECT_TIME = 0x700C, + SO_UPDATE_CONNECT_CONTEXT = 0x7010, +} -- cgit v1.2.3