15 Commits

Author SHA1 Message Date
531cae51a3 Stringish to integral type conversion. Fix #44 2018-07-18 06:33:45 +02:00
1b203507f6 conv: Fix overflow hanndling in readIntegral 2018-07-17 16:03:05 +02:00
99e06e0d04 format: Support text in the format string
Fix  #25.
2018-07-14 19:09:21 +02:00
158a47d54a Update dmd to 2.081.1. Remove 2.077.1 2018-07-13 05:39:58 +02:00
5865e355cd Fix EV_SET 0 length on 32-bit BSD 2018-07-08 12:54:47 +02:00
a94b1b0af4 Add functional module. Fix #52 2018-07-07 12:17:59 +02:00
3df4eb6259 Don't check UTF-8 correctness when inserting chars
- Fix bug when inserting char ranges that are not arrays
- Optimize insertion from the String own range
- Assume char and char ranges are correclty encoded (as it actually
should be) and don't throw an exception. This should make the most
common use cases nothrow (Fix #19). Dchars and Wchars are still encoded
because they should be converted to UTF-8 before inserting anyway.
2018-07-06 05:36:13 +02:00
a332d727af Implement errol2 for floating point formatting
Handles whole floating point numbers in the range between 2^54 and 2^131.
2018-07-02 10:47:05 +02:00
8241943a58 Add uint128 tailored for errol2 computations 2018-07-02 10:26:45 +02:00
d54e06f43c Iterate hash table by key or by value 2018-06-29 20:43:05 +02:00
5e901f505c Make HashTable work complex types as key
- Add toHash() function for String
- The key type shouldn't match exact for a lookup.
The key type and lookup key type should be comparable.
- Move elements when inserting if passed by value.
2018-06-28 12:14:45 +02:00
533fa3b023 container.HashTable: Fix infinite rehashing when inserting
Fix #53.
2018-06-27 05:45:53 +02:00
adf2d8b689 Add Option to typecons
Fix #47.
2018-06-26 04:25:32 +02:00
74ece7ddf4 Replace floating pointer formatter with errol1 2018-06-23 16:21:19 +02:00
411e45ec5c Remove functions deprecated in 0.9.0 2018-06-22 07:11:56 +02:00
19 changed files with 2402 additions and 841 deletions

View File

@ -7,10 +7,10 @@ os:
language: d
d:
- dmd-2.081.0
- dmd-2.080.1
- dmd-2.079.1
- dmd-2.078.3
- dmd-2.077.1
env:
matrix:
@ -23,7 +23,7 @@ addons:
- gcc-multilib
before_script:
- if [ "`$DC --version | head -n 1 | grep 'v2.080.1'`" ]; then
- if [ "`$DC --version | head -n 1 | grep 'v2.081.0'`" ]; then
export UNITTEST="unittest-cov";
fi

View File

@ -32,6 +32,8 @@ types.
* `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions.
* `functional`: Functions that manipulate other functions and their argument
lists.
* `hash`: Hash algorithms.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
@ -172,10 +174,10 @@ parameter is used)
| DMD | GCC |
|:-------:|:---------:|
| 2.080.1 | *master* |
| 2.081.1 | *master* |
| 2.080.1 | |
| 2.079.1 | |
| 2.078.3 | |
| 2.077.1 | |
### Release management

View File

@ -3,6 +3,12 @@ os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.081.1
arch: x64
- DC: dmd
DVersion: 2.081.1
arch: x86
- DC: dmd
DVersion: 2.080.1
arch: x64
@ -21,12 +27,6 @@ environment:
- DC: dmd
DVersion: 2.078.3
arch: x86
- DC: dmd
DVersion: 2.077.1
arch: x64
- DC: dmd
DVersion: 2.077.1
arch: x86
skip_tags: true

View File

@ -84,12 +84,12 @@ enum : short
struct kevent_t
{
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
uintptr_t ident; // Identifier for this event
short filter; // Filter for event
ushort flags;
uint fflags;
intptr_t data;
void *udata; /* opaque user data identifier */
void* udata; // Opaque user data identifier
}
enum
@ -168,7 +168,7 @@ final class KqueueLoop : SelectorLoop
filter,
flags,
0U,
0L,
0,
null);
++changeCount;
}

View File

@ -82,12 +82,18 @@ package struct Bucket(K, V = void)
}
}
bool opEquals(ref inout(K) key) inout
void moveKey(ref K key)
{
move(key, this.key());
this.status = BucketStatus.used;
}
bool opEquals(T)(ref const T key) const
{
return this.status == BucketStatus.used && this.key == key;
}
bool opEquals(ref inout(typeof(this)) that) inout
bool opEquals(ref const(typeof(this)) that) const
{
return key == that.key && this.status == that.status;
}
@ -171,24 +177,29 @@ package struct HashArray(alias hasher, K, V = void)
this.length = that.length;
}
@property size_t bucketCount() const
{
return primes[this.lengthIndex];
}
/*
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
size_t locateBucket(ref const Key key) const
size_t locateBucket(T)(ref const T key) const
{
return this.array.length == 0
? 0
: hasher(key) % primes[this.lengthIndex];
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
}
/*
* Inserts a key into an empty or deleted bucket. If the key is
* already in there, does nothing. Returns the bucket with the key.
* If the key doesn't already exists, returns an empty bucket the key can
* be inserted in and adjusts the element count. Otherwise returns the
* bucket containing the key.
*/
ref Bucket insert(ref Key key)
{
while ((this.lengthIndex + 1) != primes.length)
const newLengthIndex = this.lengthIndex + 1;
if (newLengthIndex != primes.length)
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
@ -203,17 +214,29 @@ package struct HashArray(alias hasher, K, V = void)
}
}
if (this.rehashToSize(this.lengthIndex + 1))
this.rehashToSize(newLengthIndex);
}
foreach (ref e; this.array[locateBucket(key) .. $])
{
++this.lengthIndex;
if (e == key)
{
return e;
}
else if (e.status != BucketStatus.used)
{
++this.length;
return e;
}
}
this.array.insertBack(Bucket(key));
this.array.length = this.array.length + 1;
++this.length;
return this.array[$ - 1];
}
// Takes an index in the primes array.
bool rehashToSize(const size_t n)
void rehashToSize(const size_t n)
in
{
assert(n < primes.length);
@ -231,15 +254,15 @@ package struct HashArray(alias hasher, K, V = void)
{
if (e2.status != BucketStatus.used) // Insert the key
{
e2 = e1;
.move(e1, e2);
continue DataLoop;
}
}
return false; // Rehashing failed.
storage.insertBack(.move(e1));
}
}
.move(storage, this.array);
return true;
this.lengthIndex = n;
}
void rehash(const size_t n)
@ -252,9 +275,9 @@ package struct HashArray(alias hasher, K, V = void)
break;
}
}
if (this.rehashToSize(lengthIndex))
if (lengthIndex > this.lengthIndex)
{
this.lengthIndex = lengthIndex;
this.rehashToSize(lengthIndex);
}
}
@ -287,7 +310,7 @@ package struct HashArray(alias hasher, K, V = void)
return 0;
}
bool opBinaryRight(string op : "in")(ref inout(Key) key) inout
bool opBinaryRight(string op : "in", T)(ref const T key) const
{
foreach (ref e; this.array[locateBucket(key) .. $])
{

View File

@ -14,6 +14,7 @@
*/
module tanya.container.hashtable;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.container.entry;
import tanya.hash.lookup;
@ -138,6 +139,236 @@ struct Range(T)
}
}
/**
* Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
*
* Params:
* T = Type of the internal hash storage.
*/
struct ByKey(T)
{
private alias Key = CopyConstness!(T, T.Key);
static if (isMutable!T)
{
private alias DataRange = T.array.Range;
}
else
{
private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@disable this();
private this(DataRange dataRange)
{
while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
{
dataRange.popFront();
}
while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
{
dataRange.popBack();
}
this.dataRange = dataRange;
}
@property ByKey save()
{
return this;
}
@property bool empty() const
{
return this.dataRange.empty();
}
@property void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popFront();
}
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popBack();
}
while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(Key) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{
return this.dataRange.front.key;
}
@property ref inout(Key) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{
return this.dataRange.back.key;
}
ByKey opIndex()
{
return typeof(return)(this.dataRange[]);
}
ByKey!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
}
/**
* Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
*
* Params:
* T = Type of the internal hash storage.
*/
struct ByValue(T)
{
private alias Value = CopyConstness!(T, T.Value);
static if (isMutable!T)
{
private alias DataRange = T.array.Range;
}
else
{
private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@disable this();
private this(DataRange dataRange)
{
while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
{
dataRange.popFront();
}
while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
{
dataRange.popBack();
}
this.dataRange = dataRange;
}
@property ByValue save()
{
return this;
}
@property bool empty() const
{
return this.dataRange.empty();
}
@property void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popFront();
}
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popBack();
}
while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(Value) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{
return this.dataRange.front.kv.value;
}
@property ref inout(Value) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{
return this.dataRange.back.kv.value;
}
ByValue opIndex()
{
return typeof(return)(this.dataRange[]);
}
ByValue!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
}
/**
* Hash table is a data structure that stores pairs of keys and values without
* any particular order.
@ -155,7 +386,7 @@ struct Range(T)
* hasher = Hash function for $(D_PARAM Key).
*/
struct HashTable(Key, Value, alias hasher = hash)
if (is(typeof(hasher(Key.init)) == size_t))
if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t))
{
private alias HashArray = .HashArray!(hasher, Key, Value);
private alias Buckets = HashArray.Buckets;
@ -171,11 +402,18 @@ if (is(typeof(hasher(Key.init)) == size_t))
/// ditto
alias ConstRange = .Range!(const HashArray);
/// ditto
alias ByKey = .ByKey!(const HashArray);
/// ditto
alias ByValue = .ByValue!HashArray;
/// ditto
alias ConstByValue = .ByValue!(const HashArray);
invariant
{
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]);
}
/**
@ -440,6 +678,23 @@ if (is(typeof(hasher(Key.init)) == size_t))
assert(hashTable.empty);
}
/**
* Returns current bucket count in the container.
*
* Bucket count equals to the number of the elements can be saved in the
* container in the best case scenario for key distribution, i.d. every key
* has a unique hash value. In a worse case the bucket count can be less
* than the number of elements stored in the container.
*
* Returns: Current bucket count.
*
* See_Also: $(D_PSYMBOL rehash).
*/
@property size_t bucketCount() const
{
return this.data.bucketCount;
}
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
@ -453,15 +708,29 @@ if (is(typeof(hasher(Key.init)) == size_t))
*
* Returns: Just inserted element.
*/
ref Value opIndexAssign(Value value, Key key)
ref Value opIndexAssign()(auto ref Value value, auto ref Key key)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(key);
if (e.status != BucketStatus.used)
{
static if (__traits(isRef, key))
{
e.key = key;
}
else
{
e.moveKey(key);
}
}
static if (__traits(isRef, value))
{
return e.kv.value = value;
}
else
{
return e.kv.value = move(value);
}
}
///
@nogc nothrow pure @safe unittest
@ -490,7 +759,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
*
* Returns: The number of the inserted elements with a unique key.
*/
size_t insert(KeyValue keyValue)
size_t insert(ref KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
@ -503,6 +772,20 @@ if (is(typeof(hasher(Key.init)) == size_t))
return inserted;
}
/// ditto
size_t insert(KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
if (e.status != BucketStatus.used)
{
e.moveKey(keyValue.key);
inserted = 1;
}
move(keyValue.value, e.kv.value);
return inserted;
}
///
@nogc nothrow pure @safe unittest
{
@ -557,13 +840,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
* Find the element with the key $(D_PARAM key).
*
* Params:
* T = Type comparable with the key type, used for the lookup.
* key = The key to be find.
*
* Returns: The value associated with $(D_PARAM key).
*
* Precondition: Element with $(D_PARAM key) is in this hash table.
*/
ref Value opIndex(Key key)
ref Value opIndex(T)(auto ref const T key)
if (ifTestable!(T, a => Key.init == a))
{
const code = this.data.locateBucket(key);
@ -619,12 +904,14 @@ if (is(typeof(hasher(Key.init)) == size_t))
* Looks for $(D_PARAM key) in this hash table.
*
* Params:
* T = Type comparable with the key type, used for the lookup.
* key = The key to look for.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref inout(Key) key) inout
bool opBinaryRight(string op : "in", T)(auto ref const T key) const
if (ifTestable!(T, a => Key.init == a))
{
return key in this.data;
}
@ -645,18 +932,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
* Sets the number of buckets in the container to at least $(D_PARAM n)
* and rearranges all the elements according to their hash values.
*
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
* forced.
*
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
*
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
* is forced without resizing the container.
*
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
* function may have no effect.
* If $(D_PARAM n) is less than or equal to the current
* $(D_PSYMBOL bucketCount), the function may have no effect.
*
* Rehashing is automatically performed whenever the container needs space
* to insert new elements.
@ -697,6 +981,86 @@ if (is(typeof(hasher(Key.init)) == size_t))
assert(hashTable[].front == hashTable.KeyValue("Iguanodon", 9));
assert(hashTable[].back == hashTable.KeyValue("Iguanodon", 9));
}
/**
* Returns a bidirectional range that iterats over the keys of this
* $(D_PSYMBOL HashTable).
*
* This function always returns a $(D_KEYWORD const) range, since changing
* a key of a hash table would probably change its hash value and require
* rehashing.
*
* Returns: $(D_KEYWORD const) bidirectional range that iterates over the
* keys of the container.
*
* See_Also: $(D_PSYMBOL byValue).
*/
ByKey byKey() const
{
return typeof(return)(this.data.array[]);
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable["one"] = 1;
hashTable["two"] = 2;
auto byKey = hashTable.byKey();
assert(!byKey.empty);
assert(byKey.front == "one" || byKey.front == "two");
assert(byKey.back == "one" || byKey.back == "two");
assert(byKey.front != byKey.back);
byKey.popFront();
assert(byKey.front == byKey.back);
byKey.popBack();
assert(byKey.empty);
}
/**
* Returns a bidirectional range that iterats over the values of this
* $(D_PSYMBOL HashTable).
*
* Returns: A bidirectional range that iterates over the values of the
* container.
*
* See_Also: $(D_PSYMBOL byKey).
*/
ByValue byValue()
{
return typeof(return)(this.data.array[]);
}
/// ditto
ConstByValue byValue() const
{
return typeof(return)(this.data.array[]);
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable["one"] = 1;
hashTable["two"] = 2;
auto byValue = hashTable.byValue();
assert(!byValue.empty);
assert(byValue.front == 1 || byValue.front == 2);
assert(byValue.back == 1 || byValue.back == 2);
assert(byValue.front != byValue.back);
byValue.popFront();
assert(byValue.front == byValue.back);
byValue.popBack();
assert(byValue.empty);
}
}
@nogc nothrow pure @safe unittest
@ -727,6 +1091,9 @@ if (is(typeof(hasher(Key.init)) == size_t))
static assert(is(HashTable!(string, int) a));
static assert(is(const HashTable!(string, int)));
static assert(isForwardRange!(HashTable!(string, int).Range));
static assert(is(HashTable!(int, int, (ref const int) => size_t.init)));
static assert(is(HashTable!(int, int, (int) => size_t.init)));
}
// Constructs by reference
@ -773,3 +1140,60 @@ if (is(typeof(hasher(Key.init)) == size_t))
}
testFunc(hashTable);
}
// Issue 53: https://github.com/caraus-ecms/tanya/issues/53
@nogc nothrow pure @safe unittest
{
{
HashTable!(uint, uint) hashTable;
foreach (uint i; 0 .. 14)
{
hashTable[i + 1] = i;
}
assert(hashTable.length == 14);
}
{
HashTable!(int, int) hashtable;
hashtable[1194250162] = 3;
hashtable[-1131293824] = 6;
hashtable[838100082] = 9;
hashtable.rehash(11);
assert(hashtable[-1131293824] == 6);
}
}
@nogc nothrow pure @safe unittest
{
static struct String
{
bool opEquals(string) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(ref const string) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(String) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(ref const String) const @nogc nothrow pure @safe
{
return true;
}
size_t toHash() const @nogc nothrow pure @safe
{
return 0;
}
}
static assert(is(typeof("asdf" in HashTable!(String, int)())));
static assert(is(typeof(HashTable!(String, int)()["asdf"])));
}

View File

@ -20,26 +20,3 @@ public import tanya.container.hashtable;
public import tanya.container.list;
public import tanya.container.set;
public import tanya.container.string;
/**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
* is full.
*/
deprecated
class HashContainerFullException : Exception
{
/**
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
}

View File

@ -15,7 +15,6 @@
*/
module tanya.container.set;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.container.entry;
import tanya.hash.lookup;
@ -432,6 +431,23 @@ if (is(typeof(hasher(T.init)) == size_t))
assert(set.empty);
}
/**
* Returns current bucket count in the container.
*
* Bucket count equals to the number of the elements can be saved in the
* container in the best case scenario for key distribution, i.d. every key
* has a unique hash value. In a worse case the bucket count can be less
* than the number of elements stored in the container.
*
* Returns: Current bucket count.
*
* See_Also: $(D_PSYMBOL rehash).
*/
@property size_t bucketCount() const
{
return this.data.bucketCount;
}
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
@ -443,6 +459,17 @@ if (is(typeof(hasher(T.init)) == size_t))
*
* Returns: Amount of new elements inserted.
*/
size_t insert(ref T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{
e.moveKey(value);
return 1;
}
return 0;
}
size_t insert(T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
@ -534,12 +561,14 @@ if (is(typeof(hasher(T.init)) == size_t))
* $(D_KEYWORD in) operator.
*
* Params:
* U = Type comparable with the element type, used for the lookup.
* value = Element to be searched for.
*
* Returns: $(D_KEYWORD true) if the given element exists in the container,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref inout(T) value) inout
bool opBinaryRight(string op : "in", U)(auto ref const U value) const
if (ifTestable!(U, a => T.init == a))
{
return value in this.data;
}
@ -559,18 +588,15 @@ if (is(typeof(hasher(T.init)) == size_t))
* Sets the number of buckets in the container to at least $(D_PARAM n)
* and rearranges all the elements according to their hash values.
*
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
* forced.
*
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
*
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
* is forced without resizing the container.
*
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
* function may have no effect.
* If $(D_PARAM n) is less than or equal to the current
* $(D_PSYMBOL bucketCount), the function may have no effect.
*
* Rehashing is automatically performed whenever the container needs space
* to insert new elements.

View File

@ -31,6 +31,7 @@ import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.hash.lookup;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
@ -499,7 +500,7 @@ struct String
}
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
@ -579,16 +580,10 @@ struct String
* Params:
* chr = The character should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
* Returns: The number of bytes inserted (1).
*/
size_t insertBack(const char chr) @nogc pure @trusted
size_t insertBack(char chr) @nogc nothrow pure @trusted
{
if ((chr & 0x80) != 0)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
}
reserve(length + 1);
*(data + length) = chr;
@ -652,8 +647,6 @@ struct String
* str = String should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
*/
size_t insertBack(R)(R str) @trusted
if (!isInfinite!R
@ -673,46 +666,18 @@ struct String
this.length_ = size;
return str.length;
}
else static if (isInstanceOf!(ByCodeUnit, R))
{
str.get.copy(this.data[length .. size]);
this.length_ = size;
return str.length;
}
else
{
size_t insertedLength;
while (!str.empty)
foreach (c; str)
{
ubyte expectedLength;
if ((str.front & 0x80) == 0x00)
{
expectedLength = 1;
}
else if ((str.front & 0xe0) == 0xc0)
{
expectedLength = 2;
}
else if ((str.front & 0xf0) == 0xe0)
{
expectedLength = 3;
}
else if ((str.front & 0xf8) == 0xf0)
{
expectedLength = 4;
}
else
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
size = length + expectedLength;
reserve(size);
for (; expectedLength > 0; --expectedLength)
{
if (str.empty)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
*(data + length) = str.front;
str.popFront();
}
insertedLength += expectedLength;
this.length_ = size;
insertedLength += insertBack(c);
}
return insertedLength;
}
@ -828,7 +793,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.capacity == 0);
@ -869,7 +834,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Die Alten lasen laut.");
assert(s.capacity == 21);
@ -894,7 +859,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("In allem Schreiben ist Schamlosigkeit.");
assert(s.capacity == 38);
@ -991,7 +956,7 @@ struct String
*
* Returns: Null-terminated string.
*/
const(char)* toStringz() @nogc nothrow pure
const(char)* toStringz() @nogc nothrow pure @system
{
reserve(length + 1);
this.data[length] = '\0';
@ -999,7 +964,7 @@ struct String
}
///
@nogc pure unittest
@nogc nothrow pure @system unittest
{
auto s = String("C string.");
assert(s.toStringz()[0] == 'C');
@ -1018,7 +983,7 @@ struct String
alias opDollar = length;
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Piscis primuin a capite foetat.");
assert(s.length == 31);
@ -1044,7 +1009,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Alea iacta est.");
assert(s[0] == 'A');
@ -1067,7 +1032,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Plutarchus");
auto r = s[];
@ -1086,7 +1051,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = const String("Was ich vermag, soll gern geschehen. Goethe");
auto r1 = s[];
@ -1162,7 +1127,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.empty);
@ -1207,7 +1172,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Vladimir Soloviev");
auto r = s[9 .. $];
@ -1271,7 +1236,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Черная, потом пропахшая выть!");
s = String("Как мне тебя не ласкать, не любить?");
@ -1299,10 +1264,11 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Оловом светится лужная голь...");
s = "Грустная песня, ты - русская боль.";
assert(s == "Грустная песня, ты - русская боль.");
}
/**
@ -1344,7 +1310,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Голубая кофта.") < String("Синие глаза."));
assert(String("Никакой я правды") < String("милой не сказал")[]);
@ -1397,7 +1363,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Милая спросила:") != String("Крутит ли метель?"));
assert(String("Затопить бы печку,") != String("постелить постель.")[]);
@ -1430,7 +1396,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("alea iacta est.");
@ -1455,7 +1421,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
@ -1469,7 +1435,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
@ -1482,7 +1448,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
@ -1574,7 +1540,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertAfter(s[0 .. 27], ",");
@ -1603,7 +1569,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertBefore(s[27 .. $], ",");
@ -1614,11 +1580,21 @@ struct String
assert(s == "Казнить, нельзя помиловать.");
}
/**
* Calculates the hash value for the string.
*
* Returns: Hash value for the string.
*/
size_t toHash() const @nogc nothrow pure @safe
{
return hash(get);
}
mixin DefaultAllocator;
}
// Postblit works.
@nogc pure @safe unittest
// Postblit works
@nogc nothrow pure @safe unittest
{
void internFunc(String arg)
{
@ -1637,7 +1613,7 @@ struct String
topFunc(String("asdf"));
}
// Const range produces mutable ranges.
// Const range produces mutable ranges
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
@ -1663,7 +1639,7 @@ struct String
}
}
// Can pop multibyte characters.
// Can pop multibyte characters
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
@ -1680,3 +1656,12 @@ struct String
s[$ - 3] = 0xf0;
assertThrown!UTFException(&(range.popFront));
}
// Inserts own char range correctly
@nogc nothrow pure @safe unittest
{
auto s1 = String(`ü`);
String s2;
s2.insertBack(s1[]);
assert(s1 == s2);
}

View File

@ -282,8 +282,8 @@ final class ConvException : Exception
*
* The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
*/
package T readIntegral(T, R)(ref R range, ubyte base = 10)
if (isForwardRange!R
package T readIntegral(T, R)(ref R range, const ubyte base = 10)
if (isInputRange!R
&& isSomeChar!(ElementType!R)
&& isIntegral!T
&& isUnsigned!T)
@ -301,9 +301,9 @@ do
}
T n;
while (n <= boundary)
{
int digit;
do
{
if (range.front >= 'a')
{
digit = range.front - 'W';
@ -332,17 +332,30 @@ do
return n;
}
}
if (range.length > 1)
while (n < boundary);
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
{
digit = range.front - '7';
}
else if (range.front >= '0')
{
digit = range.front - '0';
}
else
{
return n;
}
int digit = range.front - '0';
if (n > cast(T) ((T.max - digit) / base))
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
return n;
}
@ -410,6 +423,14 @@ do
assert(number.empty);
}
// Handles small overflows
@nogc nothrow pure @safe unittest
{
string number = "256";
assert(readIntegral!ubyte(number, 10) == 25);
assert(number.front == '6');
}
/**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
@ -854,3 +875,135 @@ if (is(Unqual!To == String))
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
}
/**
* Converts a stringish range to an integral value.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) doesn't contain an
* integral value.
*/
To to(To, From)(auto ref From from)
if (isInputRange!From && isSomeChar!(ElementType!From) && isIntegral!To)
{
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
static if (isSigned!To)
{
bool negative;
}
if (from.front == '-')
{
static if (isUnsigned!To)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
else
{
negative = true;
from.popFront();
}
}
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
ubyte base = 10;
if (from.front == '0')
{
from.popFront();
if (from.empty)
{
return To.init;
}
else if (from.front == 'x' || from.front == 'X')
{
base = 16;
from.popFront();
}
else if (from.front == 'b' || from.front == 'B')
{
base = 2;
from.popFront();
}
else
{
base = 8;
}
}
auto unsigned = readIntegral!(Unsigned!To, From)(from, base);
if (!from.empty)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
static if (isSigned!To)
{
if (negative)
{
auto predecessor = cast(Unsigned!To) (unsigned - 1);
if (predecessor > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
return cast(To) (-(cast(Largest!(To, ptrdiff_t)) predecessor) - 1);
}
else if (unsigned > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
else
{
return unsigned;
}
}
else
{
return unsigned;
}
}
///
@nogc pure @safe unittest
{
assert("1234".to!uint() == 1234);
assert("1234".to!int() == 1234);
assert("1234".to!int() == 1234);
assert("0".to!int() == 0);
assert("-0".to!int() == 0);
assert("0x10".to!int() == 16);
assert("0X10".to!int() == 16);
assert("-0x10".to!int() == -16);
assert("0b10".to!int() == 2);
assert("0B10".to!int() == 2);
assert("-0b10".to!int() == -2);
assert("010".to!int() == 8);
assert("-010".to!int() == -8);
assert("-128".to!byte == cast(byte) -128);
assertThrown!ConvException(() => "".to!int);
assertThrown!ConvException(() => "-".to!int);
assertThrown!ConvException(() => "-5".to!uint);
assertThrown!ConvException(() => "-129".to!byte);
assertThrown!ConvException(() => "256".to!ubyte);
}

File diff suppressed because it is too large Load Diff

71
source/tanya/functional.d Normal file
View File

@ -0,0 +1,71 @@
/* 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/. */
/**
* Functions that manipulate other functions and their argument lists.
*
* Copyright: Eugene Wissner 2018.
* 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/functional.d,
* tanya/functional.d)
*/
module tanya.functional;
import tanya.algorithm.mutation;
import tanya.meta.metafunction;
private template forwardOne(alias arg)
{
static if (__traits(isRef, arg) || __traits(isOut, arg))
{
alias forwardOne = arg;
}
else
{
@property auto forwardOne()
{
return move(arg);
}
}
}
/**
* Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
* storage classes.
*
* $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an
* argument list of the same length that can be for example passed to a
* function accepting the arguments of this type.
*
* Params:
* args = Argument list.
*
* Returns: $(D_PARAM args) with their original storage classes.
*/
template forward(args...)
{
static if (args.length == 1)
{
alias forward = forwardOne!(args[0]);
}
else
{
alias forward = Map!(forwardOne, args);
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((int i) { int v = forward!i; })));
static assert(is(typeof((ref int i) { int v = forward!i; })));
static assert(is(typeof({
void f(int i, ref int j, out int k)
{
f(forward!(i, j, k));
}
})));
}

View File

@ -87,7 +87,7 @@ if (isFloatingPoint!F)
static assert(ieeePrecision!double == IEEEPrecision.double_);
}
private union FloatBits(F)
package(tanya) union FloatBits(F)
{
Unqual!F floating;
static if (ieeePrecision!F == IEEEPrecision.single)
@ -739,101 +739,3 @@ bool isPseudoprime(ulong x) @nogc nothrow pure @safe
assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime);
}
deprecated("Use tanya.algorithm.comparison.min instead")
T min(T)(T x, T y)
if (isIntegral!T)
{
return x < y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.min instead")
T min(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x < y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.min instead")
ref T min(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x < y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.min instead")
T min(T)(T x, T y)
if (is(T == Integer))
{
return x < y ? move(x) : move(y);
}
@nogc nothrow pure @safe unittest
{
assert(min(Integer(5), Integer(3)) == 3);
}
deprecated("Use tanya.algorithm.comparison.max instead")
T max(T)(T x, T y)
if (isIntegral!T)
{
return x > y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.max instead")
T max(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x > y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.max instead")
ref T max(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x > y ? x : y;
}
deprecated("Use tanya.algorithm.comparison.max instead")
T max(T)(T x, T y)
if (is(T == Integer))
{
return x > y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(max(Integer(5), Integer(3)) == 5);
}
// min/max accept const and mutable references.
@nogc nothrow pure @safe unittest
{
{
Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
{
const Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
}

View File

@ -70,26 +70,6 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
static assert(!isWideString!(dchar[10]));
}
deprecated("Use tanya.meta.transform.Smallest instead")
template Smallest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Smallest = Args[0];
}
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
{
alias Smallest = Smallest!(Args[1 .. $]);
}
else
{
alias Smallest = Args[0];
}
}
/**
* Determines whether $(D_PARAM T) is a complex type.
*
@ -178,7 +158,7 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
*/
template hasStaticMember(T, string member)
{
static if (__traits(hasMember, T, member))
static if (hasMember!(T, member))
{
alias Member = Alias!(__traits(getMember, T, member));
@ -928,26 +908,6 @@ template mostNegative(T)
static assert(mostNegative!cfloat == -cfloat.max);
}
deprecated("Use tanya.meta.transform.Largest instead")
template Largest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Largest = Args[0];
}
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
{
alias Largest = Largest!(Args[1 .. $]);
}
else
{
alias Largest = Args[0];
}
}
/**
* Determines whether the type $(D_PARAM T) is copyable.
*
@ -1540,7 +1500,6 @@ if (F.length == 1)
* Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
* $(D_PARAM member), $(D_KEYWORD false) otherwise.
*/
deprecated("Use __traits(hasMember) instead")
enum bool hasMember(T, string member) = __traits(hasMember, T, member);
///

View File

@ -112,17 +112,10 @@ else version (Windows)
socket,
socklen_t,
SOL_SOCKET,
WSAEWOULDBLOCK,
WSAGetLastError;
import tanya.async.iocp;
import tanya.sys.windows.def;
import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
ENOBUFS = WSAENOBUFS,
EOPNOTSUPP = WSAEOPNOTSUPP,
EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
EPROTOTYPE = WSAEPROTOTYPE,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT,
ETIMEDOUT = WSAETIMEDOUT,
EWOULDBLOCK = WSAEWOULDBLOCK;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;
@ -616,37 +609,6 @@ enum AddressFamily : int
inet6 = 10, /// IP version 6.
}
deprecated("Use tanya.os.error.ErrorCode.ErrorNo instead")
enum SocketError : int
{
/// Unknown error.
unknown = 0,
/// Firewall rules forbid connection.
accessDenied = EPERM,
/// A socket operation was attempted on a non-socket.
notSocket = EBADF,
/// The network is not available.
networkDown = ECONNABORTED,
/// An invalid pointer address was detected by the underlying socket provider.
fault = EFAULT,
/// An invalid argument was supplied to a $(D_PSYMBOL Socket) member.
invalidArgument = EINVAL,
/// The limit on the number of open sockets has been reached.
tooManyOpenSockets = ENFILE,
/// No free buffer space is available for a Socket operation.
noBufferSpaceAvailable = ENOBUFS,
/// The address family is not supported by the protocol family.
operationNotSupported = EOPNOTSUPP,
/// The protocol is not implemented or has not been configured.
protocolNotSupported = EPROTONOSUPPORT,
/// Protocol error.
protocolError = EPROTOTYPE,
/// The connection attempt timed out, or the connected host has failed to respond.
timedOut = ETIMEDOUT,
/// The support for the specified socket type does not exist in this address family.
socketNotSupported = ESOCKTNOSUPPORT,
}
/**
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
@ -1476,7 +1438,7 @@ bool wouldHaveBlocked() nothrow @trusted @nogc
else version (Windows)
{
return WSAGetLastError() == ERROR_IO_PENDING
|| WSAGetLastError() == EWOULDBLOCK
|| WSAGetLastError() == WSAEWOULDBLOCK
|| WSAGetLastError() == ERROR_IO_INCOMPLETE;
}
}

View File

@ -14,8 +14,8 @@
*/
module tanya.range.primitive;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.math;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;

View File

@ -1,115 +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/. */
/**
* Copyright: Eugene Wissner 2017-2018.
* 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/sys/windows/error.d,
* tanya/sys/windows/error.d)
*/
deprecated("Use core.sys.windows.winerror instead")
module tanya.sys.windows.error;
version (Windows):
private enum WSABASEERR = 10000;
enum
{
WSAEINTR = WSABASEERR + 4,
WSAEBADF = WSABASEERR + 9,
WSAEACCES = WSABASEERR + 13,
WSAEFAULT = WSABASEERR + 14,
WSAEINVAL = WSABASEERR + 22,
WSAEMFILE = WSABASEERR + 24,
WSAEWOULDBLOCK = WSABASEERR + 35,
WSAEINPROGRESS = WSABASEERR + 36,
WSAEALREADY = WSABASEERR + 37,
WSAENOTSOCK = WSABASEERR + 38,
WSAEDESTADDRREQ = WSABASEERR + 39,
WSAEMSGSIZE = WSABASEERR + 40,
WSAEPROTOTYPE = WSABASEERR + 41,
WSAENOPROTOOPT = WSABASEERR + 42,
WSAEPROTONOSUPPORT = WSABASEERR + 43,
WSAESOCKTNOSUPPORT = WSABASEERR + 44,
WSAEOPNOTSUPP = WSABASEERR + 45,
WSAEPFNOSUPPORT = WSABASEERR + 46,
WSAEAFNOSUPPORT = WSABASEERR + 47,
WSAEADDRINUSE = WSABASEERR + 48,
WSAEADDRNOTAVAIL = WSABASEERR + 49,
WSAENETDOWN = WSABASEERR + 50,
WSAENETUNREACH = WSABASEERR + 51,
WSAENETRESET = WSABASEERR + 52,
WSAECONNABORTED = WSABASEERR + 53,
WSAECONNRESET = WSABASEERR + 54,
WSAENOBUFS = WSABASEERR + 55,
WSAEISCONN = WSABASEERR + 56,
WSAENOTCONN = WSABASEERR + 57,
WSAESHUTDOWN = WSABASEERR + 58,
WSAETOOMANYREFS = WSABASEERR + 59,
WSAETIMEDOUT = WSABASEERR + 60,
WSAECONNREFUSED = WSABASEERR + 61,
WSAELOOP = WSABASEERR + 62,
WSAENAMETOOLONG = WSABASEERR + 63,
WSAEHOSTDOWN = WSABASEERR + 64,
WSAEHOSTUNREACH = WSABASEERR + 65,
WSAENOTEMPTY = WSABASEERR + 66,
WSAEPROCLIM = WSABASEERR + 67,
WSAEUSERS = WSABASEERR + 68,
WSAEDQUOT = WSABASEERR + 69,
WSAESTALE = WSABASEERR + 70,
WSAEREMOTE = WSABASEERR + 71,
WSASYSNOTREADY = WSABASEERR + 91,
WSAVERNOTSUPPORTED = WSABASEERR + 92,
WSANOTINITIALISED = WSABASEERR + 93,
WSAEDISCON = WSABASEERR + 101,
WSAENOMORE = WSABASEERR + 102,
WSAECANCELLED = WSABASEERR + 103,
WSAEINVALIDPROCTABLE = WSABASEERR + 104,
WSAEINVALIDPROVIDER = WSABASEERR + 105,
WSAEPROVIDERFAILEDINIT = WSABASEERR + 106,
WSASYSCALLFAILURE = WSABASEERR + 107,
WSASERVICE_NOT_FOUND = WSABASEERR + 108,
WSATYPE_NOT_FOUND = WSABASEERR + 109,
WSA_E_NO_MORE = WSABASEERR + 110,
WSA_E_CANCELLED = WSABASEERR + 111,
WSAEREFUSED = WSABASEERR + 112,
WSAHOST_NOT_FOUND = WSABASEERR + 1001,
WSATRY_AGAIN = WSABASEERR + 1002,
WSANO_RECOVERY = WSABASEERR + 1003,
WSANO_DATA = WSABASEERR + 1004,
WSA_QOS_RECEIVERS = WSABASEERR + 1005,
WSA_QOS_SENDERS = WSABASEERR + 1006,
WSA_QOS_NO_SENDERS = WSABASEERR + 1007,
WSA_QOS_NO_RECEIVERS = WSABASEERR + 1008,
WSA_QOS_REQUEST_CONFIRMED = WSABASEERR + 1009,
WSA_QOS_ADMISSION_FAILURE = WSABASEERR + 1010,
WSA_QOS_POLICY_FAILURE = WSABASEERR + 1011,
WSA_QOS_BAD_STYLE = WSABASEERR + 1012,
WSA_QOS_BAD_OBJECT = WSABASEERR + 1013,
WSA_QOS_TRAFFIC_CTRL_ERROR = WSABASEERR + 1014,
WSA_QOS_GENERIC_ERROR = WSABASEERR + 1015,
WSA_QOS_ESERVICETYPE = WSABASEERR + 1016,
WSA_QOS_EFLOWSPEC = WSABASEERR + 1017,
WSA_QOS_EPROVSPECBUF = WSABASEERR + 1018,
WSA_QOS_EFILTERSTYLE = WSABASEERR + 1019,
WSA_QOS_EFILTERTYPE = WSABASEERR + 1020,
WSA_QOS_EFILTERCOUNT = WSABASEERR + 1021,
WSA_QOS_EOBJLENGTH = WSABASEERR + 1022,
WSA_QOS_EFLOWCOUNT = WSABASEERR + 1023,
WSA_QOS_EUNKOWNPSOBJ = WSABASEERR + 1024,
WSA_QOS_EPOLICYOBJ = WSABASEERR + 1025,
WSA_QOS_EFLOWDESC = WSABASEERR + 1026,
WSA_QOS_EPSFLOWSPEC = WSABASEERR + 1027,
WSA_QOS_EPSFILTERSPEC = WSABASEERR + 1028,
WSA_QOS_ESDMODEOBJ = WSABASEERR + 1029,
WSA_QOS_ESHAPERATEOBJ = WSABASEERR + 1030,
WSA_QOS_RESERVED_PETYPE = WSABASEERR + 1031,
}

View File

@ -15,6 +15,5 @@ module tanya.sys.windows;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.error;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;

View File

@ -17,65 +17,10 @@
*/
module tanya.typecons;
import tanya.algorithm.mutation;
import tanya.format;
import tanya.meta.metafunction : AliasSeq, AliasTuple = Tuple, Map;
deprecated("Use tanya.typecons.Tuple instead")
template Pair(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!(AliasTuple!(Specs[0], Specs[1]),
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
}
else
{
alias parseSpecs
= AliasSeq!(AliasTuple!(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 == 2, "Invalid argument count");
struct Pair
{
/// Field types.
alias Types = Map!(ChooseType, ParsedSpecs);
// Create field aliases.
static if (ParsedSpecs[0].length == 2)
{
mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
}
static if (ParsedSpecs[1].length == 2)
{
mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
}
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
Types expand;
alias expand this;
}
}
import tanya.meta.trait;
/**
* $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
@ -187,3 +132,323 @@ template Tuple(Specs...)
static assert(!is(Tuple!(int, double, char)));
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
}
/**
* $(D_PSYMBOL Option) is a type that contains an optional value.
*
* Params:
* T = Type of the encapsulated value.
*/
struct Option(T)
{
private bool isNothing_ = true;
private T value = void;
/**
* Constructs a new option with $(D_PARAM value).
*
* Params:
* value = Encapsulated value.
*/
this()(ref T value)
{
this.value = value;
this.isNothing_ = false;
}
/// ditto
this()(T value) @trusted
{
moveEmplace(value, this.value);
this.isNothing_ = false;
}
/**
* Tells if the option is just a value or nothing.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a nothing,
* $(D_KEYWORD false) if it contains a value.
*/
@property bool isNothing() const
{
return this.isNothing_;
}
/**
* Returns the encapsulated value.
*
* Returns: Value encapsulated in this $(D_PSYMBOL Option).
*
* See_Also: $(D_PSYMBOL or).
*
* Precondition: `!isNothing`.
*/
@property ref inout(T) get() inout
in
{
assert(!isNothing, "Option is nothing");
}
do
{
return this.value;
}
/**
* Returns the encapsulated value if available or a default value
* otherwise.
*
* Note that the contained value can be returned by reference only if the
* default value is passed by reference as well.
*
* Params:
* U = Type of the default value.
* defaultValue = Default value.
*
* Returns: The value of this $(D_PSYMBOL Option) if available,
* $(D_PARAM defaultValue) otherwise.
*
* See_Also: $(D_PSYMBOL isNothing), $(D_PSYMBOL get).
*/
@property U or(U)(U defaultValue) inout
if (is(U == T) && isCopyable!T)
{
return isNothing ? defaultValue : this.value;
}
/// ditto
@property ref inout(T) or(ref inout(T) defaultValue) inout
{
return isNothing ? defaultValue : this.value;
}
/**
* Casts this $(D_PSYMBOL Option) to $(D_KEYWORD bool).
*
* An $(D_PSYMBOL Option) is $(D_KEYWORD true) if it contains a value,
* ($D_KEYWORD false) if it contains nothing.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a value,
* ($D_KEYWORD false) if it contains nothing.
*/
bool opCast(U : bool)()
{
return !isNothing;
}
/**
* Compares this $(D_PSYMBOL Option) with $(D_PARAM that).
*
* If both objects are options of the same type and they don't contain a
* value, they are considered equal. If only one of them contains a value,
* they aren't equal. Otherwise, the encapsulated values are compared for
* equality.
*
* If $(D_PARAM U) is a type comparable with the type encapsulated by this
* $(D_PSYMBOL Option), the value of this $(D_PSYMBOL Option) is compared
* with $(D_PARAM that), this $(D_PSYMBOL Option) must have a value then.
*
* Params:
* U = Type of the object to compare with.
* that = Object to compare with.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) and
* $(D_PARAM that) are equal, $(D_KEYWORD false) if not.
*
* Precondition: `!isNothing` if $(D_PARAM U) is equality comparable with
* $(D_PARAM T).
*/
bool opEquals(U)(auto ref const U that) const
if (is(U == Option))
{
if (!isNothing && !that.isNothing)
{
return this.value == that.value;
}
return isNothing == that.isNothing;
}
/// ditto
bool opEquals(U)(auto ref const U that) const
if (ifTestable!(U, a => a == T.init) && !is(U == Option))
in
{
assert(!isNothing);
}
do
{
return get == that;
}
/**
* Resets this $(D_PSYMBOL Option) and destroys the contained value.
*
* $(D_PSYMBOL reset) can be safely called on an $(D_PSYMBOL Option) that
* doesn't contain any value.
*/
void reset()
{
static if (hasElaborateDestructor!T)
{
destroy(this.value);
}
this.isNothing_ = true;
}
/**
* Assigns a new value.
*
* Params:
* U = Type of the new value.
* that = New value.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(U)(ref U that)
if (is(U : T) && !is(U == Option))
{
this.value = that;
this.isNothing_ = false;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(U that)
if (is(U == T))
{
move(that, this.value);
this.isNothing_ = false;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(ref U that)
if (is(U == Option))
{
this.value = that;
this.isNothing_ = that.isNothing;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(U that)
if (is(U == Option))
{
move(that.value, this.value);
this.isNothing_ = that.isNothing_;
return this;
}
alias get this;
}
///
@nogc nothrow pure @safe unittest
{
Option!int option;
assert(option.isNothing);
assert(option.or(8) == 8);
option = 5;
assert(!option.isNothing);
assert(option.get == 5);
assert(option.or(8) == 5);
option.reset();
assert(option.isNothing);
}
// Assigns a new value
@nogc nothrow pure @safe unittest
{
{
Option!int option = 5;
option = 8;
assert(!option.isNothing);
assert(option == 8);
}
{
Option!int option;
const int newValue = 8;
assert(option.isNothing);
option = newValue;
assert(!option.isNothing);
assert(option == newValue);
}
{
Option!int option1;
Option!int option2 = 5;
assert(option1.isNothing);
option1 = option2;
assert(!option1.isNothing);
assert(option1.get == 5);
}
}
// Constructs with a value passed by reference
@nogc nothrow pure @safe unittest
{
int i = 5;
assert(Option!int(i).get == 5);
}
// Moving
@nogc nothrow pure @safe unittest
{
static struct NotCopyable
{
@disable this(this);
}
static assert(is(typeof(Option!NotCopyable(NotCopyable()))));
// The value cannot be returned by reference because the default value
// isn't passed by reference
static assert(!is(typeof(Option!DisabledPostblit().or(NotCopyable()))));
{
NotCopyable notCopyable;
static assert(is(typeof(Option!NotCopyable().or(notCopyable))));
}
{
Option!NotCopyable option;
assert(option.isNothing);
option = NotCopyable();
assert(!option.isNothing);
}
{
Option!NotCopyable option;
assert(option.isNothing);
option = Option!NotCopyable(NotCopyable());
assert(!option.isNothing);
}
}
// Cast to bool is done before touching the encapsulated value
@nogc nothrow pure @safe unittest
{
assert(Option!bool(false));
}
// Option can be const
@nogc nothrow pure @safe unittest
{
assert((const Option!int(5)).get == 5);
assert((const Option!int()).or(5) == 5);
}
// Equality
@nogc nothrow pure @safe unittest
{
assert(Option!int() == Option!int());
assert(Option!int(0) != Option!int());
assert(Option!int(5) == Option!int(5));
assert(Option!int(5) == 5);
assert(Option!int(5) == cast(ubyte) 5);
}
// Returns default value
@nogc nothrow pure @safe unittest
{
{
int i = 5;
assert(((ref e) => e)(Option!int().or(i)) == 5);
}
}