1 module mutils.container.vector_allocator;
2 
3 import std.experimental.allocator;
4 import std.traits;
5 
6 /**
7  * Vector backed by given allocator, it is not releaseing data after destruction, used in lua_json_token to treat dynamic arrays as a custom vector
8  **/
9 struct VectorAllocator(T, Allocator) {
10 	static if (hasStaticMember!(Allocator, "instance")) {
11 		alias allocator = Allocator.instance;
12 	} else {
13 		Allocator allocator;
14 	}
15 
16 	T[] array;
17 
18 	this(size_t numElements) {
19 		assert(numElements > 0);
20 		setLenght(numElements);
21 	}
22 
23 	void clear() {
24 		removeAll();
25 	}
26 
27 	void removeAll() {
28 		if (array !is null) {
29 			freeData(cast(void[]) array);
30 		}
31 		array = T[].init;
32 	}
33 
34 	bool empty() {
35 		return (array.length == 0);
36 	}
37 
38 	size_t length() {
39 		return array.length;
40 	}
41 
42 	void reset() {
43 		clear();
44 	}
45 
46 	void setLenght(size_t newNumOfElements) {
47 		if (array is null) {
48 			array = allocator.makeArray!(T)(newNumOfElements);
49 		} else {
50 			if (array.length < newNumOfElements) {
51 				allocator.expandArray(array, newNumOfElements - array.length);
52 			} else if (array.length > newNumOfElements) {
53 				allocator.shrinkArray(array, array.length - newNumOfElements);
54 			}
55 		}
56 	}
57 
58 	void freeData(void[] data) {
59 		allocator.dispose(array);
60 	}
61 
62 	void add(T t) {
63 		setLenght(array.length + 1);
64 		array[$ - 1] = t;
65 	}
66 
67 	void add(X)(X[] t) if (is(Unqual!X == Unqual!T)) {
68 		size_t sizeBefore = array.length;
69 		setLenght(array.length + t.length);
70 		foreach (i; 0 .. t.length) {
71 			array[sizeBefore + i] = t[i];
72 		}
73 	}
74 
75 	void remove(size_t elemNum) {
76 		array[elemNum] = array[$ - 1];
77 		setLenght(array.length - 1);
78 	}
79 
80 	void removeElement(T elem) {
81 		foreach (i, ref el; array) {
82 			if (el == elem) {
83 				remove(i);
84 				return;
85 			}
86 		}
87 	}
88 
89 	T opIndex(size_t elemNum) {
90 		return array[elemNum];
91 	}
92 
93 	auto opSlice() {
94 		return array;
95 	}
96 
97 	T[] opSlice(size_t x, size_t y) {
98 		return array[x .. y];
99 	}
100 
101 	size_t opDollar() {
102 		return array.length;
103 	}
104 
105 	void opOpAssign(string op)(T obj) {
106 		static assert(op == "~");
107 		add(obj);
108 	}
109 
110 	/*void opOpAssign(string op)(T[] obj){
111 		static assert(op=="~");
112 		add(obj);
113 	}*/
114 	void opOpAssign(string op, X)(X[] obj) {
115 		static assert(op == "~");
116 		add(obj);
117 	}
118 
119 	void opIndexAssign(T obj, size_t elemNum) {
120 		array[elemNum] = obj;
121 
122 	}
123 
124 	void opAssign(X)(X[] slice) {
125 		reset();
126 		this ~= slice;
127 	}
128 
129 }
130 
131 // Helper to avoid GC
132 private T[n] s(T, size_t n)(auto ref T[n] array) pure nothrow @nogc @safe {
133 	return array;
134 }
135 
136 unittest {
137 	import std.experimental.allocator.mallocator;
138 
139 	VectorAllocator!(int, Mallocator) vec;
140 	assert(vec.empty);
141 	vec.add(0);
142 	vec.add(1);
143 	vec.add(2);
144 	vec.add(3);
145 	vec.add(4);
146 	vec.add(5);
147 	assert(vec.length == 6);
148 	assert(vec[3] == 3);
149 	assert(vec[5] == 5);
150 	assert(vec[] == [0, 1, 2, 3, 4, 5].s);
151 	assert(!vec.empty);
152 	vec.remove(3);
153 	assert(vec.length == 5);
154 	assert(vec[] == [0, 1, 2, 5, 4].s); //unstable remove
155 
156 	Mallocator.instance.dispose(vec.array);
157 }