Construct an object without any user-provided blocks. All blocks will be allocated dynamically.
Construct an object that will use the provided buffer
Construct an object that will use the provided buffers
Clears all blocks in reverse order
Total element capacity
Release all unoccupied heap blocks from the blocks array
Construct a new _element at the end
Total number of heap blocks and the number of those that are occupied by at least one element
Number of elements currently available
Move an _element to the end
Number of elements in the block
Return a reference to an element
Append an _element
A range providing access _to the specified elements
A range to all elements; the same as [0..$]
Remove elements from the head of the block
String representation of this object useful mostly for debugging
the type of the elements to store
// This example starts with user-provided buffers. (It is possible to // provide a single buffer but that case would need to allocate at least one // heap buffer later e.g. in a sliding window use case.) enum size = 42; enum count = 2; ubyte[size][count] buffers; // Create a circular buffer of ints using those buffers auto c = CircularBlocks!int(buffers); // We can't be certain about total capacity because initial parts of the // buffers may not be used due to alignment requirements. At least make sure // the tests will be valid const initialCapacity = c.capacity; assert(initialCapacity > 0, "Invalid unittest"); // Populate with some elements iota(initialCapacity).each!(i => c ~= i.to!int); // All capacity should be utilized at this point c.length.shouldBe(c.capacity); // As all elements are on provided buffers so far, there should be no heap // allocation yet c.heapBlockOccupancy.total.shouldBe(0); c.heapBlockOccupancy.occupied.shouldBe(0); // Adding one more element should allocate one heap block c ~= 42; c.heapBlockOccupancy.total.shouldBe(1); c.heapBlockOccupancy.occupied.shouldBe(1); assert(c.capacity > initialCapacity); // Remove all elements c.removeFrontN(c.length); c.length.shouldBe(0); c.heapBlockOccupancy.total.shouldBe(1); c.heapBlockOccupancy.occupied.shouldBe(0); // Release the unoccupied heap blocks c.compact(); c.heapBlockOccupancy.total.shouldBe(0); c.heapBlockOccupancy.occupied.shouldBe(0); // Because we started with user buffers, the capacity should never be less // than initial capacity c.capacity.shouldBe(initialCapacity);
// This example uses a single user-provided buffer. It is inevitable that // there will be at least one heap block allocation if more elements added // than the capacity of the user-provided buffer. ubyte[100] buffer; auto c = CircularBlocks!string(buffer[]); iota(c.capacity).each!(_ => c ~= "hi"); c.length.shouldBe(c.capacity); // There should be no heap block allocation c.heapBlockOccupancy.total.shouldBe(0); c.heapBlockOccupancy.occupied.shouldBe(0);
// This example does not start with any user-provided buffer const heapBlockCapacity = 100; auto c = CircularBlocks!double(heapBlockCapacity); // Due to lazy allocation, no heap block should be allocated yet c.capacity.shouldBe(0); // Adding elements should cause heap block allocations c ~= 1; assert(c.capacity != 0); c.heapBlockOccupancy.total.shouldBe(1); c.heapBlockOccupancy.occupied.shouldBe(1);
// When user-provided buffers are sufficiently large for a sliding window of // elements, no heap block should be allocated ubyte[64][2] buffers; auto c = CircularBlocks!int(buffers); assert(c.capacity != 0); // Start with some elements filling half the capacity iota(c.capacity / 2).each!(i => c ~= i.to!int); // Use the rest of the capacity as the width of a sliding window const windowWidth = c.capacity - c.length; // Add and remove equal number of elements for many number of times foreach (i; 0 .. 117) { iota(windowWidth).each!(i => c ~= i.to!int); // Prove that buffer is completely full after the additions c.length.shouldBe(c.capacity); c.removeFrontN(windowWidth); } // No heap block should have been allocated c.heapBlockOccupancy.total.shouldBe(0);
Represents an expanding circular buffer implemented as blocks of elements of T.
The elements are added to the end, removed from the front, and are never moved around on the buffers.
If user blocks are provided, those blocks are used for the elements until there is no more room on them, in which case new blocks are allocated from the GC heap.