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 }