diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/tanya/async/event/iocp.d | 9 | ||||
| -rw-r--r-- | source/tanya/async/event/selector.d | 12 | ||||
| -rw-r--r-- | source/tanya/async/watcher.d | 5 | ||||
| -rw-r--r-- | source/tanya/container/buffer.d | 1260 | ||||
| -rw-r--r-- | source/tanya/memory/allocator.d | 7 |
5 files changed, 651 insertions, 642 deletions
diff --git a/source/tanya/async/event/iocp.d b/source/tanya/async/event/iocp.d index 4eb3bc4..1214302 100644 --- a/source/tanya/async/event/iocp.d +++ b/source/tanya/async/event/iocp.d @@ -30,7 +30,7 @@ class IOCPStreamTransport : StreamTransport { private OverlappedConnectedSocket socket_; - private WriteBuffer input; + private WriteBuffer!ubyte input; /** * Creates new completion port transport. @@ -45,12 +45,7 @@ class IOCPStreamTransport : StreamTransport body { socket_ = socket; - input = MmapPool.instance.make!WriteBuffer(8192, MmapPool.instance); - } - - ~this() - { - MmapPool.instance.dispose(input); + input = WriteBuffer!ubyte(8192, MmapPool.instance); } @property inout(OverlappedConnectedSocket) socket() diff --git a/source/tanya/async/event/selector.d b/source/tanya/async/event/selector.d index 85f8298..5f42bd2 100644 --- a/source/tanya/async/event/selector.d +++ b/source/tanya/async/event/selector.d @@ -30,7 +30,7 @@ class SelectorStreamTransport : StreamTransport private ConnectedSocket socket_; /// Input buffer. - package WriteBuffer input; + package WriteBuffer!ubyte input; private SelectorLoop loop; @@ -46,15 +46,7 @@ class SelectorStreamTransport : StreamTransport { socket_ = socket; this.loop = loop; - input = MmapPool.instance.make!WriteBuffer(8192, MmapPool.instance); - } - - /** - * Close the transport and deallocate the data buffers. - */ - ~this() @nogc - { - MmapPool.instance.dispose(input); + input = WriteBuffer!ubyte(8192, MmapPool.instance); } /** diff --git a/source/tanya/async/watcher.d b/source/tanya/async/watcher.d index f97d692..f6915ab 100644 --- a/source/tanya/async/watcher.d +++ b/source/tanya/async/watcher.d @@ -139,7 +139,7 @@ class IOWatcher : ConnectionWatcher /** * Returns: Underlying output buffer. */ - package ReadBuffer output; + package ReadBuffer!ubyte output; /** * Params: @@ -157,7 +157,7 @@ class IOWatcher : ConnectionWatcher super(); transport_ = transport; protocol_ = protocol; - output = MmapPool.instance.make!ReadBuffer(8192, 1024, MmapPool.instance); + output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance); active = true; } @@ -166,7 +166,6 @@ class IOWatcher : ConnectionWatcher */ protected ~this() @nogc { - MmapPool.instance.dispose(output); MmapPool.instance.dispose(protocol_); } diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index 29ceb17..6551b65 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -10,27 +10,28 @@ */
module tanya.container.buffer;
+import std.traits;
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;
+ }
}
/**
@@ -42,627 +43,650 @@ version (unittest) * available data. But only one asynchronous call at a time is supported. Be
* sure to call $(D_PSYMBOL ReadBuffer.clear()) before you append the result
* of the pended asynchronous call.
+ *
+ * Params:
+ * T = Buffer type.
*/
-class ReadBuffer
+struct ReadBuffer(T = ubyte)
+ if (isScalarType!T)
{
- /// Internal buffer.
- protected ubyte[] buffer_;
-
- /// Filled buffer length.
- protected size_t length_;
-
- /// Start of available data.
- protected size_t start;
-
- /// Last position returned with $(D_KEYWORD []).
- protected size_t ring;
-
- /// Available space.
- protected immutable size_t minAvailable;
-
- /// Size by which the buffer will grow.
- protected immutable size_t blockSize;
-
- /// Allocator.
- protected shared Allocator allocator;
-
- @nogc invariant
- {
- assert(length_ <= buffer_.length);
- assert(blockSize > 0);
- assert(minAvailable > 0);
- assert(allocator !is null);
- }
-
- /**
- * 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)).
+ /// 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(size_t size = 8192,
- size_t minAvailable = 1024,
- shared Allocator allocator = defaultAllocator) @nogc
- {
- this.minAvailable = minAvailable;
- this.blockSize = size;
- this.allocator = allocator;
- allocator.resizeArray!ubyte(buffer_, size);
- }
-
- /**
- * Deallocates the internal buffer.
- */
- ~this() @nogc
- {
- allocator.dispose(buffer_);
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!ReadBuffer;
- assert(b.capacity == 8192);
- assert(b.length == 0);
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * Returns: The size of the internal buffer.
- */
- @property size_t capacity() const @nogc @safe pure nothrow
- {
- return buffer_.length;
- }
-
- /**
- * Returns: Data size.
- */
- @property size_t length() const @nogc @safe pure nothrow
- {
- return length_ - start;
- }
-
- /**
- * Clears the buffer.
- *
- * Returns: $(D_KEYWORD this).
- */
- ReadBuffer clear() pure nothrow @safe @nogc
- {
- start = length_ = ring;
- return this;
- }
-
- /**
- * Returns: Available space.
- */
- @property size_t free() const pure nothrow @safe @nogc
- {
- return length > ring ? capacity - length : capacity - ring;
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!ReadBuffer;
- size_t numberRead;
-
- // Fills the buffer with values 0..10
- assert(b.free == b.blockSize);
-
- numberRead = fillBuffer(b[], b.free, 0, 10);
- b += numberRead;
- assert(b.free == b.blockSize - numberRead);
- b.clear();
- assert(b.free == b.blockSize);
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * Appends some data to the buffer.
- *
- * Params:
- * length = Number of the bytes read.
- *
- * Returns: $(D_KEYWORD this).
- */
- ReadBuffer opOpAssign(string op)(size_t length) @nogc
- if (op == "+")
- {
- length_ += length;
- ring = start;
- return this;
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!ReadBuffer;
- 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);
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * Returns: Length of available data.
- */
- @property size_t opDollar() const pure nothrow @safe @nogc
- {
- return length;
- }
-
- /**
- * Params:
- * start = Start position.
- * end = End position.
- *
- * Returns: Array between $(D_PARAM start) and $(D_PARAM end).
- */
- @property ubyte[] opSlice(size_t start, size_t end) pure nothrow @safe @nogc
- {
- 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.
- */
- ubyte[] opIndex() @nogc
- {
- if (start > 0)
- {
- auto ret = buffer_[0..start];
- ring = 0;
- return ret;
- }
- else
- {
- if (capacity - length < minAvailable)
- {
- allocator.resizeArray!ubyte(buffer_, capacity + blockSize);
- }
- ring = length_;
- return buffer_[length_..$];
- }
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!ReadBuffer;
- 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);
-
- defaultAllocator.dispose(b);
- }
+ */
+ 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;
+ }();
+ buffer_[cap .. $] = T.init;
+ }
+ 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));
}
/**
* Circular, self-expanding buffer with overflow support. Can be used with
- * functions returning returning the number of the transferred bytes.
+ * functions returning the number of the transferred 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.
+ *
+ * Params:
+ * T = Buffer type.
*/
-class WriteBuffer
+struct WriteBuffer(T = ubyte)
+ if (isScalarType!T)
{
- /// Internal buffer.
- protected ubyte[] buffer_;
+ /// Internal buffer.
+ private T[] buffer_;
- /// Buffer start position.
- protected size_t start;
+ /// Buffer start position.
+ private size_t start;
- /// Buffer ring area size. After this position begins buffer overflow area.
- protected size_t ring;
+ /// Buffer ring area size. After this position begins buffer overflow area.
+ private size_t ring;
- /// Size by which the buffer will grow.
- protected immutable size_t blockSize;
+ /// Size by which the buffer will grow.
+ private immutable size_t blockSize;
- /// The position of the free area in the buffer.
- protected size_t position;
+ /// The position of the free area in the buffer.
+ private size_t position;
- /// Allocator.
- protected shared Allocator allocator;
+ invariant
+ {
+ assert(blockSize > 0);
+ // Position can refer to an element outside the buffer if the buffer is full.
+ assert(position <= buffer_.length);
+ }
- @nogc 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.
+ */
+ this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
+ in
+ {
+ assert(size > 0);
+ }
+ body
+ {
+ blockSize = size;
+ ring = size - 1;
+ this(allocator);
+ buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
+ }
+
+ @disable this();
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
assert(allocator !is null);
- }
+ }
+ body
+ {
+ allocator_ = allocator;
+ }
+
+ /**
+ * 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);
+
+ b = WriteBuffer!ubyte(2);
+
+ 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;
+}
- /**
- * Params:
- * size = Initial buffer size and the size by which the buffer will
- * grow.
- * allocator = Allocator.
- */
- this(size_t size = 8192, shared Allocator allocator = defaultAllocator)
- @nogc
- {
- this.allocator = allocator;
- blockSize = size;
- ring = size - 1;
- allocator.resizeArray!ubyte(buffer_, size);
- }
-
- /**
- * Deallocates the internal buffer.
- */
- ~this() @nogc
- {
- allocator.dispose(buffer_);
- }
-
- /**
- * Returns: The size of the internal buffer.
- */
- @property size_t capacity() const @nogc @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_KEYWORD +=)
- * until $(D_PSYMBOL length) returns 0.
- *
- * Returns: Data size.
- */
- @property size_t length() const @nogc @safe pure nothrow
- {
- if (position > ring || position < start) // Buffer overflowed
- {
- return ring - start + 1;
- }
- else
- {
- return position - start;
- }
- }
-
- /**
- * Returns: Length of available data.
- */
- @property size_t opDollar() const pure nothrow @safe @nogc
- {
- return length;
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!WriteBuffer(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);
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * Returns: Available space.
- */
- @property size_t free() const @nogc @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) @nogc
- 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;
-
- allocator.resizeArray!ubyte(buffer_, newSize);
- }
- buffer_[position..end] = buffer[start..$];
- position = end;
- if (this.start == 0)
- {
- ring = capacity - 1;
- }
- }
-
- return this;
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!WriteBuffer(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);
-
- defaultAllocator.dispose(b);
-
- b = make!WriteBuffer(defaultAllocator, 2);
-
- b ~= buf;
- assert(b.start == 0);
- assert(b.capacity == 4);
- assert(b.ring == 3);
- assert(b.position == 3);
-
- defaultAllocator.dispose(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.
- *
- * Returns: $(D_KEYWORD this).
- */
- @property WriteBuffer opOpAssign(string op)(size_t length) pure nothrow @safe @nogc
- 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) {
- 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;
- }
- return this;
- }
-
- ///
- unittest
- {
- auto b = defaultAllocator.make!WriteBuffer;
- 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);
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * Returns a chunk with data.
- *
- * After calling it, set $(D_KEYWORD +=) 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_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.
- */
- @property ubyte[] opSlice(size_t start, size_t end) pure nothrow @safe @nogc
- {
- 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 = defaultAllocator.make!WriteBuffer(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;
-
- defaultAllocator.dispose(b);
- }
-
- /**
- * After calling it, set $(D_KEYWORD +=) 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_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.
- */
- @property ubyte[] opIndex() pure nothrow @safe @nogc
- {
- return opSlice(0, length);
- }
+private unittest
+{
+ static assert(is(WriteBuffer!int));
}
diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index 4da32c8..3acf08b 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -54,10 +54,9 @@ interface Allocator /** * The mixin generates common methods for classes and structs using - * allocators. It provides a protected member, constructor and a read-only - * property, that checks if an allocator was already set and sets it to the - * default one, if not (useful for structs which don't have a default - * constructor). + * allocators. It provides a protected member, constructor and a read-only property, + * that checks if an allocator was already set and sets it to the default + * one, if not (useful for structs which don't have a default constructor). */ mixin template DefaultAllocator() { |
