1 module memutils.vector;
2 
3 import std.algorithm : swap, initializeAll;
4 import std.range : empty;
5 import std.traits;
6 import std.c.string;
7 import std.range : isInputRange, isForwardRange, isRandomAccessRange, ElementType, refRange, RefRange, hasLength;
8 import core.exception : RangeError;
9 import std.exception : enforce;
10 import memutils.allocators;
11 import memutils.helpers;
12 import memutils.utils;
13 import memutils.refcounted;
14 
15 public alias SecureArray(T) = Array!(T, SecureMem);
16 
17 template Array(T, ALLOC = ThreadMem) 
18 	if (!is (T == RefCounted!(Vector!(T, ALLOC), ALLOC)))
19 {
20 	alias Array = RefCounted!(Vector!(T, ALLOC), ALLOC);
21 }
22 
23 public alias SecureVector(T) = Vector!(T, SecureMem);
24 // TODO: Remove implicit string casting for Vector!ubyte! Encourage use of Vector!char [].idup instead.
25 
26 /// An array that uses a custom allocator.
27 struct Vector(T, ALLOC = ThreadMem)
28 {	
29 	@disable this(this);
30 	
31 	void opAssign()(auto ref Vector!(T, ALLOC) other) {
32 		if (other.ptr !is this.ptr)
33 			this.swap(other);
34 	}
35 
36 	void opAssign()(ref T[] other) {
37 		this[] = other[];
38 	}
39 	
40 	// Payload cannot be copied
41 	private struct Payload
42 	{
43 		size_t _capacity;
44 		T[] _payload;
45 		
46 		// Convenience constructor
47 		this()(auto ref T[] p) 
48 		{ 
49 			if (p.length == 0) return;
50 
51 			length = p.length;
52 
53 			static if (isImplicitlyConvertible!(T, T)) {
54 				if (_payload.ptr is p.ptr && _payload.length == p.length) {
55 					p = null;
56 				}
57 				else {
58 					_payload[] = p[0 .. $]; // todo: use emplace?
59 				}
60 			}
61 			else
62 			{
63 				memmove(_payload.ptr, p.ptr, T.sizeof*p.length);
64 			}
65 		}
66 		
67 		// Destructor releases array memory
68 		~this() const
69 		{
70 			if (_capacity == 0 || _payload.ptr is null)
71 				return;
72 			T[] data = cast(T[]) _payload.ptr[0 .. _capacity];
73 			freeArray!(T, ALLOC)(data, _payload.length); // calls destructors and frees memory
74 		}
75 		
76 		void opAssign(Payload rhs)
77 		{
78 			assert(false);
79 		}
80 		
81 		// Duplicate data
82 		// @property Payload dup()
83 		// {
84 		//     Payload result;
85 		//     result._payload = _payload.dup;
86 		//     // Conservatively assume initial capacity == length
87 		//     result._capacity = result._payload.length;
88 		//     return result;
89 		// }
90 		
91 		// length
92 		@property size_t length() const
93 		{
94 			return _payload.length;
95 		}
96 		
97 		// length
98 		@property void length(size_t newLength)
99 		{
100 			if (length > 0 && length >= newLength)
101 			{
102 				// shorten
103 				static if (hasElaborateDestructor!T) {
104 					foreach (ref e; _payload.ptr[newLength .. _payload.length]) {
105 						static if (is(T == struct) && !isPointer!T) // call destructors but not for indirections...
106 							.destroy(e);
107 					}
108 					
109 					// Zero out unused capacity to prevent gc from seeing
110 					// false pointers
111 					static if (hasIndirections!T)
112 						memset(_payload.ptr + newLength, 0, (_payload.length - newLength) * T.sizeof);
113 				}
114 				_payload = _payload.ptr[0 .. newLength];
115 				return;
116 			}
117 			
118 			if (newLength > 0) {
119 				// enlarge
120 				auto startEmplace = length;
121 				reserve(newLength);
122 				_payload = _payload.ptr[0 .. newLength];
123 				static if (!isImplicitlyConvertible!(T, T)) {
124 					T t = T();
125 					foreach (size_t i; startEmplace .. length) 
126 						memmove(_payload.ptr + i, &t, T.sizeof); 
127 					
128 				} else
129 					initializeAll(_payload.ptr[startEmplace .. length]);
130 			}
131 		}
132 		
133 		// capacity
134 		@property size_t capacity() const
135 		{
136 			return _capacity;
137 		}
138 		
139 		// reserve
140 		void reserve(size_t elements)
141 		{
142 			if (elements <= capacity) return;
143 			// TODO: allow vector to become smaller?
144 
145 			TRACE("Reserve ", length, " => ", elements, " elements.");
146 
147 			if (_capacity > 0) {
148 				size_t len = _payload.length;
149 				_payload = _payload.ptr[0 .. _capacity];
150 				_payload = reallocArray!(T, ALLOC)(_payload, elements)[0 .. len];
151 			}
152 			else if (elements > 0) {
153 				_payload = allocArray!(T, ALLOC)(elements)[0 .. _payload.length];
154 			}
155 			_capacity = elements;
156 		}
157 
158 		static if (is(T == char))
159 		size_t pushBack(Stuff)(Stuff stuff) 
160 			if (is(Stuff == char[]) || is(Stuff == string))
161 		{
162 			TRACE("Vector.append @disabled this(this)");
163 			if (_capacity <= length + stuff.length)
164 			{
165 				reserve(1 + (length + stuff.length) * 3 / 2);
166 			}
167 			assert(capacity >= length + stuff.length && _payload.ptr);
168 						
169 			memmove(_payload.ptr + _payload.length, stuff.ptr, stuff.length);
170 			_payload = _payload.ptr[0 .. _payload.length + stuff.length];
171 			
172 			return 1;
173 		}
174 
175 		size_t pushBack(Stuff)(auto ref Stuff stuff)
176 			if (!isImplicitlyConvertible!(T, T) && is(T == Stuff))
177 		{
178 			TRACE("Vector.append @disabled this(this)");
179 			if (_capacity == length)
180 			{
181 				reserve(1 + capacity * 3 / 2);
182 			}
183 			assert(capacity > length && _payload.ptr);
184 			
185 			T* t = &stuff;
186 			
187 			memmove(_payload.ptr + _payload.length, t, T.sizeof);
188 			memset(t, 0, T.sizeof);
189 			_payload = _payload.ptr[0 .. _payload.length + 1];
190 			
191 			return 1;
192 		}
193 		
194 		// Insert one item
195 		size_t pushBack(Stuff)(auto ref Stuff stuff)
196 			if (isImplicitlyConvertible!(T, T) && isImplicitlyConvertible!(Stuff, T))
197 		{
198 			TRACE("Vector.append");
199 			logTrace("Capacity: ", _capacity, " length: ", length);
200 			if (_capacity == length)
201 			{
202 				reserve(1 + capacity * 3 / 2);
203 			}
204 			assert(capacity > length && _payload.ptr, "Payload pointer " ~ _payload.ptr.to!string ~ "'s capacity: " ~ capacity.to!string ~ " must be more than " ~ length.to!string);
205 			emplace(_payload.ptr + _payload.length, stuff);
206 			_payload = _payload.ptr[0 .. _payload.length + 1];
207 			return 1;
208 		}
209 		
210 		/// Insert a range of items
211 		size_t pushBack(Stuff)(auto ref Stuff stuff)
212 			if (isInputRange!Stuff && (isImplicitlyConvertible!(ElementType!Stuff, T) || is(T == ElementType!Stuff)))
213 		{
214 			TRACE("Vector.append 2");
215 			static if (hasLength!Stuff)
216 			{
217 				immutable oldLength = length;
218 				reserve(oldLength + stuff.length);
219 			}
220 			size_t result;
221 			foreach (ref item; stuff)
222 			{
223 				pushBack(item);
224 				++result;
225 			}
226 			static if (hasLength!Stuff)
227 			{
228 				assert(length == oldLength + stuff.length);
229 			}
230 			return result;
231 		}
232 	}
233 	
234 	private alias Data = Payload;
235 	private Data _data;
236 	
237 	this(size_t elms) {
238 		resize(elms);
239 	}
240 	
241 	/**
242         Constructor taking a number of items
243      */
244 	this(U)(U[] values...)
245 		if (isImplicitlyConvertible!(U, T))
246 	{
247 		// TODO: This doesn't work with refcounted
248 		_data = Data(cast(T[])values);
249 	}
250 
251 	/**
252         Constructor taking an input range
253      */
254 	this(Stuff)(Stuff stuff)
255 		if (isInputRange!Stuff && isImplicitlyConvertible!(UnConst!(ElementType!Stuff), T) && !is(Stuff == T[]))
256 	{
257 		insertBack(stuff);
258 	}
259 	
260 	/**
261 	 * Move Constructor
262 	*/
263 	this()(auto ref typeof(this) other) {
264 		if (this.ptr !is other.ptr)
265 			this.swap(other);
266 		else {
267 			typeof(this) ini;
268 			other.swap(ini);
269 		}
270 	}
271 
272 	/**
273         Duplicates the container. The elements themselves are not transitively
274         duplicated.
275 
276         Complexity: $(BIGOH n).
277      */
278 	@property Vector!(T, ALLOC) dup() const
279 	{
280 		static if (__traits(compiles, { T a; T b; a = b; } ())) {
281 			auto ret = Vector!(T, ALLOC)(cast(T[])_data._payload);
282 			return ret.move;
283 		}
284 		else static if (__traits(hasMember, T, "dup")) // Element is @disable this(this) but has dup()
285 		{
286 			Vector!(T, ALLOC) vec = Vector!(T, ALLOC)(length);
287 			// swap each element with a duplicate
288 			foreach (size_t i, ref el; _data._payload) {
289 				T t = el.dup;
290 				memmove(vec._data._payload.ptr + i, &t, T.sizeof);
291 				memset(&t, 0, T.sizeof);
292 			}
293 			return vec.move();
294 		} else static assert(false, "Cannot dup() the element: " ~ T.stringof);
295 	}
296 	
297 	/// ditto
298 	@property RefCounted!(Vector!(T, ALLOC), ALLOC) dupr() const
299 	{
300 		return RefCounted!(Vector!(T, ALLOC), ALLOC)(cast(T[])_data._payload);
301 	}
302 	
303 	void swap(ref Vector!(T, ALLOC) other) {
304 		import std.algorithm : swap;
305 		.swap(_data._payload, other._data._payload);
306 		.swap(_data._capacity, other._data._capacity);
307 	}
308 	
309 	@property Vector!(T, ALLOC) move() {
310 		return Vector!(T, ALLOC)(this);
311 	}
312 	
313 	/**
314         Property returning $(D true) if and only if the container has no
315         elements.
316 
317         Complexity: $(BIGOH 1)
318      */
319 	@property bool empty() const
320 	{
321 		return _data._payload.empty;
322 	}
323 	
324 	/**
325         Returns the number of elements in the container.
326 
327         Complexity: $(BIGOH 1).
328      */
329 	@property size_t length() const
330 	{
331 		return _data._payload.length;
332 	}
333 	
334 	/// ditto
335 	size_t opDollar() const
336 	{
337 		return length;
338 	}
339 	
340 	@property T* ptr() inout {
341 		return cast(T*) _data._payload.ptr;
342 	}
343 	
344 	@property T* end() inout {
345 		return this.ptr + this.length;
346 	}
347 	
348 	/**
349         Returns the maximum number of elements the container can store without
350            (a) allocating memory, (b) invalidating iterators upon insertion.
351 
352         Complexity: $(BIGOH 1)
353      */
354 	@property size_t capacity() const
355 	{
356 		return _data._capacity;
357 	}
358 
359 	/*
360 	@property auto range() {
361 		return refRange(&_data._payload);
362 	}
363 	*/
364 	
365 	/**
366         Ensures sufficient capacity to accommodate $(D e) elements.
367 
368         Postcondition: $(D capacity >= e)
369 
370         Complexity: $(BIGOH 1)
371      */
372 	void reserve(size_t elements)
373 	{
374 		_data.reserve(elements);
375 	}
376 	
377 	/**
378         Returns an array that can be translated to a range using ($D refRange).
379 
380         Complexity: $(BIGOH 1)
381      */
382 	auto opSlice() inout
383 	{
384 		//static if (is(T[] == ubyte[]))
385 		//	return cast(string) _data._payload;
386 		//else
387 		return *cast(T[]*)&_data._payload;
388 	}
389 	
390 	/**
391         Returns an array of the container from index $(D a) up to (excluding) index $(D b).
392 
393         Precondition: $(D a <= b && b <= length)
394 
395         Complexity: $(BIGOH 1)
396      */
397 	T[] opSlice(size_t i, size_t j) const
398 	{
399 		version (assert) if (i > j || j > length) throw new RangeError();
400 		return (cast(T[])_data._payload)[i .. j];
401 	}
402 	
403 	/**
404         Forward to $(D opSlice().front) and $(D opSlice().back), respectively.
405 
406         Precondition: $(D !empty)
407 
408         Complexity: $(BIGOH 1)
409      */
410 	@property ref T front()
411 	{
412 		return _data._payload[0];
413 	}
414 	
415 	/// ditto
416 	@property ref T back()
417 	{
418 		return _data._payload[$ - 1];
419 	}
420 	
421 	/**
422         Indexing operators yield or modify the value at a specified index.
423 
424         Precondition: $(D i < length)
425 
426         Complexity: $(BIGOH 1)
427      */	
428 	ref T opIndex(size_t i) const
429 	{
430 		return *cast(T*)&_data._payload[i];
431 	}
432 	
433 	void opIndexAssign(U)(auto ref U val, size_t i)
434 	{
435 		static if (__traits(compiles, {_data._payload[i] = cast(T) val; }()))
436 			_data._payload[i] = cast(T) val;
437 		else { // swap
438 			memmove(_data._payload.ptr + i, &val, U.sizeof);
439 			memset(&val, 0, U.sizeof);
440 		}
441 	}
442 	/**
443         Slicing operations execute an operation on an entire slice.
444 
445         Precondition: $(D i < j && j < length)
446 
447         Complexity: $(BIGOH slice.length)
448      */
449 	void opSliceAssign(Stuff)(auto ref Stuff value)
450 	{
451 		static if (isRandomAccessRange!Stuff)
452 		{
453 			_data.length = value.length;
454 			_data._payload.ptr[0 .. value.length] = value[0 .. $];
455 		} else static if (is(UnConst!Stuff == Vector!(T, ALLOC))) {
456 			_data.length = value._data.length;
457 			_data._payload[] = value._data._payload[];
458 		}
459 		else static if (isImplicitlyConvertible!(T, ElementType!Stuff)) {
460 			_data.length = value.length;
461 			_data._payload[] = cast(T[]) value;
462 		} else static assert(false, "Can't convert " ~ Stuff.stringof ~ " to " ~ T.stringof ~ "[]");
463 	}
464 	
465 	/// ditto
466 	void opSliceAssign(Stuff)(Stuff value, size_t i, size_t j)
467 	{
468 		auto slice = _data._payload;
469 		slice[i .. j] = value;
470 	}
471 	
472 	/// ditto
473 	void opSliceUnary(string op)()
474 		if(op == "++" || op == "--")
475 	{
476 		mixin(op~"_data._payload[];");
477 	}
478 	
479 	/// ditto
480 	void opSliceUnary(string op)(size_t i, size_t j)
481 		if(op == "++" || op == "--")
482 	{
483 		mixin(op~"slice[i .. j];");
484 	}
485 	
486 	/// ditto
487 	void opSliceOpAssign(string op)(T value)
488 	{
489 		mixin("_data._payload[] "~op~"= value;");
490 	}
491 	
492 	/// ditto
493 	void opSliceOpAssign(string op)(T value, size_t i, size_t j)
494 	{
495 		mixin("slice[i .. j] "~op~"= value;");
496 	}
497 	
498 	/**
499         Returns a new container that's the concatenation of $(D this) and its
500         argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
501         define $(D opBinary).
502 
503         Complexity: $(BIGOH n + m), where m is the number of elements in $(D
504         stuff)
505      */
506 	auto opBinary(string op, Stuff)(Stuff stuff)
507 		if (op == "~")
508 	{
509 		TRACE("Appending stuff");
510 		RefCounted!(Vector!(T, ALLOC), ALLOC) result;
511 		// @@@BUG@@ result ~= this[] doesn't work
512 		auto r = this[];
513 		result ~= r;
514 		assert(result.length == length);
515 		result ~= stuff[];
516 		return result;
517 	}
518 	
519 	void opOpAssign(string op, U)(auto ref U input)
520 		if (op == "^")
521 	{
522 		if (this.length < input.length)
523 			this.resize(input.length);
524 
525 		pure static void xorBuf(T)(T* output, const(T)* input, size_t length)
526 		{
527 			while (length >= 8)
528 			{
529 				output[0 .. 8] ^= input[0 .. 8];
530 				
531 				output += 8; input += 8; length -= 8;
532 			}
533 			
534 			output[0 .. length] ^= input[0 .. length];
535 		}
536 
537 		xorBuf(this.ptr, input.ptr, input.length);
538 	}
539 	
540 	/**
541         Forwards to $(D pushBack(stuff)).
542      */
543 	void opOpAssign(string op, Stuff)(auto ref Stuff stuff)
544 		if (op == "~")
545 	{
546 		static if (is (Stuff == RefCounted!(typeof(this)))) {
547 			insertBack(cast(T[]) stuff[]);
548 		}
549 		else static if (is (Stuff == typeof(this))) {
550 			insertBack(cast(T[]) stuff[]);
551 		}
552 		else
553 		{
554 			insertBack(stuff);
555 		}
556 	}
557 	
558 	/**
559         Removes all contents from the container. The container decides how $(D
560         capacity) is affected.
561 
562         Postcondition: $(D empty)
563 
564         Complexity: $(BIGOH n)
565      */
566 	void clear()
567 	{
568 		TRACE("Vector.clear()");
569 		_data.length = 0;
570 	}
571 	
572 	
573 	/**
574         Sets the number of elements in the container to $(D newSize). If $(D
575         newSize) is greater than $(D length), the added elements are added to
576         unspecified positions in the container and initialized with $(D
577         T.init).
578 
579         Complexity: $(BIGOH abs(n - newLength))
580 
581         Postcondition: $(D length == newLength)
582      */
583 	@property void length(size_t newLength)
584 	{
585 		_data.length = newLength;
586 	}
587 	
588 	void resize(size_t newLength)
589 	{
590 		this.length = newLength;
591 	}
592 	
593 	import std.traits : isNumeric;
594 	
595 	int opCmp(Alloc)(auto const ref Vector!(T, Alloc) other) const 
596 	{
597 		if (this[] == other[])
598 			return 0;
599 		else if (this[] < other[])
600 			return -1;
601 		else
602 			return 0;
603 	}
604 
605 	size_t pushBack(Stuff...)(Stuff stuff) 
606 		if (!isNumeric!Stuff || !is ( T == ubyte ))
607 	{
608 		return insertBack(stuff);
609 	}
610 	
611 	size_t pushBack(Stuff...)(Stuff stuff) 
612 		if (isNumeric!Stuff && is(T == ubyte))
613 	{
614 		return insertBack(cast(T) stuff);
615 	}
616 	
617 	size_t insert(Stuff...)(Stuff stuff) {
618 		return insertBack(stuff);
619 	}
620 	
621 	/**
622         Inserts $(D value) to the front or back of the container. $(D stuff)
623         can be a value convertible to $(D T) or a range of objects convertible
624         to $(D T). The stable version behaves the same, but guarantees that
625         ranges iterating over the container are never invalidated.
626 
627         Returns: The number of elements inserted
628 
629         Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
630         elements in $(D stuff)
631     */
632 	size_t insertBack(Stuff)(auto ref Stuff stuff)
633 	{
634 		import std.traits : isImplicitlyConvertible;
635 		static if (isImplicitlyConvertible!(Stuff, T[]))
636 			return _data.pushBack(cast(T[])stuff);
637 		else static if (isSomeString!(Stuff) && !isImplicitlyConvertible!(Stuff, T)) {
638 			return _data.pushBack(cast(T[])stuff);
639 		}
640 		else static if (isSomeString!(Stuff) && isImplicitlyConvertible!(Stuff, T)) {
641 			return _data.pushBack(cast(T)stuff);
642 		}
643 		else static if (isInputRange!(Stuff) && isImplicitlyConvertible!(ForeachType!Stuff, T)) {
644 			size_t i;
645 			foreach (ref el; stuff) {
646 				_data.pushBack(el);
647 				i++;
648 			}
649 			return i;
650 		}
651 		else
652 			return _data.pushBack(cast(T) stuff);
653 	}
654 
655 	/**
656         Removes the value at the back of the container. The stable version
657         behaves the same, but guarantees that ranges iterating over the
658         container are never invalidated.
659 
660         Precondition: $(D !empty)
661 
662         Complexity: $(BIGOH log(n)).
663     */
664 	void removeBack()
665 	{
666 		enforce(!empty);
667 		static if (hasElaborateDestructor!T)
668 			.destroy(_data._payload[$ - 1]);
669 		
670 		_data._payload = _data._payload[0 .. $ - 1];
671 	}
672 	
673 	void removeFront() { 
674 	
675 		enforce(!empty);
676 		static if (hasElaborateDestructor!T)
677 			.destroy(_data._payload[0]);
678 	
679 		_data._payload = _data._payload[1 .. $];
680 	}
681 	
682 	/**
683         Removes $(D howMany) values at the front or back of the
684         container. Unlike the unparameterized versions above, these functions
685         do not throw if they could not remove $(D howMany) elements. Instead,
686         if $(D howMany > n), all elements are removed. The returned value is
687         the effective number of elements removed. The stable version behaves
688         the same, but guarantees that ranges iterating over the container are
689         never invalidated.
690 
691         Returns: The number of elements removed
692 
693         Complexity: $(BIGOH howMany).
694     */
695 	size_t removeBack(size_t howMany)
696 	{
697 		if (howMany > length) howMany = length;
698 		static if (hasElaborateDestructor!T)
699 			foreach (ref e; _data._payload[$ - howMany .. $])
700 				.destroy(e);
701 		
702 		_data._payload = _data._payload[0 .. $ - howMany];
703 		return howMany;
704 	}
705 
706 	/**
707         Inserts $(D stuff) before position i.
708 
709         Returns: The number of values inserted.
710 
711         Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff)
712      */
713 	void insertBefore(Stuff)(size_t i, Stuff stuff)
714 		if (isImplicitlyConvertible!(Stuff, T))
715 	{
716 		enforce(i <= length);
717 		reserve(length + 1);
718 
719 		// Move elements over by one slot
720 		memmove(_data._payload.ptr + i + 1,
721 				_data._payload.ptr + i,
722 				T.sizeof * (length - i));
723 		emplace(_data._payload.ptr + i, stuff);
724 		_data._payload = _data._payload.ptr[0 .. _data._payload.length + 1];
725 	}
726 	
727 	/// ditto
728 	size_t insertBefore(Stuff)(size_t i, Stuff stuff)
729 		if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
730 	{
731 		enforce(i <= length);
732 		static if (isForwardRange!Stuff)
733 		{
734 			// Can find the length in advance
735 			auto extra = walkLength(stuff);
736 			if (!extra) return 0;
737 			reserve(length + extra);
738 			// Move elements over by extra slots
739 			memmove(_data._payload.ptr + i + extra,
740 				_data._payload.ptr + i,
741 				T.sizeof * (length - i));
742 			foreach (p; _data._payload.ptr + i ..
743 				_data._payload.ptr + i + extra)
744 			{
745 				emplace(p, stuff.front);
746 				stuff.popFront();
747 			}
748 			_data._payload = _data._payload.ptr[0 .. _data._payload.length + extra];
749 			return extra;
750 		}
751 		else
752 		{
753 			enforce(_data);
754 			immutable offset = i;
755 			enforce(offset <= length);
756 			auto result = pushBack(stuff);
757 			bringToFront(this[offset .. length - result],
758 						 this[length - result .. length]);
759 			return result;
760 		}
761 	}
762 	
763 	/// ditto
764 	size_t insertAfter(Stuff)(size_t i, Stuff stuff)
765 	{
766 		enforce(r._outer._data is _data);
767 		// TODO: optimize
768 		immutable offset = i;
769 		enforce(offset <= length);
770 		auto result = pushBack(stuff);
771 		bringToFront(this[offset .. length - result],
772 					 this[length - result .. length]);
773 		return result;
774 	}
775 
776 	bool opEquals()(auto const ref RefCounted!(Vector!(T, ALLOC), ALLOC) other_) const {
777 		import memutils.constants : logTrace;
778 		if (other_.empty && empty())
779 			return true;
780 		else if (other_.empty)
781 			return false;
782 		if (other_.length != length)
783 			return false;
784 		// TODO: use the true types
785 		return _data._payload[0 .. length] == other_._data._payload[0 .. length];
786 	}
787 
788 	bool opEquals()(auto const ref Vector!(T, ALLOC) other_) const {
789 		if (other_.empty && empty())
790 			return true;
791 		else if (other_.empty)
792 			return false;
793 		if (other_.length != length)
794 			return false;
795 
796 		return _data._payload[0 .. length] == other_._data._payload[0 .. length];
797 	}
798 
799 	bool opEquals()(auto const ref T[] other) {
800 		logTrace("other: ", other, " this: ", _data._payload);
801 		return other == _data._payload;
802 	}
803 	
804 }
805 
806 auto array(T)(T[] val) 
807 {
808 	return Array!(Unqual!T)(val);
809 }
810 
811 auto vector(T)(T[] val)
812 {
813 	return Vector!(Unqual!T)(val);
814 }
815 
816 void TRACE(T...)(T t) {
817 	//import std.stdio : writeln;
818 	//writeln(t);
819 }