summaryrefslogtreecommitdiff
path: root/middle
diff options
context:
space:
mode:
Diffstat (limited to 'middle')
-rw-r--r--middle/dub.json5
-rw-r--r--middle/tanya/memory/allocator.d4
-rw-r--r--middle/tanya/memory/lifetime.d7
-rw-r--r--middle/tanya/memory/smartref.d4
-rw-r--r--middle/tanya/meta.d920
-rw-r--r--middle/tanya/os/error.d413
-rw-r--r--middle/tanya/os/package.d18
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;