Merge branch 'master' into utf8string

This commit is contained in:
Eugen Wissner 2017-04-08 08:44:21 +02:00
commit e1964e47a5
19 changed files with 7546 additions and 6267 deletions

View File

@ -7,7 +7,7 @@ os:
language: d language: d
d: d:
- dmd-2.073.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 - dmd-2.070.2

View File

@ -29,7 +29,7 @@ helper functions).
### Supported compilers ### Supported compilers
* dmd 2.073.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 * dmd 2.070.2

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,9 @@ 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,6 +3,8 @@
* 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).

View File

@ -3,6 +3,8 @@
* 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).
@ -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,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.math; module tanya.math;
@ -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,162 @@ 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 = y.length;
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);
if (x.length == 0 && i != 0) if (x.length == 0 && i != 0)
{ {
i = 0; i = 0;
} }
else else
{ {
result = 1; result = 1;
} }
while (i) while (i)
{ {
--i; --i;
for (ubyte mask = 0x01; mask; 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;
} }
tmp2 = tmp1; auto tmp2 = tmp1;
tmp1 *= tmp2; tmp1 *= tmp2;
tmp1 %= z; tmp1 %= z;
} }
} }
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,7 +8,7 @@
* 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;
@ -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,7 @@
module tanya.memory; module tanya.memory;
import core.exception; import core.exception;
public import std.experimental.allocator : make, makeArray; public import std.experimental.allocator : make;
import std.traits; import std.traits;
public import tanya.memory.allocator; public import tanya.memory.allocator;
@ -23,59 +23,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 +87,28 @@ shared Allocator allocator;
shared static this() nothrow @trusted @nogc shared static this() nothrow @trusted @nogc
{ {
import tanya.memory.mmappool; import tanya.memory.mallocator;
allocator = MmapPool.instance; allocator = Mallocator.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 +116,90 @@ 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);
} }
/** /**
@ -217,101 +208,101 @@ 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) void dispose(T)(shared Allocator allocator, auto ref T* p)
{ {
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
{ {
destroy(*p); destroy(*p);
} }
() @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }();
p = null; p = null;
} }
/// Ditto. /// 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 (is(T == class) || is(T == interface))
{ {
if (p is null) if (p is null)
{ {
return; return;
} }
static if (is(T == interface)) static if (is(T == interface))
{ {
version(Windows) version(Windows)
{ {
import core.sys.windows.unknwn : IUnknown; import core.sys.windows.unknwn : IUnknown;
static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in "
~ __PRETTY_FUNCTION__); ~ __PRETTY_FUNCTION__);
} }
auto ob = cast(Object) p; auto ob = cast(Object) p;
} }
else else
{ {
alias ob = p; alias ob = p;
} }
auto ptr = cast(void *) ob; auto ptr = cast(void *) ob;
auto support = ptr[0 .. typeid(ob).initializer.length]; auto support = ptr[0 .. typeid(ob).initializer.length];
scope (success) scope (success)
{ {
() @trusted { allocator.deallocate(support); }(); () @trusted { allocator.deallocate(support); }();
p = null; p = null;
} }
auto ppv = cast(void**) ptr; auto ppv = cast(void**) ptr;
if (!*ppv) if (!*ppv)
{ {
return; return;
} }
auto pc = cast(ClassInfo*) *ppv; auto pc = cast(ClassInfo*) *ppv;
scope (exit) scope (exit)
{ {
*ppv = null; *ppv = null;
} }
auto c = *pc; auto c = *pc;
do do
{ {
// Assume the destructor is @nogc. Leave it nothrow since the destructor // Assume the destructor is @nogc. Leave it nothrow since the destructor
// shouldn't throw and if it does, it is an error anyway. // shouldn't throw and if it does, it is an error anyway.
if (c.destructor) if (c.destructor)
{ {
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob); (cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
} }
} }
while ((c = c.base) !is null); while ((c = c.base) !is null);
if (ppv[1]) // if monitor is not null if (ppv[1]) // if monitor is not null
{ {
_d_monitordelete(cast(Object) ptr, true); _d_monitordelete(cast(Object) ptr, true);
} }
} }
/// Ditto. /// Ditto.
void dispose(T)(shared Allocator allocator, auto ref T[] p) void dispose(T)(shared Allocator allocator, auto ref T[] p)
{ {
static if (hasElaborateDestructor!(typeof(p[0]))) static if (hasElaborateDestructor!(typeof(p[0])))
{ {
import std.algorithm.iteration; import std.algorithm.iteration;
p.each!(e => destroy(e)); p.each!(e => destroy(e));
} }
() @trusted { allocator.deallocate(p); }(); () @trusted { allocator.deallocate(p); }();
p = null; p = null;
} }
unittest unittest
{ {
struct S struct S
{ {
~this() ~this()
{ {
} }
} }
auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); auto p = cast(S[]) defaultAllocator.allocate(S.sizeof);
defaultAllocator.dispose(p); 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;
@ -23,345 +23,345 @@ import tanya.memory;
* 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))
{ {
private alias Payload = T; private alias Payload = T;
} }
else else
{ {
private alias Payload = T*; private alias Payload = T*;
} }
private class Storage private class Storage
{ {
private Payload payload; private Payload payload;
private size_t counter = 1; private size_t counter = 1;
private final size_t opUnary(string op)() pure nothrow @safe @nogc private final size_t opUnary(string op)() pure nothrow @safe @nogc
if (op == "--" || op == "++") if (op == "--" || op == "++")
in in
{ {
assert(counter > 0); assert(counter > 0);
} }
body body
{ {
mixin("return " ~ op ~ "counter;"); mixin("return " ~ op ~ "counter;");
} }
private final int opCmp(size_t counter) const pure nothrow @safe @nogc private final int opCmp(size_t counter) const pure nothrow @safe @nogc
{ {
if (this.counter > counter) if (this.counter > counter)
{ {
return 1; return 1;
} }
else if (this.counter < counter) else if (this.counter < counter)
{ {
return -1; return -1;
} }
else else
{ {
return 0; return 0;
} }
} }
private final int opEquals(size_t counter) const pure nothrow @safe @nogc private final int opEquals(size_t counter) const pure nothrow @safe @nogc
{ {
return this.counter == counter; return this.counter == counter;
} }
} }
private final class RefCountedStorage : Storage private final class RefCountedStorage : Storage
{ {
private shared Allocator allocator; private shared Allocator allocator;
this(shared Allocator allocator) pure nothrow @safe @nogc this(shared Allocator allocator) pure nothrow @safe @nogc
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
this.allocator = allocator; this.allocator = allocator;
} }
~this() nothrow @nogc ~this() nothrow @nogc
{ {
allocator.dispose(payload); allocator.dispose(payload);
} }
} }
private Storage storage; private Storage storage;
invariant invariant
{ {
assert(storage is null || allocator_ !is null); assert(storage is null || allocator_ !is null);
} }
/** /**
* Takes ownership over $(D_PARAM value), setting the counter to 1. * Takes ownership over $(D_PARAM value), setting the counter to 1.
* $(D_PARAM value) may be a pointer, an object or a dynamic array. * $(D_PARAM value) may be a pointer, an object or a dynamic array.
* *
* Params: * Params:
* value = Value whose ownership is taken over. * value = Value whose ownership is taken over.
* allocator = Allocator used to destroy the $(D_PARAM value) and to * allocator = Allocator used to destroy the $(D_PARAM value) and to
* allocate/deallocate internal storage. * allocate/deallocate internal storage.
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(Payload value, shared Allocator allocator = defaultAllocator) this(Payload value, shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
storage = allocator.make!RefCountedStorage(allocator); storage = allocator.make!RefCountedStorage(allocator);
move(value, storage.payload); move(value, storage.payload);
} }
/// Ditto. /// Ditto.
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body body
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
/** /**
* Increases the reference counter by one. * Increases the reference counter by one.
*/ */
this(this) this(this)
{ {
if (count != 0) if (count != 0)
{ {
++storage; ++storage;
} }
} }
/** /**
* Decreases the reference counter by one. * Decreases the reference counter by one.
* *
* If the counter reaches 0, destroys the owned object. * If the counter reaches 0, destroys the owned object.
*/ */
~this() ~this()
{ {
if (storage !is null && !(storage.counter && --storage)) if (storage !is null && !(storage.counter && --storage))
{ {
allocator_.dispose(storage); allocator_.dispose(storage);
} }
} }
/** /**
* Takes ownership over $(D_PARAM rhs). Initializes this * Takes ownership over $(D_PARAM rhs). Initializes this
* $(D_PSYMBOL RefCounted) if needed. * $(D_PSYMBOL RefCounted) if needed.
* *
* If it is the last reference of the previously owned object, * If it is the last reference of the previously owned object,
* it will be destroyed. * it will be destroyed.
* *
* To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null). * To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
* *
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will * If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
* be used. If you need a different allocator, create a new * be used. If you need a different allocator, create a new
* $(D_PSYMBOL RefCounted) and assign it. * $(D_PSYMBOL RefCounted) and assign it.
* *
* Params: * Params:
* rhs = $(D_KEYWORD this). * rhs = $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(Payload rhs) ref typeof(this) opAssign(Payload rhs)
{ {
if (storage is null) if (storage is null)
{ {
storage = allocator.make!RefCountedStorage(allocator); storage = allocator.make!RefCountedStorage(allocator);
} }
else if (storage > 1) else if (storage > 1)
{ {
--storage; --storage;
storage = allocator.make!RefCountedStorage(allocator); storage = allocator.make!RefCountedStorage(allocator);
} }
else if (cast(RefCountedStorage) storage is null) else if (cast(RefCountedStorage) storage is null)
{ {
// Created with refCounted. Always destroyed togethter with the pointer. // Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0); assert(storage.counter != 0);
allocator.dispose(storage); allocator.dispose(storage);
storage = allocator.make!RefCountedStorage(allocator); storage = allocator.make!RefCountedStorage(allocator);
} }
else else
{ {
allocator.dispose(storage.payload); allocator.dispose(storage.payload);
} }
move(rhs, storage.payload); move(rhs, storage.payload);
return this; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
if (storage is null) if (storage is null)
{ {
return this; return this;
} }
else if (storage > 1) else if (storage > 1)
{ {
--storage; --storage;
storage = null; storage = null;
} }
else if (cast(RefCountedStorage) storage is null) else if (cast(RefCountedStorage) storage is null)
{ {
// Created with refCounted. Always destroyed togethter with the pointer. // Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0); assert(storage.counter != 0);
allocator.dispose(storage); allocator.dispose(storage);
return this; return this;
} }
else else
{ {
allocator.dispose(storage.payload); allocator.dispose(storage.payload);
} }
return this; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(allocator_, rhs.allocator_); swap(allocator_, rhs.allocator_);
swap(storage, rhs.storage); swap(storage, rhs.storage);
return this; return this;
} }
/** /**
* Returns: Reference to the owned object. * Returns: Reference to the owned object.
*/ */
inout(Payload) get() inout pure nothrow @safe @nogc inout(Payload) get() inout pure nothrow @safe @nogc
in in
{ {
assert(count > 0, "Attempted to access an uninitialized reference."); assert(count > 0, "Attempted to access an uninitialized reference.");
} }
body body
{ {
return storage.payload; return storage.payload;
} }
static if (isPointer!Payload) static if (isPointer!Payload)
{ {
/** /**
* Params: * Params:
* op = Operation. * op = Operation.
* *
* Dereferences the pointer. It is defined only for pointers, not for * Dereferences the pointer. It is defined only for pointers, not for
* reference types like classes, that can be accessed directly. * reference types like classes, that can be accessed directly.
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
ref T opUnary(string op)() ref T opUnary(string op)()
if (op == "*") if (op == "*")
{ {
return *storage.payload; return *storage.payload;
} }
} }
/** /**
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* storage. * storage.
*/ */
@property bool isInitialized() const @property bool isInitialized() const
{ {
return storage !is null; return storage !is null;
} }
/** /**
* Returns: The number of $(D_PSYMBOL RefCounted) instances that share * Returns: The number of $(D_PSYMBOL RefCounted) instances that share
* ownership over the same pointer (including $(D_KEYWORD this)). * ownership over the same pointer (including $(D_KEYWORD this)).
* If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`. * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
*/ */
@property size_t count() const @property size_t count() const
{ {
return storage is null ? 0 : storage.counter; return storage is null ? 0 : storage.counter;
} }
mixin DefaultAllocator; mixin DefaultAllocator;
alias get this; 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 +374,85 @@ 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).
*/ */
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) && !isArray!T && !isAssociativeArray!T)
{ {
auto rc = typeof(return)(allocator); auto rc = typeof(return)(allocator);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize); immutable 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; return rc;
} }
/// ///
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);
} }
} }

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