1 module dmdtags.appender; 2 3 import core.memory: GC; 4 5 enum initialCapacity = 16; 6 enum growFactor = 2; 7 8 // Simple extern(C++)-compatible Appender 9 struct Appender(T) 10 { 11 private static struct Header 12 { 13 size_t length; 14 size_t capacity; 15 T[0] data; // for alignment 16 } 17 18 private Header* header; 19 20 private static size_t allocSize(size_t capacity) 21 { 22 import std.experimental.checkedint: checked; 23 24 return (Header.sizeof + T.sizeof.checked * capacity).get; 25 } 26 27 private inout(T)[] data() inout 28 { 29 if (header is null) { 30 return null; 31 } else { 32 return header.data.ptr[0 .. header.length]; 33 } 34 } 35 36 inout(T)[] opIndex() inout 37 { 38 return data; 39 } 40 41 void put(T item) 42 { 43 import std.experimental.checkedint: checked; 44 45 if (header is null) { 46 header = cast(Header*) GC.malloc(allocSize(initialCapacity)); 47 header.length = 0; 48 header.capacity = initialCapacity; 49 } 50 51 assert(header.length <= header.capacity); 52 53 if (header.length == header.capacity) { 54 T[] oldData = data; 55 Header* oldHeader = header; 56 57 immutable size_t newCapacity = (oldHeader.capacity.checked * growFactor).get; 58 header = cast(Header*) GC.malloc(allocSize(newCapacity)); 59 header.length = oldHeader.length; 60 header.capacity = newCapacity; 61 62 data[] = oldData[]; 63 } 64 65 header.length += 1; 66 data[$ - 1] = item; 67 } 68 } 69 70 unittest { 71 import std.range: iota; 72 import std.array: array; 73 74 Appender!int app; 75 76 foreach (i; 0 .. 20) { 77 app.put(i); 78 } 79 80 assert(app[] == iota(20).array); 81 }