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.ds.vector;
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 vector 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 vector values
51 	*  n = Initial vector 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 	///ditto
67 	private this(Payload* src, size_t a, size_t b) nothrow @nogc
68 	{
69 
70 		payload = (cast(Payload*)malloc(Payload.sizeof));
71 		payload.length = b - a;
72 		payload.initCap = src.initCap;
73 		payload.capacity = src.capacity;
74 		payload.arr = (cast(T*)malloc(T.sizeof*payload.length));
75 		payload.count = 1;
76 		memcpy(payload.arr, src.arr+a, payload.length);
77 	}
78 
79 	///ditto
80 	this(this) nothrow @nogc
81 	{
82 		if (payload !is null)
83 			payload.count++;
84 	}
85 
86 	///Ref. counting during structure assignment
87 	ref typeof(this) opAssign(ref typeof(this) rhs)
88 	{
89 		this.payload = rhs.payload;
90 		if (payload !is null)
91 			payload.count++;
92 
93 		return this;
94 	}
95 
96 	/*******
97 	* Destructor: frees all memory
98 	*/
99 	~this() nothrow @nogc
100 	{
101 		//TODO: call elements destructors if present
102 		if (payload !is null && --payload.count == 0) {
103 			free(payload.arr);
104 			free(payload);
105 		}
106 	}
107 
108 	/*******
109 	* Shrinks vector
110 	*
111 	* Params:
112 	*  len = New vector size
113 	*/
114 	public ref typeof(this) shrink(size_t len = 0) nothrow @nogc
115 	{
116 		if (len < payload.length) payload.length = len;
117 		if (payload.length == 0 || payload.capacity/payload.length > 1) shrink_to_fit();
118 		return this;
119 	}
120 
121 	/*******
122 	* Requests the removal of unused capacity.
123 	*
124 	*/
125 	public ref typeof(this) shrink_to_fit() nothrow @nogc
126 	{
127 		payload.capacity = payload.length == 0 ? payload.initCap : (payload.length * 3) / 2;
128 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
129 		return this;
130 	}
131 
132 	/*******
133 	* Clears entire vector
134 	*
135 	*/
136 	pragma(inline)
137 	public ref typeof(this) clear() nothrow @nogc
138 	{
139 		return shrink(0);
140 	}
141 
142 	/*******
143 	* Erases nth vector element
144 	*
145 	* Params:
146 	*  n = Element number
147 	*/
148 	public ref typeof(this) erase(in size_t n) nothrow @nogc
149 	{
150 		if (n >= payload.length) return this;
151 
152 		if (n == payload.length - 1) {
153 			payload.length--;
154 			return this;
155 		}
156 
157 		payload.length--;
158 		memmove(payload.arr+n, payload.arr+n+1, payload.length - n);
159 
160 		return this;
161 	}
162 
163 	/*******
164 	* Trims the capacity of the vector to be the vector's current size.
165 	*/
166 	public ref typeof(this) trim()
167 	{
168 		payload.capacity = payload.length;
169 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
170 
171 		return this;
172 	}
173 
174 	/*******
175 	* Returns: the current length of the vector
176 	*/
177 	public size_t length() const pure nothrow @nogc
178 	{
179 		return payload.length;
180 	}
181 
182 	/*******
183 	* Returns: the current capacity of the vector
184 	*/
185 	public size_t capacity() pure nothrow @nogc
186 	{
187 		return payload.capacity;
188 	}
189 
190 	/*******
191 	* The [] operator
192 	*/
193 	public auto ref T opIndex(in size_t id) pure nothrow @nogc
194 	{
195 		if (payload.length == 0) return T.init;
196 		return payload.arr[id>=payload.length ? payload.length - 1 : id];
197 	}
198 
199 	/*******
200 	* Appends a value to the vector
201 	*
202 	* Params:
203 	*  rhs = The value to add
204 	*/
205 	public ref typeof(this) push(in T rhs) nothrow @nogc
206 	{
207 		payload.length++;
208 		if (payload.length > payload.capacity) grow();
209 		payload.arr[payload.length - 1] = rhs;
210 
211 		return this;
212 	}
213 
214 	///ditto
215 	public ref typeof(this) push(in T[] rhs) nothrow @nogc
216 	{
217 		foreach (v; rhs) push(v);
218 		return this;
219 	}
220 
221 	///ditto
222 	public ref typeof(this) push(typeof(this) rhs) nothrow @nogc
223 	{
224 		foreach (v; rhs) push(v);
225 		return this;
226 	}
227 
228 	private void grow() nothrow @nogc
229 	{
230 		payload.capacity = (payload.capacity * 3) / 2 + 1;
231 		payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity));
232 	}
233 
234 	/*******
235 	* Removes the last element from the vector
236 	*/
237 	public void pop() nothrow @nogc
238 	{
239 		if (payload.length > 0) payload.length--;
240 	}
241 
242 	/*******
243 	* Appends a value to the vector using << operator
244 	*
245 	* Params:
246 	*  rhs = The value to add
247 	*/
248 	pragma(inline)
249 	public ref typeof(this) opBinary(string op)(in T rhs) nothrow @nogc if (op == "<<")
250 	{
251 		return push(rhs);
252 	}
253 
254 	///ditto
255 	pragma(inline)
256 	public ref typeof(this) opBinary(string op)(in T[] rhs) nothrow @nogc if (op == "<<")
257 	{
258 		return push(rhs);
259 	}
260 
261 	///ditto
262 	pragma(inline)
263 	public ref typeof(this) opBinary(string op)(typeof(this) rhs) nothrow @nogc if (op == "<<")
264 	{
265 		return push(rhs);
266 	}
267 
268 
269 	/*******
270 	* The $ operator returns the length of the vector
271 	*/
272 	size_t opDollar() nothrow @nogc
273 	{
274 		return payload.length;
275 	}
276 
277 	/*******
278 	* The [a..b] operator returns the slice of the vector
279 	*
280 	* Returns: a new vector containing values from a to b
281 	*/
282 	public typeof(this) opSlice(size_t a, size_t b) nothrow @nogc
283 	{
284 		if (a > b) {
285 			immutable c = b;
286 			b = a;
287 			a = c;
288 		}
289 		if (a > payload.length) a = payload.length;
290 		if (b > payload.length) b = payload.length;
291 
292 		return typeof(this)(payload, a, b);
293 	}
294 
295 
296 	/*******
297 	* This method is used in foreach()
298 	*/
299 	int opApply(scope int delegate(ref T) nothrow @nogc dg) nothrow @nogc
300 	{
301 		int result = 0;
302 
303 		foreach (size_t i; 0 .. payload.length) {
304 			result = dg(payload.arr[i]);
305 			if (result) break;
306 		}
307 
308 		return result;
309 	}
310 
311 	///ditto
312 	int opApply(scope int delegate(ref size_t, ref T) nothrow @nogc dg) nothrow @nogc
313 	{
314 		int result;
315 
316 		foreach (size_t i; 0 .. payload.length) {
317 			result = dg(i, payload.arr[i]);
318 			if (result) break;
319 		}
320 
321 		return result;
322 	}
323 
324 	/*******
325 	* Returns: the pointer to the underlying vector
326 	*/
327 	T* ptr() pure nothrow @nogc
328 	{
329 		return payload.arr;
330 	}
331 }