12 Commits

Author SHA1 Message Date
6072bfab68 Marke SingletonByRef functions scope 2025-05-03 18:52:24 +02:00
4acf163b42 Combine dependencies and dependencies-linux
Platform dependencies aren't supported according to the dub
documentation, that states: "this setting does not support platform
suffixes".

dub test with dub 1.34.0 produces a warning:

"dependencies-linux: Key is not a valid member of this section.
Did you mean: dependencies"

Then the build fails because it cannot find modules defined in the
dependency.
2023-09-25 18:35:33 +02:00
e1fd528607 Merge remote-tracking branch 'n8sh/DMD_2.105' 2023-09-23 17:22:27 +02:00
1c57368f43 Merge remote-tracking branch 'n8sh/windows-iface-mmappool' 2023-09-23 16:16:33 +02:00
1c5e18b92e Add Windows support to tanya.memory.mmappool 2023-09-22 16:08:24 -04:00
07b388eecb Update tanya.net.iface to work on Windows
Needed because 0fcc83d00e removed
tanya.sys.windows.ifdef and tanya.sys.windows.iphlpapi.
2023-09-22 16:07:59 -04:00
20ae6465d6 Update to compile with DMD 2.105
https://dlang.org/changelog/2.105.0.html#dmd.enum-function

> enum on a function declaration had no effect other than being
> equivalent to the auto storage class when no return type was present.
> That syntax could be confused with enum manifest constants and is now
> an error... Instead, remove enum and use auto where necessary
2023-09-22 15:43:36 -04:00
728eaf88fb Remove traits depending on deprecated complex
All checks were successful
test Test.
2023-03-25 15:55:57 +01:00
90797a48be Replace tuples with custom types 2022-06-07 08:40:18 +02:00
5453c646f6 Replace Variant with SumType 2022-06-06 09:46:45 +02:00
7dd4c44140 Remove math function wrappers 2022-06-01 12:55:03 +02:00
81b4fb88f5 Remove bitmanip available in Phobos 2022-05-31 09:48:37 +02:00
19 changed files with 153 additions and 1419 deletions

View File

@ -1,6 +1,5 @@
# Tanya
[![CI/CD](https://img.shields.io/badge/CI-CD-brightgreen)](https://build.caraus.tech/go/pipelines)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
[![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya)
[![License: MPL 2.0](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](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

View File

@ -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"
},

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@
*/
module tanya.container.buffer;
import std.traits : isScalarType;
import tanya.memory.allocator;
import tanya.meta.trait;

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@
*/
module tanya.hash.lookup;
import std.traits : isScalarType;
import tanya.meta.trait;
import tanya.range.primitive;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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