module utility; private import std.range : isInputRange, isInfinite, isForwardRange, ElementType; auto concat(T : E[n], E, size_t n)(in E[][] args...) @nogc { size_t offset = 0; T result = void; foreach (arr; args) { result[offset .. offset + arr.length] = arr; offset += arr.length; } assert(offset == result.length); return result; } @safe unittest { assert(concat!(int[0])() == []); assert(concat!(int[0])([]) == []); assert(concat!(int[0])([], []) == []); assert(concat!(int[1])([1]) == [1]); assert(concat!(int[1])([1], []) == [1]); assert(concat!(int[1])([], [1]) == [1]); assert(concat!(int[2])([1, 2]) == [1, 2]); assert(concat!(int[2])([1], [2]) == [1, 2]); assert(concat!(int[6])([1], [2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6]); assert(concat!(char[12])("Hello", " ", "World!") == "Hello World!"); } auto bufferedChunks(Source)(Source source, size_t chunkSize) if (isInputRange!Source) { static struct BufferedChunks { this(Source source, size_t chunkSize) { assert(chunkSize != 0, "Cannot create a Chunk with an empty chunkSize"); this.source = source; this.chunkSize = chunkSize; buffer.length = chunkSize; foreach (i; 0 .. chunkSize) { if (!this.source.empty) { buffer[i] = this.source.front; this.source.popFront; bufferLength++; } else break; } } auto front() @property { assert(!empty); return buffer[0 .. bufferLength]; } void popFront() { assert(!empty); bufferLength = 0; if (!source.empty) { foreach (i; 0 .. chunkSize) { if (!source.empty) { buffer[i] = source.front; source.popFront; bufferLength++; } else break; } } } static if (isInfinite!Source) { enum empty = false; } else { bool empty() @property { return bufferLength == 0; } } static if (isForwardRange!Source) { auto save() { auto ret = this; ret.source = source.save; ret.buffer = buffer.dup; return ret; } } private: Source source; size_t chunkSize; ElementType!Source[] buffer; size_t bufferLength; } return BufferedChunks(source, chunkSize); } version (unittest) private import std.array; @safe unittest { int[] testArray; auto _bufferedChunks = bufferedChunks(testArray, 1); assert(_bufferedChunks.empty); } @safe unittest { int[] testArray; auto _bufferedChunks = bufferedChunks(testArray, 2); assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0], 1); assert(_bufferedChunks.front == [0]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0], 2); assert(_bufferedChunks.front == [0]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0, 1], 1); assert(_bufferedChunks.front == [0]); _bufferedChunks.popFront; assert(_bufferedChunks.front == [1]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0, 1], 2); assert(_bufferedChunks.front == [0, 1]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0, 1, 2], 2); assert(_bufferedChunks.front == [0, 1]); _bufferedChunks.popFront; assert(_bufferedChunks.front == [2]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @safe unittest { auto _bufferedChunks = bufferedChunks([0, 1, 2, 3], 2); assert(_bufferedChunks.front == [0, 1]); _bufferedChunks.popFront; assert(_bufferedChunks.front == [2, 3]); _bufferedChunks.popFront; assert(_bufferedChunks.empty); } @system unittest { import std.exception : assertThrown; import core.exception : AssertError; assertThrown!AssertError(bufferedChunks([0], 0)); } @safe unittest { auto _bufferedChunks = bufferedChunks([0], 1); auto otherBufferedChunks = _bufferedChunks.save; _bufferedChunks.popFront; assert(_bufferedChunks.empty); assert(otherBufferedChunks.front == [0]); } @safe unittest { auto _bufferedChunks = bufferedChunks([0, 1], 1); auto otherBufferedChunks = _bufferedChunks.save; _bufferedChunks.popFront; assert(_bufferedChunks.front == [1]); assert(otherBufferedChunks.front == [0]); }