Merge os, middle and meta subpackages

This commit is contained in:
2025-08-25 16:09:03 +02:00
parent 720d259cfc
commit b8fa670c5a
37 changed files with 193 additions and 1640 deletions

View File

@@ -3,11 +3,6 @@
"description": "Runtime, middle-level utilities",
"targetType": "library",
"dependencies": {
"tanya:meta": "*",
"tanya:os": "*"
},
"dependencies-linux": {
"mir-linux-kernel": "~>1.0.0"
},

View File

@@ -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.

View File

@@ -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)
{

View File

@@ -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)
{

920
middle/tanya/meta.d Normal file
View File

@@ -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));
}

413
middle/tanya/os/error.d Normal file
View File

@@ -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;
}

18
middle/tanya/os/package.d Normal file
View File

@@ -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;