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
|
||||
|
||||
d:
|
||||
- dmd-2.074.0
|
||||
- dmd-2.074.1
|
||||
- dmd-2.073.2
|
||||
- dmd-2.072.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
|
||||
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)
|
||||
|
||||
## Overview
|
||||
@ -24,17 +24,16 @@ Tanya consists of the following packages:
|
||||
|
||||
* `async`: Event loop (epoll, kqueue and IOCP).
|
||||
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
|
||||
string.
|
||||
string, Hash set.
|
||||
* `math`: Arbitrary precision integer and a set of functions.
|
||||
* `memory`: Tools for manual memory management (allocator, reference counting,
|
||||
helper functions).
|
||||
* `memory`: Tools for manual memory management (allocators, smart pointers).
|
||||
* `network`: URL-Parsing, sockets, utilities.
|
||||
|
||||
### Supported compilers
|
||||
|
||||
| dmd |
|
||||
|:-------:|
|
||||
| 2.074.0 |
|
||||
| 2.074.1 |
|
||||
| 2.073.2 |
|
||||
| 2.072.2 |
|
||||
| 2.071.2 |
|
||||
@ -43,30 +42,27 @@ helper functions).
|
||||
|
||||
Following modules are under development:
|
||||
|
||||
| Feature | Branch | Build status |
|
||||
|--------------|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| BitVector | 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) |
|
||||
| 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) |
|
||||
| Feature | Branch | Build status |
|
||||
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 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) |
|
||||
| File IO | io | [](https://travis-ci.org/caraus-ecms/tanya) [](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
3-week release cycle.
|
||||
|
||||
Deprecated features are removed after one release (in approximately 6 weeks after deprecating).
|
||||
|
||||
## 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.
|
||||
|
@ -4,7 +4,7 @@ os: Visual Studio 2017
|
||||
environment:
|
||||
matrix:
|
||||
- DC: dmd
|
||||
DVersion: 2.074.0
|
||||
DVersion: 2.074.1
|
||||
arch: x86
|
||||
- DC: dmd
|
||||
DVersion: 2.073.2
|
||||
|
2
dub.json
2
dub.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tanya",
|
||||
"description": "General purpose, @nogc library",
|
||||
"description": "General purpose, @nogc library. Containers, networking, memory management, utilities",
|
||||
"license": "MPL-2.0",
|
||||
"copyright": "(c) Eugene Wissner <info@caraus.de>",
|
||||
"authors": [
|
||||
|
@ -151,7 +151,7 @@ final class KqueueLoop : SelectorLoop
|
||||
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)
|
||||
{
|
||||
|
@ -38,11 +38,11 @@
|
||||
*
|
||||
* version (Windows)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.INET);
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.INET);
|
||||
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
*
|
||||
|
@ -160,7 +160,7 @@ struct Range(E)
|
||||
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];
|
||||
}
|
||||
@ -174,6 +174,12 @@ struct Range(E)
|
||||
*/
|
||||
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 T* data;
|
||||
private size_t capacity_;
|
||||
@ -638,7 +644,7 @@ struct Array(T)
|
||||
*
|
||||
* 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
|
||||
{
|
||||
assert(r.container is &this);
|
||||
@ -648,9 +654,9 @@ struct Array(T)
|
||||
body
|
||||
{
|
||||
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;
|
||||
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).
|
||||
*/
|
||||
size_t insertAfter(R)(Range!T r, R el)
|
||||
size_t insertAfter(R)(Range r, R el)
|
||||
if (!isInfinite!R
|
||||
&& isInputRange!R
|
||||
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||
@ -808,7 +814,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
assert(r.container is &this);
|
||||
@ -821,7 +827,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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))
|
||||
in
|
||||
{
|
||||
@ -848,7 +854,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
size_t insertBefore(R)(Range!T r, R el)
|
||||
size_t insertBefore(R)(Range r, R el)
|
||||
if (!isInfinite!R
|
||||
&& isInputRange!R
|
||||
&& isImplicitlyConvertible!(ElementType!R, T))
|
||||
@ -860,11 +866,11 @@ struct Array(T)
|
||||
}
|
||||
body
|
||||
{
|
||||
return insertAfter(Range!T(this, this.data, r.begin), el);
|
||||
return insertAfter(.Range!T(this, this.data, r.begin), el);
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
assert(r.container is &this);
|
||||
@ -877,7 +883,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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))
|
||||
in
|
||||
{
|
||||
@ -993,7 +999,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
Range!T opIndexAssign(E : T)(auto ref E value)
|
||||
Range opIndexAssign(E : T)(auto ref E value)
|
||||
{
|
||||
return opSliceAssign(value, 0, length);
|
||||
}
|
||||
@ -1017,13 +1023,13 @@ struct Array(T)
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
Range!T opIndexAssign(Range!T value)
|
||||
Range opIndexAssign(Range value)
|
||||
{
|
||||
return opSliceAssign(value, 0, length);
|
||||
}
|
||||
@ -1066,13 +1072,13 @@ struct Array(T)
|
||||
* Returns: Random access range that iterates over elements of the array,
|
||||
* in forward order.
|
||||
*/
|
||||
Range!T opIndex() @trusted
|
||||
Range opIndex() @trusted
|
||||
{
|
||||
return typeof(return)(this, this.data, this.data + length);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
Range!(const T) opIndex() const @trusted
|
||||
ConstRange opIndex() const @trusted
|
||||
{
|
||||
return typeof(return)(this, this.data, this.data + length);
|
||||
}
|
||||
@ -1105,13 +1111,13 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
bool opEquals(Range!T that)
|
||||
bool opEquals(Range that)
|
||||
{
|
||||
return equal(opIndex(), that);
|
||||
}
|
||||
@ -1126,8 +1132,8 @@ struct Array(T)
|
||||
* Returns: $(D_KEYWORD true) if the array and the range are equal,
|
||||
* $(D_KEYWORD false) otherwise.
|
||||
*/
|
||||
bool opEquals(R)(Range!R that) const
|
||||
if (is(Unqual!R == T))
|
||||
bool opEquals(R)(R that) const
|
||||
if (is(R == Range) || is(R == ConstRange))
|
||||
{
|
||||
return equal(opIndex(), that);
|
||||
}
|
||||
@ -1216,7 +1222,7 @@ struct Array(T)
|
||||
*
|
||||
* 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
|
||||
{
|
||||
assert(i <= j);
|
||||
@ -1228,7 +1234,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
assert(i <= j);
|
||||
@ -1298,7 +1304,7 @@ struct Array(T)
|
||||
* Precondition: $(D_INLINECODE i <= j && j <= length
|
||||
* && 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
|
||||
in
|
||||
{
|
||||
@ -1312,7 +1318,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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
|
||||
in
|
||||
{
|
||||
@ -1326,7 +1332,7 @@ struct Array(T)
|
||||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
assert(i <= j);
|
||||
|
@ -12,6 +12,9 @@
|
||||
*/
|
||||
module tanya.container.entry;
|
||||
|
||||
import std.traits;
|
||||
import tanya.typecons;
|
||||
|
||||
package struct SEntry(T)
|
||||
{
|
||||
// Item content.
|
||||
@ -29,3 +32,80 @@ package struct DEntry(T)
|
||||
// Previous and next item.
|
||||
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)
|
||||
{
|
||||
/// The range types for $(D_PSYMBOL SList).
|
||||
alias Range = SRange!T;
|
||||
|
||||
/// Ditto.
|
||||
alias ConstRange = SRange!(const T);
|
||||
|
||||
private alias Entry = SEntry!T;
|
||||
|
||||
// 0th element of the list.
|
||||
@ -497,7 +503,7 @@ struct SList(T)
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
size_t insertBefore(SRange!T r, ref T el) @trusted
|
||||
size_t insertBefore(Range r, ref T el) @trusted
|
||||
in
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
@ -530,7 +536,7 @@ struct SList(T)
|
||||
*
|
||||
* 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[]);
|
||||
}
|
||||
@ -685,7 +691,7 @@ struct SList(T)
|
||||
*
|
||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||
*/
|
||||
SRange!T remove(SRange!T r)
|
||||
Range remove(Range r)
|
||||
in
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
@ -716,13 +722,13 @@ struct SList(T)
|
||||
* Returns: Range that iterates over all elements of the container, in
|
||||
* forward order.
|
||||
*/
|
||||
SRange!T opIndex()
|
||||
Range opIndex()
|
||||
{
|
||||
return typeof(return)(this.head);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
SRange!(const T) opIndex() const
|
||||
ConstRange opIndex() const
|
||||
{
|
||||
return typeof(return)(this.head);
|
||||
}
|
||||
@ -966,6 +972,12 @@ struct DRange(E)
|
||||
*/
|
||||
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;
|
||||
|
||||
// 0th and the last elements of the list.
|
||||
@ -1536,7 +1548,7 @@ struct DList(T)
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
size_t insertBefore(DRange!T r, ref T el) @trusted
|
||||
size_t insertBefore(Range r, ref T el) @trusted
|
||||
in
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
@ -1569,7 +1581,7 @@ struct DList(T)
|
||||
*
|
||||
* 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[]);
|
||||
}
|
||||
@ -1728,7 +1740,7 @@ struct DList(T)
|
||||
*
|
||||
* Precondition: $(D_PARAM r) is extracted from this list.
|
||||
*/
|
||||
DRange!T remove(DRange!T r)
|
||||
Range remove(Range r)
|
||||
in
|
||||
{
|
||||
assert(checkRangeBelonging(r));
|
||||
@ -1768,13 +1780,13 @@ struct DList(T)
|
||||
* Returns: Range that iterates over all elements of the container, in
|
||||
* forward order.
|
||||
*/
|
||||
DRange!T opIndex()
|
||||
Range opIndex()
|
||||
{
|
||||
return typeof(return)(this.head, this.tail);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
DRange!(const T) opIndex() const
|
||||
ConstRange opIndex() const
|
||||
{
|
||||
return typeof(return)(this.head, this.tail);
|
||||
}
|
||||
|
@ -14,6 +14,29 @@ module tanya.container;
|
||||
|
||||
public import tanya.container.array;
|
||||
public import tanya.container.buffer;
|
||||
public import tanya.container.set;
|
||||
public import tanya.container.list;
|
||||
public import tanya.container.string;
|
||||
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);
|
||||
}
|
||||
|
||||
const(E)[] toString() const @trusted
|
||||
{
|
||||
return this.begin[0 .. length];
|
||||
}
|
||||
|
||||
E[] toString() @trusted
|
||||
inout(E)[] get() inout @trusted
|
||||
{
|
||||
return this.begin[0 .. length];
|
||||
}
|
||||
@ -948,13 +943,7 @@ struct String
|
||||
*
|
||||
* Returns: The array representing the string.
|
||||
*/
|
||||
const(char)[] toString() const pure nothrow @trusted @nogc
|
||||
{
|
||||
return this.data[0 .. this.length_];
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
char[] toString() pure nothrow @trusted @nogc
|
||||
inout(char)[] get() inout pure nothrow @trusted @nogc
|
||||
{
|
||||
return this.data[0 .. this.length_];
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Socket programming.
|
||||
* Low-level socket programming.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2017.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
@ -12,15 +12,16 @@
|
||||
*/
|
||||
module tanya.network.socket;
|
||||
|
||||
import tanya.memory;
|
||||
import core.stdc.errno;
|
||||
import core.time;
|
||||
import std.algorithm.comparison;
|
||||
import std.algorithm.searching;
|
||||
public import std.socket : socket_t, Linger, SocketOptionLevel, SocketOption,
|
||||
SocketType, AddressFamily, AddressInfo;
|
||||
public import std.socket : SocketOptionLevel, SocketOption;
|
||||
import std.traits;
|
||||
import std.typecons;
|
||||
import tanya.memory;
|
||||
|
||||
/// Value returned by socket operations on error.
|
||||
enum int socketError = -1;
|
||||
|
||||
version (Posix)
|
||||
{
|
||||
@ -32,7 +33,12 @@ version (Posix)
|
||||
import core.sys.posix.sys.time;
|
||||
import core.sys.posix.unistd;
|
||||
|
||||
private enum SOCKET_ERROR = -1;
|
||||
enum SocketType : int
|
||||
{
|
||||
init = -1,
|
||||
}
|
||||
|
||||
private alias LingerField = int;
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
@ -43,16 +49,23 @@ else version (Windows)
|
||||
import core.sys.windows.windef;
|
||||
import core.sys.windows.winsock2;
|
||||
|
||||
enum SocketType : size_t
|
||||
{
|
||||
init = ~0,
|
||||
}
|
||||
|
||||
private alias LingerField = ushort;
|
||||
|
||||
enum : uint
|
||||
{
|
||||
IOC_UNIX = 0x00000000,
|
||||
IOC_WS2 = 0x08000000,
|
||||
IOC_PROTOCOL = 0x10000000,
|
||||
IOC_VOID = 0x20000000, /// No parameters.
|
||||
IOC_OUT = 0x40000000, /// Copy parameters back.
|
||||
IOC_IN = 0x80000000, /// Copy parameters into.
|
||||
IOC_VOID = 0x20000000, // No parameters.
|
||||
IOC_OUT = 0x40000000, // Copy parameters back.
|
||||
IOC_IN = 0x80000000, // Copy parameters into.
|
||||
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)
|
||||
@ -183,19 +196,26 @@ else version (Windows)
|
||||
LPINT,
|
||||
SOCKADDR**,
|
||||
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;
|
||||
CHAR* buf;
|
||||
}
|
||||
alias WSABUF* LPWSABUF;
|
||||
|
||||
struct WSAOVERLAPPED {
|
||||
struct WSAOVERLAPPED
|
||||
{
|
||||
ULONG_PTR Internal;
|
||||
ULONG_PTR InternalHigh;
|
||||
union {
|
||||
struct {
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD Offset;
|
||||
DWORD OffsetHigh;
|
||||
}
|
||||
@ -219,36 +239,13 @@ else version (Windows)
|
||||
private WSABUF buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket returned if a connection has been established.
|
||||
*/
|
||||
class OverlappedConnectedSocket : ConnectedSocket
|
||||
{
|
||||
/**
|
||||
* Create a socket.
|
||||
*
|
||||
* Params:
|
||||
* handle = Socket handle.
|
||||
* af = Address family.
|
||||
*/
|
||||
this(socket_t handle, AddressFamily af) @nogc
|
||||
this(SocketType handle, AddressFamily af) @nogc
|
||||
{
|
||||
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,
|
||||
SocketState overlapped,
|
||||
Flags flags = Flags(Flag.none)) @nogc @trusted
|
||||
@ -268,23 +265,13 @@ else version (Windows)
|
||||
&overlapped.overlapped,
|
||||
NULL);
|
||||
|
||||
if (result == SOCKET_ERROR && !wouldHaveBlocked)
|
||||
if (result == socketError && !wouldHaveBlocked)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Unable to receive");
|
||||
}
|
||||
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
|
||||
out (count)
|
||||
{
|
||||
@ -309,19 +296,6 @@ else version (Windows)
|
||||
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,
|
||||
SocketState overlapped,
|
||||
Flags flags = Flags(Flag.none)) @nogc @trusted
|
||||
@ -339,7 +313,7 @@ else version (Windows)
|
||||
&overlapped.overlapped,
|
||||
NULL);
|
||||
|
||||
if (result == SOCKET_ERROR && !wouldHaveBlocked)
|
||||
if (result == socketError && !wouldHaveBlocked)
|
||||
{
|
||||
disconnected_ = true;
|
||||
throw defaultAllocator.make!SocketException("Unable to send");
|
||||
@ -347,16 +321,6 @@ else version (Windows)
|
||||
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
|
||||
out (count)
|
||||
{
|
||||
@ -380,17 +344,9 @@ else version (Windows)
|
||||
|
||||
class OverlappedStreamSocket : StreamSocket
|
||||
{
|
||||
/// Accept extension function pointer.
|
||||
// Accept extension function pointer.
|
||||
package LPFN_ACCEPTEX acceptExtension;
|
||||
|
||||
/**
|
||||
* Create a socket.
|
||||
*
|
||||
* Params:
|
||||
* af = Address family.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL SocketException) on errors.
|
||||
*/
|
||||
this(AddressFamily af) @nogc @trusted
|
||||
{
|
||||
super(af);
|
||||
@ -412,28 +368,17 @@ else version (Windows)
|
||||
&dwBytes,
|
||||
NULL,
|
||||
NULL);
|
||||
if (!result == SOCKET_ERROR)
|
||||
if (!result == socketError)
|
||||
{
|
||||
throw make!SocketException(defaultAllocator,
|
||||
"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
|
||||
{
|
||||
auto socket = cast(socket_t) socket(addressFamily, SOCK_STREAM, 0);
|
||||
if (socket == socket_t.init)
|
||||
auto socket = cast(SocketType) socket(addressFamily, 1, 0);
|
||||
if (socket == SocketType.init)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Unable to create socket");
|
||||
}
|
||||
@ -445,7 +390,7 @@ else version (Windows)
|
||||
overlapped.handle = cast(HANDLE) socket;
|
||||
overlapped.event = OverlappedSocketEvent.accept;
|
||||
|
||||
immutable len = (sockaddr_in.sizeof + 16) * 2;
|
||||
const len = (sockaddr_in.sizeof + 16) * 2;
|
||||
overlapped.buffer.len = len;
|
||||
overlapped.buffer.buf = cast(char*) defaultAllocator.allocate(len).ptr;
|
||||
|
||||
@ -465,6 +410,139 @@ else version (Windows)
|
||||
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
|
||||
* new socket to handle remote host communication.
|
||||
@ -476,24 +554,116 @@ else version (Windows)
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
defaultAllocator.dispose(overlapped.buffer.buf[0 .. overlapped.buffer.len]);
|
||||
}
|
||||
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(5);
|
||||
assert(linger.enabled);
|
||||
assert(linger.time == 5);
|
||||
}
|
||||
{
|
||||
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)
|
||||
{
|
||||
enum ESOCKTNOSUPPORT = 44; /// Socket type not suppoted.
|
||||
enum ESOCKTNOSUPPORT = 44; // Socket type not suppoted.
|
||||
}
|
||||
|
||||
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).
|
||||
*/
|
||||
enum SocketError : int
|
||||
{
|
||||
/// Unknown error
|
||||
/// Unknown error.
|
||||
unknown = 0,
|
||||
/// Firewall rules forbid connection.
|
||||
accessDenied = EPERM,
|
||||
@ -587,12 +777,12 @@ enum SocketError : int
|
||||
|
||||
/**
|
||||
* $(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),
|
||||
* because $(D_PSYMBOL SocketException) relies on the $(D_PSYMBOL errno) value.
|
||||
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
|
||||
* $(D_PSYMBOL SocketException) relies on the $(D_PSYMBOL errno) value.
|
||||
*/
|
||||
class SocketException : Exception
|
||||
{
|
||||
immutable SocketError error = SocketError.unknown;
|
||||
const SocketError error = SocketError.unknown;
|
||||
|
||||
/**
|
||||
* Params:
|
||||
@ -680,16 +870,16 @@ abstract class Socket
|
||||
}
|
||||
|
||||
/// Socket handle.
|
||||
protected socket_t handle_;
|
||||
protected SocketType handle_;
|
||||
|
||||
/// Address family.
|
||||
protected AddressFamily family;
|
||||
|
||||
private @property void handle(socket_t handle) @nogc
|
||||
private @property void handle(SocketType handle) @nogc
|
||||
in
|
||||
{
|
||||
assert(handle != socket_t.init);
|
||||
assert(handle_ == socket_t.init, "Socket handle cannot be changed");
|
||||
assert(handle != SocketType.init);
|
||||
assert(handle_ == SocketType.init, "Socket handle cannot be changed");
|
||||
}
|
||||
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_;
|
||||
}
|
||||
@ -715,10 +905,10 @@ abstract class Socket
|
||||
* handle = Socket.
|
||||
* af = Address family.
|
||||
*/
|
||||
this(socket_t handle, AddressFamily af) @nogc
|
||||
this(SocketType handle, AddressFamily af) @nogc
|
||||
in
|
||||
{
|
||||
assert(handle != socket_t.init);
|
||||
assert(handle != SocketType.init);
|
||||
}
|
||||
body
|
||||
{
|
||||
@ -759,7 +949,7 @@ abstract class Socket
|
||||
cast(int) level,
|
||||
cast(int) option,
|
||||
result.ptr,
|
||||
&length) == SOCKET_ERROR)
|
||||
&length) == socketError)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Unable to get socket option");
|
||||
}
|
||||
@ -771,7 +961,7 @@ abstract class Socket
|
||||
SocketOption option,
|
||||
out size_t result) const @trusted @nogc
|
||||
{
|
||||
return getOption(level, option, (&result)[0..1]);
|
||||
return getOption(level, option, (&result)[0 .. 1]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
@ -779,7 +969,7 @@ abstract class Socket
|
||||
SocketOption option,
|
||||
out Linger result) const @trusted @nogc
|
||||
{
|
||||
return getOption(level, option, (&result.clinger)[0..1]);
|
||||
return getOption(level, option, (&result)[0 .. 1]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
@ -792,7 +982,7 @@ abstract class Socket
|
||||
version (Posix)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else version (Windows)
|
||||
@ -826,7 +1016,7 @@ abstract class Socket
|
||||
cast(int)level,
|
||||
cast(int)option,
|
||||
value.ptr,
|
||||
cast(uint) value.length) == SOCKET_ERROR)
|
||||
cast(uint) value.length) == socketError)
|
||||
{
|
||||
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)
|
||||
const @trusted @nogc
|
||||
{
|
||||
setOption(level, option, (&value)[0..1]);
|
||||
setOption(level, option, (&value)[0 .. 1]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
void setOption(SocketOptionLevel level, SocketOption option, Linger value)
|
||||
const @trusted @nogc
|
||||
{
|
||||
setOption(level, option, (&value.clinger)[0..1]);
|
||||
setOption(level, option, (&value)[0 .. 1]);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
@ -854,7 +1044,7 @@ abstract class Socket
|
||||
{
|
||||
timeval tv;
|
||||
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)
|
||||
{
|
||||
@ -878,7 +1068,7 @@ abstract class Socket
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
return blocking_;
|
||||
return this.blocking_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -892,12 +1082,12 @@ abstract class Socket
|
||||
{
|
||||
int fl = fcntl(handle_, F_GETFL, 0);
|
||||
|
||||
if (fl != SOCKET_ERROR)
|
||||
if (fl != socketError)
|
||||
{
|
||||
fl = yes ? fl & ~O_NONBLOCK : fl | O_NONBLOCK;
|
||||
fl = fcntl(handle_, F_SETFL, fl);
|
||||
}
|
||||
if (fl == SOCKET_ERROR)
|
||||
if (fl == socketError)
|
||||
{
|
||||
throw make!SocketException(defaultAllocator,
|
||||
"Unable to set socket blocking");
|
||||
@ -906,12 +1096,12 @@ abstract class Socket
|
||||
else version (Windows)
|
||||
{
|
||||
uint num = !yes;
|
||||
if (ioctlsocket(handle_, FIONBIO, &num) == SOCKET_ERROR)
|
||||
if (ioctlsocket(handle_, FIONBIO, &num) == socketError)
|
||||
{
|
||||
throw make!SocketException(defaultAllocator,
|
||||
"Unable to set socket blocking");
|
||||
}
|
||||
blocking_ = yes;
|
||||
this.blocking_ = yes;
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,7 +1153,7 @@ abstract class Socket
|
||||
{
|
||||
.close(handle_);
|
||||
}
|
||||
handle_ = socket_t.init;
|
||||
handle_ = SocketType.init;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -971,12 +1161,12 @@ abstract class Socket
|
||||
* can $(D_PSYMBOL listen).
|
||||
*
|
||||
* Params:
|
||||
* backlog = Request of how many pending incoming connections are
|
||||
* backlog = Request of how many pending incoming connections are
|
||||
* queued until $(D_PSYMBOL accept)ed.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
@ -1029,8 +1219,8 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
||||
*/
|
||||
this(AddressFamily af) @trusted @nogc
|
||||
{
|
||||
auto handle = cast(socket_t) socket(af, SOCK_STREAM, 0);
|
||||
if (handle == socket_t.init)
|
||||
auto handle = cast(SocketType) socket(af, 1, 0);
|
||||
if (handle == SocketType.init)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Unable to create socket");
|
||||
}
|
||||
@ -1047,7 +1237,7 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
||||
*/
|
||||
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");
|
||||
}
|
||||
@ -1066,7 +1256,7 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
||||
*/
|
||||
ConnectedSocket accept() @trusted @nogc
|
||||
{
|
||||
socket_t sock;
|
||||
SocketType sock;
|
||||
|
||||
version (linux)
|
||||
{
|
||||
@ -1075,14 +1265,14 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
||||
{
|
||||
flags |= SOCK_NONBLOCK;
|
||||
}
|
||||
sock = cast(socket_t).accept4(handle_, null, null, flags);
|
||||
sock = cast(SocketType).accept4(handle_, null, null, flags);
|
||||
}
|
||||
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())
|
||||
{
|
||||
@ -1147,7 +1337,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
||||
* handle = Socket.
|
||||
* af = Address family.
|
||||
*/
|
||||
this(socket_t handle, AddressFamily af) @nogc
|
||||
this(SocketType handle, AddressFamily af) @nogc
|
||||
{
|
||||
super(handle, af);
|
||||
}
|
||||
@ -1196,7 +1386,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
||||
{
|
||||
disconnected_ = true;
|
||||
}
|
||||
else if (ret == SOCKET_ERROR)
|
||||
else if (ret == socketError)
|
||||
{
|
||||
if (wouldHaveBlocked())
|
||||
{
|
||||
@ -1233,7 +1423,7 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
||||
}
|
||||
|
||||
sent = .send(handle_, buf.ptr, capToMaxBuffer(buf.length), sendFlags);
|
||||
if (sent != SOCKET_ERROR)
|
||||
if (sent != socketError)
|
||||
{
|
||||
return sent;
|
||||
}
|
||||
@ -1273,14 +1463,11 @@ class InternetAddress : Address
|
||||
/// Internal internet address representation.
|
||||
protected sockaddr_storage storage;
|
||||
}
|
||||
immutable ushort port_;
|
||||
const ushort port_;
|
||||
|
||||
enum
|
||||
{
|
||||
anyPort = 0,
|
||||
}
|
||||
enum ushort 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)
|
||||
{
|
||||
@ -1288,11 +1475,11 @@ class InternetAddress : Address
|
||||
"Address info lookup is not available on this system");
|
||||
}
|
||||
addrinfo* ai_res;
|
||||
port_ = port;
|
||||
this.port_ = port;
|
||||
|
||||
// Make C-string from host.
|
||||
auto node = cast(char[]) allocator.allocate(host.length + 1);
|
||||
node[0.. $ - 1] = host;
|
||||
node[0 .. $ - 1] = host;
|
||||
node[$ - 1] = '\0';
|
||||
scope (exit)
|
||||
{
|
||||
@ -1304,18 +1491,19 @@ class InternetAddress : Address
|
||||
const(char)* servicePointer;
|
||||
if (port)
|
||||
{
|
||||
ushort originalPort = port;
|
||||
ushort start;
|
||||
for (ushort j = 10, i = 4; i > 0; j *= 10, --i)
|
||||
{
|
||||
ushort rest = port % 10;
|
||||
ushort rest = originalPort % 10;
|
||||
if (rest != 0)
|
||||
{
|
||||
service[i] = cast(char) (rest + '0');
|
||||
start = i;
|
||||
}
|
||||
port /= 10;
|
||||
originalPort /= 10;
|
||||
}
|
||||
servicePointer = service[start..$].ptr;
|
||||
servicePointer = service[start .. $].ptr;
|
||||
}
|
||||
|
||||
auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res);
|
||||
@ -1327,18 +1515,29 @@ class InternetAddress : Address
|
||||
{
|
||||
freeaddrinfoPointer(ai_res);
|
||||
}
|
||||
|
||||
|
||||
ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr;
|
||||
for (auto i = ai_res.ai_addrlen; i > 0; --i, *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");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
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.
|
||||
*/
|
||||
@ -1355,9 +1554,9 @@ class InternetAddress : Address
|
||||
// FreeBSD wants to know the exact length of the address on bind.
|
||||
switch (family)
|
||||
{
|
||||
case AddressFamily.INET:
|
||||
case AddressFamily.inet:
|
||||
return sockaddr_in.sizeof;
|
||||
case AddressFamily.INET6:
|
||||
case AddressFamily.inet6:
|
||||
return sockaddr_in6.sizeof;
|
||||
default:
|
||||
assert(false);
|
||||
@ -1376,6 +1575,15 @@ class InternetAddress : Address
|
||||
{
|
||||
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