68 Commits

Author SHA1 Message Date
a648e2120a Fix parameter count for docs in container.string 2017-05-19 20:01:04 +02:00
bc61809050 Implement DList.insertBack 2017-05-16 13:16:18 +02:00
8c42cbfd63 Rename Vector to Array
For consistency with Phobos.
2017-05-16 12:12:57 +02:00
58664570f9 Add new branch, add DList to package description 2017-05-15 20:09:32 +02:00
decb82f437 Remove crypto.mac for now
It wasn't released yet and needs some work.
2017-05-15 19:57:36 +02:00
357c7e279d Add doubly-linked list
DList is an adjusted copy of SList. Missing:
* insertBack
* insertAfter
* removeBack
2017-05-15 19:50:20 +02:00
32e19c8b58 Rename String.get into toString. Add String.toStringz 2017-05-14 11:56:57 +02:00
f5c6c5b483 Add Payload template for memory.types 2017-05-13 08:43:49 +02:00
ba2d086fb8 Add memory.types.Scoped 2017-05-13 08:27:51 +02:00
7a0241b484 Fix unittest text for strings 2017-05-12 22:42:43 +02:00
36dad80e18 Use char ranges to avoid compilation errors on elder compilers 2017-05-12 21:46:48 +02:00
29d883150e Fix unittests on 2.072 and 2.071 2017-05-12 21:10:22 +02:00
e2bed0cfcb Replace tabs with spaces in mmappool and buffer 2017-05-12 21:02:24 +02:00
38afeac071 Insert String.insertFront and String.insertBack 2017-05-12 20:55:42 +02:00
001c7c3e33 Replace immutable with const in Vector 2017-05-12 20:35:16 +02:00
d4ab339feb Add String.remove 2017-05-12 20:23:16 +02:00
8477312769 Add editorconfig 2017-05-11 13:57:24 +02:00
67f90e137d Add codecov badge 2017-05-11 13:15:04 +02:00
f264fd5597 Generate unittest coverage information 2017-05-11 13:11:12 +02:00
9e75620f1b Fix appveyor branch badges 2017-05-11 07:05:13 +02:00
45825946c0 Appveyor (#10)
* Add appveyor.yml

* Try major VC version

* Switch to VC 2015

* Try new version

* Try enterprise

* Try another path

* Change VC template

* Set arch

* Set LINKCMD64

* Fix quotes

* Update LINKCMD64

* remove dir

* Update arch

* Fix syntax

* Set arch to x64

* Remove extra dub downloading

* Remove dub version

* Download dub for 2.071.2

* Use DVersion

* Fix nul in powershell

* Put quotes to commands

* Add badges
2017-05-11 06:26:59 +02:00
8afb552d59 mp.Integer: add two's complement constructor 2017-05-10 19:27:25 +02:00
e4091669f8 Add information about io branch 2017-05-10 13:18:58 +02:00
1cb9349226 math.mp.Integer.toVector return two's complement 2017-05-09 06:27:30 +02:00
06620dc5df math.mp.Integer: Return two's complement length 2017-05-08 21:09:52 +02:00
708d95db49 Remove utf8string branch 2017-05-06 11:55:20 +02:00
85d9361bfb Fix fill with char on older compilers 2017-05-05 07:03:16 +02:00
a6a6f496eb Implement string slice assignments 2017-05-04 23:17:50 +02:00
db12f03264 Merge branch 'master' into utf8string 2017-05-03 19:15:13 +02:00
231aedb8ad Add HMAC 2017-05-03 19:05:23 +02:00
c3b63ee40d Merge branch 'master' into utf8string 2017-05-02 10:59:00 +02:00
6f405c5e08 Make Vector's opSliceAssign accept only own ranges
Vector.opSliceAssign and Vector.opIndexAssign should accept only vector
ranges. For assigning other ranges, std.algorithm.mutation.copy and
std.algorithm.mutation.fill should be used.
2017-05-02 10:56:32 +02:00
16cf8478cf Add ByCodePoint 2017-05-01 20:17:37 +02:00
8915a0c7a7 Implement opCmp and opEquals for the String 2017-05-01 18:43:12 +02:00
e5c7edb72c Implement String opAssign 2017-05-01 12:58:37 +02:00
64e0d666ed Merge branch 'master' of github.com:caraus-ecms/tanya into utf8string 2017-05-01 09:59:29 +02:00
f2aac680c5 Fix container ctors and opAssign ref parameters
Container constructors and opAssign should accept any ref container and
not only const, otherwise the source container will be copied because
the constructor/opAssign without ref would be a better match.
2017-05-01 09:48:12 +02:00
65c3ca14ec Integer storage optimization 2017-04-30 16:07:44 +02:00
4fa47153ba Make Integer representation little endian 2017-04-25 19:50:06 +02:00
d629525a4b Make String to be a char Slice alias 2017-04-21 14:03:20 +02:00
33d321f0d7 Merge branch 'master' into utf8string 2017-04-20 17:32:59 +02:00
3d64d59ba9 Merge branch 'master' of github.com:caraus-ecms/tanya 2017-04-20 17:32:29 +02:00
4635835a99 Rename Vector range to Slice 2017-04-20 17:32:16 +02:00
8725ec5f20 Make Integer representation little endian 2017-04-19 13:49:44 +02:00
9a4c8cea06 Merge branch 'master' into utf8string 2017-04-16 20:52:40 +02:00
eb360bda38 Add unittest to check RefCounted calles struct destructors 2017-04-16 20:52:24 +02:00
4b1cd2cbfd Merge branch 'master' into utf8string 2017-04-16 20:15:11 +02:00
628153e2e8 Make RefCounted work with dynamic arrays 2017-04-16 20:14:04 +02:00
7aa9ac9f4a Add internal finalize method for finalizing an object without deallocating 2017-04-16 20:13:20 +02:00
cd944a61b7 Merge remote-tracking branch 'origin/master' into utf8string 2017-04-13 16:03:00 +02:00
8156d0fe3a Add support for dmd 2.074.0, remove 2.070.2 2017-04-13 16:02:18 +02:00
47ef787353 Add missing constructors to the String 2017-04-10 08:10:08 +02:00
6436ad49df Add ByteRange to the String 2017-04-08 17:44:08 +02:00
e1964e47a5 Merge branch 'master' into utf8string 2017-04-08 08:44:21 +02:00
6e2ce5d686 Remove opApply from containers
opApply requires additional overloads for the const containers (with a
const delegate). If using a templated opApply foreach cannot infer the
types for the variables. foreach with one argument still works
(opIndex() is used), for more complex cases slicing should be used.
2017-04-07 16:00:50 +02:00
ba6bf554fb Make SList range public 2017-04-07 15:17:14 +02:00
b1d2b9bd9e Fix Vector.insertAfter/Before an empty range 2017-04-04 15:11:14 +02:00
9b953198fa Fix network.inet release build 2017-04-04 08:36:42 +02:00
bc2a6d2703 Swap toHostOrder template parameters 2017-04-03 15:32:15 +02:00
b458250ad7 Make NetworkOrder work with 8-byte integers 2017-04-02 20:55:22 +02:00
b08d5e5d83 Add tanya.network.inet.toHostOrder
The function reverts NetworkOrder.
2017-04-02 11:16:08 +02:00
445b872e91 Add tanya.network.inet.NetworkOrder
NetworkOrder converts an integral type into a bidirectional range with
big-endian byte order.
2017-04-02 09:29:54 +02:00
5e16fe98d6 Add tanya.network package file 2017-04-01 09:53:59 +02:00
43319e4e3a Initialization from a UTF-16 string 2017-02-27 11:27:24 +01:00
33dbf042c2 Add dchar constructor 2017-02-26 22:40:27 +01:00
885fca9b5e Add String.reserve and shrink 2017-02-20 12:01:15 +01:00
074d027629 Merge branch 'master' into utf8string 2017-02-20 08:02:01 +01:00
f4b90d8b51 Add string skeleton 2017-02-10 19:22:46 +01:00
29 changed files with 9800 additions and 6200 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

View File

@ -7,14 +7,17 @@ os:
language: d language: d
d: d:
- dmd-2.074.0
- dmd-2.073.2 - dmd-2.073.2
- dmd-2.072.2 - dmd-2.072.2
- dmd-2.071.2 - dmd-2.071.2
- dmd-2.070.2
env: env:
matrix: matrix:
- ARCH=x86_64 - ARCH=x86_64
script: script:
- dub test --arch=$ARCH - dub test -b unittest-cov --arch=$ARCH
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,6 +1,8 @@
# Tanya # Tanya
[![Build Status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya) [![Build status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya)
[![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
[![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya) [![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
[![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya) [![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya)
[![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE) [![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE)
@ -21,32 +23,32 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
Tanya consists of the following packages: Tanya consists of the following packages:
* `async`: Event loop (epoll, kqueue and IOCP). * `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Vector, Singly linked list, buffers. * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string.
* `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 (allocator, reference counting,
helper functions). helper functions).
* `network`: URL-Parsing, sockets. * `network`: URL-Parsing, sockets, utilities.
### Supported compilers ### Supported compilers
* dmd 2.073.2 | dmd |
* dmd 2.072.2 |:-------:|
* dmd 2.071.2 | 2.074.0 |
* dmd 2.070.2 | 2.073.2 |
| 2.072.2 |
| 2.071.2 |
### Current status ### Current status
The library is currently under development, but the API is becoming gradually Following modules are under development:
stable.
`container`s are being extended to support ranges. Also following modules are | Feature | Branch | Build status |
coming soon: |--------------|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* UTF-8 string. | BitVector | bitvector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) [![bitvector](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/bitvector?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
* Hash table. | TLS | crypto | [![crypto](https://travis-ci.org/caraus-ecms/tanya.svg?branch=crypto)](https://travis-ci.org/caraus-ecms/tanya) [![crypto](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/crypto?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
| File IO | io | [![io](https://travis-ci.org/caraus-ecms/tanya.svg?branch=io)](https://travis-ci.org/caraus-ecms/tanya) [![io](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/io?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
`math` package contains an arbitrary precision integer implementation that | Hash table | horton-table | [![horton-table](https://travis-ci.org/caraus-ecms/tanya.svg?branch=horton-table)](https://travis-ci.org/caraus-ecms/tanya) [![horton-table](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/horton-table?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/horton-table) |
needs more test cases, better performance and some additional features
(constructing from a string and an ubyte array, and converting it back).
### Further characteristics ### Further characteristics
@ -57,10 +59,14 @@ 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. Thread-safity should be added later.
## Release management
3-week release cycle.
## Contributing ## Contributing
Since I'm mostly busy writing new code and implementing new features I would 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 appreciate, if anyone uses the library. It would help me to improve the
codebase and fix issues. codebase and fix issues.
Feel free to contact me if you have any questions. Feel free to contact me if you have any questions: info@caraus.de.

52
appveyor.yml Normal file
View File

@ -0,0 +1,52 @@
platform: x64
os: Visual Studio 2017
environment:
matrix:
- DC: dmd
DVersion: 2.074.0
arch: x86
- DC: dmd
DVersion: 2.073.2
arch: x86
- DC: dmd
DVersion: 2.072.2
arch: x86
- DC: dmd
DVersion: 2.071.2
arch: x86
skip_tags: true
install:
- ps: function SetUpDCompiler
{
$env:toolchain = "msvc";
$version = $env:DVersion;
Invoke-WebRequest "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z" -OutFile "c:\dmd.7z";
echo "finished.";
pushd c:\\;
7z x dmd.7z > $null;
popd;
}
- ps: SetUpDCompiler
- ps: if($env:DVersion -eq "2.071.2"){
Invoke-WebRequest "http://code.dlang.org/files/dub-1.2.1-windows-x86.zip" -OutFile "dub.zip";
7z x dub.zip -odub > $null;
Move-Item "dub/dub.exe" "C:\dmd2\windows\bin"
}
before_build:
- ps: $env:PATH += ";C:\dmd2\windows\bin;";
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=%arch%
build_script:
- echo dummy build script - dont remove me
test_script:
- echo %DC%
- echo %PATH%
- 'dub --version'
- '%DC% --version'
- dub test --arch=x86 --compiler=%DC%

View File

@ -18,7 +18,7 @@ import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -37,7 +37,7 @@ extern (C) nothrow @nogc
final class EpollLoop : SelectorLoop final class EpollLoop : SelectorLoop
{ {
protected int fd; protected int fd;
private Vector!epoll_event events; private Array!epoll_event events;
/** /**
* Initializes the loop. * Initializes the loop.
@ -49,7 +49,7 @@ final class EpollLoop : SelectorLoop
throw defaultAllocator.make!BadLoopException("epoll initialization failed"); throw defaultAllocator.make!BadLoopException("epoll initialization failed");
} }
super(); super();
events = Vector!epoll_event(maxEvents, MmapPool.instance); events = Array!epoll_event(maxEvents, MmapPool.instance);
} }
/** /**

View File

@ -50,7 +50,7 @@ import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -116,8 +116,8 @@ extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
final class KqueueLoop : SelectorLoop final class KqueueLoop : SelectorLoop
{ {
protected int fd; protected int fd;
private Vector!kevent_t events; private Array!kevent_t events;
private Vector!kevent_t changes; private Array!kevent_t changes;
private size_t changeCount; private size_t changeCount;
/** /**
@ -139,8 +139,8 @@ final class KqueueLoop : SelectorLoop
throw make!BadLoopException(defaultAllocator, throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed"); "kqueue initialization failed");
} }
events = Vector!kevent_t(64, MmapPool.instance); events = Array!kevent_t(64, MmapPool.instance);
changes = Vector!kevent_t(64, MmapPool.instance); changes = Array!kevent_t(64, MmapPool.instance);
} }
/** /**

View File

@ -17,7 +17,7 @@ import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.container.vector; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -78,7 +78,8 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
return cast(ConnectedSocket) socket_; return cast(ConnectedSocket) socket_;
} }
private @property void socket(ConnectedSocket socket) pure nothrow @safe @nogc private @property void socket(ConnectedSocket socket)
pure nothrow @safe @nogc
in in
{ {
assert(socket !is null); assert(socket !is null);
@ -133,7 +134,9 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
void close() @nogc void close() @nogc
{ {
closing = true; closing = true;
loop.reify(this, EventMask(Event.read, Event.write), EventMask(Event.write)); loop.reify(this,
EventMask(Event.read, Event.write),
EventMask(Event.write));
} }
/** /**
@ -205,20 +208,20 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
abstract class SelectorLoop : Loop abstract class SelectorLoop : Loop
{ {
/// Pending connections. /// Pending connections.
protected Vector!SocketWatcher connections; protected Array!SocketWatcher connections;
this() @nogc this() @nogc
{ {
super(); super();
connections = Vector!SocketWatcher(maxEvents, MmapPool.instance); connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
} }
~this() @nogc ~this() @nogc
{ {
foreach (ref connection; connections) foreach (ref connection; connections)
{ {
// We want to free only the transports. ConnectionWatcher are created by the // We want to free only the transports. ConnectionWatcher are
// user and should be freed by himself. // created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null) if (cast(StreamTransport) connection !is null)
{ {
MmapPool.instance.dispose(connection); MmapPool.instance.dispose(connection);

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,18 @@ module tanya.container.entry;
package struct SEntry(T) package struct SEntry(T)
{ {
/// Item content. // Item content.
T content; T content;
/// Next item. // Next item.
SEntry* next; SEntry* next;
} }
package struct DEntry(T)
{
// Item content.
T content;
// Previous and next item.
DEntry* next, prev;
}

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,8 @@
*/ */
module tanya.container; module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer; public import tanya.container.buffer;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.vector; public import tanya.container.string;
public import tanya.container.queue; public import tanya.container.queue;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -87,22 +87,22 @@ in
} }
body body
{ {
size_t i = y.length; size_t i;
auto tmp1 = Integer(x, x.allocator); auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator); auto result = Integer(x.allocator);
bool firstBit;
if (x.length == 0 && i != 0) if (x.size == 0 && y.size != 0)
{ {
i = 0; i = y.size;
} }
else else
{ {
result = 1; result = 1;
} }
while (i) while (i < y.size)
{ {
--i; for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
for (ubyte mask = 0x01; mask; mask <<= 1)
{ {
if (y.rep[i] & mask) if (y.rep[i] & mask)
{ {
@ -113,6 +113,7 @@ body
tmp1 *= tmp2; tmp1 *= tmp2;
tmp1 %= z; tmp1 %= z;
} }
++i;
} }
return result; return result;
} }

View File

@ -11,6 +11,7 @@
module tanya.memory; module tanya.memory;
import core.exception; import core.exception;
import std.algorithm.iteration;
public import std.experimental.allocator : make; public import std.experimental.allocator : make;
import std.traits; import std.traits;
public import tanya.memory.allocator; public import tanya.memory.allocator;
@ -87,8 +88,8 @@ shared Allocator allocator;
shared static this() nothrow @trusted @nogc shared static this() nothrow @trusted @nogc
{ {
import tanya.memory.mallocator; import tanya.memory.mmappool;
allocator = Mallocator.instance; allocator = MmapPool.instance;
} }
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
@ -202,40 +203,32 @@ private unittest
assert(p is null); assert(p is null);
} }
/** /*
* Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). * Destroys the object.
* It is assumed the respective entities had been allocated with the same * Returns the memory should be freed.
* allocator.
*
* Params:
* T = Type of $(D_PARAM p).
* allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed.
*/ */
void dispose(T)(shared Allocator allocator, auto ref T* p) package(tanya) void[] finalize(T)(ref T* p)
{ {
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
{ {
destroy(*p); destroy(*p);
} }
() @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); return (cast(void*) p)[0 .. T.sizeof];
p = null;
} }
/// Ditto. package(tanya) void[] finalize(T)(ref T p)
void dispose(T)(shared Allocator allocator, auto ref T p)
if (is(T == class) || is(T == interface)) if (is(T == class) || is(T == interface))
{ {
if (p is null) if (p is null)
{ {
return; return null;
} }
static if (is(T == interface)) static if (is(T == interface))
{ {
version(Windows) version(Windows)
{ {
import core.sys.windows.unknwn : IUnknown; import core.sys.windows.unknwn : IUnknown;
static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in "
~ __PRETTY_FUNCTION__); ~ __PRETTY_FUNCTION__);
} }
auto ob = cast(Object) p; auto ob = cast(Object) p;
@ -244,19 +237,13 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
{ {
alias ob = p; alias ob = p;
} }
auto ptr = cast(void *) ob; auto ptr = cast(void*) ob;
auto support = ptr[0 .. typeid(ob).initializer.length]; auto support = ptr[0 .. typeid(ob).initializer.length];
scope (success)
{
() @trusted { allocator.deallocate(support); }();
p = null;
}
auto ppv = cast(void**) ptr; auto ppv = cast(void**) ptr;
if (!*ppv) if (!*ppv)
{ {
return; return null;
} }
auto pc = cast(ClassInfo*) *ppv; auto pc = cast(ClassInfo*) *ppv;
scope (exit) scope (exit)
@ -280,21 +267,35 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
{ {
_d_monitordelete(cast(Object) ptr, true); _d_monitordelete(cast(Object) ptr, true);
} }
return support;
} }
/// Ditto. package(tanya) void[] finalize(T)(ref T[] p)
void dispose(T)(shared Allocator allocator, auto ref T[] p)
{ {
static if (hasElaborateDestructor!(typeof(p[0]))) static if (hasElaborateDestructor!(typeof(p[0])))
{ {
import std.algorithm.iteration; p.each!((ref e) => destroy(e));
p.each!(e => destroy(e));
} }
() @trusted { allocator.deallocate(p); }(); return p;
}
/**
* Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T).
* It is assumed the respective entities had been allocated with the same
* allocator.
*
* Params:
* T = Type of $(D_PARAM p).
* allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed.
*/
void dispose(T)(shared Allocator allocator, auto ref T p)
{
() @trusted { allocator.deallocate(finalize(p)); }();
p = null; p = null;
} }
unittest private unittest
{ {
struct S struct S
{ {

View File

@ -14,34 +14,28 @@ import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import std.conv;
import std.range;
import std.traits; import std.traits;
import tanya.memory; import tanya.memory;
/** private template Payload(T)
* Reference-counted object containing a $(D_PARAM T) value as payload.
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
* when the reference count goes down to zero, frees the underlying store.
*
* Params:
* T = Type of the reference-counted value.
*/
struct RefCounted(T)
{ {
static if (is(T == class) || is(T == interface)) static if (is(T == class) || is(T == interface) || isArray!T)
{ {
private alias Payload = T; alias Payload = T;
} }
else else
{ {
private alias Payload = T*; alias Payload = T*;
} }
}
private class Storage final class RefCountedStore(T)
{ {
private Payload payload; T payload;
private size_t counter = 1; size_t counter = 1;
private final size_t opUnary(string op)() pure nothrow @safe @nogc size_t opUnary(string op)()
if (op == "--" || op == "++") if (op == "--" || op == "++")
in in
{ {
@ -52,7 +46,7 @@ struct RefCounted(T)
mixin("return " ~ op ~ "counter;"); mixin("return " ~ op ~ "counter;");
} }
private final int opCmp(size_t counter) const pure nothrow @safe @nogc int opCmp(const size_t counter)
{ {
if (this.counter > counter) if (this.counter > counter)
{ {
@ -68,37 +62,47 @@ struct RefCounted(T)
} }
} }
private final int opEquals(size_t counter) const pure nothrow @safe @nogc int opEquals(const size_t counter)
{ {
return this.counter == counter; return this.counter == counter;
} }
} }
private final class RefCountedStorage : Storage private void separateDeleter(T)(RefCountedStore!T storage,
{ shared Allocator allocator)
private shared Allocator allocator; {
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
this(shared Allocator allocator) pure nothrow @safe @nogc private void unifiedDeleter(T)(RefCountedStore!T storage,
in shared Allocator allocator)
{ {
assert(allocator !is null); auto ptr1 = finalize(storage);
} auto ptr2 = finalize(storage.payload);
body allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
{ }
this.allocator = allocator;
}
~this() nothrow @nogc /**
{ * Reference-counted object containing a $(D_PARAM T) value as payload.
allocator.dispose(payload); * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
} * when the reference count goes down to zero, frees the underlying store.
} *
* Params:
* T = Type of the reference-counted value.
*/
struct RefCounted(T)
{
private alias Storage = RefCountedStore!(Payload!T);
private Storage storage; private Storage storage;
private void function(Storage storage,
shared Allocator allocator) @nogc deleter;
invariant invariant
{ {
assert(storage is null || allocator_ !is null); assert(storage is null || allocator_ !is null);
assert(storage is null || deleter !is null);
} }
/** /**
@ -112,11 +116,18 @@ struct RefCounted(T)
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(Payload value, shared Allocator allocator = defaultAllocator) this()(auto ref Payload!T value,
shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
move(value, storage.payload); this.deleter = &separateDeleter!(Payload!T);
move(value, this.storage.payload);
static if (__traits(isRef, value))
{
value = null;
}
} }
/// Ditto. /// Ditto.
@ -137,7 +148,7 @@ struct RefCounted(T)
{ {
if (count != 0) if (count != 0)
{ {
++storage; ++this.storage;
} }
} }
@ -148,9 +159,9 @@ struct RefCounted(T)
*/ */
~this() ~this()
{ {
if (storage !is null && !(storage.counter && --storage)) if (this.storage !is null && !(this.storage.counter && --this.storage))
{ {
allocator_.dispose(storage); deleter(this.storage, allocator);
} }
} }
@ -161,79 +172,74 @@ struct RefCounted(T)
* If it is the last reference of the previously owned object, * If it is the last reference of the previously owned object,
* it will be destroyed. * it will be destroyed.
* *
* To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). * To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
* *
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
* be used. If you need a different allocator, create a new * be used. If you need a different allocator, create a new
* $(D_PSYMBOL RefCounted) and assign it. * $(D_PSYMBOL RefCounted) and assign it.
* *
* Params: * Params:
* rhs = $(D_KEYWORD this). * rhs = New object.
*
* Returns: $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(Payload rhs) ref typeof(this) opAssign()(auto ref Payload!T rhs)
{ {
if (storage is null) if (this.storage is null)
{ {
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!(Payload!T);
} }
else if (storage > 1) else if (this.storage > 1)
{ {
--storage; --this.storage;
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
} this.deleter = &separateDeleter!(Payload!T);
else if (cast(RefCountedStorage) storage is null)
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0);
allocator.dispose(storage);
storage = allocator.make!RefCountedStorage(allocator);
} }
else else
{ {
allocator.dispose(storage.payload); finalize(this.storage.payload);
this.storage.payload = Payload!T.init;
} }
move(rhs, storage.payload); move(rhs, this.storage.payload);
return this; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
if (storage is null) if (this.storage is null)
{ {
return this; return this;
} }
else if (storage > 1) else if (this.storage > 1)
{ {
--storage; --this.storage;
storage = null;
}
else if (cast(RefCountedStorage) storage is null)
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0);
allocator.dispose(storage);
return this;
} }
else else
{ {
allocator.dispose(storage.payload); deleter(this.storage, allocator);
} }
this.storage = null;
return this; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(allocator_, rhs.allocator_); swap(this.allocator_, rhs.allocator_);
swap(storage, rhs.storage); swap(this.storage, rhs.storage);
swap(this.deleter, rhs.deleter);
return this; return this;
} }
/** /**
* Returns: Reference to the owned object. * Returns: Reference to the owned object.
*
* Precondition: $(D_INLINECODE cound > 0).
*/ */
inout(Payload) get() inout pure nothrow @safe @nogc Payload!T get() pure nothrow @safe @nogc
in in
{ {
assert(count > 0, "Attempted to access an uninitialized reference."); assert(count > 0, "Attempted to access an uninitialized reference.");
@ -243,7 +249,7 @@ struct RefCounted(T)
return storage.payload; return storage.payload;
} }
static if (isPointer!Payload) version (D_Ddoc)
{ {
/** /**
* Params: * Params:
@ -254,6 +260,11 @@ struct RefCounted(T)
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
ref T opUnary(string op)()
if (op == "*");
}
else static if (isPointer!(Payload!T))
{
ref T opUnary(string op)() ref T opUnary(string op)()
if (op == "*") if (op == "*")
{ {
@ -355,6 +366,44 @@ private unittest
assert(rc.count == 1); assert(rc.count == 1);
} }
private unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1);
void func(RefCounted!int rc)
{
assert(rc.count == 2);
rc = null;
assert(!rc.isInitialized);
assert(rc.count == 0);
}
assert(rc.count == 1);
func(rc);
assert(rc.count == 1);
rc = null;
assert(!rc.isInitialized);
assert(rc.count == 0);
}
private unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(*rc == 5);
void func(RefCounted!int rc)
{
assert(rc.count == 2);
rc = defaultAllocator.refCounted!int(4);
assert(*rc == 4);
assert(rc.count == 1);
}
func(rc);
assert(*rc == 5);
}
private unittest private unittest
{ {
static assert(is(typeof(RefCounted!int.storage.payload) == int*)); static assert(is(typeof(RefCounted!int.storage.payload) == int*));
@ -380,15 +429,22 @@ private unittest
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/ */
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T if (!is(T == interface) && !isAbstractClass!T
&& !isArray!T && !isAssociativeArray!T) && !isAssociativeArray!T && !isArray!T)
in
{
assert(allocator !is null);
}
body
{ {
auto rc = typeof(return)(allocator); auto rc = typeof(return)(allocator);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize); const size = alignedSize(stateSize!T + storageSize);
auto mem = (() @trusted => allocator.allocate(size))(); auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
@ -399,7 +455,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
static if (is(T == class)) static if (is(T == class))
{ {
@ -410,9 +466,38 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args); rc.storage.payload = emplace!T(ptr, args);
} }
rc.deleter = &unifiedDeleter!(Payload!T);
return rc; return rc;
} }
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
* $(D_PSYMBOL RefCounted).
*
* Params:
* T = Array type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
*/
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
@trusted
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return RefCounted!T(payload, allocator);
}
/// ///
unittest unittest
{ {
@ -456,3 +541,272 @@ private @nogc unittest
assert(rc.count); assert(rc.count);
} }
} }
private @nogc unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
}
private @nogc unittest
{
static bool destroyed = false;
struct F
{
~this() @nogc
{
destroyed = true;
}
}
{
auto rc = defaultAllocator.refCounted!F();
}
assert(destroyed);
}
/**
* $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
*
* Params:
* T = Value type.
*/
struct Scoped(T)
{
private Payload!T payload;
invariant
{
assert(payload is null || allocator_ !is null);
}
/**
* Takes ownership over $(D_PARAM value), setting the counter to 1.
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
*
* Params:
* value = Value whose ownership is taken over.
* allocator = Allocator used to destroy the $(D_PARAM value) and to
* allocate/deallocate internal storage.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this()(auto ref Payload!T value,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
move(value, this.payload);
static if (__traits(isRef, value))
{
value = null;
}
}
/// Ditto.
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
/**
* $(D_PSYMBOL Scoped) is noncopyable.
*/
@disable this(this);
/**
* Destroys the owned object.
*/
~this()
{
if (this.payload !is null)
{
allocator.dispose(this.payload);
}
}
/**
* Initialized this $(D_PARAM Scoped) and takes ownership over
* $(D_PARAM rhs).
*
* To reset $(D_PSYMBOL Scoped) assign $(D_KEYWORD null).
*
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
* be used. If you need a different allocator, create a new
* $(D_PSYMBOL Scoped) and assign it.
*
* Params:
* rhs = New object.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign()(auto ref Payload!T rhs)
{
allocator.dispose(this.payload);
move(rhs, this.payload);
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(null))
{
allocator.dispose(this.payload);
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(this.allocator_, rhs.allocator_);
swap(this.payload, rhs.payload);
return this;
}
/**
* Returns: Reference to the owned object.
*/
Payload!T get() pure nothrow @safe @nogc
{
return payload;
}
version (D_Ddoc)
{
/**
* Params:
* op = Operation.
*
* Dereferences the pointer. It is defined only for pointers, not for
* reference types like classes, that can be accessed directly.
*
* Returns: Reference to the pointed value.
*/
ref T opUnary(string op)()
if (op == "*");
}
else static if (isPointer!(Payload!T))
{
ref T opUnary(string op)()
if (op == "*")
{
return *payload;
}
}
mixin DefaultAllocator;
alias get this;
}
///
@nogc unittest
{
auto p = defaultAllocator.make!int(5);
auto s = Scoped!int(p, defaultAllocator);
assert(p is null);
assert(*s == 5);
}
///
@nogc unittest
{
static bool destroyed = false;
struct F
{
~this() @nogc
{
destroyed = true;
}
}
{
auto s = Scoped!F(defaultAllocator.make!F(), defaultAllocator);
}
assert(destroyed);
}
/**
* Constructs a new object of type $(D_PARAM T) and wraps it in a
* $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
* the constructor of $(D_PARAM T).
*
* Params:
* T = Type of the constructed object.
* A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
* Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T
&& !isAssociativeArray!T && !isArray!T)
in
{
assert(allocator !is null);
}
body
{
auto payload = allocator.make!(T, shared Allocator, A)(args);
return Scoped!T(payload, allocator);
}
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
* $(D_PSYMBOL Scoped).
*
* Params:
* T = Array type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
*/
Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
@trusted
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return Scoped!T(payload, allocator);
}
private unittest
{
static assert(is(typeof(defaultAllocator.scoped!B(5))));
static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
}
private unittest
{
auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
s = null;
assert(s is null);
}
private unittest
{
auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
s = defaultAllocator.scoped!int(4);
assert(*s == 4);
}

356
source/tanya/network/inet.d Normal file
View File

@ -0,0 +1,356 @@
/* 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/. */
/**
* Internet utilities.
*
* Copyright: Eugene Wissner 2016-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.network.inet;
import std.math;
import std.range.primitives;
import std.traits;
version (unittest)
{
version (Windows)
{
import core.sys.windows.winsock2;
version = PlattformUnittest;
}
else version (Posix)
{
import core.sys.posix.arpa.inet;
version = PlattformUnittest;
}
}
/**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
*
* The range is bidirectional. The byte order is always big-endian.
*
* It can accept any unsigned integral type but the value should fit
* in $(D_PARAM L) bytes.
*
* Params:
* L = Desired range length.
*/
struct NetworkOrder(uint L)
if (L > ubyte.sizeof && L <= ulong.sizeof)
{
static if (L > uint.sizeof)
{
private alias StorageType = ulong;
}
else static if (L > ushort.sizeof)
{
private alias StorageType = uint;
}
else static if (L > ubyte.sizeof)
{
private alias StorageType = ushort;
}
else
{
private alias StorageType = ubyte;
}
private StorageType value;
private size_t size = L;
const pure nothrow @safe @nogc invariant
{
assert(this.size <= L);
}
/**
* Constructs a new range.
*
* $(D_PARAM T) can be any unsigned type but $(D_PARAM value) cannot be
* larger than the maximum can be stored in $(D_PARAM L) bytes. Otherwise
* an assertion failure will be caused.
*
* Params:
* T = Value type.
* value = The value should be represented by this range.
*
* Precondition: $(D_INLINECODE value <= 2 ^^ (length * 8) - 1).
*/
this(T)(const T value)
if (isUnsigned!T)
in
{
assert(value <= pow(2, L * 8) - 1);
}
body
{
this.value = value & StorageType.max;
}
/**
* Returns: LSB.
*
* Precondition: $(D_INLINECODE length > 0).
*/
@property ubyte back() const
in
{
assert(this.length > 0);
}
body
{
return this.value & 0xff;
}
/**
* Returns: MSB.
*
* Precondition: $(D_INLINECODE length > 0).
*/
@property ubyte front() const
in
{
assert(this.length > 0);
}
body
{
return (this.value >> ((this.length - 1) * 8)) & 0xff;
}
/**
* Eliminates the LSB.
*
* Precondition: $(D_INLINECODE length > 0).
*/
void popBack()
in
{
assert(this.length > 0);
}
body
{
this.value >>= 8;
--this.size;
}
/**
* Eliminates the MSB.
*
* Precondition: $(D_INLINECODE length > 0).
*/
void popFront()
in
{
assert(this.length > 0);
}
body
{
this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8);
--this.size;
}
/**
* Returns: Copy of this range.
*/
typeof(this) save() const
{
return this;
}
/**
* Returns: Whether the range is empty.
*/
@property bool empty() const
{
return this.length == 0;
}
/**
* Returns: Byte length.
*/
@property size_t length() const
{
return this.size;
}
}
///
pure nothrow @safe @nogc unittest
{
auto networkOrder = NetworkOrder!3(0xae34e2u);
assert(!networkOrder.empty);
assert(networkOrder.front == 0xae);
networkOrder.popFront();
assert(networkOrder.length == 2);
assert(networkOrder.front == 0x34);
assert(networkOrder.back == 0xe2);
networkOrder.popBack();
assert(networkOrder.length == 1);
assert(networkOrder.front == 0x34);
assert(networkOrder.front == 0x34);
networkOrder.popFront();
assert(networkOrder.empty);
}
// Static.
private unittest
{
static assert(isBidirectionalRange!(NetworkOrder!4));
static assert(isBidirectionalRange!(NetworkOrder!8));
static assert(!is(NetworkOrder!9));
static assert(!is(NetworkOrder!1));
}
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.length == 4);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 3));
networkOrder.popBack();
assert(networkOrder.length == 3);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 2);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 2));
assert(networkOrder.back == *(p + 2));
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(networkOrder.length == 2);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 1));
networkOrder.popBack();
assert(networkOrder.length == 1);
assert(networkOrder.front == *p);
assert(networkOrder.back == *p);
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
networkOrder = NetworkOrder!2(value);
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 1));
networkOrder.popFront();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
}
}
/**
* Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to
* $(D_PARAM T).
*
* The byte order of $(D_PARAM r) is assumed to be big-endian. The length
* cannot be larger than $(D_INLINECODE T.sizeof). Otherwise an assertion
* failure will be caused.
*
* Params:
* T = Desired return type.
* R = Range type.
* range = Input range.
*
* Returns: Integral representation of $(D_PARAM range) with the host byte
* order.
*/
T toHostOrder(T = size_t, R)(R range)
if (isInputRange!R
&& !isInfinite!R
&& is(Unqual!(ElementType!R) == ubyte)
&& isUnsigned!T)
{
T ret;
ushort pos = T.sizeof * 8;
for (; !range.empty && range.front == 0; pos -= 8, range.popFront())
{
}
for (; !range.empty; range.popFront())
{
assert(pos != 0);
pos -= 8;
ret |= (cast(T) range.front) << pos;
}
return ret >> pos;
}
///
pure nothrow @safe @nogc unittest
{
const value = 0xae34e2u;
auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.toHostOrder() == value);
}
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(p[0 .. uint.sizeof].toHostOrder() == value);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(p[0 .. ushort.sizeof].toHostOrder() == value);
}
}
}

View File

@ -0,0 +1,17 @@
/* 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/. */
/**
* Network programming.
*
* Copyright: Eugene Wissner 2016-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.network;
public import tanya.network.inet;
public import tanya.network.socket;
public import tanya.network.url;

View File

@ -3,7 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Copyright: Eugene Wissner 2016. * Socket programming.
*
* 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/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -1004,10 +1006,14 @@ interface ConnectionOrientedSocket
*/ */
enum Flag : int enum Flag : int
{ {
none = 0, /// No flags specified /// No flags specified.
outOfBand = MSG_OOB, /// Out-of-band stream data none = 0,
peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving /// Out-of-band stream data.
dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending outOfBand = MSG_OOB,
/// Peek at incoming data without removing it from the queue, only for receiving.
peek = MSG_PEEK,
/// Data should not be subject to routing; this flag may be ignored. Only for sending.
dontRoute = MSG_DONTROUTE,
} }
alias Flags = BitFlags!Flag; alias Flags = BitFlags!Flag;

View File

@ -3,10 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Copyright: Eugene Wissner 2016. * URL parser.
*
* 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/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.network.url; module tanya.network.url;
@ -911,8 +913,8 @@ struct URL
} }
} }
~this() ~this()
{ {
if (scheme !is null) if (scheme !is null)
{ {
scheme = null; scheme = null;
@ -941,7 +943,7 @@ struct URL
{ {
fragment = null; fragment = null;
} }
} }
/** /**
* Attempts to parse and set the port. * Attempts to parse and set the port.
@ -1107,7 +1109,7 @@ URL parseURL(typeof(null) T)(in char[] source)
/// Ditto. /// Ditto.
const(char)[] parseURL(immutable(char)[] T)(in char[] source) const(char)[] parseURL(immutable(char)[] T)(in char[] source)
if (T == "scheme" if (T == "scheme"
|| T =="host" || T == "host"
|| T == "user" || T == "user"
|| T == "pass" || T == "pass"
|| T == "path" || T == "path"