diff options
Diffstat (limited to 'middle')
| -rw-r--r-- | middle/dub.json | 5 | ||||
| -rw-r--r-- | middle/tanya/memory/allocator.d | 4 | ||||
| -rw-r--r-- | middle/tanya/memory/lifetime.d | 7 | ||||
| -rw-r--r-- | middle/tanya/memory/smartref.d | 4 | ||||
| -rw-r--r-- | middle/tanya/meta.d | 920 | ||||
| -rw-r--r-- | middle/tanya/os/error.d | 413 | ||||
| -rw-r--r-- | middle/tanya/os/package.d | 18 |
7 files changed, 1358 insertions, 13 deletions
diff --git a/middle/dub.json b/middle/dub.json index 0e91cd0..aef09d2 100644 --- a/middle/dub.json +++ b/middle/dub.json @@ -3,11 +3,6 @@ "description": "Runtime, middle-level utilities", "targetType": "library", - "dependencies": { - "tanya:meta": "*", - "tanya:os": "*" - }, - "dependencies-linux": { "mir-linux-kernel": "~>1.0.0" }, diff --git a/middle/tanya/memory/allocator.d b/middle/tanya/memory/allocator.d index 2acd044..30ef03b 100644 --- a/middle/tanya/memory/allocator.d +++ b/middle/tanya/memory/allocator.d @@ -17,9 +17,9 @@ */ module tanya.memory.allocator; -import std.traits : hasElaborateDestructor, isAssociativeArray, isArray; +import std.traits; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; /** * Abstract class implementing a basic allocator. diff --git a/middle/tanya/memory/lifetime.d b/middle/tanya/memory/lifetime.d index a5b98cd..aa9c7d1 100644 --- a/middle/tanya/memory/lifetime.d +++ b/middle/tanya/memory/lifetime.d @@ -14,11 +14,10 @@ */ module tanya.memory.lifetime; -import std.traits : isInnerClass, hasElaborateAssign, hasElaborateCopyConstructor, hasElaborateDestructor, - isAssignable, isNested, isAbstractClass, isAggregateType, isStaticArray; +import std.traits; +import std.meta : AliasSeq; import tanya.memory.allocator; -import tanya.meta.metafunction; -import tanya.meta.trait; +import tanya.meta; package(tanya) void destroyAllImpl(R, E)(R p) { diff --git a/middle/tanya/memory/smartref.d b/middle/tanya/memory/smartref.d index 59ac3af..6134a04 100644 --- a/middle/tanya/memory/smartref.d +++ b/middle/tanya/memory/smartref.d @@ -23,10 +23,10 @@ */ module tanya.memory.smartref; -import std.traits : isPointer, isAbstractClass, isAssociativeArray, isDynamicArray, isArray; +import std.traits; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; private template Payload(T) { diff --git a/middle/tanya/meta.d b/middle/tanya/meta.d new file mode 100644 index 0000000..909415e --- /dev/null +++ b/middle/tanya/meta.d @@ -0,0 +1,920 @@ +/* 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-2025. + * 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; + +import std.meta; +import std.traits; + +/** + * Determines whether $(D_PARAM T) is a wide string, i.e. consists of + * $(D_KEYWORD dchar). + * + * The character type of the string and the string itself can have any type + * qualifiers. + * + * Static $(D_KEYWORD dchar) 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. + */ +enum bool isWideString(T) = is(immutable T == immutable dchar[]); + +/// +@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])); +} + +/* + * 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); +} + +/** + * 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); +} + +/** + * 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)); +} + +/** + * 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. + */ +enum bool canFind(T, L...) = staticIndexOf!(T, L) != -1; + +/// ditto +enum bool canFind(alias T, L...) = staticIndexOf!(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. + */ +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)); +} + +/** + * Attaches 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)); +} + +/** + * Attaches 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/middle/tanya/os/error.d b/middle/tanya/os/error.d new file mode 100644 index 0000000..733ff46 --- /dev/null +++ b/middle/tanya/os/error.d @@ -0,0 +1,413 @@ +/* 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-2025. + * 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; + +// 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) return @nogc nothrow pure @safe + { + this.value_ = that; + return this; + } + + /// ditto + ref ErrorCode opAssign(const ErrorCode that) return @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"); + } + + private ErrorNo value_ = ErrorNo.success; + + alias ErrorNo this; +} diff --git a/middle/tanya/os/package.d b/middle/tanya/os/package.d new file mode 100644 index 0000000..9b81c18 --- /dev/null +++ b/middle/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-2025. + * 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; |
