122 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
647cfe03c2 Update latest supported compiler 2017-03-29 17:23:10 +02:00
4cd6126d6b Fix SList documentation for insertFront and insertBefore 2017-03-29 17:22:25 +02:00
b870179a35 Move bitvector to another branch till it is finished 2017-03-29 11:17:03 +02:00
aabb4fb534 Add SList.opAssign 2017-03-29 10:35:45 +02:00
4d8b95812e Implement opAssign for the Vector 2017-03-28 20:42:42 +02:00
e921413249 Merge branch 'master' of github.com:caraus-ecms/tanya 2017-03-24 20:54:47 +01:00
49cae88645 Add insertBefore and remove to SList 2017-03-24 20:54:28 +01:00
402fdfae89 math.mp: Fix initialization issues after resizing 2017-03-23 15:36:17 +01:00
7892c1a930 Remove Init template parameter from memory.resize() 2017-03-22 08:51:00 +01:00
b90517580e Merge math.mp.Integer changes from the crypto branch 2017-03-21 19:25:12 +01:00
85380ac3fc Remove makeArray import 2017-03-19 06:54:59 +01:00
b90c56395c Remove resizeArray alias 2017-03-19 06:10:27 +01:00
d0ada39fa7 Add Mallocator as an alternative allocator 2017-03-18 08:07:01 +01:00
f4145abfd1 Add SList constructors 2017-03-09 06:07:23 +01:00
093d499729 Fix element order inserted from a range into list 2017-03-08 07:12:23 +01:00
f90a03501b Move BitVector from the crypto branch 2017-03-02 11:27:26 +01:00
c6a99b114e SList.insertFront for ranges 2017-03-01 19:23:54 +01: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
3c23aca6a6 Improve Vector module and reserve documentation 2017-02-20 12:03:49 +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
70632d975d Add documentation link 2017-02-18 16:35:06 +01:00
d210a39249 Implement IOCPTransport.close and isClosing 2017-02-18 14:10:54 +01:00
e86ff63f91 Add DuplexTransport.close for the selector transport 2017-02-12 18:51:00 +01:00
3454a1965a Move all IOWatcher members to the transports 2017-02-11 19:47:57 +01:00
c41fa2e98f Adjust kqueue build 2017-02-10 23:01:33 +01:00
a012ca4003 Remove StreamTransport interface
Implement DuplexTransport and SocketTransport separately.
2017-02-10 22:30:12 +01:00
f4b90d8b51 Add string skeleton 2017-02-10 19:22:46 +01:00
b74e5aa4ee Make event loop implementations final 2017-02-10 19:19:37 +01:00
44ac15ab78 Move the bug tracker to Redmine 2017-02-10 17:28:55 +01:00
b1b652b943 Fix Kqueue buil with the new watcher-transport 2017-02-09 21:40:52 +01:00
530a482402 Inherit IOCPTransport from IOWatcher 2017-02-09 21:40:52 +01:00
f9023cf0ab Let Transport extend IOWatcher 2017-02-09 21:40:52 +01:00
0e91ea6786 Pass client socket directly to the IOWatcher 2017-02-09 21:40:52 +01:00
63c6226a2a Implement protocol property for IOCPTransport 2017-02-08 21:21:12 +01:00
48a49c2a2d Add protocol property to the transport
Transport should be protocol aware because it should be possible to
switch the protocol if the operation is supported, for example for upgrading
HTTP to web sockets or HTTP 1.1 to HTTP/2.
2017-02-08 20:04:05 +01:00
43c28b749d Rename async.loop.Loop.done_ to done
Since there is no property with a conflicting name.
2017-02-04 14:55:52 +01:00
241767df13 Move DefaultAllocator mixin to tanya.memory
Since it depends on defaultAllocator property defined in the module.
2017-02-03 13:07:40 +01:00
b2baba9237 SList: Add length and opEquals 2017-01-25 19:41:05 +01:00
3e36ec0984 Add support for dmd 2.070.2 2017-01-25 07:24:19 +01:00
5be89e4858 Add support for dmd 2.073.0 2017-01-24 16:15:14 +01:00
a48d9cb739 Add range support for SList 2017-01-24 08:20:07 +01:00
a7206cbd02 Fix #4 2017-01-22 10:48:34 +01:00
1450a6adfe Vector.insertBack: Accept by value and by ref 2017-01-20 05:40:28 +01:00
5fa9bd7b49 Rename Allocator.expand to reallocateInPlace
Rename and extend Allocator.expand to reallocateInPlace. The problem is
that realloc for example doesn't guarante that the shrinking of the
memory block won't cause address change. So not only expanding should
have "in place" version, but the shrinking as well.
2017-01-18 09:33:39 +01:00
c7eb233fc7 Make passed length parameter const 2017-01-16 10:56:45 +01:00
20c8b659d1 Remove mutation methods from vector range
in favor of std.algorithm.mutation.
2017-01-16 09:02:00 +01:00
4ea9c2b740 Vector: Reuse available methods 2017-01-15 08:38:19 +01:00
48205b2fc9 MmapPool: Add invariant
Add invariant to ensure blocks are linked correctly since this error
appeared several times.
2017-01-14 21:48:21 +01:00
f5fe7bec4a Queue optimization. Fix #5 2017-01-14 21:27:07 +01:00
c567b88d5d MmapPool: Fix expand block moving.
D dereferences the pointer wrong because of missing difference between .
and -> operators, if trying to write a block over another
block. So use memmove first to move the memory and then update the fields
that should be changed (only size).
2017-01-14 20:39:33 +01:00
fe884541fc Rename Vector.data to Vector.get 2017-01-13 15:23:42 +01:00
8973bdb2af Fix if EPOLLIN and EPOLLOUT come together 2017-01-13 10:20:11 +01:00
4c4e65b373 MmapPool: (p[] is null) != (p[].ptr is null) 2017-01-12 19:47:07 +01:00
7bed7f039f Remove default parameter value from Protocol.disconnected 2017-01-12 18:07:39 +01:00
8ddea0aa46 Loop.maxEvents is const, not inout const 2017-01-12 10:43:02 +01:00
cb6cc65113 async: Switch to the internal use of the vector instead of built-in arrays 2017-01-12 10:17:12 +01:00
4de42ca227 Use only one queue for the async events 2017-01-12 09:09:33 +01:00
42 changed files with 12993 additions and 9726 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

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
.dub
__test__*__
__test__*__.core
/tanya-test-library
/docs/
/docs.json

View File

@ -2,10 +2,13 @@ sudo: false
os:
- linux
- osx
language: d
d:
- dmd-2.074.0
- dmd-2.073.2
- dmd-2.072.2
- dmd-2.071.2
@ -14,4 +17,7 @@ env:
- ARCH=x86_64
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
[![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 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)
@ -13,57 +15,58 @@ 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)
* [Documentation](https://docs.caraus.io/tanya)
## Overview
Tanya consists of the following packages:
* `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Vector, Singly linked list, buffers.
* `crypto`: Work in progress TLS implementation.
* `math`: Multiple precision integer and a set of functions.
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocator, reference counting,
helper functions).
* `network`: URL-Parsing, sockets.
* `network`: URL-Parsing, sockets, utilities.
### Supported compilers
* dmd 2.072.2
* dmd 2.071.2
| dmd |
|:-------:|
| 2.074.0 |
| 2.073.2 |
| 2.072.2 |
| 2.071.2 |
### Current status
The library is currently under development, but some parts of it can already be
used.
Following modules are under development:
`network` and `async` exist for quite some time and could be better tested than
other components.
Containers were newly reworked and the API won't change significantly, but will
be only extended. The same is true for the `memory` package.
`math` package contains an arbitrary precision integer implementation that has
a stable API (that mostly consists of operator overloads), but still needs
testing and work on its performance.
I'm currently mostly working on `crypto` that is not a complete cryptographic
suite, but contains (will contain) algorithm implementations required by TLS.
| Feature | Branch | Build status |
|--------------|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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) |
| 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) |
| 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) |
### Further characteristics
* Tanya is a native D library.
* Documentation and usage examples can be found in the source code.
Online documentation will be published soon.
* 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.
## Release management
3-week release cycle.
## 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.
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

@ -1,6 +1,6 @@
{
"name": "tanya",
"description": "D library with event loop",
"description": "General purpose, @nogc library",
"license": "MPL-2.0",
"copyright": "(c) Eugene Wissner <info@caraus.de>",
"authors": [

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.event.epoll;
@ -18,6 +18,7 @@ import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
@ -33,10 +34,10 @@ extern (C) nothrow @nogc
int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
}
class EpollLoop : SelectorLoop
final class EpollLoop : SelectorLoop
{
protected int fd;
private epoll_event[] events;
private Array!epoll_event events;
/**
* Initializes the loop.
@ -45,18 +46,17 @@ class EpollLoop : SelectorLoop
{
if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
{
throw MmapPool.instance.make!BadLoopException("epoll initialization failed");
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
}
super();
MmapPool.instance.resizeArray(events, maxEvents);
events = Array!epoll_event(maxEvents, MmapPool.instance);
}
/**
* Free loop internals.
* Frees loop internals.
*/
~this() @nogc
{
MmapPool.instance.dispose(events);
close(fd);
}
@ -70,14 +70,9 @@ class EpollLoop : SelectorLoop
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
protected override bool reify(ConnectionWatcher watcher,
protected override bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
in
{
assert(watcher !is null);
}
body
{
int op = EPOLL_CTL_DEL;
epoll_event ev;
@ -110,7 +105,7 @@ class EpollLoop : SelectorLoop
{
// Don't block
immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto eventCount = epoll_wait(fd, events.ptr, maxEvents, timeout);
auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
if (eventCount < 0)
{
@ -123,29 +118,30 @@ class EpollLoop : SelectorLoop
for (auto i = 0; i < eventCount; ++i)
{
auto io = cast(IOWatcher) connections[events[i].data.fd];
auto transport = cast(StreamTransport) connections[events[i].data.fd];
if (io is null)
if (transport is null)
{
acceptConnections(connections[events[i].data.fd]);
auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
assert(connection !is null);
acceptConnections(connection);
}
else if (events[i].events & EPOLLERR)
{
kill(io, null);
kill(transport);
continue;
}
else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
{
auto transport = cast(SelectorStreamTransport) io.transport;
assert(transport !is null);
SocketException exception;
try
{
ptrdiff_t received;
do
{
received = transport.socket.receive(io.output[]);
io.output += received;
received = transport.socket.receive(transport.output[]);
transport.output += received;
}
while (received);
}
@ -155,18 +151,16 @@ class EpollLoop : SelectorLoop
}
if (transport.socket.disconnected)
{
kill(io, exception);
kill(transport, exception);
continue;
}
else if (io.output.length)
else if (transport.output.length)
{
swapPendings.enqueue(io);
pendings.enqueue(transport);
}
}
else if (events[i].events & EPOLLOUT)
if (events[i].events & EPOLLOUT)
{
auto transport = cast(SelectorStreamTransport) io.transport;
assert(transport !is null);
transport.writeReady = true;
if (transport.input.length)
{

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.event.iocp;
@ -26,32 +26,68 @@ import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
class IOCPStreamTransport : StreamTransport
/**
* Transport for stream sockets.
*/
final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
private OverlappedConnectedSocket socket_;
private SocketException exception;
private ReadBuffer!ubyte output;
private WriteBuffer!ubyte input;
private Protocol protocol_;
private bool closing;
/**
* Creates new completion port transport.
*
* Params:
* socket = Socket.
*
* Precondition: $(D_INLINECODE socket !is null)
*/
this(OverlappedConnectedSocket socket) @nogc
in
{
super(socket);
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true;
}
/**
* Returns: Socket.
*
* Postcondition: $(D_INLINECODE socket !is null)
*/
override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
out (socket)
{
assert(socket !is null);
}
body
{
socket_ = socket;
input = WriteBuffer!ubyte(8192, MmapPool.instance);
return cast(OverlappedConnectedSocket) socket_;
}
@property inout(OverlappedConnectedSocket) socket()
inout pure nothrow @safe @nogc
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc
{
return socket_;
return closing;
}
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() pure nothrow @safe @nogc
{
closing = true;
}
/**
@ -62,8 +98,48 @@ class IOCPStreamTransport : StreamTransport
*/
void write(ubyte[] data) @nogc
{
immutable empty = input.length == 0;
input ~= data;
}
/**
* Returns: Application protocol.
*/
@property Protocol protocol() pure nothrow @safe @nogc
{
return protocol_;
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
body
{
protocol_ = protocol;
}
/**
* Invokes the watcher callback.
*/
override void invoke() @nogc
{
if (output.length)
{
immutable empty = input.length == 0;
protocol.received(output[0 .. $]);
output.clear();
if (empty)
{
SocketState overlapped;
@ -79,9 +155,17 @@ class IOCPStreamTransport : StreamTransport
}
}
}
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
}
}
class IOCPLoop : Loop
final class IOCPLoop : Loop
{
protected HANDLE completionPort;
@ -112,7 +196,7 @@ class IOCPLoop : Loop
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override protected bool reify(ConnectionWatcher watcher,
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
@ -145,10 +229,7 @@ class IOCPLoop : Loop
if (!(oldEvents & Event.read) && (events & Event.read)
|| !(oldEvents & Event.write) && (events & Event.write))
{
auto io = cast(IOWatcher) watcher;
assert(io !is null);
auto transport = cast(IOCPStreamTransport) io.transport;
auto transport = cast(StreamTransport) watcher;
assert(transport !is null);
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
@ -165,7 +246,7 @@ class IOCPLoop : Loop
try
{
overlapped = MmapPool.instance.make!SocketState;
transport.socket.beginReceive(io.output[], overlapped);
transport.socket.beginReceive(transport.output[], overlapped);
}
catch (SocketException e)
{
@ -178,6 +259,20 @@ class IOCPLoop : Loop
return true;
}
private void kill(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
}
/**
* Does the actual polling.
*/
@ -215,29 +310,26 @@ class IOCPLoop : Loop
assert(listener !is null);
auto socket = listener.endAccept(overlapped);
auto transport = MmapPool.instance.make!IOCPStreamTransport(socket);
auto io = MmapPool.instance.make!IOWatcher(transport, connection.protocol);
auto transport = MmapPool.instance.make!StreamTransport(socket);
connection.incoming.enqueue(io);
connection.incoming.enqueue(transport);
reify(io, EventMask(Event.none), EventMask(Event.read, Event.write));
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
swapPendings.enqueue(connection);
pendings.enqueue(connection);
listener.beginAccept(overlapped);
break;
case OverlappedSocketEvent.read:
auto io = cast(IOWatcher) (cast(void*) key);
assert(io !is null);
if (!io.active)
auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null);
if (!transport.active)
{
MmapPool.instance.dispose(io);
MmapPool.instance.dispose(transport);
MmapPool.instance.dispose(overlapped);
return;
}
auto transport = cast(IOCPStreamTransport) io.transport;
assert(transport !is null);
int received;
SocketException exception;
try
@ -250,38 +342,39 @@ class IOCPLoop : Loop
}
if (transport.socket.disconnected)
{
// We want to get one last notification to destroy the watcher
transport.socket.beginReceive(io.output[], overlapped);
kill(io, exception);
// We want to get one last notification to destroy the watcher.
transport.socket.beginReceive(transport.output[], overlapped);
kill(transport, exception);
}
else if (received > 0)
{
immutable full = io.output.free == received;
immutable full = transport.output.free == received;
io.output += received;
// Receive was interrupted because the buffer is full. We have to continue
transport.output += received;
// Receive was interrupted because the buffer is full. We have to continue.
if (full)
{
transport.socket.beginReceive(io.output[], overlapped);
transport.socket.beginReceive(transport.output[], overlapped);
}
swapPendings.enqueue(io);
pendings.enqueue(transport);
}
break;
case OverlappedSocketEvent.write:
auto io = cast(IOWatcher) (cast(void*) key);
assert(io !is null);
auto transport = cast(IOCPStreamTransport) io.transport;
auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null);
transport.input += transport.socket.endSend(overlapped);
if (transport.input.length)
if (transport.input.length > 0)
{
transport.socket.beginSend(transport.input[], overlapped);
}
else
{
transport.socket.beginReceive(io.output[], overlapped);
transport.socket.beginReceive(transport.output[], overlapped);
if (transport.isClosing())
{
kill(transport);
}
}
break;
default:

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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)
@ -41,8 +41,19 @@ else version (DragonFlyBSD)
version (MacBSD):
import core.stdc.stdint; // intptr_t, uintptr_t
import core.stdc.errno;
import core.sys.posix.time; // timespec
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
{
@ -102,32 +113,19 @@ extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
kevent_t *eventlist, int nevents, const timespec *timeout)
nothrow @nogc;
import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import core.stdc.errno;
import core.sys.posix.unistd;
import core.sys.posix.sys.time;
import core.time;
import std.algorithm.comparison;
class KqueueLoop : SelectorLoop
final class KqueueLoop : SelectorLoop
{
protected int fd;
private kevent_t[] events;
private kevent_t[] changes;
private Array!kevent_t events;
private Array!kevent_t changes;
private size_t changeCount;
/**
* Returns: Maximal event count can be got at a time
* (should be supported by the backend).
*/
override protected @property inout(uint) maxEvents()
inout const pure nothrow @safe @nogc
override protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return cast(uint) events.length;
}
@ -138,19 +136,18 @@ class KqueueLoop : SelectorLoop
if ((fd = kqueue()) == -1)
{
throw MmapPool.instance.make!BadLoopException("epoll initialization failed");
throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed");
}
MmapPool.instance.resizeArray(events, 64);
MmapPool.instance.resizeArray(changes, 64);
events = Array!kevent_t(64, MmapPool.instance);
changes = Array!kevent_t(64, MmapPool.instance);
}
/**
* Free loop internals.
* Frees loop internals.
*/
~this() @nogc
{
MmapPool.instance.dispose(events);
MmapPool.instance.dispose(changes);
close(fd);
}
@ -158,7 +155,7 @@ class KqueueLoop : SelectorLoop
{
if (changes.length <= changeCount)
{
MmapPool.instance.resizeArray(changes, changeCount + maxEvents);
changes.length = changeCount + maxEvents;
}
EV_SET(&changes[changeCount],
cast(ulong) socket,
@ -180,7 +177,7 @@ class KqueueLoop : SelectorLoop
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override protected bool reify(ConnectionWatcher watcher,
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
@ -216,10 +213,15 @@ class KqueueLoop : SelectorLoop
if (changeCount > maxEvents)
{
MmapPool.instance.resizeArray(events, changes.length);
events.length = changes.length;
}
auto eventCount = kevent(fd, changes.ptr, cast(int) changeCount, events.ptr, maxEvents, &ts);
auto eventCount = kevent(fd,
changes.get().ptr,
cast(int) changeCount,
events.get().ptr,
maxEvents,
&ts);
changeCount = 0;
if (eventCount < 0)
@ -235,29 +237,29 @@ class KqueueLoop : SelectorLoop
{
assert(connections.length > events[i].ident);
IOWatcher io = cast(IOWatcher) connections[events[i].ident];
auto transport = cast(StreamTransport) connections[events[i].ident];
// If it is a ConnectionWatcher. Accept connections.
if (io is null)
if (transport is null)
{
acceptConnections(connections[events[i].ident]);
auto connection = cast(ConnectionWatcher) connections[events[i].ident];
assert(connection !is null);
acceptConnections(connection);
}
else if (events[i].flags & EV_ERROR)
{
kill(io, null);
kill(transport);
}
else if (events[i].filter == EVFILT_READ)
{
auto transport = cast(SelectorStreamTransport) io.transport;
assert(transport !is null);
SocketException exception;
try
{
ptrdiff_t received;
do
{
received = transport.socket.receive(io.output[]);
io.output += received;
received = transport.socket.receive(transport.output[]);
transport.output += received;
}
while (received);
}
@ -267,18 +269,15 @@ class KqueueLoop : SelectorLoop
}
if (transport.socket.disconnected)
{
kill(io, exception);
kill(transport, exception);
}
else if (io.output.length)
else if (transport.output.length)
{
swapPendings.enqueue(io);
pendings.enqueue(transport);
}
}
else if (events[i].filter == EVFILT_WRITE)
{
auto transport = cast(SelectorStreamTransport) io.transport;
assert(transport !is null);
transport.writeReady = true;
if (transport.input.length)
{
@ -307,9 +306,9 @@ class KqueueLoop : SelectorLoop
*
* Returns: $(D_KEYWORD true) if the operation could be successfully
* completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport is be destroyed then).
* transport will be destroyed then).
*/
protected override bool feed(SelectorStreamTransport transport,
protected override bool feed(StreamTransport transport,
SocketException exception = null) @nogc
{
if (!super.feed(transport, exception))

View File

@ -3,36 +3,41 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.event.selector;
version (Posix):
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import core.sys.posix.netinet.in_;
import core.stdc.errno;
/**
* Transport for stream sockets.
*/
class SelectorStreamTransport : StreamTransport
package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
private ConnectedSocket socket_;
private SelectorLoop loop;
private SocketException exception;
package ReadBuffer!ubyte output;
/// Input buffer.
package WriteBuffer!ubyte input;
private SelectorLoop loop;
private Protocol protocol_;
private bool closing;
/// Received notification that the underlying socket is write-ready.
package bool writeReady;
@ -41,20 +46,120 @@ class SelectorStreamTransport : StreamTransport
* Params:
* loop = Event loop.
* socket = Socket.
*
* Precondition: $(D_INLINECODE loop !is null && socket !is null)
*/
this(SelectorLoop loop, ConnectedSocket socket) @nogc
in
{
socket_ = socket;
assert(loop !is null);
}
body
{
super(socket);
this.loop = loop;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true;
}
/**
* Returns: Transport socket.
* Returns: Socket.
*
* Postcondition: $(D_INLINECODE socket !is null)
*/
inout(ConnectedSocket) socket() inout pure nothrow @safe @nogc
override @property ConnectedSocket socket() pure nothrow @safe @nogc
out (socket)
{
return socket_;
assert(socket !is null);
}
body
{
return cast(ConnectedSocket) socket_;
}
private @property void socket(ConnectedSocket socket)
pure nothrow @safe @nogc
in
{
assert(socket !is null);
}
body
{
socket_ = socket;
}
/**
* Returns: Application protocol.
*/
@property Protocol protocol() pure nothrow @safe @nogc
{
return protocol_;
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
body
{
protocol_ = protocol;
}
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc
{
return closing;
}
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() @nogc
{
closing = true;
loop.reify(this,
EventMask(Event.read, Event.write),
EventMask(Event.write));
}
/**
* Invokes the watcher callback.
*/
override void invoke() @nogc
{
if (output.length)
{
protocol.received(output[0 .. $]);
output.clear();
if (isClosing() && input.length == 0)
{
loop.kill(this);
}
}
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
}
/**
@ -103,28 +208,60 @@ class SelectorStreamTransport : StreamTransport
abstract class SelectorLoop : Loop
{
/// Pending connections.
protected ConnectionWatcher[] connections;
protected Array!SocketWatcher connections;
this() @nogc
{
super();
MmapPool.instance.resizeArray(connections, maxEvents);
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
}
~this() @nogc
{
foreach (ref connection; connections)
{
// We want to free only IOWatchers. ConnectionWatcher are created by the
// user and should be freed by himself.
auto io = cast(IOWatcher) connection;
if (io !is null)
// We want to free only the transports. ConnectionWatcher are
// created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null)
{
MmapPool.instance.dispose(io);
connection = null;
MmapPool.instance.dispose(connection);
}
}
MmapPool.instance.dispose(connections);
}
/**
* Should be called if the backend configuration changes.
*
* Params:
* watcher = Watcher.
* oldEvents = The events were already set.
* events = The events should be set.
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc;
/**
* Kills the watcher and closes the connection.
*
* Params:
* transport = Transport.
* exception = Occurred exception.
*/
protected void kill(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
}
/**
@ -139,8 +276,13 @@ abstract class SelectorLoop : Loop
* completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport will be destroyed then).
*/
protected bool feed(SelectorStreamTransport transport,
protected bool feed(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
while (transport.input.length && transport.writeReady)
{
@ -164,12 +306,13 @@ abstract class SelectorLoop : Loop
}
if (exception !is null)
{
auto watcher = cast(IOWatcher) connections[transport.socket.handle];
assert(watcher !is null);
kill(watcher, exception);
kill(transport, exception);
return false;
}
if (transport.input.length == 0 && transport.isClosing())
{
kill(transport);
}
return true;
}
@ -188,7 +331,7 @@ abstract class SelectorLoop : Loop
if (connections.length <= watcher.socket)
{
MmapPool.instance.resizeArray(connections, watcher.socket.handle + maxEvents / 2);
connections.length = watcher.socket.handle + maxEvents / 2;
}
connections[watcher.socket.handle] = watcher;
@ -225,35 +368,33 @@ abstract class SelectorLoop : Loop
break;
}
IOWatcher io;
auto transport = MmapPool.instance.make!SelectorStreamTransport(this, client);
StreamTransport transport;
if (connections.length > client.handle)
{
io = cast(IOWatcher) connections[client.handle];
transport = cast(StreamTransport) connections[client.handle];
}
else
{
MmapPool.instance.resizeArray(connections, client.handle + maxEvents / 2);
connections.length = client.handle + maxEvents / 2;
}
if (io is null)
if (transport is null)
{
io = MmapPool.instance.make!IOWatcher(transport,
connection.protocol);
connections[client.handle] = io;
transport = MmapPool.instance.make!StreamTransport(this, client);
connections[client.handle] = transport;
}
else
{
io(transport, connection.protocol);
transport.socket = client;
}
reify(io, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(io);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(transport);
}
if (!connection.incoming.empty)
{
swapPendings.enqueue(connection);
pendings.enqueue(connection);
}
}
}

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.iocp;

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*
* ---
* import tanya.async;
@ -17,7 +17,7 @@
* {
* private DuplexTransport transport;
*
* void received(ubyte[] data) @nogc
* void received(in ubyte[] data) @nogc
* {
* transport.write(data);
* }
@ -27,7 +27,7 @@
* this.transport = transport;
* }
*
* void disconnected(SocketException e = null) @nogc
* void disconnected(SocketException e) @nogc
* {
* }
* }
@ -68,7 +68,6 @@ import core.time;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.typecons;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
@ -130,18 +129,17 @@ alias EventMask = BitFlags!Event;
*/
abstract class Loop
{
/// Pending watchers.
protected Queue!Watcher* pendings;
private bool done;
/// Ditto.
protected Queue!Watcher* swapPendings;
/// Pending watchers.
protected Queue!Watcher pendings;
/**
* Returns: Maximal event count can be got at a time
* (should be supported by the backend).
*/
protected @property inout(uint) maxEvents()
inout const pure nothrow @safe @nogc
protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return 128U;
}
@ -151,8 +149,7 @@ abstract class Loop
*/
this() @nogc
{
pendings = MmapPool.instance.make!(Queue!Watcher)(MmapPool.instance);
swapPendings = MmapPool.instance.make!(Queue!Watcher)(MmapPool.instance);
pendings = Queue!Watcher(MmapPool.instance);
}
/**
@ -160,17 +157,10 @@ abstract class Loop
*/
~this() @nogc
{
foreach (w; *pendings)
foreach (w; pendings)
{
MmapPool.instance.dispose(w);
}
MmapPool.instance.dispose(pendings);
foreach (w; *swapPendings)
{
MmapPool.instance.dispose(w);
}
MmapPool.instance.dispose(swapPendings);
}
/**
@ -178,19 +168,18 @@ abstract class Loop
*/
void run() @nogc
{
done_ = false;
done = false;
do
{
poll();
// Invoke pendings
foreach (ref w; *swapPendings)
foreach (ref w; pendings)
{
w.invoke();
}
swap(pendings, swapPendings);
}
while (!done_);
while (!done);
}
/**
@ -198,7 +187,7 @@ abstract class Loop
*/
void unloop() @safe pure nothrow @nogc
{
done_ = true;
done = true;
}
/**
@ -214,6 +203,7 @@ abstract class Loop
return;
}
watcher.active = true;
reify(watcher, EventMask(Event.none), EventMask(Event.accept));
}
@ -244,7 +234,7 @@ abstract class Loop
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
abstract protected bool reify(ConnectionWatcher watcher,
abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc;
@ -255,7 +245,7 @@ abstract class Loop
inout @safe pure nothrow @nogc
{
// Don't block if we have to do.
return swapPendings.empty ? blockTime_ : Duration.zero;
return pendings.empty ? blockTime_ : Duration.zero;
}
/**
@ -276,26 +266,11 @@ abstract class Loop
blockTime_ = blockTime;
}
/**
* Kills the watcher and closes the connection.
*/
protected void kill(IOWatcher watcher, SocketException exception) @nogc
{
watcher.socket.shutdown();
defaultAllocator.dispose(watcher.socket);
MmapPool.instance.dispose(watcher.transport);
watcher.exception = exception;
swapPendings.enqueue(watcher);
}
/**
* Does the actual polling.
*/
abstract protected void poll() @nogc;
/// Whether the event loop should be stopped.
private bool done_;
/// Maximal block time.
protected Duration blockTime_ = 1.dur!"minutes";
}

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async;

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.protocol;
@ -22,7 +22,7 @@ interface Protocol
* Params:
* data = Read data.
*/
void received(ubyte[] data) @nogc;
void received(in ubyte[] data) @nogc;
/**
* Called when a connection is made.
@ -39,7 +39,7 @@ interface Protocol
* exception = $(D_PSYMBOL Exception) if an error caused
* the disconnect, $(D_KEYWORD null) otherwise.
*/
void disconnected(SocketException exception = null) @nogc;
void disconnected(SocketException exception) @nogc;
}
/**

View File

@ -3,13 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.transport;
import tanya.async.protocol;
import tanya.network.socket;
/**
@ -45,6 +46,46 @@ interface WriteTransport : Transport
*/
interface DuplexTransport : ReadTransport, WriteTransport
{
/**
* Returns: Application protocol.
*
* Postcondition: $(D_INLINECODE protocol !is null)
*/
@property Protocol protocol() pure nothrow @safe @nogc
out (protocol)
{
assert(protocol !is null);
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc;
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() @nogc;
}
/**
@ -52,12 +93,8 @@ interface DuplexTransport : ReadTransport, WriteTransport
*/
interface SocketTransport : Transport
{
@property inout(Socket) socket() inout pure nothrow @safe @nogc;
}
/**
* Represents a connection-oriented socket transport.
* Returns: Socket.
*/
package interface StreamTransport : DuplexTransport, SocketTransport
{
@property Socket socket() pure nothrow @safe @nogc;
}

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.async.watcher;
@ -21,15 +21,6 @@ import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
version (Windows)
{
import core.sys.windows.basetyps;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
}
/**
* A watcher is an opaque structure that you allocate and register to record
* your interest in some event.
@ -45,15 +36,48 @@ abstract class Watcher
void invoke() @nogc;
}
class ConnectionWatcher : Watcher
/**
* Socket watcher.
*/
abstract class SocketWatcher : Watcher
{
/// Watched socket.
private Socket socket_;
protected Socket socket_;
/// Protocol factory.
protected Protocol delegate() @nogc protocolFactory;
/**
* Params:
* socket = Socket.
*
* Precondition: $(D_INLINECODE socket !is null)
*/
this(Socket socket) pure nothrow @safe @nogc
in
{
assert(socket !is null);
}
body
{
socket_ = socket;
}
package Queue!IOWatcher incoming;
/**
* Returns: Socket.
*/
@property Socket socket() pure nothrow @safe @nogc
{
return socket_;
}
}
/**
* Connection watcher.
*/
class ConnectionWatcher : SocketWatcher
{
/// Incoming connection queue.
Queue!DuplexTransport incoming;
private Protocol delegate() @nogc protocolFactory;
/**
* Params:
@ -61,20 +85,8 @@ class ConnectionWatcher : Watcher
*/
this(Socket socket) @nogc
{
socket_ = socket;
}
/// Ditto.
protected this() pure nothrow @safe @nogc
{
}
~this() @nogc
{
foreach (w; incoming)
{
MmapPool.instance.dispose(w);
}
super(socket);
incoming = Queue!DuplexTransport(MmapPool.instance);
}
/**
@ -87,159 +99,19 @@ class ConnectionWatcher : Watcher
}
/**
* Returns: Socket.
* Invokes new connection callback.
*/
@property inout(Socket) socket() inout pure nothrow @nogc
{
return socket_;
}
/**
* Returns: New protocol instance.
*/
@property Protocol protocol() @nogc
override void invoke() @nogc
in
{
assert(protocolFactory !is null, "Protocol isn't set.");
}
body
{
return protocolFactory();
}
/**
* Invokes new connection callback.
*/
override void invoke() @nogc
foreach (transport; incoming)
{
foreach (io; incoming)
{
io.protocol.connected(cast(DuplexTransport) io.transport);
}
}
}
/**
* Contains a pending watcher with the invoked events or a transport can be
* read from.
*/
class IOWatcher : ConnectionWatcher
{
/// If an exception was thrown the transport should be already invalid.
private union
{
StreamTransport transport_;
SocketException exception_;
}
private Protocol protocol_;
/**
* Returns: Underlying output buffer.
*/
package ReadBuffer!ubyte output;
/**
* Params:
* transport = Transport.
* protocol = New instance of the application protocol.
*/
this(StreamTransport transport, Protocol protocol) @nogc
in
{
assert(transport !is null);
assert(protocol !is null);
}
body
{
super();
transport_ = transport;
protocol_ = protocol;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
active = true;
}
/**
* Destroys the watcher.
*/
~this() @nogc
{
MmapPool.instance.dispose(protocol_);
}
/**
* Assigns a transport.
*
* Params:
* transport = Transport.
* protocol = Application protocol.
*
* Returns: $(D_KEYWORD this).
*/
IOWatcher opCall(StreamTransport transport, Protocol protocol)
pure nothrow @nogc
in
{
assert(transport !is null);
assert(protocol !is null);
}
body
{
transport_ = transport;
protocol_ = protocol;
active = true;
return this;
}
/**
* Returns: Transport used by this watcher.
*/
@property inout(StreamTransport) transport() inout pure nothrow @nogc
{
return transport_;
}
/**
* Sets an exception occurred during a read/write operation.
*
* Params:
* exception = Thrown exception.
*/
@property void exception(SocketException exception) pure nothrow @nogc
{
exception_ = exception;
}
/**
* Returns: Application protocol.
*/
override @property Protocol protocol() pure nothrow @safe @nogc
{
return protocol_;
}
/**
* Returns: Socket.
*/
override @property inout(Socket) socket() inout pure nothrow @nogc
{
return transport.socket;
}
/**
* Invokes the watcher callback.
*/
override void invoke() @nogc
{
if (output.length)
{
protocol.received(output[0..$]);
output.clear();
}
else
{
protocol.disconnected(exception_);
active = false;
transport.protocol = protocolFactory();
transport.protocol.connected(transport);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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)
@ -261,7 +261,6 @@ struct ReadBuffer(T = ubyte)
allocator.reallocate(buf, (cap + blockSize) * T.sizeof);
buffer_ = cast(T[]) buf;
}();
buffer_[cap .. $] = T.init;
}
ring = length_;
return buffer_[length_ .. $];
@ -337,33 +336,25 @@ struct WriteBuffer(T = ubyte)
* size = Initial buffer size and the size by which the buffer will
* grow.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE size > 0 && allocator !is null)
*/
this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
in
{
assert(size > 0);
assert(allocator !is null);
}
body
{
blockSize = size;
ring = size - 1;
this(allocator);
allocator_ = allocator;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
}
@disable this();
/// Ditto.
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
allocator_ = allocator;
}
/**
* Deallocates the internal buffer.
*/

View File

@ -5,41 +5,27 @@
/*
* Internal package used by containers that rely on entries/nodes.
*
* Copyright: Eugene Wissner 2016.
* 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.container.entry;
version (unittest)
package struct SEntry(T)
{
package struct ConstEqualsStruct
{
int opEquals(typeof(this) that) const @nogc
{
return true;
}
}
package struct MutableEqualsStruct
{
int opEquals(typeof(this) that) @nogc
{
return true;
}
}
package struct NoEqualsStruct
{
}
}
package struct Entry(T)
{
/// Item content.
// Item content.
T content;
/// Next item.
Entry* next;
// Next item.
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

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* Abstract data types whose instances are collections of other objects.
*
* 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.container;
public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.list;
public import tanya.container.vector;
public import tanya.container.string;
public import tanya.container.queue;

View File

@ -3,13 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* FIFO queue.
*
* 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.container.queue;
import core.exception;
import std.traits;
import std.algorithm.mutation;
import tanya.container.entry;
@ -34,18 +37,6 @@ struct Queue(T)
}
}
/**
* Removes all elements from the queue.
*/
deprecated
void clear()
{
while (!empty)
{
dequeue();
}
}
/**
* Returns how many elements are in the queue. It iterates through the queue
* to count the elements.
@ -55,7 +46,7 @@ struct Queue(T)
size_t length() const
{
size_t len;
for (const(Entry!T)* i = first.next; i !is null; i = i.next)
for (const(SEntry!T)* i = first; i !is null; i = i.next)
{
++len;
}
@ -83,84 +74,27 @@ struct Queue(T)
assert(q.length == 0);
}
version (D_Ddoc)
private void enqueueEntry(ref SEntry!T* entry)
{
/**
* Compares two queues. Checks if all elements of the both queues are equal.
*
* Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal.
*/
deprecated
int opEquals(ref typeof(this) that);
/// Ditto.
deprecated
int opEquals(typeof(this) that);
}
else static if (!hasMember!(T, "opEquals")
|| (functionAttributes!(T.opEquals) & FunctionAttribute.const_))
if (empty)
{
deprecated
bool opEquals(in ref typeof(this) that) const
{
const(Entry!T)* i = first.next;
const(Entry!T)* j = that.first.next;
while (i !is null && j !is null)
{
if (i.content != j.content)
{
return false;
}
i = i.next;
j = j.next;
}
return i is null && j is null;
}
deprecated
bool opEquals(in typeof(this) that) const
{
return opEquals(that);
}
first = rear = entry;
}
else
{
deprecated
bool opEquals(ref typeof(this) that)
{
Entry!T* i = first.next;
Entry!T* j = that.first.next;
while (i !is null && j !is null)
{
if (i.content != j.content)
{
return false;
}
i = i.next;
j = j.next;
}
return i is null && j is null;
}
deprecated
bool opEquals(typeof(this) that)
{
return opEquals(that);
rear.next = entry;
rear = rear.next;
}
}
/**
* Returns: First element.
*/
deprecated("Use dequeue instead.")
@property ref inout(T) front() inout
in
private SEntry!T* allocateEntry()
{
assert(!empty);
auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof);
if (temp is null)
{
onOutOfMemoryError();
}
body
{
return first.next.content;
return temp;
}
/**
@ -168,43 +102,36 @@ struct Queue(T)
*
* Params:
* x = New element.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) enqueue(ref T x)
void enqueue(ref T x)
{
auto temp = allocator.make!(Entry!T)(x);
if (empty)
{
first.next = rear = temp;
}
else
{
rear.next = temp;
rear = rear.next;
}
return this;
auto temp = allocateEntry();
*temp = SEntry!T.init;
temp.content = x;
enqueueEntry(temp);
}
/// Ditto.
ref typeof(this) enqueue(T x)
void enqueue(T x)
{
return enqueue(x);
auto temp = allocateEntry();
moveEmplace(x, (*temp).content);
(*temp).next = null;
enqueueEntry(temp);
}
deprecated("Use enqueue instead.")
alias insert = enqueue;
deprecated("Use enqueue instead.")
alias insertBack = enqueue;
///
unittest
{
Queue!int q;
assert(q.empty);
q.enqueue(8).enqueue(9);
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
@ -214,7 +141,7 @@ struct Queue(T)
*/
@property bool empty() const
{
return first.next is null;
return first is null;
}
///
@ -237,27 +164,24 @@ struct Queue(T)
in
{
assert(!empty);
assert(allocator !is null);
}
body
{
auto n = first.next.next;
T ret = move(first.next.content);
auto n = first.next;
T ret = move(first.content);
dispose(allocator, first.next);
first.next = n;
allocator.dispose(first);
first = n;
return ret;
}
deprecated("Use dequeue instead.")
alias popFront = dequeue;
///
unittest
{
Queue!int q;
q.enqueue(8).enqueue(9);
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
@ -268,6 +192,8 @@ struct Queue(T)
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
@ -306,7 +232,9 @@ struct Queue(T)
Queue!int q;
size_t j;
q.enqueue(5).enqueue(4).enqueue(9);
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (i, e; q)
{
assert(i != 2 || e == 9);
@ -318,7 +246,9 @@ struct Queue(T)
assert(q.empty);
j = 0;
q.enqueue(5).enqueue(4).enqueue(9);
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (e; q)
{
assert(j != 2 || e == 9);
@ -330,11 +260,8 @@ struct Queue(T)
assert(q.empty);
}
/// The first element of the list.
private Entry!T first;
/// The last element of the list.
private Entry!T* rear;
private SEntry!T* first;
private SEntry!T* rear;
mixin DefaultAllocator;
}
@ -347,7 +274,8 @@ unittest
q.enqueue(5);
assert(!q.empty);
q.enqueue(4).enqueue(9);
q.enqueue(4);
q.enqueue(9);
assert(q.dequeue() == 5);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,510 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.crypto.bit;
/**
* Wrapper that allows bit manipulation on $(D_KEYWORD ubyte[]) array.
*/
struct BitVector
{
protected ubyte[] vector;
/**
* Params:
* array = Array should be manipulated on.
*/
this(inout(ubyte[]) array) inout pure nothrow @safe @nogc
in
{
assert(array.length <= size_t.max / 8);
assert(array !is null);
}
body
{
vector = array;
}
///
unittest
{
ubyte[5] array1 = [234, 3, 252, 10, 18];
ubyte[3] array2 = [65, 13, 173];
auto bits = BitVector(array1);
assert(bits[] is array1);
assert(bits[] !is array2);
bits = BitVector(array2);
assert(bits[] is array2);
}
/**
* Returns: Number of bits in the vector.
*/
@property inout(size_t) length() inout const pure nothrow @safe @nogc
{
return vector.length * 8;
}
/// Ditto.
inout(size_t) opDollar() inout const pure nothrow @safe @nogc
{
return vector.length * 8;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] arr = [65, 13, 173];
auto bits = BitVector(arr);
assert(bits.length == 24);
}
/**
* Params:
* bit = Bit position.
*
* Returns: $(D_KEYWORD true) if the bit on position $(D_PARAM bit) is set,
* $(D_KEYWORD false) if not set.
*/
inout(bool) opIndex(size_t bit) inout const pure nothrow @safe @nogc
in
{
assert(bit / 8 <= vector.length);
}
body
{
return (vector[bit / 8] & (0x80 >> (bit % 8))) != 0;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] arr = [65, 13, 173];
auto bits = BitVector(arr);
assert(!bits[0]);
assert(bits[1]);
assert(bits[7]);
assert(!bits[8]);
assert(!bits[11]);
assert(bits[12]);
assert(bits[20]);
assert(bits[23]);
}
/**
* Returns: Underlying array.
*/
inout(ubyte[]) opIndex() inout pure nothrow @safe @nogc
{
return vector;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] arr = [65, 13, 173];
auto bits = BitVector(arr);
assert(bits[] is arr);
}
/**
* Params:
* value = $(D_KEYWORD true) if the bit should be set,
* $(D_KEYWORD false) if cleared.
* bit = Bit position.
*
* Returns: $(D_PSYMBOL this).
*/
bool opIndexAssign(bool value, size_t bit) pure nothrow @safe @nogc
in
{
assert(bit / 8 <= vector.length);
}
body
{
if (value)
{
vector[bit / 8] |= (0x80 >> (bit % 8));
}
else
{
vector[bit / 8] &= ~(0x80 >> (bit % 8));
}
return value;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] arr = [65, 13, 173];
auto bits = BitVector(arr);
bits[5] = bits[6] = true;
assert(bits[][0] == 71);
bits[14] = true;
bits[15] = false;
assert(bits[][1] == 14);
bits[16] = bits[23] = false;
assert(bits[][2] == 44);
}
/**
* Copies bits from $(D_PARAM vector) into this $(D_PSYMBOL BitVector).
*
* The array that should be assigned, can be smaller (but not larger) than
* the underlying array of this $(D_PSYMBOL BitVector), leading zeros will
* be added in this case to the left.
*
* Params:
* vector = $(D_KEYWORD ubyte[]) array not larger than
* `$(D_PSYMBOL length) / 8`.
*
* Returns: $(D_KEYWORD this).
*/
BitVector opAssign(ubyte[] vector) pure nothrow @safe @nogc
in
{
assert(vector.length <= this.vector.length);
}
body
{
immutable delta = this.vector.length - vector.length;
if (delta > 0)
{
this.vector[0..delta] = 0;
}
this.vector[delta..$] = vector[0..$];
return this;
}
///
unittest
{
ubyte[5] array1 = [234, 3, 252, 10, 18];
ubyte[3] array2 = [65, 13, 173];
auto bits = BitVector(array1);
bits = array2;
assert(bits[][0] == 0);
assert(bits[][1] == 0);
assert(bits[][2] == 65);
assert(bits[][3] == 13);
assert(bits[][4] == 173);
bits = array2[0..2];
assert(bits[][0] == 0);
assert(bits[][1] == 0);
assert(bits[][2] == 0);
assert(bits[][3] == 65);
assert(bits[][4] == 13);
}
/**
* Support for bitwise operations.
*
* Params:
* that = Another bit vector.
*
* Returns: $(D_KEYWORD this).
*/
BitVector opOpAssign(string op)(BitVector that) pure nothrow @safe @nogc
if ((op == "^") || (op == "|") || (op == "&"))
{
return opOpAssign(op)(that.vector);
}
/// Ditto.
BitVector opOpAssign(string op)(ubyte[] that) pure nothrow @safe @nogc
if ((op == "^") || (op == "|") || (op == "&"))
in
{
assert(that.length <= vector.length);
}
body
{
for (int i = cast(int) vector.length - 1; i >= 0; --i)
{
mixin("vector[i] " ~ op ~ "= " ~ "that[i];");
}
immutable delta = vector.length - that.length;
if (delta)
{
static if (op == "&")
{
vector[0..delta] = 0;
}
}
return this;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] array1 = [65, 13, 173];
ubyte[3] array2 = [0b01010010, 0b10111110, 0b10111110];
auto bits = BitVector(array1);
bits |= array2;
assert(bits[][0] == 0b01010011);
assert(bits[][1] == 0b10111111);
assert(bits[][2] == 0b10111111);
bits &= array2;
assert(bits[][0] == array2[0]);
assert(bits[][1] == array2[1]);
assert(bits[][2] == array2[2]);
bits ^= array2;
assert(bits[][0] == 0);
assert(bits[][1] == 0);
assert(bits[][2] == 0);
}
/**
* Support for shift operations.
*
* Params:
* n = Number of bits.
*
* Returns: $(D_KEYWORD this).
*/
BitVector opOpAssign(string op)(in size_t n) pure nothrow @safe @nogc
if ((op == "<<") || (op == ">>"))
{
if (n >= length)
{
vector[0..$] = 0;
}
else if (n != 0)
{
immutable bit = n % 8, step = n / 8;
immutable delta = 8 - bit;
size_t i, j;
static if (op == "<<")
{
for (j = step; j < vector.length - 1; ++i)
{
vector[i] = cast(ubyte)((vector[j] << bit)
| vector[++j] >> delta);
}
vector[i] = cast(ubyte)(vector[j] << bit);
vector[$ - step ..$] = 0;
}
else static if (op == ">>")
{
for (i = vector.length - 1, j = i - step; j > 0; --i)
{
vector[i] = cast(ubyte)((vector[j] >> bit)
| vector[--j] << delta);
}
vector[i] = cast(ubyte)(vector[j] >> bit);
vector[0..step] = 0;
}
}
return this;
}
///
nothrow @safe @nogc unittest
{
ubyte[4] arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011];
auto bits = BitVector(arr);
bits <<= 0;
assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010
&& bits[][2] == 0b01010010 && bits[][3] == 0b01010011);
bits <<= 2;
assert(bits[][0] == 0b11111011 && bits[][1] == 0b11001001
&& bits[][2] == 0b01001001 && bits[][3] == 0b01001100);
bits <<= 4;
assert(bits[][0] == 0b10111100 && bits[][1] == 0b10010100
&& bits[][2] == 0b10010100 && bits[][3] == 0b11000000);
bits <<= 8;
assert(bits[][0] == 0b10010100 && bits[][1] == 0b10010100
&& bits[][2] == 0b11000000 && bits[][3] == 0b00000000);
bits <<= 7;
assert(bits[][0] == 0b01001010 && bits[][1] == 0b01100000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
bits <<= 25;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101];
bits <<= 24;
assert(bits[][0] == 0b01010101 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
arr[1] = 0b11001100;
arr[2] = 0b11111111;
arr[3] = 0b01010101;
bits <<= 12;
assert(bits[][0] == 0b11001111 && bits[][1] == 0b11110101
&& bits[][2] == 0b01010000 && bits[][3] == 0b00000000);
bits <<= 100;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011];
bits >>= 0;
assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010
&& bits[][2] == 0b01010010 && bits[][3] == 0b01010011);
bits >>= 2;
assert(bits[][0] == 0b00101111 && bits[][1] == 0b10111100
&& bits[][2] == 0b10010100 && bits[][3] == 0b10010100);
bits >>= 4;
assert(bits[][0] == 0b00000010 && bits[][1] == 0b11111011
&& bits[][2] == 0b11001001 && bits[][3] == 0b01001001);
bits >>= 8;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000010
&& bits[][2] == 0b11111011 && bits[][3] == 0b11001001);
bits >>= 7;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000101 && bits[][3] == 0b11110111);
bits >>= 25;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101];
bits >>= 24;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00110011);
arr[1] = 0b11001100;
arr[2] = 0b11111111;
arr[3] = 0b01010101;
bits >>= 12;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00001100 && bits[][3] == 0b11001111);
bits >>= 100;
assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000
&& bits[][2] == 0b00000000 && bits[][3] == 0b00000000);
}
/**
* Negates all bits.
*
* Returns: $(D_KEYWORD this).
*/
BitVector opUnary(string op)() pure nothrow @safe @nogc
if (op == "~")
{
foreach (ref b; vector)
{
b = ~b;
}
return this;
}
///
unittest
{
// [01000001, 00001101, 10101101]
ubyte[3] arr = [65, 13, 173];
auto bits = BitVector(arr);
~bits;
assert(bits[][0] == 0b10111110);
assert(bits[][1] == 0b11110010);
assert(bits[][2] == 0b01010010);
}
/**
* Iterates through all bits.
*
* Params:
* dg = $(D_KEYWORD foreach) delegate.
*
* Returns: By $(D_PARAM dg) returned value.
*/
int opApply(int delegate(size_t, bool) dg)
{
int result;
foreach (i, ref v; vector)
{
foreach (c; 0..8)
{
result = dg(i * 8 + c, (v & (0x80 >> c)) != 0);
if (result)
{
return result;
}
}
}
return result;
}
/// Ditto.
int opApply(int delegate(bool) dg)
{
int result;
foreach (ref v; vector)
{
foreach (c; 0..8)
{
result = dg((v & (0x80 >> c)) != 0);
if (result)
{
return result;
}
}
}
return result;
}
///
unittest
{
ubyte[2] arr = [0b01000001, 0b00001101];
auto bits = BitVector(arr);
size_t c;
foreach (i, v; bits)
{
assert(i == c);
if (i == 1 || i == 7 || i == 15 || i == 13 || i == 12)
{
assert(v);
}
else
{
assert(!v);
}
++c;
}
assert(c == 16);
}
}

View File

@ -1,607 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.crypto.des;
import tanya.crypto.bit;
import tanya.crypto.symmetric;
/// Initial permutation table.
private immutable ubyte[64] ipTable = [58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7];
/// Final permutation table.
private immutable ubyte[64] fpTable = [40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25];
/// Key permutation table 1.
private immutable ubyte[64] pc1Table = [57, 49, 41, 33, 25, 17, 9, 1,
58, 50, 42, 34, 26, 18, 10, 2,
59, 51, 43, 35, 27, 19, 11, 3,
60, 52, 44, 36, 63, 55, 47, 39,
31, 23, 15, 7, 62, 54, 46, 38,
30, 22, 14, 6, 61, 53, 45, 37,
29, 21, 13, 5, 28, 20, 12, 4];
/// Key permutation table 2.
private immutable ubyte[48] pc2Table = [14, 17, 11, 24, 1, 5, 3, 28,
15, 6, 21, 10, 23, 19, 12, 4,
26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40,
51, 45, 33, 48, 44, 49, 39, 56,
34, 53, 46, 42, 50, 36, 29, 32];
/// Expansion table.
private immutable ubyte[48] expansionTable = [32, 1, 2, 3, 4, 5, 4, 5,
6, 7, 8, 9, 8, 9, 10, 11,
12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21,
22, 23, 24, 25, 24, 25, 26, 27,
28, 29, 28, 29, 30, 31, 32, 1];
/// Final input block permutation.
private immutable ubyte[32] pTable = [16, 7, 20, 21, 29, 12, 28, 17,
1, 15, 23, 26, 5, 18, 31, 10,
2, 8, 24, 14, 32, 27, 3, 9,
19, 13, 30, 6, 22, 11, 4, 25];
/// The (in)famous S-boxes.
private immutable ubyte[64][8] sBox = [[
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13,
],[
15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9,
],[
10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12,
],[
7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14,
],[
2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3,
],[
12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13,
],[
4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12,
],[
13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11,
]];
/**
* Data Encryption Standard.
*
* Params:
* L = Number of keys.
*/
class DES(ushort L = 1) : BlockCipher
if (L == 1)
{
mixin FixedBlockSize!8;
mixin KeyLength!8;
private enum expansionBlockSize = 6;
private enum pc1KeyLength = 7;
private enum subkeyLength = 6;
private ubyte[] key_;
/**
* Params:
* key = Key.
*/
@property void key(ubyte[] key) pure nothrow @safe @nogc
in
{
assert(key.length >= minKeyLength);
assert(key.length <= maxKeyLength);
}
body
{
key_ = key;
}
/**
* Encrypts a block.
*
* Params:
* plain = Plain text, input.
* cipher = Cipher text, output.
*/
void encrypt(in ubyte[] plain, ubyte[] cipher)
nothrow
in
{
assert(plain.length == blockSize);
assert(cipher.length == blockSize);
}
body
{
operateBlock!(Direction.encryption)(plain, cipher);
}
/**
* Decrypts a block.
*
* Params:
* cipher = Cipher text, input.
* plain = Plain text, output.
*/
void decrypt(in ubyte[] cipher, ubyte[] plain)
nothrow
in
{
assert(plain.length == blockSize);
assert(cipher.length == blockSize);
}
body
{
operateBlock!(Direction.decryption)(cipher, plain);
}
private void operateBlock(Direction D)(in ubyte[] source, ref ubyte[] target)
{
ubyte[blockSize_] ipBlock;
ubyte[expansionBlockSize] expansionBlock;
ubyte[4] substitutionBlock;
ubyte[4] pBoxTarget;
ubyte[pc1KeyLength] pc1Key;
ubyte[subkeyLength] subkey;
// Initial permutation
permute(source, ipBlock, ipTable, blockSize);
// Key schedule computation
permute(key_, pc1Key, pc1Table, pc1KeyLength);
// Feistel function
for (ubyte round; round < 16; ++round)
{
auto bitVector = BitVector(expansionBlock);
/* Expansion. This permutation only looks at the first 4 bytes (32
bits of ipBlock); 16 of these are repeated in expansion table.*/
permute(BitVector(ipBlock[4..$]), bitVector, expansionTable, 6);
// Key mixing
static if (D == Direction.encryption)
{
rotateLeft(pc1Key);
if (!(round <= 1 || round == 8 || round == 15))
{
// Rotate twice.
rotateLeft(pc1Key);
}
}
permute(pc1Key, subkey, pc2Table, subkeyLength);
static if (D == Direction.decryption)
{
rotateRight(pc1Key);
if (!(round >= 14 || round == 7 || round == 0))
{
// Rotate twice.
rotateRight(pc1Key);
}
}
bitVector ^= subkey;
// Substitution; copy from updated expansion block to ciphertext block
substitutionBlock[0] = cast(ubyte) (sBox[0][(expansionBlock[0] & 0xfc ) >> 2] << 4);
substitutionBlock[0] |= sBox[1][(expansionBlock[0] & 0x03) << 4 | (expansionBlock[1] & 0xf0) >> 4];
substitutionBlock[1] = cast(ubyte) (sBox[2][(expansionBlock[1] & 0x0f) << 2 | (expansionBlock[2] & 0xc0) >> 6] << 4);
substitutionBlock[1] |= sBox[3][(expansionBlock[2] & 0x3f)];
substitutionBlock[2] = cast(ubyte) (sBox[4][(expansionBlock[3] & 0xfc) >> 2 ] << 4);
substitutionBlock[2] |= sBox[5][(expansionBlock[3] & 0x03) << 4 | (expansionBlock[4] & 0xf0) >> 4];
substitutionBlock[3] = cast(ubyte) (sBox[6][(expansionBlock[4] & 0x0F) << 2 | (expansionBlock[5] & 0xc0) >> 6] << 4);
substitutionBlock[3] |= sBox[7][(expansionBlock[5] & 0x3f)];
// Permutation
bitVector = BitVector(substitutionBlock);
permute(bitVector, pBoxTarget, pTable, blockSize / 2);
// Swap the halves.
substitutionBlock = ipBlock[0..4];
ipBlock[0..4] = ipBlock[4..$];
bitVector ^= pBoxTarget;
ipBlock[4..$] = substitutionBlock;
}
substitutionBlock = ipBlock[0..4];
ipBlock[0..4] = ipBlock[4..$];
ipBlock[4..$] = substitutionBlock;
// Final permutaion (undo initial permuation).
permute(ipBlock, target, fpTable, blockSize);
}
/**
* Performs the left rotation operation on the key.
*
* Params:
* key = The key to rotate.
*/
private void rotateLeft(ref ubyte[7] key) const pure nothrow @safe @nogc
{
immutable carryLeft = (key[0] & 0x80) >> 3;
key[0] = cast(ubyte) ((key[0] << 1) | ((key[1] & 0x80) >> 7));
key[1] = cast(ubyte) ((key[1] << 1) | ((key[2] & 0x80) >> 7));
key[2] = cast(ubyte) ((key[2] << 1) | ((key[3] & 0x80) >> 7));
immutable carryRight = (key[3] & 0x08) >> 3;
key[3] = cast(ubyte) ((((key[3] << 1) | ((key[4] & 0x80) >> 7)) & ~0x10) | carryLeft);
key[4] = cast(ubyte) ((key[4] << 1) | ((key[5] & 0x80) >> 7));
key[5] = cast(ubyte) ((key[5] << 1) | ((key[6] & 0x80) >> 7));
key[6] = cast(ubyte) ((key[6] << 1) | carryRight);
}
/**
* Performs the right rotation operation on the key.
*
* Params:
* key = The key to rotate.
*/
private void rotateRight(ref ubyte[7] key) const pure nothrow @safe @nogc
{
immutable carryRight = (key[6] & 0x01) << 3;
key[6] = cast(ubyte) ((key[6] >> 1) | ((key[5] & 0x01) << 7));
key[5] = cast(ubyte) ((key[5] >> 1) | ((key[4] & 0x01) << 7));
key[4] = cast(ubyte) ((key[4] >> 1) | ((key[3] & 0x01) << 7));
immutable carryLeft = (key[3] & 0x10) << 3;
key[3] = cast(ubyte) ((((key[3] >> 1) | ((key[2] & 0x01) << 7)) & ~0x08) | carryRight);
key[2] = cast(ubyte) ((key[2] >> 1) | ((key[1] & 0x01) << 7));
key[1] = cast(ubyte) ((key[1] >> 1) | ((key[0] & 0x01) << 7));
key[0] = cast(ubyte) ((key[0] >> 1) | carryLeft);
}
private void permute(in ubyte[] source, ubyte[] target, immutable(ubyte[]) permuteTable, size_t length)
const pure nothrow @safe @nogc
{
const sourceVector = const BitVector(source);
auto targetVector = BitVector(target);
permute(sourceVector, targetVector, permuteTable, length);
}
private void permute(in BitVector source, ubyte[] target, immutable(ubyte[]) permuteTable, size_t length)
const pure nothrow @safe @nogc
{
auto targetVector = BitVector(target);
permute(source, targetVector, permuteTable, length);
}
private void permute(in BitVector source, ref BitVector target, immutable(ubyte[]) permuteTable, size_t length)
const pure nothrow @safe @nogc
{
for (uint i; i < length * 8; ++i)
{
target[i] = source[permuteTable[i] - 1];
}
}
}
version (unittest)
{
import std.typecons;
/* Test vectors for DES. Source:
"Validating the Correctness of Hardware
Implementations of the NBS Data Encryption Standard"
NBS Special Publication 500-20, 1980. Appendix B */
// Initial and reverse Permutation and Expansion tests. Encrypt.
ubyte[8][64] desTestVectors1 = [
[0x95, 0xf8, 0xa5, 0xe5, 0xdd, 0x31, 0xd9, 0x00],
[0xdd, 0x7f, 0x12, 0x1c, 0xa5, 0x01, 0x56, 0x19],
[0x2e, 0x86, 0x53, 0x10, 0x4f, 0x38, 0x34, 0xea],
[0x4b, 0xd3, 0x88, 0xff, 0x6c, 0xd8, 0x1d, 0x4f],
[0x20, 0xb9, 0xe7, 0x67, 0xb2, 0xfb, 0x14, 0x56],
[0x55, 0x57, 0x93, 0x80, 0xd7, 0x71, 0x38, 0xef],
[0x6c, 0xc5, 0xde, 0xfa, 0xaf, 0x04, 0x51, 0x2f],
[0x0d, 0x9f, 0x27, 0x9b, 0xa5, 0xd8, 0x72, 0x60],
[0xd9, 0x03, 0x1b, 0x02, 0x71, 0xbd, 0x5a, 0x0a],
[0x42, 0x42, 0x50, 0xb3, 0x7c, 0x3d, 0xd9, 0x51],
[0xb8, 0x06, 0x1b, 0x7e, 0xcd, 0x9a, 0x21, 0xe5],
[0xf1, 0x5d, 0x0f, 0x28, 0x6b, 0x65, 0xbd, 0x28],
[0xad, 0xd0, 0xcc, 0x8d, 0x6e, 0x5d, 0xeb, 0xa1],
[0xe6, 0xd5, 0xf8, 0x27, 0x52, 0xad, 0x63, 0xd1],
[0xec, 0xbf, 0xe3, 0xbd, 0x3f, 0x59, 0x1a, 0x5e],
[0xf3, 0x56, 0x83, 0x43, 0x79, 0xd1, 0x65, 0xcd],
[0x2b, 0x9f, 0x98, 0x2f, 0x20, 0x03, 0x7f, 0xa9],
[0x88, 0x9d, 0xe0, 0x68, 0xa1, 0x6f, 0x0b, 0xe6],
[0xe1, 0x9e, 0x27, 0x5d, 0x84, 0x6a, 0x12, 0x98],
[0x32, 0x9a, 0x8e, 0xd5, 0x23, 0xd7, 0x1a, 0xec],
[0xe7, 0xfc, 0xe2, 0x25, 0x57, 0xd2, 0x3c, 0x97],
[0x12, 0xa9, 0xf5, 0x81, 0x7f, 0xf2, 0xd6, 0x5d],
[0xa4, 0x84, 0xc3, 0xad, 0x38, 0xdc, 0x9c, 0x19],
[0xfb, 0xe0, 0x0a, 0x8a, 0x1e, 0xf8, 0xad, 0x72],
[0x75, 0x0d, 0x07, 0x94, 0x07, 0x52, 0x13, 0x63],
[0x64, 0xfe, 0xed, 0x9c, 0x72, 0x4c, 0x2f, 0xaf],
[0xf0, 0x2b, 0x26, 0x3b, 0x32, 0x8e, 0x2b, 0x60],
[0x9d, 0x64, 0x55, 0x5a, 0x9a, 0x10, 0xb8, 0x52],
[0xd1, 0x06, 0xff, 0x0b, 0xed, 0x52, 0x55, 0xd7],
[0xe1, 0x65, 0x2c, 0x6b, 0x13, 0x8c, 0x64, 0xa5],
[0xe4, 0x28, 0x58, 0x11, 0x86, 0xec, 0x8f, 0x46],
[0xae, 0xb5, 0xf5, 0xed, 0xe2, 0x2d, 0x1a, 0x36],
[0xe9, 0x43, 0xd7, 0x56, 0x8a, 0xec, 0x0c, 0x5c],
[0xdf, 0x98, 0xc8, 0x27, 0x6f, 0x54, 0xb0, 0x4b],
[0xb1, 0x60, 0xe4, 0x68, 0x0f, 0x6c, 0x69, 0x6f],
[0xfa, 0x07, 0x52, 0xb0, 0x7d, 0x9c, 0x4a, 0xb8],
[0xca, 0x3a, 0x2b, 0x03, 0x6d, 0xbc, 0x85, 0x02],
[0x5e, 0x09, 0x05, 0x51, 0x7b, 0xb5, 0x9b, 0xcf],
[0x81, 0x4e, 0xeb, 0x3b, 0x91, 0xd9, 0x07, 0x26],
[0x4d, 0x49, 0xdb, 0x15, 0x32, 0x91, 0x9c, 0x9f],
[0x25, 0xeb, 0x5f, 0xc3, 0xf8, 0xcf, 0x06, 0x21],
[0xab, 0x6a, 0x20, 0xc0, 0x62, 0x0d, 0x1c, 0x6f],
[0x79, 0xe9, 0x0d, 0xbc, 0x98, 0xf9, 0x2c, 0xca],
[0x86, 0x6e, 0xce, 0xdd, 0x80, 0x72, 0xbb, 0x0e],
[0x8b, 0x54, 0x53, 0x6f, 0x2f, 0x3e, 0x64, 0xa8],
[0xea, 0x51, 0xd3, 0x97, 0x55, 0x95, 0xb8, 0x6b],
[0xca, 0xff, 0xc6, 0xac, 0x45, 0x42, 0xde, 0x31],
[0x8d, 0xd4, 0x5a, 0x2d, 0xdf, 0x90, 0x79, 0x6c],
[0x10, 0x29, 0xd5, 0x5e, 0x88, 0x0e, 0xc2, 0xd0],
[0x5d, 0x86, 0xcb, 0x23, 0x63, 0x9d, 0xbe, 0xa9],
[0x1d, 0x1c, 0xa8, 0x53, 0xae, 0x7c, 0x0c, 0x5f],
[0xce, 0x33, 0x23, 0x29, 0x24, 0x8f, 0x32, 0x28],
[0x84, 0x05, 0xd1, 0xab, 0xe2, 0x4f, 0xb9, 0x42],
[0xe6, 0x43, 0xd7, 0x80, 0x90, 0xca, 0x42, 0x07],
[0x48, 0x22, 0x1b, 0x99, 0x37, 0x74, 0x8a, 0x23],
[0xdd, 0x7c, 0x0b, 0xbd, 0x61, 0xfa, 0xfd, 0x54],
[0x2f, 0xbc, 0x29, 0x1a, 0x57, 0x0d, 0xb5, 0xc4],
[0xe0, 0x7c, 0x30, 0xd7, 0xe4, 0xe2, 0x6e, 0x12],
[0x09, 0x53, 0xe2, 0x25, 0x8e, 0x8e, 0x90, 0xa1],
[0x5b, 0x71, 0x1b, 0xc4, 0xce, 0xeb, 0xf2, 0xee],
[0xcc, 0x08, 0x3f, 0x1e, 0x6d, 0x9e, 0x85, 0xf6],
[0xd2, 0xfd, 0x88, 0x67, 0xd5, 0x0d, 0x2d, 0xfe],
[0x06, 0xe7, 0xea, 0x22, 0xce, 0x92, 0x70, 0x8f],
[0x16, 0x6b, 0x40, 0xb4, 0x4a, 0xba, 0x4b, 0xd6],
];
// Key Permutation test. Encrypt.
// Test of right-shifts. Decrypt.
ubyte[8][56] desTestVectors2 = [
[0x95, 0xa8, 0xd7, 0x28, 0x13, 0xda, 0xa9, 0x4d],
[0x0e, 0xec, 0x14, 0x87, 0xdd, 0x8c, 0x26, 0xd5],
[0x7a, 0xd1, 0x6f, 0xfb, 0x79, 0xc4, 0x59, 0x26],
[0xd3, 0x74, 0x62, 0x94, 0xca, 0x6a, 0x6c, 0xf3],
[0x80, 0x9f, 0x5f, 0x87, 0x3c, 0x1f, 0xd7, 0x61],
[0xc0, 0x2f, 0xaf, 0xfe, 0xc9, 0x89, 0xd1, 0xfc],
[0x46, 0x15, 0xaa, 0x1d, 0x33, 0xe7, 0x2f, 0x10],
[0x20, 0x55, 0x12, 0x33, 0x50, 0xc0, 0x08, 0x58],
[0xdf, 0x3b, 0x99, 0xd6, 0x57, 0x73, 0x97, 0xc8],
[0x31, 0xfe, 0x17, 0x36, 0x9b, 0x52, 0x88, 0xc9],
[0xdf, 0xdd, 0x3c, 0xc6, 0x4d, 0xae, 0x16, 0x42],
[0x17, 0x8c, 0x83, 0xce, 0x2b, 0x39, 0x9d, 0x94],
[0x50, 0xf6, 0x36, 0x32, 0x4a, 0x9b, 0x7f, 0x80],
[0xa8, 0x46, 0x8e, 0xe3, 0xbc, 0x18, 0xf0, 0x6d],
[0xa2, 0xdc, 0x9e, 0x92, 0xfd, 0x3c, 0xde, 0x92],
[0xca, 0xc0, 0x9f, 0x79, 0x7d, 0x03, 0x12, 0x87],
[0x90, 0xba, 0x68, 0x0b, 0x22, 0xae, 0xb5, 0x25],
[0xce, 0x7a, 0x24, 0xf3, 0x50, 0xe2, 0x80, 0xb6],
[0x88, 0x2b, 0xff, 0x0a, 0xa0, 0x1a, 0x0b, 0x87],
[0x25, 0x61, 0x02, 0x88, 0x92, 0x45, 0x11, 0xc2],
[0xc7, 0x15, 0x16, 0xc2, 0x9c, 0x75, 0xd1, 0x70],
[0x51, 0x99, 0xc2, 0x9a, 0x52, 0xc9, 0xf0, 0x59],
[0xc2, 0x2f, 0x0a, 0x29, 0x4a, 0x71, 0xf2, 0x9f],
[0xee, 0x37, 0x14, 0x83, 0x71, 0x4c, 0x02, 0xea],
[0xa8, 0x1f, 0xbd, 0x44, 0x8f, 0x9e, 0x52, 0x2f],
[0x4f, 0x64, 0x4c, 0x92, 0xe1, 0x92, 0xdf, 0xed],
[0x1a, 0xfa, 0x9a, 0x66, 0xa6, 0xdf, 0x92, 0xae],
[0xb3, 0xc1, 0xcc, 0x71, 0x5c, 0xb8, 0x79, 0xd8],
[0x19, 0xd0, 0x32, 0xe6, 0x4a, 0xb0, 0xbd, 0x8b],
[0x3c, 0xfa, 0xa7, 0xa7, 0xdc, 0x87, 0x20, 0xdc],
[0xb7, 0x26, 0x5f, 0x7f, 0x44, 0x7a, 0xc6, 0xf3],
[0x9d, 0xb7, 0x3b, 0x3c, 0x0d, 0x16, 0x3f, 0x54],
[0x81, 0x81, 0xb6, 0x5b, 0xab, 0xf4, 0xa9, 0x75],
[0x93, 0xc9, 0xb6, 0x40, 0x42, 0xea, 0xa2, 0x40],
[0x55, 0x70, 0x53, 0x08, 0x29, 0x70, 0x55, 0x92],
[0x86, 0x38, 0x80, 0x9e, 0x87, 0x87, 0x87, 0xa0],
[0x41, 0xb9, 0xa7, 0x9a, 0xf7, 0x9a, 0xc2, 0x08],
[0x7a, 0x9b, 0xe4, 0x2f, 0x20, 0x09, 0xa8, 0x92],
[0x29, 0x03, 0x8d, 0x56, 0xba, 0x6d, 0x27, 0x45],
[0x54, 0x95, 0xc6, 0xab, 0xf1, 0xe5, 0xdf, 0x51],
[0xae, 0x13, 0xdb, 0xd5, 0x61, 0x48, 0x89, 0x33],
[0x02, 0x4d, 0x1f, 0xfa, 0x89, 0x04, 0xe3, 0x89],
[0xd1, 0x39, 0x97, 0x12, 0xf9, 0x9b, 0xf0, 0x2e],
[0x14, 0xc1, 0xd7, 0xc1, 0xcf, 0xfe, 0xc7, 0x9e],
[0x1d, 0xe5, 0x27, 0x9d, 0xae, 0x3b, 0xed, 0x6f],
[0xe9, 0x41, 0xa3, 0x3f, 0x85, 0x50, 0x13, 0x03],
[0xda, 0x99, 0xdb, 0xbc, 0x9a, 0x03, 0xf3, 0x79],
[0xb7, 0xfc, 0x92, 0xf9, 0x1d, 0x8e, 0x92, 0xe9],
[0xae, 0x8e, 0x5c, 0xaa, 0x3c, 0xa0, 0x4e, 0x85],
[0x9c, 0xc6, 0x2d, 0xf4, 0x3b, 0x6e, 0xed, 0x74],
[0xd8, 0x63, 0xdb, 0xb5, 0xc5, 0x9a, 0x91, 0xa0],
[0xa1, 0xab, 0x21, 0x90, 0x54, 0x5b, 0x91, 0xd7],
[0x08, 0x75, 0x04, 0x1e, 0x64, 0xc5, 0x70, 0xf7],
[0x5a, 0x59, 0x45, 0x28, 0xbe, 0xbe, 0xf1, 0xcc],
[0xfc, 0xdb, 0x32, 0x91, 0xde, 0x21, 0xf0, 0xc0],
[0x86, 0x9e, 0xfd, 0x7f, 0x9f, 0x26, 0x5a, 0x09],
];
// Data permutation test. Encrypt.
ubyte[8][2][32] desTestVectors3 = [
[[0x10, 0x46, 0x91, 0x34, 0x89, 0x98, 0x01, 0x31], [0x88, 0xd5, 0x5e, 0x54, 0xf5, 0x4c, 0x97, 0xb4]],
[[0x10, 0x07, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20], [0x0c, 0x0c, 0xc0, 0x0c, 0x83, 0xea, 0x48, 0xfd]],
[[0x10, 0x07, 0x10, 0x34, 0xc8, 0x98, 0x01, 0x20], [0x83, 0xbc, 0x8e, 0xf3, 0xa6, 0x57, 0x01, 0x83]],
[[0x10, 0x46, 0x10, 0x34, 0x89, 0x98, 0x80, 0x20], [0xdf, 0x72, 0x5d, 0xca, 0xd9, 0x4e, 0xa2, 0xe9]],
[[0x10, 0x86, 0x91, 0x15, 0x19, 0x19, 0x01, 0x01], [0xe6, 0x52, 0xb5, 0x3b, 0x55, 0x0b, 0xe8, 0xb0]],
[[0x10, 0x86, 0x91, 0x15, 0x19, 0x58, 0x01, 0x01], [0xaf, 0x52, 0x71, 0x20, 0xc4, 0x85, 0xcb, 0xb0]],
[[0x51, 0x07, 0xb0, 0x15, 0x19, 0x58, 0x01, 0x01], [0x0f, 0x04, 0xce, 0x39, 0x3d, 0xb9, 0x26, 0xd5]],
[[0x10, 0x07, 0xb0, 0x15, 0x19, 0x19, 0x01, 0x01], [0xc9, 0xf0, 0x0f, 0xfc, 0x74, 0x07, 0x90, 0x67]],
[[0x31, 0x07, 0x91, 0x54, 0x98, 0x08, 0x01, 0x01], [0x7c, 0xfd, 0x82, 0xa5, 0x93, 0x25, 0x2b, 0x4e]],
[[0x31, 0x07, 0x91, 0x94, 0x98, 0x08, 0x01, 0x01], [0xcb, 0x49, 0xa2, 0xf9, 0xe9, 0x13, 0x63, 0xe3]],
[[0x10, 0x07, 0x91, 0x15, 0xb9, 0x08, 0x01, 0x40], [0x00, 0xb5, 0x88, 0xbe, 0x70, 0xd2, 0x3f, 0x56]],
[[0x31, 0x07, 0x91, 0x15, 0x98, 0x08, 0x01, 0x40], [0x40, 0x6a, 0x9a, 0x6a, 0xb4, 0x33, 0x99, 0xae]],
[[0x10, 0x07, 0xd0, 0x15, 0x89, 0x98, 0x01, 0x01], [0x6c, 0xb7, 0x73, 0x61, 0x1d, 0xca, 0x9a, 0xda]],
[[0x91, 0x07, 0x91, 0x15, 0x89, 0x98, 0x01, 0x01], [0x67, 0xfd, 0x21, 0xc1, 0x7d, 0xbb, 0x5d, 0x70]],
[[0x91, 0x07, 0xd0, 0x15, 0x89, 0x19, 0x01, 0x01], [0x95, 0x92, 0xcb, 0x41, 0x10, 0x43, 0x07, 0x87]],
[[0x10, 0x07, 0xd0, 0x15, 0x98, 0x98, 0x01, 0x20], [0xa6, 0xb7, 0xff, 0x68, 0xa3, 0x18, 0xdd, 0xd3]],
[[0x10, 0x07, 0x94, 0x04, 0x98, 0x19, 0x01, 0x01], [0x4d, 0x10, 0x21, 0x96, 0xc9, 0x14, 0xca, 0x16]],
[[0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x04, 0x01], [0x2d, 0xfa, 0x9f, 0x45, 0x73, 0x59, 0x49, 0x65]],
[[0x01, 0x07, 0x91, 0x04, 0x91, 0x19, 0x01, 0x01], [0xb4, 0x66, 0x04, 0x81, 0x6c, 0x0e, 0x07, 0x74]],
[[0x01, 0x07, 0x94, 0x04, 0x91, 0x19, 0x04, 0x01], [0x6e, 0x7e, 0x62, 0x21, 0xa4, 0xf3, 0x4e, 0x87]],
[[0x19, 0x07, 0x92, 0x10, 0x98, 0x1a, 0x01, 0x01], [0xaa, 0x85, 0xe7, 0x46, 0x43, 0x23, 0x31, 0x99]],
[[0x10, 0x07, 0x91, 0x19, 0x98, 0x19, 0x08, 0x01], [0x2e, 0x5a, 0x19, 0xdb, 0x4d, 0x19, 0x62, 0xd6]],
[[0x10, 0x07, 0x91, 0x19, 0x98, 0x1a, 0x08, 0x01], [0x23, 0xa8, 0x66, 0xa8, 0x09, 0xd3, 0x08, 0x94]],
[[0x10, 0x07, 0x92, 0x10, 0x98, 0x19, 0x01, 0x01], [0xd8, 0x12, 0xd9, 0x61, 0xf0, 0x17, 0xd3, 0x20]],
[[0x10, 0x07, 0x91, 0x15, 0x98, 0x19, 0x01, 0x0b], [0x05, 0x56, 0x05, 0x81, 0x6e, 0x58, 0x60, 0x8f]],
[[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x01], [0xab, 0xd8, 0x8e, 0x8b, 0x1b, 0x77, 0x16, 0xf1]],
[[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x02], [0x53, 0x7a, 0xc9, 0x5b, 0xe6, 0x9d, 0xa1, 0xe1]],
[[0x10, 0x04, 0x80, 0x15, 0x98, 0x19, 0x01, 0x08], [0xae, 0xd0, 0xf6, 0xae, 0x3c, 0x25, 0xcd, 0xd8]],
[[0x10, 0x02, 0x91, 0x14, 0x98, 0x10, 0x01, 0x04], [0xb3, 0xe3, 0x5a, 0x5e, 0xe5, 0x3e, 0x7b, 0x8d]],
[[0x10, 0x02, 0x91, 0x15, 0x98, 0x19, 0x01, 0x04], [0x61, 0xc7, 0x9c, 0x71, 0x92, 0x1a, 0x2e, 0xf8]],
[[0x10, 0x02, 0x91, 0x15, 0x98, 0x10, 0x02, 0x01], [0xe2, 0xf5, 0x72, 0x8f, 0x09, 0x95, 0x01, 0x3c]],
[[0x10, 0x02, 0x91, 0x16, 0x98, 0x10, 0x01, 0x01], [0x1a, 0xea, 0xc3, 0x9a, 0x61, 0xf0, 0xa4, 0x64]],
];
// S-Box test. Encrypt.
ubyte[8][3][19] desTestVectors4 = [
[[0x7c, 0xa1, 0x10, 0x45, 0x4a, 0x1a, 0x6e, 0x57], [0x01, 0xa1, 0xd6, 0xd0, 0x39, 0x77, 0x67, 0x42],
[0x69, 0x0f, 0x5b, 0x0d, 0x9a, 0x26, 0x93, 0x9b]],
[[0x01, 0x31, 0xd9, 0x61, 0x9d, 0xc1, 0x37, 0x6e], [0x5c, 0xd5, 0x4c, 0xa8, 0x3d, 0xef, 0x57, 0xda],
[0x7a, 0x38, 0x9d, 0x10, 0x35, 0x4b, 0xd2, 0x71]],
[[0x07, 0xa1, 0x13, 0x3e, 0x4a, 0x0b, 0x26, 0x86], [0x02, 0x48, 0xd4, 0x38, 0x06, 0xf6, 0x71, 0x72],
[0x86, 0x8e, 0xbb, 0x51, 0xca, 0xb4, 0x59, 0x9a]],
[[0x38, 0x49, 0x67, 0x4c, 0x26, 0x02, 0x31, 0x9e], [0x51, 0x45, 0x4b, 0x58, 0x2d, 0xdf, 0x44, 0x0a],
[0x71, 0x78, 0x87, 0x6e, 0x01, 0xf1, 0x9b, 0x2a]],
[[0x04, 0xb9, 0x15, 0xba, 0x43, 0xfe, 0xb5, 0xb6], [0x42, 0xfd, 0x44, 0x30, 0x59, 0x57, 0x7f, 0xa2],
[0xaf, 0x37, 0xfb, 0x42, 0x1f, 0x8c, 0x40, 0x95]],
[[0x01, 0x13, 0xb9, 0x70, 0xfd, 0x34, 0xf2, 0xce], [0x05, 0x9b, 0x5e, 0x08, 0x51, 0xcf, 0x14, 0x3a],
[0x86, 0xa5, 0x60, 0xf1, 0x0e, 0xc6, 0xd8, 0x5b]],
[[0x01, 0x70, 0xf1, 0x75, 0x46, 0x8f, 0xb5, 0xe6], [0x07, 0x56, 0xd8, 0xe0, 0x77, 0x47, 0x61, 0xd2],
[0x0c, 0xd3, 0xda, 0x02, 0x00, 0x21, 0xdc, 0x09]],
[[0x43, 0x29, 0x7f, 0xad, 0x38, 0xe3, 0x73, 0xfe], [0x76, 0x25, 0x14, 0xb8, 0x29, 0xbf, 0x48, 0x6a],
[0xea, 0x67, 0x6b, 0x2c, 0xb7, 0xdb, 0x2b, 0x7a]],
[[0x07, 0xa7, 0x13, 0x70, 0x45, 0xda, 0x2a, 0x16], [0x3b, 0xdd, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02],
[0xdf, 0xd6, 0x4a, 0x81, 0x5c, 0xaf, 0x1a, 0x0f]],
[[0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd, 0x3b, 0x2f], [0x26, 0x95, 0x5f, 0x68, 0x35, 0xaf, 0x60, 0x9a],
[0x5c, 0x51, 0x3c, 0x9c, 0x48, 0x86, 0xc0, 0x88]],
[[0x37, 0xd0, 0x6b, 0xb5, 0x16, 0xcb, 0x75, 0x46], [0x16, 0x4d, 0x5e, 0x40, 0x4f, 0x27, 0x52, 0x32],
[0x0a, 0x2a, 0xee, 0xae, 0x3f, 0xf4, 0xab, 0x77]],
[[0x1f, 0x08, 0x26, 0x0d, 0x1a, 0xc2, 0x46, 0x5e], [0x6b, 0x05, 0x6e, 0x18, 0x75, 0x9f, 0x5c, 0xca],
[0xef, 0x1b, 0xf0, 0x3e, 0x5d, 0xfa, 0x57, 0x5a]],
[[0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76], [0x00, 0x4b, 0xd6, 0xef, 0x09, 0x17, 0x60, 0x62],
[0x88, 0xbf, 0x0d, 0xb6, 0xd7, 0x0d, 0xee, 0x56]],
[[0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xb0, 0x07], [0x48, 0x0d, 0x39, 0x00, 0x6e, 0xe7, 0x62, 0xf2],
[0xa1, 0xf9, 0x91, 0x55, 0x41, 0x02, 0x0b, 0x56]],
[[0x49, 0x79, 0x3e, 0xbc, 0x79, 0xb3, 0x25, 0x8f], [0x43, 0x75, 0x40, 0xc8, 0x69, 0x8f, 0x3c, 0xfa],
[0x6f, 0xbf, 0x1c, 0xaf, 0xcf, 0xfd, 0x05, 0x56]],
[[0x4f, 0xb0, 0x5e, 0x15, 0x15, 0xab, 0x73, 0xa7], [0x07, 0x2d, 0x43, 0xa0, 0x77, 0x07, 0x52, 0x92],
[0x2f, 0x22, 0xe4, 0x9b, 0xab, 0x7c, 0xa1, 0xac]],
[[0x49, 0xe9, 0x5d, 0x6d, 0x4c, 0xa2, 0x29, 0xbf], [0x02, 0xfe, 0x55, 0x77, 0x81, 0x17, 0xf1, 0x2a],
[0x5a, 0x6b, 0x61, 0x2c, 0xc2, 0x6c, 0xce, 0x4a]],
[[0x01, 0x83, 0x10, 0xdc, 0x40, 0x9b, 0x26, 0xd6], [0x1d, 0x9d, 0x5c, 0x50, 0x18, 0xf7, 0x28, 0xc2],
[0x5f, 0x4c, 0x03, 0x8e, 0xd1, 0x2b, 0x2e, 0x41]],
[[0x1c, 0x58, 0x7f, 0x1c, 0x13, 0x92, 0x4f, 0xef], [0x30, 0x55, 0x32, 0x28, 0x6d, 0x6f, 0x29, 0x5a],
[0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93]],
];
}
///
unittest
{
auto des = scoped!(DES!1);
ubyte[8] key = [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01];
ubyte[8] plain = [0x80, 0, 0, 0, 0, 0, 0, 0];
ubyte[8] cipher;
des.key = key;
foreach (ubyte i; 0..64)
{
if (i != 0)
{
plain[i / 8] = i % 8 ? plain[i / 8] >> 1 : 0x80;
if (i % 8 == 0)
{
plain[i / 8 - 1] = 0;
}
}
// Initial Permutation and Expansion test.
des.encrypt(plain, cipher);
assert(cipher == desTestVectors1[i]);
// Inverse Permutation and Expansion test.
des.encrypt(cipher, cipher);
assert(cipher == plain);
}
plain[0..$] = 0;
foreach (ubyte i; 0..56)
{
key[i / 7] = i % 7 ? key[i / 7] >> 1 : 0x80;
if (i % 7 == 0 && i != 0)
{
key[i / 7 - 1] = 0x01;
}
des.key = key;
// Initial Permutation and Expansion test.
des.encrypt(plain, cipher);
assert(cipher == desTestVectors2[i]);
// Test of right-shifts in Decryption.
des.decrypt(desTestVectors2[i], cipher);
assert(cipher == plain);
}
// Data permutation test.
plain[0..$] = 0;
foreach (i; desTestVectors3)
{
des.key = i[0];
des.encrypt(plain, cipher);
assert(cipher == i[1]);
}
// S-Box test.
foreach (i; desTestVectors4)
{
des.key = i[0];
des.encrypt(i[1], cipher);
assert(cipher == i[2]);
}
}

View File

@ -1,279 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Block cipher modes of operation.
*
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.crypto.mode;
import tanya.memory;
import std.algorithm.iteration;
import std.typecons;
/**
* Supported padding mode.
*
* See_Also:
* $(D_PSYMBOL pad)
*/
enum PaddingMode
{
zero,
pkcs7,
ansiX923,
}
/**
* Params:
* input = Sequence that should be padded.
* mode = Padding mode.
* blockSize = Block size.
* allocator = Allocator was used to allocate $(D_PARAM input).
*
* Returns: The function modifies the initial array and returns it.
*
* See_Also:
* $(D_PSYMBOL PaddingMode)
*/
ubyte[] pad(ref ubyte[] input,
in PaddingMode mode,
in ushort blockSize,
shared Allocator allocator = defaultAllocator)
in
{
assert(blockSize > 0 && blockSize <= 256);
assert(blockSize % 64 == 0);
assert(input.length > 0);
}
body
{
immutable rest = cast(ubyte) input.length % blockSize;
immutable size_t lastBlock = input.length - (rest > 0 ? rest : blockSize);
immutable needed = cast(ubyte) (rest > 0 ? blockSize - rest : 0);
final switch (mode) with (PaddingMode)
{
case zero:
allocator.resizeArray(input, input.length + needed);
break;
case pkcs7:
if (needed)
{
allocator.resizeArray(input, input.length + needed);
input[input.length - needed ..$].each!((ref e) => e = needed);
}
else
{
allocator.resizeArray(input, input.length + blockSize);
}
break;
case ansiX923:
allocator.resizeArray(input, input.length + (needed ? needed : blockSize));
input[$ - 1] = needed;
break;
}
return input;
}
///
unittest
{
{ // Zeros
auto input = defaultAllocator.makeArray!ubyte(50);
pad(input, PaddingMode.zero, 64);
assert(input.length == 64);
pad(input, PaddingMode.zero, 64);
assert(input.length == 64);
assert(input[63] == 0);
defaultAllocator.dispose(input);
}
{ // PKCS#7
auto input = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i)
{
input[i] = i;
}
pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 64);
for (ubyte i; i < 64; ++i)
{
if (i >= 40 && i < 50)
{
assert(input[i] == 0);
}
else if (i >= 50)
{
assert(input[i] == 14);
}
else
{
assert(input[i] == i);
}
}
pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 128);
for (ubyte i; i < 128; ++i)
{
if (i >= 64 || (i >= 40 && i < 50))
{
assert(input[i] == 0);
}
else if (i >= 50 && i < 64)
{
assert(input[i] == 14);
}
else
{
assert(input[i] == i);
}
}
defaultAllocator.dispose(input);
}
{ // ANSI X.923
auto input = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i)
{
input[i] = i;
}
pad(input, PaddingMode.ansiX923, 64);
assert(input.length == 64);
for (ubyte i; i < 64; ++i)
{
if (i < 40)
{
assert(input[i] == i);
}
else if (i == 63)
{
assert(input[i] == 14);
}
else
{
assert(input[i] == 0);
}
}
pad(input, PaddingMode.pkcs7, 64);
assert(input.length == 128);
for (ubyte i = 0; i < 128; ++i)
{
if (i < 40)
{
assert(input[i] == i);
}
else if (i == 63)
{
assert(input[i] == 14);
}
else
{
assert(input[i] == 0);
}
}
defaultAllocator.dispose(input);
}
}
/**
* Params:
* input = Sequence that should be padded.
* mode = Padding mode.
* blockSize = Block size.
* allocator = Allocator was used to allocate $(D_PARAM input).
*
* Returns: The function modifies the initial array and returns it.
*
* See_Also:
* $(D_PSYMBOL pad)
*/
ref ubyte[] unpad(ref ubyte[] input,
in PaddingMode mode,
in ushort blockSize,
shared Allocator allocator = defaultAllocator)
in
{
assert(input.length != 0);
assert(input.length % 64 == 0);
}
body
{
final switch (mode) with (PaddingMode)
{
case zero:
break;
case pkcs7:
case ansiX923:
immutable last = input[$ - 1];
allocator.resizeArray(input, input.length - (last ? last : blockSize));
break;
}
return input;
}
///
unittest
{
{ // Zeros
auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = defaultAllocator.makeArray!ubyte(50);
pad(input, PaddingMode.zero, 64);
pad(inputDup, PaddingMode.zero, 64);
unpad(input, PaddingMode.zero, 64);
assert(input == inputDup);
defaultAllocator.dispose(input);
defaultAllocator.dispose(inputDup);
}
{ // PKCS#7
auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i)
{
input[i] = i;
inputDup[i] = i;
}
pad(input, PaddingMode.pkcs7, 64);
unpad(input, PaddingMode.pkcs7, 64);
assert(input == inputDup);
defaultAllocator.dispose(input);
defaultAllocator.dispose(inputDup);
}
{ // ANSI X.923
auto input = defaultAllocator.makeArray!ubyte(50);
auto inputDup = defaultAllocator.makeArray!ubyte(50);
for (ubyte i; i < 40; ++i)
{
input[i] = i;
inputDup[i] = i;
}
pad(input, PaddingMode.pkcs7, 64);
unpad(input, PaddingMode.pkcs7, 64);
assert(input == inputDup);
defaultAllocator.dispose(input);
defaultAllocator.dispose(inputDup);
}
}

View File

@ -1,16 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.crypto;
public import tanya.crypto.bit;
public import tanya.crypto.des;
public import tanya.crypto.mode;
public import tanya.crypto.symmetric;

View File

@ -1,177 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Interfaces for implementing secret key algorithms.
*
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.crypto.symmetric;
/**
* Implemented by secret key algorithms.
*/
interface SymmetricCipher
{
/**
* Returns: Key length.
*/
@property inout(uint) keyLength() inout const pure nothrow @safe @nogc;
/**
* Returns: Minimum key length.
*/
@property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc;
/**
* Returns: Maximum key length.
*/
@property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc;
/// Cipher direction.
protected enum Direction : ushort
{
encryption,
decryption,
}
/**
* Params:
* key = Key.
*/
@property void key(ubyte[] key) pure nothrow @safe @nogc
in
{
assert(key.length >= minKeyLength);
assert(key.length <= maxKeyLength);
}
}
/**
* Implemented by block ciphers.
*/
interface BlockCipher : SymmetricCipher
{
/**
* Returns: Block size.
*/
@property inout(uint) blockSize() inout const pure nothrow @safe @nogc;
/**
* Encrypts a block.
*
* Params:
* plain = Plain text, input.
* cipher = Cipher text, output.
*/
void encrypt(in ubyte[] plain, ubyte[] cipher)
in
{
assert(plain.length == blockSize);
assert(cipher.length == blockSize);
}
/**
* Decrypts a block.
*
* Params:
* cipher = Cipher text, input.
* plain = Plain text, output.
*/
void decrypt(in ubyte[] cipher, ubyte[] plain)
in
{
assert(plain.length == blockSize);
assert(cipher.length == blockSize);
}
}
/**
* Mixed in by algorithms with fixed block size.
*
* Params:
* N = Block size.
*/
mixin template FixedBlockSize(uint N)
if (N != 0)
{
private enum uint blockSize_ = N;
/**
* Returns: Fixed block size.
*/
final @property inout(uint) blockSize() inout const pure nothrow @safe @nogc
{
return blockSize_;
}
}
/**
* Mixed in by symmetric algorithms.
* If $(D_PARAM Min) equals $(D_PARAM Max) fixed key length is assumed.
*
* Params:
* Min = Minimum key length.
* Max = Maximum key length.
*/
mixin template KeyLength(uint Min, uint Max = Min)
if (Min != 0 && Max != 0)
{
static if (Min == Max)
{
private enum uint keyLength_ = Min;
/**
* Returns: Key length.
*/
final @property inout(uint) keyLength() inout const pure nothrow @safe @nogc
{
return keyLength_;
}
/**
* Returns: Minimum key length.
*/
final @property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc
{
return keyLength_;
}
/**
* Returns: Maximum key length.
*/
final @property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc
{
return keyLength_;
}
}
else static if (Min < Max)
{
private enum uint minKeyLength_ = Min;
private enum uint maxKeyLength_ = Max;
/**
* Returns: Minimum key length.
*/
final @property inout(uint) minKeyLength() inout const pure nothrow @safe @nogc
{
return minKeyLength_;
}
/**
* Returns: Maximum key length.
*/
final @property inout(uint) maxKeyLength() inout const pure nothrow @safe @nogc
{
return maxKeyLength_;
}
}
else
{
static assert(false, "Max should be larger or equal to Min");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.math;
@ -79,7 +79,7 @@ body
}
/// Ditto.
I pow(I)(in auto ref I x, in auto ref I y, in auto ref I z)
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z)
if (is(I == Integer))
in
{
@ -87,32 +87,33 @@ in
}
body
{
size_t i = y.length;
auto tmp2 = Integer(x.allocator), tmp1 = Integer(x, x.allocator);
Integer result = Integer(x.allocator);
size_t i;
auto tmp1 = Integer(x, 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
{
result = 1;
}
while (i)
while (i < y.size)
{
--i;
for (ubyte mask = 0x01; mask; mask <<= 1)
for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
{
if (y.rep[i] & mask)
{
result *= tmp1;
result %= z;
}
tmp2 = tmp1;
auto tmp2 = tmp1;
tmp1 *= tmp2;
tmp1 %= z;
}
++i;
}
return result;
}
@ -134,15 +135,15 @@ pure nothrow @safe @nogc unittest
///
unittest
{
assert(cast(long) pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(cast(long) pow(Integer(2), Integer(2), Integer(1)) == 0);
assert(cast(long) pow(Integer(3), Integer(3), Integer(3)) == 0);
assert(cast(long) pow(Integer(7), Integer(4), Integer(2)) == 1);
assert(cast(long) pow(Integer(53), Integer(0), Integer(2)) == 1);
assert(cast(long) pow(Integer(53), Integer(1), Integer(3)) == 2);
assert(cast(long) pow(Integer(53), Integer(2), Integer(5)) == 4);
assert(cast(long) pow(Integer(0), Integer(0), Integer(5)) == 1);
assert(cast(long) pow(Integer(0), Integer(5), Integer(5)) == 0);
assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
assert(pow(Integer(3), Integer(3), Integer(3)) == 0);
assert(pow(Integer(7), Integer(4), Integer(2)) == 1);
assert(pow(Integer(53), Integer(0), Integer(2)) == 1);
assert(pow(Integer(53), Integer(1), Integer(3)) == 2);
assert(pow(Integer(53), Integer(2), Integer(5)) == 4);
assert(pow(Integer(0), Integer(0), Integer(5)) == 1);
assert(pow(Integer(0), Integer(5), Integer(5)) == 0);
}
/**
@ -170,3 +171,31 @@ unittest
known.each!((ref x) => assert(isPseudoprime(x)));
}
/**
* Params:
* I = Value type.
* x = Value.
*
* Returns: The absolute value of a number.
*/
I abs(I : Integer)(const auto ref I x)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// Ditto.
I abs(I : Integer)(I x)
{
x.sign = Sign.positive;
return x;
}
/// Ditto.
I abs(I)(const I x)
if (isIntegral!I)
{
return x >= 0 ? x : -x;
}

View File

@ -8,7 +8,7 @@
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/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.math.random;
@ -156,7 +156,7 @@ version (linux)
*
* output = entropy.random;
*
* defaultAllocator.finalize(entropy);
* defaultAllocator.dispose(entropy);
* ---
*/
class Entropy
@ -185,7 +185,7 @@ class Entropy
}
body
{
allocator.resizeArray(sources, maxSources);
allocator.resize(sources, maxSources);
version (linux)
{

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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)
@ -52,7 +52,10 @@ interface Allocator
bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc;
/**
* Expands a memory block in place.
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
* $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
*
* Params:
* p = A pointer to the memory block.
@ -60,68 +63,5 @@ interface Allocator
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool expand(ref void[] p, in size_t size) shared nothrow @nogc;
}
/**
* The mixin generates common methods for classes and structs using
* allocators. It provides a protected member, constructor and a read-only property,
* that checks if an allocator was already set and sets it to the default
* one, if not (useful for structs which don't have a default constructor).
*/
mixin template DefaultAllocator()
{
/// Allocator.
protected shared Allocator allocator_;
/**
* Params:
* allocator = The allocator should be used.
*/
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
/**
* This property checks if the allocator was set in the constructor
* and sets it to the default one, if not.
*
* Returns: Used allocator.
*
* Postcondition: $(D_INLINECODE allocator_ !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
allocator_ = defaultAllocator;
}
return allocator_;
}
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
return defaultAllocator;
}
return cast(shared Allocator) allocator_;
}
bool reallocateInPlace(ref void[] p, in size_t size) shared nothrow @nogc;
}

View File

@ -0,0 +1,185 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2017.
* 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.memory.mallocator;
import core.stdc.stdlib;
import std.algorithm.comparison;
import tanya.memory.allocator;
/**
* Wrapper for malloc/realloc/free from the C standard library.
*/
final class Mallocator : Allocator
{
/**
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
* size = Amount of memory to allocate.
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(in size_t size) shared nothrow @nogc
{
if (!size)
{
return null;
}
auto p = malloc(size + psize);
return p is null ? null : p[psize .. psize + size];
}
///
@nogc nothrow unittest
{
auto p = Mallocator.instance.allocate(20);
assert(p.length == 20);
Mallocator.instance.deallocate(p);
}
/**
* Deallocates a memory block.
*
* Params:
* p = A pointer to the memory block to be freed.
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
{
if (p !is null)
{
free(p.ptr - psize);
}
return true;
}
///
@nogc nothrow unittest
{
void[] p;
assert(Mallocator.instance.deallocate(p));
p = Mallocator.instance.allocate(10);
assert(Mallocator.instance.deallocate(p));
}
/**
* Reallocating in place isn't supported.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size) shared nothrow @nogc
{
return false;
}
/**
* Increases or decreases the size of a memory block.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
{
if (size == 0)
{
if (deallocate(p))
{
p = null;
return true;
}
}
else if (p is null)
{
p = allocate(size);
return p is null ? false : true;
}
else
{
auto r = realloc(p.ptr - psize, size + psize);
if (r !is null)
{
p = r[psize .. psize + size];
return true;
}
}
return false;
}
///
@nogc nothrow unittest
{
void[] p;
assert(Mallocator.instance.reallocate(p, 20));
assert(p.length == 20);
assert(Mallocator.instance.reallocate(p, 30));
assert(p.length == 30);
assert(Mallocator.instance.reallocate(p, 10));
assert(p.length == 10);
assert(Mallocator.instance.reallocate(p, 0));
assert(p is null);
}
/**
* Returns: The alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
{
return cast(uint) max(double.alignof, real.alignof);
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property ref shared(Mallocator) instance() @nogc nothrow
{
if (instance_ is null)
{
immutable size = __traits(classInstanceSize, Mallocator) + psize;
void* p = malloc(size);
if (p !is null)
{
p[psize .. size] = typeid(Mallocator).initializer[];
instance_ = cast(shared Mallocator) p[psize .. size].ptr;
}
}
return instance_;
}
///
@nogc nothrow unittest
{
assert(instance is instance);
}
private enum psize = 8;
private shared static Mallocator instance_;
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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)
@ -50,6 +50,21 @@ else version (Windows)
*/
final class MmapPool : Allocator
{
invariant
{
for (auto r = &head; *r !is null; r = &((*r).next))
{
auto block = cast(Block) (cast(void*) *r + RegionEntry.sizeof);
do
{
assert(block.prev is null || block.prev.next is block);
assert(block.next is null || block.next.prev is block);
assert(block.region is *r);
}
while ((block = block.next) !is null);
}
}
/**
* Allocates $(D_PARAM size) bytes of memory.
*
@ -156,7 +171,7 @@ final class MmapPool : Allocator
*/
bool deallocate(void[] p) shared nothrow @nogc
{
if (p is null)
if (p.ptr is null)
{
return true;
}
@ -216,7 +231,10 @@ final class MmapPool : Allocator
}
/**
* Expands a memory block in place.
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
* $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
*
* Params:
* p = A pointer to the memory block.
@ -224,16 +242,18 @@ final class MmapPool : Allocator
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool expand(ref void[] p, in size_t size) shared nothrow @nogc
bool reallocateInPlace(ref void[] p, in size_t size) shared nothrow @nogc
{
if (size <= p.length)
{
return true;
}
if (p is null)
if (p is null || size == 0)
{
return false;
}
if (size <= p.length)
{
// Leave the block as is.
p = p.ptr[0 .. size];
return true;
}
Block block1 = cast(Block) (p.ptr - BlockEntry.sizeof);
if (block1.size >= size)
@ -243,7 +263,7 @@ final class MmapPool : Allocator
return true;
}
immutable dataSize = addAlignment(size);
immutable delta = dataSize - p.length;
immutable delta = dataSize - addAlignment(p.length);
if (block1.next is null
|| !block1.next.free
@ -255,21 +275,17 @@ final class MmapPool : Allocator
}
if (block1.next.size >= delta + alignment_)
{
// We should move the start position of the next block. The order may be
// important because the old block and the new one can overlap.
auto block2 = cast(Block) (p.ptr + dataSize);
block2.size = block1.next.size - delta;
block2.free = true;
block2.region = block1.region;
block2.next = block1.next.next;
block2.prev = block1;
// Move size from block2 to block1.
block1.next.size = block1.next.size - delta;
block1.size = block1.size + delta;
auto block2 = cast(Block) (p.ptr + dataSize);
if (block1.next.next !is null)
{
block1.next.next.prev = block2;
}
// block1.next and block2 can overlap.
memmove(cast(void*) block2, cast(void*) block1.next, BlockEntry.sizeof);
block1.next = block2;
}
else
@ -287,22 +303,22 @@ final class MmapPool : Allocator
nothrow unittest
{
void[] p;
assert(!MmapPool.instance.expand(p, 5));
assert(!MmapPool.instance.reallocateInPlace(p, 5));
assert(p is null);
p = MmapPool.instance.allocate(1);
auto orig = p.ptr;
assert(MmapPool.instance.expand(p, 2));
assert(MmapPool.instance.reallocateInPlace(p, 2));
assert(p.length == 2);
assert(p.ptr == orig);
assert(MmapPool.instance.expand(p, 4));
assert(MmapPool.instance.reallocateInPlace(p, 4));
assert(p.length == 4);
assert(p.ptr == orig);
assert(MmapPool.instance.expand(p, 2));
assert(p.length == 4);
assert(MmapPool.instance.reallocateInPlace(p, 2));
assert(p.length == 2);
assert(p.ptr == orig);
MmapPool.instance.deallocate(p);
@ -328,17 +344,12 @@ final class MmapPool : Allocator
}
return false;
}
else if (size <= p.length)
{
// Leave the block as is.
p = p.ptr[0 .. size];
return true;
}
else if (expand(p, size))
else if (reallocateInPlace(p, size))
{
return true;
}
// Can't extend, allocate a new block, copy and delete the previous.
// Can't reallocate in place, allocate a new block,
// copy and delete the previous one.
void[] reallocP = allocate(size);
if (reallocP is null)
{
@ -574,3 +585,61 @@ final class MmapPool : Allocator
}
private alias Block = shared BlockEntry*;
}
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);
auto b = MmapPool.instance.allocate(16);
auto e = MmapPool.instance.allocate(16);
auto c = MmapPool.instance.allocate(16);
auto f = MmapPool.instance.allocate(16);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(c);
a = MmapPool.instance.allocate(50);
MmapPool.instance.reallocateInPlace(a, 64);
MmapPool.instance.deallocate(a);
a = MmapPool.instance.allocate(1);
auto tmp1 = MmapPool.instance.allocate(1);
auto h1 = MmapPool.instance.allocate(1);
auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1);
auto h2 = MmapPool.instance.allocate(2);
tmp1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
MmapPool.instance.deallocate(h1);
h2 = MmapPool.instance.allocate(2);
h1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
auto rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 9);
rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 17);
tmp2[$ - 1] = 0;
MmapPool.instance.deallocate(tmp1);
b = MmapPool.instance.allocate(16);
MmapPool.instance.deallocate(h1);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(d);
MmapPool.instance.deallocate(e);
MmapPool.instance.deallocate(f);
}

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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)
@ -11,10 +11,75 @@
module tanya.memory;
import core.exception;
public import std.experimental.allocator : make, makeArray;
import std.algorithm.iteration;
public import std.experimental.allocator : make;
import std.traits;
public import tanya.memory.allocator;
public import tanya.memory.types;
/**
* The mixin generates common methods for classes and structs using
* allocators. It provides a protected member, constructor and a read-only property,
* that checks if an allocator was already set and sets it to the default
* one, if not (useful for structs which don't have a default constructor).
*/
mixin template DefaultAllocator()
{
/// Allocator.
protected shared Allocator allocator_;
/**
* Params:
* allocator = The allocator should be used.
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
/**
* This property checks if the allocator was set in the constructor
* and sets it to the default one, if not.
*
* Returns: Used allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
allocator_ = defaultAllocator;
}
return allocator_;
}
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
return defaultAllocator;
}
return cast(shared Allocator) allocator_;
}
}
// From druntime
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
@ -28,11 +93,21 @@ shared static this() nothrow @trusted @nogc
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
return allocator;
}
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in
{
assert(allocator !is null);
}
body
{
.allocator = allocator;
}
@ -63,109 +138,90 @@ template stateSize(T)
*
* Returns: Aligned size.
*/
size_t alignedSize(in size_t size, in size_t alignment = 8) pure nothrow @safe @nogc
size_t alignedSize(const size_t size, const size_t alignment = 8)
pure nothrow @safe @nogc
{
return (size - 1) / alignment * alignment + alignment;
}
/**
* Internal function used to create, resize or destroy a dynamic array. It
* throws $(D_PSYMBOL OutOfMemoryError) if $(D_PARAM Throws) is set. The new
* allocated part of the array is initialized only if $(D_PARAM Init)
* is set. This function can be trusted only in the data structures that
* can ensure that the array is allocated/rellocated/deallocated with the
* same allocator.
* may throw $(D_PSYMBOL OutOfMemoryError). The new
* allocated part of the array isn't initialized. This function can be trusted
* only in the data structures that can ensure that the array is
* allocated/rellocated/deallocated with the same allocator.
*
* Params:
* T = Element type of the array being created.
* Init = If should be initialized.
* Throws = If $(D_PSYMBOL OutOfMemoryError) should be throwsn.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
*
* Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could
* not be reallocated. In the latter
* Returns: $(D_PARAM array).
*/
package(tanya) bool resize(T,
bool Init = true,
bool Throws = true)
(shared Allocator allocator,
ref T[] array,
in size_t length) @trusted
package(tanya) T[] resize(T)(shared Allocator allocator,
auto ref T[] array,
const size_t length) @trusted
{
void[] buf = array;
static if (Init)
if (length == 0)
{
immutable oldLength = array.length;
if (allocator.deallocate(array))
{
return null;
}
else
{
onOutOfMemoryErrorNoGC();
}
}
void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof))
{
static if (Throws)
{
onOutOfMemoryError;
}
return false;
onOutOfMemoryErrorNoGC();
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
static if (Init)
{
if (oldLength < length)
{
array[oldLength .. $] = T.init;
return array;
}
}
return true;
}
package(tanya) alias resizeArray = resize;
///
unittest
private unittest
{
int[] p;
defaultAllocator.resizeArray(p, 20);
p = defaultAllocator.resize(p, 20);
assert(p.length == 20);
defaultAllocator.resizeArray(p, 30);
p = defaultAllocator.resize(p, 30);
assert(p.length == 30);
defaultAllocator.resizeArray(p, 10);
p = defaultAllocator.resize(p, 10);
assert(p.length == 10);
defaultAllocator.resizeArray(p, 0);
p = defaultAllocator.resize(p, 0);
assert(p is null);
}
/**
* 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.
/*
* Destroys the object.
* Returns the memory should be freed.
*/
void dispose(T)(shared Allocator allocator, auto ref T* p)
package(tanya) void[] finalize(T)(ref T* p)
{
static if (hasElaborateDestructor!T)
{
destroy(*p);
}
() @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }();
p = null;
return (cast(void*) p)[0 .. T.sizeof];
}
/// Ditto.
void dispose(T)(shared Allocator allocator, auto ref T p)
package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface))
{
if (p is null)
{
return;
return null;
}
static if (is(T == interface))
{
@ -182,18 +238,12 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
alias ob = p;
}
auto ptr = cast(void*) ob;
auto support = ptr[0 .. typeid(ob).initializer.length];
scope (success)
{
() @trusted { allocator.deallocate(support); }();
p = null;
}
auto ppv = cast(void**) ptr;
if (!*ppv)
{
return;
return null;
}
auto pc = cast(ClassInfo*) *ppv;
scope (exit)
@ -217,18 +267,43 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
{
_d_monitordelete(cast(Object) ptr, true);
}
return support;
}
/// Ditto.
void dispose(T)(shared Allocator allocator, auto ref T[] array)
package(tanya) void[] finalize(T)(ref T[] p)
{
static if (hasElaborateDestructor!(typeof(array[0])))
static if (hasElaborateDestructor!(typeof(p[0])))
{
foreach (ref e; array)
p.each!((ref e) => destroy(e));
}
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;
}
private unittest
{
struct S
{
~this()
{
destroy(e);
}
}
() @trusted { allocator.deallocate(array); }();
array = null;
auto p = cast(S[]) defaultAllocator.allocate(S.sizeof);
defaultAllocator.dispose(p);
}

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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:belka@caraus.de, Eugene Wissner)
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
module tanya.memory.types;
@ -14,34 +14,28 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range;
import std.traits;
import tanya.memory;
/**
* 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)
private template Payload(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
{
private alias Payload = T*;
alias Payload = T*;
}
}
private class Storage
final class RefCountedStore(T)
{
private Payload payload;
private size_t counter = 1;
T payload;
size_t counter = 1;
private final size_t opUnary(string op)() pure nothrow @safe @nogc
size_t opUnary(string op)()
if (op == "--" || op == "++")
in
{
@ -52,7 +46,7 @@ struct RefCounted(T)
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)
{
@ -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;
}
}
private final class RefCountedStorage : Storage
private void separateDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
private shared Allocator allocator;
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
}
body
{
this.allocator = allocator;
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
~this() nothrow @nogc
private void unifiedDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
allocator.dispose(payload);
}
auto ptr1 = finalize(storage);
auto ptr2 = finalize(storage.payload);
allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
}
/**
* 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)
{
private alias Storage = RefCountedStore!(Payload!T);
private Storage storage;
private void function(Storage storage,
shared Allocator allocator) @nogc deleter;
invariant
{
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)
*/
this(Payload value, shared Allocator allocator = defaultAllocator)
this()(auto ref Payload!T value,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
storage = allocator.make!RefCountedStorage(allocator);
move(value, storage.payload);
this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!(Payload!T);
move(value, this.storage.payload);
static if (__traits(isRef, value))
{
value = null;
}
}
/// Ditto.
@ -137,7 +148,7 @@ struct RefCounted(T)
{
if (count != 0)
{
++storage;
++this.storage;
}
}
@ -148,9 +159,9 @@ struct RefCounted(T)
*/
~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,
* 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
* be used. If you need a different allocator, create a new
* $(D_PSYMBOL RefCounted) and assign it.
*
* 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;
storage = allocator.make!RefCountedStorage(allocator);
}
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);
--this.storage;
this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!(Payload!T);
}
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;
}
/// Ditto.
ref typeof(this) opAssign(typeof(null))
{
if (storage is null)
if (this.storage is null)
{
return this;
}
else if (storage > 1)
else if (this.storage > 1)
{
--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;
--this.storage;
}
else
{
allocator.dispose(storage.payload);
deleter(this.storage, allocator);
}
this.storage = null;
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(allocator_, rhs.allocator_);
swap(storage, rhs.storage);
swap(this.allocator_, rhs.allocator_);
swap(this.storage, rhs.storage);
swap(this.deleter, rhs.deleter);
return this;
}
/**
* 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
{
assert(count > 0, "Attempted to access an uninitialized reference.");
@ -243,7 +249,7 @@ struct RefCounted(T)
return storage.payload;
}
static if (isPointer!Payload)
version (D_Ddoc)
{
/**
* Params:
@ -254,6 +260,11 @@ struct RefCounted(T)
*
* 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 == "*")
{
@ -355,6 +366,44 @@ private unittest
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
{
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
@ -380,15 +429,22 @@ private unittest
* args = Constructor arguments of $(D_PARAM 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)
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);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize);
const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
const size = alignedSize(stateSize!T + storageSize);
auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null)
@ -399,7 +455,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
{
() @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))
{
@ -410,9 +466,38 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args);
}
rc.deleter = &unifiedDeleter!(Payload!T);
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
{
@ -456,3 +541,272 @@ private @nogc unittest
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);
}

View File

@ -1,49 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Templates that generate values.
*
* Copyright: Eugene Wissner 2016.
* 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.meta.gen;
import std.traits;
/**
* Initializer list.
*
* Generates a static array with elements from $(D_PARAM args). All elements
* should have the same type. It can be used in constructors which accept a
* list of the elements of the same type in the situations where variadic
* functions and templates can't be used.
*
* Params:
* Args = Argument type.
* args = Arguments.
*/
enum IL(Args...)(Args args)
if (Args.length > 0)
{
alias BaseType = typeof(args[0]);
BaseType[args.length] result;
foreach (i, a; args)
{
static assert(isImplicitlyConvertible!(typeof(a), BaseType));
result[i] = a;
}
return result;
}
///
unittest
{
static assert(IL(1, 5, 8).length == 3);
static assert(IL(1, 5, 8).sizeof == 3 * int.sizeof);
}

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

@ -3,13 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Metaprogramming.
* Network programming.
*
* Copyright: Eugene Wissner 2016.
* 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.meta;
module tanya.network;
public import tanya.meta.gen;
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/. */
/**
* Copyright: Eugene Wissner 2016.
* Socket 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)
@ -255,7 +257,7 @@ else version (Windows)
overlapped.handle = cast(HANDLE) handle_;
overlapped.event = OverlappedSocketEvent.read;
overlapped.buffer.len = buffer.length;
overlapped.buffer.len = cast(ULONG) buffer.length;
overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSARecv(handle_,
@ -326,7 +328,7 @@ else version (Windows)
{
overlapped.handle = cast(HANDLE) handle_;
overlapped.event = OverlappedSocketEvent.write;
overlapped.buffer.len = buffer.length;
overlapped.buffer.len = cast(ULONG) buffer.length;
overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSASend(handle_,
@ -1004,10 +1006,14 @@ interface ConnectionOrientedSocket
*/
enum Flag : int
{
none = 0, /// No flags specified
outOfBand = MSG_OOB, /// Out-of-band stream data
peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving
dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending
/// No flags specified.
none = 0,
/// Out-of-band stream data.
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;

View File

@ -3,10 +3,12 @@
* 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/,
* 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;