1 /**
2  * Betterc: Frequently used primitives suitable for use in the BetterC D subset.
3  *
4  * Copyright: Maxim Freck, 2018.
5  * Authors:   Maxim Freck <maxim@freck.pp.ru>
6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  */
8 module betterc.dynamicvector;
9 
10 /*******
11  * Dynamic Vector without Garbage Collection
12  */
13 struct Vector(T)
14 {
15 	private import core.stdc.stdlib: malloc, realloc, free;
16 	private import core.stdc.string: memcpy, memmove;
17 
18 	private struct Payload {
19 		T* arr;
20 		size_t length;
21 		size_t capacity;
22 		size_t initCap;
23 
24 		size_t count;
25 		@disable this(this); // not copyable
26 	}
27 
28 	private Payload* payload;
29 
30 	/*******
31 	* Constructor
32 	*
33 	* Params:
34 	*  n = Initial array capacity
35 	*/
36 	this (size_t n) nothrow @nogc
37 	{
38 		payload = (cast(Payload*)malloc(Payload.sizeof));
39 		payload.length = 0;
40 		payload.capacity = n;
41 		payload.initCap = n;
42 		payload.arr = (cast(T*)malloc(T.sizeof*n));
43 		payload.count = 1;
44 	}
45 
46 	/*******
47 	* Constructor
48 	*
49 	* Params:
50 	*  src = Initial array values
51 	*  n = Initial array capacity
52 	*/
53 	this(T[] src, size_t n = 0) nothrow @nogc
54 	{
55 
56 		payload = (cast(Payload*)malloc(Payload.sizeof));
57 		payload.length = 0;
58 		payload.initCap = n == 0 ? src.length : n;
59 		payload.capacity = payload.initCap;
60 		payload.arr = (cast(T*)malloc(T.sizeof*payload.capacity));
61 		payload.count = 1;
62 
63 		push(src);
64 	}
65 
66 	private this(Payload* src, size_t a, size_t b) nothrow @nogc
67 	{
68 
69 		payload = (cast(Payload*)malloc(Payload.sizeof));
70 		payload.length = b - a;
71 		payload.initCap = src.initCap;
72 		payload.capacity = src.capacity;
73 		payload.arr = (cast(T*)malloc(T.sizeof*payload.length));
74 		payload.count = 1;
75 		memcpy(payload.arr, src.arr+a, payload.length);
76 	}
77 
78 	this(this) nothrow @nogc
79 	{
80 		if (payload !is null)
81 			payload.count++;
82 	}
83 
84 	///Ref. counting during structure assignation
85 	ref typeof(this) opAssign(ref typeof(this) rhs)
86 	{
87 		this.payload = rhs.payload;
88 		if (payload !is null)
89 			payload.count++;
90 
91 		return this;
92 	}
93 
94 	~this() nothrow @nogc
95 	{
96 		//TODO: call elements destructors if present
97 		if (payload !is null && --payload.count == 0) {
98 			free(payload.arr);
99 			free(payload);
100 		}
101 	}
102 
103 	/*******
104 	* Shrinks array
105 	*
106 	* Params:
107 	*  len = New array size
108 	*/
109 	public ref typeof(this) shrink(size_t len = 0) nothrow @nogc
110 	{
111 		if (len < payload.length) payload.length = len;
112 		if (payload.length == 0 || payload.capacity/payload.length > 1) shrink_to_fit();
113 		return this;
114 	}
115 
116 	/*******
117 	* Requests the removal of unused capacity.
118 	*
119 	*/
120 	public ref typeof(this) shrink_to_fit() nothrow @nogc
121 	{
122 		payload.capacity = payload.length == 0 ? payload.initCap : (payload.length * 3) / 2;
123 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
124 		return this;
125 	}
126 
127 	/*******
128 	* Clears entire array
129 	*
130 	*/
131 	pragma(inline)
132 	public ref typeof(this) clear() nothrow @nogc
133 	{
134 		return shrink(0);
135 	}
136 
137 	/*******
138 	* Erases nth array element
139 	*
140 	* Params:
141 	*  n = Element number
142 	*/
143 	public ref typeof(this) erase(in size_t n) nothrow @nogc
144 	{
145 		if (n >= payload.length) return this;
146 
147 		if (n == payload.length - 1) {
148 			payload.length--;
149 			return this;
150 		}
151 
152 		payload.length--;
153 		memmove(payload.arr+n, payload.arr+n+1, payload.length - n);
154 
155 		return this;
156 	}
157 
158 	public ref typeof(this) trim()
159 	{
160 		payload.capacity = payload.length;
161 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
162 
163 		return this;
164 	}
165 
166 	public size_t length() const pure nothrow @nogc
167 	{
168 		return payload.length;
169 	}
170 
171 	public size_t capacity() pure nothrow @nogc
172 	{
173 		return payload.capacity;
174 	}
175 
176 	public auto ref T opIndex(in size_t id) pure nothrow @nogc
177 	{
178 		if (payload.length == 0) return T.init;
179 		return payload.arr[id>=payload.length ? payload.length - 1 : id];
180 	}
181 
182 	/*******
183 	* Appends a value to an array
184 	*
185 	* Params:
186 	*  rhs = The value to add
187 	*/
188 	public ref typeof(this) push(in T rhs) nothrow @nogc
189 	{
190 		payload.length++;
191 		if (payload.length > payload.capacity) grow();
192 		payload.arr[payload.length - 1] = rhs;
193 
194 		return this;
195 	}
196 
197 	private void grow() nothrow @nogc
198 	{
199 		payload.capacity = (payload.capacity * 3) / 2 + 1;
200 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
201 	}
202 
203 	///ditto
204 	public ref typeof(this) push(in T[] rhs) nothrow @nogc
205 	{
206 		foreach (v; rhs) push(v);
207 
208 		return this;
209 	}
210 
211 	///ditto
212 	public ref typeof(this) push(typeof(this) rhs) nothrow @nogc
213 	{
214 		foreach (v; rhs) push(v);
215 
216 		return this;
217 	}
218 
219 	/*******
220 	* Removes the last element from an array
221 	*/
222 	public void pop() nothrow @nogc
223 	{
224 		if (payload.length > 0) payload.length--;
225 	}
226 
227 	/*******
228 	* Appends a value to an array using << operator
229 	*
230 	* Params:
231 	*  rhs = The value to add
232 	*/
233 	pragma(inline)
234 	public ref typeof(this) opBinary(string op)(in T rhs) nothrow @nogc if (op == "<<")
235 	{
236 		return push(rhs);
237 	}
238 
239 	///ditto
240 	pragma(inline)
241 	public ref typeof(this) opBinary(string op)(in T[] rhs) nothrow @nogc if (op == "<<")
242 	{
243 		return push(rhs);
244 	}
245 
246 	///ditto
247 	pragma(inline)
248 	public ref typeof(this) opBinary(string op)(typeof(this) rhs) nothrow @nogc if (op == "<<")
249 	{
250 		return push(rhs);
251 	}
252 
253 
254 	/*******
255 	* The $ operator returns the length of the array
256 	*/
257 	size_t opDollar() nothrow @nogc
258 	{
259 		return payload.length;
260 	}
261 
262 	public typeof(this) opSlice(size_t a, size_t b) nothrow @nogc
263 	{
264 		if (a > b) {
265 			immutable c = b;
266 			b = a;
267 			a = c;
268 		}
269 		if (a > payload.length) a = payload.length;
270 		if (b > payload.length) b = payload.length;
271 
272 		return typeof(this)(payload, a, b);
273 	}
274 
275 
276 	int opApply(scope int delegate(ref T) nothrow @nogc dg) nothrow @nogc
277 	{
278 		int result = 0;
279 
280 		foreach (size_t i; 0 .. payload.length) {
281 			result = dg(payload.arr[i]);
282 			if (result) break;
283 		}
284 
285 		return result;
286 	}
287 
288 	int opApply(scope int delegate(ref size_t, ref T) nothrow @nogc dg) nothrow @nogc
289 	{
290 		int result;
291 
292 		foreach (size_t i; 0 .. payload.length) {
293 			result = dg(i, payload.arr[i]);
294 			if (result) break;
295 		}
296 
297 		return result;
298 	}
299 
300 	T* ptr() pure nothrow @nogc
301 	{
302 		return payload.arr;
303 	}
304 }