Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
278e851414 | |||
6f549df243 | |||
4633bcc680 | |||
dc39efd316 | |||
260937e4fb | |||
e17fff2881 | |||
bc32511254 | |||
f30972f948 | |||
ea33ca62c8 | |||
0f365758e1 | |||
2815b53a88 | |||
6c0588164a | |||
8ee1d647ce | |||
25791775e6 | |||
f013e2f1f4 | |||
ac3935d71b | |||
b1c217e272 | |||
d007aaa310 | |||
8aaf9e14be | |||
ae3e6b46f6 | |||
8687df1fbb | |||
ba0aff6737 |
@ -7,7 +7,7 @@ os:
|
|||||||
language: d
|
language: d
|
||||||
|
|
||||||
d:
|
d:
|
||||||
- dmd-2.074.0
|
- dmd-2.074.1
|
||||||
- dmd-2.073.2
|
- dmd-2.073.2
|
||||||
- dmd-2.072.2
|
- dmd-2.072.2
|
||||||
- dmd-2.071.2
|
- dmd-2.071.2
|
||||||
|
30
README.md
30
README.md
@ -15,7 +15,7 @@ Garbage Collector heap. Everything in the library is usable in @nogc code.
|
|||||||
Tanya extends Phobos functionality and provides alternative implementations for
|
Tanya extends Phobos functionality and provides alternative implementations for
|
||||||
data structures and utilities that depend on the Garbage Collector in Phobos.
|
data structures and utilities that depend on the Garbage Collector in Phobos.
|
||||||
|
|
||||||
* [Bug tracker](https://issues.caraus.io/projects/tanya)
|
* [Bug tracker](https://issues.caraus.io/projects/tanya/issues)
|
||||||
* [Documentation](https://docs.caraus.io/tanya)
|
* [Documentation](https://docs.caraus.io/tanya)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
@ -24,17 +24,16 @@ Tanya consists of the following packages:
|
|||||||
|
|
||||||
* `async`: Event loop (epoll, kqueue and IOCP).
|
* `async`: Event loop (epoll, kqueue and IOCP).
|
||||||
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
|
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
|
||||||
string.
|
string, Hash set.
|
||||||
* `math`: Arbitrary precision integer and a set of functions.
|
* `math`: Arbitrary precision integer and a set of functions.
|
||||||
* `memory`: Tools for manual memory management (allocator, reference counting,
|
* `memory`: Tools for manual memory management (allocators, smart pointers).
|
||||||
helper functions).
|
|
||||||
* `network`: URL-Parsing, sockets, utilities.
|
* `network`: URL-Parsing, sockets, utilities.
|
||||||
|
|
||||||
### Supported compilers
|
### Supported compilers
|
||||||
|
|
||||||
| dmd |
|
| dmd |
|
||||||
|:-------:|
|
|:-------:|
|
||||||
| 2.074.0 |
|
| 2.074.1 |
|
||||||
| 2.073.2 |
|
| 2.073.2 |
|
||||||
| 2.072.2 |
|
| 2.072.2 |
|
||||||
| 2.071.2 |
|
| 2.071.2 |
|
||||||
@ -43,30 +42,27 @@ helper functions).
|
|||||||
|
|
||||||
Following modules are under development:
|
Following modules are under development:
|
||||||
|
|
||||||
| Feature | Branch | Build status |
|
| Feature | Branch | Build status |
|
||||||
|--------------|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| BitVector | bitvector | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
|
| BitArray | bitvector | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
|
||||||
| TLS | crypto | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
|
| TLS | crypto | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
|
||||||
| File IO | io | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
|
| File IO | io | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
|
||||||
| Hash table | horton-table | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/horton-table) |
|
|
||||||
|
|
||||||
### Further characteristics
|
### Further characteristics
|
||||||
|
|
||||||
* Tanya is a native D library.
|
* Tanya is a native D library without any external dependencies.
|
||||||
|
|
||||||
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it
|
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it
|
||||||
is being tested on Windows and FreeBSD as well.
|
is being tested on Windows and FreeBSD as well.
|
||||||
|
|
||||||
* The library isn't thread-safe. Thread-safity should be added later.
|
* The library isn't thread-safe yet.
|
||||||
|
|
||||||
## Release management
|
## Release management
|
||||||
|
|
||||||
3-week release cycle.
|
3-week release cycle.
|
||||||
|
|
||||||
|
Deprecated features are removed after one release (in approximately 6 weeks after deprecating).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Since I'm mostly busy writing new code and implementing new features I would
|
|
||||||
appreciate, if anyone uses the library. It would help me to improve the
|
|
||||||
codebase and fix issues.
|
|
||||||
|
|
||||||
Feel free to contact me if you have any questions: info@caraus.de.
|
Feel free to contact me if you have any questions: info@caraus.de.
|
||||||
|
@ -4,7 +4,7 @@ os: Visual Studio 2017
|
|||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- DC: dmd
|
- DC: dmd
|
||||||
DVersion: 2.074.0
|
DVersion: 2.074.1
|
||||||
arch: x86
|
arch: x86
|
||||||
- DC: dmd
|
- DC: dmd
|
||||||
DVersion: 2.073.2
|
DVersion: 2.073.2
|
||||||
|
2
dub.json
2
dub.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tanya",
|
"name": "tanya",
|
||||||
"description": "General purpose, @nogc library",
|
"description": "General purpose, @nogc library. Containers, networking, memory management, utilities",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"copyright": "(c) Eugene Wissner <info@caraus.de>",
|
"copyright": "(c) Eugene Wissner <info@caraus.de>",
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -151,7 +151,7 @@ final class KqueueLoop : SelectorLoop
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void set(socket_t socket, short filter, ushort flags) @nogc
|
private void set(SocketType socket, short filter, ushort flags) @nogc
|
||||||
{
|
{
|
||||||
if (changes.length <= changeCount)
|
if (changes.length <= changeCount)
|
||||||
{
|
{
|
||||||
|
@ -38,11 +38,11 @@
|
|||||||
*
|
*
|
||||||
* version (Windows)
|
* version (Windows)
|
||||||
* {
|
* {
|
||||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
|
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
|
||||||
* }
|
* }
|
||||||
* else
|
* else
|
||||||
* {
|
* {
|
||||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
|
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||||
* sock.blocking = false;
|
* sock.blocking = false;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
@ -160,7 +160,7 @@ struct Range(E)
|
|||||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||||
}
|
}
|
||||||
|
|
||||||
inout(E[]) get() inout @trusted
|
inout(E)[] get() inout @trusted
|
||||||
{
|
{
|
||||||
return this.begin[0 .. length];
|
return this.begin[0 .. length];
|
||||||
}
|
}
|
||||||
@ -174,6 +174,12 @@ struct Range(E)
|
|||||||
*/
|
*/
|
||||||
struct Array(T)
|
struct Array(T)
|
||||||
{
|
{
|
||||||
|
/// The range types for $(D_PSYMBOL Array).
|
||||||
|
alias Range = .Range!T;
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
alias ConstRange = .Range!(const T);
|
||||||
|
|
||||||
private size_t length_;
|
private size_t length_;
|
||||||
private T* data;
|
private T* data;
|
||||||
private size_t capacity_;
|
private size_t capacity_;
|
||||||
@ -638,7 +644,7 @@ struct Array(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
|
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
|
||||||
*/
|
*/
|
||||||
Range!T remove(Range!T r) @trusted
|
Range remove(Range r) @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(r.container is &this);
|
assert(r.container is &this);
|
||||||
@ -648,9 +654,9 @@ struct Array(T)
|
|||||||
body
|
body
|
||||||
{
|
{
|
||||||
auto end = this.data + this.length;
|
auto end = this.data + this.length;
|
||||||
moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end));
|
moveAll(.Range!T(this, r.end, end), .Range!T(this, r.begin, end));
|
||||||
length = length - r.length;
|
length = length - r.length;
|
||||||
return Range!T(this, r.begin, this.data + length);
|
return .Range!T(this, r.begin, this.data + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -788,7 +794,7 @@ struct Array(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
|
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
|
||||||
*/
|
*/
|
||||||
size_t insertAfter(R)(Range!T r, R el)
|
size_t insertAfter(R)(Range r, R el)
|
||||||
if (!isInfinite!R
|
if (!isInfinite!R
|
||||||
&& isInputRange!R
|
&& isInputRange!R
|
||||||
&& isImplicitlyConvertible!(ElementType!R, T))
|
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||||
@ -808,7 +814,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertAfter(size_t R)(Range!T r, T[R] el)
|
size_t insertAfter(size_t R)(Range r, T[R] el)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(r.container is &this);
|
assert(r.container is &this);
|
||||||
@ -821,7 +827,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertAfter(R)(Range!T r, auto ref R el)
|
size_t insertAfter(R)(Range r, auto ref R el)
|
||||||
if (isImplicitlyConvertible!(R, T))
|
if (isImplicitlyConvertible!(R, T))
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -848,7 +854,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertBefore(R)(Range!T r, R el)
|
size_t insertBefore(R)(Range r, R el)
|
||||||
if (!isInfinite!R
|
if (!isInfinite!R
|
||||||
&& isInputRange!R
|
&& isInputRange!R
|
||||||
&& isImplicitlyConvertible!(ElementType!R, T))
|
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||||
@ -860,11 +866,11 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
return insertAfter(Range!T(this, this.data, r.begin), el);
|
return insertAfter(.Range!T(this, this.data, r.begin), el);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertBefore(size_t R)(Range!T r, T[R] el)
|
size_t insertBefore(size_t R)(Range r, T[R] el)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(r.container is &this);
|
assert(r.container is &this);
|
||||||
@ -877,7 +883,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertBefore(R)(Range!T r, auto ref R el)
|
size_t insertBefore(R)(Range r, auto ref R el)
|
||||||
if (isImplicitlyConvertible!(R, T))
|
if (isImplicitlyConvertible!(R, T))
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -993,7 +999,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!T opIndexAssign(E : T)(auto ref E value)
|
Range opIndexAssign(E : T)(auto ref E value)
|
||||||
{
|
{
|
||||||
return opSliceAssign(value, 0, length);
|
return opSliceAssign(value, 0, length);
|
||||||
}
|
}
|
||||||
@ -1017,13 +1023,13 @@ struct Array(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_INLINECODE length == value.length).
|
* Precondition: $(D_INLINECODE length == value.length).
|
||||||
*/
|
*/
|
||||||
Range!T opIndexAssign(size_t R)(T[R] value)
|
Range opIndexAssign(size_t R)(T[R] value)
|
||||||
{
|
{
|
||||||
return opSliceAssign!R(value, 0, length);
|
return opSliceAssign!R(value, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!T opIndexAssign(Range!T value)
|
Range opIndexAssign(Range value)
|
||||||
{
|
{
|
||||||
return opSliceAssign(value, 0, length);
|
return opSliceAssign(value, 0, length);
|
||||||
}
|
}
|
||||||
@ -1066,13 +1072,13 @@ struct Array(T)
|
|||||||
* Returns: Random access range that iterates over elements of the array,
|
* Returns: Random access range that iterates over elements of the array,
|
||||||
* in forward order.
|
* in forward order.
|
||||||
*/
|
*/
|
||||||
Range!T opIndex() @trusted
|
Range opIndex() @trusted
|
||||||
{
|
{
|
||||||
return typeof(return)(this, this.data, this.data + length);
|
return typeof(return)(this, this.data, this.data + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!(const T) opIndex() const @trusted
|
ConstRange opIndex() const @trusted
|
||||||
{
|
{
|
||||||
return typeof(return)(this, this.data, this.data + length);
|
return typeof(return)(this, this.data, this.data + length);
|
||||||
}
|
}
|
||||||
@ -1105,13 +1111,13 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
bool opEquals()(const auto ref typeof(this) that) const @trusted
|
bool opEquals()(auto ref const typeof(this) that) const @trusted
|
||||||
{
|
{
|
||||||
return equal(this.data[0 .. length], that.data[0 .. that.length]);
|
return equal(this.data[0 .. length], that.data[0 .. that.length]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
bool opEquals(Range!T that)
|
bool opEquals(Range that)
|
||||||
{
|
{
|
||||||
return equal(opIndex(), that);
|
return equal(opIndex(), that);
|
||||||
}
|
}
|
||||||
@ -1126,8 +1132,8 @@ struct Array(T)
|
|||||||
* Returns: $(D_KEYWORD true) if the array and the range are equal,
|
* Returns: $(D_KEYWORD true) if the array and the range are equal,
|
||||||
* $(D_KEYWORD false) otherwise.
|
* $(D_KEYWORD false) otherwise.
|
||||||
*/
|
*/
|
||||||
bool opEquals(R)(Range!R that) const
|
bool opEquals(R)(R that) const
|
||||||
if (is(Unqual!R == T))
|
if (is(R == Range) || is(R == ConstRange))
|
||||||
{
|
{
|
||||||
return equal(opIndex(), that);
|
return equal(opIndex(), that);
|
||||||
}
|
}
|
||||||
@ -1216,7 +1222,7 @@ struct Array(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_INLINECODE i <= j && j <= length).
|
* Precondition: $(D_INLINECODE i <= j && j <= length).
|
||||||
*/
|
*/
|
||||||
Range!T opSlice(const size_t i, const size_t j) @trusted
|
Range opSlice(const size_t i, const size_t j) @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(i <= j);
|
assert(i <= j);
|
||||||
@ -1228,7 +1234,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!(const T) opSlice(const size_t i, const size_t j) const @trusted
|
ConstRange opSlice(const size_t i, const size_t j) const @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(i <= j);
|
assert(i <= j);
|
||||||
@ -1298,7 +1304,7 @@ struct Array(T)
|
|||||||
* Precondition: $(D_INLINECODE i <= j && j <= length
|
* Precondition: $(D_INLINECODE i <= j && j <= length
|
||||||
* && value.length == j - i)
|
* && value.length == j - i)
|
||||||
*/
|
*/
|
||||||
Range!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
|
Range opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
|
||||||
@trusted
|
@trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -1312,7 +1318,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!T opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
|
Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
|
||||||
@trusted
|
@trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -1326,7 +1332,7 @@ struct Array(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
Range!T opSliceAssign(Range!T value, const size_t i, const size_t j) @trusted
|
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(i <= j);
|
assert(i <= j);
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.container.entry;
|
module tanya.container.entry;
|
||||||
|
|
||||||
|
import std.traits;
|
||||||
|
import tanya.typecons;
|
||||||
|
|
||||||
package struct SEntry(T)
|
package struct SEntry(T)
|
||||||
{
|
{
|
||||||
// Item content.
|
// Item content.
|
||||||
@ -29,3 +32,80 @@ package struct DEntry(T)
|
|||||||
// Previous and next item.
|
// Previous and next item.
|
||||||
DEntry* next, prev;
|
DEntry* next, prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package struct HashEntry(K, V)
|
||||||
|
{
|
||||||
|
this(ref K key, ref V value)
|
||||||
|
{
|
||||||
|
this.pair = Pair!(K, V)(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair!(K, V) pair;
|
||||||
|
HashEntry* next;
|
||||||
|
}
|
||||||
|
|
||||||
|
package enum BucketStatus : byte
|
||||||
|
{
|
||||||
|
deleted = -1,
|
||||||
|
empty = 0,
|
||||||
|
used = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
package struct Bucket(T)
|
||||||
|
{
|
||||||
|
this(ref T content)
|
||||||
|
{
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property void content(ref T content)
|
||||||
|
{
|
||||||
|
this.content_ = content;
|
||||||
|
this.status = BucketStatus.used;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property ref inout(T) content() inout
|
||||||
|
{
|
||||||
|
return this.content_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref T content)
|
||||||
|
{
|
||||||
|
if (this.status == BucketStatus.used && this.content == content)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref T content) const
|
||||||
|
{
|
||||||
|
if (this.status == BucketStatus.used && this.content == content)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref typeof(this) that)
|
||||||
|
{
|
||||||
|
return this.content == that.content && this.status == that.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref typeof(this) that) const
|
||||||
|
{
|
||||||
|
return this.content == that.content && this.status == that.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove()
|
||||||
|
{
|
||||||
|
static if (hasElaborateDestructor!T)
|
||||||
|
{
|
||||||
|
destroy(this.content);
|
||||||
|
}
|
||||||
|
this.status = BucketStatus.deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
T content_;
|
||||||
|
BucketStatus status = BucketStatus.empty;
|
||||||
|
}
|
||||||
|
@ -93,6 +93,12 @@ struct SRange(E)
|
|||||||
*/
|
*/
|
||||||
struct SList(T)
|
struct SList(T)
|
||||||
{
|
{
|
||||||
|
/// The range types for $(D_PSYMBOL SList).
|
||||||
|
alias Range = SRange!T;
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
alias ConstRange = SRange!(const T);
|
||||||
|
|
||||||
private alias Entry = SEntry!T;
|
private alias Entry = SEntry!T;
|
||||||
|
|
||||||
// 0th element of the list.
|
// 0th element of the list.
|
||||||
@ -497,7 +503,7 @@ struct SList(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertBefore(SRange!T r, ref T el) @trusted
|
size_t insertBefore(Range r, ref T el) @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(checkRangeBelonging(r));
|
assert(checkRangeBelonging(r));
|
||||||
@ -530,7 +536,7 @@ struct SList(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||||
*/
|
*/
|
||||||
size_t insertBefore(size_t R)(SRange!T r, T[R] el)
|
size_t insertBefore(size_t R)(Range r, T[R] el)
|
||||||
{
|
{
|
||||||
return insertFront!(T[])(el[]);
|
return insertFront!(T[])(el[]);
|
||||||
}
|
}
|
||||||
@ -685,7 +691,7 @@ struct SList(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||||
*/
|
*/
|
||||||
SRange!T remove(SRange!T r)
|
Range remove(Range r)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(checkRangeBelonging(r));
|
assert(checkRangeBelonging(r));
|
||||||
@ -716,13 +722,13 @@ struct SList(T)
|
|||||||
* Returns: Range that iterates over all elements of the container, in
|
* Returns: Range that iterates over all elements of the container, in
|
||||||
* forward order.
|
* forward order.
|
||||||
*/
|
*/
|
||||||
SRange!T opIndex()
|
Range opIndex()
|
||||||
{
|
{
|
||||||
return typeof(return)(this.head);
|
return typeof(return)(this.head);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
SRange!(const T) opIndex() const
|
ConstRange opIndex() const
|
||||||
{
|
{
|
||||||
return typeof(return)(this.head);
|
return typeof(return)(this.head);
|
||||||
}
|
}
|
||||||
@ -966,6 +972,12 @@ struct DRange(E)
|
|||||||
*/
|
*/
|
||||||
struct DList(T)
|
struct DList(T)
|
||||||
{
|
{
|
||||||
|
/// The range types for $(D_PSYMBOL DList).
|
||||||
|
alias Range = DRange!T;
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
alias ConstRange = DRange!(const T);
|
||||||
|
|
||||||
private alias Entry = DEntry!T;
|
private alias Entry = DEntry!T;
|
||||||
|
|
||||||
// 0th and the last elements of the list.
|
// 0th and the last elements of the list.
|
||||||
@ -1536,7 +1548,7 @@ struct DList(T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
size_t insertBefore(DRange!T r, ref T el) @trusted
|
size_t insertBefore(Range r, ref T el) @trusted
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(checkRangeBelonging(r));
|
assert(checkRangeBelonging(r));
|
||||||
@ -1569,7 +1581,7 @@ struct DList(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||||
*/
|
*/
|
||||||
size_t insertBefore(size_t R)(DRange!T r, T[R] el)
|
size_t insertBefore(size_t R)(Range r, T[R] el)
|
||||||
{
|
{
|
||||||
return insertFront!(T[])(el[]);
|
return insertFront!(T[])(el[]);
|
||||||
}
|
}
|
||||||
@ -1728,7 +1740,7 @@ struct DList(T)
|
|||||||
*
|
*
|
||||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||||
*/
|
*/
|
||||||
DRange!T remove(DRange!T r)
|
Range remove(Range r)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(checkRangeBelonging(r));
|
assert(checkRangeBelonging(r));
|
||||||
@ -1768,13 +1780,13 @@ struct DList(T)
|
|||||||
* Returns: Range that iterates over all elements of the container, in
|
* Returns: Range that iterates over all elements of the container, in
|
||||||
* forward order.
|
* forward order.
|
||||||
*/
|
*/
|
||||||
DRange!T opIndex()
|
Range opIndex()
|
||||||
{
|
{
|
||||||
return typeof(return)(this.head, this.tail);
|
return typeof(return)(this.head, this.tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
DRange!(const T) opIndex() const
|
ConstRange opIndex() const
|
||||||
{
|
{
|
||||||
return typeof(return)(this.head, this.tail);
|
return typeof(return)(this.head, this.tail);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,29 @@ module tanya.container;
|
|||||||
|
|
||||||
public import tanya.container.array;
|
public import tanya.container.array;
|
||||||
public import tanya.container.buffer;
|
public import tanya.container.buffer;
|
||||||
|
public import tanya.container.set;
|
||||||
public import tanya.container.list;
|
public import tanya.container.list;
|
||||||
public import tanya.container.string;
|
public import tanya.container.string;
|
||||||
public import tanya.container.queue;
|
public import tanya.container.queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
|
||||||
|
* is full.
|
||||||
|
*/
|
||||||
|
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 @safe pure nothrow
|
||||||
|
{
|
||||||
|
super(msg, file, line, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
710
source/tanya/container/set.d
Normal file
710
source/tanya/container/set.d
Normal file
@ -0,0 +1,710 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module implements a $(D_PSYMBOL Set) container that stores unique
|
||||||
|
* values without any particular order.
|
||||||
|
*
|
||||||
|
* Copyright: Eugene Wissner 2017.
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
module tanya.container.set;
|
||||||
|
|
||||||
|
import std.algorithm.mutation;
|
||||||
|
import std.traits;
|
||||||
|
import tanya.container;
|
||||||
|
import tanya.container.entry;
|
||||||
|
import tanya.memory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* E = Element type.
|
||||||
|
*/
|
||||||
|
struct Range(E)
|
||||||
|
{
|
||||||
|
static if (isMutable!E)
|
||||||
|
{
|
||||||
|
private alias DataRange = Array!(Bucket!(Unqual!E)).Range;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
private alias DataRange = Array!(Bucket!(Unqual!E)).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 Range save()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property bool empty() const
|
||||||
|
{
|
||||||
|
return this.dataRange.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@property void popFront()
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(!this.dataRange.empty);
|
||||||
|
assert(this.dataRange.front.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
{
|
||||||
|
assert(this.dataRange.empty
|
||||||
|
|| this.dataRange.back.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dataRange.popFront();
|
||||||
|
}
|
||||||
|
while (!dataRange.empty && dataRange.front.status != BucketStatus.used);
|
||||||
|
}
|
||||||
|
|
||||||
|
@property void popBack()
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(!this.dataRange.empty);
|
||||||
|
assert(this.dataRange.back.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
{
|
||||||
|
assert(this.dataRange.empty
|
||||||
|
|| this.dataRange.back.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dataRange.popBack();
|
||||||
|
}
|
||||||
|
while (!dataRange.empty && dataRange.back.status != BucketStatus.used);
|
||||||
|
}
|
||||||
|
|
||||||
|
@property ref inout(E) front() inout
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(!this.dataRange.empty);
|
||||||
|
assert(this.dataRange.front.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
return dataRange.front.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property ref inout(E) back() inout
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(!this.dataRange.empty);
|
||||||
|
assert(this.dataRange.back.status == BucketStatus.used);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
return dataRange.back.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range opIndex()
|
||||||
|
{
|
||||||
|
return typeof(return)(this.dataRange[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Range!(const E) opIndex() const
|
||||||
|
{
|
||||||
|
return typeof(return)(this.dataRange[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set is a data structure that stores unique values without any particular
|
||||||
|
* order.
|
||||||
|
*
|
||||||
|
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
|
||||||
|
* are resolved with linear probing.
|
||||||
|
*
|
||||||
|
* Currently works only with integral types.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Element type.
|
||||||
|
*/
|
||||||
|
struct Set(T)
|
||||||
|
if (isIntegral!T || is(Unqual!T == bool))
|
||||||
|
{
|
||||||
|
/// The range types for $(D_PSYMBOL Set).
|
||||||
|
alias Range = .Range!T;
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
alias ConstRange = .Range!(const T);
|
||||||
|
|
||||||
|
invariant
|
||||||
|
{
|
||||||
|
assert(this.lengthIndex < primes.length);
|
||||||
|
assert(this.data.length == 0
|
||||||
|
|| this.data.length == primes[this.lengthIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* n = Minimum number of buckets.
|
||||||
|
* allocator = Allocator.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null).
|
||||||
|
*/
|
||||||
|
this(const size_t n, shared Allocator allocator = defaultAllocator)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this(allocator);
|
||||||
|
rehash(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
this(shared Allocator allocator)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this.data = typeof(this.data)(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto set = Set!int(defaultAllocator);
|
||||||
|
assert(set.capacity == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto set = Set!int(8);
|
||||||
|
assert(set.capacity == 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this $(D_PARAM Set) from another one.
|
||||||
|
*
|
||||||
|
* If $(D_PARAM init) is passed by reference, it will be copied.
|
||||||
|
* If $(D_PARAM init) is passed by value, it will be moved.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* S = Source set type.
|
||||||
|
* init = Source set.
|
||||||
|
* allocator = Allocator.
|
||||||
|
*/
|
||||||
|
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
|
||||||
|
if (is(Unqual!S == Set))
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this.data = typeof(this.data)(init.data, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
this(S)(S init, shared Allocator allocator = defaultAllocator)
|
||||||
|
if (is(S == Set))
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this.data = typeof(this.data)(move(init.data), allocator);
|
||||||
|
this.lengthIndex = init.lengthIndex;
|
||||||
|
init.lengthIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns another set.
|
||||||
|
*
|
||||||
|
* If $(D_PARAM that) is passed by reference, it will be copied.
|
||||||
|
* If $(D_PARAM that) is passed by value, it will be moved.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* S = Content type.
|
||||||
|
* that = The value should be assigned.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD this).
|
||||||
|
*/
|
||||||
|
ref typeof(this) opAssign(S)(ref S that)
|
||||||
|
if (is(Unqual!S == Set))
|
||||||
|
{
|
||||||
|
this.data = that.data;
|
||||||
|
this.lengthIndex = that.lengthIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ref typeof(this) opAssign(S)(S that) @trusted
|
||||||
|
if (is(S == Set))
|
||||||
|
{
|
||||||
|
swap(this.data, that.data);
|
||||||
|
swap(this.lengthIndex, that.lengthIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Used allocator.
|
||||||
|
*
|
||||||
|
* Postcondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
@property shared(Allocator) allocator() const
|
||||||
|
out (allocator)
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
return cast(shared Allocator) this.data.allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum amount of elements this $(D_PSYMBOL Set) can hold without
|
||||||
|
* resizing and rehashing. Note that it doesn't mean that the
|
||||||
|
* $(D_PSYMBOL Set) will hold $(I exactly) $(D_PSYMBOL capacity) elements.
|
||||||
|
* $(D_PSYMBOL capacity) tells the size of the container under a best-case
|
||||||
|
* distribution of elements.
|
||||||
|
*
|
||||||
|
* Returns: $(D_PSYMBOL Set) capacity.
|
||||||
|
*/
|
||||||
|
@property size_t capacity() const
|
||||||
|
{
|
||||||
|
return this.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
assert(set.capacity == 0);
|
||||||
|
|
||||||
|
set.insert(8);
|
||||||
|
assert(set.capacity == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over the $(D_PSYMBOL Set) and counts the elements.
|
||||||
|
*
|
||||||
|
* Returns: Count of elements within the $(D_PSYMBOL Set).
|
||||||
|
*/
|
||||||
|
@property size_t length() const
|
||||||
|
{
|
||||||
|
size_t count;
|
||||||
|
foreach (ref e; this.data[])
|
||||||
|
{
|
||||||
|
if (e.status == BucketStatus.used)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
assert(set.length == 0);
|
||||||
|
|
||||||
|
set.insert(8);
|
||||||
|
assert(set.length == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static const size_t[41] primes = [
|
||||||
|
3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521,
|
||||||
|
769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593,
|
||||||
|
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
|
||||||
|
12582917, 25165843, 139022417, 282312799, 573292817, 1164186217,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// The maximum number of buckets the container can have.
|
||||||
|
enum size_t maxBucketCount = primes[$ - 1];
|
||||||
|
|
||||||
|
static private size_t calculateHash(ref T value)
|
||||||
|
{
|
||||||
|
static if (isIntegral!T || isSomeChar!T || is(T == bool))
|
||||||
|
{
|
||||||
|
return (cast(size_t) value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private size_t locateBucket(ref const DataType buckets, size_t hash)
|
||||||
|
{
|
||||||
|
return hash % buckets.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum InsertStatus : byte
|
||||||
|
{
|
||||||
|
found = -1,
|
||||||
|
failed = 0,
|
||||||
|
added = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inserts the value in an empty or deleted bucket. If the value is
|
||||||
|
* already in there, does nothing and returns InsertStatus.found. If the
|
||||||
|
* hash array is full returns InsertStatus.failed.
|
||||||
|
*/
|
||||||
|
private InsertStatus insertInUnusedBucket(ref T value)
|
||||||
|
{
|
||||||
|
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||||
|
|
||||||
|
foreach (ref e; this.data[bucketPosition .. $])
|
||||||
|
{
|
||||||
|
if (e == value) // Already in the set.
|
||||||
|
{
|
||||||
|
return InsertStatus.found;
|
||||||
|
}
|
||||||
|
else if (e.status != BucketStatus.used) // Insert the value.
|
||||||
|
{
|
||||||
|
e.content = value;
|
||||||
|
return InsertStatus.added;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InsertStatus.failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new element.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* value = Element value.
|
||||||
|
*
|
||||||
|
* Returns: Amount of new elements inserted.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL HashContainerFullException) if the insertion failed.
|
||||||
|
*/
|
||||||
|
size_t insert(T value)
|
||||||
|
{
|
||||||
|
if (this.data.length == 0)
|
||||||
|
{
|
||||||
|
this.data = DataType(primes[0], allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertStatus status = insertInUnusedBucket(value);
|
||||||
|
for (; !status; status = insertInUnusedBucket(value))
|
||||||
|
{
|
||||||
|
if ((this.primes.length - 1) == this.lengthIndex)
|
||||||
|
{
|
||||||
|
throw make!HashContainerFullException(defaultAllocator,
|
||||||
|
"Set is full");
|
||||||
|
}
|
||||||
|
rehashToSize(this.lengthIndex + 1);
|
||||||
|
}
|
||||||
|
return status == InsertStatus.added;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
assert(8 !in set);
|
||||||
|
|
||||||
|
assert(set.insert(8) == 1);
|
||||||
|
assert(set.length == 1);
|
||||||
|
assert(8 in set);
|
||||||
|
|
||||||
|
assert(set.insert(8) == 0);
|
||||||
|
assert(set.length == 1);
|
||||||
|
assert(8 in set);
|
||||||
|
|
||||||
|
assert(set.remove(8));
|
||||||
|
assert(set.insert(8) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an element.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* value = Element value.
|
||||||
|
*
|
||||||
|
* Returns: Number of elements removed, which is in the container with
|
||||||
|
* unique values `1` if an element existed, and `0` otherwise.
|
||||||
|
*/
|
||||||
|
size_t remove(T value)
|
||||||
|
{
|
||||||
|
if (this.data.length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||||
|
foreach (ref e; this.data[bucketPosition .. $])
|
||||||
|
{
|
||||||
|
if (e == value) // Found.
|
||||||
|
{
|
||||||
|
e.remove();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (e.status == BucketStatus.empty)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
assert(8 !in set);
|
||||||
|
|
||||||
|
set.insert(8);
|
||||||
|
assert(8 in set);
|
||||||
|
|
||||||
|
assert(set.remove(8) == 1);
|
||||||
|
assert(set.remove(8) == 0);
|
||||||
|
assert(8 !in set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_KEYWORD in) operator.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* 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 T value)
|
||||||
|
{
|
||||||
|
if (this.data.length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||||
|
foreach (ref e; this.data[bucketPosition .. $])
|
||||||
|
{
|
||||||
|
if (e == value) // Found.
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (e.status == BucketStatus.empty)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
bool opBinaryRight(string op : "in")(auto ref const T value) const
|
||||||
|
{
|
||||||
|
if (this.data.length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bucketPosition = locateBucket(this.data, calculateHash(value));
|
||||||
|
foreach (ref e; this.data[bucketPosition .. $])
|
||||||
|
{
|
||||||
|
if (e.status == BucketStatus.used && e.content == value) // Found.
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (e.status == BucketStatus.empty)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
|
||||||
|
assert(5 !in set);
|
||||||
|
set.insert(5);
|
||||||
|
assert(5 in set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Rehashing is automatically performed whenever the container needs space
|
||||||
|
* to insert new elements.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* n = Minimum number of buckets.
|
||||||
|
*/
|
||||||
|
void rehash(const size_t n)
|
||||||
|
{
|
||||||
|
size_t lengthIndex;
|
||||||
|
for (; lengthIndex < primes.length; ++lengthIndex)
|
||||||
|
{
|
||||||
|
if (primes[lengthIndex] >= n)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rehashToSize(lengthIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes an index in the primes array.
|
||||||
|
private void rehashToSize(const size_t n)
|
||||||
|
{
|
||||||
|
auto storage = DataType(primes[n], allocator);
|
||||||
|
DataLoop: foreach (ref e1; this.data[])
|
||||||
|
{
|
||||||
|
if (e1.status == BucketStatus.used)
|
||||||
|
{
|
||||||
|
auto bucketPosition = locateBucket(storage,
|
||||||
|
calculateHash(e1.content));
|
||||||
|
|
||||||
|
foreach (ref e2; storage[bucketPosition .. $])
|
||||||
|
{
|
||||||
|
if (e2.status != BucketStatus.used) // Insert the value.
|
||||||
|
{
|
||||||
|
e2.content = e1.content;
|
||||||
|
continue DataLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // Rehashing failed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
move(storage, this.data);
|
||||||
|
this.lengthIndex = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
|
||||||
|
* elements.
|
||||||
|
*/
|
||||||
|
Range opIndex()
|
||||||
|
{
|
||||||
|
return typeof(return)(this.data[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ConstRange opIndex() const
|
||||||
|
{
|
||||||
|
return typeof(return)(this.data[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
assert(set[].empty);
|
||||||
|
|
||||||
|
set.insert(8);
|
||||||
|
assert(!set[].empty);
|
||||||
|
assert(set[].front == 8);
|
||||||
|
assert(set[].back == 8);
|
||||||
|
|
||||||
|
set.remove(8);
|
||||||
|
assert(set[].empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private alias DataType = Array!(Bucket!T);
|
||||||
|
private DataType data;
|
||||||
|
private size_t lengthIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic insertion logic.
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
Set!int set;
|
||||||
|
|
||||||
|
assert(set.insert(5) == 1);
|
||||||
|
assert(set.data[0].status == BucketStatus.empty);
|
||||||
|
assert(set.data[1].status == BucketStatus.empty);
|
||||||
|
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
|
||||||
|
assert(set.data.length == 3);
|
||||||
|
|
||||||
|
assert(set.insert(5) == 0);
|
||||||
|
assert(set.data[0].status == BucketStatus.empty);
|
||||||
|
assert(set.data[1].status == BucketStatus.empty);
|
||||||
|
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
|
||||||
|
assert(set.data.length == 3);
|
||||||
|
|
||||||
|
assert(set.insert(9) == 1);
|
||||||
|
assert(set.data[0].content == 9 && set.data[0].status == BucketStatus.used);
|
||||||
|
assert(set.data[1].status == BucketStatus.empty);
|
||||||
|
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
|
||||||
|
assert(set.data.length == 3);
|
||||||
|
|
||||||
|
assert(set.insert(7) == 1);
|
||||||
|
assert(set.insert(8) == 1);
|
||||||
|
assert(set.data[0].content == 7);
|
||||||
|
assert(set.data[1].content == 8);
|
||||||
|
assert(set.data[2].content == 9);
|
||||||
|
assert(set.data[3].status == BucketStatus.empty);
|
||||||
|
assert(set.data[5].content == 5);
|
||||||
|
assert(set.data.length == 7);
|
||||||
|
|
||||||
|
assert(set.insert(16) == 1);
|
||||||
|
assert(set.data[2].content == 9);
|
||||||
|
assert(set.data[3].content == 16);
|
||||||
|
assert(set.data[4].status == BucketStatus.empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static checks.
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
import std.range.primitives;
|
||||||
|
|
||||||
|
static assert(isBidirectionalRange!(Set!int.ConstRange));
|
||||||
|
static assert(isBidirectionalRange!(Set!int.Range));
|
||||||
|
|
||||||
|
static assert(!isInfinite!(Set!int.Range));
|
||||||
|
static assert(!hasLength!(Set!int.Range));
|
||||||
|
|
||||||
|
static assert(is(Set!uint));
|
||||||
|
static assert(is(Set!long));
|
||||||
|
static assert(is(Set!ulong));
|
||||||
|
static assert(is(Set!short));
|
||||||
|
static assert(is(Set!ushort));
|
||||||
|
static assert(is(Set!bool));
|
||||||
|
}
|
@ -199,12 +199,7 @@ struct ByCodeUnit(E)
|
|||||||
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
return typeof(return)(*this.container, this.begin + i, this.begin + j);
|
||||||
}
|
}
|
||||||
|
|
||||||
const(E)[] toString() const @trusted
|
inout(E)[] get() inout @trusted
|
||||||
{
|
|
||||||
return this.begin[0 .. length];
|
|
||||||
}
|
|
||||||
|
|
||||||
E[] toString() @trusted
|
|
||||||
{
|
{
|
||||||
return this.begin[0 .. length];
|
return this.begin[0 .. length];
|
||||||
}
|
}
|
||||||
@ -948,13 +943,7 @@ struct String
|
|||||||
*
|
*
|
||||||
* Returns: The array representing the string.
|
* Returns: The array representing the string.
|
||||||
*/
|
*/
|
||||||
const(char)[] toString() const pure nothrow @trusted @nogc
|
inout(char)[] get() inout pure nothrow @trusted @nogc
|
||||||
{
|
|
||||||
return this.data[0 .. this.length_];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ditto.
|
|
||||||
char[] toString() pure nothrow @trusted @nogc
|
|
||||||
{
|
{
|
||||||
return this.data[0 .. this.length_];
|
return this.data[0 .. this.length_];
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Socket programming.
|
* Low-level socket programming.
|
||||||
*
|
*
|
||||||
* Copyright: Eugene Wissner 2016-2017.
|
* Copyright: Eugene Wissner 2016-2017.
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||||
@ -12,15 +12,16 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.network.socket;
|
module tanya.network.socket;
|
||||||
|
|
||||||
import tanya.memory;
|
|
||||||
import core.stdc.errno;
|
import core.stdc.errno;
|
||||||
import core.time;
|
import core.time;
|
||||||
import std.algorithm.comparison;
|
import std.algorithm.comparison;
|
||||||
import std.algorithm.searching;
|
public import std.socket : SocketOptionLevel, SocketOption;
|
||||||
public import std.socket : socket_t, Linger, SocketOptionLevel, SocketOption,
|
|
||||||
SocketType, AddressFamily, AddressInfo;
|
|
||||||
import std.traits;
|
import std.traits;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
|
import tanya.memory;
|
||||||
|
|
||||||
|
/// Value returned by socket operations on error.
|
||||||
|
enum int socketError = -1;
|
||||||
|
|
||||||
version (Posix)
|
version (Posix)
|
||||||
{
|
{
|
||||||
@ -32,7 +33,12 @@ version (Posix)
|
|||||||
import core.sys.posix.sys.time;
|
import core.sys.posix.sys.time;
|
||||||
import core.sys.posix.unistd;
|
import core.sys.posix.unistd;
|
||||||
|
|
||||||
private enum SOCKET_ERROR = -1;
|
enum SocketType : int
|
||||||
|
{
|
||||||
|
init = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
private alias LingerField = int;
|
||||||
}
|
}
|
||||||
else version (Windows)
|
else version (Windows)
|
||||||
{
|
{
|
||||||
@ -43,16 +49,23 @@ else version (Windows)
|
|||||||
import core.sys.windows.windef;
|
import core.sys.windows.windef;
|
||||||
import core.sys.windows.winsock2;
|
import core.sys.windows.winsock2;
|
||||||
|
|
||||||
|
enum SocketType : size_t
|
||||||
|
{
|
||||||
|
init = ~0,
|
||||||
|
}
|
||||||
|
|
||||||
|
private alias LingerField = ushort;
|
||||||
|
|
||||||
enum : uint
|
enum : uint
|
||||||
{
|
{
|
||||||
IOC_UNIX = 0x00000000,
|
IOC_UNIX = 0x00000000,
|
||||||
IOC_WS2 = 0x08000000,
|
IOC_WS2 = 0x08000000,
|
||||||
IOC_PROTOCOL = 0x10000000,
|
IOC_PROTOCOL = 0x10000000,
|
||||||
IOC_VOID = 0x20000000, /// No parameters.
|
IOC_VOID = 0x20000000, // No parameters.
|
||||||
IOC_OUT = 0x40000000, /// Copy parameters back.
|
IOC_OUT = 0x40000000, // Copy parameters back.
|
||||||
IOC_IN = 0x80000000, /// Copy parameters into.
|
IOC_IN = 0x80000000, // Copy parameters into.
|
||||||
IOC_VENDOR = 0x18000000,
|
IOC_VENDOR = 0x18000000,
|
||||||
IOC_INOUT = (IOC_IN | IOC_OUT), /// Copy parameter into and get back.
|
IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back.
|
||||||
}
|
}
|
||||||
|
|
||||||
template _WSAIO(int x, int y)
|
template _WSAIO(int x, int y)
|
||||||
@ -183,19 +196,26 @@ else version (Windows)
|
|||||||
LPINT,
|
LPINT,
|
||||||
SOCKADDR**,
|
SOCKADDR**,
|
||||||
LPINT);
|
LPINT);
|
||||||
const GUID WSAID_GETACCEPTEXSOCKADDRS = {0xb5367df2,0xcbac,0x11cf,[0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]};
|
const GUID WSAID_GETACCEPTEXSOCKADDRS = {
|
||||||
|
0xb5367df2, 0xcbac, 0x11cf,
|
||||||
|
[ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 ],
|
||||||
|
};
|
||||||
|
|
||||||
struct WSABUF {
|
struct WSABUF
|
||||||
|
{
|
||||||
ULONG len;
|
ULONG len;
|
||||||
CHAR* buf;
|
CHAR* buf;
|
||||||
}
|
}
|
||||||
alias WSABUF* LPWSABUF;
|
alias WSABUF* LPWSABUF;
|
||||||
|
|
||||||
struct WSAOVERLAPPED {
|
struct WSAOVERLAPPED
|
||||||
|
{
|
||||||
ULONG_PTR Internal;
|
ULONG_PTR Internal;
|
||||||
ULONG_PTR InternalHigh;
|
ULONG_PTR InternalHigh;
|
||||||
union {
|
union
|
||||||
struct {
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
DWORD Offset;
|
DWORD Offset;
|
||||||
DWORD OffsetHigh;
|
DWORD OffsetHigh;
|
||||||
}
|
}
|
||||||
@ -219,36 +239,13 @@ else version (Windows)
|
|||||||
private WSABUF buffer;
|
private WSABUF buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Socket returned if a connection has been established.
|
|
||||||
*/
|
|
||||||
class OverlappedConnectedSocket : ConnectedSocket
|
class OverlappedConnectedSocket : ConnectedSocket
|
||||||
{
|
{
|
||||||
/**
|
this(SocketType handle, AddressFamily af) @nogc
|
||||||
* Create a socket.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* handle = Socket handle.
|
|
||||||
* af = Address family.
|
|
||||||
*/
|
|
||||||
this(socket_t handle, AddressFamily af) @nogc
|
|
||||||
{
|
{
|
||||||
super(handle, af);
|
super(handle, af);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Begins to asynchronously receive data from a connected socket.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* buffer = Storage location for the received data.
|
|
||||||
* flags = Flags.
|
|
||||||
* overlapped = Unique operation identifier.
|
|
||||||
*
|
|
||||||
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
|
||||||
* $(D_KEYWORD false) otherwise.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
|
||||||
*/
|
|
||||||
bool beginReceive(ubyte[] buffer,
|
bool beginReceive(ubyte[] buffer,
|
||||||
SocketState overlapped,
|
SocketState overlapped,
|
||||||
Flags flags = Flags(Flag.none)) @nogc @trusted
|
Flags flags = Flags(Flag.none)) @nogc @trusted
|
||||||
@ -268,23 +265,13 @@ else version (Windows)
|
|||||||
&overlapped.overlapped,
|
&overlapped.overlapped,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (result == SOCKET_ERROR && !wouldHaveBlocked)
|
if (result == socketError && !wouldHaveBlocked)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to receive");
|
throw defaultAllocator.make!SocketException("Unable to receive");
|
||||||
}
|
}
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends a pending asynchronous read.
|
|
||||||
*
|
|
||||||
* Params
|
|
||||||
* overlapped = Unique operation identifier.
|
|
||||||
*
|
|
||||||
* Returns: Number of bytes received.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
|
||||||
*/
|
|
||||||
int endReceive(SocketState overlapped) @nogc @trusted
|
int endReceive(SocketState overlapped) @nogc @trusted
|
||||||
out (count)
|
out (count)
|
||||||
{
|
{
|
||||||
@ -309,19 +296,6 @@ else version (Windows)
|
|||||||
return lpNumber;
|
return lpNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends data asynchronously to a connected socket.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* buffer = Data to be sent.
|
|
||||||
* flags = Flags.
|
|
||||||
* overlapped = Unique operation identifier.
|
|
||||||
*
|
|
||||||
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
|
||||||
* $(D_KEYWORD false) otherwise.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) if unable to send.
|
|
||||||
*/
|
|
||||||
bool beginSend(ubyte[] buffer,
|
bool beginSend(ubyte[] buffer,
|
||||||
SocketState overlapped,
|
SocketState overlapped,
|
||||||
Flags flags = Flags(Flag.none)) @nogc @trusted
|
Flags flags = Flags(Flag.none)) @nogc @trusted
|
||||||
@ -339,7 +313,7 @@ else version (Windows)
|
|||||||
&overlapped.overlapped,
|
&overlapped.overlapped,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (result == SOCKET_ERROR && !wouldHaveBlocked)
|
if (result == socketError && !wouldHaveBlocked)
|
||||||
{
|
{
|
||||||
disconnected_ = true;
|
disconnected_ = true;
|
||||||
throw defaultAllocator.make!SocketException("Unable to send");
|
throw defaultAllocator.make!SocketException("Unable to send");
|
||||||
@ -347,16 +321,6 @@ else version (Windows)
|
|||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends a pending asynchronous send.
|
|
||||||
*
|
|
||||||
* Params
|
|
||||||
* overlapped = Unique operation identifier.
|
|
||||||
*
|
|
||||||
* Returns: Number of bytes sent.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
|
||||||
*/
|
|
||||||
int endSend(SocketState overlapped) @nogc @trusted
|
int endSend(SocketState overlapped) @nogc @trusted
|
||||||
out (count)
|
out (count)
|
||||||
{
|
{
|
||||||
@ -380,17 +344,9 @@ else version (Windows)
|
|||||||
|
|
||||||
class OverlappedStreamSocket : StreamSocket
|
class OverlappedStreamSocket : StreamSocket
|
||||||
{
|
{
|
||||||
/// Accept extension function pointer.
|
// Accept extension function pointer.
|
||||||
package LPFN_ACCEPTEX acceptExtension;
|
package LPFN_ACCEPTEX acceptExtension;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a socket.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* af = Address family.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) on errors.
|
|
||||||
*/
|
|
||||||
this(AddressFamily af) @nogc @trusted
|
this(AddressFamily af) @nogc @trusted
|
||||||
{
|
{
|
||||||
super(af);
|
super(af);
|
||||||
@ -412,28 +368,17 @@ else version (Windows)
|
|||||||
&dwBytes,
|
&dwBytes,
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL);
|
||||||
if (!result == SOCKET_ERROR)
|
if (!result == socketError)
|
||||||
{
|
{
|
||||||
throw make!SocketException(defaultAllocator,
|
throw make!SocketException(defaultAllocator,
|
||||||
"Unable to retrieve an accept extension function pointer");
|
"Unable to retrieve an accept extension function pointer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Begins an asynchronous operation to accept an incoming connection attempt.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* overlapped = Unique operation identifier.
|
|
||||||
*
|
|
||||||
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
|
||||||
* $(D_KEYWORD false) otherwise.
|
|
||||||
*
|
|
||||||
* Throws: $(D_PSYMBOL SocketException) on accept errors.
|
|
||||||
*/
|
|
||||||
bool beginAccept(SocketState overlapped) @nogc @trusted
|
bool beginAccept(SocketState overlapped) @nogc @trusted
|
||||||
{
|
{
|
||||||
auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0);
|
auto socket = cast(SocketType) socket(addressFamily, 1, 0);
|
||||||
if (socket == socket_t.init)
|
if (socket == SocketType.init)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to create socket");
|
throw defaultAllocator.make!SocketException("Unable to create socket");
|
||||||
}
|
}
|
||||||
@ -445,7 +390,7 @@ else version (Windows)
|
|||||||
overlapped.handle = cast(HANDLE) socket;
|
overlapped.handle = cast(HANDLE) socket;
|
||||||
overlapped.event = OverlappedSocketEvent.accept;
|
overlapped.event = OverlappedSocketEvent.accept;
|
||||||
|
|
||||||
immutable len = (sockaddr_in.sizeof + 16) * 2;
|
const len = (sockaddr_in.sizeof + 16) * 2;
|
||||||
overlapped.buffer.len = len;
|
overlapped.buffer.len = len;
|
||||||
overlapped.buffer.buf = cast(char*) defaultAllocator.allocate(len).ptr;
|
overlapped.buffer.buf = cast(char*) defaultAllocator.allocate(len).ptr;
|
||||||
|
|
||||||
@ -465,6 +410,139 @@ else version (Windows)
|
|||||||
return result == TRUE;
|
return result == TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OverlappedConnectedSocket endAccept(SocketState overlapped)
|
||||||
|
@nogc @trusted
|
||||||
|
{
|
||||||
|
scope (exit)
|
||||||
|
{
|
||||||
|
defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]);
|
||||||
|
}
|
||||||
|
auto socket = make!OverlappedConnectedSocket(defaultAllocator,
|
||||||
|
cast(SocketType) overlapped.handle,
|
||||||
|
addressFamily);
|
||||||
|
scope (failure)
|
||||||
|
{
|
||||||
|
defaultAllocator.dispose(socket);
|
||||||
|
}
|
||||||
|
socket.setOption(SocketOptionLevel.SOCKET,
|
||||||
|
cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
|
cast(size_t) handle);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else version (D_Ddoc)
|
||||||
|
{
|
||||||
|
/// Native socket representation type.
|
||||||
|
enum SocketType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket returned if a connection has been established.
|
||||||
|
*
|
||||||
|
* Note: Available only on Windows.
|
||||||
|
*/
|
||||||
|
class OverlappedConnectedSocket : ConnectedSocket
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a socket.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* handle = Socket handle.
|
||||||
|
* af = Address family.
|
||||||
|
*/
|
||||||
|
this(SocketType handle, AddressFamily af) @nogc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins to asynchronously receive data from a connected socket.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* buffer = Storage location for the received data.
|
||||||
|
* flags = Flags.
|
||||||
|
* overlapped = Unique operation identifier.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
||||||
|
*/
|
||||||
|
bool beginReceive(ubyte[] buffer,
|
||||||
|
SocketState overlapped,
|
||||||
|
Flags flags = Flags(Flag.none)) @nogc @trusted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends a pending asynchronous read.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* overlapped = Unique operation identifier.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes received.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
||||||
|
*
|
||||||
|
* Postcondition: $(D_INLINECODE result >= 0).
|
||||||
|
*/
|
||||||
|
int endReceive(SocketState overlapped) @nogc @trusted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends data asynchronously to a connected socket.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* buffer = Data to be sent.
|
||||||
|
* flags = Flags.
|
||||||
|
* overlapped = Unique operation identifier.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) if unable to send.
|
||||||
|
*/
|
||||||
|
bool beginSend(ubyte[] buffer,
|
||||||
|
SocketState overlapped,
|
||||||
|
Flags flags = Flags(Flag.none)) @nogc @trusted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends a pending asynchronous send.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* overlapped = Unique operation identifier.
|
||||||
|
*
|
||||||
|
* Returns: Number of bytes sent.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
|
||||||
|
*
|
||||||
|
* Postcondition: $(D_INLINECODE result >= 0).
|
||||||
|
*/
|
||||||
|
int endSend(SocketState overlapped) @nogc @trusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windows stream socket overlapped I/O.
|
||||||
|
*/
|
||||||
|
class OverlappedStreamSocket : StreamSocket
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a socket.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* af = Address family.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) on errors.
|
||||||
|
*/
|
||||||
|
this(AddressFamily af) @nogc @trusted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins an asynchronous operation to accept an incoming connection attempt.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* overlapped = Unique operation identifier.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
|
||||||
|
* $(D_KEYWORD false) otherwise.
|
||||||
|
*
|
||||||
|
* Throws: $(D_PSYMBOL SocketException) on accept errors.
|
||||||
|
*/
|
||||||
|
bool beginAccept(SocketState overlapped) @nogc @trusted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously accepts an incoming connection attempt and creates a
|
* Asynchronously accepts an incoming connection attempt and creates a
|
||||||
* new socket to handle remote host communication.
|
* new socket to handle remote host communication.
|
||||||
@ -476,24 +554,116 @@ else version (Windows)
|
|||||||
*
|
*
|
||||||
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
|
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
|
||||||
*/
|
*/
|
||||||
OverlappedConnectedSocket endAccept(SocketState overlapped) @nogc @trusted
|
OverlappedConnectedSocket endAccept(SocketState overlapped)
|
||||||
|
@nogc @trusted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket option that specifies what should happen when the socket that
|
||||||
|
* promises reliable delivery still has untransmitted messages when
|
||||||
|
* it is closed.
|
||||||
|
*/
|
||||||
|
struct Linger
|
||||||
|
{
|
||||||
|
/// If nonzero, $(D_PSYMBOL close) and $(D_PSYMBOL shutdown) block until
|
||||||
|
/// the data are transmitted or the timeout period has expired.
|
||||||
|
LingerField l_onoff;
|
||||||
|
|
||||||
|
/// Time, in seconds to wait before any buffered data to be sent is
|
||||||
|
/// discarded.
|
||||||
|
LingerField l_linger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If $(D_PARAM timeout) is `0`, linger is disabled, otherwise enables the
|
||||||
|
* linger and sets the timeout.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* timeout = Timeout, in seconds.
|
||||||
|
*/
|
||||||
|
this(const ushort timeout)
|
||||||
|
{
|
||||||
|
time = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
{
|
{
|
||||||
scope (exit)
|
auto linger = Linger(5);
|
||||||
{
|
assert(linger.enabled);
|
||||||
defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]);
|
assert(linger.time == 5);
|
||||||
}
|
|
||||||
auto socket = make!OverlappedConnectedSocket(defaultAllocator,
|
|
||||||
cast(socket_t) overlapped.handle,
|
|
||||||
addressFamily);
|
|
||||||
scope (failure)
|
|
||||||
{
|
|
||||||
defaultAllocator.dispose(socket);
|
|
||||||
}
|
|
||||||
socket.setOption(SocketOptionLevel.SOCKET,
|
|
||||||
cast(SocketOption) SO_UPDATE_ACCEPT_CONTEXT,
|
|
||||||
cast(size_t) handle);
|
|
||||||
return socket;
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto linger = Linger(0);
|
||||||
|
assert(!linger.enabled);
|
||||||
|
}
|
||||||
|
{ // Default constructor.
|
||||||
|
Linger linger;
|
||||||
|
assert(!linger.enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System dependent constructor.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* l_onoff = $(D_PSYMBOL l_onoff) value.
|
||||||
|
* l_linger = $(D_PSYMBOL l_linger) value.
|
||||||
|
*/
|
||||||
|
this(LingerField l_onoff, LingerField l_linger)
|
||||||
|
{
|
||||||
|
this.l_onoff = l_onoff;
|
||||||
|
this.l_linger = l_linger;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
auto linger = Linger(1, 5);
|
||||||
|
assert(linger.l_onoff == 1);
|
||||||
|
assert(linger.l_linger == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* value = Whether to linger after the socket is closed.
|
||||||
|
*
|
||||||
|
* See_Also: $(D_PSYMBOL time).
|
||||||
|
*/
|
||||||
|
@property enabled(const bool value) pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
this.l_onoff = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Whether to linger after the socket is closed.
|
||||||
|
*/
|
||||||
|
@property bool enabled() const pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
return this.l_onoff != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Timeout period, in seconds, to wait before closing the socket
|
||||||
|
* if the $(D_PSYMBOL Linger) is $(D_PSYMBOL enabled).
|
||||||
|
*/
|
||||||
|
@property ushort time() const pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
return this.l_linger & ushort.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets timeout period, to wait before closing the socket if the
|
||||||
|
* $(D_PSYMBOL Linger) is $(D_PSYMBOL enabled), ignored otherwise.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* timeout = Timeout period, in seconds.
|
||||||
|
*/
|
||||||
|
@property void time(const ushort timeout) pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
this.l_onoff = timeout > 0;
|
||||||
|
this.l_linger = timeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +695,7 @@ else version (DragonFlyBSD)
|
|||||||
|
|
||||||
version (MacBSD)
|
version (MacBSD)
|
||||||
{
|
{
|
||||||
enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted.
|
enum ESOCKTNOSUPPORT = 44; // Socket type not suppoted.
|
||||||
}
|
}
|
||||||
|
|
||||||
private immutable
|
private immutable
|
||||||
@ -552,12 +722,32 @@ shared static this()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects
|
||||||
|
* the protocol family which will be used for communication.
|
||||||
|
*/
|
||||||
|
enum AddressFamily : int
|
||||||
|
{
|
||||||
|
unspec = 0, /// Unspecified.
|
||||||
|
local = 1, /// Local to host (pipes and file-domain).
|
||||||
|
unix = local, /// POSIX name for PF_LOCAL.
|
||||||
|
inet = 2, /// IP protocol family.
|
||||||
|
ax25 = 3, /// Amateur Radio AX.25.
|
||||||
|
ipx = 4, /// Novell Internet Protocol.
|
||||||
|
appletalk = 5, /// Appletalk DDP.
|
||||||
|
netrom = 6, /// Amateur radio NetROM.
|
||||||
|
bridge = 7, /// Multiprotocol bridge.
|
||||||
|
atmpvc = 8, /// ATM PVCs.
|
||||||
|
x25 = 9, /// Reserved for X.25 project.
|
||||||
|
inet6 = 10, /// IP version 6.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error codes for $(D_PSYMBOL Socket).
|
* Error codes for $(D_PSYMBOL Socket).
|
||||||
*/
|
*/
|
||||||
enum SocketError : int
|
enum SocketError : int
|
||||||
{
|
{
|
||||||
/// Unknown error
|
/// Unknown error.
|
||||||
unknown = 0,
|
unknown = 0,
|
||||||
/// Firewall rules forbid connection.
|
/// Firewall rules forbid connection.
|
||||||
accessDenied = EPERM,
|
accessDenied = EPERM,
|
||||||
@ -587,12 +777,12 @@ enum SocketError : int
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
|
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
|
||||||
* returns -1 or $(D_PSYMBOL SOCKET_ERROR) and sets $(D_PSYMBOL errno),
|
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
|
||||||
* because $(D_PSYMBOL SocketException) relies on the $(D_PSYMBOL errno) value.
|
* $(D_PSYMBOL SocketException) relies on the $(D_PSYMBOL errno) value.
|
||||||
*/
|
*/
|
||||||
class SocketException : Exception
|
class SocketException : Exception
|
||||||
{
|
{
|
||||||
immutable SocketError error = SocketError.unknown;
|
const SocketError error = SocketError.unknown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params:
|
* Params:
|
||||||
@ -680,16 +870,16 @@ abstract class Socket
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Socket handle.
|
/// Socket handle.
|
||||||
protected socket_t handle_;
|
protected SocketType handle_;
|
||||||
|
|
||||||
/// Address family.
|
/// Address family.
|
||||||
protected AddressFamily family;
|
protected AddressFamily family;
|
||||||
|
|
||||||
private @property void handle(socket_t handle) @nogc
|
private @property void handle(SocketType handle) @nogc
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(handle != socket_t.init);
|
assert(handle != SocketType.init);
|
||||||
assert(handle_ == socket_t.init, "Socket handle cannot be changed");
|
assert(handle_ == SocketType.init, "Socket handle cannot be changed");
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
@ -703,7 +893,7 @@ abstract class Socket
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@property inout(socket_t) handle() inout const pure nothrow @safe @nogc
|
@property inout(SocketType) handle() inout const pure nothrow @safe @nogc
|
||||||
{
|
{
|
||||||
return handle_;
|
return handle_;
|
||||||
}
|
}
|
||||||
@ -715,10 +905,10 @@ abstract class Socket
|
|||||||
* handle = Socket.
|
* handle = Socket.
|
||||||
* af = Address family.
|
* af = Address family.
|
||||||
*/
|
*/
|
||||||
this(socket_t handle, AddressFamily af) @nogc
|
this(SocketType handle, AddressFamily af) @nogc
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert(handle != socket_t.init);
|
assert(handle != SocketType.init);
|
||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
@ -759,7 +949,7 @@ abstract class Socket
|
|||||||
cast(int) level,
|
cast(int) level,
|
||||||
cast(int) option,
|
cast(int) option,
|
||||||
result.ptr,
|
result.ptr,
|
||||||
&length) == SOCKET_ERROR)
|
&length) == socketError)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to get socket option");
|
throw defaultAllocator.make!SocketException("Unable to get socket option");
|
||||||
}
|
}
|
||||||
@ -771,7 +961,7 @@ abstract class Socket
|
|||||||
SocketOption option,
|
SocketOption option,
|
||||||
out size_t result) const @trusted @nogc
|
out size_t result) const @trusted @nogc
|
||||||
{
|
{
|
||||||
return getOption(level, option, (&result)[0..1]);
|
return getOption(level, option, (&result)[0 .. 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
@ -779,7 +969,7 @@ abstract class Socket
|
|||||||
SocketOption option,
|
SocketOption option,
|
||||||
out Linger result) const @trusted @nogc
|
out Linger result) const @trusted @nogc
|
||||||
{
|
{
|
||||||
return getOption(level, option, (&result.clinger)[0..1]);
|
return getOption(level, option, (&result)[0 .. 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
@ -792,7 +982,7 @@ abstract class Socket
|
|||||||
version (Posix)
|
version (Posix)
|
||||||
{
|
{
|
||||||
timeval tv;
|
timeval tv;
|
||||||
auto ret = getOption(level, option, (&tv)[0..1]);
|
auto ret = getOption(level, option, (&tv)[0 .. 1]);
|
||||||
result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec);
|
result = dur!"seconds"(tv.tv_sec) + dur!"usecs"(tv.tv_usec);
|
||||||
}
|
}
|
||||||
else version (Windows)
|
else version (Windows)
|
||||||
@ -826,7 +1016,7 @@ abstract class Socket
|
|||||||
cast(int)level,
|
cast(int)level,
|
||||||
cast(int)option,
|
cast(int)option,
|
||||||
value.ptr,
|
value.ptr,
|
||||||
cast(uint) value.length) == SOCKET_ERROR)
|
cast(uint) value.length) == socketError)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to set socket option");
|
throw defaultAllocator.make!SocketException("Unable to set socket option");
|
||||||
}
|
}
|
||||||
@ -836,14 +1026,14 @@ abstract class Socket
|
|||||||
void setOption(SocketOptionLevel level, SocketOption option, size_t value)
|
void setOption(SocketOptionLevel level, SocketOption option, size_t value)
|
||||||
const @trusted @nogc
|
const @trusted @nogc
|
||||||
{
|
{
|
||||||
setOption(level, option, (&value)[0..1]);
|
setOption(level, option, (&value)[0 .. 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
void setOption(SocketOptionLevel level, SocketOption option, Linger value)
|
void setOption(SocketOptionLevel level, SocketOption option, Linger value)
|
||||||
const @trusted @nogc
|
const @trusted @nogc
|
||||||
{
|
{
|
||||||
setOption(level, option, (&value.clinger)[0..1]);
|
setOption(level, option, (&value)[0 .. 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
@ -854,7 +1044,7 @@ abstract class Socket
|
|||||||
{
|
{
|
||||||
timeval tv;
|
timeval tv;
|
||||||
value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec);
|
value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec);
|
||||||
setOption(level, option, (&tv)[0..1]);
|
setOption(level, option, (&tv)[0 .. 1]);
|
||||||
}
|
}
|
||||||
else version (Windows)
|
else version (Windows)
|
||||||
{
|
{
|
||||||
@ -878,7 +1068,7 @@ abstract class Socket
|
|||||||
}
|
}
|
||||||
else version (Windows)
|
else version (Windows)
|
||||||
{
|
{
|
||||||
return blocking_;
|
return this.blocking_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,12 +1082,12 @@ abstract class Socket
|
|||||||
{
|
{
|
||||||
int fl = fcntl(handle_, F_GETFL, 0);
|
int fl = fcntl(handle_, F_GETFL, 0);
|
||||||
|
|
||||||
if (fl != SOCKET_ERROR)
|
if (fl != socketError)
|
||||||
{
|
{
|
||||||
fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK;
|
fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK;
|
||||||
fl = fcntl(handle_, F_SETFL, fl);
|
fl = fcntl(handle_, F_SETFL, fl);
|
||||||
}
|
}
|
||||||
if (fl == SOCKET_ERROR)
|
if (fl == socketError)
|
||||||
{
|
{
|
||||||
throw make!SocketException(defaultAllocator,
|
throw make!SocketException(defaultAllocator,
|
||||||
"Unable to set socket blocking");
|
"Unable to set socket blocking");
|
||||||
@ -906,12 +1096,12 @@ abstract class Socket
|
|||||||
else version (Windows)
|
else version (Windows)
|
||||||
{
|
{
|
||||||
uint num = !yes;
|
uint num = !yes;
|
||||||
if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR)
|
if (ioctlsocket(handle_, FIONBIO, &num) == socketError)
|
||||||
{
|
{
|
||||||
throw make!SocketException(defaultAllocator,
|
throw make!SocketException(defaultAllocator,
|
||||||
"Unable to set socket blocking");
|
"Unable to set socket blocking");
|
||||||
}
|
}
|
||||||
blocking_ = yes;
|
this.blocking_ = yes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,7 +1153,7 @@ abstract class Socket
|
|||||||
{
|
{
|
||||||
.close(handle_);
|
.close(handle_);
|
||||||
}
|
}
|
||||||
handle_ = socket_t.init;
|
handle_ = SocketType.init;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -976,7 +1166,7 @@ abstract class Socket
|
|||||||
*/
|
*/
|
||||||
void listen(int backlog) const @trusted @nogc
|
void listen(int backlog) const @trusted @nogc
|
||||||
{
|
{
|
||||||
if (.listen(handle_, backlog) == SOCKET_ERROR)
|
if (.listen(handle_, backlog) == socketError)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to listen on socket");
|
throw defaultAllocator.make!SocketException("Unable to listen on socket");
|
||||||
}
|
}
|
||||||
@ -1029,8 +1219,8 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
|||||||
*/
|
*/
|
||||||
this(AddressFamily af) @trusted @nogc
|
this(AddressFamily af) @trusted @nogc
|
||||||
{
|
{
|
||||||
auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0);
|
auto handle = cast(SocketType) socket(af, 1, 0);
|
||||||
if (handle == socket_t.init)
|
if (handle == SocketType.init)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to create socket");
|
throw defaultAllocator.make!SocketException("Unable to create socket");
|
||||||
}
|
}
|
||||||
@ -1047,7 +1237,7 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
|||||||
*/
|
*/
|
||||||
void bind(Address address) const @trusted @nogc
|
void bind(Address address) const @trusted @nogc
|
||||||
{
|
{
|
||||||
if (.bind(handle_, address.name, address.length) == SOCKET_ERROR)
|
if (.bind(handle_, address.name, address.length) == socketError)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Unable to bind socket");
|
throw defaultAllocator.make!SocketException("Unable to bind socket");
|
||||||
}
|
}
|
||||||
@ -1066,7 +1256,7 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
|||||||
*/
|
*/
|
||||||
ConnectedSocket accept() @trusted @nogc
|
ConnectedSocket accept() @trusted @nogc
|
||||||
{
|
{
|
||||||
socket_t sock;
|
SocketType sock;
|
||||||
|
|
||||||
version (linux)
|
version (linux)
|
||||||
{
|
{
|
||||||
@ -1075,14 +1265,14 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
|||||||
{
|
{
|
||||||
flags |= SOCK_NONBLOCK;
|
flags |= SOCK_NONBLOCK;
|
||||||
}
|
}
|
||||||
sock = cast(socket_t).accept4(handle_, null, null, flags);
|
sock = cast(SocketType).accept4(handle_, null, null, flags);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sock = cast(socket_t).accept(handle_, null, null);
|
sock = cast(SocketType).accept(handle_, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sock == socket_t.init)
|
if (sock == SocketType.init)
|
||||||
{
|
{
|
||||||
if (wouldHaveBlocked())
|
if (wouldHaveBlocked())
|
||||||
{
|
{
|
||||||
@ -1147,7 +1337,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
|||||||
* handle = Socket.
|
* handle = Socket.
|
||||||
* af = Address family.
|
* af = Address family.
|
||||||
*/
|
*/
|
||||||
this(socket_t handle, AddressFamily af) @nogc
|
this(SocketType handle, AddressFamily af) @nogc
|
||||||
{
|
{
|
||||||
super(handle, af);
|
super(handle, af);
|
||||||
}
|
}
|
||||||
@ -1196,7 +1386,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
|||||||
{
|
{
|
||||||
disconnected_ = true;
|
disconnected_ = true;
|
||||||
}
|
}
|
||||||
else if (ret == SOCKET_ERROR)
|
else if (ret == socketError)
|
||||||
{
|
{
|
||||||
if (wouldHaveBlocked())
|
if (wouldHaveBlocked())
|
||||||
{
|
{
|
||||||
@ -1233,7 +1423,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
|||||||
}
|
}
|
||||||
|
|
||||||
sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags);
|
sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags);
|
||||||
if (sent != SOCKET_ERROR)
|
if (sent != socketError)
|
||||||
{
|
{
|
||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
@ -1273,14 +1463,11 @@ class InternetAddress : Address
|
|||||||
/// Internal internet address representation.
|
/// Internal internet address representation.
|
||||||
protected sockaddr_storage storage;
|
protected sockaddr_storage storage;
|
||||||
}
|
}
|
||||||
immutable ushort port_;
|
const ushort port_;
|
||||||
|
|
||||||
enum
|
enum ushort anyPort = 0;
|
||||||
{
|
|
||||||
anyPort = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
this(in string host, ushort port = anyPort) @nogc
|
this(string host, const ushort port = anyPort) @nogc
|
||||||
{
|
{
|
||||||
if (getaddrinfoPointer is null || freeaddrinfoPointer is null)
|
if (getaddrinfoPointer is null || freeaddrinfoPointer is null)
|
||||||
{
|
{
|
||||||
@ -1288,11 +1475,11 @@ class InternetAddress : Address
|
|||||||
"Address info lookup is not available on this system");
|
"Address info lookup is not available on this system");
|
||||||
}
|
}
|
||||||
addrinfo* ai_res;
|
addrinfo* ai_res;
|
||||||
port_ = port;
|
this.port_ = port;
|
||||||
|
|
||||||
// Make C-string from host.
|
// Make C-string from host.
|
||||||
auto node = cast(char[]) allocator.allocate(host.length + 1);
|
auto node = cast(char[]) allocator.allocate(host.length + 1);
|
||||||
node[0.. $ - 1] = host;
|
node[0 .. $ - 1] = host;
|
||||||
node[$ - 1] = '\0';
|
node[$ - 1] = '\0';
|
||||||
scope (exit)
|
scope (exit)
|
||||||
{
|
{
|
||||||
@ -1304,18 +1491,19 @@ class InternetAddress : Address
|
|||||||
const(char)* servicePointer;
|
const(char)* servicePointer;
|
||||||
if (port)
|
if (port)
|
||||||
{
|
{
|
||||||
|
ushort originalPort = port;
|
||||||
ushort start;
|
ushort start;
|
||||||
for (ushort j = 10, i = 4; i > 0; j *= 10, --i)
|
for (ushort j = 10, i = 4; i > 0; j *= 10, --i)
|
||||||
{
|
{
|
||||||
ushort rest = port % 10;
|
ushort rest = originalPort % 10;
|
||||||
if (rest != 0)
|
if (rest != 0)
|
||||||
{
|
{
|
||||||
service[i] = cast(char) (rest + '0');
|
service[i] = cast(char) (rest + '0');
|
||||||
start = i;
|
start = i;
|
||||||
}
|
}
|
||||||
port /= 10;
|
originalPort /= 10;
|
||||||
}
|
}
|
||||||
servicePointer = service[start..$].ptr;
|
servicePointer = service[start .. $].ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res);
|
auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res);
|
||||||
@ -1333,12 +1521,23 @@ class InternetAddress : Address
|
|||||||
{
|
{
|
||||||
*dp = *sp;
|
*dp = *sp;
|
||||||
}
|
}
|
||||||
if (ai_res.ai_family != AddressFamily.INET && ai_res.ai_family != AddressFamily.INET6)
|
if (ai_res.ai_family != AddressFamily.inet && ai_res.ai_family != AddressFamily.inet6)
|
||||||
{
|
{
|
||||||
throw defaultAllocator.make!SocketException("Wrong address family");
|
throw defaultAllocator.make!SocketException("Wrong address family");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
auto address = defaultAllocator.make!InternetAddress("127.0.0.1");
|
||||||
|
assert(address.port == InternetAddress.anyPort);
|
||||||
|
assert(address.name !is null);
|
||||||
|
assert(address.family == AddressFamily.inet);
|
||||||
|
|
||||||
|
defaultAllocator.dispose(address);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure.
|
* Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure.
|
||||||
*/
|
*/
|
||||||
@ -1355,9 +1554,9 @@ class InternetAddress : Address
|
|||||||
// FreeBSD wants to know the exact length of the address on bind.
|
// FreeBSD wants to know the exact length of the address on bind.
|
||||||
switch (family)
|
switch (family)
|
||||||
{
|
{
|
||||||
case AddressFamily.INET:
|
case AddressFamily.inet:
|
||||||
return sockaddr_in.sizeof;
|
return sockaddr_in.sizeof;
|
||||||
case AddressFamily.INET6:
|
case AddressFamily.inet6:
|
||||||
return sockaddr_in6.sizeof;
|
return sockaddr_in6.sizeof;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -1376,6 +1575,15 @@ class InternetAddress : Address
|
|||||||
{
|
{
|
||||||
return port_;
|
return port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
|
||||||
|
cast(ushort) 1234);
|
||||||
|
assert(address.port == 1234);
|
||||||
|
defaultAllocator.dispose(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
107
source/tanya/typecons.d
Normal file
107
source/tanya/typecons.d
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/* 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.
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
module tanya.typecons;
|
||||||
|
|
||||||
|
import std.meta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL Pair) can store two heterogeneous objects.
|
||||||
|
*
|
||||||
|
* The objects can by accessed by index as $(D_INLINECODE obj[0]) and
|
||||||
|
* $(D_INLINECODE obj[1]) or by optional names (e.g.
|
||||||
|
* $(D_INLINECODE 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.
|
||||||
|
*/
|
||||||
|
template Pair(Specs...)
|
||||||
|
{
|
||||||
|
template parseSpecs(int 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!(Specs[0],
|
||||||
|
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias parseSpecs
|
||||||
|
= AliasSeq!(Specs[0],
|
||||||
|
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false, "Invalid argument: " ~ Specs[0].stringof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pair
|
||||||
|
{
|
||||||
|
/// Field types.
|
||||||
|
alias Types = parseSpecs!(0, Specs);
|
||||||
|
|
||||||
|
static assert(Types.length == 2, "Invalid argument count.");
|
||||||
|
|
||||||
|
// Create field aliases.
|
||||||
|
static if (is(typeof(Specs[1]) == string))
|
||||||
|
{
|
||||||
|
mixin("alias " ~ Specs[1] ~ " = expand[0];");
|
||||||
|
}
|
||||||
|
static if (is(typeof(Specs[2]) == string))
|
||||||
|
{
|
||||||
|
mixin("alias " ~ Specs[2] ~ " = expand[1];");
|
||||||
|
}
|
||||||
|
else static if (is(typeof(Specs[3]) == string))
|
||||||
|
{
|
||||||
|
mixin("alias " ~ Specs[3] ~ " = expand[1];");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
|
||||||
|
Types expand;
|
||||||
|
|
||||||
|
alias expand this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
static assert(is(Pair!(int, int)));
|
||||||
|
static assert(!is(Pair!(int, 5)));
|
||||||
|
|
||||||
|
static assert(is(Pair!(int, "first", int)));
|
||||||
|
static assert(is(Pair!(int, "first", int, "second")));
|
||||||
|
static assert(is(Pair!(int, "first", int)));
|
||||||
|
|
||||||
|
static assert(is(Pair!(int, int, "second")));
|
||||||
|
static assert(!is(Pair!("first", int, "second", int)));
|
||||||
|
static assert(!is(Pair!(int, int, int)));
|
||||||
|
|
||||||
|
static assert(!is(Pair!(int, "first")));
|
||||||
|
}
|
Reference in New Issue
Block a user