diff options
| -rw-r--r-- | source/tanya/container/bitvector.d | 510 | ||||
| -rw-r--r-- | source/tanya/container/buffer.d | 1264 | ||||
| -rw-r--r-- | source/tanya/container/entry.d | 10 | ||||
| -rw-r--r-- | source/tanya/container/package.d | 5 | ||||
| -rw-r--r-- | source/tanya/container/queue.d | 506 | ||||
| -rw-r--r-- | source/tanya/container/vector.d | 78 |
6 files changed, 1444 insertions, 929 deletions
diff --git a/source/tanya/container/bitvector.d b/source/tanya/container/bitvector.d new file mode 100644 index 0000000..3597a73 --- /dev/null +++ b/source/tanya/container/bitvector.d @@ -0,0 +1,510 @@ +/* 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/. */ + +/** + * Single-dimensioned bit array. + * + * 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.container.bitvector; + +/** + * Wrapper that allows bit manipulation on $(D_KEYWORD ubyte[]) array. + */ +struct BitVector +{ + protected ubyte[] vector; + + /** + * Params: + * array = Array should be manipulated on. + */ + this(inout(ubyte[]) array) inout pure nothrow @safe @nogc + in + { + assert(array.length <= size_t.max / 8); + assert(array !is null); + } + body + { + vector = array; + } + + /// + unittest + { + ubyte[5] array1 = [234, 3, 252, 10, 18]; + ubyte[3] array2 = [65, 13, 173]; + auto bits = BitVector(array1); + + assert(bits[] is array1); + assert(bits[] !is array2); + + bits = BitVector(array2); + assert(bits[] is array2); + } + + /** + * Returns: Number of bits in the vector. + */ + @property inout(size_t) length() inout const pure nothrow @safe @nogc + { + return vector.length * 8; + } + + /// Ditto. + inout(size_t) opDollar() inout const pure nothrow @safe @nogc + { + return vector.length * 8; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(bits.length == 24); + } + + /** + * Params: + * bit = Bit position. + * + * Returns: $(D_KEYWORD true) if the bit on position $(D_PARAM bit) is set, + * $(D_KEYWORD false) if not set. + */ + inout(bool) opIndex(size_t bit) inout const pure nothrow @safe @nogc + in + { + assert(bit / 8 <= vector.length); + } + body + { + return (vector[bit / 8] & (0x80 >> (bit % 8))) != 0; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(!bits[0]); + assert(bits[1]); + assert(bits[7]); + assert(!bits[8]); + assert(!bits[11]); + assert(bits[12]); + assert(bits[20]); + assert(bits[23]); + } + + /** + * Returns: Underlying array. + */ + inout(ubyte[]) opIndex() inout pure nothrow @safe @nogc + { + return vector; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + assert(bits[] is arr); + } + + /** + * Params: + * value = $(D_KEYWORD true) if the bit should be set, + * $(D_KEYWORD false) if cleared. + * bit = Bit position. + * + * Returns: $(D_PSYMBOL this). + */ + bool opIndexAssign(bool value, size_t bit) pure nothrow @safe @nogc + in + { + assert(bit / 8 <= vector.length); + } + body + { + if (value) + { + vector[bit / 8] |= (0x80 >> (bit % 8)); + } + else + { + vector[bit / 8] &= ~(0x80 >> (bit % 8)); + } + return value; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + bits[5] = bits[6] = true; + assert(bits[][0] == 71); + + bits[14] = true; + bits[15] = false; + assert(bits[][1] == 14); + + bits[16] = bits[23] = false; + assert(bits[][2] == 44); + } + + /** + * Copies bits from $(D_PARAM vector) into this $(D_PSYMBOL BitVector). + * + * The array that should be assigned, can be smaller (but not larger) than + * the underlying array of this $(D_PSYMBOL BitVector), leading zeros will + * be added in this case to the left. + * + * Params: + * vector = $(D_KEYWORD ubyte[]) array not larger than + * `$(D_PSYMBOL length) / 8`. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opAssign(ubyte[] vector) pure nothrow @safe @nogc + in + { + assert(vector.length <= this.vector.length); + } + body + { + immutable delta = this.vector.length - vector.length; + if (delta > 0) + { + this.vector[0..delta] = 0; + } + this.vector[delta..$] = vector[0..$]; + return this; + } + + /// + unittest + { + ubyte[5] array1 = [234, 3, 252, 10, 18]; + ubyte[3] array2 = [65, 13, 173]; + auto bits = BitVector(array1); + + bits = array2; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 65); + assert(bits[][3] == 13); + assert(bits[][4] == 173); + + bits = array2[0..2]; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 0); + assert(bits[][3] == 65); + assert(bits[][4] == 13); + } + + /** + * Support for bitwise operations. + * + * Params: + * that = Another bit vector. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opOpAssign(string op)(BitVector that) pure nothrow @safe @nogc + if ((op == "^") || (op == "|") || (op == "&")) + { + return opOpAssign(op)(that.vector); + } + + /// Ditto. + BitVector opOpAssign(string op)(ubyte[] that) pure nothrow @safe @nogc + if ((op == "^") || (op == "|") || (op == "&")) + in + { + assert(that.length <= vector.length); + } + body + { + for (int i = cast(int) vector.length - 1; i >= 0; --i) + { + mixin("vector[i] " ~ op ~ "= " ~ "that[i];"); + } + immutable delta = vector.length - that.length; + if (delta) + { + static if (op == "&") + { + vector[0..delta] = 0; + } + } + return this; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] array1 = [65, 13, 173]; + ubyte[3] array2 = [0b01010010, 0b10111110, 0b10111110]; + auto bits = BitVector(array1); + + bits |= array2; + assert(bits[][0] == 0b01010011); + assert(bits[][1] == 0b10111111); + assert(bits[][2] == 0b10111111); + + bits &= array2; + assert(bits[][0] == array2[0]); + assert(bits[][1] == array2[1]); + assert(bits[][2] == array2[2]); + + bits ^= array2; + assert(bits[][0] == 0); + assert(bits[][1] == 0); + assert(bits[][2] == 0); + } + + /** + * Support for shift operations. + * + * Params: + * n = Number of bits. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opOpAssign(string op)(in size_t n) pure nothrow @safe @nogc + if ((op == "<<") || (op == ">>")) + { + if (n >= length) + { + vector[0..$] = 0; + } + else if (n != 0) + { + immutable bit = n % 8, step = n / 8; + immutable delta = 8 - bit; + size_t i, j; + + static if (op == "<<") + { + for (j = step; j < vector.length - 1; ++i) + { + vector[i] = cast(ubyte)((vector[j] << bit) + | vector[++j] >> delta); + } + vector[i] = cast(ubyte)(vector[j] << bit); + vector[$ - step ..$] = 0; + } + else static if (op == ">>") + { + for (i = vector.length - 1, j = i - step; j > 0; --i) + { + vector[i] = cast(ubyte)((vector[j] >> bit) + | vector[--j] << delta); + } + vector[i] = cast(ubyte)(vector[j] >> bit); + vector[0..step] = 0; + } + } + return this; + } + + /// + nothrow @safe @nogc unittest + { + ubyte[4] arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; + auto bits = BitVector(arr); + + bits <<= 0; + assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 + && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); + + bits <<= 2; + assert(bits[][0] == 0b11111011 && bits[][1] == 0b11001001 + && bits[][2] == 0b01001001 && bits[][3] == 0b01001100); + + bits <<= 4; + assert(bits[][0] == 0b10111100 && bits[][1] == 0b10010100 + && bits[][2] == 0b10010100 && bits[][3] == 0b11000000); + + bits <<= 8; + assert(bits[][0] == 0b10010100 && bits[][1] == 0b10010100 + && bits[][2] == 0b11000000 && bits[][3] == 0b00000000); + + bits <<= 7; + assert(bits[][0] == 0b01001010 && bits[][1] == 0b01100000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + bits <<= 25; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; + bits <<= 24; + assert(bits[][0] == 0b01010101 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr[1] = 0b11001100; + arr[2] = 0b11111111; + arr[3] = 0b01010101; + bits <<= 12; + assert(bits[][0] == 0b11001111 && bits[][1] == 0b11110101 + && bits[][2] == 0b01010000 && bits[][3] == 0b00000000); + + bits <<= 100; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b10111110, 0b11110010, 0b01010010, 0b01010011]; + bits >>= 0; + assert(bits[][0] == 0b10111110 && bits[][1] == 0b11110010 + && bits[][2] == 0b01010010 && bits[][3] == 0b01010011); + + bits >>= 2; + assert(bits[][0] == 0b00101111 && bits[][1] == 0b10111100 + && bits[][2] == 0b10010100 && bits[][3] == 0b10010100); + + bits >>= 4; + assert(bits[][0] == 0b00000010 && bits[][1] == 0b11111011 + && bits[][2] == 0b11001001 && bits[][3] == 0b01001001); + + bits >>= 8; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000010 + && bits[][2] == 0b11111011 && bits[][3] == 0b11001001); + + bits >>= 7; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000101 && bits[][3] == 0b11110111); + + bits >>= 25; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + + arr = [0b00110011, 0b11001100, 0b11111111, 0b01010101]; + bits >>= 24; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00110011); + + arr[1] = 0b11001100; + arr[2] = 0b11111111; + arr[3] = 0b01010101; + bits >>= 12; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00001100 && bits[][3] == 0b11001111); + + bits >>= 100; + assert(bits[][0] == 0b00000000 && bits[][1] == 0b00000000 + && bits[][2] == 0b00000000 && bits[][3] == 0b00000000); + } + + /** + * Negates all bits. + * + * Returns: $(D_KEYWORD this). + */ + BitVector opUnary(string op)() pure nothrow @safe @nogc + if (op == "~") + { + foreach (ref b; vector) + { + b = ~b; + } + return this; + } + + /// + unittest + { + // [01000001, 00001101, 10101101] + ubyte[3] arr = [65, 13, 173]; + auto bits = BitVector(arr); + + ~bits; + assert(bits[][0] == 0b10111110); + assert(bits[][1] == 0b11110010); + assert(bits[][2] == 0b01010010); + } + + /** + * Iterates through all bits. + * + * Params: + * dg = $(D_KEYWORD foreach) delegate. + * + * Returns: By $(D_PARAM dg) returned value. + */ + int opApply(int delegate(size_t, bool) dg) + { + int result; + foreach (i, ref v; vector) + { + foreach (c; 0..8) + { + result = dg(i * 8 + c, (v & (0x80 >> c)) != 0); + if (result) + { + return result; + } + } + } + return result; + } + + /// Ditto. + int opApply(int delegate(bool) dg) + { + int result; + foreach (ref v; vector) + { + foreach (c; 0..8) + { + result = dg((v & (0x80 >> c)) != 0); + if (result) + { + return result; + } + } + } + return result; + } + + /// + unittest + { + ubyte[2] arr = [0b01000001, 0b00001101]; + auto bits = BitVector(arr); + size_t c; + + foreach (i, v; bits) + { + assert(i == c); + if (i == 1 || i == 7 || i == 15 || i == 13 || i == 12) + { + assert(v); + } + else + { + assert(!v); + } + ++c; + } + assert(c == 16); + } +} diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index 4d01ad5..9dde930 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -7,7 +7,7 @@ * 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.buffer;
import std.traits;
@@ -15,23 +15,23 @@ import tanya.memory; version (unittest)
{
- private int fillBuffer(ubyte[] buffer,
- in size_t size,
- int start = 0,
- int end = 10) @nogc pure nothrow
- in
- {
- assert(start < end);
- }
- body
- {
- auto numberRead = end - start;
- for (ubyte i; i < numberRead; ++i)
- {
- buffer[i] = cast(ubyte) (start + i);
- }
- return numberRead;
- }
+ private int fillBuffer(ubyte[] buffer,
+ in size_t size,
+ int start = 0,
+ int end = 10) @nogc pure nothrow
+ in
+ {
+ assert(start < end);
+ }
+ body
+ {
+ auto numberRead = end - start;
+ for (ubyte i; i < numberRead; ++i)
+ {
+ buffer[i] = cast(ubyte) (start + i);
+ }
+ return numberRead;
+ }
}
/**
@@ -45,253 +45,253 @@ version (unittest) * of the pended asynchronous call.
*
* Params:
- * T = Buffer type.
+ * T = Buffer type.
*/
struct ReadBuffer(T = ubyte)
- if (isScalarType!T)
+ if (isScalarType!T)
{
- /// Internal buffer.
- private T[] buffer_;
-
- /// Filled buffer length.
- private size_t length_;
-
- /// Start of available data.
- private size_t start;
-
- /// Last position returned with $(D_KEYWORD []).
- private size_t ring;
-
- /// Available space.
- private immutable size_t minAvailable = 1024;
-
- /// Size by which the buffer will grow.
- private immutable size_t blockSize = 8192;
-
- invariant
- {
- assert(length_ <= buffer_.length);
- assert(blockSize > 0);
- assert(minAvailable > 0);
- }
-
- /**
- * Creates a new read buffer.
- *
- * 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)).
- * allocator = Allocator.
- */
- this(in size_t size,
- in size_t minAvailable = 1024,
- shared Allocator allocator = defaultAllocator) @trusted
- {
- this(allocator);
- this.minAvailable = minAvailable;
- this.blockSize = size;
- buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
- }
-
- /// Ditto.
- this(shared Allocator allocator)
- in
- {
- assert(allocator_ is null);
- }
- body
- {
- allocator_ = allocator;
- }
-
- /**
- * Deallocates the internal buffer.
- */
- ~this() @trusted
- {
- allocator.deallocate(buffer_);
- }
-
- ///
- unittest
- {
- ReadBuffer!ubyte b;
- assert(b.capacity == 0);
- assert(b.length == 0);
- }
-
- /**
- * Returns: The size of the internal buffer.
- */
- @property size_t capacity() const
- {
- return buffer_.length;
- }
-
- /**
- * Returns: Data size.
- */
- @property size_t length() const
- {
- return length_ - start;
- }
-
- /// Ditto.
- alias opDollar = length;
-
- /**
- * Clears the buffer.
- *
- * Returns: $(D_KEYWORD this).
- */
- void clear()
- {
- start = length_ = ring;
- }
-
- /**
- * Returns: Available space.
- */
- @property size_t free() const
- {
- return length > ring ? capacity - length : capacity - ring;
- }
-
- ///
- unittest
- {
- ReadBuffer!ubyte b;
- size_t numberRead;
-
- assert(b.free == 0);
-
- // Fills the buffer with values 0..10
- numberRead = fillBuffer(b[], b.free, 0, 10);
- b += numberRead;
- assert(b.free == b.blockSize - numberRead);
- b.clear();
- assert(b.free == b.blockSize);
- }
-
- /**
- * Appends some data to the buffer.
- *
- * Params:
- * length = Number of the bytes read.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref ReadBuffer opOpAssign(string op)(in size_t length)
- if (op == "+")
- {
- length_ += length;
- ring = start;
- return this;
- }
-
- ///
- unittest
- {
- ReadBuffer!ubyte b;
- size_t numberRead;
- ubyte[] result;
-
- // Fills the buffer with values 0..10
- numberRead = fillBuffer(b[], b.free, 0, 10);
- b += numberRead;
-
- result = b[0 .. $];
- assert(result[0] == 0);
- assert(result[1] == 1);
- assert(result[9] == 9);
- b.clear();
-
- // It shouldn't overwrite, but append another 5 bytes to the buffer
- numberRead = fillBuffer(b[], b.free, 0, 10);
- b += numberRead;
-
- numberRead = fillBuffer(b[], b.free, 20, 25);
- b += numberRead;
-
- result = b[0..$];
- assert(result[0] == 0);
- assert(result[1] == 1);
- assert(result[9] == 9);
- assert(result[10] == 20);
- assert(result[14] == 24);
- }
-
- /**
- * Params:
- * start = Start position.
- * end = End position.
- *
- * Returns: Array between $(D_PARAM start) and $(D_PARAM end).
- */
- T[] opSlice(in size_t start, in size_t end)
- {
- return buffer_[this.start + start .. this.start + end];
- }
-
- /**
- * Returns a free chunk of the buffer.
- *
- * Add ($(D_KEYWORD +=)) the number of the read bytes after using it.
- *
- * Returns: A free chunk of the buffer.
- */
- T[] opIndex()
- {
- if (start > 0)
- {
- auto ret = buffer_[0 .. start];
- ring = 0;
- return ret;
- }
- else
- {
- if (capacity - length < minAvailable)
- {
- void[] buf = buffer_;
- immutable cap = capacity;
- () @trusted {
- allocator.reallocate(buf, (cap + blockSize) * T.sizeof);
- buffer_ = cast(T[]) buf;
- }();
- }
- ring = length_;
- return buffer_[length_ .. $];
- }
- }
-
- ///
- unittest
- {
- ReadBuffer!ubyte b;
- size_t numberRead;
- ubyte[] result;
-
- // Fills the buffer with values 0..10
- numberRead = fillBuffer(b[], b.free, 0, 10);
- b += numberRead;
-
- assert(b.length == 10);
- result = b[0 .. $];
- assert(result[0] == 0);
- assert(result[9] == 9);
- b.clear();
- assert(b.length == 0);
- }
-
- mixin DefaultAllocator;
+ /// Internal buffer.
+ private T[] buffer_;
+
+ /// Filled buffer length.
+ private size_t length_;
+
+ /// Start of available data.
+ private size_t start;
+
+ /// Last position returned with $(D_KEYWORD []).
+ private size_t ring;
+
+ /// Available space.
+ private immutable size_t minAvailable = 1024;
+
+ /// Size by which the buffer will grow.
+ private immutable size_t blockSize = 8192;
+
+ invariant
+ {
+ assert(length_ <= buffer_.length);
+ assert(blockSize > 0);
+ assert(minAvailable > 0);
+ }
+
+ /**
+ * Creates a new read buffer.
+ *
+ * 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)).
+ * allocator = Allocator.
+ */
+ this(in size_t size,
+ in size_t minAvailable = 1024,
+ shared Allocator allocator = defaultAllocator) @trusted
+ {
+ this(allocator);
+ this.minAvailable = minAvailable;
+ this.blockSize = size;
+ buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
+ assert(allocator_ is null);
+ }
+ body
+ {
+ allocator_ = allocator;
+ }
+
+ /**
+ * Deallocates the internal buffer.
+ */
+ ~this() @trusted
+ {
+ allocator.deallocate(buffer_);
+ }
+
+ ///
+ unittest
+ {
+ ReadBuffer!ubyte b;
+ assert(b.capacity == 0);
+ assert(b.length == 0);
+ }
+
+ /**
+ * Returns: The size of the internal buffer.
+ */
+ @property size_t capacity() const
+ {
+ return buffer_.length;
+ }
+
+ /**
+ * Returns: Data size.
+ */
+ @property size_t length() const
+ {
+ return length_ - start;
+ }
+
+ /// Ditto.
+ alias opDollar = length;
+
+ /**
+ * Clears the buffer.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ void clear()
+ {
+ start = length_ = ring;
+ }
+
+ /**
+ * Returns: Available space.
+ */
+ @property size_t free() const
+ {
+ return length > ring ? capacity - length : capacity - ring;
+ }
+
+ ///
+ unittest
+ {
+ ReadBuffer!ubyte b;
+ size_t numberRead;
+
+ assert(b.free == 0);
+
+ // Fills the buffer with values 0..10
+ numberRead = fillBuffer(b[], b.free, 0, 10);
+ b += numberRead;
+ assert(b.free == b.blockSize - numberRead);
+ b.clear();
+ assert(b.free == b.blockSize);
+ }
+
+ /**
+ * Appends some data to the buffer.
+ *
+ * Params:
+ * length = Number of the bytes read.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref ReadBuffer opOpAssign(string op)(in size_t length)
+ if (op == "+")
+ {
+ length_ += length;
+ ring = start;
+ return this;
+ }
+
+ ///
+ unittest
+ {
+ ReadBuffer!ubyte b;
+ size_t numberRead;
+ ubyte[] result;
+
+ // Fills the buffer with values 0..10
+ numberRead = fillBuffer(b[], b.free, 0, 10);
+ b += numberRead;
+
+ result = b[0 .. $];
+ assert(result[0] == 0);
+ assert(result[1] == 1);
+ assert(result[9] == 9);
+ b.clear();
+
+ // It shouldn't overwrite, but append another 5 bytes to the buffer
+ numberRead = fillBuffer(b[], b.free, 0, 10);
+ b += numberRead;
+
+ numberRead = fillBuffer(b[], b.free, 20, 25);
+ b += numberRead;
+
+ result = b[0..$];
+ assert(result[0] == 0);
+ assert(result[1] == 1);
+ assert(result[9] == 9);
+ assert(result[10] == 20);
+ assert(result[14] == 24);
+ }
+
+ /**
+ * Params:
+ * start = Start position.
+ * end = End position.
+ *
+ * Returns: Array between $(D_PARAM start) and $(D_PARAM end).
+ */
+ T[] opSlice(in size_t start, in size_t end)
+ {
+ return buffer_[this.start + start .. this.start + end];
+ }
+
+ /**
+ * Returns a free chunk of the buffer.
+ *
+ * Add ($(D_KEYWORD +=)) the number of the read bytes after using it.
+ *
+ * Returns: A free chunk of the buffer.
+ */
+ T[] opIndex()
+ {
+ if (start > 0)
+ {
+ auto ret = buffer_[0 .. start];
+ ring = 0;
+ return ret;
+ }
+ else
+ {
+ if (capacity - length < minAvailable)
+ {
+ void[] buf = buffer_;
+ immutable cap = capacity;
+ () @trusted {
+ allocator.reallocate(buf, (cap + blockSize) * T.sizeof);
+ buffer_ = cast(T[]) buf;
+ }();
+ }
+ ring = length_;
+ return buffer_[length_ .. $];
+ }
+ }
+
+ ///
+ unittest
+ {
+ ReadBuffer!ubyte b;
+ size_t numberRead;
+ ubyte[] result;
+
+ // Fills the buffer with values 0..10
+ numberRead = fillBuffer(b[], b.free, 0, 10);
+ b += numberRead;
+
+ assert(b.length == 10);
+ result = b[0 .. $];
+ assert(result[0] == 0);
+ assert(result[9] == 9);
+ b.clear();
+ assert(b.length == 0);
+ }
+
+ mixin DefaultAllocator;
}
private unittest
{
- static assert(is(ReadBuffer!int));
+ static assert(is(ReadBuffer!int));
}
/**
@@ -304,385 +304,385 @@ private unittest * reading, because it may allocate and move elements.
*
* Params:
- * T = Buffer type.
+ * T = Buffer type.
*/
struct WriteBuffer(T = ubyte)
- if (isScalarType!T)
+ if (isScalarType!T)
{
- /// Internal buffer.
- private T[] buffer_;
-
- /// Buffer start position.
- private size_t start;
-
- /// Buffer ring area size. After this position begins buffer overflow area.
- private size_t ring;
-
- /// Size by which the buffer will grow.
- private immutable size_t blockSize;
-
- /// The position of the free area in the buffer.
- private size_t position;
-
- 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.
- * allocator = Allocator.
- *
- * Precondition: $(D_INLINECODE size > 0 && allocator !is null)
- */
- this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
- in
- {
- assert(size > 0);
- assert(allocator !is null);
- }
- body
- {
- blockSize = size;
- ring = size - 1;
- allocator_ = allocator;
- buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
- }
-
- @disable this();
-
- /**
- * Deallocates the internal buffer.
- */
- ~this()
- {
- allocator.deallocate(buffer_);
- }
-
- /**
- * Returns: The size of the internal buffer.
- */
- @property size_t capacity() const
- {
- 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 opIndex)
- * next time. Be sure to call $(D_PSYMBOL opIndex) and set $(D_KEYWORD +=)
- * until $(D_PSYMBOL length) returns 0.
- *
- * Returns: Data size.
- */
- @property size_t length() const
- {
- if (position > ring || position < start) // Buffer overflowed
- {
- return ring - start + 1;
- }
- else
- {
- return position - start;
- }
- }
-
- /// Ditto.
- alias opDollar = length;
-
- ///
- unittest
- {
- auto b = WriteBuffer!ubyte(4);
- ubyte[3] buf = [48, 23, 255];
-
- b ~= buf;
- assert(b.length == 3);
- b += 2;
- assert(b.length == 1);
-
- b ~= buf;
- assert(b.length == 2);
- b += 2;
- assert(b.length == 2);
-
- b ~= buf;
- assert(b.length == 5);
- b += b.length;
- assert(b.length == 0);
- }
-
- /**
- * Returns: Available space.
- */
- @property size_t free() const
- {
- return capacity - length;
- }
-
- /**
- * Appends data to the buffer.
- *
- * Params:
- * buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
- */
- ref WriteBuffer opOpAssign(string op)(in T[] 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) * T.sizeof;
- () @trusted {
- void[] buf = buffer_;
- allocator.reallocate(buf, newSize);
- buffer_ = cast(T[]) buf;
- }();
- }
- buffer_[position .. end] = buffer[start .. $];
- position = end;
- if (this.start == 0)
- {
- ring = capacity - 1;
- }
- }
-
- return this;
- }
-
- ///
- unittest
- {
- auto b = WriteBuffer!ubyte(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 += 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 += 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);
- }
-
- ///
- unittest
- {
- auto b = WriteBuffer!ubyte(2);
- ubyte[3] buf = [48, 23, 255];
-
- b ~= buf;
- assert(b.start == 0);
- assert(b.capacity == 4);
- assert(b.ring == 3);
- assert(b.position == 3);
- }
-
- /**
- * Sets how many bytes were written. It will shrink the buffer
- * appropriately. Always call it after $(D_PSYMBOL opIndex).
- *
- * Params:
- * length = Length of the written data.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref WriteBuffer opOpAssign(string op)(in size_t length)
- if (op == "+")
- in
- {
- assert(length <= this.length);
- }
- body
- {
- auto afterRing = ring + 1;
- auto oldStart = start;
-
- if (length <= 0)
- {
- return this;
- }
- else if (position <= afterRing)
- {
- start += length;
- if (start > 0 && position == afterRing)
- {
- position = oldStart;
- }
- }
- else
- {
- auto overflow = position - afterRing;
-
- if (overflow > length)
- {
- immutable afterLength = afterRing + length;
- buffer_[start .. start + length] = buffer_[afterRing .. afterLength];
- buffer_[afterRing .. afterLength] = buffer_[afterLength .. 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;
- }
- return this;
- }
-
- ///
- unittest
- {
- auto b = WriteBuffer!ubyte(6);
- ubyte[6] buf = [23, 23, 255, 128, 127, 9];
-
- b ~= buf;
- assert(b.length == 6);
- b += 2;
- assert(b.length == 4);
- b += 4;
- assert(b.length == 0);
- }
-
- /**
- * Returns a chunk with data.
- *
- * After calling it, set $(D_KEYWORD +=) to the length could be
- * written.
- *
- * $(D_PSYMBOL opIndex) may return only part of the data. You may need
- * to call it and set $(D_KEYWORD +=) 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.
- */
- T[] opSlice(in size_t start, in size_t end)
- {
- immutable internStart = this.start + start;
-
- if (position > ring || position < start) // Buffer overflowed
- {
- return buffer_[this.start .. ring + 1 - length + end];
- }
- else
- {
- return buffer_[this.start .. this.start + end];
- }
- }
-
- ///
- unittest
- {
- auto b = WriteBuffer!ubyte(6);
- ubyte[6] buf = [23, 23, 255, 128, 127, 9];
-
- b ~= buf;
- assert(b[0 .. $] == buf[0 .. 6]);
- b += 2;
-
- assert(b[0 .. $] == buf[2 .. 6]);
-
- b ~= buf;
- assert(b[0 .. $] == buf[2 .. 6]);
- b += b.length;
-
- assert(b[0 .. $] == buf[0 .. 6]);
- b += b.length;
- }
-
- /**
- * After calling it, set $(D_KEYWORD +=) to the length could be
- * written.
- *
- * $(D_PSYMBOL opIndex) may return only part of the data. You may need
- * to call it and set $(D_KEYWORD +=) 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.
- */
- T[] opIndex()
- {
- return opSlice(0, length);
- }
-
- mixin DefaultAllocator;
+ /// Internal buffer.
+ private T[] buffer_;
+
+ /// Buffer start position.
+ private size_t start;
+
+ /// Buffer ring area size. After this position begins buffer overflow area.
+ private size_t ring;
+
+ /// Size by which the buffer will grow.
+ private immutable size_t blockSize;
+
+ /// The position of the free area in the buffer.
+ private size_t position;
+
+ 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.
+ * allocator = Allocator.
+ *
+ * Precondition: $(D_INLINECODE size > 0 && allocator !is null)
+ */
+ this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
+ in
+ {
+ assert(size > 0);
+ assert(allocator !is null);
+ }
+ body
+ {
+ blockSize = size;
+ ring = size - 1;
+ allocator_ = allocator;
+ buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
+ }
+
+ @disable this();
+
+ /**
+ * Deallocates the internal buffer.
+ */
+ ~this()
+ {
+ allocator.deallocate(buffer_);
+ }
+
+ /**
+ * Returns: The size of the internal buffer.
+ */
+ @property size_t capacity() const
+ {
+ 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 opIndex)
+ * next time. Be sure to call $(D_PSYMBOL opIndex) and set $(D_KEYWORD +=)
+ * until $(D_PSYMBOL length) returns 0.
+ *
+ * Returns: Data size.
+ */
+ @property size_t length() const
+ {
+ if (position > ring || position < start) // Buffer overflowed
+ {
+ return ring - start + 1;
+ }
+ else
+ {
+ return position - start;
+ }
+ }
+
+ /// Ditto.
+ alias opDollar = length;
+
+ ///
+ unittest
+ {
+ auto b = WriteBuffer!ubyte(4);
+ ubyte[3] buf = [48, 23, 255];
+
+ b ~= buf;
+ assert(b.length == 3);
+ b += 2;
+ assert(b.length == 1);
+
+ b ~= buf;
+ assert(b.length == 2);
+ b += 2;
+ assert(b.length == 2);
+
+ b ~= buf;
+ assert(b.length == 5);
+ b += b.length;
+ assert(b.length == 0);
+ }
+
+ /**
+ * Returns: Available space.
+ */
+ @property size_t free() const
+ {
+ return capacity - length;
+ }
+
+ /**
+ * Appends data to the buffer.
+ *
+ * Params:
+ * buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
+ */
+ ref WriteBuffer opOpAssign(string op)(in T[] 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) * T.sizeof;
+ () @trusted {
+ void[] buf = buffer_;
+ allocator.reallocate(buf, newSize);
+ buffer_ = cast(T[]) buf;
+ }();
+ }
+ buffer_[position .. end] = buffer[start .. $];
+ position = end;
+ if (this.start == 0)
+ {
+ ring = capacity - 1;
+ }
+ }
+
+ return this;
+ }
+
+ ///
+ unittest
+ {
+ auto b = WriteBuffer!ubyte(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 += 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 += 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);
+ }
+
+ ///
+ unittest
+ {
+ auto b = WriteBuffer!ubyte(2);
+ ubyte[3] buf = [48, 23, 255];
+
+ b ~= buf;
+ assert(b.start == 0);
+ assert(b.capacity == 4);
+ assert(b.ring == 3);
+ assert(b.position == 3);
+ }
+
+ /**
+ * Sets how many bytes were written. It will shrink the buffer
+ * appropriately. Always call it after $(D_PSYMBOL opIndex).
+ *
+ * Params:
+ * length = Length of the written data.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref WriteBuffer opOpAssign(string op)(in size_t length)
+ if (op == "+")
+ in
+ {
+ assert(length <= this.length);
+ }
+ body
+ {
+ auto afterRing = ring + 1;
+ auto oldStart = start;
+
+ if (length <= 0)
+ {
+ return this;
+ }
+ else if (position <= afterRing)
+ {
+ start += length;
+ if (start > 0 && position == afterRing)
+ {
+ position = oldStart;
+ }
+ }
+ else
+ {
+ auto overflow = position - afterRing;
+
+ if (overflow > length)
+ {
+ immutable afterLength = afterRing + length;
+ buffer_[start .. start + length] = buffer_[afterRing .. afterLength];
+ buffer_[afterRing .. afterLength] = buffer_[afterLength .. 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;
+ }
+ return this;
+ }
+
+ ///
+ unittest
+ {
+ auto b = WriteBuffer!ubyte(6);
+ ubyte[6] buf = [23, 23, 255, 128, 127, 9];
+
+ b ~= buf;
+ assert(b.length == 6);
+ b += 2;
+ assert(b.length == 4);
+ b += 4;
+ assert(b.length == 0);
+ }
+
+ /**
+ * Returns a chunk with data.
+ *
+ * After calling it, set $(D_KEYWORD +=) to the length could be
+ * written.
+ *
+ * $(D_PSYMBOL opIndex) may return only part of the data. You may need
+ * to call it and set $(D_KEYWORD +=) 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.
+ */
+ T[] opSlice(in size_t start, in size_t end)
+ {
+ immutable internStart = this.start + start;
+
+ if (position > ring || position < start) // Buffer overflowed
+ {
+ return buffer_[this.start .. ring + 1 - length + end];
+ }
+ else
+ {
+ return buffer_[this.start .. this.start + end];
+ }
+ }
+
+ ///
+ unittest
+ {
+ auto b = WriteBuffer!ubyte(6);
+ ubyte[6] buf = [23, 23, 255, 128, 127, 9];
+
+ b ~= buf;
+ assert(b[0 .. $] == buf[0 .. 6]);
+ b += 2;
+
+ assert(b[0 .. $] == buf[2 .. 6]);
+
+ b ~= buf;
+ assert(b[0 .. $] == buf[2 .. 6]);
+ b += b.length;
+
+ assert(b[0 .. $] == buf[0 .. 6]);
+ b += b.length;
+ }
+
+ /**
+ * After calling it, set $(D_KEYWORD +=) to the length could be
+ * written.
+ *
+ * $(D_PSYMBOL opIndex) may return only part of the data. You may need
+ * to call it and set $(D_KEYWORD +=) 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.
+ */
+ T[] opIndex()
+ {
+ return opSlice(0, length);
+ }
+
+ mixin DefaultAllocator;
}
private unittest
{
- static assert(is(typeof(WriteBuffer!int(5))));
+ static assert(is(typeof(WriteBuffer!int(5))));
}
diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 1194f6b..cc74204 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -9,14 +9,14 @@ * 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.entry; package struct SEntry(T) { - /// Item content. - T content; + /// Item content. + T content; - /// Next item. - SEntry* next; + /// Next item. + SEntry* next; } diff --git a/source/tanya/container/package.d b/source/tanya/container/package.d index db8f025..7ba9f20 100644 --- a/source/tanya/container/package.d +++ b/source/tanya/container/package.d @@ -3,13 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * Abstract data types whose instances are collections of other objects. + * * Copyright: Eugene Wissner 2016-2017. * 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.bitvector; public import tanya.container.buffer; public import tanya.container.list; public import tanya.container.vector; diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index 6388013..48cc483 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -3,11 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * FIFO queue. + * * 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.container.queue; import core.exception; @@ -20,267 +22,267 @@ import tanya.memory; * FIFO queue. * * Params: - * T = Content type. + * T = Content type. */ struct Queue(T) { - /** - * Removes all elements from the queue. - */ - ~this() - { - while (!empty) - { - dequeue(); - } - } - - /** - * Returns how many elements are in the queue. It iterates through the queue - * to count the elements. - * - * Returns: How many elements are in the queue. - */ - size_t length() const - { - size_t len; - for (const(SEntry!T)* i = first; i !is null; i = i.next) - { - ++len; - } - return len; - } - - /// - unittest - { - Queue!int q; - - assert(q.length == 0); - q.enqueue(5); - assert(q.length == 1); - q.enqueue(4); - assert(q.length == 2); - q.enqueue(9); - assert(q.length == 3); - - q.dequeue(); - assert(q.length == 2); - q.dequeue(); - assert(q.length == 1); - q.dequeue(); - assert(q.length == 0); - } - - private void enqueueEntry(ref SEntry!T* entry) - { - if (empty) - { - first = rear = entry; - } - else - { - rear.next = entry; - rear = rear.next; - } - } - - private SEntry!T* allocateEntry() - { - auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof); - if (temp is null) - { - onOutOfMemoryError(); - } - return temp; - } - - /** - * Inserts a new element. - * - * Params: - * x = New element. - */ - void enqueue(ref T x) - { - auto temp = allocateEntry(); - - *temp = SEntry!T.init; - temp.content = x; - - enqueueEntry(temp); - } - - /// Ditto. - void enqueue(T x) - { - auto temp = allocateEntry(); - - moveEmplace(x, (*temp).content); - (*temp).next = null; - - enqueueEntry(temp); - } - - /// - unittest - { - Queue!int q; - - assert(q.empty); - q.enqueue(8); - q.enqueue(9); - assert(q.dequeue() == 8); - assert(q.dequeue() == 9); - } - - /** - * Returns: $(D_KEYWORD true) if the queue is empty. - */ - @property bool empty() const - { - return first is null; - } - - /// - unittest - { - Queue!int q; - int value = 7; - - assert(q.empty); - q.enqueue(value); - assert(!q.empty); - } - - /** - * Move the position to the next element. - * - * Returns: Dequeued element. - */ - T dequeue() - in - { - assert(!empty); - } - body - { - auto n = first.next; - T ret = move(first.content); - - allocator.dispose(first); - first = n; - return ret; - } - - /// - unittest - { - Queue!int q; - - q.enqueue(8); - q.enqueue(9); - assert(q.dequeue() == 8); - assert(q.dequeue() == 9); - } - - /** - * $(D_KEYWORD foreach) iteration. The elements will be automatically - * dequeued. - * - * 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; - - for (size_t i = 0; !empty; ++i) - { - auto e = dequeue(); - if ((result = dg(i, e)) != 0) - { - return result; - } - } - return result; - } - - /// Ditto. - int opApply(scope int delegate(ref T) @nogc dg) - { - int result; - - while (!empty) - { - auto e = dequeue(); - if ((result = dg(e)) != 0) - { - return result; - } - } - return result; - } - - /// - unittest - { - Queue!int q; - - size_t j; - q.enqueue(5); - q.enqueue(4); - q.enqueue(9); - foreach (i, e; q) - { - assert(i != 2 || e == 9); - assert(i != 1 || e == 4); - assert(i != 0 || e == 5); - ++j; - } - assert(j == 3); - assert(q.empty); - - j = 0; - q.enqueue(5); - q.enqueue(4); - q.enqueue(9); - foreach (e; q) - { - assert(j != 2 || e == 9); - assert(j != 1 || e == 4); - assert(j != 0 || e == 5); - ++j; - } - assert(j == 3); - assert(q.empty); - } - - private SEntry!T* first; - private SEntry!T* rear; - - mixin DefaultAllocator; + /** + * Removes all elements from the queue. + */ + ~this() + { + while (!empty) + { + dequeue(); + } + } + + /** + * Returns how many elements are in the queue. It iterates through the queue + * to count the elements. + * + * Returns: How many elements are in the queue. + */ + size_t length() const + { + size_t len; + for (const(SEntry!T)* i = first; i !is null; i = i.next) + { + ++len; + } + return len; + } + + /// + unittest + { + Queue!int q; + + assert(q.length == 0); + q.enqueue(5); + assert(q.length == 1); + q.enqueue(4); + assert(q.length == 2); + q.enqueue(9); + assert(q.length == 3); + + q.dequeue(); + assert(q.length == 2); + q.dequeue(); + assert(q.length == 1); + q.dequeue(); + assert(q.length == 0); + } + + private void enqueueEntry(ref SEntry!T* entry) + { + if (empty) + { + first = rear = entry; + } + else + { + rear.next = entry; + rear = rear.next; + } + } + + private SEntry!T* allocateEntry() + { + auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof); + if (temp is null) + { + onOutOfMemoryError(); + } + return temp; + } + + /** + * Inserts a new element. + * + * Params: + * x = New element. + */ + void enqueue(ref T x) + { + auto temp = allocateEntry(); + + *temp = SEntry!T.init; + temp.content = x; + + enqueueEntry(temp); + } + + /// Ditto. + void enqueue(T x) + { + auto temp = allocateEntry(); + + moveEmplace(x, (*temp).content); + (*temp).next = null; + + enqueueEntry(temp); + } + + /// + unittest + { + Queue!int q; + + assert(q.empty); + q.enqueue(8); + q.enqueue(9); + assert(q.dequeue() == 8); + assert(q.dequeue() == 9); + } + + /** + * Returns: $(D_KEYWORD true) if the queue is empty. + */ + @property bool empty() const + { + return first is null; + } + + /// + unittest + { + Queue!int q; + int value = 7; + + assert(q.empty); + q.enqueue(value); + assert(!q.empty); + } + + /** + * Move the position to the next element. + * + * Returns: Dequeued element. + */ + T dequeue() + in + { + assert(!empty); + } + body + { + auto n = first.next; + T ret = move(first.content); + + allocator.dispose(first); + first = n; + return ret; + } + + /// + unittest + { + Queue!int q; + + q.enqueue(8); + q.enqueue(9); + assert(q.dequeue() == 8); + assert(q.dequeue() == 9); + } + + /** + * $(D_KEYWORD foreach) iteration. The elements will be automatically + * dequeued. + * + * 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; + + for (size_t i = 0; !empty; ++i) + { + auto e = dequeue(); + if ((result = dg(i, e)) != 0) + { + return result; + } + } + return result; + } + + /// Ditto. + int opApply(scope int delegate(ref T) @nogc dg) + { + int result; + + while (!empty) + { + auto e = dequeue(); + if ((result = dg(e)) != 0) + { + return result; + } + } + return result; + } + + /// + unittest + { + Queue!int q; + + size_t j; + q.enqueue(5); + q.enqueue(4); + q.enqueue(9); + foreach (i, e; q) + { + assert(i != 2 || e == 9); + assert(i != 1 || e == 4); + assert(i != 0 || e == 5); + ++j; + } + assert(j == 3); + assert(q.empty); + + j = 0; + q.enqueue(5); + q.enqueue(4); + q.enqueue(9); + foreach (e; q) + { + assert(j != 2 || e == 9); + assert(j != 1 || e == 4); + assert(j != 0 || e == 5); + ++j; + } + assert(j == 3); + assert(q.empty); + } + + private SEntry!T* first; + private SEntry!T* rear; + + mixin DefaultAllocator; } /// unittest { - Queue!int q; + Queue!int q; - q.enqueue(5); - assert(!q.empty); + q.enqueue(5); + assert(!q.empty); - q.enqueue(4); - q.enqueue(9); + q.enqueue(4); + q.enqueue(9); - assert(q.dequeue() == 5); + assert(q.dequeue() == 5); - foreach (i, ref e; q) - { - assert(i != 0 || e == 4); - assert(i != 1 || e == 9); - } - assert(q.empty); + foreach (i, ref e; q) + { + assert(i != 0 || e == 4); + assert(i != 1 || e == 9); + } + assert(q.empty); } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index c14e060..0f3dc00 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * One-dimensional array. + * Single-dimensioned array. * * Copyright: Eugene Wissner 2016-2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, @@ -166,10 +166,10 @@ struct Vector(T) * range or a static array $(D_PARAM init). * * Params: - * R = Type of the initial range or size of the static array. - * init = Values to initialize the array with. - * to generate a list. - * allocator = Allocator. + * R = Type of the initial range or size of the static array. + * init = Values to initialize the array with. + * to generate a list. + * allocator = Allocator. */ this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) { @@ -200,9 +200,9 @@ struct Vector(T) * If $(D_PARAM init) is passed by reference, it will be copied. * * Params: - * R = Vector type. - * init = Source vector. - * allocator = Allocator. + * R = Vector type. + * init = Source vector. + * allocator = Allocator. */ this(R)(ref R init, shared Allocator allocator = defaultAllocator) if (is(Unqual!R == Vector)) @@ -268,8 +268,8 @@ struct Vector(T) * Creates a new $(D_PSYMBOL Vector). * * Params: - * len = Initial length of the vector. - * allocator = Allocator. + * len = Initial length of the vector. + * allocator = Allocator. */ this(in size_t len, shared Allocator allocator = defaultAllocator) { @@ -281,9 +281,9 @@ struct Vector(T) * Creates a new $(D_PSYMBOL Vector). * * Params: - * len = Initial length of the vector. - * init = Initial value to fill the vector with. - * allocator = Allocator. + * len = Initial length of the vector. + * init = Initial value to fill the vector with. + * allocator = Allocator. */ this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted { @@ -399,7 +399,7 @@ struct Vector(T) * Expands/shrinks the vector. * * Params: - * len = New length. + * len = New length. */ @property void length(in size_t len) @trusted { @@ -456,7 +456,7 @@ struct Vector(T) * affected. * * Params: - * size = Desired size. + * size = Desired size. */ void reserve(in size_t size) @trusted { @@ -514,7 +514,7 @@ struct Vector(T) * $(D_PARAM length). * * Params: - * size = Desired size. + * size = Desired size. */ void shrink(in size_t size) @trusted { @@ -585,7 +585,7 @@ struct Vector(T) * length, all elements are removed. * * Params: - * howMany = How many elements should be removed. + * howMany = How many elements should be removed. * * Returns: The number of elements removed */ @@ -618,7 +618,7 @@ struct Vector(T) * Remove all elements beloning to $(D_PARAM r). * * Params: - * r = Range originally obtained from this vector. + * r = Range originally obtained from this vector. * * Returns: Elements in $(D_PARAM r) after removing. * @@ -674,8 +674,8 @@ struct Vector(T) * Inserts the $(D_PARAM el) into the vector. * * Params: - * R = Type of the inserted value(s) (single value, range or static array). - * el = Value(s) should be inserted. + * R = Type of the inserted value(s) (single value, range or static array). + * el = Value(s) should be inserted. * * Returns: The number of elements inserted. */ @@ -767,9 +767,9 @@ struct Vector(T) * Inserts $(D_PARAM el) before or after $(D_PARAM r). * * Params: - * R = Type of the inserted value(s) (single value, range or static array). - * r = Range originally obtained from this vector. - * el = Value(s) should be inserted. + * R = Type of the inserted value(s) (single value, range or static array). + * r = Range originally obtained from this vector. + * el = Value(s) should be inserted. * * Returns: The number of elements inserted. */ @@ -946,8 +946,8 @@ struct Vector(T) * Assigns a value to the element with the index $(D_PARAM pos). * * Params: - * value = Value. - * pos = Position. + * value = Value. + * pos = Position. * * Returns: Assigned value. * @@ -987,8 +987,8 @@ struct Vector(T) * Assigns a range or a static array. * * Params: - * R = Range type or static array length. - * value = Value. + * R = Range type or static array length. + * value = Value. * * Returns: Assigned value. * @@ -1025,7 +1025,7 @@ struct Vector(T) /** * Params: - * pos = Index. + * pos = Index. * * Returns: The value at a specified index. * @@ -1073,7 +1073,7 @@ struct Vector(T) * Comparison for equality. * * Params: - * that = The vector to compare with. + * that = The vector to compare with. * * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) * otherwise. @@ -1099,8 +1099,8 @@ struct Vector(T) * Comparison for equality. * * Params: - * R = Right hand side type. - * that = Right hand side vector range. + * R = Right hand side type. + * that = Right hand side vector range. * * Returns: $(D_KEYWORD true) if the vector and the range are equal, * $(D_KEYWORD false) otherwise. @@ -1135,7 +1135,7 @@ struct Vector(T) * $(D_KEYWORD foreach) iteration. * * Params: - * dg = $(D_KEYWORD foreach) body. + * dg = $(D_KEYWORD foreach) body. * * Returns: The value returned from $(D_PARAM dg). */ @@ -1301,8 +1301,8 @@ struct Vector(T) /** * Params: - * i = Slice start. - * j = Slice end. + * i = Slice start. + * j = Slice end. * * Returns: A range that iterates over elements of the container from * index $(D_PARAM i) up to (excluding) index $(D_PARAM j). @@ -1380,11 +1380,11 @@ struct Vector(T) * Slicing assignment. * * Params: - * R = Type of the assigned slice or length of the static array should be - * assigned. - * value = New value (single value, input range or static array). - * i = Slice start. - * j = Slice end. + * R = Type of the assigned slice or length of the static array should be + * assigned. + * value = New value (single value, input range or static array). + * i = Slice start. + * j = Slice end. * * Returns: Slice with the assigned part of the vector. * |
