14 Commits

Author SHA1 Message Date
65c3ca14ec Integer storage optimization 2017-04-30 16:07:44 +02:00
4fa47153ba Make Integer representation little endian 2017-04-25 19:50:06 +02:00
628153e2e8 Make RefCounted work with dynamic arrays 2017-04-16 20:14:04 +02:00
7aa9ac9f4a Add internal finalize method for finalizing an object without deallocating 2017-04-16 20:13:20 +02:00
8156d0fe3a Add support for dmd 2.074.0, remove 2.070.2 2017-04-13 16:02:18 +02:00
6e2ce5d686 Remove opApply from containers
opApply requires additional overloads for the const containers (with a
const delegate). If using a templated opApply foreach cannot infer the
types for the variables. foreach with one argument still works
(opIndex() is used), for more complex cases slicing should be used.
2017-04-07 16:00:50 +02:00
ba6bf554fb Make SList range public 2017-04-07 15:17:14 +02:00
b1d2b9bd9e Fix Vector.insertAfter/Before an empty range 2017-04-04 15:11:14 +02:00
9b953198fa Fix network.inet release build 2017-04-04 08:36:42 +02:00
bc2a6d2703 Swap toHostOrder template parameters 2017-04-03 15:32:15 +02:00
b458250ad7 Make NetworkOrder work with 8-byte integers 2017-04-02 20:55:22 +02:00
b08d5e5d83 Add tanya.network.inet.toHostOrder
The function reverts NetworkOrder.
2017-04-02 11:16:08 +02:00
445b872e91 Add tanya.network.inet.NetworkOrder
NetworkOrder converts an integral type into a bidirectional range with
big-endian byte order.
2017-04-02 09:29:54 +02:00
5e16fe98d6 Add tanya.network package file 2017-04-01 09:53:59 +02:00
12 changed files with 3371 additions and 2801 deletions

View File

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

View File

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

View File

@ -20,25 +20,31 @@ import std.traits;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
private struct Range(Entry) /**
if (__traits(isSame, TemplateOf!Entry, SEntry)) * Forward range for the $(D_PSYMBOL SList).
*
* Params:
* E = Element type.
*/
struct SRange(E)
{ {
private alias T = typeof(E.content); private alias EntryPointer = CopyConstness!(E, SEntry!(Unqual!E)*);
private alias E = CopyConstness!(Entry, Entry*);
private E* head; private EntryPointer* head;
invariant invariant
{ {
assert(head !is null); assert(head !is null);
} }
private this(ref E head) @trusted private this(ref EntryPointer head) @trusted
{ {
this.head = &head; this.head = &head;
} }
@property Range save() @disable this();
@property SRange save()
{ {
return this; return this;
} }
@ -53,7 +59,7 @@ private struct Range(Entry)
return *head is null; return *head is null;
} }
@property ref inout(T) front() inout @property ref inout(E) front() inout
in in
{ {
assert(!empty); assert(!empty);
@ -73,12 +79,12 @@ private struct Range(Entry)
head = &(*head).next; head = &(*head).next;
} }
Range opIndex() SRange opIndex()
{ {
return typeof(return)(*head); return typeof(return)(*head);
} }
Range!(const Entry) opIndex() const SRange!(const E) opIndex() const
{ {
return typeof(return)(*head); return typeof(return)(*head);
} }
@ -340,6 +346,14 @@ struct SList(T)
return moveEntry(head, el); return moveEntry(head, el);
} }
/// Ditto.
size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
head = allocator.make!Entry(el, head);
return 1;
}
/// Ditto. /// Ditto.
size_t insertFront(R)(R el) @trusted size_t insertFront(R)(R el) @trusted
if (!isInfinite!R if (!isInfinite!R
@ -376,13 +390,6 @@ struct SList(T)
return insertFront!(T[])(el[]); return insertFront!(T[])(el[]);
} }
/// Ditto.
size_t insertFront(ref T el) @trusted
{
head = allocator.make!Entry(el, head);
return 1;
}
/// Ditto. /// Ditto.
alias insert = insertFront; alias insert = insertFront;
@ -407,7 +414,7 @@ struct SList(T)
version (assert) version (assert)
{ {
private bool checkRangeBelonging(ref Range!Entry r) const private bool checkRangeBelonging(ref SRange!T r) const
{ {
const(Entry*)* pos; const(Entry*)* pos;
for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next) for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next)
@ -429,7 +436,7 @@ struct SList(T)
* *
* Precondition: $(D_PARAM r) is extracted from this list. * Precondition: $(D_PARAM r) is extracted from this list.
*/ */
size_t insertBefore(R)(Range!Entry r, R el) size_t insertBefore(R)(SRange!T r, R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in
{ {
@ -450,7 +457,7 @@ struct SList(T)
} }
/// Ditto. /// Ditto.
size_t insertBefore(R)(Range!Entry r, R el) size_t insertBefore(R)(SRange!T r, R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
@ -482,7 +489,7 @@ struct SList(T)
} }
/// Ditto. /// Ditto.
size_t insertBefore(Range!Entry r, ref T el) @trusted size_t insertBefore(SRange!T r, ref T el) @trusted
in in
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
@ -515,7 +522,7 @@ struct SList(T)
* *
* Precondition: $(D_PARAM r) is extracted from this list. * Precondition: $(D_PARAM r) is extracted from this list.
*/ */
size_t insertBefore(size_t R)(Range!Entry r, T[R] el) size_t insertBefore(size_t R)(SRange!T r, T[R] el)
{ {
return insertFront!(T[])(el[]); return insertFront!(T[])(el[]);
} }
@ -670,7 +677,7 @@ struct SList(T)
* *
* Precondition: $(D_PARAM r) is extracted from this list. * Precondition: $(D_PARAM r) is extracted from this list.
*/ */
Range!Entry remove(Range!Entry r) SRange!T remove(SRange!T r)
in in
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
@ -697,75 +704,17 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/**
* $(D_KEYWORD foreach) iteration.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
size_t i;
for (auto pos = head; pos; pos = pos.next, ++i)
{
result = dg(i, pos.content);
if (result != 0)
{
return result;
}
}
return result;
}
/// Ditto.
int opApply(scope int delegate(ref T) @nogc dg)
{
int result;
for (auto pos = head; pos; pos = pos.next)
{
result = dg(pos.content);
if (result != 0)
{
return result;
}
}
return result;
}
///
@nogc unittest
{
SList!int l;
l.insertFront(5);
l.insertFront(4);
l.insertFront(9);
foreach (i, e; l)
{
assert(i != 0 || e == 9);
assert(i != 1 || e == 4);
assert(i != 2 || e == 5);
}
}
/** /**
* Returns: Range that iterates over all elements of the container, in * Returns: Range that iterates over all elements of the container, in
* forward order. * forward order.
*/ */
Range!Entry opIndex() SRange!T opIndex()
{ {
return typeof(return)(head); return typeof(return)(head);
} }
/// Ditto. /// Ditto.
Range!(const Entry) opIndex() const SRange!(const T) opIndex() const
{ {
return typeof(return)(head); return typeof(return)(head);
} }
@ -827,7 +776,7 @@ struct SList(T)
} }
next = &(*next).next; next = &(*next).next;
} }
remove(Range!Entry(*next)); remove(SRange!T(*next));
return this; return this;
} }
@ -894,3 +843,21 @@ struct SList(T)
} }
static assert(is(SList!Stuff)); static assert(is(SList!Stuff));
} }
// foreach called using opIndex().
private @nogc @safe unittest
{
SList!int l;
size_t i;
l.insertFront(5);
l.insertFront(4);
l.insertFront(9);
foreach (e; l)
{
assert(i != 0 || e == 9);
assert(i != 1 || e == 4);
assert(i != 2 || e == 5);
++i;
}
}

View File

@ -22,22 +22,42 @@ import std.meta;
import std.traits; import std.traits;
import tanya.memory; import tanya.memory;
// Defines the container's primary range. /**
private struct Range(E) * Random-access range for the $(D_PSYMBOL Vector).
*
* Params:
* E = Element type.
*/
struct Range(E)
{ {
private E* begin, end; private E* begin, end;
private alias ContainerType = CopyConstness!(E, Vector!(Unqual!E));
private ContainerType* vector;
invariant invariant
{ {
assert(begin <= end); assert(this.begin <= this.end);
assert(this.vector !is null);
assert(this.begin >= this.vector.data);
assert(this.end <= this.vector.data + this.vector.length);
} }
private this(E* begin, E* end) private this(ref ContainerType vector, E* begin, E* end) @trusted
in
{ {
assert(begin <= end);
assert(begin >= vector.data);
assert(end <= vector.data + vector.length);
}
body
{
this.vector = &vector;
this.begin = begin; this.begin = begin;
this.end = end; this.end = end;
} }
@disable this();
@property Range save() @property Range save()
{ {
return this; return this;
@ -45,12 +65,12 @@ private struct Range(E)
@property bool empty() const @property bool empty() const
{ {
return begin == end; return this.begin == this.end;
} }
@property size_t length() const @property size_t length() const
{ {
return end - begin; return this.end - this.begin;
} }
alias opDollar = length; alias opDollar = length;
@ -62,7 +82,7 @@ private struct Range(E)
} }
body body
{ {
return *begin; return *this.begin;
} }
@property ref inout(E) back() inout @trusted @property ref inout(E) back() inout @trusted
@ -72,7 +92,7 @@ private struct Range(E)
} }
body body
{ {
return *(end - 1); return *(this.end - 1);
} }
void popFront() @trusted void popFront() @trusted
@ -82,7 +102,7 @@ private struct Range(E)
} }
body body
{ {
++begin; ++this.begin;
} }
void popBack() @trusted void popBack() @trusted
@ -92,7 +112,7 @@ private struct Range(E)
} }
body body
{ {
--end; --this.end;
} }
ref inout(E) opIndex(const size_t i) inout @trusted ref inout(E) opIndex(const size_t i) inout @trusted
@ -102,17 +122,17 @@ private struct Range(E)
} }
body body
{ {
return *(begin + i); return *(this.begin + i);
} }
Range opIndex() Range opIndex()
{ {
return typeof(return)(begin, end); return typeof(return)(*this.vector, this.begin, this.end);
} }
Range!(const E) opIndex() const Range!(const E) opIndex() const
{ {
return typeof(return)(begin, end); return typeof(return)(*this.vector, this.begin, this.end);
} }
Range opSlice(const size_t i, const size_t j) @trusted Range opSlice(const size_t i, const size_t j) @trusted
@ -123,7 +143,7 @@ private struct Range(E)
} }
body body
{ {
return typeof(return)(begin + i, begin + j); return typeof(return)(*this.vector, this.begin + i, this.begin + j);
} }
Range!(const E) opSlice(const size_t i, const size_t j) const @trusted Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
@ -134,12 +154,12 @@ private struct Range(E)
} }
body body
{ {
return typeof(return)(begin + i, begin + j); return typeof(return)(*this.vector, this.begin + i, this.begin + j);
} }
inout(E[]) get() inout @trusted inout(E[]) get() inout @trusted
{ {
return begin[0 .. length]; return this.begin[0 .. length];
} }
} }
@ -152,13 +172,13 @@ private struct Range(E)
struct Vector(T) struct Vector(T)
{ {
private size_t length_; private size_t length_;
private T* vector; private T* data;
private size_t capacity_; private size_t capacity_;
invariant invariant
{ {
assert(length_ <= capacity_); assert(this.length_ <= this.capacity_);
assert(capacity_ == 0 || vector !is null); assert(this.capacity_ == 0 || this.data !is null);
} }
/** /**
@ -224,20 +244,20 @@ struct Vector(T)
if (allocator is init.allocator) if (allocator is init.allocator)
{ {
// Just steal all references and the allocator. // Just steal all references and the allocator.
vector = init.vector; this.data = init.data;
length_ = init.length_; this.length_ = init.length_;
capacity_ = init.capacity_; this.capacity_ = init.capacity_;
// Reset the source vector, so it can't destroy the moved storage. // Reset the source vector, so it can't destroy the moved storage.
init.length_ = init.capacity_ = 0; init.length_ = init.capacity_ = 0;
init.vector = null; init.data = null;
} }
else else
{ {
// Move each element. // Move each element.
reserve(init.length); reserve(init.length_);
moveEmplaceAll(init.vector[0 .. init.length_], vector[0 .. init.length_]); moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]);
length_ = init.length; this.length_ = init.length_;
// Destructor of init should destroy it here. // Destructor of init should destroy it here.
} }
} }
@ -247,7 +267,6 @@ struct Vector(T)
{ {
auto v1 = Vector!int([1, 2, 3]); auto v1 = Vector!int([1, 2, 3]);
auto v2 = Vector!int(v1); auto v2 = Vector!int(v1);
assert(v1.vector !is v2.vector);
assert(v1 == v2); assert(v1 == v2);
auto v3 = Vector!int(Vector!int([1, 2, 3])); auto v3 = Vector!int(Vector!int([1, 2, 3]));
@ -260,7 +279,7 @@ struct Vector(T)
{ {
auto v1 = const Vector!int([1, 2, 3]); auto v1 = const Vector!int([1, 2, 3]);
auto v2 = Vector!int(v1); auto v2 = Vector!int(v1);
assert(v1.vector !is v2.vector); assert(v1.data !is v2.data);
assert(v1 == v2); assert(v1 == v2);
auto v3 = const Vector!int(Vector!int([1, 2, 3])); auto v3 = const Vector!int(Vector!int([1, 2, 3]));
@ -281,7 +300,7 @@ struct Vector(T)
{ {
this(allocator); this(allocator);
reserve(len); reserve(len);
uninitializedFill(vector[0 .. len], init); uninitializedFill(this.data[0 .. len], init);
length_ = len; length_ = len;
} }
@ -334,7 +353,7 @@ struct Vector(T)
~this() @trusted ~this() @trusted
{ {
clear(); clear();
allocator.deallocate(vector[0 .. capacity_]); allocator.deallocate(this.data[0 .. capacity]);
} }
/** /**
@ -342,9 +361,9 @@ struct Vector(T)
*/ */
this(this) this(this)
{ {
auto buf = opIndex(); auto buf = this.data[0 .. this.length_];
length_ = capacity_ = 0; this.length_ = capacity_ = 0;
vector = null; this.data = null;
insertBack(buf); insertBack(buf);
} }
@ -409,14 +428,14 @@ struct Vector(T)
else if (len > length) else if (len > length)
{ {
reserve(len); reserve(len);
initializeAll(vector[length_ .. len]); initializeAll(this.data[length_ .. len]);
} }
else else
{ {
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
{ {
const T* end = vector + length_ - 1; const T* end = this.data + length_ - 1;
for (T* e = vector + len; e != end; ++e) for (T* e = this.data + len; e != end; ++e)
{ {
destroy(*e); destroy(*e);
} }
@ -467,7 +486,7 @@ struct Vector(T)
immutable byteSize = mulu(size, T.sizeof, overflow); immutable byteSize = mulu(size, T.sizeof, overflow);
assert(!overflow); assert(!overflow);
void[] buf = vector[0 .. capacity_]; void[] buf = this.data[0 .. this.capacity_];
if (!allocator.reallocateInPlace(buf, byteSize)) if (!allocator.reallocateInPlace(buf, byteSize))
{ {
buf = allocator.allocate(byteSize); buf = allocator.allocate(byteSize);
@ -479,8 +498,8 @@ struct Vector(T)
{ {
allocator.deallocate(buf); allocator.deallocate(buf);
} }
const T* end = vector + length_; const T* end = this.data + this.length_;
for (T* src = vector, dest = cast(T*) buf; src != end; ++src, ++dest) for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
{ {
moveEmplace(*src, *dest); moveEmplace(*src, *dest);
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
@ -488,10 +507,10 @@ struct Vector(T)
destroy(*src); destroy(*src);
} }
} }
allocator.deallocate(vector[0 .. capacity_]); allocator.deallocate(this.data[0 .. this.capacity_]);
vector = cast(T*) buf; this.data = cast(T*) buf;
} }
capacity_ = size; this.capacity_ = size;
} }
/// ///
@ -517,15 +536,15 @@ struct Vector(T)
*/ */
void shrink(const size_t size) @trusted void shrink(const size_t size) @trusted
{ {
if (capacity_ <= size) if (capacity <= size)
{ {
return; return;
} }
immutable n = max(length, size); immutable n = max(length, size);
void[] buf = vector[0 .. capacity_]; void[] buf = this.data[0 .. this.capacity_];
if (allocator.reallocateInPlace(buf, n * T.sizeof)) if (allocator.reallocateInPlace(buf, n * T.sizeof))
{ {
capacity_ = n; this.capacity_ = n;
} }
} }
@ -556,7 +575,7 @@ struct Vector(T)
* *
* Returns: The number of elements removed * Returns: The number of elements removed
* *
* Precondition: $(D_INLINECODE !empty) * Precondition: $(D_INLINECODE !empty).
*/ */
void removeBack() void removeBack()
in in
@ -611,22 +630,24 @@ struct Vector(T)
* Params: * Params:
* r = Range originally obtained from this vector. * r = Range originally obtained from this vector.
* *
* Returns: Elements in $(D_PARAM r) after removing. * Returns: A range spanning the remaining elements in the array that
* initially were right after $(D_PARAM r).
* *
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/ */
Range!T remove(Range!T r) @trusted Range!T remove(Range!T r) @trusted
in in
{ {
assert(r.begin >= vector); assert(r.vector is &this);
assert(r.end <= vector + length_); assert(r.begin >= this.data);
assert(r.end <= this.data + length);
} }
body body
{ {
auto end = vector + length_; auto end = this.data + this.length;
moveAll(Range!T(r.end, end), Range!T(r.begin, end)); moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end));
length = length - r.length; length = length - r.length;
return r; return Range!T(this, r.begin, this.data + length);
} }
/// ///
@ -634,31 +655,28 @@ struct Vector(T)
{ {
auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]); auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]);
assert(v.remove(v[1 .. 3]).length == 2); assert(v.remove(v[1 .. 3]).length == 4);
assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
assert(v.length == 5); assert(v.length == 5);
assert(v.remove(v[4 .. 4]).length == 0); assert(v.remove(v[4 .. 4]).length == 1);
assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
assert(v.length == 5); assert(v.length == 5);
assert(v.remove(v[4 .. 5]).length == 1); assert(v.remove(v[4 .. 5]).length == 0);
assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6); assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6);
assert(v.length == 4); assert(v.length == 4);
assert(v.remove(v[]).length == 4);
assert(v.empty);
assert(v.remove(v[]).length == 0); assert(v.remove(v[]).length == 0);
assert(v.empty);
} }
private void moveBack(R)(ref R el) @trusted private void moveBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(length + 1); reserve(this.length + 1);
moveEmplace(el, *(vector + length_)); moveEmplace(el, *(this.data + this.length_));
++length_; ++this.length_;
} }
/** /**
@ -670,19 +688,20 @@ struct Vector(T)
* *
* Returns: The number of elements inserted. * Returns: The number of elements inserted.
*/ */
size_t insertBack(R)(auto ref R el) @trusted size_t insertBack(R)(R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
static if (__traits(isRef, el)) moveBack(el);
{ return 1;
reserve(length + 1); }
emplace(vector + length_, el);
++length_; /// Ditto.
} size_t insertBack(R)(ref R el) @trusted
else if (isImplicitlyConvertible!(R, T))
{ {
moveBack(el); reserve(this.length_ + 1);
} emplace(this.data + this.length_, el);
++this.length_;
return 1; return 1;
} }
@ -763,6 +782,8 @@ struct Vector(T)
* el = Value(s) should be inserted. * el = Value(s) should be inserted.
* *
* Returns: The number of elements inserted. * Returns: The number of elements inserted.
*
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/ */
size_t insertAfter(R)(Range!T r, R el) size_t insertAfter(R)(Range!T r, R el)
if (!isInfinite!R if (!isInfinite!R
@ -770,20 +791,28 @@ struct Vector(T)
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in
{ {
assert(r.begin >= vector); assert(r.vector is &this);
assert(r.end <= vector + length_); assert(r.begin >= this.data);
assert(r.end <= this.data + length);
} }
body body
{ {
immutable oldLen = length; immutable oldLen = length;
immutable offset = r.end - vector; immutable offset = r.end - this.data;
immutable inserted = insertBack(el); immutable inserted = insertBack(el);
bringToFront(vector[offset .. oldLen], vector[oldLen .. length]); bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
return inserted; return inserted;
} }
/// Ditto. /// Ditto.
size_t insertAfter(size_t R)(Range!T r, T[R] el) size_t insertAfter(size_t R)(Range!T r, T[R] el)
in
{
assert(r.vector is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
{ {
return insertAfter!(T[])(r, el[]); return insertAfter!(T[])(r, el[]);
} }
@ -793,13 +822,14 @@ struct Vector(T)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in
{ {
assert(r.begin >= vector); assert(r.vector is &this);
assert(r.end <= vector + length_); assert(r.begin >= this.data);
assert(r.end <= this.data + length);
} }
body body
{ {
immutable oldLen = length; immutable oldLen = length;
immutable offset = r.end - vector; immutable offset = r.end - this.data;
static if (__traits(isRef, el)) static if (__traits(isRef, el))
{ {
@ -809,7 +839,7 @@ struct Vector(T)
{ {
moveBack(el); moveBack(el);
} }
bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
return 1; return 1;
} }
@ -821,16 +851,24 @@ struct Vector(T)
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in
{ {
assert(r.begin >= vector); assert(r.vector is &this);
assert(r.end <= vector + length_); assert(r.begin >= this.data);
assert(r.end <= this.data + length);
} }
body body
{ {
return insertAfter(Range!T(vector, r.begin), el); return insertAfter(Range!T(this, this.data, r.begin), el);
} }
/// Ditto. /// Ditto.
size_t insertBefore(size_t R)(Range!T r, T[R] el) size_t insertBefore(size_t R)(Range!T r, T[R] el)
in
{
assert(r.vector is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
{ {
return insertBefore!(T[])(r, el[]); return insertBefore!(T[])(r, el[]);
} }
@ -840,13 +878,14 @@ struct Vector(T)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in
{ {
assert(r.begin >= vector); assert(r.vector is &this);
assert(r.end <= vector + length_); assert(r.begin >= this.data);
assert(r.end <= this.data + length);
} }
body body
{ {
immutable oldLen = length; immutable oldLen = length;
immutable offset = r.begin - vector; immutable offset = r.begin - this.data;
static if (__traits(isRef, el)) static if (__traits(isRef, el))
{ {
@ -856,7 +895,7 @@ struct Vector(T)
{ {
moveBack(el); moveBack(el);
} }
bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
return 1; return 1;
} }
@ -942,7 +981,7 @@ struct Vector(T)
* *
* Returns: Assigned value. * Returns: Assigned value.
* *
* Precondition: $(D_INLINECODE length > pos) * Precondition: $(D_INLINECODE length > pos).
*/ */
ref T opIndexAssign(ref T value, const size_t pos) ref T opIndexAssign(ref T value, const size_t pos)
{ {
@ -983,7 +1022,7 @@ struct Vector(T)
* *
* Returns: Assigned value. * Returns: Assigned value.
* *
* Precondition: $(D_INLINECODE length == value.length) * Precondition: $(D_INLINECODE length == value.length).
*/ */
Range!T opIndexAssign(R)(R value) Range!T opIndexAssign(R)(R value)
if (!isInfinite!R && isInputRange!R if (!isInfinite!R && isInputRange!R
@ -1020,7 +1059,7 @@ struct Vector(T)
* *
* Returns: The value at a specified index. * Returns: The value at a specified index.
* *
* Precondition: $(D_INLINECODE length > pos) * Precondition: $(D_INLINECODE length > pos).
*/ */
ref inout(T) opIndex(const size_t pos) inout @trusted ref inout(T) opIndex(const size_t pos) inout @trusted
in in
@ -1029,7 +1068,7 @@ struct Vector(T)
} }
body body
{ {
return *(vector + pos); return *(this.data + pos);
} }
/** /**
@ -1038,13 +1077,13 @@ struct Vector(T)
*/ */
Range!T opIndex() @trusted Range!T opIndex() @trusted
{ {
return typeof(return)(vector, vector + length_); return typeof(return)(this, this.data, this.data + length);
} }
/// Ditto. /// Ditto.
Range!(const T) opIndex() const @trusted Range!(const T) opIndex() const @trusted
{ {
return typeof(return)(vector, vector + length_); return typeof(return)(this, this.data, this.data + length);
} }
/// ///
@ -1071,13 +1110,13 @@ struct Vector(T)
*/ */
bool opEquals()(auto ref typeof(this) that) @trusted bool opEquals()(auto ref typeof(this) that) @trusted
{ {
return equal(vector[0 .. length_], that.vector[0 .. that.length_]); return equal(this.data[0 .. length], that.data[0 .. that.length]);
} }
/// Ditto. /// Ditto.
bool opEquals()(const auto ref typeof(this) that) const @trusted bool opEquals()(const auto ref typeof(this) that) const @trusted
{ {
return equal(vector[0 .. length_], that.vector[0 .. that.length_]); return equal(this.data[0 .. length], that.data[0 .. that.length]);
} }
/// Ditto. /// Ditto.
@ -1122,124 +1161,10 @@ struct Vector(T)
assert(v1 == v2); assert(v1 == v2);
} }
/**
* $(D_KEYWORD foreach) iteration.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref T) @nogc dg)
{
T* end = vector + length_ - 1;
for (T* begin = vector; begin != end; ++begin)
{
int result = dg(*begin);
if (result != 0)
{
return result;
}
}
return 0;
}
/// Ditto.
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
for (size_t i = 0; i < length; ++i)
{
assert(i < length);
int result = dg(i, *(vector + i));
if (result != 0)
{
return result;
}
}
return 0;
}
/// Ditto.
int opApplyReverse(scope int delegate(ref T) dg)
{
for (T* end = vector + length - 1; vector != end; --end)
{
int result = dg(*end);
if (result != 0)
{
return result;
}
}
return 0;
}
/// Ditto.
int opApplyReverse(scope int delegate(ref size_t i, ref T) dg)
{
if (length > 0)
{
size_t i = length;
do
{
--i;
assert(i < length);
int result = dg(i, *(vector + i));
if (result != 0)
{
return result;
}
}
while (i > 0);
}
return 0;
}
///
unittest
{
auto v = Vector!int([5, 15, 8]);
size_t i;
foreach (j, ref e; v)
{
i = j;
}
assert(i == 2);
foreach (j, e; v)
{
assert(j != 0 || e == 5);
assert(j != 1 || e == 15);
assert(j != 2 || e == 8);
}
}
///
unittest
{
auto v = Vector!int([5, 15, 8]);
size_t i;
foreach_reverse (j, ref e; v)
{
i = j;
}
assert(i == 0);
foreach_reverse (j, e; v)
{
assert(j != 2 || e == 8);
assert(j != 1 || e == 15);
assert(j != 0 || e == 5);
}
}
/** /**
* Returns: The first element. * Returns: The first element.
* *
* Precondition: $(D_INLINECODE !empty) * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in
@ -1248,7 +1173,7 @@ struct Vector(T)
} }
body body
{ {
return *vector; return *this.data;
} }
/// ///
@ -1266,7 +1191,7 @@ struct Vector(T)
/** /**
* Returns: The last element. * Returns: The last element.
* *
* Precondition: $(D_INLINECODE !empty) * Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) back() inout @trusted @property ref inout(T) back() inout @trusted
in in
@ -1275,7 +1200,7 @@ struct Vector(T)
} }
body body
{ {
return *(vector + length_ - 1); return *(this.data + length - 1);
} }
/// ///
@ -1298,7 +1223,7 @@ struct Vector(T)
* Returns: A range that iterates over elements of the container from * Returns: A range that iterates over elements of the container from
* index $(D_PARAM i) up to (excluding) index $(D_PARAM j). * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
* *
* Precondition: $(D_INLINECODE i <= j && j <= length) * Precondition: $(D_INLINECODE i <= j && j <= length).
*/ */
Range!T opSlice(const size_t i, const size_t j) @trusted Range!T opSlice(const size_t i, const size_t j) @trusted
in in
@ -1308,7 +1233,7 @@ struct Vector(T)
} }
body body
{ {
return typeof(return)(vector + i, vector + j); return typeof(return)(this, this.data + i, this.data + j);
} }
/// Ditto. /// Ditto.
@ -1320,7 +1245,7 @@ struct Vector(T)
} }
body body
{ {
return typeof(return)(vector + i, vector + j); return typeof(return)(this, this.data + i, this.data + j);
} }
/// ///
@ -1394,7 +1319,7 @@ struct Vector(T)
} }
body body
{ {
copy(value, vector[i .. j]); copy(value, this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
} }
@ -1413,7 +1338,7 @@ struct Vector(T)
} }
body body
{ {
fill(vector[i .. j], value); fill(this.data[i .. j], value);
return opSlice(i, j); return opSlice(i, j);
} }
@ -1454,7 +1379,7 @@ struct Vector(T)
*/ */
inout(T[]) get() inout @trusted inout(T[]) get() inout @trusted
{ {
return vector[0 .. length]; return this.data[0 .. length];
} }
/// ///
@ -1498,7 +1423,7 @@ struct Vector(T)
ref typeof(this) opAssign(R)(R that) @trusted ref typeof(this) opAssign(R)(R that) @trusted
if (is(R == Vector)) if (is(R == Vector))
{ {
swap(this.vector, that.vector); swap(this.data, that.data);
swap(this.length_, that.length_); swap(this.length_, that.length_);
swap(this.capacity_, that.capacity_); swap(this.capacity_, that.capacity_);
swap(this.allocator_, that.allocator_); swap(this.allocator_, that.allocator_);
@ -1519,7 +1444,7 @@ struct Vector(T)
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
{ {
this.length = 0; length = 0;
insertBack(that); insertBack(that);
return this; return this;
} }
@ -1596,7 +1521,7 @@ unittest
const v1 = Vector!int(); const v1 = Vector!int();
const Vector!int v2; const Vector!int v2;
const v3 = Vector!int([1, 5, 8]); const v3 = Vector!int([1, 5, 8]);
static assert(is(PointerTarget!(typeof(v3.vector)) == const(int))); static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
} }
@nogc unittest @nogc unittest
@ -1691,7 +1616,7 @@ unittest
auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors. auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors.
} }
unittest private unittest
{ {
class A class A
{ {
@ -1699,3 +1624,32 @@ unittest
A a1, a2; A a1, a2;
auto v1 = Vector!A([a1, a2]); auto v1 = Vector!A([a1, a2]);
} }
private @safe @nogc unittest
{
auto v = Vector!int([5, 15, 8]);
{
size_t i;
foreach (e; v)
{
assert(i != 0 || e == 5);
assert(i != 1 || e == 15);
assert(i != 2 || e == 8);
++i;
}
assert(i == 3);
}
{
size_t i = 3;
foreach_reverse (e; v)
{
--i;
assert(i != 2 || e == 8);
assert(i != 1 || e == 15);
assert(i != 0 || e == 5);
}
assert(i == 0);
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -14,9 +14,63 @@ import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import std.conv;
import std.range;
import std.traits; import std.traits;
import tanya.memory; import tanya.memory;
package(tanya) final class RefCountedStore(T)
{
T payload;
size_t counter = 1;
size_t opUnary(string op)()
if (op == "--" || op == "++")
in
{
assert(counter > 0);
}
body
{
mixin("return " ~ op ~ "counter;");
}
int opCmp(size_t counter)
{
if (this.counter > counter)
{
return 1;
}
else if (this.counter < counter)
{
return -1;
}
else
{
return 0;
}
}
int opEquals(size_t counter)
{
return this.counter == counter;
}
}
private void separateDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
private void unifiedDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
auto ptr1 = finalize(storage);
auto ptr2 = finalize(storage.payload);
allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
}
/** /**
* Reference-counted object containing a $(D_PARAM T) value as payload. * Reference-counted object containing a $(D_PARAM T) value as payload.
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
@ -27,7 +81,7 @@ import tanya.memory;
*/ */
struct RefCounted(T) struct RefCounted(T)
{ {
static if (is(T == class) || is(T == interface)) static if (is(T == class) || is(T == interface) || isArray!T)
{ {
private alias Payload = T; private alias Payload = T;
} }
@ -35,70 +89,16 @@ struct RefCounted(T)
{ {
private alias Payload = T*; private alias Payload = T*;
} }
private alias Storage = RefCountedStore!Payload;
private class Storage
{
private Payload payload;
private size_t counter = 1;
private final size_t opUnary(string op)() pure nothrow @safe @nogc
if (op == "--" || op == "++")
in
{
assert(counter > 0);
}
body
{
mixin("return " ~ op ~ "counter;");
}
private final int opCmp(size_t counter) const pure nothrow @safe @nogc
{
if (this.counter > counter)
{
return 1;
}
else if (this.counter < counter)
{
return -1;
}
else
{
return 0;
}
}
private final int opEquals(size_t counter) const pure nothrow @safe @nogc
{
return this.counter == counter;
}
}
private final class RefCountedStorage : Storage
{
private shared Allocator allocator;
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
}
body
{
this.allocator = allocator;
}
~this() nothrow @nogc
{
allocator.dispose(payload);
}
}
private Storage storage; private Storage storage;
private void function(Storage storage,
shared Allocator allocator) @nogc deleter;
invariant invariant
{ {
assert(storage is null || allocator_ !is null); assert(storage is null || allocator_ !is null);
assert(storage is null || deleter !is null);
} }
/** /**
@ -112,11 +112,13 @@ struct RefCounted(T)
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(Payload value, shared Allocator allocator = defaultAllocator) this()(auto ref Payload value,
shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
move(value, storage.payload); this.deleter = &separateDeleter!Payload;
move(value, this.storage.payload);
} }
/// Ditto. /// Ditto.
@ -148,9 +150,9 @@ struct RefCounted(T)
*/ */
~this() ~this()
{ {
if (storage !is null && !(storage.counter && --storage)) if (this.storage !is null && !(this.storage.counter && --this.storage))
{ {
allocator_.dispose(storage); deleter(storage, allocator);
} }
} }
@ -170,54 +172,48 @@ struct RefCounted(T)
* Params: * Params:
* rhs = $(D_KEYWORD this). * rhs = $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(Payload rhs) ref typeof(this) opAssign()(auto ref Payload rhs)
{ {
if (storage is null) if (this.storage is null)
{ {
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
this.deleter = &separateDeleter!Payload;
} }
else if (storage > 1) else if (this.storage > 1)
{ {
--storage; --this.storage;
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
} this.deleter = &separateDeleter!Payload;
else if (cast(RefCountedStorage) storage is null)
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0);
allocator.dispose(storage);
storage = allocator.make!RefCountedStorage(allocator);
} }
else else
{ {
allocator.dispose(storage.payload); // Created with refCounted. Always destroyed togethter with the pointer.
assert(this.storage.counter != 0);
finalize(this.storage.payload);
this.storage.payload = Payload.init;
} }
move(rhs, storage.payload); move(rhs, this.storage.payload);
return this; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
if (storage is null) if (this.storage is null)
{ {
return this; return this;
} }
else if (storage > 1) else if (this.storage > 1)
{ {
--storage; --this.storage;
storage = null; this.storage = null;
}
else if (cast(RefCountedStorage) storage is null)
{
// Created with refCounted. Always destroyed togethter with the pointer.
assert(storage.counter != 0);
allocator.dispose(storage);
return this;
} }
else else
{ {
allocator.dispose(storage.payload); // Created with refCounted. Always destroyed togethter with the pointer.
assert(this.storage.counter != 0);
finalize(this.storage.payload);
this.storage.payload = Payload.init;
} }
return this; return this;
} }
@ -225,8 +221,9 @@ struct RefCounted(T)
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(allocator_, rhs.allocator_); swap(this.allocator_, rhs.allocator_);
swap(storage, rhs.storage); swap(this.storage, rhs.storage);
swap(this.deleter, rhs.deleter);
return this; return this;
} }
@ -380,15 +377,22 @@ private unittest
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/ */
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T if (!is(T == interface) && !isAbstractClass!T
&& !isArray!T && !isAssociativeArray!T) && !isAssociativeArray!T && !isArray!T)
in
{
assert(allocator !is null);
}
body
{ {
auto rc = typeof(return)(allocator); auto rc = typeof(return)(allocator);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize); const size = alignedSize(stateSize!T + storageSize);
auto mem = (() @trusted => allocator.allocate(size))(); auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
@ -399,7 +403,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
static if (is(T == class)) static if (is(T == class))
{ {
@ -410,9 +414,38 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args); rc.storage.payload = emplace!T(ptr, args);
} }
rc.deleter = &unifiedDeleter!(RefCounted!T.Payload);
return rc; return rc;
} }
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
* $(D_PSYMBOL RefCounted) using.
*
* Params:
* T = Array type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
*/
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
{
auto payload = cast(T) allocator.allocate(ElementType!T.sizeof * size);
initializeAll(payload);
return RefCounted!T(payload, allocator);
}
/// ///
unittest unittest
{ {
@ -456,3 +489,9 @@ private @nogc unittest
assert(rc.count); assert(rc.count);
} }
} }
private @nogc unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
}

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

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff