From e32af2d09e2e26f7c88c0147989eb5596a82cfe2 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 19 Dec 2016 21:24:28 +0100 Subject: [PATCH] Add scalar type template parameter for buffers --- source/tanya/async/event/iocp.d | 9 +- source/tanya/async/event/selector.d | 12 +- source/tanya/async/watcher.d | 5 +- source/tanya/container/buffer.d | 1122 ++++++++++++++------------- source/tanya/memory/allocator.d | 7 +- 5 files changed, 582 insertions(+), 573 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_; + /// Internal buffer. + private T[] buffer_; - /// Filled buffer length. - protected size_t length_; + /// Filled buffer length. + private size_t length_; - /// Start of available data. - protected size_t start; + /// Start of available data. + private size_t start; - /// Last position returned with $(D_KEYWORD []). - protected size_t ring; + /// Last position returned with $(D_KEYWORD []). + private size_t ring; - /// Available space. - protected immutable size_t minAvailable; + /// Available space. + private immutable size_t minAvailable = 1024; - /// Size by which the buffer will grow. - protected immutable size_t blockSize; + /// Size by which the buffer will grow. + private immutable size_t blockSize = 8192; - /// Allocator. - protected shared Allocator allocator; + invariant + { + assert(length_ <= buffer_.length); + assert(blockSize > 0); + assert(minAvailable > 0); + } - @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)). + /** + * 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); - } + */ + 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); + } - /** - * Deallocates the internal buffer. - */ - ~this() @nogc - { - allocator.dispose(buffer_); - } + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator_ is null); + } + body + { + allocator_ = allocator; + } - /// - unittest - { - auto b = defaultAllocator.make!ReadBuffer; - assert(b.capacity == 8192); - assert(b.length == 0); + /** + * Deallocates the internal buffer. + */ + ~this() @trusted + { + allocator.deallocate(buffer_); + } - defaultAllocator.dispose(b); - } + /// + unittest + { + ReadBuffer!ubyte b; + assert(b.capacity == 0); + assert(b.length == 0); + } - /** - * Returns: The size of the internal buffer. - */ - @property size_t capacity() const @nogc @safe pure nothrow - { - return buffer_.length; - } + /** + * Returns: The size of the internal buffer. + */ + @property size_t capacity() const + { + return buffer_.length; + } - /** - * Returns: Data size. - */ - @property size_t length() const @nogc @safe pure nothrow - { - return length_ - start; - } + /** + * Returns: Data size. + */ + @property size_t length() const + { + return length_ - start; + } - /** - * Clears the buffer. - * - * Returns: $(D_KEYWORD this). - */ - ReadBuffer clear() pure nothrow @safe @nogc - { - start = length_ = ring; - return this; - } + /// Ditto. + alias opDollar = length; - /** - * Returns: Available space. - */ - @property size_t free() const pure nothrow @safe @nogc - { - return length > ring ? capacity - length : capacity - ring; - } + /** + * Clears the buffer. + * + * Returns: $(D_KEYWORD this). + */ + void clear() + { + start = length_ = ring; + } - /// - unittest - { - auto b = defaultAllocator.make!ReadBuffer; - size_t numberRead; + /** + * Returns: Available space. + */ + @property size_t free() const + { + return length > ring ? capacity - length : capacity - ring; + } - // Fills the buffer with values 0..10 - assert(b.free == b.blockSize); + /// + unittest + { + ReadBuffer!ubyte b; + size_t numberRead; - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; - assert(b.free == b.blockSize - numberRead); - b.clear(); - assert(b.free == b.blockSize); + assert(b.free == 0); - defaultAllocator.dispose(b); - } + // 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). - */ - ReadBuffer opOpAssign(string op)(size_t length) @nogc - if (op == "+") - { - length_ += length; - ring = start; - return this; - } + /** + * 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 - { - auto b = defaultAllocator.make!ReadBuffer; - size_t numberRead; - ubyte[] result; + /// + 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; + // 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(); + 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; + // 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; + 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); + 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); - } + /** + * 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: Length of available data. - */ - @property size_t opDollar() const pure nothrow @safe @nogc - { - return length; - } + /** + * 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_ .. $]; + } + } - /** - * 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]; - } + /// + unittest + { + ReadBuffer!ubyte b; + size_t numberRead; + ubyte[] result; - /** - * 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_..$]; - } - } + // Fills the buffer with values 0..10 + numberRead = fillBuffer(b[], b.free, 0, 10); + b += numberRead; - /// - unittest - { - auto b = defaultAllocator.make!ReadBuffer; - size_t numberRead; - ubyte[] result; + assert(b.length == 10); + result = b[0..$]; + assert(result[0] == 0); + assert(result[9] == 9); + b.clear(); + assert(b.length == 0); + } - // Fills the buffer with values 0..10 - numberRead = fillBuffer(b[], b.free, 0, 10); - b += numberRead; + mixin DefaultAllocator; +} - assert(b.length == 10); - result = b[0..$]; - assert(result[0] == 0); - assert(result[9] == 9); - b.clear(); - assert(b.length == 0); - - defaultAllocator.dispose(b); - } +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); - assert(allocator !is null); - } - - /** - * Params: - * size = Initial buffer size and the size by which the buffer will - * grow. + /** + * 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); - } + */ + 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); + } - /** - * Deallocates the internal buffer. - */ - ~this() @nogc - { - allocator.dispose(buffer_); - } + @disable this(); - /** - * Returns: The size of the internal buffer. - */ - @property size_t capacity() const @nogc @safe pure nothrow - { - return buffer_.length; - } + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + allocator_ = allocator; + } - /** - * 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; - } - } + /** + * Deallocates the internal buffer. + */ + ~this() + { + allocator.deallocate(buffer_); + } - /** - * Returns: Length of available data. - */ - @property size_t opDollar() const pure nothrow @safe @nogc - { - return length; - } + /** + * Returns: The size of the internal buffer. + */ + @property size_t capacity() const + { + return buffer_.length; + } - /// - unittest - { - auto b = defaultAllocator.make!WriteBuffer(4); - ubyte[3] buf = [48, 23, 255]; + /** + * 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; + } + } - b ~= buf; - assert(b.length == 3); - b += 2; - assert(b.length == 1); + /// Ditto. + alias opDollar = length; - b ~= buf; - assert(b.length == 2); - b += 2; - assert(b.length == 2); + /// + unittest + { + auto b = WriteBuffer!ubyte(4); + ubyte[3] buf = [48, 23, 255]; - b ~= buf; - assert(b.length == 5); - b += b.length; - assert(b.length == 0); + b ~= buf; + assert(b.length == 3); + b += 2; + assert(b.length == 1); - defaultAllocator.dispose(b); - } + b ~= buf; + assert(b.length == 2); + b += 2; + assert(b.length == 2); - /** - * Returns: Available space. - */ - @property size_t free() const @nogc @safe pure nothrow - { - return capacity - length; - } + b ~= buf; + assert(b.length == 5); + b += b.length; + assert(b.length == 0); + } - /** - * 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; + /** + * Returns: Available space. + */ + @property size_t free() const + { + return capacity - length; + } - if (position >= this.start && position <= ring) - { - auto afterRing = ring + 1; + /** + * 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; - 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; - } - } + if (position >= this.start && position <= ring) + { + auto afterRing = ring + 1; - // 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; - } + 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; + } + } - // 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; + // 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; + } - allocator.resizeArray!ubyte(buffer_, newSize); - } - buffer_[position..end] = buffer[start..$]; - position = end; - if (this.start == 0) - { - ring = capacity - 1; - } - } + // 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; - } + return this; + } - /// - unittest - { - auto b = defaultAllocator.make!WriteBuffer(4); - ubyte[3] buf = [48, 23, 255]; + /// + 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 ~= 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 == 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 += 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 = WriteBuffer!ubyte(2); - b = make!WriteBuffer(defaultAllocator, 2); + b ~= buf; + assert(b.start == 0); + assert(b.capacity == 4); + assert(b.ring == 3); + assert(b.position == 3); + } - 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; - defaultAllocator.dispose(b); - } + if (length <= 0) + { + return this; + } + else if (position <= afterRing) + { + start += length; + if (start > 0 && position == afterRing) + { + position = oldStart; + } + } + else + { + auto overflow = position - afterRing; - /** - * 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 (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 (length <= 0) - { - return this; - } - else if (position <= afterRing) - { - start += length; - if (start > 0 && position == afterRing) - { - position = oldStart; - } - } - else - { - auto overflow = position - afterRing; + if (start == position) + { + if (position != afterRing) + { + position = 0; + } + start = 0; + ring = capacity - 1; + } + } + if (start > ring) + { + start = 0; + } + return this; + } - 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; + /// + unittest + { + auto b = WriteBuffer!ubyte(6); + ubyte[6] buf = [23, 23, 255, 128, 127, 9]; - if (start == position) - { - if (position != afterRing) - { - position = 0; - } - start = 0; - ring = capacity - 1; - } - } - if (start > ring) - { - start = 0; - } - return this; - } + b ~= buf; + assert(b.length == 6); + b += 2; + assert(b.length == 4); + b += 4; + assert(b.length == 0); + } - /// - unittest - { - auto b = defaultAllocator.make!WriteBuffer; - ubyte[6] buf = [23, 23, 255, 128, 127, 9]; + /** + * 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; - b ~= buf; - assert(b.length == 6); - b += 2; - assert(b.length == 4); - b += 4; - assert(b.length == 0); + if (position > ring || position < start) // Buffer overflowed + { + return buffer_[this.start .. ring + 1 - length + end]; + } + else + { + return buffer_[this.start .. this.start + end]; + } + } - defaultAllocator.dispose(b); - } + /// + unittest + { + auto b = WriteBuffer!ubyte(6); + ubyte[6] buf = [23, 23, 255, 128, 127, 9]; - /** - * 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; + b ~= buf; + assert(b[0 .. $] == buf[0 .. 6]); + b += 2; - if (position > ring || position < start) // Buffer overflowed - { - return buffer_[this.start.. ring + 1 - length + end]; - } - else - { - return buffer_[this.start.. this.start + end]; - } - } + assert(b[0 .. $] == buf[2 .. 6]); - /// - unittest - { - auto b = defaultAllocator.make!WriteBuffer(6); - ubyte[6] buf = [23, 23, 255, 128, 127, 9]; + b ~= buf; + assert(b[0 .. $] == buf[2 .. 6]); + b += b.length; - b ~= buf; - assert(b[0..$] == buf[0..6]); - b += 2; + assert(b[0 .. $] == buf[0 .. 6]); + b += b.length; + } - assert(b[0..$] == buf[2..6]); + /** + * 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); + } - 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); - } + mixin DefaultAllocator; +} + +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() {