This commit is contained in:
2016-08-24 18:15:21 +02:00
parent c0df3c9330
commit a3efee6d7f
21 changed files with 4193 additions and 0 deletions

View File

@ -0,0 +1,641 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:belka@caraus.de, Eugene Wissner)
*/
module tanya.container.buffer;
import tanya.memory;
@nogc:
version (unittest)
{
private int fillBuffer(void* buffer,
in size_t size,
int start = 0,
int end = 10)
in
{
assert(start < end);
}
body
{
ubyte[] buf = cast(ubyte[]) buffer[0..size];
auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i)
{
buf[i] = cast(ubyte) (start + i);
}
return numberRead;
}
}
/**
* Interface for implemeting input/output buffers.
*/
interface Buffer
{
@nogc:
/**
* Returns: The size of the internal buffer.
*/
@property size_t capacity() const @safe pure nothrow;
/**
* Returns: Data size.
*/
@property size_t length() const @safe pure nothrow;
/**
* Returns: Available space.
*/
@property size_t free() const @safe pure nothrow;
/**
* Appends some data to the buffer.
*
* Params:
* buffer = Buffer chunk got with $(D_PSYMBOL buffer).
*/
Buffer opOpAssign(string op)(void[] buffer)
if (op == "~");
}
/**
* Buffer that can be used with C functions accepting void pointer and
* returning the number of the read bytes.
*/
class ReadBuffer : Buffer
{
@nogc:
/// Internal buffer.
protected ubyte[] _buffer;
/// Filled buffer length.
protected size_t _length;
/// Available space.
protected immutable size_t minAvailable;
/// Size by which the buffer will grow.
protected immutable size_t blockSize;
private Allocator allocator;
invariant
{
assert(_length <= _buffer.length);
assert(blockSize > 0);
assert(minAvailable > 0);
}
/**
* Params:
* size = Initial buffer size and the size by which the buffer
* will grow.
* minAvailable = minimal size should be always available to fill.
* So it will reallocate if $(D_INLINECODE
* $(D_PSYMBOL free) < $(D_PARAM minAvailable)
* ).
*/
this(size_t size = 8192,
size_t minAvailable = 1024,
Allocator allocator = defaultAllocator)
{
this.allocator = allocator;
this.minAvailable = minAvailable;
this.blockSize = size;
resizeArray!ubyte(this.allocator, _buffer, size);
}
/**
* Deallocates the internal buffer.
*/
~this()
{
finalize(allocator, _buffer);
}
///
unittest
{
auto b = make!ReadBuffer(defaultAllocator);
assert(b.capacity == 8192);
assert(b.length == 0);
finalize(defaultAllocator, b);
}
/**
* Returns: The size of the internal buffer.
*/
@property size_t capacity() const @safe pure nothrow
{
return _buffer.length;
}
/**
* Returns: Data size.
*/
@property size_t length() const @safe pure nothrow
{
return _length;
}
/**
* Returns: Available space.
*/
@property size_t free() const @safe pure nothrow
{
return capacity - length;
}
///
unittest
{
auto b = make!ReadBuffer(defaultAllocator);
size_t numberRead;
void* buf;
// Fills the buffer with values 0..10
assert(b.free == b.blockSize);
buf = b.buffer;
numberRead = fillBuffer(buf, b.free, 0, 10);
b ~= buf[0..numberRead];
assert(b.free == b.blockSize - numberRead);
b[];
assert(b.free == b.blockSize);
finalize(defaultAllocator, b);
}
/**
* Returns a pointer to a chunk of the internal buffer. You can pass it to
* a function that requires such a buffer.
*
* Set the buffer again after reading something into it. Append
* $(D_KEYWORD ~=) a slice from the beginning of the buffer you got and
* till the number of the read bytes. The data will be appended to the
* existing buffer.
*
* Returns: A chunk of available buffer.
*/
@property void* buffer()
{
if (capacity - length < minAvailable)
{
resizeArray!ubyte(this.allocator, _buffer, capacity + blockSize);
}
return _buffer[_length..$].ptr;
}
/**
* Appends some data to the buffer. Use only the buffer you got
* with $(D_PSYMBOL buffer)!
*
* Params:
* buffer = Buffer chunk got with $(D_PSYMBOL buffer).
*/
ReadBuffer opOpAssign(string op)(void[] buffer)
if (op == "~")
{
_length += buffer.length;
return this;
}
///
unittest
{
auto b = make!ReadBuffer(defaultAllocator);
size_t numberRead;
void* buf;
ubyte[] result;
// Fills the buffer with values 0..10
buf = b.buffer;
numberRead = fillBuffer(buf, b.free, 0, 10);
b ~= buf[0..numberRead];
result = b[];
assert(result[0] == 0);
assert(result[1] == 1);
assert(result[9] == 9);
// It shouldn't overwrite, but append another 5 bytes to the buffer
buf = b.buffer;
numberRead = fillBuffer(buf, b.free, 0, 10);
b ~= buf[0..numberRead];
buf = b.buffer;
numberRead = fillBuffer(buf, b.free, 20, 25);
b ~= buf[0..numberRead];
result = b[];
assert(result[0] == 0);
assert(result[1] == 1);
assert(result[9] == 9);
assert(result[10] == 20);
assert(result[14] == 24);
finalize(defaultAllocator, b);
}
/**
* Returns the buffer. The buffer is cleared after that. So you can get it
* only one time.
*
* Returns: The buffer as array.
*/
@property ubyte[] opIndex()
{
auto ret = _buffer[0.._length];
_length = 0;
return ret;
}
///
unittest
{
auto b = make!ReadBuffer(defaultAllocator);
size_t numberRead;
void* buf;
ubyte[] result;
// Fills the buffer with values 0..10
buf = b.buffer;
numberRead = fillBuffer(buf, b.free, 0, 10);
b ~= buf[0..numberRead];
assert(b.length == 10);
result = b[];
assert(result[0] == 0);
assert(result[9] == 9);
assert(b.length == 0);
finalize(defaultAllocator, b);
}
}
/**
* Circular, self-expanding buffer that can be used with C functions accepting
* void pointer and returning the number of the read bytes.
*
* The buffer is optimized for situations where you read all the data from it
* at once (without writing to it occasionally). It can become ineffective if
* you permanently keep some data in the buffer and alternate writing and
* reading, because it may allocate and move elements.
*/
class WriteBuffer : Buffer
{
@nogc:
/// Internal buffer.
protected ubyte[] _buffer;
/// Buffer start position.
protected size_t start;
/// Buffer ring area size. After this position begins buffer overflow area.
protected size_t ring;
/// Size by which the buffer will grow.
protected immutable size_t blockSize;
/// The position of the free area in the buffer.
protected size_t position;
private Allocator allocator;
invariant
{
assert(blockSize > 0);
// position can refer to an element outside the buffer if the buffer is full.
assert(position <= _buffer.length);
}
/**
* Params:
* size = Initial buffer size and the size by which the buffer
* will grow.
*/
this(size_t size = 8192,
Allocator allocator = defaultAllocator)
{
this.allocator = allocator;
blockSize = size;
ring = size - 1;
resizeArray!ubyte(this.allocator, _buffer, size);
}
/**
* Deallocates the internal buffer.
*/
~this()
{
finalize(allocator, _buffer);
}
/**
* Returns: The size of the internal buffer.
*/
@property size_t capacity() const @safe pure nothrow
{
return _buffer.length;
}
/**
* Note that $(D_PSYMBOL length) doesn't return the real length of the data,
* but only the array length that will be returned with $(D_PSYMBOL buffer)
* next time. Be sure to call $(D_PSYMBOL buffer) and set $(D_PSYMBOL written)
* until $(D_PSYMBOL length) returns 0.
*
* Returns: Data size.
*/
@property size_t length() const @safe pure nothrow
{
if (position > ring || position < start) // Buffer overflowed
{
return ring - start + 1;
}
else
{
return position - start;
}
}
///
unittest
{
auto b = make!WriteBuffer(defaultAllocator, 4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.length == 3);
b.written = 2;
assert(b.length == 1);
b ~= buf;
assert(b.length == 2);
b.written = 2;
assert(b.length == 2);
b ~= buf;
assert(b.length == 5);
b.written = b.length;
assert(b.length == 0);
finalize(defaultAllocator, b);
}
/**
* Returns: Available space.
*/
@property size_t free() const @safe pure nothrow
{
return capacity - length;
}
/**
* Appends data to the buffer.
*
* Params:
* buffer = Buffer chunk got with $(D_PSYMBOL buffer).
*/
WriteBuffer opOpAssign(string op)(ubyte[] buffer)
if (op == "~")
{
size_t end, start;
if (position >= this.start && position <= ring)
{
auto afterRing = ring + 1;
end = position + buffer.length;
if (end > afterRing)
{
end = afterRing;
}
start = end - position;
_buffer[position..end] = buffer[0..start];
if (end == afterRing)
{
position = this.start == 0 ? afterRing : 0;
}
else
{
position = end;
}
}
// Check if we have some free space at the beginning
if (start < buffer.length && position < this.start)
{
end = position + buffer.length - start;
if (end > this.start)
{
end = this.start;
}
auto areaEnd = end - position + start;
_buffer[position..end] = buffer[start..areaEnd];
position = end == this.start ? ring + 1 : end - position;
start = areaEnd;
}
// And if we still haven't found any place, save the rest in the overflow area
if (start < buffer.length)
{
end = position + buffer.length - start;
if (end > capacity)
{
auto newSize = end / blockSize * blockSize + blockSize;
resizeArray!ubyte(this.allocator, _buffer, newSize);
}
_buffer[position..end] = buffer[start..$];
position = end;
if (this.start == 0)
{
ring = capacity - 1;
}
}
return this;
}
///
unittest
{
auto b = make!WriteBuffer(defaultAllocator, 4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b._buffer[0] == 48 && b._buffer[1] == 23 && b._buffer[2] == 255);
b.written = 2;
b ~= buf;
assert(b.capacity == 4);
assert(b._buffer[0] == 23 && b._buffer[1] == 255
&& b._buffer[2] == 255 && b._buffer[3] == 48);
b.written = 2;
b ~= buf;
assert(b.capacity == 8);
assert(b._buffer[0] == 23 && b._buffer[1] == 255
&& b._buffer[2] == 48 && b._buffer[3] == 23 && b._buffer[4] == 255);
finalize(defaultAllocator, b);
b = make!WriteBuffer(defaultAllocator, 2);
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
finalize(defaultAllocator, b);
}
/**
* Sets how many bytes were written. It will shrink the buffer
* appropriately. Always set this property after calling
* $(D_PSYMBOL buffer).
*
* Params:
* length = Length of the written data.
*/
@property void written(size_t length) @safe pure nothrow
in
{
assert(length <= this.length);
}
body
{
auto afterRing = ring + 1;
auto oldStart = start;
if (length <= 0)
{
return;
}
else if (position <= afterRing)
{
start += length;
if (start > 0 && position == afterRing)
{
position = oldStart;
}
}
else
{
auto overflow = position - afterRing;
if (overflow > length) {
_buffer[start.. start + length] = _buffer[afterRing.. afterRing + length];
_buffer[afterRing.. afterRing + length] = _buffer[afterRing + length ..position];
position -= length;
}
else if (overflow == length)
{
_buffer[start.. start + overflow] = _buffer[afterRing..position];
position -= overflow;
}
else
{
_buffer[start.. start + overflow] = _buffer[afterRing..position];
position = overflow;
}
start += length;
if (start == position)
{
if (position != afterRing)
{
position = 0;
}
start = 0;
ring = capacity - 1;
}
}
if (start > ring)
{
start = 0;
}
}
///
unittest
{
auto b = make!WriteBuffer(defaultAllocator);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
b ~= buf;
assert(b.length == 6);
b.written = 2;
assert(b.length == 4);
b.written = 4;
assert(b.length == 0);
finalize(defaultAllocator, b);
}
/**
* Returns a pointer to a buffer chunk with data. You can pass it to
* a function that requires such a buffer.
*
* After calling it, set $(D_PSYMBOL written) to the length could be
* written.
*
* $(D_PSYMBOL buffer) may return only part of the data. You may need
* to call it (and set $(D_PSYMBOL written) several times until
* $(D_PSYMBOL length) is 0. If all the data can be written,
* maximally 3 calls are required.
*
* Returns: A chunk of data buffer.
*/
@property void* buffer() @safe pure nothrow
{
if (position > ring || position < start) // Buffer overflowed
{
return _buffer[start.. ring + 1].ptr;
}
else
{
return _buffer[start..position].ptr;
}
}
///
unittest
{
auto b = make!WriteBuffer(defaultAllocator, 6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
void* returnedBuf;
b ~= buf;
returnedBuf = b.buffer;
assert(returnedBuf[0..b.length] == buf[0..6]);
b.written = 2;
returnedBuf = b.buffer;
assert(returnedBuf[0..b.length] == buf[2..6]);
b ~= buf;
returnedBuf = b.buffer;
assert(returnedBuf[0..b.length] == buf[2..6]);
b.written = b.length;
returnedBuf = b.buffer;
assert(returnedBuf[0..b.length] == buf[0..6]);
b.written = b.length;
finalize(defaultAllocator, b);
}
}

View File

@ -0,0 +1,405 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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.container.list;
import tanya.memory;
/**
* Singly linked list.
*
* Params:
* T = Content type.
*/
class SList(T)
{
@nogc:
/**
* Creates a new $(D_PSYMBOL SList).
*
* Params:
* allocator = The allocator should be used for the element
* allocations.
*/
this(Allocator allocator = defaultAllocator)
{
this.allocator = allocator;
reset();
}
/**
* Removes all elements from the list.
*/
~this()
{
while (!empty)
{
static if (isFinalizable!T)
{
finalize(allocator, front);
}
popFront();
}
}
/**
* Returns: First element.
*/
@property ref T front()
in
{
assert(!empty);
}
body
{
return first.next.content;
}
/**
* Inserts a new element at the beginning.
*
* Params:
* x = New element.
*/
@property void front(T x)
{
Entry* temp = make!Entry(allocator);
temp.content = x;
temp.next = first.next;
first.next = temp;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[2] values = [8, 9];
l.front = values[0];
assert(l.front == values[0]);
l.front = values[1];
assert(l.front == values[1]);
finalize(defaultAllocator, l);
}
/**
* Inserts a new element at the beginning.
*
* Params:
* x = New element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) opOpAssign(string Op)(ref T x)
if (Op == "~")
{
front = x;
return this;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int value = 5;
assert(l.empty);
l ~= value;
assert(l.front == value);
assert(!l.empty);
finalize(defaultAllocator, l);
}
/**
* Returns: $(D_KEYWORD true) if the list is empty.
*/
@property bool empty() const @safe pure nothrow
{
return first.next is null;
}
/**
* Returns the first element and moves to the next one.
*
* Returns: The first element.
*/
T popFront()
in
{
assert(!empty);
}
body
{
auto n = first.next.next;
auto content = first.next.content;
finalize(allocator, first.next);
first.next = n;
return content;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[2] values = [8, 9];
l.front = values[0];
l.front = values[1];
assert(l.front == values[1]);
l.popFront();
assert(l.front == values[0]);
finalize(defaultAllocator, l);
}
/**
* Returns the current item from the list and removes from the list.
*
* Params:
* x = The item should be removed.
*
* Returns: Removed item.
*/
T remove()
in
{
assert(!empty);
}
body
{
auto temp = position.next.next;
auto content = position.next.content;
finalize(allocator, position.next);
position.next = temp;
return content;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[3] values = [8, 5, 4];
l.front = values[0];
l.front = values[1];
assert(l.remove() == 5);
l.front = values[2];
assert(l.remove() == 4);
assert(l.remove() == 8);
assert(l.empty);
finalize(defaultAllocator, l);
}
/**
* Resets the current position.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) reset()
{
position = &first;
return this;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[2] values = [8, 5];
l.current = values[0];
l.current = values[1];
assert(l.current == 5);
l.advance();
assert(l.current == 8);
l.reset();
assert(l.current == 5);
finalize(defaultAllocator, l);
}
/**
* $(D_KEYWORD foreach) iteration.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*/
int opApply(int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
size_t i;
for (position = first.next; position; position = position.next, ++i)
{
result = dg(i, position.content);
if (result != 0)
{
return result;
}
}
reset();
return result;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[3] values = [5, 4, 9];
l.front = values[0];
l.front = values[1];
l.front = values[2];
foreach (i, e; l)
{
assert(i != 0 || e == values[2]);
assert(i != 1 || e == values[1]);
assert(i != 2 || e == values[0]);
}
finalize(defaultAllocator, l);
}
/// Ditto.
int opApply(int delegate(ref T) @nogc dg)
{
int result;
for (position = first.next; position; position = position.next)
{
result = dg(position.content);
if (result != 0)
{
return result;
}
}
reset();
return result;
}
///
unittest
{
auto l = make!(SList!int)(defaultAllocator);
int[3] values = [5, 4, 9];
size_t i;
l.front = values[0];
l.front = values[1];
l.front = values[2];
foreach (e; l)
{
assert(i != 0 || e == values[2]);
assert(i != 1 || e == values[1]);
assert(i != 2 || e == values[0]);
++i;
}
finalize(defaultAllocator, l);
}
/**
* Returns: $(D_KEYWORD true) if the current position is the end position.
*/
@property bool end() const
{
return empty || position.next.next is null;
}
/**
* Moves to the next element and returns it.
*
* Returns: The element on the next position.
*/
T advance()
in
{
assert(!end);
}
body
{
position = position.next;
return position.content;
}
/**
* Returns: Element on the current position.
*/
@property ref T current()
in
{
assert(!empty);
}
body
{
return position.next.content;
}
/**
* Inserts a new element at the current position.
*
* Params:
* x = New element.
*/
@property void current(T x)
{
Entry* temp = make!Entry(allocator);
temp.content = x;
temp.next = position.next;
position.next = temp;
}
/**
* List entry.
*/
protected struct Entry
{
/// List item content.
T content;
/// Next list item.
Entry* next;
}
/// 0th element of the list.
protected Entry first;
/// Current position in the list.
protected Entry* position;
private Allocator allocator;
}
interface Stuff
{
}
///
unittest
{
auto l = make!(SList!Stuff)(defaultAllocator);
finalize(defaultAllocator, l);
}

View File

@ -0,0 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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.container;
public import tanya.container.buffer;
public import tanya.container.list;
public import tanya.container.vector;
public import tanya.container.queue;

View File

@ -0,0 +1,219 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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.container.queue;
import tanya.memory;
/**
* Queue.
*
* Params:
* T = Content type.
*/
class Queue(T)
{
@nogc:
/**
* Creates a new $(D_PSYMBOL Queue).
*
* Params:
* allocator = The allocator should be used for the element
* allocations.
*/
this(Allocator allocator = defaultAllocator)
{
this.allocator = allocator;
}
/**
* Removes all elements from the queue.
*/
~this()
{
foreach (e; this)
{
static if (isFinalizable!T)
{
finalize(allocator, e);
}
}
}
/**
* Returns: First element.
*/
@property ref T front()
in
{
assert(!empty);
}
body
{
return first.next.content;
}
/**
* Inserts a new element.
*
* Params:
* x = New element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) insertBack(T x)
{
Entry* temp = make!Entry(allocator);
temp.content = x;
if (empty)
{
first.next = rear = temp;
}
else
{
rear.next = temp;
rear = rear.next;
}
return this;
}
alias insert = insertBack;
///
unittest
{
auto q = make!(Queue!int)(defaultAllocator);
int[2] values = [8, 9];
q.insertBack(values[0]);
assert(q.front is values[0]);
q.insertBack(values[1]);
assert(q.front is values[0]);
finalize(defaultAllocator, q);
}
/**
* Inserts a new element.
*
* Params:
* x = New element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) opOpAssign(string Op)(ref T x)
if (Op == "~")
{
return insertBack(x);
}
///
unittest
{
auto q = make!(Queue!int)(defaultAllocator);
int value = 5;
assert(q.empty);
q ~= value;
assert(q.front == value);
assert(!q.empty);
finalize(defaultAllocator, q);
}
/**
* Returns: $(D_KEYWORD true) if the queue is empty.
*/
@property bool empty() const @safe pure nothrow
{
return first.next is null;
}
///
unittest
{
auto q = make!(Queue!int)(defaultAllocator);
int value = 7;
assert(q.empty);
q.insertBack(value);
assert(!q.empty);
finalize(defaultAllocator, q);
}
/**
* Move position to the next element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) popFront()
in
{
assert(!empty);
}
body
{
auto n = first.next.next;
finalize(allocator, first.next);
first.next = n;
return this;
}
///
unittest
{
auto q = make!(Queue!int)(defaultAllocator);
int[2] values = [8, 9];
q.insertBack(values[0]);
q.insertBack(values[1]);
assert(q.front is values[0]);
q.popFront();
assert(q.front is values[1]);
finalize(defaultAllocator, q);
}
/**
* Queue entry.
*/
protected struct Entry
{
/// Queue item content.
T content;
/// Next list item.
Entry* next;
}
/// The first element of the list.
protected Entry first;
/// The last element of the list.
protected Entry* rear;
private Allocator allocator;
}
///
unittest
{
auto q = make!(Queue!int)(defaultAllocator);
finalize(defaultAllocator, q);
}

View File

@ -0,0 +1,420 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016.
* 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.container.vector;
import tanya.memory;
@nogc:
/**
* One dimensional array. It allocates automatically if needed.
*
* If you assign a value:
* ---
* auto v = make!(Vector!int)(defaultAllocator);
* int value = 5;
*
* v[1000] = value;
*
* finalize(defaultAllocator, v);
* ---
* it will allocate not only for one, but for 1000 elements. So this
* implementation is more suitable for sequential data with random access.
*
* Params:
* T = Content type.
*/
class Vector(T)
{
@nogc:
/**
* Creates a new $(D_PSYMBOL Vector).
*
* Params:
* length = Initial length.
* allocator = The allocator should be used for the element
* allocations.
*/
this(size_t length, Allocator allocator = defaultAllocator)
{
this.allocator = allocator;
vector = makeArray!T(allocator, length);
}
/// Ditto.
this(Allocator allocator = defaultAllocator)
{
this(0, allocator);
}
/**
* Removes all elements from the vector.
*/
~this()
{
finalize(allocator, vector);
}
/**
* Returns: Vector length.
*/
@property size_t length() const
{
return vector.length;
}
/**
* Expans/shrinks the vector.
*
* Params:
* length = New length.
*/
@property void length(size_t length)
{
resizeArray!T(allocator, vector, length);
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator);
v.length = 5;
assert(v.length == 5);
// TODO
v.length = 7;
assert(v.length == 7);
v.length = 0;
assert(v.length == 0);
finalize(defaultAllocator, v);
}
/**
* Returns: $(D_KEYWORD true) if the vector is empty.
*/
@property bool empty() const
{
return length == 0;
}
static if (isFinalizable!T)
{
/**
* Removes an elements from the vector.
*
* Params:
* pos = Element index.
*/
void remove(size_t pos)
{
auto el = vector[pos];
finalize(allocator, el);
}
}
/**
* Assigns a value. Allocates if needed.
*
* Params:
* value = Value.
*
* Returns: Assigned value.
*/
T opIndexAssign(T value, size_t pos)
{
if (pos >= length)
{
resizeArray!T(allocator, vector, pos + 1);
}
return vector[pos] = value;
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator);
int[2] values = [5, 15];
assert(v.length == 0);
v[1] = values[0];
assert(v.length == 2);
v[3] = values[0];
assert(v.length == 4);
v[4] = values[1];
assert(v.length == 5);
finalize(defaultAllocator, v);
}
/**
* Returns: The value on index $(D_PARAM pos).
*/
ref T opIndex(in size_t pos)
in
{
assert(length > pos);
}
body
{
return vector[pos];
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator);
int[2] values = [5, 15];
v[1] = values[0];
assert(v[1] is values[0]);
v[3] = values[0];
assert(v[3] is values[0]);
v[4] = values[1];
assert(v[4] is values[1]);
v[0] = values[1];
assert(v[0] is values[1]);
finalize(defaultAllocator, v);
}
/**
* $(D_KEYWORD foreach) iteration.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*/
int opApply(int delegate(ref T) @nogc dg)
{
int result;
foreach (e; vector)
{
result = dg(e);
if (result != 0)
{
return result;
}
}
return result;
}
/// Ditto.
int opApply(int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
foreach (i, e; vector)
{
result = dg(i, e);
if (result != 0)
{
return result;
}
}
return result;
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator, 1);
int[3] values = [5, 15, 8];
v[0] = values[0];
v[1] = values[1];
v[2] = values[2];
int i;
foreach (e; v)
{
assert(i != 0 || e is values[0]);
assert(i != 1 || e is values[1]);
assert(i != 2 || e is values[2]);
++i;
}
foreach (j, e; v)
{
assert(j != 0 || e is values[0]);
assert(j != 1 || e is values[1]);
assert(j != 2 || e is values[2]);
}
finalize(defaultAllocator, v);
}
/**
* Sets the first element. Allocates if the vector is empty.
*
* Params:
* x = New element.
*/
@property void front(ref T x)
{
this[0] = x;
}
/**
* Returns: The first element.
*/
@property ref inout(T) front() inout
in
{
assert(!empty);
}
body
{
return vector[0];
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator, 1);
int[2] values = [5, 15];
v.front = values[0];
assert(v.front == 5);
v.front = values[1];
assert(v.front == 15);
finalize(defaultAllocator, v);
}
/**
* Move position to the next element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) popFront()
in
{
assert(!empty);
}
body
{
vector[0 .. $ - 1] = vector[1..$];
resizeArray(allocator, vector, length - 1);
return this;
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator, 1);
int[2] values = [5, 15];
v[0] = values[0];
v[1] = values[1];
assert(v.front is values[0]);
assert(v.length == 2);
v.popFront();
assert(v.front is values[1]);
assert(v.length == 1);
v.popFront();
assert(v.empty);
finalize(defaultAllocator, v);
}
/**
* Sets the last element. Allocates if the vector is empty.
*
* Params:
* x = New element.
*/
@property void back(ref T x)
{
vector[empty ? 0 : $ - 1] = x;
}
/**
* Returns: The last element.
*/
@property ref inout(T) back() inout
in
{
assert(!empty);
}
body
{
return vector[$ - 1];
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator, 1);
int[2] values = [5, 15];
v.back = values[0];
assert(v.back == 5);
v.back = values[1];
assert(v.back == 15);
finalize(defaultAllocator, v);
}
/**
* Move position to the previous element.
*
* Returns: $(D_KEYWORD this).
*/
typeof(this) popBack()
in
{
assert(!empty);
}
body
{
resizeArray(allocator, vector, length - 1);
return this;
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator, 1);
int[2] values = [5, 15];
v[0] = values[0];
v[1] = values[1];
assert(v.back is values[1]);
assert(v.length == 2);
v.popBack();
assert(v.back is values[0]);
assert(v.length == 1);
v.popBack();
assert(v.empty);
finalize(defaultAllocator, v);
}
/// Container.
protected T[] vector;
private Allocator allocator;
}
///
unittest
{
auto v = make!(Vector!int)(defaultAllocator);
finalize(defaultAllocator, v);
}