Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
65c3ca14ec | |||
4fa47153ba | |||
628153e2e8 | |||
7aa9ac9f4a | |||
8156d0fe3a | |||
6e2ce5d686 | |||
ba6bf554fb | |||
b1d2b9bd9e | |||
9b953198fa | |||
bc2a6d2703 | |||
b458250ad7 | |||
b08d5e5d83 | |||
445b872e91 | |||
5e16fe98d6 |
@ -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:
|
||||||
|
21
README.md
21
README.md
@ -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 | [](https://travis-ci.org/caraus-ecms/tanya) |
|
||||||
|
| 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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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))
|
|
||||||
{
|
|
||||||
reserve(length + 1);
|
|
||||||
emplace(vector + length_, el);
|
|
||||||
++length_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
moveBack(el);
|
moveBack(el);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
size_t insertBack(R)(ref R el) @trusted
|
||||||
|
if (isImplicitlyConvertible!(R, T))
|
||||||
|
{
|
||||||
|
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
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,33 +203,25 @@ 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))
|
||||||
{
|
{
|
||||||
@ -245,18 +238,12 @@ 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
|
||||||
{
|
{
|
||||||
|
@ -14,34 +14,16 @@ 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)
|
||||||
* Reference-counted object containing a $(D_PARAM T) value as payload.
|
|
||||||
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
|
|
||||||
* when the reference count goes down to zero, frees the underlying store.
|
|
||||||
*
|
|
||||||
* Params:
|
|
||||||
* T = Type of the reference-counted value.
|
|
||||||
*/
|
|
||||||
struct RefCounted(T)
|
|
||||||
{
|
{
|
||||||
static if (is(T == class) || is(T == interface))
|
T payload;
|
||||||
{
|
size_t counter = 1;
|
||||||
private alias Payload = T;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
private alias Payload = T*;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Storage
|
size_t opUnary(string op)()
|
||||||
{
|
|
||||||
private Payload payload;
|
|
||||||
private size_t counter = 1;
|
|
||||||
|
|
||||||
private final size_t opUnary(string op)() pure nothrow @safe @nogc
|
|
||||||
if (op == "--" || op == "++")
|
if (op == "--" || op == "++")
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -52,7 +34,7 @@ struct RefCounted(T)
|
|||||||
mixin("return " ~ op ~ "counter;");
|
mixin("return " ~ op ~ "counter;");
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int opCmp(size_t counter) const pure nothrow @safe @nogc
|
int opCmp(size_t counter)
|
||||||
{
|
{
|
||||||
if (this.counter > counter)
|
if (this.counter > counter)
|
||||||
{
|
{
|
||||||
@ -68,37 +50,55 @@ struct RefCounted(T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int opEquals(size_t counter) const pure nothrow @safe @nogc
|
int opEquals(size_t counter)
|
||||||
{
|
{
|
||||||
return this.counter == counter;
|
return this.counter == counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class RefCountedStorage : Storage
|
private void separateDeleter(T)(RefCountedStore!T storage,
|
||||||
|
shared Allocator allocator)
|
||||||
{
|
{
|
||||||
private shared Allocator allocator;
|
allocator.dispose(storage.payload);
|
||||||
|
allocator.dispose(storage);
|
||||||
this(shared Allocator allocator) pure nothrow @safe @nogc
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(allocator !is null);
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
this.allocator = allocator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() nothrow @nogc
|
private void unifiedDeleter(T)(RefCountedStore!T storage,
|
||||||
|
shared Allocator allocator)
|
||||||
{
|
{
|
||||||
allocator.dispose(payload);
|
auto ptr1 = finalize(storage);
|
||||||
|
auto ptr2 = finalize(storage.payload);
|
||||||
|
allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference-counted object containing a $(D_PARAM T) value as payload.
|
||||||
|
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
|
||||||
|
* when the reference count goes down to zero, frees the underlying store.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Type of the reference-counted value.
|
||||||
|
*/
|
||||||
|
struct RefCounted(T)
|
||||||
|
{
|
||||||
|
static if (is(T == class) || is(T == interface) || isArray!T)
|
||||||
|
{
|
||||||
|
private alias Payload = T;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
private alias Payload = T*;
|
||||||
|
}
|
||||||
|
private alias Storage = RefCountedStore!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
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;
|
@ -3,7 +3,9 @@
|
|||||||
* 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.
|
* Socket programming.
|
||||||
|
*
|
||||||
|
* Copyright: Eugene Wissner 2016-2017.
|
||||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||||
* Mozilla Public License, v. 2.0).
|
* Mozilla Public License, v. 2.0).
|
||||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||||
@ -1004,10 +1006,14 @@ interface ConnectionOrientedSocket
|
|||||||
*/
|
*/
|
||||||
enum Flag : int
|
enum Flag : int
|
||||||
{
|
{
|
||||||
none = 0, /// No flags specified
|
/// No flags specified.
|
||||||
outOfBand = MSG_OOB, /// Out-of-band stream data
|
none = 0,
|
||||||
peek = MSG_PEEK, /// Peek at incoming data without removing it from the queue, only for receiving
|
/// Out-of-band stream data.
|
||||||
dontRoute = MSG_DONTROUTE, /// Data should not be subject to routing; this flag may be ignored. Only for sending
|
outOfBand = MSG_OOB,
|
||||||
|
/// Peek at incoming data without removing it from the queue, only for receiving.
|
||||||
|
peek = MSG_PEEK,
|
||||||
|
/// Data should not be subject to routing; this flag may be ignored. Only for sending.
|
||||||
|
dontRoute = MSG_DONTROUTE,
|
||||||
}
|
}
|
||||||
|
|
||||||
alias Flags = BitFlags!Flag;
|
alias Flags = BitFlags!Flag;
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
* 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.
|
* URL parser.
|
||||||
|
*
|
||||||
|
* 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.network.url;
|
module tanya.network.url;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user