Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
6072bfab68
|
|||
4acf163b42 | |||
e1fd528607 | |||
1c57368f43 | |||
1c5e18b92e | |||
07b388eecb | |||
20ae6465d6 | |||
728eaf88fb
|
|||
90797a48be
|
|||
5453c646f6
|
|||
7dd4c44140
|
|||
81b4fb88f5
|
@ -1,6 +1,5 @@
|
||||
# Tanya
|
||||
|
||||
[](https://build.caraus.tech/go/pipelines)
|
||||
[](https://code.dlang.org/packages/tanya)
|
||||
[](https://code.dlang.org/packages/tanya)
|
||||
[](https://opensource.org/licenses/MPL-2.0)
|
||||
@ -163,7 +162,7 @@ parameter is used)
|
||||
|
||||
| DMD | GCC |
|
||||
|:-------:|:---------:|
|
||||
| 2.098.1 | 11.2 |
|
||||
| 2.100.0 | 12.1 |
|
||||
|
||||
## Further characteristics
|
||||
|
||||
|
7
dub.json
7
dub.json
@ -2,7 +2,7 @@
|
||||
"name": "tanya",
|
||||
"description": "@nogc library. Containers, networking, metaprogramming, memory management, utilities",
|
||||
"license": "MPL-2.0",
|
||||
"copyright": "© Eugene Wissner <info@caraus.de>",
|
||||
"copyright": "© Eugene Wissner <belka@caraus.de>",
|
||||
"authors": [
|
||||
"Eugene Wissner"
|
||||
],
|
||||
@ -13,10 +13,7 @@
|
||||
"tanya:meta": "*",
|
||||
"tanya:os": "*",
|
||||
"tanya:middle": "*",
|
||||
"tanya:test": "*"
|
||||
},
|
||||
|
||||
"dependencies-linux": {
|
||||
"tanya:test": "*",
|
||||
"mir-linux-kernel": "~>1.0.0"
|
||||
},
|
||||
|
||||
|
@ -70,47 +70,6 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
|
||||
static assert(!isWideString!(dchar[10]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a complex type.
|
||||
*
|
||||
* Complex types are:
|
||||
* $(UL
|
||||
* $(LI cfloat)
|
||||
* $(LI ifloat)
|
||||
* $(LI cdouble)
|
||||
* $(LI idouble)
|
||||
* $(LI creal)
|
||||
* $(LI ireal)
|
||||
* )
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a complex type,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
|
||||
|| is(Unqual!(OriginalType!T) == ifloat)
|
||||
|| is(Unqual!(OriginalType!T) == cdouble)
|
||||
|| is(Unqual!(OriginalType!T) == idouble)
|
||||
|| is(Unqual!(OriginalType!T) == creal)
|
||||
|| is(Unqual!(OriginalType!T) == ireal);
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(isComplex!cfloat);
|
||||
static assert(isComplex!ifloat);
|
||||
static assert(isComplex!cdouble);
|
||||
static assert(isComplex!idouble);
|
||||
static assert(isComplex!creal);
|
||||
static assert(isComplex!ireal);
|
||||
|
||||
static assert(!isComplex!float);
|
||||
static assert(!isComplex!double);
|
||||
static assert(!isComplex!real);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests whether $(D_PARAM T) is an interface.
|
||||
*
|
||||
@ -353,32 +312,6 @@ enum bool isIntegral(T) = isUnsigned!T
|
||||
static assert(!isIntegral!float);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a numeric (floating point, integral or
|
||||
* complex) type.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a numeric type,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isIntegral!T),
|
||||
* $(D_PSYMBOL isFloatingPoint),
|
||||
* $(D_PSYMBOL isComplex).
|
||||
*/
|
||||
enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T;
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
alias F = float;
|
||||
static assert(isNumeric!F);
|
||||
static assert(!isNumeric!bool);
|
||||
static assert(!isNumeric!char);
|
||||
static assert(!isNumeric!wchar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a boolean type, i.e. $(D_KEYWORD bool).
|
||||
*
|
||||
@ -458,67 +391,6 @@ enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char)
|
||||
static assert(!isSomeChar!uint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a scalar type.
|
||||
*
|
||||
* Scalar types are numbers, booleans and characters.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a scalar type,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isNumeric),
|
||||
* $(D_PSYMBOL isBoolean),
|
||||
* $(D_PSYMBOL isSomeChar).
|
||||
*/
|
||||
enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T;
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(isScalarType!int);
|
||||
static assert(!isScalarType!(int[]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a basic type.
|
||||
*
|
||||
* Basic types are scalar types and $(D_KEYWORD void).
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a basic type,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isScalarType).
|
||||
*/
|
||||
enum bool isBasicType(T) = isScalarType!T || is(T : void);
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct S
|
||||
{
|
||||
}
|
||||
class C
|
||||
{
|
||||
}
|
||||
enum E : int
|
||||
{
|
||||
i = 0,
|
||||
}
|
||||
|
||||
static assert(isBasicType!void);
|
||||
static assert(isBasicType!(shared void));
|
||||
static assert(isBasicType!E);
|
||||
static assert(!isBasicType!(int*));
|
||||
static assert(!isBasicType!(void function()));
|
||||
static assert(!isBasicType!C);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a pointer type.
|
||||
*
|
||||
@ -676,34 +548,6 @@ template isAssociativeArray(T)
|
||||
static assert(!isAssociativeArray!bool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is a built-in type.
|
||||
*
|
||||
* Built-in types are all basic types and arrays.
|
||||
*
|
||||
* Params:
|
||||
* T = A type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a built-in type,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isBasicType!T),
|
||||
* $(D_PSYMBOL isArray),
|
||||
* $(D_PSYMBOL isAssociativeArray).
|
||||
*/
|
||||
enum bool isBuiltinType(T) = isBasicType!T
|
||||
|| isArray!T
|
||||
|| isAssociativeArray!T;
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(isBuiltinType!int);
|
||||
static assert(isBuiltinType!(int[]));
|
||||
static assert(isBuiltinType!(int[int]));
|
||||
static assert(!isBuiltinType!(int*));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM T) is an aggregate type.
|
||||
*
|
||||
@ -844,57 +688,6 @@ enum bool isSomeString(T) = isNarrowString!T || isWideString!T;
|
||||
static assert(!isSomeString!(char[10]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value of type $(D_PARAM T). In contrast to
|
||||
* $(D_INLINECODE T.min) this template works with floating point and complex
|
||||
* types as well.
|
||||
*
|
||||
* Params:
|
||||
* T = Integral, boolean, floating point, complex or character type.
|
||||
*
|
||||
* Returns: The minimum value of $(D_PARAM T).
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL isIntegral),
|
||||
* $(D_PSYMBOL isBoolean),
|
||||
* $(D_PSYMBOL isSomeChar),
|
||||
* $(D_PSYMBOL isFloatingPoint),
|
||||
* $(D_PSYMBOL isComplex).
|
||||
*/
|
||||
template mostNegative(T)
|
||||
{
|
||||
static if (isIntegral!T || isBoolean!T || isSomeChar!T)
|
||||
{
|
||||
enum T mostNegative = T.min;
|
||||
}
|
||||
else static if (isFloatingPoint!T || isComplex!T)
|
||||
{
|
||||
enum T mostNegative = -T.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, T.stringof ~ " doesn't have the minimum value");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(mostNegative!char == char.min);
|
||||
static assert(mostNegative!wchar == wchar.min);
|
||||
static assert(mostNegative!dchar == dchar.min);
|
||||
|
||||
static assert(mostNegative!byte == byte.min);
|
||||
static assert(mostNegative!ubyte == ubyte.min);
|
||||
static assert(mostNegative!bool == bool.min);
|
||||
|
||||
static assert(mostNegative!float == -float.max);
|
||||
static assert(mostNegative!double == -double.max);
|
||||
static assert(mostNegative!real == -real.max);
|
||||
|
||||
static assert(mostNegative!ifloat == -ifloat.max);
|
||||
static assert(mostNegative!cfloat == -cfloat.max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the type $(D_PARAM T) is copyable.
|
||||
*
|
||||
@ -2335,7 +2128,7 @@ if (isCallable!F)
|
||||
}
|
||||
else
|
||||
{
|
||||
enum getDefault(T[i .. i + 1] name)
|
||||
auto getDefault(T[i .. i + 1] name)
|
||||
{
|
||||
return name[0];
|
||||
}
|
||||
|
@ -19,13 +19,30 @@ import tanya.memory.allocator;
|
||||
import tanya.memory.op;
|
||||
import tanya.os.error;
|
||||
|
||||
extern(C) pragma(mangle, "mmap")
|
||||
private void* mapMemory(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
@nogc nothrow pure @system;
|
||||
version (Windows)
|
||||
{
|
||||
import core.sys.windows.basetsd : SIZE_T;
|
||||
import core.sys.windows.windef : BOOL, DWORD;
|
||||
import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, PAGE_READWRITE, PVOID;
|
||||
|
||||
extern(C) pragma(mangle, "munmap")
|
||||
private bool unmapMemory(shared void* addr, size_t length)
|
||||
@nogc nothrow pure @system;
|
||||
extern (Windows)
|
||||
private PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD)
|
||||
@nogc nothrow pure @system;
|
||||
|
||||
extern (Windows)
|
||||
private BOOL VirtualFree(shared PVOID, SIZE_T, DWORD)
|
||||
@nogc nothrow pure @system;
|
||||
}
|
||||
else
|
||||
{
|
||||
extern(C) pragma(mangle, "mmap")
|
||||
private void* mapMemory(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||
@nogc nothrow pure @system;
|
||||
|
||||
extern(C) pragma(mangle, "munmap")
|
||||
private bool unmapMemory(shared void* addr, size_t length)
|
||||
@nogc nothrow pure @system;
|
||||
}
|
||||
|
||||
/*
|
||||
* This allocator allocates memory in regions (multiple of 64 KB for example).
|
||||
@ -192,7 +209,10 @@ final class MmapPool : Allocator
|
||||
{
|
||||
block.region.next.prev = block.region.prev;
|
||||
}
|
||||
return unmapMemory(block.region, block.region.size) == 0;
|
||||
version (Windows)
|
||||
return VirtualFree(block.region, 0, MEM_RELEASE) != 0;
|
||||
else
|
||||
return unmapMemory(block.region, block.region.size) == 0;
|
||||
}
|
||||
// Merge blocks if neigbours are free.
|
||||
if (block.next !is null && block.next.free)
|
||||
@ -380,15 +400,29 @@ final class MmapPool : Allocator
|
||||
{
|
||||
return null;
|
||||
}
|
||||
void* p = mapMemory(null,
|
||||
regionSize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0);
|
||||
if (cast(ptrdiff_t) p == -1)
|
||||
version (Windows)
|
||||
{
|
||||
return null;
|
||||
void* p = VirtualAlloc(null,
|
||||
regionSize,
|
||||
MEM_COMMIT,
|
||||
PAGE_READWRITE);
|
||||
if (p is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void* p = mapMemory(null,
|
||||
regionSize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0);
|
||||
if (cast(ptrdiff_t) p == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Region region = cast(Region) p;
|
||||
|
@ -109,7 +109,7 @@ private struct SingletonByRef(E)
|
||||
this.element = &element;
|
||||
}
|
||||
|
||||
@property ref inout(E) front() inout return
|
||||
@property ref inout(E) front() inout return scope
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
@ -148,7 +148,7 @@ private struct SingletonByRef(E)
|
||||
return typeof(this)(*this.element);
|
||||
}
|
||||
|
||||
ref inout(E) opIndex(size_t i) inout return
|
||||
ref inout(E) opIndex(size_t i) inout return scope
|
||||
in
|
||||
{
|
||||
assert(!empty);
|
||||
@ -226,7 +226,7 @@ if (F.length == 1)
|
||||
*
|
||||
* Returns: Accumulated value.
|
||||
*/
|
||||
auto foldr(R, T)(scope R range, scope return auto ref T init)
|
||||
auto foldr(R, T)(scope R range, auto ref T init)
|
||||
if (isBidirectionalRange!R)
|
||||
{
|
||||
if (range.empty)
|
||||
|
@ -1,318 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Bit manipulation.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2018-2020.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/bitmanip.d,
|
||||
* tanya/bitmanip.d)
|
||||
*/
|
||||
module tanya.bitmanip;
|
||||
|
||||
import tanya.meta.metafunction;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
|
||||
/**
|
||||
* Determines whether $(D_PARAM E) is a $(D_KEYWORD enum), whose members can be
|
||||
* used as bit flags.
|
||||
*
|
||||
* This is the case if all members of $(D_PARAM E) are integral numbers that
|
||||
* are either 0 or positive integral powers of 2.
|
||||
*
|
||||
* Params:
|
||||
* E = Some $(D_KEYWORD enum).
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM E) contains only bit flags,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
template isBitFlagEnum(E)
|
||||
{
|
||||
enum bool isValid(OriginalType!E x) = x == 0
|
||||
|| (x > 0 && ((x & (x - 1)) == 0));
|
||||
static if (isIntegral!E)
|
||||
{
|
||||
enum bool isBitFlagEnum = allSatisfy!(isValid, EnumMembers!E);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum bool isBitFlagEnum = false;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
enum Valid
|
||||
{
|
||||
none = 0,
|
||||
one = 1 << 0,
|
||||
two = 1 << 1,
|
||||
}
|
||||
static assert(isBitFlagEnum!Valid);
|
||||
|
||||
enum Invalid
|
||||
{
|
||||
one,
|
||||
two,
|
||||
three,
|
||||
four,
|
||||
}
|
||||
static assert(!isBitFlagEnum!Invalid);
|
||||
|
||||
enum Negative
|
||||
{
|
||||
one = -1,
|
||||
two = -2,
|
||||
}
|
||||
static assert(!isBitFlagEnum!Negative);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that $(D_PARAM field) contains only bits from $(D_PARAM E).
|
||||
*
|
||||
* Params:
|
||||
* E = Some $(D_KEYWORD enum).
|
||||
* field = Bit field.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_PARAM field) is valid, $(D_KEYWORD false)
|
||||
* otherwise.
|
||||
*/
|
||||
bool containsBitFlags(E)(E field)
|
||||
if (isBitFlagEnum!E)
|
||||
{
|
||||
OriginalType!E fillField()
|
||||
{
|
||||
typeof(return) full;
|
||||
static foreach (member; EnumMembers!E)
|
||||
{
|
||||
full |= member;
|
||||
}
|
||||
return full;
|
||||
}
|
||||
enum OriginalType!E full = fillField();
|
||||
return (field & ~full) == OriginalType!E.init;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
enum E
|
||||
{
|
||||
one,
|
||||
two,
|
||||
three,
|
||||
}
|
||||
assert(containsBitFlags(E.one | E.two));
|
||||
assert(!containsBitFlags(cast(E) 0x8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to use $(D_KEYWORD enum) values as a set of bit flags.
|
||||
*
|
||||
* $(D_PSYMBOL BitFlags) behaves the same as a bit field of type $(D_PARAM E),
|
||||
* but does additional cheks to ensure that the bit field contains only valid
|
||||
* values, this is only values from $(D_PARAM E).
|
||||
*
|
||||
* Params:
|
||||
* E = Some $(D_KEYWORD enum).
|
||||
*/
|
||||
struct BitFlags(E)
|
||||
if (isBitFlagEnum!E)
|
||||
{
|
||||
private OriginalType!E field;
|
||||
|
||||
/**
|
||||
* Constructs $(D_PSYMBOL BitFlags) from $(D_PARAM field).
|
||||
*
|
||||
* Params:
|
||||
* field = Bits to be set.
|
||||
*/
|
||||
this(E field)
|
||||
{
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts $(D_PSYMBOL BitFlags) to a boolean.
|
||||
*
|
||||
* It is $(D_KEYWORD true) if any bit is set, $(D_KEYWORD false) otherwise.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL BitFlags) contains any
|
||||
* set bits, $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opCast(T : bool)()
|
||||
{
|
||||
return this.field != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to the original type of $(D_PARAM E) ($(D_KEYWORD int) by
|
||||
* default).
|
||||
*
|
||||
* Returns: $(D_KEYWORD this) as $(D_INLINECODE OriginalType!T).
|
||||
*/
|
||||
OriginalType!E opCast(T : OriginalType!E)() const
|
||||
{
|
||||
return this.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests (&), sets (|) or toggles (^) bits.
|
||||
*
|
||||
* Params:
|
||||
* op = Operation.
|
||||
* that = 0 or more bit flags.
|
||||
*
|
||||
* Returns: New $(D_PSYMBOL BitFlags) object.
|
||||
*/
|
||||
BitFlags opBinary(string op)(E that) const
|
||||
if (op == "&" || op == "|" || op == "^")
|
||||
{
|
||||
BitFlags result = this;
|
||||
mixin("return result " ~ op ~ "= that;");
|
||||
}
|
||||
|
||||
/// ditto
|
||||
BitFlags opBinary(string op)(BitFlags that) const
|
||||
if (op == "&" || op == "|" || op == "^")
|
||||
{
|
||||
BitFlags result = this;
|
||||
mixin("return result " ~ op ~ "= that;");
|
||||
}
|
||||
|
||||
/// ditto
|
||||
BitFlags opBinaryRight(string op)(E that) const
|
||||
if (op == "&" || op == "|" || op == "^")
|
||||
{
|
||||
BitFlags result = this;
|
||||
mixin("return result " ~ op ~ "= that;");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests (&), sets (|) or toggles (^) bits.
|
||||
*
|
||||
* Params:
|
||||
* op = Operation.
|
||||
* that = 0 or more bit flags.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref BitFlags opOpAssign(string op)(E that)
|
||||
if (op == "&" || op == "|" || op == "^")
|
||||
{
|
||||
mixin("this.field " ~ op ~ "= that;");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref BitFlags opOpAssign(string op)(BitFlags that)
|
||||
if (op == "&" || op == "|" || op == "^")
|
||||
{
|
||||
mixin("this.field " ~ op ~ "= that.field;");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts all bit flags.
|
||||
*
|
||||
* Returns: New $(D_PSYMBOL BitFlags) object with all bits inverted.
|
||||
*/
|
||||
BitFlags opUnary(string op : "~")() const
|
||||
{
|
||||
BitFlags result;
|
||||
result.field = ~this.field;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a bit field.
|
||||
*
|
||||
* Params:
|
||||
* that = Bit field of type $(D_PARAM E).
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref BitFlags opAssign(E that)
|
||||
{
|
||||
this.field = that;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this $(D_PSYMBOL BitFlags) object to another bit field.
|
||||
*
|
||||
* Params:
|
||||
* that = $(D_PSYMBOL BitFlags) object or a bit field of type
|
||||
* $(D_PARAM E).
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if $(D_KEYWORD this) and $(D_PARAM that)
|
||||
* contain the same bits ,$(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opEquals(E that) const
|
||||
{
|
||||
return this.field == that;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
bool opEquals(BitFlags that) const
|
||||
{
|
||||
return this.field == that.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash value of this object.
|
||||
*
|
||||
* Returns: Hash value.
|
||||
*/
|
||||
size_t toHash() const
|
||||
{
|
||||
return cast(size_t) this.field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a $(D_PSYMBOL BitFlags) object initialized with $(D_PARAM field).
|
||||
*
|
||||
* Params:
|
||||
* E = Some $(D_KEYWORD enum).
|
||||
* field = Bits to be set.
|
||||
*/
|
||||
BitFlags!E bitFlags(E)(E field)
|
||||
if (isBitFlagEnum!E)
|
||||
{
|
||||
return BitFlags!E(field);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
enum E
|
||||
{
|
||||
one = 1 << 0,
|
||||
two = 1 << 1,
|
||||
three = 1 << 2,
|
||||
}
|
||||
// Construct with E.one and E.two set
|
||||
auto flags = bitFlags(E.one | E.two);
|
||||
|
||||
// Test wheter E.one is set
|
||||
assert(flags & E.one);
|
||||
|
||||
// Toggle E.one
|
||||
flags ^= E.one;
|
||||
assert(!(flags & E.one));
|
||||
|
||||
// Set E.three
|
||||
flags |= E.three;
|
||||
assert(flags & E.three);
|
||||
|
||||
// Clear E.three
|
||||
flags &= ~E.three;
|
||||
assert(!(flags & E.three));
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
module tanya.container.buffer;
|
||||
|
||||
import std.traits : isScalarType;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.meta.trait;
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
/*
|
||||
* Internal package used by containers that rely on entries/nodes.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2020.
|
||||
* Copyright: Eugene Wissner 2016-2022.
|
||||
* 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)
|
||||
@ -19,7 +19,6 @@ import tanya.memory.allocator;
|
||||
import tanya.memory.lifetime;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
import tanya.typecons;
|
||||
|
||||
package struct SEntry(T)
|
||||
{
|
||||
@ -54,7 +53,11 @@ package struct Bucket(K, V = void)
|
||||
}
|
||||
else
|
||||
{
|
||||
alias KV = Tuple!(K, "key", V, "value");
|
||||
package struct KV
|
||||
{
|
||||
package K key;
|
||||
package V value;
|
||||
}
|
||||
KV kv;
|
||||
}
|
||||
BucketStatus status = BucketStatus.empty;
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
module tanya.conv;
|
||||
|
||||
import std.traits : Unsigned;
|
||||
import std.traits : Unsigned, isNumeric;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.meta.trait;
|
||||
|
@ -38,7 +38,7 @@
|
||||
*
|
||||
* More advanced formatting is currently not implemented.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017-2020.
|
||||
* Copyright: Eugene Wissner 2017-2022.
|
||||
* 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)
|
||||
@ -56,7 +56,6 @@ import tanya.meta.metafunction;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
import tanya.range;
|
||||
import tanya.typecons : Tuple;
|
||||
|
||||
// Returns the last part of buffer with converted number.
|
||||
package(tanya) char[] integral2String(T)(T number, return ref char[21] buffer)
|
||||
@ -940,6 +939,12 @@ private struct uint128
|
||||
{
|
||||
ulong[2] data;
|
||||
|
||||
private struct DivMod
|
||||
{
|
||||
uint128 quotient;
|
||||
uint128 remainder;
|
||||
}
|
||||
|
||||
this(ulong upper, ulong lower) @nogc nothrow pure @safe
|
||||
{
|
||||
this.data[0] = upper;
|
||||
@ -1174,7 +1179,7 @@ private struct uint128
|
||||
return this.data[1];
|
||||
}
|
||||
|
||||
Tuple!(uint128, uint128) divMod(ulong rhs) const @nogc nothrow pure @safe
|
||||
DivMod divMod(ulong rhs) const @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(rhs != uint128(), "Division by 0");
|
||||
@ -1197,22 +1202,22 @@ private struct uint128
|
||||
typeof(return) result;
|
||||
for (ubyte x = this.bits; x > 0; --x)
|
||||
{
|
||||
result[0] = result[0] << 1;
|
||||
result[1] = result[1] << 1;
|
||||
result.quotient = result.quotient << 1;
|
||||
result.remainder = result.remainder << 1;
|
||||
|
||||
if ((this >> (x - 1U)) & 1)
|
||||
{
|
||||
++result[1];
|
||||
++result.remainder;
|
||||
}
|
||||
|
||||
if (result[1] >= rhs)
|
||||
if (result.remainder >= rhs)
|
||||
{
|
||||
if (result[1].data[1] < rhs)
|
||||
if (result.remainder.data[1] < rhs)
|
||||
{
|
||||
--result[1].data[0];
|
||||
--result.remainder.data[0];
|
||||
}
|
||||
result[1].data[1] -= rhs;
|
||||
++result[0];
|
||||
result.remainder.data[1] -= rhs;
|
||||
++result.quotient;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -1220,12 +1225,12 @@ private struct uint128
|
||||
|
||||
uint128 opBinary(string op : "/")(ulong rhs)
|
||||
{
|
||||
return divMod(rhs)[0];
|
||||
return divMod(rhs).quotient;
|
||||
}
|
||||
|
||||
uint128 opBinary(string op : "%")(ulong rhs) const
|
||||
{
|
||||
return divMod(rhs)[1];
|
||||
return divMod(rhs).remainder;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1302,12 +1307,12 @@ do
|
||||
enum ulong power19 = cast(ulong) 1e19;
|
||||
|
||||
auto qr = leftBoundary.divMod(power19);
|
||||
auto low = cast(ulong) qr[1];
|
||||
const lowFactor = cast(ulong) (qr[0] % power19);
|
||||
auto low = cast(ulong) qr.remainder;
|
||||
const lowFactor = cast(ulong) (qr.quotient % power19);
|
||||
|
||||
qr = rightBoundary.divMod(power19);
|
||||
auto high = cast(ulong) qr[1];
|
||||
const highFactor = cast(ulong) (qr[0] % power19);
|
||||
auto high = cast(ulong) qr.remainder;
|
||||
const highFactor = cast(ulong) (qr.quotient % power19);
|
||||
size_t digitIndex;
|
||||
|
||||
if (lowFactor != highFactor)
|
||||
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
module tanya.hash.lookup;
|
||||
|
||||
import std.traits : isScalarType;
|
||||
import tanya.meta.trait;
|
||||
import tanya.range.primitive;
|
||||
|
||||
|
@ -1,111 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Number theory.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017-2020.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/nbtheory.d,
|
||||
* tanya/math/nbtheory.d)
|
||||
*/
|
||||
module tanya.math.nbtheory;
|
||||
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
|
||||
import core.math : fabs;
|
||||
import std.math : log;
|
||||
|
||||
/**
|
||||
* Calculates the absolute value of a number.
|
||||
*
|
||||
* Params:
|
||||
* T = Argument type.
|
||||
* x = Argument.
|
||||
*
|
||||
* Returns: Absolute value of $(D_PARAM x).
|
||||
*/
|
||||
Unqual!T abs(T)(T x)
|
||||
if (isIntegral!T)
|
||||
{
|
||||
static if (isSigned!T)
|
||||
{
|
||||
return x >= 0 ? x : -x;
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
int i = -1;
|
||||
assert(i.abs == 1);
|
||||
static assert(is(typeof(i.abs) == int));
|
||||
|
||||
uint u = 1;
|
||||
assert(u.abs == 1);
|
||||
static assert(is(typeof(u.abs) == uint));
|
||||
}
|
||||
|
||||
/// ditto
|
||||
Unqual!T abs(T)(T x)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
return fabs(x);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
float f = -1.64;
|
||||
assert(f.abs == 1.64F);
|
||||
static assert(is(typeof(f.abs) == float));
|
||||
|
||||
double d = -1.64;
|
||||
assert(d.abs == 1.64);
|
||||
static assert(is(typeof(d.abs) == double));
|
||||
|
||||
real r = -1.64;
|
||||
assert(r.abs == 1.64L);
|
||||
static assert(is(typeof(r.abs) == real));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates natural logarithm of $(D_PARAM x).
|
||||
*
|
||||
* Params:
|
||||
* T = Argument type.
|
||||
* x = Argument.
|
||||
*
|
||||
* Returns: Natural logarithm of $(D_PARAM x).
|
||||
*/
|
||||
Unqual!T ln(T)(T x)
|
||||
if (isFloatingPoint!T)
|
||||
{
|
||||
return log(x);
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
import tanya.math;
|
||||
|
||||
assert(isNaN(ln(-7.389f)));
|
||||
assert(isNaN(ln(-7.389)));
|
||||
assert(isNaN(ln(-7.389L)));
|
||||
|
||||
assert(isInfinity(ln(0.0f)));
|
||||
assert(isInfinity(ln(0.0)));
|
||||
assert(isInfinity(ln(0.0L)));
|
||||
|
||||
assert(ln(1.0f) == 0.0f);
|
||||
assert(ln(1.0) == 0.0);
|
||||
assert(ln(1.0L) == 0.0L);
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
|
||||
* submodules publically, they should be imported explicitly.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2020.
|
||||
* Copyright: Eugene Wissner 2016-2022.
|
||||
* 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)
|
||||
@ -21,7 +21,7 @@
|
||||
*/
|
||||
module tanya.math;
|
||||
|
||||
import tanya.math.nbtheory;
|
||||
import std.math;
|
||||
import tanya.meta.trait;
|
||||
import tanya.meta.transform;
|
||||
|
||||
@ -543,73 +543,3 @@ if (isFloatingPoint!F)
|
||||
assert(signBit(-1.0L));
|
||||
assert(!signBit(1.0L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes $(D_PARAM x) to the power $(D_PARAM y) modulo $(D_PARAM z).
|
||||
*
|
||||
* Params:
|
||||
* I = Base type.
|
||||
* G = Exponent type.
|
||||
* H = Divisor type:
|
||||
* x = Base.
|
||||
* y = Exponent.
|
||||
* z = Divisor.
|
||||
*
|
||||
* Returns: Reminder of the division of $(D_PARAM x) to the power $(D_PARAM y)
|
||||
* by $(D_PARAM z).
|
||||
*
|
||||
* Precondition: $(D_INLINECODE z > 0)
|
||||
*/
|
||||
H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z)
|
||||
if (isIntegral!I && isIntegral!G && isIntegral!H)
|
||||
in
|
||||
{
|
||||
assert(z > 0, "Division by zero");
|
||||
}
|
||||
do
|
||||
{
|
||||
G mask = G.max / 2 + 1;
|
||||
H result;
|
||||
|
||||
if (y == 0)
|
||||
{
|
||||
return 1 % z;
|
||||
}
|
||||
else if (y == 1)
|
||||
{
|
||||
return x % z;
|
||||
}
|
||||
do
|
||||
{
|
||||
immutable bit = y & mask;
|
||||
if (!result && bit)
|
||||
{
|
||||
result = x;
|
||||
continue;
|
||||
}
|
||||
|
||||
result *= result;
|
||||
if (bit)
|
||||
{
|
||||
result *= x;
|
||||
}
|
||||
result %= z;
|
||||
}
|
||||
while (mask >>= 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(pow(3, 5, 7) == 5);
|
||||
assert(pow(2, 2, 1) == 0);
|
||||
assert(pow(3, 3, 3) == 0);
|
||||
assert(pow(7, 4, 2) == 1);
|
||||
assert(pow(53, 0, 2) == 1);
|
||||
assert(pow(53, 1, 3) == 2);
|
||||
assert(pow(53, 2, 5) == 4);
|
||||
assert(pow(0, 0, 5) == 1);
|
||||
assert(pow(0, 5, 5) == 0);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Random number generator.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2020.
|
||||
* Copyright: Eugene Wissner 2016-2022.
|
||||
* 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)
|
||||
@ -16,7 +16,6 @@ module tanya.math.random;
|
||||
|
||||
import std.typecons;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.typecons;
|
||||
|
||||
/// Maximum amount gathered from the entropy sources.
|
||||
enum maxGather = 128;
|
||||
|
@ -22,8 +22,22 @@ import tanya.range;
|
||||
|
||||
version (Windows)
|
||||
{
|
||||
import tanya.sys.windows.ifdef;
|
||||
import tanya.sys.windows.iphlpapi;
|
||||
private union NET_LUID_LH { ulong Value, Info; }
|
||||
private alias NET_LUID = NET_LUID_LH;
|
||||
private alias NET_IFINDEX = uint;
|
||||
private enum IF_MAX_STRING_SIZE = 256;
|
||||
extern(Windows) @nogc nothrow private @system
|
||||
{
|
||||
uint ConvertInterfaceNameToLuidA(const(char)* InterfaceName,
|
||||
NET_LUID* InterfaceLuid);
|
||||
uint ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid,
|
||||
NET_IFINDEX* InterfaceIndex);
|
||||
uint ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex,
|
||||
NET_LUID* InterfaceLuid);
|
||||
uint ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid,
|
||||
char* InterfaceName,
|
||||
size_t Length);
|
||||
}
|
||||
}
|
||||
else version (Posix)
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ module tanya.net.ip;
|
||||
|
||||
import std.algorithm.comparison;
|
||||
import std.ascii;
|
||||
import std.sumtype;
|
||||
import std.typecons;
|
||||
import tanya.algorithm.iteration;
|
||||
import tanya.algorithm.mutation;
|
||||
@ -28,7 +29,6 @@ import tanya.meta.transform;
|
||||
import tanya.net.iface;
|
||||
import tanya.net.inet;
|
||||
import tanya.range;
|
||||
import tanya.typecons;
|
||||
|
||||
/**
|
||||
* IPv4 internet address.
|
||||
@ -1061,7 +1061,7 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte))
|
||||
*/
|
||||
struct Address
|
||||
{
|
||||
private Variant!(Address4, Address6) address;
|
||||
private SumType!(Address4, Address6) address;
|
||||
|
||||
@disable this();
|
||||
|
||||
@ -1095,7 +1095,10 @@ struct Address
|
||||
*/
|
||||
bool isV4() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.address.peek!Address4;
|
||||
return this.address.match!(
|
||||
(Address4 address4) => true,
|
||||
(Address6 address6) => false
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1112,7 +1115,10 @@ struct Address
|
||||
*/
|
||||
bool isV6() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.address.peek!Address6;
|
||||
return this.address.match!(
|
||||
(Address4 address4) => false,
|
||||
(Address6 address6) => true
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1131,14 +1137,12 @@ struct Address
|
||||
*
|
||||
* Precondition: This is an IPv4 address.
|
||||
*/
|
||||
ref inout(Address4) toV4() inout @nogc nothrow pure @safe
|
||||
in
|
||||
Address4 toV4() inout @nogc nothrow pure @safe
|
||||
{
|
||||
assert(this.address.peek!Address4);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.address.get!Address4;
|
||||
return this.address.match!(
|
||||
(Address4 address4) => address4,
|
||||
_ => assert(false, "Not an IPv4 address")
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1158,14 +1162,12 @@ struct Address
|
||||
*
|
||||
* Precondition: This is an IPv6 address.
|
||||
*/
|
||||
ref inout(Address6) toV6() inout @nogc nothrow pure @safe
|
||||
in
|
||||
Address6 toV6() inout @nogc nothrow pure @safe
|
||||
{
|
||||
assert(this.address.peek!Address6);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.address.get!Address6;
|
||||
return this.address.match!(
|
||||
(Address6 address6) => address6,
|
||||
_ => assert(false, "Not an IPv6 address")
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1185,17 +1187,11 @@ struct Address
|
||||
* $(D_PSYMBOL Address6.loopback).
|
||||
*/
|
||||
bool isLoopback() const @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(this.address.hasValue);
|
||||
}
|
||||
do
|
||||
{
|
||||
if (this.address.peek!Address4)
|
||||
{
|
||||
return this.address.get!Address4.isLoopback();
|
||||
}
|
||||
return this.address.get!Address6.isLoopback();
|
||||
return this.address.match!(
|
||||
(Address4 address) => address.isLoopback(),
|
||||
(Address6 address) => address.isLoopback()
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1215,17 +1211,11 @@ struct Address
|
||||
* $(D_PSYMBOL Address6.isMulticast).
|
||||
*/
|
||||
bool isMulticast() const @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(this.address.hasValue);
|
||||
}
|
||||
do
|
||||
{
|
||||
if (this.address.peek!Address4)
|
||||
{
|
||||
return this.address.get!Address4.isMulticast();
|
||||
}
|
||||
return this.address.get!Address6.isMulticast();
|
||||
return this.address.match!(
|
||||
(Address4 address) => address.isMulticast(),
|
||||
(Address6 address) => address.isMulticast()
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1244,17 +1234,11 @@ struct Address
|
||||
* See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny).
|
||||
*/
|
||||
bool isAny() const @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(this.address.hasValue);
|
||||
}
|
||||
do
|
||||
{
|
||||
if (this.address.peek!Address4)
|
||||
{
|
||||
return this.address.get!Address4.isAny();
|
||||
}
|
||||
return this.address.get!Address6.isAny();
|
||||
return this.address.match!(
|
||||
(Address4 address) => address.isAny(),
|
||||
(Address6 address) => address.isAny()
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
@ -1277,14 +1261,22 @@ struct Address
|
||||
* otherwise.
|
||||
*/
|
||||
bool opEquals(T)(T that) const
|
||||
if (is(Unqual!T == Address4) || is(Unqual!T == Address6))
|
||||
if (is(Unqual!T == Address4))
|
||||
{
|
||||
alias AddressType = Unqual!T;
|
||||
if (this.address.peek!AddressType)
|
||||
{
|
||||
return this.address.get!AddressType == that;
|
||||
}
|
||||
return false;
|
||||
return this.address.match!(
|
||||
(Address4 address) => address == that,
|
||||
(Address6 address) => false
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
bool opEquals(T)(T that) const
|
||||
if (is(Unqual!T == Address6))
|
||||
{
|
||||
return this.address.match!(
|
||||
(Address4 address) => false,
|
||||
(Address6 address) => address == that,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1,427 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Type constructors.
|
||||
*
|
||||
* This module contains templates that allow to build new types from the
|
||||
* available ones.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2017-2020.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/typecons.d,
|
||||
* tanya/typecons.d)
|
||||
*/
|
||||
module tanya.typecons;
|
||||
|
||||
import tanya.format;
|
||||
import tanya.memory.lifetime;
|
||||
import tanya.meta.metafunction;
|
||||
import tanya.meta.trait;
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
|
||||
*
|
||||
* The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
|
||||
* names (e.g. `obj.first`).
|
||||
*
|
||||
* $(D_PARAM Specs) contains a list of object types and names. First
|
||||
* comes the object type, then an optional string containing the name.
|
||||
* If you want the object be accessible only by its index (`0` or `1`),
|
||||
* just skip the name.
|
||||
*
|
||||
* Params:
|
||||
* Specs = Field types and names.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL tuple).
|
||||
*/
|
||||
template Tuple(Specs...)
|
||||
{
|
||||
template parseSpecs(size_t fieldCount, Specs...)
|
||||
{
|
||||
static if (Specs.length == 0)
|
||||
{
|
||||
alias parseSpecs = AliasSeq!();
|
||||
}
|
||||
else static if (is(Specs[0]) && fieldCount < 2)
|
||||
{
|
||||
static if (is(typeof(Specs[1]) == string))
|
||||
{
|
||||
alias parseSpecs
|
||||
= AliasSeq!(Pack!(Specs[0], Specs[1]),
|
||||
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
|
||||
}
|
||||
else
|
||||
{
|
||||
alias parseSpecs
|
||||
= AliasSeq!(Pack!(Specs[0]),
|
||||
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "Invalid argument: " ~ Specs[0].stringof);
|
||||
}
|
||||
}
|
||||
|
||||
alias ChooseType(alias T) = T.Seq[0];
|
||||
alias ParsedSpecs = parseSpecs!(0, Specs);
|
||||
|
||||
static assert(ParsedSpecs.length > 1, "Invalid argument count");
|
||||
|
||||
private string formatAliases(size_t n, Specs...)()
|
||||
{
|
||||
static if (Specs.length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
string fieldAlias;
|
||||
static if (Specs[0].length == 2)
|
||||
{
|
||||
char[21] buffer;
|
||||
fieldAlias = "alias " ~ Specs[0][1] ~ " = expand["
|
||||
~ integral2String(n, buffer).idup ~ "];";
|
||||
}
|
||||
return fieldAlias ~ formatAliases!(n + 1, Specs[1 .. $])();
|
||||
}
|
||||
}
|
||||
|
||||
struct Tuple
|
||||
{
|
||||
/// Field types.
|
||||
alias Types = Map!(ChooseType, ParsedSpecs);
|
||||
|
||||
// Create field aliases.
|
||||
mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
|
||||
|
||||
/// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
|
||||
Types expand;
|
||||
|
||||
alias expand this;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto pair = Tuple!(int, "first", string, "second")(1, "second");
|
||||
assert(pair.first == 1);
|
||||
assert(pair[0] == 1);
|
||||
assert(pair.second == "second");
|
||||
assert(pair[1] == "second");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new $(D_PSYMBOL Tuple).
|
||||
*
|
||||
* Params:
|
||||
* Names = Field names.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL Tuple).
|
||||
*/
|
||||
template tuple(Names...)
|
||||
{
|
||||
/**
|
||||
* Creates a new $(D_PSYMBOL Tuple).
|
||||
*
|
||||
* Params:
|
||||
* Args = Field types.
|
||||
* args = Field values.
|
||||
*
|
||||
* Returns: Newly created $(D_PSYMBOL Tuple).
|
||||
*/
|
||||
auto tuple(Args...)(auto ref Args args)
|
||||
if (Args.length >= Names.length && isTypeTuple!Args)
|
||||
{
|
||||
alias Zipped = ZipWith!(AliasSeq, Pack!Args, Pack!Names);
|
||||
alias Nameless = Args[Names.length .. $];
|
||||
|
||||
return Tuple!(Zipped, Nameless)(forward!args);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto t = tuple!("one", "two")(20, 5);
|
||||
assert(t.one == 20);
|
||||
assert(t.two == 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type that can hold one of the types listed as its template parameters.
|
||||
*
|
||||
* $(D_PSYMBOL Variant) is a type similar to $(D_KEYWORD union), but
|
||||
* $(D_PSYMBOL Variant) keeps track of the actually used type and throws an
|
||||
* assertion error when trying to access an invalid type at runtime.
|
||||
*
|
||||
* Params:
|
||||
* Specs = Types this $(D_SPYBMOL Variant) can hold.
|
||||
*/
|
||||
template Variant(Specs...)
|
||||
if (isTypeTuple!Specs && NoDuplicates!Specs.length == Specs.length)
|
||||
{
|
||||
union AlignedUnion(Args...)
|
||||
{
|
||||
static if (Args.length > 0)
|
||||
{
|
||||
Args[0] value;
|
||||
}
|
||||
static if (Args.length > 1)
|
||||
{
|
||||
AlignedUnion!(Args[1 .. $]) rest;
|
||||
}
|
||||
}
|
||||
|
||||
private struct VariantAccessorInfo
|
||||
{
|
||||
string accessor;
|
||||
ptrdiff_t tag;
|
||||
}
|
||||
|
||||
template accessor(T, Union)
|
||||
{
|
||||
enum VariantAccessorInfo info = accessorImpl!(T, Union, 1);
|
||||
enum accessor = VariantAccessorInfo("this.values" ~ info.accessor, info.tag);
|
||||
}
|
||||
|
||||
template accessorImpl(T, Union, size_t tag)
|
||||
{
|
||||
static if (is(T == typeof(Union.value)))
|
||||
{
|
||||
enum accessorImpl = VariantAccessorInfo(".value", tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum VariantAccessorInfo info = accessorImpl!(T, typeof(Union.rest), tag + 1);
|
||||
enum accessorImpl = VariantAccessorInfo(".rest" ~ info.accessor, info.tag);
|
||||
}
|
||||
}
|
||||
|
||||
struct Variant
|
||||
{
|
||||
/// Types can be present in this $(D_PSYMBOL Variant).
|
||||
alias Types = Specs;
|
||||
|
||||
private ptrdiff_t tag = -1;
|
||||
private AlignedUnion!Types values;
|
||||
|
||||
/**
|
||||
* Constructs this $(D_PSYMBOL Variant) with one of the types supported
|
||||
* in it.
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the initial value.
|
||||
* value = Initial value.
|
||||
*/
|
||||
this(T)(ref T value)
|
||||
if (canFind!(T, Types))
|
||||
{
|
||||
copyAssign!T(value);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
this(T)(T value)
|
||||
if (canFind!(T, Types))
|
||||
{
|
||||
moveAssign!T(value);
|
||||
}
|
||||
|
||||
~this()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
this(this)
|
||||
{
|
||||
alias pred(U) = hasElaborateCopyConstructor!(U.Seq[1]);
|
||||
static foreach (Type; Filter!(pred, Enumerate!Types))
|
||||
{
|
||||
if (this.tag == Type.Seq[0])
|
||||
{
|
||||
get!(Type.Seq[1]).__postblit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this $(D_PSYMBOL Variant) is initialized.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) contains a
|
||||
* value, $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool hasValue() const
|
||||
{
|
||||
return this.tag != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this $(D_PSYMBOL Variant) holds currently a value of
|
||||
* type $(D_PARAM T).
|
||||
*
|
||||
* Params:
|
||||
* T = Examined type.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) currently
|
||||
* contains a value of type $(D_PARAM T), $(D_KEYWORD false)
|
||||
* otherwise.
|
||||
*/
|
||||
bool peek(T)() const
|
||||
if (canFind!(T, Types))
|
||||
{
|
||||
return this.tag == staticIndexOf!(T, Types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying value, assuming it is of the type $(D_PARAM T).
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the value should be returned.
|
||||
*
|
||||
* Returns: The underyling value.
|
||||
*
|
||||
* Precondition: The $(D_PSYMBOL Variant) has a value.
|
||||
*
|
||||
* See_Also: $(D_PSYMBOL peek), $(D_PSYMBOL hasValue).
|
||||
*/
|
||||
ref inout(T) get(T)() inout
|
||||
if (canFind!(T, Types))
|
||||
in
|
||||
{
|
||||
assert(this.tag == staticIndexOf!(T, Types), "Variant isn't initialized");
|
||||
}
|
||||
do
|
||||
{
|
||||
mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassigns the value.
|
||||
*
|
||||
* Params:
|
||||
* T = Type of the new value
|
||||
* that = New value.
|
||||
*
|
||||
* Returns: $(D_KEYWORD this).
|
||||
*/
|
||||
ref typeof(this) opAssign(T)(T that)
|
||||
if (canFind!(T, Types))
|
||||
{
|
||||
reset();
|
||||
return moveAssign!T(that);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
ref typeof(this) opAssign(T)(ref T that)
|
||||
if (canFind!(T, Types))
|
||||
{
|
||||
reset();
|
||||
return copyAssign!T(that);
|
||||
}
|
||||
|
||||
private ref typeof(this) moveAssign(T)(ref T that) @trusted
|
||||
{
|
||||
this.tag = staticIndexOf!(T, Types);
|
||||
|
||||
enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor;
|
||||
moveEmplace(that, mixin(accessorMixin));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private ref typeof(this) copyAssign(T)(ref T that) return
|
||||
{
|
||||
this.tag = staticIndexOf!(T, Types);
|
||||
|
||||
enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor;
|
||||
emplace!T((() @trusted => (&mixin(accessorMixin))[0 .. 1])(), that);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
alias pred(U) = hasElaborateDestructor!(U.Seq[1]);
|
||||
static foreach (Type; Filter!(pred, Enumerate!Types))
|
||||
{
|
||||
if (this.tag == Type.Seq[0])
|
||||
{
|
||||
destroy(get!(Type.Seq[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $(D_PSYMBOL TypeInfo) corresponding to the current type.
|
||||
*
|
||||
* If this $(D_PSYMBOL Variant) isn't initialized, returns
|
||||
* $(D_KEYWORD null).
|
||||
*
|
||||
* Returns: $(D_PSYMBOL TypeInfo) of the current type.
|
||||
*/
|
||||
@property TypeInfo type()
|
||||
{
|
||||
static foreach (i, Type; Types)
|
||||
{
|
||||
if (this.tag == i)
|
||||
{
|
||||
return typeid(Type);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this $(D_PSYMBOL Variant) with another one with the same
|
||||
* specification for equality.
|
||||
*
|
||||
* $(UL
|
||||
* $(LI If both hold values of the same type, these values are
|
||||
* compared.)
|
||||
* $(LI If they hold values of different types, then the
|
||||
* $(D_PSYMBOL Variant)s aren't equal.)
|
||||
* $(LI If only one of them is initialized but another one not, they
|
||||
* aren't equal.)
|
||||
* $(LI If neither of them is initialized, they are equal.)
|
||||
* )
|
||||
*
|
||||
* Params:
|
||||
* that = The $(D_PSYMBOL Variant) to compare with.
|
||||
*
|
||||
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) is equal to
|
||||
* $(D_PARAM that), $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opEquals()(auto ref inout(Variant) that) inout
|
||||
{
|
||||
if (this.tag != that.tag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static foreach (i, Type; Types)
|
||||
{
|
||||
if (this.tag == i)
|
||||
{
|
||||
return get!Type == that.get!Type;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant = 5;
|
||||
assert(variant.peek!int);
|
||||
assert(variant.get!int == 5);
|
||||
|
||||
variant = 5.4;
|
||||
assert(!variant.peek!int);
|
||||
assert(variant.get!double == 5.4);
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
module tanya.tests.bitmanip;
|
||||
|
||||
import tanya.bitmanip;
|
||||
|
||||
// Casts to a boolean
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert(BitFlags!One(One.one));
|
||||
assert(!BitFlags!One());
|
||||
}
|
||||
|
||||
// Assigns to and compares with a single value
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
BitFlags!One bitFlags;
|
||||
bitFlags = One.one;
|
||||
assert(bitFlags == One.one);
|
||||
}
|
||||
|
||||
// Assigns to and compares with the same type
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
auto bitFlags1 = BitFlags!One(One.one);
|
||||
BitFlags!One bitFlags2;
|
||||
bitFlags2 = bitFlags1;
|
||||
assert(bitFlags1 == bitFlags2);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
assert((BitFlags!One() | One.one) == BitFlags!One(One.one));
|
||||
assert((BitFlags!One() | BitFlags!One(One.one)) == BitFlags!One(One.one));
|
||||
|
||||
assert(!(BitFlags!One() & BitFlags!One(One.one)));
|
||||
|
||||
assert(!(BitFlags!One(One.one) ^ One.one));
|
||||
assert(BitFlags!One() ^ BitFlags!One(One.one));
|
||||
|
||||
assert(~BitFlags!One());
|
||||
|
||||
assert(BitFlags!One().toHash() == 0);
|
||||
assert(BitFlags!One(One.one).toHash() != 0);
|
||||
}
|
||||
|
||||
// opBinaryRight is allowed
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(is(typeof({ One.one | BitFlags!One(); })));
|
||||
}
|
||||
|
||||
private enum One : int
|
||||
{
|
||||
one = 1,
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
module tanya.tests.typecons;
|
||||
|
||||
import tanya.test.stub;
|
||||
import tanya.typecons;
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(is(Tuple!(int, int)));
|
||||
static assert(!is(Tuple!(int, 5)));
|
||||
|
||||
static assert(is(Tuple!(int, "first", int)));
|
||||
static assert(is(Tuple!(int, "first", int, "second")));
|
||||
static assert(is(Tuple!(int, "first", int)));
|
||||
|
||||
static assert(is(Tuple!(int, int, "second")));
|
||||
static assert(!is(Tuple!("first", int, "second", int)));
|
||||
static assert(!is(Tuple!(int, int, int)));
|
||||
|
||||
static assert(!is(Tuple!(int, "first")));
|
||||
|
||||
static assert(!is(Tuple!(int, double, char)));
|
||||
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant;
|
||||
variant = 5;
|
||||
assert(variant.peek!int);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant;
|
||||
variant = 5.0;
|
||||
assert(!variant.peek!int);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant = 5;
|
||||
assert(variant.get!int == 5);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(is(Variant!(int, float)));
|
||||
static assert(is(Variant!int));
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct WithDestructorAndCopy
|
||||
{
|
||||
this(this) @nogc nothrow pure @safe
|
||||
{
|
||||
}
|
||||
|
||||
~this() @nogc nothrow pure @safe
|
||||
{
|
||||
}
|
||||
}
|
||||
static assert(is(Variant!WithDestructorAndCopy));
|
||||
}
|
||||
|
||||
// Equality compares the underlying objects
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant1 = 5;
|
||||
Variant!(int, double) variant2 = 5;
|
||||
assert(variant1 == variant2);
|
||||
}
|
||||
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant1 = 5;
|
||||
Variant!(int, double) variant2 = 6;
|
||||
assert(variant1 != variant2);
|
||||
}
|
||||
|
||||
// Differently typed variants aren't equal
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant1 = 5;
|
||||
Variant!(int, double) variant2 = 5.0;
|
||||
assert(variant1 != variant2);
|
||||
}
|
||||
|
||||
// Uninitialized variants are equal
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, double) variant1, variant2;
|
||||
assert(variant1 == variant2);
|
||||
}
|
||||
|
||||
// Calls postblit constructor of the active type
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static struct S
|
||||
{
|
||||
bool called;
|
||||
|
||||
this(this)
|
||||
{
|
||||
this.called = true;
|
||||
}
|
||||
}
|
||||
Variant!(int, S) variant1 = S();
|
||||
auto variant2 = variant1;
|
||||
assert(variant2.get!S.called);
|
||||
}
|
||||
|
||||
// Variant.type is null if the Variant doesn't have a value
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
Variant!(int, uint) variant;
|
||||
assert(variant.type is null);
|
||||
}
|
||||
|
||||
// Variant can contain only distinct types
|
||||
@nogc nothrow pure @safe unittest
|
||||
{
|
||||
static assert(!is(Variant!(int, int)));
|
||||
}
|
Reference in New Issue
Block a user