Merge branch 'master' into utf8string
This commit is contained in:
commit
e1964e47a5
@ -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
|
||||||
|
@ -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
@ -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
@ -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).
|
||||||
|
@ -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
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
185
source/tanya/memory/mallocator.d
Normal file
185
source/tanya/memory/mallocator.d
Normal 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_;
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
356
source/tanya/network/inet.d
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
source/tanya/network/package.d
Normal file
17
source/tanya/network/package.d
Normal 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
Loading…
x
Reference in New Issue
Block a user