32 Commits

Author SHA1 Message Date
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
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
8156d0fe3a Add support for dmd 2.074.0, remove 2.070.2 2017-04-13 16:02:18 +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
3c23aca6a6 Improve Vector module and reserve documentation 2017-02-20 12:03:49 +01:00
19 changed files with 7845 additions and 6298 deletions

View File

@ -7,10 +7,10 @@ os:
language: d language: d
d: d:
- dmd-2.073.0 - dmd-2.074.0
- dmd-2.073.2
- dmd-2.072.2 - dmd-2.072.2
- dmd-2.071.2 - dmd-2.071.2
- dmd-2.070.2
env: env:
matrix: matrix:

View File

@ -29,20 +29,25 @@ helper functions).
### Supported compilers ### Supported compilers
* dmd 2.073.0 | dmd |
* dmd 2.072.2 |:-------:|
* dmd 2.071.2 | 2.074.0 |
* dmd 2.070.2 | 2.073.2 |
| 2.072.2 |
| 2.071.2 |
### Current status ### Current status
The library is currently under development, but the API is becoming gradually The library is currently under development, but the API is becoming gradually
stable. stable.
`container`s are being extended to support ranges. Also following modules are Following modules are coming soon:
coming soon:
* UTF-8 string. | Feature | Build status |
* Hash table. |--------------|:-----------------------------------------------------------------------------------------------------------------------:|
| UTF-8 string | [![utf8string](https://travis-ci.org/caraus-ecms/tanya.svg?branch=utf8string)](https://travis-ci.org/caraus-ecms/tanya) |
| BitVector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) |
| Hash table | N/A |
`math` package contains an arbitrary precision integer implementation that `math` package contains an arbitrary precision integer implementation that
needs more test cases, better performance and some additional features needs more test cases, better performance and some additional features

File diff suppressed because it is too large Load Diff

View File

@ -9,14 +9,14 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.container.entry; module tanya.container.entry;
package struct SEntry(T) package struct SEntry(T)
{ {
/// Item content. /// Item content.
T content; T content;
/// Next item. /// Next item.
SEntry* next; SEntry* next;
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Abstract data types whose instances are collections of other objects.
*
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.container; module tanya.container;
public import tanya.container.buffer; public import tanya.container.buffer;

View File

@ -3,11 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* FIFO queue.
*
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.container.queue; module tanya.container.queue;
import core.exception; import core.exception;
@ -20,267 +22,267 @@ import tanya.memory;
* FIFO queue. * FIFO queue.
* *
* Params: * Params:
* T = Content type. * T = Content type.
*/ */
struct Queue(T) struct Queue(T)
{ {
/** /**
* Removes all elements from the queue. * Removes all elements from the queue.
*/ */
~this() ~this()
{ {
while (!empty) while (!empty)
{ {
dequeue(); dequeue();
} }
} }
/** /**
* Returns how many elements are in the queue. It iterates through the queue * Returns how many elements are in the queue. It iterates through the queue
* to count the elements. * to count the elements.
* *
* Returns: How many elements are in the queue. * Returns: How many elements are in the queue.
*/ */
size_t length() const size_t length() const
{ {
size_t len; size_t len;
for (const(SEntry!T)* i = first; i !is null; i = i.next) for (const(SEntry!T)* i = first; i !is null; i = i.next)
{ {
++len; ++len;
} }
return len; return len;
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
assert(q.length == 0); assert(q.length == 0);
q.enqueue(5); q.enqueue(5);
assert(q.length == 1); assert(q.length == 1);
q.enqueue(4); q.enqueue(4);
assert(q.length == 2); assert(q.length == 2);
q.enqueue(9); q.enqueue(9);
assert(q.length == 3); assert(q.length == 3);
q.dequeue(); q.dequeue();
assert(q.length == 2); assert(q.length == 2);
q.dequeue(); q.dequeue();
assert(q.length == 1); assert(q.length == 1);
q.dequeue(); q.dequeue();
assert(q.length == 0); assert(q.length == 0);
} }
private void enqueueEntry(ref SEntry!T* entry) private void enqueueEntry(ref SEntry!T* entry)
{ {
if (empty) if (empty)
{ {
first = rear = entry; first = rear = entry;
} }
else else
{ {
rear.next = entry; rear.next = entry;
rear = rear.next; rear = rear.next;
} }
} }
private SEntry!T* allocateEntry() private SEntry!T* allocateEntry()
{ {
auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof); auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof);
if (temp is null) if (temp is null)
{ {
onOutOfMemoryError(); onOutOfMemoryError();
} }
return temp; return temp;
} }
/** /**
* Inserts a new element. * Inserts a new element.
* *
* Params: * Params:
* x = New element. * x = New element.
*/ */
void enqueue(ref T x) void enqueue(ref T x)
{ {
auto temp = allocateEntry(); auto temp = allocateEntry();
*temp = SEntry!T.init; *temp = SEntry!T.init;
temp.content = x; temp.content = x;
enqueueEntry(temp); enqueueEntry(temp);
} }
/// Ditto. /// Ditto.
void enqueue(T x) void enqueue(T x)
{ {
auto temp = allocateEntry(); auto temp = allocateEntry();
moveEmplace(x, (*temp).content); moveEmplace(x, (*temp).content);
(*temp).next = null; (*temp).next = null;
enqueueEntry(temp); enqueueEntry(temp);
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
assert(q.empty); assert(q.empty);
q.enqueue(8); q.enqueue(8);
q.enqueue(9); q.enqueue(9);
assert(q.dequeue() == 8); assert(q.dequeue() == 8);
assert(q.dequeue() == 9); assert(q.dequeue() == 9);
} }
/** /**
* Returns: $(D_KEYWORD true) if the queue is empty. * Returns: $(D_KEYWORD true) if the queue is empty.
*/ */
@property bool empty() const @property bool empty() const
{ {
return first is null; return first is null;
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
int value = 7; int value = 7;
assert(q.empty); assert(q.empty);
q.enqueue(value); q.enqueue(value);
assert(!q.empty); assert(!q.empty);
} }
/** /**
* Move the position to the next element. * Move the position to the next element.
* *
* Returns: Dequeued element. * Returns: Dequeued element.
*/ */
T dequeue() T dequeue()
in in
{ {
assert(!empty); assert(!empty);
} }
body body
{ {
auto n = first.next; auto n = first.next;
T ret = move(first.content); T ret = move(first.content);
allocator.dispose(first); allocator.dispose(first);
first = n; first = n;
return ret; return ret;
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
q.enqueue(8); q.enqueue(8);
q.enqueue(9); q.enqueue(9);
assert(q.dequeue() == 8); assert(q.dequeue() == 8);
assert(q.dequeue() == 9); assert(q.dequeue() == 9);
} }
/** /**
* $(D_KEYWORD foreach) iteration. The elements will be automatically * $(D_KEYWORD foreach) iteration. The elements will be automatically
* dequeued. * dequeued.
* *
* Params: * Params:
* dg = $(D_KEYWORD foreach) body. * dg = $(D_KEYWORD foreach) body.
* *
* Returns: The value returned from $(D_PARAM dg). * Returns: The value returned from $(D_PARAM dg).
*/ */
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg) int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{ {
int result; int result;
for (size_t i = 0; !empty; ++i) for (size_t i = 0; !empty; ++i)
{ {
auto e = dequeue(); auto e = dequeue();
if ((result = dg(i, e)) != 0) if ((result = dg(i, e)) != 0)
{ {
return result; return result;
} }
} }
return result; return result;
} }
/// Ditto. /// Ditto.
int opApply(scope int delegate(ref T) @nogc dg) int opApply(scope int delegate(ref T) @nogc dg)
{ {
int result; int result;
while (!empty) while (!empty)
{ {
auto e = dequeue(); auto e = dequeue();
if ((result = dg(e)) != 0) if ((result = dg(e)) != 0)
{ {
return result; return result;
} }
} }
return result; return result;
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
size_t j; size_t j;
q.enqueue(5); q.enqueue(5);
q.enqueue(4); q.enqueue(4);
q.enqueue(9); q.enqueue(9);
foreach (i, e; q) foreach (i, e; q)
{ {
assert(i != 2 || e == 9); assert(i != 2 || e == 9);
assert(i != 1 || e == 4); assert(i != 1 || e == 4);
assert(i != 0 || e == 5); assert(i != 0 || e == 5);
++j; ++j;
} }
assert(j == 3); assert(j == 3);
assert(q.empty); assert(q.empty);
j = 0; j = 0;
q.enqueue(5); q.enqueue(5);
q.enqueue(4); q.enqueue(4);
q.enqueue(9); q.enqueue(9);
foreach (e; q) foreach (e; q)
{ {
assert(j != 2 || e == 9); assert(j != 2 || e == 9);
assert(j != 1 || e == 4); assert(j != 1 || e == 4);
assert(j != 0 || e == 5); assert(j != 0 || e == 5);
++j; ++j;
} }
assert(j == 3); assert(j == 3);
assert(q.empty); assert(q.empty);
} }
private SEntry!T* first; private SEntry!T* first;
private SEntry!T* rear; private SEntry!T* rear;
mixin DefaultAllocator; mixin DefaultAllocator;
} }
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
q.enqueue(5); q.enqueue(5);
assert(!q.empty); assert(!q.empty);
q.enqueue(4); q.enqueue(4);
q.enqueue(9); q.enqueue(9);
assert(q.dequeue() == 5); assert(q.dequeue() == 5);
foreach (i, ref e; q) foreach (i, ref e; q)
{ {
assert(i != 0 || e == 4); assert(i != 0 || e == 4);
assert(i != 1 || e == 9); assert(i != 1 || e == 9);
} }
assert(q.empty); assert(q.empty);
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Copyright: Eugene Wissner 2016. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.math; module tanya.math;
import std.traits; import std.traits;
@ -16,7 +16,7 @@ public import tanya.math.random;
version (unittest) version (unittest)
{ {
import std.algorithm.iteration; import std.algorithm.iteration;
} }
/** /**
@ -26,12 +26,12 @@ version (unittest)
* is used to allocate the result. * is used to allocate the result.
* *
* Params: * Params:
* I = Base type. * I = Base type.
* G = Exponent type. * G = Exponent type.
* H = Divisor type: * H = Divisor type:
* x = Base. * x = Base.
* y = Exponent. * y = Exponent.
* z = Divisor. * z = Divisor.
* *
* Returns: Reminder of the division of $(D_PARAM x) to the power $(D_PARAM y) * Returns: Reminder of the division of $(D_PARAM x) to the power $(D_PARAM y)
* by $(D_PARAM z). * by $(D_PARAM z).
@ -39,134 +39,163 @@ version (unittest)
* Precondition: $(D_INLINECODE z > 0) * Precondition: $(D_INLINECODE z > 0)
*/ */
H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z) H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z)
if (isIntegral!I && isIntegral!G && isIntegral!H) if (isIntegral!I && isIntegral!G && isIntegral!H)
in in
{ {
assert(z > 0, "Division by zero."); assert(z > 0, "Division by zero.");
} }
body body
{ {
G mask = G.max / 2 + 1; G mask = G.max / 2 + 1;
H result; H result;
if (y == 0) if (y == 0)
{ {
return 1 % z; return 1 % z;
} }
else if (y == 1) else if (y == 1)
{ {
return x % z; return x % z;
} }
do do
{ {
immutable bit = y & mask; immutable bit = y & mask;
if (!result && bit) if (!result && bit)
{ {
result = x; result = x;
continue; continue;
} }
result *= result; result *= result;
if (bit) if (bit)
{ {
result *= x; result *= x;
} }
result %= z; result %= z;
} }
while (mask >>= 1); while (mask >>= 1);
return result; return result;
} }
/// Ditto. /// 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)) if (is(I == Integer))
in in
{ {
assert(z.length > 0, "Division by zero."); assert(z.length > 0, "Division by zero.");
} }
body body
{ {
size_t i = y.length; size_t i;
auto tmp2 = Integer(x.allocator), tmp1 = Integer(x, x.allocator); auto tmp1 = Integer(x, x.allocator);
Integer result = Integer(x.allocator); auto result = Integer(x.allocator);
bool firstBit;
if (x.length == 0 && i != 0) if (x.size == 0 && y.size != 0)
{ {
i = 0; i = y.size;
} }
else else
{ {
result = 1; result = 1;
} }
while (i) while (i < y.size)
{ {
--i; for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
for (ubyte mask = 0x01; mask; mask <<= 1) {
{ if (y.rep[i] & mask)
if (y.rep[i] & mask) {
{ result *= tmp1;
result *= tmp1; result %= z;
result %= z; }
} auto tmp2 = tmp1;
tmp2 = tmp1; tmp1 *= tmp2;
tmp1 *= tmp2; tmp1 %= z;
tmp1 %= z; }
} ++i;
} }
return result; return result;
} }
/// ///
pure nothrow @safe @nogc unittest pure nothrow @safe @nogc unittest
{ {
assert(pow(3, 5, 7) == 5); assert(pow(3, 5, 7) == 5);
assert(pow(2, 2, 1) == 0); assert(pow(2, 2, 1) == 0);
assert(pow(3, 3, 3) == 0); assert(pow(3, 3, 3) == 0);
assert(pow(7, 4, 2) == 1); assert(pow(7, 4, 2) == 1);
assert(pow(53, 0, 2) == 1); assert(pow(53, 0, 2) == 1);
assert(pow(53, 1, 3) == 2); assert(pow(53, 1, 3) == 2);
assert(pow(53, 2, 5) == 4); assert(pow(53, 2, 5) == 4);
assert(pow(0, 0, 5) == 1); assert(pow(0, 0, 5) == 1);
assert(pow(0, 5, 5) == 0); assert(pow(0, 5, 5) == 0);
} }
/// ///
unittest unittest
{ {
assert(cast(long) pow(Integer(3), Integer(5), Integer(7)) == 5); assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(cast(long) pow(Integer(2), Integer(2), Integer(1)) == 0); assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
assert(cast(long) pow(Integer(3), Integer(3), Integer(3)) == 0); assert(pow(Integer(3), Integer(3), Integer(3)) == 0);
assert(cast(long) pow(Integer(7), Integer(4), Integer(2)) == 1); assert(pow(Integer(7), Integer(4), Integer(2)) == 1);
assert(cast(long) pow(Integer(53), Integer(0), Integer(2)) == 1); assert(pow(Integer(53), Integer(0), Integer(2)) == 1);
assert(cast(long) pow(Integer(53), Integer(1), Integer(3)) == 2); assert(pow(Integer(53), Integer(1), Integer(3)) == 2);
assert(cast(long) pow(Integer(53), Integer(2), Integer(5)) == 4); assert(pow(Integer(53), Integer(2), Integer(5)) == 4);
assert(cast(long) pow(Integer(0), Integer(0), Integer(5)) == 1); assert(pow(Integer(0), Integer(0), Integer(5)) == 1);
assert(cast(long) pow(Integer(0), Integer(5), Integer(5)) == 0); assert(pow(Integer(0), Integer(5), Integer(5)) == 0);
} }
/** /**
* Checks if $(D_PARAM x) is a prime. * Checks if $(D_PARAM x) is a prime.
* *
* Params: * Params:
* x = The number should be checked. * x = The number should be checked.
* *
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number, * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
bool isPseudoprime(ulong x) nothrow pure @safe @nogc bool isPseudoprime(ulong x) nothrow pure @safe @nogc
{ {
return pow(2, x - 1, x) == 1; return pow(2, x - 1, x) == 1;
} }
/// ///
unittest unittest
{ {
uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719,
74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, 74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821,
74827, 9973, 104729, 15485867, 49979693, 104395303, 74827, 9973, 104729, 15485867, 49979693, 104395303,
593441861, 104729, 15485867, 49979693, 104395303, 593441861, 104729, 15485867, 49979693, 104395303,
593441861, 899809363, 982451653]; 593441861, 899809363, 982451653];
known.each!((ref x) => assert(isPseudoprime(x))); 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,8 +8,8 @@
* Copyright: Eugene Wissner 2016. * Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.math.random; module tanya.math.random;
import std.digest.sha; import std.digest.sha;
@ -27,20 +27,20 @@ enum maxGather = 128;
*/ */
class EntropyException : Exception class EntropyException : Exception
{ {
/** /**
* Params: * Params:
* msg = Message to output. * msg = Message to output.
* file = The file where the exception occurred. * file = The file where the exception occurred.
* line = The line number where the exception occurred. * line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any. * next = The previous exception in the chain of exceptions, if any.
*/ */
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) pure @safe nothrow const @nogc Throwable next = null) pure @safe nothrow const @nogc
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }
} }
/** /**
@ -48,103 +48,103 @@ class EntropyException : Exception
*/ */
abstract class EntropySource abstract class EntropySource
{ {
/// Amount of already generated entropy. /// Amount of already generated entropy.
protected ushort size_; protected ushort size_;
/** /**
* Returns: Minimum bytes required from the entropy source. * Returns: Minimum bytes required from the entropy source.
*/ */
@property immutable(ubyte) threshold() const @safe pure nothrow; @property immutable(ubyte) threshold() const @safe pure nothrow;
/** /**
* Returns: Whether this entropy source is strong. * Returns: Whether this entropy source is strong.
*/ */
@property immutable(bool) strong() const @safe pure nothrow; @property immutable(bool) strong() const @safe pure nothrow;
/** /**
* Returns: Amount of already generated entropy. * Returns: Amount of already generated entropy.
*/ */
@property ushort size() const @safe pure nothrow @property ushort size() const @safe pure nothrow
{ {
return size_; return size_;
} }
/** /**
* Params: * Params:
* size = Amount of already generated entropy. Cannot be smaller than the * size = Amount of already generated entropy. Cannot be smaller than the
* already set value. * already set value.
*/ */
@property void size(ushort size) @safe pure nothrow @property void size(ushort size) @safe pure nothrow
{ {
size_ = size; size_ = size;
} }
/** /**
* Poll the entropy source. * Poll the entropy source.
* *
* Params: * Params:
* output = Buffer to save the generate random sequence (the method will * output = Buffer to save the generate random sequence (the method will
* to fill the buffer). * to fill the buffer).
* *
* Returns: Number of bytes that were copied to the $(D_PARAM output) * Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error. * or $(D_PSYMBOL Nullable!ubyte.init) on error.
*/ */
Nullable!ubyte poll(out ubyte[maxGather] output); Nullable!ubyte poll(out ubyte[maxGather] output);
} }
version (linux) version (linux)
{ {
extern (C) long syscall(long number, ...) nothrow; extern (C) long syscall(long number, ...) nothrow;
/** /**
* Uses getrandom system call. * Uses getrandom system call.
*/ */
class PlatformEntropySource : EntropySource class PlatformEntropySource : EntropySource
{ {
/** /**
* Returns: Minimum bytes required from the entropy source. * Returns: Minimum bytes required from the entropy source.
*/ */
override @property immutable(ubyte) threshold() const @safe pure nothrow override @property immutable(ubyte) threshold() const @safe pure nothrow
{ {
return 32; return 32;
} }
/** /**
* Returns: Whether this entropy source is strong. * Returns: Whether this entropy source is strong.
*/ */
override @property immutable(bool) strong() const @safe pure nothrow override @property immutable(bool) strong() const @safe pure nothrow
{ {
return true; return true;
} }
/** /**
* Poll the entropy source. * Poll the entropy source.
* *
* Params: * Params:
* output = Buffer to save the generate random sequence (the method will * output = Buffer to save the generate random sequence (the method will
* to fill the buffer). * to fill the buffer).
* *
* Returns: Number of bytes that were copied to the $(D_PARAM output) * Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error. * or $(D_PSYMBOL Nullable!ubyte.init) on error.
*/ */
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow
out (length) out (length)
{ {
assert(length <= maxGather); assert(length <= maxGather);
} }
body body
{ {
// int getrandom(void *buf, size_t buflen, unsigned int flags); // int getrandom(void *buf, size_t buflen, unsigned int flags);
auto length = syscall(318, output.ptr, output.length, 0); auto length = syscall(318, output.ptr, output.length, 0);
Nullable!ubyte ret; Nullable!ubyte ret;
if (length >= 0) if (length >= 0)
{ {
ret = cast(ubyte) length; ret = cast(ubyte) length;
} }
return ret; return ret;
} }
} }
} }
/** /**
@ -156,165 +156,165 @@ version (linux)
* *
* output = entropy.random; * output = entropy.random;
* *
* defaultAllocator.finalize(entropy); * defaultAllocator.dispose(entropy);
* --- * ---
*/ */
class Entropy class Entropy
{ {
/// Entropy sources. /// Entropy sources.
protected EntropySource[] sources; protected EntropySource[] sources;
private ubyte sourceCount_; private ubyte sourceCount_;
private shared Allocator allocator; private shared Allocator allocator;
/// Entropy accumulator. /// Entropy accumulator.
protected SHA!(maxGather * 8, 512) accumulator; protected SHA!(maxGather * 8, 512) accumulator;
/** /**
* Params: * Params:
* maxSources = Maximum amount of entropy sources can be set. * maxSources = Maximum amount of entropy sources can be set.
* allocator = Allocator to allocate entropy sources available on the * allocator = Allocator to allocate entropy sources available on the
* system. * system.
*/ */
this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator) this(size_t maxSources = 20, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(maxSources > 0 && maxSources <= ubyte.max); assert(maxSources > 0 && maxSources <= ubyte.max);
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
allocator.resizeArray(sources, maxSources); allocator.resize(sources, maxSources);
version (linux) version (linux)
{ {
this ~= allocator.make!PlatformEntropySource; this ~= allocator.make!PlatformEntropySource;
} }
} }
/** /**
* Returns: Amount of the registered entropy sources. * Returns: Amount of the registered entropy sources.
*/ */
@property ubyte sourceCount() const @safe pure nothrow @property ubyte sourceCount() const @safe pure nothrow
{ {
return sourceCount_; return sourceCount_;
} }
/** /**
* Add an entropy source. * Add an entropy source.
* *
* Params: * Params:
* source = Entropy source. * source = Entropy source.
* *
* Returns: $(D_PSYMBOL this). * Returns: $(D_PSYMBOL this).
* *
* See_Also: * See_Also:
* $(D_PSYMBOL EntropySource) * $(D_PSYMBOL EntropySource)
*/ */
Entropy opOpAssign(string Op)(EntropySource source) @safe pure nothrow Entropy opOpAssign(string Op)(EntropySource source) @safe pure nothrow
if (Op == "~") if (Op == "~")
in in
{ {
assert(sourceCount_ <= sources.length); assert(sourceCount_ <= sources.length);
} }
body body
{ {
sources[sourceCount_++] = source; sources[sourceCount_++] = source;
return this; return this;
} }
/** /**
* Returns: Generated random sequence. * Returns: Generated random sequence.
* *
* Throws: $(D_PSYMBOL EntropyException) if no strong entropy source was * Throws: $(D_PSYMBOL EntropyException) if no strong entropy source was
* registered or it failed. * registered or it failed.
*/ */
@property ubyte[blockSize] random() @property ubyte[blockSize] random()
in in
{ {
assert(sourceCount_ > 0, "No entropy sources defined."); assert(sourceCount_ > 0, "No entropy sources defined.");
} }
body body
{ {
bool haveStrong; bool haveStrong;
ushort done; ushort done;
ubyte[blockSize] output; ubyte[blockSize] output;
do do
{ {
ubyte[maxGather] buffer; ubyte[maxGather] buffer;
// Run through our entropy sources // Run through our entropy sources
for (ubyte i; i < sourceCount; ++i) for (ubyte i; i < sourceCount; ++i)
{ {
auto outputLength = sources[i].poll(buffer); auto outputLength = sources[i].poll(buffer);
if (!outputLength.isNull) if (!outputLength.isNull)
{ {
if (outputLength > 0) if (outputLength > 0)
{ {
update(i, buffer, outputLength); update(i, buffer, outputLength);
sources[i].size = cast(ushort) (sources[i].size + outputLength); sources[i].size = cast(ushort) (sources[i].size + outputLength);
} }
if (sources[i].size < sources[i].threshold) if (sources[i].size < sources[i].threshold)
{ {
continue; continue;
} }
else if (sources[i].strong) else if (sources[i].strong)
{ {
haveStrong = true; haveStrong = true;
} }
} }
done = 257; done = 257;
} }
} }
while (++done < 256); while (++done < 256);
if (!haveStrong) if (!haveStrong)
{ {
throw allocator.make!EntropyException("No strong entropy source defined."); throw allocator.make!EntropyException("No strong entropy source defined.");
} }
output = accumulator.finish(); output = accumulator.finish();
// Reset accumulator and counters and recycle existing entropy // Reset accumulator and counters and recycle existing entropy
accumulator.start(); accumulator.start();
// Perform second SHA-512 on entropy // Perform second SHA-512 on entropy
output = sha512Of(output); output = sha512Of(output);
for (ubyte i = 0; i < sourceCount; ++i) for (ubyte i = 0; i < sourceCount; ++i)
{ {
sources[i].size = 0; sources[i].size = 0;
} }
return output; return output;
} }
/** /**
* Update entropy accumulator. * Update entropy accumulator.
* *
* Params: * Params:
* sourceId = Entropy source index in $(D_PSYMBOL sources). * sourceId = Entropy source index in $(D_PSYMBOL sources).
* data = Data got from the entropy source. * data = Data got from the entropy source.
* length = Length of the received data. * length = Length of the received data.
*/ */
protected void update(in ubyte sourceId, protected void update(in ubyte sourceId,
ref ubyte[maxGather] data, ref ubyte[maxGather] data,
ubyte length) @safe pure nothrow ubyte length) @safe pure nothrow
{ {
ubyte[2] header; ubyte[2] header;
if (length > blockSize) if (length > blockSize)
{ {
data[0..64] = sha512Of(data); data[0..64] = sha512Of(data);
length = blockSize; length = blockSize;
} }
header[0] = sourceId; header[0] = sourceId;
header[1] = length; header[1] = length;
accumulator.put(header); accumulator.put(header);
accumulator.put(data[0..length]); accumulator.put(data[0..length]);
} }
} }

View File

@ -15,53 +15,53 @@ module tanya.memory.allocator;
*/ */
interface Allocator interface Allocator
{ {
/** /**
* Returns: Alignment offered. * Returns: Alignment offered.
*/ */
@property uint alignment() const shared pure nothrow @safe @nogc; @property uint alignment() const shared pure nothrow @safe @nogc;
/** /**
* Allocates $(D_PARAM size) bytes of memory. * Allocates $(D_PARAM size) bytes of memory.
* *
* Params: * Params:
* size = Amount of memory to allocate. * size = Amount of memory to allocate.
* *
* Returns: Pointer to the new allocated memory. * Returns: Pointer to the new allocated memory.
*/ */
void[] allocate(in size_t size) shared nothrow @nogc; void[] allocate(in size_t size) shared nothrow @nogc;
/** /**
* Deallocates a memory block. * Deallocates a memory block.
* *
* Params: * Params:
* p = A pointer to the memory block to be freed. * p = A pointer to the memory block to be freed.
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) shared nothrow @nogc; bool deallocate(void[] p) shared nothrow @nogc;
/** /**
* Increases or decreases the size of a memory block. * Increases or decreases the size of a memory block.
* *
* Params: * Params:
* p = A pointer to the memory block. * p = A pointer to the memory block.
* size = Size of the reallocated block. * size = Size of the reallocated block.
* *
* Returns: Pointer to the allocated memory. * Returns: Pointer to the allocated memory.
*/ */
bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc; bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc;
/** /**
* Reallocates a memory block in place if possible or returns * Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or * $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
* $(D_PARAM size) is `0`, it should return $(D_KEYWORD false). * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
* *
* Params: * Params:
* p = A pointer to the memory block. * p = A pointer to the memory block.
* size = Size of the reallocated block. * size = Size of the reallocated block.
* *
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/ */
bool reallocateInPlace(ref void[] p, in size_t size) shared nothrow @nogc; 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

@ -11,7 +11,8 @@
module tanya.memory; module tanya.memory;
import core.exception; import core.exception;
public import std.experimental.allocator : make, makeArray; import std.algorithm.iteration;
public import std.experimental.allocator : make;
import std.traits; import std.traits;
public import tanya.memory.allocator; public import tanya.memory.allocator;
@ -23,59 +24,61 @@ public import tanya.memory.allocator;
*/ */
mixin template DefaultAllocator() mixin template DefaultAllocator()
{ {
/// Allocator. /// Allocator.
protected shared Allocator allocator_; protected shared Allocator allocator_;
/** /**
* Params: * Params:
* allocator = The allocator should be used. * allocator = The allocator should be used.
*/ *
this(shared Allocator allocator) * Precondition: $(D_INLINECODE allocator_ !is null)
in */
{ this(shared Allocator allocator)
assert(allocator !is null); in
} {
body assert(allocator !is null);
{ }
this.allocator_ = allocator; body
} {
this.allocator_ = allocator;
}
/** /**
* This property checks if the allocator was set in the constructor * This property checks if the allocator was set in the constructor
* and sets it to the default one, if not. * and sets it to the default one, if not.
* *
* Returns: Used allocator. * Returns: Used allocator.
* *
* Postcondition: $(D_INLINECODE allocator_ !is null) * Postcondition: $(D_INLINECODE allocator !is null)
*/ */
protected @property shared(Allocator) allocator() nothrow @safe @nogc protected @property shared(Allocator) allocator() nothrow @safe @nogc
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
if (allocator_ is null) if (allocator_ is null)
{ {
allocator_ = defaultAllocator; allocator_ = defaultAllocator;
} }
return allocator_; return allocator_;
} }
/// Ditto. /// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc @property shared(Allocator) allocator() const nothrow @trusted @nogc
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
if (allocator_ is null) if (allocator_ is null)
{ {
return defaultAllocator; return defaultAllocator;
} }
return cast(shared Allocator) allocator_; return cast(shared Allocator) allocator_;
} }
} }
// From druntime // From druntime
@ -85,28 +88,28 @@ shared Allocator allocator;
shared static this() nothrow @trusted @nogc shared static this() nothrow @trusted @nogc
{ {
import tanya.memory.mmappool; import tanya.memory.mmappool;
allocator = MmapPool.instance; allocator = MmapPool.instance;
} }
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
return allocator; return allocator;
} }
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc @property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
.allocator = allocator; .allocator = allocator;
} }
/** /**
@ -114,101 +117,166 @@ body
* object of type $(D_PARAM T). * object of type $(D_PARAM T).
* *
* Params: * Params:
* T = Object type. * T = Object type.
*/ */
template stateSize(T) template stateSize(T)
{ {
static if (is(T == class) || is(T == interface)) static if (is(T == class) || is(T == interface))
{ {
enum stateSize = __traits(classInstanceSize, T); enum stateSize = __traits(classInstanceSize, T);
} }
else else
{ {
enum stateSize = T.sizeof; enum stateSize = T.sizeof;
} }
} }
/** /**
* Params: * Params:
* size = Raw size. * size = Raw size.
* alignment = Alignment. * alignment = Alignment.
* *
* Returns: Aligned size. * 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; return (size - 1) / alignment * alignment + alignment;
} }
/** /**
* Internal function used to create, resize or destroy a dynamic array. It * Internal function used to create, resize or destroy a dynamic array. It
* throws $(D_PSYMBOL OutOfMemoryError) if $(D_PARAM Throws) is set. The new * may throw $(D_PSYMBOL OutOfMemoryError). The new
* allocated part of the array is initialized only if $(D_PARAM Init) * allocated part of the array isn't initialized. This function can be trusted
* is set. This function can be trusted only in the data structures that * only in the data structures that can ensure that the array is
* can ensure that the array is allocated/rellocated/deallocated with the * allocated/rellocated/deallocated with the same allocator.
* same allocator.
* *
* Params: * Params:
* T = Element type of the array being created. * T = Element type of the array being created.
* Init = If should be initialized. * allocator = The allocator used for getting memory.
* Throws = If $(D_PSYMBOL OutOfMemoryError) should be throwsn. * array = A reference to the array being changed.
* allocator = The allocator used for getting memory. * length = New array length.
* array = A reference to the array being changed.
* length = New array length.
* *
* Returns: $(D_KEYWORD true) upon success, $(D_KEYWORD false) if memory could * Returns: $(D_PARAM array).
* not be reallocated. In the latter
*/ */
package(tanya) bool resize(T, package(tanya) T[] resize(T)(shared Allocator allocator,
bool Init = true, auto ref T[] array,
bool Throws = true) const size_t length) @trusted
(shared Allocator allocator,
ref T[] array,
in size_t length) @trusted
{ {
void[] buf = array; if (length == 0)
static if (Init) {
{ if (allocator.deallocate(array))
immutable oldLength = array.length; {
} return null;
if (!allocator.reallocate(buf, length * T.sizeof)) }
{ else
static if (Throws) {
{ onOutOfMemoryErrorNoGC();
onOutOfMemoryError; }
} }
return false;
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
static if (Init) void[] buf = array;
{ if (!allocator.reallocate(buf, length * T.sizeof))
if (oldLength < length) {
{ onOutOfMemoryErrorNoGC();
array[oldLength .. $] = T.init; }
} // Casting from void[] is unsafe, but we know we cast to the original type.
} array = cast(T[]) buf;
return true;
return array;
} }
package(tanya) alias resizeArray = resize;
/// private unittest
unittest
{ {
int[] p; int[] p;
defaultAllocator.resizeArray(p, 20); p = defaultAllocator.resize(p, 20);
assert(p.length == 20); assert(p.length == 20);
defaultAllocator.resizeArray(p, 30); p = defaultAllocator.resize(p, 30);
assert(p.length == 30); assert(p.length == 30);
defaultAllocator.resizeArray(p, 10); p = defaultAllocator.resize(p, 10);
assert(p.length == 10); assert(p.length == 10);
defaultAllocator.resizeArray(p, 0); p = defaultAllocator.resize(p, 0);
assert(p is null); assert(p is null);
}
/*
* Destroys the object.
* Returns the memory should be freed.
*/
package(tanya) void[] finalize(T)(ref T* p)
{
static if (hasElaborateDestructor!T)
{
destroy(*p);
}
return (cast(void*) p)[0 .. T.sizeof];
}
package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface))
{
if (p is null)
{
return null;
}
static if (is(T == interface))
{
version(Windows)
{
import core.sys.windows.unknwn : IUnknown;
static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in "
~ __PRETTY_FUNCTION__);
}
auto ob = cast(Object) p;
}
else
{
alias ob = p;
}
auto ptr = cast(void*) ob;
auto support = ptr[0 .. typeid(ob).initializer.length];
auto ppv = cast(void**) ptr;
if (!*ppv)
{
return null;
}
auto pc = cast(ClassInfo*) *ppv;
scope (exit)
{
*ppv = null;
}
auto c = *pc;
do
{
// Assume the destructor is @nogc. Leave it nothrow since the destructor
// shouldn't throw and if it does, it is an error anyway.
if (c.destructor)
{
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
if (ppv[1]) // if monitor is not null
{
_d_monitordelete(cast(Object) ptr, true);
}
return support;
}
package(tanya) void[] finalize(T)(ref T[] p)
{
static if (hasElaborateDestructor!(typeof(p[0])))
{
p.each!((ref e) => destroy(e));
}
return p;
} }
/** /**
@ -217,101 +285,25 @@ unittest
* allocator. * allocator.
* *
* Params: * Params:
* T = Type of $(D_PARAM p). * T = Type of $(D_PARAM p).
* allocator = Allocator the $(D_PARAM p) was allocated with. * allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed. * p = Object or array to be destroyed.
*/ */
void dispose(T)(shared Allocator allocator, auto ref T* p)
{
static if (hasElaborateDestructor!T)
{
destroy(*p);
}
() @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }();
p = null;
}
/// Ditto.
void dispose(T)(shared Allocator allocator, auto ref T p) void dispose(T)(shared Allocator allocator, auto ref T p)
if (is(T == class) || is(T == interface))
{ {
if (p is null) () @trusted { allocator.deallocate(finalize(p)); }();
{ p = null;
return;
}
static if (is(T == interface))
{
version(Windows)
{
import core.sys.windows.unknwn : IUnknown;
static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in "
~ __PRETTY_FUNCTION__);
}
auto ob = cast(Object) p;
}
else
{
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;
}
auto pc = cast(ClassInfo*) *ppv;
scope (exit)
{
*ppv = null;
}
auto c = *pc;
do
{
// Assume the destructor is @nogc. Leave it nothrow since the destructor
// shouldn't throw and if it does, it is an error anyway.
if (c.destructor)
{
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
if (ppv[1]) // if monitor is not null
{
_d_monitordelete(cast(Object) ptr, true);
}
} }
/// Ditto. private unittest
void dispose(T)(shared Allocator allocator, auto ref T[] p)
{ {
static if (hasElaborateDestructor!(typeof(p[0]))) struct S
{ {
import std.algorithm.iteration; ~this()
p.each!(e => destroy(e)); {
} }
() @trusted { allocator.deallocate(p); }(); }
p = null; auto p = cast(S[]) defaultAllocator.allocate(S.sizeof);
}
unittest defaultAllocator.dispose(p);
{
struct S
{
~this()
{
}
}
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/. */ * 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/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/ */
module tanya.memory.types; module tanya.memory.types;
@ -14,354 +14,351 @@ import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import std.conv;
import std.range;
import std.traits; import std.traits;
import tanya.memory; import tanya.memory;
package(tanya) final class RefCountedStore(T)
{
T payload;
size_t counter = 1;
size_t opUnary(string op)()
if (op == "--" || op == "++")
in
{
assert(counter > 0);
}
body
{
mixin("return " ~ op ~ "counter;");
}
int opCmp(size_t counter)
{
if (this.counter > counter)
{
return 1;
}
else if (this.counter < counter)
{
return -1;
}
else
{
return 0;
}
}
int opEquals(size_t counter)
{
return this.counter == counter;
}
}
private void separateDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
private void unifiedDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
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. * Reference-counted object containing a $(D_PARAM T) value as payload.
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
* when the reference count goes down to zero, frees the underlying store. * when the reference count goes down to zero, frees the underlying store.
* *
* Params: * Params:
* T = Type of the reference-counted value. * T = Type of the reference-counted value.
*/ */
struct RefCounted(T) struct RefCounted(T)
{ {
static if (is(T == class) || is(T == interface)) static if (is(T == class) || is(T == interface) || isArray!T)
{ {
private alias Payload = T; private alias Payload = T;
} }
else else
{ {
private alias Payload = T*; private alias Payload = T*;
} }
private alias Storage = RefCountedStore!Payload;
private class Storage private Storage storage;
{ private void function(Storage storage,
private Payload payload; shared Allocator allocator) @nogc deleter;
private size_t counter = 1;
private final size_t opUnary(string op)() pure nothrow @safe @nogc invariant
if (op == "--" || op == "++") {
in assert(storage is null || allocator_ !is null);
{ assert(storage is null || deleter !is null);
assert(counter > 0); }
}
body
{
mixin("return " ~ op ~ "counter;");
}
private final int opCmp(size_t counter) const pure nothrow @safe @nogc /**
{ * Takes ownership over $(D_PARAM value), setting the counter to 1.
if (this.counter > counter) * $(D_PARAM value) may be a pointer, an object or a dynamic array.
{ *
return 1; * Params:
} * value = Value whose ownership is taken over.
else if (this.counter < counter) * allocator = Allocator used to destroy the $(D_PARAM value) and to
{ * allocate/deallocate internal storage.
return -1; *
} * Precondition: $(D_INLINECODE allocator !is null)
else */
{ this()(auto ref Payload value,
return 0; shared Allocator allocator = defaultAllocator)
} {
} this(allocator);
this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!Payload;
move(value, this.storage.payload);
}
private final int opEquals(size_t counter) const pure nothrow @safe @nogc /// Ditto.
{ this(shared Allocator allocator)
return this.counter == counter; in
} {
} assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
private final class RefCountedStorage : Storage /**
{ * Increases the reference counter by one.
private shared Allocator allocator; */
this(this)
{
if (count != 0)
{
++storage;
}
}
this(shared Allocator allocator) pure nothrow @safe @nogc /**
in * Decreases the reference counter by one.
{ *
assert(allocator !is null); * If the counter reaches 0, destroys the owned object.
} */
body ~this()
{ {
this.allocator = allocator; if (this.storage !is null && !(this.storage.counter && --this.storage))
} {
deleter(storage, allocator);
}
}
~this() nothrow @nogc /**
{ * Takes ownership over $(D_PARAM rhs). Initializes this
allocator.dispose(payload); * $(D_PSYMBOL RefCounted) if needed.
} *
} * 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).
*
* 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).
*/
ref typeof(this) opAssign()(auto ref Payload rhs)
{
if (this.storage is null)
{
this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!Payload;
}
else if (this.storage > 1)
{
--this.storage;
this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!Payload;
}
else
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(this.storage.counter != 0);
finalize(this.storage.payload);
this.storage.payload = Payload.init;
}
move(rhs, this.storage.payload);
return this;
}
private Storage storage; /// Ditto.
ref typeof(this) opAssign(typeof(null))
{
if (this.storage is null)
{
return this;
}
else if (this.storage > 1)
{
--this.storage;
this.storage = null;
}
else
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(this.storage.counter != 0);
finalize(this.storage.payload);
this.storage.payload = Payload.init;
}
return this;
}
invariant /// Ditto.
{ ref typeof(this) opAssign(typeof(this) rhs)
assert(storage is null || allocator_ !is null); {
} swap(this.allocator_, rhs.allocator_);
swap(this.storage, rhs.storage);
swap(this.deleter, rhs.deleter);
return this;
}
/** /**
* Takes ownership over $(D_PARAM value), setting the counter to 1. * Returns: Reference to the owned object.
* $(D_PARAM value) may be a pointer, an object or a dynamic array. */
* inout(Payload) get() inout pure nothrow @safe @nogc
* Params: in
* value = Value whose ownership is taken over. {
* allocator = Allocator used to destroy the $(D_PARAM value) and to assert(count > 0, "Attempted to access an uninitialized reference.");
* allocate/deallocate internal storage. }
* body
* Precondition: $(D_INLINECODE allocator !is null) {
*/ return storage.payload;
this(Payload value, shared Allocator allocator = defaultAllocator) }
{
this(allocator);
storage = allocator.make!RefCountedStorage(allocator);
move(value, storage.payload);
}
/// Ditto. static if (isPointer!Payload)
this(shared Allocator allocator) {
in /**
{ * Params:
assert(allocator !is null); * op = Operation.
} *
body * Dereferences the pointer. It is defined only for pointers, not for
{ * reference types like classes, that can be accessed directly.
this.allocator_ = allocator; *
} * Returns: Reference to the pointed value.
*/
ref T opUnary(string op)()
if (op == "*")
{
return *storage.payload;
}
}
/** /**
* Increases the reference counter by one. * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
*/ * storage.
this(this) */
{ @property bool isInitialized() const
if (count != 0) {
{ return storage !is null;
++storage; }
}
}
/** /**
* Decreases the reference counter by one. * Returns: The number of $(D_PSYMBOL RefCounted) instances that share
* * ownership over the same pointer (including $(D_KEYWORD this)).
* If the counter reaches 0, destroys the owned object. * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
*/ */
~this() @property size_t count() const
{ {
if (storage !is null && !(storage.counter && --storage)) return storage is null ? 0 : storage.counter;
{ }
allocator_.dispose(storage);
}
}
/** mixin DefaultAllocator;
* Takes ownership over $(D_PARAM rhs). Initializes this alias get this;
* $(D_PSYMBOL RefCounted) if needed.
*
* 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).
*
* 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).
*/
ref typeof(this) opAssign(Payload rhs)
{
if (storage is null)
{
storage = allocator.make!RefCountedStorage(allocator);
}
else if (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);
}
else
{
allocator.dispose(storage.payload);
}
move(rhs, storage.payload);
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(null))
{
if (storage is null)
{
return this;
}
else if (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;
}
else
{
allocator.dispose(storage.payload);
}
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(allocator_, rhs.allocator_);
swap(storage, rhs.storage);
return this;
}
/**
* Returns: Reference to the owned object.
*/
inout(Payload) get() inout pure nothrow @safe @nogc
in
{
assert(count > 0, "Attempted to access an uninitialized reference.");
}
body
{
return storage.payload;
}
static if (isPointer!Payload)
{
/**
* 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 == "*")
{
return *storage.payload;
}
}
/**
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* storage.
*/
@property bool isInitialized() const
{
return storage !is null;
}
/**
* Returns: The number of $(D_PSYMBOL RefCounted) instances that share
* ownership over the same pointer (including $(D_KEYWORD this)).
* If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
*/
@property size_t count() const
{
return storage is null ? 0 : storage.counter;
}
mixin DefaultAllocator;
alias get this;
} }
/// ///
unittest unittest
{ {
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
auto val = rc.get; auto val = rc.get;
*val = 8; *val = 8;
assert(*rc.storage.payload == 8); assert(*rc.storage.payload == 8);
val = null; val = null;
assert(rc.storage.payload !is null); assert(rc.storage.payload !is null);
assert(*rc.storage.payload == 8); assert(*rc.storage.payload == 8);
*rc = 9; *rc = 9;
assert(*rc.storage.payload == 9); assert(*rc.storage.payload == 9);
} }
version (unittest) version (unittest)
{ {
private class A private class A
{ {
uint *destroyed; uint *destroyed;
this(ref uint destroyed) @nogc this(ref uint destroyed) @nogc
{ {
this.destroyed = &destroyed; this.destroyed = &destroyed;
} }
~this() @nogc ~this() @nogc
{ {
++(*destroyed); ++(*destroyed);
} }
} }
private struct B private struct B
{ {
int prop; int prop;
@disable this(); @disable this();
this(int param1) @nogc this(int param1) @nogc
{ {
prop = param1; prop = param1;
} }
} }
} }
private unittest private unittest
{ {
uint destroyed; uint destroyed;
auto a = defaultAllocator.make!A(destroyed); auto a = defaultAllocator.make!A(destroyed);
assert(destroyed == 0); assert(destroyed == 0);
{ {
auto rc = RefCounted!A(a, defaultAllocator); auto rc = RefCounted!A(a, defaultAllocator);
assert(rc.count == 1); assert(rc.count == 1);
void func(RefCounted!A rc) void func(RefCounted!A rc)
{ {
assert(rc.count == 2); assert(rc.count == 2);
} }
func(rc); func(rc);
assert(rc.count == 1); assert(rc.count == 1);
} }
assert(destroyed == 1); assert(destroyed == 1);
RefCounted!int rc; RefCounted!int rc;
assert(rc.count == 0); assert(rc.count == 0);
rc = defaultAllocator.make!int(8); rc = defaultAllocator.make!int(8);
assert(rc.count == 1); assert(rc.count == 1);
} }
private unittest private unittest
{ {
static assert(is(typeof(RefCounted!int.storage.payload) == int*)); static assert(is(typeof(RefCounted!int.storage.payload) == int*));
static assert(is(typeof(RefCounted!A.storage.payload) == A)); static assert(is(typeof(RefCounted!A.storage.payload) == A));
static assert(is(RefCounted!B)); static assert(is(RefCounted!B));
static assert(is(RefCounted!A)); static assert(is(RefCounted!A));
} }
/** /**
@ -374,85 +371,127 @@ private unittest
* object). * object).
* *
* Params: * Params:
* T = Type of the constructed object. * T = Type of the constructed object.
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/ */
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T if (!is(T == interface) && !isAbstractClass!T
&& !isArray!T && !isAssociativeArray!T) && !isAssociativeArray!T && !isArray!T)
in
{ {
auto rc = typeof(return)(allocator); assert(allocator !is null);
}
body
{
auto rc = typeof(return)(allocator);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize); const size = alignedSize(stateSize!T + storageSize);
auto mem = (() @trusted => allocator.allocate(size))(); auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
{ {
onOutOfMemoryError(); onOutOfMemoryError();
} }
scope (failure) scope (failure)
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
static if (is(T == class)) static if (is(T == class))
{ {
rc.storage.payload = emplace!T(mem[storageSize .. $], args); rc.storage.payload = emplace!T(mem[storageSize .. $], args);
} }
else else
{ {
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args); rc.storage.payload = emplace!T(ptr, args);
} }
return rc; rc.deleter = &unifiedDeleter!(RefCounted!T.Payload);
return rc;
}
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
* $(D_PSYMBOL RefCounted) using.
*
* 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)
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
{
auto payload = cast(T) allocator.allocate(ElementType!T.sizeof * size);
initializeAll(payload);
return RefCounted!T(payload, allocator);
} }
/// ///
unittest unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
void func(RefCounted!int param) void func(RefCounted!int param)
{ {
if (param.count == 2) if (param.count == 2)
{ {
func(param); func(param);
} }
else else
{ {
assert(param.count == 3); assert(param.count == 3);
} }
} }
func(rc); func(rc);
assert(rc.count == 1); assert(rc.count == 1);
} }
private @nogc unittest private @nogc unittest
{ {
struct E struct E
{ {
} }
auto b = defaultAllocator.refCounted!B(15); auto b = defaultAllocator.refCounted!B(15);
static assert(is(typeof(b.storage.payload) == B*)); static assert(is(typeof(b.storage.payload) == B*));
static assert(is(typeof(b.prop) == int)); static assert(is(typeof(b.prop) == int));
static assert(!is(typeof(defaultAllocator.refCounted!B()))); static assert(!is(typeof(defaultAllocator.refCounted!B())));
static assert(is(typeof(defaultAllocator.refCounted!E()))); static assert(is(typeof(defaultAllocator.refCounted!E())));
static assert(!is(typeof(defaultAllocator.refCounted!E(5)))); static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
{ {
auto rc = defaultAllocator.refCounted!B(3); auto rc = defaultAllocator.refCounted!B(3);
assert(rc.get.prop == 3); assert(rc.get.prop == 3);
} }
{ {
auto rc = defaultAllocator.refCounted!E(); auto rc = defaultAllocator.refCounted!E();
assert(rc.count); assert(rc.count);
} }
}
private @nogc unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
} }

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

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff