1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module vox.utils.buffer; 7 8 struct Buffer(T) 9 { 10 import vox.utils : nextPOT, format; 11 import std.experimental.allocator.gc_allocator; 12 alias allocator = GCAllocator.instance; 13 14 T* bufPtr; 15 uint capacity; 16 T[] buf() { return bufPtr[0..capacity]; } 17 // Must be kept private since it can be used to check for avaliable space 18 // when used as output range 19 uint length; 20 alias opDollar = length; 21 22 // postblit 23 this(this) 24 { 25 import core.memory; 26 void[] tmp = allocator.allocate(capacity*T.sizeof); 27 T* newBufPtr = cast(T*)tmp.ptr; 28 newBufPtr[0..length] = bufPtr[0..length]; 29 bufPtr = newBufPtr; 30 GC.addRange(bufPtr, capacity * T.sizeof, typeid(T)); 31 } 32 33 bool empty() { return length == 0; } 34 35 void put(T[] items ...) 36 { 37 reserve(items.length); 38 bufPtr[length..length+items.length] = items; 39 length += cast(uint)items.length; 40 } 41 42 void put(R)(R itemRange) 43 { 44 foreach(item; itemRange) 45 put(item); 46 } 47 48 void stealthPut(T item) 49 { 50 reserve(1); 51 bufPtr[length] = item; 52 } 53 54 /// Increases length and returns void-initialized slice to be filled by user 55 T[] voidPut(size_t howMany) 56 { 57 reserve(howMany); 58 length += howMany; 59 return buf[length-howMany..length]; 60 } 61 62 ref T opIndex(size_t at) 63 { 64 assert(at < capacity, format("opIndex(%s), capacity %s", at, capacity)); 65 return bufPtr[at]; 66 } 67 68 ref T back() { return bufPtr[length-1]; } 69 70 T[] data() { 71 return bufPtr[0..length]; 72 } 73 T[] data(size_t from, size_t to) { 74 return bufPtr[from..to]; 75 } 76 77 alias opSlice = data; 78 79 void clear() nothrow { 80 length = 0; 81 } 82 83 void reserve(size_t items) 84 { 85 if (capacity - length < items) 86 { 87 import core.memory; 88 GC.removeRange(bufPtr); 89 size_t newCapacity = nextPOT(capacity + items); 90 void[] tmp = buf; 91 allocator.reallocate(tmp, newCapacity*T.sizeof); 92 bufPtr = cast(T*)tmp.ptr; 93 capacity = cast(uint)(tmp.length / T.sizeof); 94 GC.addRange(bufPtr, capacity * T.sizeof, typeid(T)); 95 } 96 } 97 98 void removeInPlace(size_t index) 99 { 100 if (index+1 != length) 101 { 102 bufPtr[index] = bufPtr[length-1]; 103 } 104 --length; 105 } 106 107 void unput(size_t numItems) 108 { 109 length = cast(uint)(length - numItems); 110 } 111 }