6 Commits

14 changed files with 257 additions and 211 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@
.dub .dub
__test__*__ __test__*__
__test__*__.core __test__*__.core
/docs/
/docs.json

View File

@ -5,8 +5,7 @@
[![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya) [![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya)
[![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE) [![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE)
Tanya is a general purpose library for D programming language that doesn't Tanya is a general purpose library for D programming language.
rely on the Garbage Collector.
Its aim is to simplify the manual memory management in D and to provide a Its aim is to simplify the manual memory management in D and to provide a
guarantee with @nogc attribute that there are no hidden allocations on the guarantee with @nogc attribute that there are no hidden allocations on the
@ -36,12 +35,12 @@ helper functions).
The library is currently under development, but some parts of it can already be The library is currently under development, but some parts of it can already be
used. used.
`network` and `async` exist for quite some time and could be better tested than
other components.
Containers were newly reworked and the API won't change significantly, but will Containers were newly reworked and the API won't change significantly, but will
be only extended. The same is true for the `memory` package. be only extended. The same is true for the `memory` package.
`network` and `async` packages should be reviewed in the future and the API may
change.
`math` package contains an arbitrary precision integer implementation that has `math` package contains an arbitrary precision integer implementation that has
a stable API (that mostly consists of operator overloads), but still needs a stable API (that mostly consists of operator overloads), but still needs
testing and work on its performance. testing and work on its performance.

View File

@ -159,7 +159,7 @@ class EpollLoop : SelectorLoop
} }
else if (io.output.length) else if (io.output.length)
{ {
swapPendings.insertBack(io); swapPendings.enqueue(io);
} }
} }
else if (events[i].events & EPOLLOUT) else if (events[i].events & EPOLLOUT)

View File

@ -218,11 +218,11 @@ class IOCPLoop : Loop
auto transport = MmapPool.instance.make!IOCPStreamTransport(socket); auto transport = MmapPool.instance.make!IOCPStreamTransport(socket);
auto io = MmapPool.instance.make!IOWatcher(transport, connection.protocol); auto io = MmapPool.instance.make!IOWatcher(transport, connection.protocol);
connection.incoming.insertBack(io); connection.incoming.enqueue(io);
reify(io, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(io, EventMask(Event.none), EventMask(Event.read, Event.write));
swapPendings.insertBack(connection); swapPendings.enqueue(connection);
listener.beginAccept(overlapped); listener.beginAccept(overlapped);
break; break;
case OverlappedSocketEvent.read: case OverlappedSocketEvent.read:
@ -264,7 +264,7 @@ class IOCPLoop : Loop
{ {
transport.socket.beginReceive(io.output[], overlapped); transport.socket.beginReceive(io.output[], overlapped);
} }
swapPendings.insertBack(io); swapPendings.enqueue(io);
} }
break; break;
case OverlappedSocketEvent.write: case OverlappedSocketEvent.write:

View File

@ -271,7 +271,7 @@ class KqueueLoop : SelectorLoop
} }
else if (io.output.length) else if (io.output.length)
{ {
swapPendings.insertBack(io); swapPendings.enqueue(io);
} }
} }
else if (events[i].filter == EVFILT_WRITE) else if (events[i].filter == EVFILT_WRITE)

View File

@ -248,12 +248,12 @@ abstract class SelectorLoop : Loop
} }
reify(io, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(io, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.insertBack(io); connection.incoming.enqueue(io);
} }
if (!connection.incoming.empty) if (!connection.incoming.empty)
{ {
swapPendings.insertBack(connection); swapPendings.enqueue(connection);
} }
} }
} }

View File

@ -184,8 +184,10 @@ abstract class Loop
poll(); poll();
// Invoke pendings // Invoke pendings
swapPendings.each!((ref p) @nogc => p.invoke()); foreach (ref w; *swapPendings)
{
w.invoke();
}
swap(pendings, swapPendings); swap(pendings, swapPendings);
} }
while (!done_); while (!done_);
@ -283,7 +285,7 @@ abstract class Loop
defaultAllocator.dispose(watcher.socket); defaultAllocator.dispose(watcher.socket);
MmapPool.instance.dispose(watcher.transport); MmapPool.instance.dispose(watcher.transport);
watcher.exception = exception; watcher.exception = exception;
swapPendings.insertBack(watcher); swapPendings.enqueue(watcher);
} }
/** /**

View File

@ -632,7 +632,7 @@ struct WriteBuffer(T = ubyte)
* written. * written.
* *
* $(D_PSYMBOL opIndex) may return only part of the data. You may need * $(D_PSYMBOL opIndex) may return only part of the data. You may need
* to call it (and set $(D_KEYWORD +=) several times until * to call it and set $(D_KEYWORD +=) several times until
* $(D_PSYMBOL length) is 0. If all the data can be written, * $(D_PSYMBOL length) is 0. If all the data can be written,
* maximally 3 calls are required. * maximally 3 calls are required.
* *
@ -677,7 +677,7 @@ struct WriteBuffer(T = ubyte)
* written. * written.
* *
* $(D_PSYMBOL opIndex) may return only part of the data. You may need * $(D_PSYMBOL opIndex) may return only part of the data. You may need
* to call it (and set $(D_KEYWORD +=) several times until * to call it and set $(D_KEYWORD +=) several times until
* $(D_PSYMBOL length) is 0. If all the data can be written, * $(D_PSYMBOL length) is 0. If all the data can be written,
* maximally 3 calls are required. * maximally 3 calls are required.
* *

View File

@ -10,8 +10,9 @@
*/ */
module tanya.container.queue; module tanya.container.queue;
import tanya.container.entry;
import std.traits; import std.traits;
import std.algorithm.mutation;
import tanya.container.entry;
import tanya.memory; import tanya.memory;
/** /**
@ -27,32 +28,24 @@ struct Queue(T)
*/ */
~this() ~this()
{ {
clear(); while (!empty)
{
dequeue();
}
} }
/** /**
* Removes all elements from the queue. * Removes all elements from the queue.
*/ */
deprecated
void clear() void clear()
{ {
while (!empty) while (!empty)
{ {
popFront(); dequeue();
} }
} }
///
unittest
{
Queue!int q;
assert(q.empty);
q.insertBack(8);
q.insertBack(9);
q.clear();
assert(q.empty);
}
/** /**
* Returns how many elements are in the queue. It iterates through the queue * Returns how many elements are in the queue. It iterates through the queue
* to count the elements. * to count the elements.
@ -75,18 +68,18 @@ struct Queue(T)
Queue!int q; Queue!int q;
assert(q.length == 0); assert(q.length == 0);
q.insertBack(5); q.enqueue(5);
assert(q.length == 1); assert(q.length == 1);
q.insertBack(4); q.enqueue(4);
assert(q.length == 2); assert(q.length == 2);
q.insertBack(9); q.enqueue(9);
assert(q.length == 3); assert(q.length == 3);
q.popFront(); q.dequeue();
assert(q.length == 2); assert(q.length == 2);
q.popFront(); q.dequeue();
assert(q.length == 1); assert(q.length == 1);
q.popFront(); q.dequeue();
assert(q.length == 0); assert(q.length == 0);
} }
@ -97,14 +90,17 @@ struct Queue(T)
* *
* Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal. * Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal.
*/ */
deprecated
int opEquals(ref typeof(this) that); int opEquals(ref typeof(this) that);
/// Ditto. /// Ditto.
deprecated
int opEquals(typeof(this) that); int opEquals(typeof(this) that);
} }
else static if (!hasMember!(T, "opEquals") else static if (!hasMember!(T, "opEquals")
|| (functionAttributes!(T.opEquals) & FunctionAttribute.const_)) || (functionAttributes!(T.opEquals) & FunctionAttribute.const_))
{ {
deprecated
bool opEquals(in ref typeof(this) that) const bool opEquals(in ref typeof(this) that) const
{ {
const(Entry!T)* i = first.next; const(Entry!T)* i = first.next;
@ -121,7 +117,7 @@ struct Queue(T)
return i is null && j is null; return i is null && j is null;
} }
/// Ditto. deprecated
bool opEquals(in typeof(this) that) const bool opEquals(in typeof(this) that) const
{ {
return opEquals(that); return opEquals(that);
@ -129,11 +125,7 @@ struct Queue(T)
} }
else else
{ {
/** deprecated
* Compares two queues. Checks if all elements of the both queues are equal.
*
* Returns: How many elements are in the queue.
*/
bool opEquals(ref typeof(this) that) bool opEquals(ref typeof(this) that)
{ {
Entry!T* i = first.next; Entry!T* i = first.next;
@ -150,46 +142,17 @@ struct Queue(T)
return i is null && j is null; return i is null && j is null;
} }
/// Ditto. deprecated
bool opEquals(typeof(this) that) bool opEquals(typeof(this) that)
{ {
return opEquals(that); return opEquals(that);
} }
} }
///
unittest
{
Queue!int q1, q2;
q1.insertBack(5);
q1.insertBack(4);
q2.insertBack(5);
assert(q1 != q2);
q2.insertBack(4);
assert(q1 == q2);
q2.popFront();
assert(q1 != q2);
q1.popFront();
assert(q1 == q2);
q1.popFront();
q2.popFront();
assert(q1 == q2);
}
private unittest
{
static assert(is(Queue!ConstEqualsStruct));
static assert(is(Queue!MutableEqualsStruct));
static assert(is(Queue!NoEqualsStruct));
}
/** /**
* Returns: First element. * Returns: First element.
*/ */
deprecated("Use dequeue instead.")
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in
{ {
@ -205,13 +168,12 @@ struct Queue(T)
* *
* Params: * Params:
* x = New element. * x = New element.
*
* Returns: $(D_KEYWORD this).
*/ */
void insertBack(ref T x) ref typeof(this) enqueue(ref T x)
{ {
auto temp = allocator.make!(Entry!T); auto temp = allocator.make!(Entry!T)(x);
temp.content = x;
if (empty) if (empty)
{ {
first.next = rear = temp; first.next = rear = temp;
@ -221,16 +183,20 @@ struct Queue(T)
rear.next = temp; rear.next = temp;
rear = rear.next; rear = rear.next;
} }
return this;
} }
/// Ditto. /// Ditto.
void insertBack(T x) ref typeof(this) enqueue(T x)
{ {
insertBack(x); return enqueue(x);
} }
/// Ditto. deprecated("Use enqueue instead.")
alias insert = insertBack; alias insert = enqueue;
deprecated("Use enqueue instead.")
alias insertBack = enqueue;
/// ///
unittest unittest
@ -238,10 +204,9 @@ struct Queue(T)
Queue!int q; Queue!int q;
assert(q.empty); assert(q.empty);
q.insertBack(8); q.enqueue(8).enqueue(9);
assert(q.front == 8); assert(q.dequeue() == 8);
q.insertBack(9); assert(q.dequeue() == 9);
assert(q.front == 8);
} }
/** /**
@ -259,14 +224,16 @@ struct Queue(T)
int value = 7; int value = 7;
assert(q.empty); assert(q.empty);
q.insertBack(value); q.enqueue(value);
assert(!q.empty); assert(!q.empty);
} }
/** /**
* Move the position to the next element. * Move the position to the next element.
*
* Returns: Dequeued element.
*/ */
void popFront() T dequeue()
in in
{ {
assert(!empty); assert(!empty);
@ -275,21 +242,24 @@ struct Queue(T)
body body
{ {
auto n = first.next.next; auto n = first.next.next;
T ret = move(first.next.content);
dispose(allocator, first.next); dispose(allocator, first.next);
first.next = n; first.next = n;
return ret;
} }
deprecated("Use dequeue instead.")
alias popFront = dequeue;
/// ///
unittest unittest
{ {
Queue!int q; Queue!int q;
q.insertBack(8); q.enqueue(8).enqueue(9);
q.insertBack(9); assert(q.dequeue() == 8);
assert(q.front == 8); assert(q.dequeue() == 9);
q.popFront();
assert(q.front == 9);
} }
/** /**
@ -305,11 +275,11 @@ struct Queue(T)
for (size_t i = 0; !empty; ++i) for (size_t i = 0; !empty; ++i)
{ {
if ((result = dg(i, front)) != 0) auto e = dequeue();
if ((result = dg(i, e)) != 0)
{ {
return result; return result;
} }
popFront();
} }
return result; return result;
} }
@ -321,11 +291,11 @@ struct Queue(T)
while (!empty) while (!empty)
{ {
if ((result = dg(front)) != 0) auto e = dequeue();
if ((result = dg(e)) != 0)
{ {
return result; return result;
} }
popFront();
} }
return result; return result;
} }
@ -336,9 +306,7 @@ struct Queue(T)
Queue!int q; Queue!int q;
size_t j; size_t j;
q.insertBack(5); q.enqueue(5).enqueue(4).enqueue(9);
q.insertBack(4);
q.insertBack(9);
foreach (i, e; q) foreach (i, e; q)
{ {
assert(i != 2 || e == 9); assert(i != 2 || e == 9);
@ -350,9 +318,7 @@ struct Queue(T)
assert(q.empty); assert(q.empty);
j = 0; j = 0;
q.insertBack(5); q.enqueue(5).enqueue(4).enqueue(9);
q.insertBack(4);
q.insertBack(9);
foreach (e; q) foreach (e; q)
{ {
assert(j != 2 || e == 9); assert(j != 2 || e == 9);
@ -378,17 +344,12 @@ unittest
{ {
Queue!int q; Queue!int q;
q.insertBack(5); q.enqueue(5);
assert(!q.empty); assert(!q.empty);
q.insertBack(4); q.enqueue(4).enqueue(9);
assert(q.front == 5);
q.insertBack(9); assert(q.dequeue() == 5);
assert(q.front == 5);
q.popFront();
assert(q.front == 4);
foreach (i, ref e; q) foreach (i, ref e; q)
{ {

View File

@ -10,9 +10,10 @@
*/ */
module tanya.container.vector; module tanya.container.vector;
import core.stdc.string; import core.checkedint;
import core.exception; import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv; import std.conv;
import std.range.primitives; import std.range.primitives;
import std.meta; import std.meta;
@ -271,8 +272,7 @@ private struct Range(E)
} }
Range opSliceAssign(R)(R value, in size_t i, in size_t j) Range opSliceAssign(R)(R value, in size_t i, in size_t j)
if ((isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T)) if (isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T))
|| is(R == Vector))
{ {
return opSliceAssign(value[], i, j); return opSliceAssign(value[], i, j);
} }
@ -297,30 +297,6 @@ struct Vector(T)
assert(capacity_ == 0 || vector !is null); assert(capacity_ == 0 || vector !is null);
} }
// Reserves memory to store len objects and initializes it.
// Doesn't change the length.
private void initialize(in size_t len)
{
reserve(len);
if (capacity_ < len)
{
onOutOfMemoryError();
}
const init = typeid(T).initializer();
if (init.ptr)
{
const T* end = vector + len;
for (void* v = vector + length_; v != end; v += init.length)
{
memcpy(v, init.ptr, init.length);
}
}
else
{
memset(vector + length_, 0, (len - length_) * T.sizeof);
}
}
/** /**
* Creates a new $(D_PSYMBOL Vector). * Creates a new $(D_PSYMBOL Vector).
* *
@ -330,16 +306,15 @@ struct Vector(T)
* to generate a list. * to generate a list.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(R)(auto ref R init, shared Allocator allocator = defaultAllocator) this(R)(auto in ref R init, shared Allocator allocator = defaultAllocator)
if ((isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T)) if (isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T))
|| is(R == Vector))
{ {
this(allocator); this(allocator);
insertBack(init[]); insertBack(init[]);
} }
/// Ditto. /// Ditto.
this(R)(R init, shared Allocator allocator = defaultAllocator) this(R)(auto in ref R init, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
@ -348,12 +323,78 @@ struct Vector(T)
insertBack(init); insertBack(init);
} }
/**
* Initializes this vector from another one.
*
* If $(D_PARAM init) is passed by value, it won't be copied, but moved
* If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
* $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
* storage, otherwise, the storage will be allocated with
* $(D_PARAM allocator) and all elements will be moved;
* $(D_PARAM init) will be destroyed at the end.
*
* If $(D_PARAM init) is passed by reference, it will be copied.
*
* Params:
* init = Source vector.
* allocator = Allocator.
*/
this(ref Vector init, shared Allocator allocator = defaultAllocator) @trusted
{
this(allocator);
insertBack(init[]);
}
/// Ditto.
this(Vector init, shared Allocator allocator = defaultAllocator) @trusted
{
if (allocator is init.allocator)
{
// Just steal all references and the allocator.
this(init.allocator);
vector = init.vector;
length_ = init.length_;
capacity_ = init.capacity_;
// Reset the source vector, so it can't destroy the moved storage.
init.length_ = init.capacity_ = 0;
init.vector = null;
}
else
{
// Move each element.
this(allocator);
reserve(init.length);
const T* end = vector + init.length;
for (T* src = init.vector, dest = vector; dest != end; ++src, ++dest)
{
moveEmplace(*src, *dest);
}
length_ = init.length;
// Destructor of init should destroy it here.
}
}
///
@nogc @safe unittest
{
auto v1 = Vector!int(IL(1, 2, 3));
auto v2 = Vector!int(v1);
assert(v1.vector !is v2.vector);
assert(v1 == v2);
auto v3 = Vector!int(Vector!int(IL(1, 2, 3)));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
/** /**
* Creates a new $(D_PSYMBOL Vector). * Creates a new $(D_PSYMBOL Vector).
* *
* Params: * Params:
* len = Initial length of the vector. * len = Initial length of the vector.
* init = Initial value to fill the vector with.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(size_t len, shared Allocator allocator = defaultAllocator) @trusted this(size_t len, shared Allocator allocator = defaultAllocator) @trusted
@ -365,11 +406,19 @@ struct Vector(T)
{ {
return; return;
} }
initialize(len); reserve(len);
capacity_ = length_ = len; initializeAll(vector[0 .. len]);
length_ = len;
} }
/// Ditto. /**
* Creates a new $(D_PSYMBOL Vector).
*
* Params:
* len = Initial length of the vector.
* init = Initial value to fill the vector with.
* allocator = Allocator.
*/
this(size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted this(size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
{ {
this(allocator); this(allocator);
@ -379,9 +428,9 @@ struct Vector(T)
{ {
return; return;
} }
initialize(len); reserve(len);
capacity_ = length_ = len; uninitializedFill(vector[0 .. len], init);
opSliceAssign(init, 0, length_); length_ = len;
} }
/// Ditto. /// Ditto.
@ -415,6 +464,11 @@ struct Vector(T)
assert(v[0] == 5 && v[1] == 5 && v[2] == 5); assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
} }
@safe unittest
{
auto v1 = Vector!int(defaultAllocator);
}
/** /**
* Destroys this $(D_PSYMBOL Vector). * Destroys this $(D_PSYMBOL Vector).
*/ */
@ -497,7 +551,8 @@ struct Vector(T)
} }
else if (len > length_) else if (len > length_)
{ {
initialize(len); reserve(len);
initializeAll(vector[length_ .. len]);
} }
else else
{ {
@ -543,13 +598,39 @@ struct Vector(T)
*/ */
void reserve(in size_t size) @trusted void reserve(in size_t size) @trusted
{ {
if (capacity_ < size) if (capacity_ >= size)
{ {
void[] buf = vector[0 .. capacity_]; return;
allocator.reallocate(buf, size * T.sizeof);
vector = cast(T*) buf;
capacity_ = size;
} }
bool overflow;
immutable byteSize = mulu(size, T.sizeof, overflow);
assert(!overflow);
void[] buf = vector[0 .. capacity_];
if (!allocator.expand(buf, byteSize))
{
buf = allocator.allocate(byteSize);
if (buf is null)
{
onOutOfMemoryErrorNoGC();
}
scope (failure)
{
allocator.deallocate(buf);
}
const T* end = vector + length_;
for (T* src = vector, dest = cast(T*) buf; src != end; ++src, ++dest)
{
moveEmplace(*src, *dest);
static if (hasElaborateDestructor!T)
{
destroy(*src);
}
}
allocator.deallocate(vector[0 .. capacity_]);
vector = cast(T*) buf;
}
capacity_ = size;
} }
/// ///
@ -712,10 +793,6 @@ struct Vector(T)
if (allSatisfy!(ApplyRight!(isImplicitlyConvertible, T), R)) if (allSatisfy!(ApplyRight!(isImplicitlyConvertible, T), R))
{ {
reserve(length_ + el.length); reserve(length_ + el.length);
if (capacity_ <= length_)
{
onOutOfMemoryError();
}
foreach (i; el) foreach (i; el)
{ {
emplace(vector + length_, i); emplace(vector + length_, i);
@ -732,11 +809,11 @@ struct Vector(T)
{ {
immutable rLen = walkLength(el); immutable rLen = walkLength(el);
initialize(length_ + rLen); reserve(length_ + rLen);
T* pos = vector + length_; T* pos = vector + length_;
foreach (e; el) foreach (e; el)
{ {
*pos = e; emplace(pos, e);
++length_, ++pos; ++length_, ++pos;
} }
return rLen; return rLen;
@ -892,7 +969,6 @@ struct Vector(T)
* Comparison for equality. * Comparison for equality.
* *
* Params: * Params:
* R = Right hand side type.
* v = The vector to compare with. * v = The vector to compare with.
* *
* Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
@ -952,7 +1028,16 @@ struct Vector(T)
return true; return true;
} }
/// Ditto. /**
* Comparison for equality.
*
* Params:
* R = Right hand side type.
* v = The vector to compare with.
*
* Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
* otherwise.
*/
bool opEquals(R)(Range!R v) const @trusted bool opEquals(R)(Range!R v) const @trusted
if (is(Unqual!R == T)) if (is(Unqual!R == T))
{ {
@ -1239,14 +1324,14 @@ struct Vector(T)
* Slicing assignment. * Slicing assignment.
* *
* Params: * Params:
* value = New value. * value = New value (single value or input range).
* i = Slice start. * i = Slice start.
* j = Slice end. * j = Slice end.
* *
* Returns: Slice with the assigned part of the vector. * Returns: Slice with the assigned part of the vector.
* *
* Precondition: $(D_INLINECODE i <= j && j <= length); * Precondition: $(D_INLINECODE i <= j && j <= length);
* The lenghts of the ranges and slices match. * The lenghts of the range and slice match.
*/ */
Range!T opSliceAssign(ref T value, in size_t i, in size_t j) @trusted Range!T opSliceAssign(ref T value, in size_t i, in size_t j) @trusted
in in
@ -1256,11 +1341,7 @@ struct Vector(T)
} }
body body
{ {
const T* end = vector + j; vector[i .. j].fill(value);
for (T* v = vector + i; v != end; ++v)
{
*v = value;
}
return opSlice(i, j); return opSlice(i, j);
} }
@ -1277,6 +1358,8 @@ struct Vector(T)
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in
{ {
assert(i <= j);
assert(j <= length);
assert(j - i == walkLength(value)); assert(j - i == walkLength(value));
} }
body body
@ -1291,8 +1374,7 @@ struct Vector(T)
/// Ditto. /// Ditto.
Range!T opSliceAssign(R)(R value, in size_t i, in size_t j) Range!T opSliceAssign(R)(R value, in size_t i, in size_t j)
if ((isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T)) if (isStaticArray!R && isImplicitlyConvertible!(ElementType!R, T))
|| is(R == Vector))
{ {
return opSliceAssign(value[], i, j); return opSliceAssign(value[], i, j);
} }

View File

@ -36,7 +36,7 @@ else version (Windows)
* block as free and only if all blocks in the region are free, the complete * block as free and only if all blocks in the region are free, the complete
* region is deallocated. * region is deallocated.
* *
* ---------------------------------------------------------------------------- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* | | | | | || | | | * | | | | | || | | |
* | |prev <----------- | || | | | * | |prev <----------- | || | | |
* | R | B | | B | || R | B | | * | R | B | | B | || R | B | |
@ -46,7 +46,7 @@ else version (Windows)
* | O | K | | K | prev O | K | | * | O | K | | K | prev O | K | |
* | N | -----------> next| || N | | | * | N | -----------> next| || N | | |
* | | | | | || | | | * | | | | | || | | |
* --------------------------------------------------- ------------------------ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/ */
final class MmapPool : Allocator final class MmapPool : Allocator
{ {

View File

@ -376,6 +376,7 @@ private unittest
* Params: * Params:
* T = Type of the constructed object. * T = Type of the constructed object.
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* 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).

View File

@ -812,7 +812,7 @@ abstract class Socket
* Params: * Params:
* level = Protocol level at that the option exists. * level = Protocol level at that the option exists.
* option = Option. * option = Option.
* result = Option value. * value = Option value.
* *
* Throws: $(D_PSYMBOL SocketException) on error. * Throws: $(D_PSYMBOL SocketException) on error.
*/ */

View File

@ -630,32 +630,31 @@ static this()
/** /**
* A Unique Resource Locator. * A Unique Resource Locator.
*/ */
struct URL(U = string) struct URL
if (isSomeString!U)
{ {
/** The URL scheme. */ /** The URL scheme. */
U scheme; const(char)[] scheme;
/** The username. */ /** The username. */
U user; const(char)[] user;
/** The password. */ /** The password. */
U pass; const(char)[] pass;
/** The hostname. */ /** The hostname. */
U host; const(char)[] host;
/** The port number. */ /** The port number. */
ushort port; ushort port;
/** The path. */ /** The path. */
U path; const(char)[] path;
/** The query string. */ /** The query string. */
U query; const(char)[] query;
/** The anchor. */ /** The anchor. */
U fragment; const(char)[] fragment;
/** /**
* Attempts to parse an URL from a string. * Attempts to parse an URL from a string.
@ -666,7 +665,7 @@ struct URL(U = string)
* *
* Throws: $(D_PSYMBOL URIException) if the URL is malformed. * Throws: $(D_PSYMBOL URIException) if the URL is malformed.
*/ */
this(U source) this(in char[] source)
{ {
auto value = source; auto value = source;
ptrdiff_t pos = -1, endPos = value.length, start; ptrdiff_t pos = -1, endPos = value.length, start;
@ -954,7 +953,7 @@ struct URL(U = string)
* *
* Returns: Whether the port could be found. * Returns: Whether the port could be found.
*/ */
private bool parsePort(U port) pure nothrow @safe @nogc private bool parsePort(in char[] port) pure nothrow @safe @nogc
{ {
ptrdiff_t i = 1; ptrdiff_t i = 1;
float lPort = 0; float lPort = 0;
@ -984,14 +983,14 @@ struct URL(U = string)
/// ///
unittest unittest
{ {
auto u = URL!()("example.org"); auto u = URL("example.org");
assert(u.path == "example.org"); assert(u.path == "example.org");
u = URL!()("relative/path"); u = URL("relative/path");
assert(u.path == "relative/path"); assert(u.path == "relative/path");
// Host and scheme // Host and scheme
u = URL!()("https://example.org"); u = URL("https://example.org");
assert(u.scheme == "https"); assert(u.scheme == "https");
assert(u.host == "example.org"); assert(u.host == "example.org");
assert(u.path is null); assert(u.path is null);
@ -999,7 +998,7 @@ unittest
assert(u.fragment is null); assert(u.fragment is null);
// With user and port and path // With user and port and path
u = URL!()("https://hilary:putnam@example.org:443/foo/bar"); u = URL("https://hilary:putnam@example.org:443/foo/bar");
assert(u.scheme == "https"); assert(u.scheme == "https");
assert(u.host == "example.org"); assert(u.host == "example.org");
assert(u.path == "/foo/bar"); assert(u.path == "/foo/bar");
@ -1009,7 +1008,7 @@ unittest
assert(u.fragment is null); assert(u.fragment is null);
// With query string // With query string
u = URL!()("https://example.org/?login=true"); u = URL("https://example.org/?login=true");
assert(u.scheme == "https"); assert(u.scheme == "https");
assert(u.host == "example.org"); assert(u.host == "example.org");
assert(u.path == "/"); assert(u.path == "/");
@ -1017,14 +1016,14 @@ unittest
assert(u.fragment is null); assert(u.fragment is null);
// With query string and fragment // With query string and fragment
u = URL!()("https://example.org/?login=false#label"); u = URL("https://example.org/?login=false#label");
assert(u.scheme == "https"); assert(u.scheme == "https");
assert(u.host == "example.org"); assert(u.host == "example.org");
assert(u.path == "/"); assert(u.path == "/");
assert(u.query == "login=false"); assert(u.query == "login=false");
assert(u.fragment == "label"); assert(u.fragment == "label");
u = URL!()("redis://root:password@localhost:2201/path?query=value#fragment"); u = URL("redis://root:password@localhost:2201/path?query=value#fragment");
assert(u.scheme == "redis"); assert(u.scheme == "redis");
assert(u.user == "root"); assert(u.user == "root");
assert(u.pass == "password"); assert(u.pass == "password");
@ -1043,7 +1042,7 @@ private unittest
{ {
try try
{ {
URL!()(t[0]); URL(t[0]);
assert(0); assert(0);
} }
catch (URIException e) catch (URIException e)
@ -1053,7 +1052,7 @@ private unittest
} }
else else
{ {
auto u = URL!()(t[0]); auto u = URL(t[0]);
assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null, assert("scheme" in t[1] ? u.scheme == t[1]["scheme"] : u.scheme is null,
t[0]); t[0]);
assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]); assert("user" in t[1] ? u.user == t[1]["user"] : u.user is null, t[0]);
@ -1100,31 +1099,30 @@ enum Component : string
* *
* Returns: Requested URL components. * Returns: Requested URL components.
*/ */
URL parseURL(U)(in U source) URL parseURL(typeof(null) T)(in char[] source)
if (isSomeString!U)
{ {
return URL!U(source); return URL(source);
} }
/// Ditto. /// Ditto.
string parseURL(string T, U)(in U source) const(char)[] parseURL(immutable(char)[] T)(in char[] source)
if ((T == "scheme" if (T == "scheme"
|| T =="host" || T =="host"
|| T == "user" || T == "user"
|| T == "pass" || T == "pass"
|| T == "path" || T == "path"
|| T == "query" || T == "query"
|| T == "fragment") && isSomeString!U) || T == "fragment")
{ {
auto ret = URL!U(source); auto ret = URL(source);
return mixin("ret." ~ T); return mixin("ret." ~ T);
} }
/// Ditto. /// Ditto.
ushort parseURL(string T, U)(in U source) ushort parseURL(immutable(char)[] T)(in char[] source)
if (T == "port" && isSomeString!U) if (T == "port")
{ {
auto ret = URL!U(source); auto ret = URL(source);
return ret.port; return ret.port;
} }
@ -1158,7 +1156,7 @@ private unittest
else else
{ {
ushort port = parseURL!(Component.port)(t[0]); ushort port = parseURL!(Component.port)(t[0]);
string component = parseURL!(Component.scheme)(t[0]); auto component = parseURL!(Component.scheme)(t[0]);
assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null, assert("scheme" in t[1] ? component == t[1]["scheme"] : component is null,
t[0]); t[0]);
component = parseURL!(Component.user)(t[0]); component = parseURL!(Component.user)(t[0]);