1 /**
2 License: public domain
3 Authors: Simon Bürger
4 */
5 
6 module jive.array;
7 
8 import jive.internal;
9 import core.exception : RangeError;
10 import core.stdc.string : memmove, memcpy, memset;
11 import std.algorithm;
12 import std.conv : emplace;
13 import std.format;
14 import std.range;
15 import std.traits;
16 
17 
18 /**
19  *  Array of dynamic size.
20  *
21  *  If you add elements, new memory will be allocated automatically as needed.
22  *  Typically there is more memory allocated than is currently in use. There is
23  *  a tradeoff between wasted space and frequency of reallocations. The default
24  *  behaviour is to double the capacity every time the allocated memory is
25  *  filled up. This ensures that pushBack takes O(1) in amortized time. If you
26  *  know the number of elements in advance, you can use reserve to avoid
27  *  reallocations, but this is just an optimization and never necessary.
28  */
29 struct Array(V)
30 {
31 	private V* _ptr = null;			// unused elements are undefined
32 	private size_t _capacity = 0;	// size of buf
33 	private size_t _length = 0;		// used size
34 
35 	/** constructor for given length */
36 	this(size_t size)
37 	{
38 		resize(size);
39 	}
40 
41 	/** constructor for given length and init */
42 	this(size_t size, V val)
43 	{
44 		resize(size, val);
45 	}
46 
47 	/** constructor that gets content from arbitrary range */
48 	this(Stuff)(Stuff data)
49 		if(isInputRange!Stuff && is(ElementType!Stuff:V))
50 	{
51 		static if(hasLength!Stuff)
52 			reserve(_length + data.length);
53 
54 		foreach(ref x; data)
55 			pushBack(x);
56 	}
57 
58 	/** post-blit that does a full copy */
59 	this(this)
60 	{
61 		auto newPtr = jiveMalloc!V(_length);
62 
63 		static if(hasElaborateCopyConstructor!V)
64 		{
65 			for(size_t i = 0; i < _length; ++i)
66 				emplace(newPtr + i, _ptr[i]);
67 		}
68 		else
69 			memcpy(newPtr, _ptr, V.sizeof * _length);
70 		_ptr = newPtr;
71 		_capacity = _length;
72 	}
73 
74 	/** destructor */
75 	~this()
76 	{
77 		static if (hasElaborateDestructor!V)
78 			foreach (ref x; this[])
79 				destroy(x);
80 		jiveFree(_ptr);
81 		_ptr = null; // probably not necessary, just a precaution
82 	}
83 
84 	/** check for emptiness */
85 	bool empty() const pure nothrow @property @safe
86 	{
87 		return _length == 0;
88 	}
89 
90 	/** number of elements */
91 	size_t length() const pure nothrow @property @safe
92 	{
93 		return _length;
94 	}
95 
96 	/** ditto */
97 	size_t opDollar() const pure nothrow @property @safe
98 	{
99 		return _length;
100 	}
101 
102 	/** number of elements this structure can hold without further allocations */
103 	size_t capacity() const pure nothrow @property @safe
104 	{
105 		return _capacity;
106 	}
107 
108 	/**
109 	 * Allocated heap memory in bytes.
110 	 * This is recursive if V has a `.memUsage` property. Otherwise it is equal
111 	 * to `V.sizeof * capacity`
112 	 */
113 	size_t memUsage() const pure nothrow @property @trusted
114 	{
115 		size_t r = V.sizeof*_capacity;
116 		static if(hasMember!(V, "memUsage"))
117 			for(size_t i = 0; i < _length; ++i)
118 				r += _ptr[i].memUsage;
119 		return r;
120 	}
121 
122 	/** make sure this structure can contain given number of elements without further allocs */
123 	void reserve(size_t newCap, bool overEstimate = false) nothrow @trusted
124 	{
125 		if(newCap <= _capacity)
126 			return;
127 
128 		if(overEstimate)
129 			newCap = max(newCap, 2*_capacity);
130 
131 		auto newPtr = jiveMalloc!V(newCap);
132 		memcpy(newPtr, _ptr, V.sizeof * _length);
133 
134 		static if(hasIndirections!V)
135 			memset(newPtr + length, 0, V.sizeof * (newCap - _length)); // prevent false pointers
136 
137 		jiveFree(_ptr);
138 		_ptr = newPtr;
139 		_capacity = newCap;
140 	}
141 
142 	/** pointer to the first element */
143 	inout(V)* ptr() inout pure nothrow @property @safe
144 	{
145 		return _ptr;
146 	}
147 
148 	/** default range */
149 	inout(V)[] opSlice() inout nothrow pure @trusted
150 	{
151 		return _ptr[0 .. _length];
152 	}
153 
154 	/** subrange */
155 	inout(V)[] opSlice(string file = __FILE__, int line = __LINE__)(size_t a, size_t b) inout pure nothrow @trusted
156 	{
157 		if(boundsChecks && (a > b || b > _length))
158 			assert(false, boundsCheckMsg!(file, line));
159 		return _ptr[a .. b];
160 	}
161 
162 	/** assign all elements to the same value */
163 	void opSliceAssign(V v)
164 	{
165 		this.opSlice()[] = v;
166 	}
167 
168 	/* assign a subset of elements to the same value */
169 	void opSliceAssign(string file = __FILE__, int line = __LINE__)(V v, size_t a, size_t b)
170 	{
171 		this.opSlice!(file, line)(a, b)[] = v;
172 	}
173 
174 	/** indexing */
175 	ref inout(V) opIndex(string file = __FILE__, int line = __LINE__)(size_t i) inout pure nothrow @trusted
176 	{
177 		if(boundsChecks && i >= _length)
178 			assert(false, boundsCheckMsg!(file, line));
179 		return _ptr[i];
180 	}
181 
182 	/** first element, same as this[0] */
183 	ref inout(V) front(string file = __FILE__, int line = __LINE__)() inout pure nothrow
184 	{
185 		return this.opIndex!(file, line)(0);
186 	}
187 
188 	/** last element, same as this[$-1] */
189 	ref inout(V) back(string file = __FILE__, int line = __LINE__)() inout pure nothrow
190 	{
191 		return this.opIndex!(file, line)(_length-1);
192 	}
193 
194 	/** add some new element to the back */
195 	void pushBack(V val) @trusted
196 	{
197 		reserve(_length + 1, true);
198 		moveEmplace(val, _ptr[_length]);
199 		++_length;
200 	}
201 
202 	/** add multiple new elements to the back */
203 	void pushBack(Stuff)(Stuff data)
204 		if(!is(Stuff:V) && isInputRange!Stuff && is(ElementType!Stuff:V))
205 	{
206 		static if(hasLength!Stuff)
207 			reserve(_length + data.length, true);
208 
209 		foreach(ref x; data)
210 			pushBack(x);
211 	}
212 
213 	/** convenience alias for pushBack */
214 	alias pushBack opCatAssign;
215 
216 	/** returns removed element */
217 	V popBack(string file = __FILE__, int line = __LINE__)() @trusted
218 	{
219 		if(boundsChecks && empty)
220 			assert(false, boundsCheckMsg!(file, line));
221 
222 		--_length;
223 		V r = void;
224 		memcpy(&r, _ptr + _length, V.sizeof);
225 		static if(hasIndirections!V)
226 			memset(_ptr + _length, 0, V.sizeof);
227 		return r;
228 	}
229 
230 	/** insert new element at given location. moves all elements behind */
231 	void insert(string file = __FILE__, int line = __LINE__)(size_t i, V data) @trusted
232 	{
233 		if(boundsChecks && i > _length)
234 			assert(false, boundsCheckMsg!(file, line));
235 
236 		reserve(_length + 1, true);
237 		memmove(_ptr + i + 1, _ptr + i, V.sizeof * (_length - i));
238 		++_length;
239 		moveEmplace(data, _ptr[i]);
240 	}
241 
242 	/** remove i'th element. moves all elements behind */
243 	V remove(string file = __FILE__, int line = __LINE__)(size_t i) @trusted
244 	{
245 		if(boundsChecks && i >= _length)
246 			assert(false, boundsCheckMsg!(file, line));
247 
248 		V r = void;
249 		memcpy(&r, _ptr + i, V.sizeof);
250 		--_length;
251 		memmove(_ptr + i, _ptr + i + 1, V.sizeof * (_length - i));
252 		static if(hasIndirections!V)
253 			memset(_ptr + _length, 0, V.sizeof);
254 		return r;
255 	}
256 
257 	/** sets the size to some value. Either cuts of some values (but does not free memory), or fills new ones with V.init */
258 	void resize(size_t size, V v) @trusted
259 	{
260 		// TODO: remove @trusted in case V's destructor is @system
261 
262 		if(size <= _length) // shrink
263 		{
264 			static if(hasElaborateDestructor!V)
265 				for(size_t i = size; i < _length; ++i)
266 					destroy(_ptr[i]);
267 			static if(hasIndirections!V)
268 				memset(_ptr + size, 0, V.sizeof * (_length - size));
269 			_length = size;
270 		}
271 		else // expand
272 		{
273 			reserve(size, false);
274 			for(size_t i = _length; i < size; ++i)
275 				emplace(_ptr + i, v);
276 			_length = size;
277 		}
278 	}
279 
280 	/** ditto */
281 	void resize(size_t size)
282 	{
283 		resize(size, V.init);
284 	}
285 
286 	/** sets the size and fills everything with one value */
287 	void assign(size_t newsize, V v)
288 	{
289 		resize(newsize);
290 		this[] = v;
291 	}
292 
293 	/** remove all content but keep allocated memory (same as resize(0)) */
294 	void clear()
295 	{
296 		resize(0);
297 	}
298 
299 	/** convert to string */
300 	void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const
301 	{
302 		formatValue(sink, this[], fmt);
303 	}
304 
305 	/** ditto */
306 	string toString() const
307 	{
308 		return format("%s", this[]);
309 	}
310 
311 	hash_t toHash() const nothrow @trusted
312 	{
313 		return this[].hashOf;
314 	}
315 
316 	bool opEquals(const ref Array other) const
317 	{
318 		return equal(this[], other[]);
319 	}
320 
321 	static if(__traits(compiles, V.init < V.init))
322 	int opCmp(const ref Array other) const
323 	{
324 		return cmp(this[], other[]);
325 	}
326 }
327 
328 ///
329 /+@nogc+/ nothrow pure @safe unittest
330 {
331 	Array!int a;
332 
333 	a.pushBack(1);
334 	a.pushBack([2,3,4,5]);
335 	assert(a.popBack() == 5);
336 	assert(equal(a[], [1,2,3,4]));
337 
338 	a[] = 0;
339 	a[1..3] = 1;
340 	a.resize(6, 2);
341 	assert(equal(a[], [0,1,1,0,2,2]));
342 }
343 
344 // check for all nice attributes in case the type is nice as well
345 @nogc nothrow pure @safe unittest
346 {
347 	struct S1
348 	{
349 		int x;
350 		//int* p; // FIXME: when GC.addRange/removeRange become pure
351 		this(this) @nogc nothrow pure @safe {}
352 		~this() @nogc nothrow pure @safe {}
353 	}
354 
355 	static assert(hasElaborateDestructor!S1);
356 	static assert(hasElaborateCopyConstructor!S1);
357 	//static assert(hasIndirections!S);
358 
359 	Array!S1 a;
360 	S1 s;
361 	a.pushBack(s);
362 	a.popBack();
363 	a.resize(5);
364 	a.insert(3, s);
365 	a.remove(3);
366 	a.reserve(10);
367 	a[] = s;
368 	a[0..1] = s;
369 	a.pushBack(a[0]);
370 	a.pushBack(a[0..1]); // only valid with the previous reserve!
371 }
372 
373 // check correct invocation of postblit/destructor
374 unittest
375 {
376 	int counter = 0;
377 
378 	struct S
379 	{
380 		bool active;
381 		this(bool active) { this.active = active; if(active) ++counter; }
382 		this(this) { if(active) ++counter; }
383 		~this() { if(active) --counter; }
384 	}
385 
386 	{
387 		Array!S a;
388 		assert(counter == 0);
389 		a.pushBack(S(true));
390 		assert(counter == 1);
391 		a.pushBack(a[0]);
392 		assert(counter == 2);
393 		a.reserve(5);
394 		a.pushBack(a[]);
395 
396 		assert(counter == 4);
397 		a.insert(1, a[1]);
398 		assert(counter == 5);
399 		a.remove(3);
400 		assert(counter == 4);
401 		Array!S b = a;
402 		assert(a[] == b[]);
403 		assert(counter == 8);
404 	}
405 	assert(counter == 0);
406 }
407 
408 // check move-semantics
409 unittest
410 {
411 	struct S3
412 	{
413 		int x;
414 		alias x this;
415 		this(this) { assert(x == 0); }
416 	}
417 
418 	Array!S3 a;
419 	a.pushBack(S3(1));
420 	a.pushBack(S3(3));
421 	a.insert(1, S3(2));
422 	a.popBack();
423 	a[1] = S3(4);
424 	assert(a[] == [S3(1),S3(4)]);
425 }
426 
427 // type with no @safe/pure/etc-attributes at all and also no opCmp
428 unittest
429 {
430 	struct S
431 	{
432 		int* x;
433 		this(this){ }
434 		~this(){ }
435 		bool opEquals(const S b) const { return x is b.x; }
436 	}
437 
438 	static assert(hasIndirections!S);
439 	static assert(hasElaborateDestructor!S);
440 
441 	S s;
442 	Array!S a;
443 	a.pushBack(s);
444 	a.pushBack([s,s]);
445 	a.popBack();
446 	a.reserve(5);
447 	a.insert(1, s);
448 	a.remove(2);
449 	a.resize(3);
450 	assert(a[] == [s,s,s]);
451 	Array!S b = a;
452 }
453 
454 // check capacity and memUsage
455 unittest
456 {
457 	Array!int a;
458 	assert(a.capacity == 0);
459 	assert(a.memUsage == 0);
460 	a.reserve(10);
461 	assert(a.capacity == 10);
462 	assert(a.memUsage == 40);
463 
464 	Array!(Array!int) b;
465 	b.reserve(10);
466 	b.pushBack(Array!int([1]));
467 	b.pushBack(Array!int([1,2]));
468 	b.pushBack(Array!int([1,2,3]));
469 	b.pushBack(Array!int([1,2,3,4]));
470 	b.pushBack(Array!int([1,2,3,4,5]));
471 	assert(b.capacity == 10);
472 	assert(b[0].capacity == 1);
473 	assert(b[1].capacity == 2);
474 	assert(b[2].capacity == 3);
475 	assert(b[3].capacity == 4);
476 	assert(b[4].capacity == 5);
477 	assert(b.memUsage == 10*Array!int.sizeof + 4*(1+2+3+4+5));
478 }
479 
480 struct Prune(V)
481 {
482 	@disable this();
483 	@disable this(this);
484 
485 	Array!V* arr;
486 
487 	this(ref Array!V arr)
488 	{
489 		this.arr = &arr;
490 	}
491 
492 	int opApply(int delegate(size_t i, ref V val, ref bool remove) dg)
493 	{
494 		size_t a = 0;
495 		size_t b = 0;
496 		int r = 0;
497 
498 		while(b < arr.length && r == 0)
499 		{
500 			bool remove = false;
501 			r = dg(b, (*arr)[b], remove);
502 
503 			if(!remove)
504 			{
505 				if(a != b)
506 					(*arr)[a] = move((*arr)[b]);
507 				++a;
508 			}
509 
510 			++b;
511 		}
512 
513 		if(a == b)
514 			return r;
515 
516 		while(b < arr.length)
517 			(*arr)[a++] = move((*arr)[b++]);
518 
519 		arr._length = a;
520 		return r;
521 	}
522 
523 	int opApply(int delegate(ref V val, ref bool remove) dg)
524 	{
525 		size_t a = 0;
526 		size_t b = 0;
527 		int r = 0;
528 
529 		while(b < arr.length && r == 0)
530 		{
531 			bool remove = false;
532 			r = dg((*arr)[b], remove);
533 
534 			if(!remove)
535 			{
536 				if(a != b)
537 					(*arr)[a] = move((*arr)[b]);
538 				++a;
539 			}
540 
541 			++b;
542 		}
543 
544 		if(a == b)
545 			return r;
546 
547 		while(b < arr.length)
548 			(*arr)[a++] = move((*arr)[b++]);
549 
550 		arr._length = a;
551 		return r;
552 	}
553 }
554 
555 Prune!V prune(V)(ref Array!V arr)
556 {
557 	return Prune!V(arr);
558 }
559 
560 ///
561 unittest
562 {
563 	auto a = Array!int([10,20,30,40,50]);
564 
565 	/* Iterate over an Array. If `remove` is set inside the loop body, the
566 	 current element is removed from the array. This is more efficient than
567 	 multiple calls to the `.remove()` method. */
568 	foreach(i, ref x, ref bool rem; prune(a))
569 	{
570 		if(x == 30)
571 			x = 31;
572 		rem = (i == 1) || (x == 40);
573 	}
574 
575 	assert(equal(a[], [10,31,50]));
576 
577 	/** same, but without indices */
578 	foreach(ref x, ref bool rem; prune(a))
579 	{
580 		if(x == 10)
581 			x = 11;
582 		rem = (x == 50);
583 	}
584 
585 	assert(equal(a[], [11,31]));
586 }