1 /**
2 	Utility functions for array processing
3 
4 	Copyright: © 2012 Sönke Ludwig
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module vibe.utils.array;
9 
10 import vibe.internal.utilallocator;
11 
12 import std.algorithm;
13 import std.range : isInputRange, isOutputRange;
14 import std.traits;
15 static import std.utf;
16 
17 
18 enum AppenderResetMode {
19 	keepData,
20 	freeData,
21 	reuseData
22 }
23 
24 struct AllocAppender(ArrayType : E[], E) {
25 	alias ElemType = Unqual!E;
26 
27 	static assert(!hasIndirections!E && !hasElaborateDestructor!E);
28 
29 	private {
30 		ElemType[] m_data;
31 		ElemType[] m_remaining;
32 		IAllocator m_alloc;
33 		bool m_allocatedBuffer = false;
34 	}
35 
36 	this(IAllocator alloc, ElemType[] initial_buffer = null)
37 	{
38 		m_alloc = alloc;
39 		m_data = initial_buffer;
40 		m_remaining = initial_buffer;
41 	}
42 
43 	@disable this(this);
44 
45 	@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_data.length - m_remaining.length]; }
46 
47 	void reset(AppenderResetMode reset_mode = AppenderResetMode.keepData)
48 	{
49 		if (reset_mode == AppenderResetMode.keepData) m_data = null;
50 		else if (reset_mode == AppenderResetMode.freeData) { if (m_allocatedBuffer) m_alloc.deallocate(m_data); m_data = null; }
51 		m_remaining = m_data;
52 	}
53 
54 	/** Grows the capacity of the internal buffer so that it can hold a minumum amount of elements.
55 
56 		Params:
57 			amount = The minimum amount of elements that shall be appendable without
58 				triggering a re-allocation.
59 
60 	*/
61 	void reserve(size_t amount)
62 	@trusted {
63 		size_t nelems = m_data.length - m_remaining.length;
64 		if (!m_data.length) {
65 			m_data = cast(ElemType[])m_alloc.allocate(amount*E.sizeof);
66 			m_remaining = m_data;
67 			m_allocatedBuffer = true;
68 		}
69 		if (m_remaining.length < amount) {
70 			if (m_allocatedBuffer) {
71 				void[] vdata = m_data;
72 				m_alloc.reallocate(vdata, (nelems+amount)*E.sizeof);
73 				m_data = () @trusted { return cast(ElemType[])vdata; } ();
74 			} else {
75 				auto newdata = cast(ElemType[])m_alloc.allocate((nelems+amount)*E.sizeof);
76 				newdata[0 .. nelems] = m_data[0 .. nelems];
77 				m_data = newdata;
78 				m_allocatedBuffer = true;
79 			}
80 		}
81 		m_remaining = m_data[nelems .. m_data.length];
82 	}
83 
84 	void put(E el)
85 	@safe {
86 		if( m_remaining.length == 0 ) grow(1);
87 		m_remaining[0] = el;
88 		m_remaining = m_remaining[1 .. $];
89 	}
90 
91 	void put(ArrayType arr)
92 	@safe {
93 		if (m_remaining.length < arr.length) grow(arr.length);
94 		m_remaining[0 .. arr.length] = arr[];
95 		m_remaining = m_remaining[arr.length .. $];
96 	}
97 
98 	static if( !hasAliasing!E ){
99 		void put(in ElemType[] arr)
100 			@trusted
101 		{
102 			put(cast(ArrayType)arr);
103 		}
104 	}
105 
106 	static if( is(ElemType == char) ){
107 		void put(dchar el)
108 			@safe
109 		{
110 			if( el < 128 ) put(cast(char)el);
111 			else {
112 				char[4] buf;
113 				auto len = std.utf.encode(buf, el);
114 				put(() @trusted { return cast(ArrayType)buf[0 .. len]; }());
115 			}
116 		}
117 	}
118 
119 	static if( is(ElemType == wchar) ){
120 		void put(dchar el)
121 			@safe
122 		{
123 			if( el < 128 ) put(cast(wchar)el);
124 			else {
125 				wchar[3] buf;
126 				auto len = std.utf.encode(buf, el);
127 				put(() @trusted { return cast(ArrayType)buf[0 .. len]; } ());
128 			}
129 		}
130 	}
131 
132 	static if (!is(E == immutable) || !hasAliasing!E) {
133 		/** Appends a number of bytes in-place.
134 
135 			The delegate will get the memory slice of the memory that follows
136 			the already written data. Use `reserve` to ensure that this slice
137 			has enough room. The delegate should overwrite as much of the
138 			slice as desired and then has to return the number of elements
139 			that should be appended (counting from the start of the slice).
140 		*/
141 		void append(scope size_t delegate(scope ElemType[] dst) @safe del)
142 		{
143 			auto n = del(m_remaining);
144 			assert(n <= m_remaining.length);
145 			m_remaining = m_remaining[n .. $];
146 		}
147 	}
148 
149 	void grow(size_t min_free)
150 	{
151 		if( !m_data.length && min_free < 16 ) min_free = 16;
152 
153 		auto min_size = m_data.length + min_free - m_remaining.length;
154 		auto new_size = max(m_data.length, 16);
155 		while( new_size < min_size )
156 			new_size = (new_size * 3) / 2;
157 		reserve(new_size - m_data.length + m_remaining.length);
158 	}
159 }
160 
161 unittest {
162 	auto a = AllocAppender!string(theAllocator());
163 	a.put("Hello");
164 	a.put(' ');
165 	a.put("World");
166 	assert(a.data == "Hello World");
167 	a.reset();
168 	assert(a.data == "");
169 }
170 
171 unittest {
172 	char[4] buf;
173 	auto a = AllocAppender!string(theAllocator(), buf);
174 	a.put("He");
175 	assert(a.data == "He");
176 	assert(a.data.ptr == buf.ptr);
177 	a.put("ll");
178 	assert(a.data == "Hell");
179 	assert(a.data.ptr == buf.ptr);
180 	a.put('o');
181 	assert(a.data == "Hello");
182 	assert(a.data.ptr != buf.ptr);
183 }
184 
185 unittest {
186 	char[4] buf;
187 	auto a = AllocAppender!string(theAllocator(), buf);
188 	a.put("Hello");
189 	assert(a.data == "Hello");
190 	assert(a.data.ptr != buf.ptr);
191 }
192 
193 unittest {
194 	auto app = AllocAppender!(int[])(theAllocator);
195 	app.reserve(2);
196 	app.append((scope mem) {
197 		assert(mem.length >= 2);
198 		mem[0] = 1;
199 		mem[1] = 2;
200 		return size_t(2);
201 	});
202 	assert(app.data == [1, 2]);
203 }
204 
205 unittest {
206 	auto app = AllocAppender!string(theAllocator);
207 	app.reserve(3);
208 	app.append((scope mem) {
209 		assert(mem.length >= 3);
210 		mem[0] = 'f';
211 		mem[1] = 'o';
212 		mem[2] = 'o';
213 		return size_t(3);
214 	});
215 	assert(app.data == "foo");
216 }
217 
218 
219 struct FixedAppender(ArrayType : E[], size_t NELEM, E) {
220 	alias ElemType = Unqual!E;
221 	private {
222 		ElemType[NELEM] m_data;
223 		size_t m_fill;
224 	}
225 
226 	void clear()
227 	{
228 		m_fill = 0;
229 	}
230 
231 	void put(E el)
232 	{
233 		m_data[m_fill++] = el;
234 	}
235 
236 	static if( is(ElemType == char) ){
237 		void put(dchar el)
238 		{
239 			if( el < 128 ) put(cast(char)el);
240 			else {
241 				char[4] buf;
242 				auto len = std.utf.encode(buf, el);
243 				put(cast(ArrayType)buf[0 .. len]);
244 			}
245 		}
246 	}
247 
248 	static if( is(ElemType == wchar) ){
249 		void put(dchar el)
250 		{
251 			if( el < 128 ) put(cast(wchar)el);
252 			else {
253 				wchar[3] buf;
254 				auto len = std.utf.encode(buf, el);
255 				put(cast(ArrayType)buf[0 .. len]);
256 			}
257 		}
258 	}
259 
260 	void put(ArrayType arr)
261 	{
262 		m_data[m_fill .. m_fill+arr.length] = arr[];
263 		m_fill += arr.length;
264 	}
265 
266 	@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_fill]; }
267 
268 	static if (!is(E == immutable)) {
269 		void reset() { m_fill = 0; }
270 	}
271 }
272 
273 
274 /**
275 	TODO: clear ring buffer fields upon removal (to run struct destructors, if T is a struct)
276 */
277 struct FixedRingBuffer(T, size_t N = 0, bool INITIALIZE = true) {
278 	private {
279 		static if( N > 0 ) {
280 			static if (INITIALIZE) T[N] m_buffer;
281 			else T[N] m_buffer = void;
282 		} else T[] m_buffer;
283 		size_t m_start = 0;
284 		size_t m_fill = 0;
285 	}
286 
287 	static if( N == 0 ){
288 		this(size_t capacity) { m_buffer = new T[capacity]; }
289 	}
290 
291 	@property bool empty() const { return m_fill == 0; }
292 
293 	@property bool full() const { return m_fill == m_buffer.length; }
294 
295 	@property size_t length() const { return m_fill; }
296 
297 	@property size_t freeSpace() const { return m_buffer.length - m_fill; }
298 
299 	@property size_t capacity() const { return m_buffer.length; }
300 
301 	static if( N == 0 ){
302 		/// Resets the capacity to zero and explicitly frees the memory for the buffer.
303 		void dispose()
304 		{
305 			import core.memory : __delete;
306 			__delete(m_buffer);
307 			m_buffer = null;
308 			m_start = m_fill = 0;
309 		}
310 
311 		@property void capacity(size_t new_size)
312 		{
313 			if( m_buffer.length ){
314 				auto newbuffer = new T[new_size];
315 				auto dst = newbuffer;
316 				auto newfill = min(m_fill, new_size);
317 				read(dst[0 .. newfill]);
318 				m_buffer = newbuffer;
319 				m_start = 0;
320 				m_fill = newfill;
321 			} else {
322 				m_buffer = new T[new_size];
323 			}
324 		}
325 	}
326 
327 	@property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
328 
329 	@property ref inout(T) back() inout { assert(!empty); return m_buffer[mod(m_start+m_fill-1)]; }
330 
331 	void clear()
332 	{
333 		popFrontN(length);
334 		assert(m_fill == 0);
335 		m_start = 0;
336 	}
337 
338 	void putBack()(T itm) { assert(m_fill < m_buffer.length); m_buffer[mod(m_start + m_fill++)] = itm; }
339 	void putBack(TC : T)(TC[] itms)
340 	{
341 		if( !itms.length ) return;
342 		assert(m_fill+itms.length <= m_buffer.length);
343 		if( mod(m_start+m_fill) >= mod(m_start+m_fill+itms.length) ){
344 			size_t chunk1 = m_buffer.length - (m_start+m_fill);
345 			size_t chunk2 = itms.length - chunk1;
346 			m_buffer[m_start+m_fill .. m_buffer.length] = itms[0 .. chunk1];
347 			m_buffer[0 .. chunk2] = itms[chunk1 .. $];
348 		} else {
349 			m_buffer[mod(m_start+m_fill) .. mod(m_start+m_fill)+itms.length] = itms[];
350 		}
351 		m_fill += itms.length;
352 	}
353 	void putBackN(size_t n) { assert(m_fill+n <= m_buffer.length); m_fill += n; }
354 
355 	alias put = putBack;
356 	alias putN = putBackN;
357 
358 	void putFront(T itm)
359 	{
360 		assert(m_fill < m_buffer.length);
361 		m_start = mod(m_start + m_buffer.length - 1);
362 		m_fill++;
363 		m_buffer[m_start] = itm;
364 	}
365 
366 	void popFront() { assert(!empty); m_start = mod(m_start+1); m_fill--; }
367 	void popFrontN(size_t n) { assert(length >= n); m_start = mod(m_start + n); m_fill -= n; }
368 
369 	void popBack() { assert(!empty); m_fill--; }
370 	void popBackN(size_t n) { assert(length >= n); m_fill -= n; }
371 
372 	void removeAt(Range r)
373 	{
374 		assert(r.m_buffer is m_buffer);
375 		if (r.m_start == m_start) { popFront(); return; }
376 		if( m_start + m_fill > m_buffer.length ){
377 			assert(r.m_start > m_start && r.m_start < m_buffer.length || r.m_start < mod(m_start+m_fill));
378 			if( r.m_start > m_start ){
379 				foreach(i; r.m_start .. m_buffer.length-1)
380 					m_buffer[i] = m_buffer[i+1];
381 				m_buffer[$-1] = m_buffer[0];
382 				foreach(i; 0 .. mod(m_start + m_fill - 1))
383 					m_buffer[i] = m_buffer[i+1];
384 			} else {
385 				foreach(i; r.m_start .. mod(m_start + m_fill - 1))
386 					m_buffer[i] = m_buffer[i+1];
387 			}
388 		} else {
389 			assert(r.m_start >= m_start && r.m_start < m_start+m_fill);
390 			foreach(i; r.m_start .. m_start+m_fill-1)
391 				m_buffer[i] = m_buffer[i+1];
392 		}
393 		m_fill--;
394 		destroy(m_buffer[mod(m_start+m_fill)]); // TODO: only call destroy for non-POD T
395 	}
396 
397 	inout(T)[] peek() inout { return m_buffer[m_start .. min(m_start+m_fill, m_buffer.length)]; }
398 	T[] peekDst() {
399 		if( m_start + m_fill < m_buffer.length ) return m_buffer[m_start+m_fill .. $];
400 		else return m_buffer[mod(m_start+m_fill) .. m_start];
401 	}
402 
403 	void read(T[] dst)
404 	{
405 		assert(dst.length <= length);
406 		if( !dst.length ) return;
407 		if( mod(m_start) >= mod(m_start+dst.length) ){
408 			size_t chunk1 = m_buffer.length - m_start;
409 			size_t chunk2 = dst.length - chunk1;
410 			dst[0 .. chunk1] = m_buffer[m_start .. $];
411 			dst[chunk1 .. $] = m_buffer[0 .. chunk2];
412 		} else {
413 			dst[] = m_buffer[m_start .. m_start+dst.length];
414 		}
415 		popFrontN(dst.length);
416 	}
417 
418 	int opApply(scope int delegate(ref T itm)  @safe del)
419 	{
420 		if( m_start+m_fill > m_buffer.length ){
421 			foreach(i; m_start .. m_buffer.length)
422 				if( auto ret = del(m_buffer[i]) )
423 					return ret;
424 			foreach(i; 0 .. mod(m_start+m_fill))
425 				if( auto ret = del(m_buffer[i]) )
426 					return ret;
427 		} else {
428 			foreach(i; m_start .. m_start+m_fill)
429 				if( auto ret = del(m_buffer[i]) )
430 					return ret;
431 		}
432 		return 0;
433 	}
434 
435 	/// iterate through elements with index
436 	int opApply(scope int delegate(size_t i, ref T itm) @safe del)
437 	{
438 		if( m_start+m_fill > m_buffer.length ){
439 			foreach(i; m_start .. m_buffer.length)
440 				if( auto ret = del(i - m_start, m_buffer[i]) )
441 					return ret;
442 			foreach(i; 0 .. mod(m_start+m_fill))
443 				if( auto ret = del(i + m_buffer.length - m_start, m_buffer[i]) )
444 					return ret;
445 		} else {
446 			foreach(i; m_start .. m_start+m_fill)
447 				if( auto ret = del(i - m_start, m_buffer[i]) )
448 					return ret;
449 		}
450 		return 0;
451 	}
452 
453 	ref inout(T) opIndex(size_t idx) inout { assert(idx < length); return m_buffer[mod(m_start+idx)]; }
454 
455 	Range opSlice() { return Range(m_buffer, m_start, m_fill); }
456 
457 	Range opSlice(size_t from, size_t to)
458 	{
459 		assert(from <= to);
460 		assert(to <= m_fill);
461 		return Range(m_buffer, mod(m_start+from), to-from);
462 	}
463 
464 	size_t opDollar(size_t dim)() const if(dim == 0) { return length; }
465 
466 	private size_t mod(size_t n)
467 	const {
468 		static if( N == 0 ){
469 			/*static if(PotOnly){
470 				return x & (m_buffer.length-1);
471 			} else {*/
472 				return n % m_buffer.length;
473 			//}
474 		} else static if( ((N - 1) & N) == 0 ){
475 			return n & (N - 1);
476 		} else return n % N;
477 	}
478 
479 	static struct Range {
480 		private {
481 			T[] m_buffer;
482 			size_t m_start;
483 			size_t m_length;
484 		}
485 
486 		private this(T[] buffer, size_t start, size_t length)
487 		{
488 			m_buffer = buffer;
489 			m_start = start;
490 			m_length = length;
491 		}
492 
493 		@property bool empty() const { return m_length == 0; }
494 
495 		@property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
496 
497 		void popFront()
498 		{
499 			assert(!empty);
500 			m_start++;
501 			m_length--;
502 			if( m_start >= m_buffer.length )
503 				m_start = 0;
504 		}
505 	}
506 }
507 
508 unittest {
509 	import std.range : only;
510 
511 	static assert(isInputRange!(FixedRingBuffer!int) && isOutputRange!(FixedRingBuffer!int, int));
512 
513 	FixedRingBuffer!(int, 5) buf;
514 	assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . .
515 	assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . .
516 	assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . .
517 	assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 .
518 	assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5
519 	assert(buf.length == 5 && buf.freeSpace == 0);
520 	assert(buf.front == 1);
521 	buf.popFront(); // .|2 3 4 5
522 	assert(buf.front == 2);
523 	buf.popFrontN(2); // . . .|4 5
524 	assert(buf.front == 4);
525 	assert(buf.length == 2 && buf.freeSpace == 3);
526 	buf.put([6, 7, 8]); // 6 7 8|4 5
527 	assert(buf.length == 5 && buf.freeSpace == 0);
528 	int[5] dst;
529 	buf.read(dst); // . . .|. .
530 	assert(dst == [4, 5, 6, 7, 8]);
531 	assert(buf.length == 0 && buf.freeSpace == 5);
532 	buf.put([1, 2]); // . . .|1 2
533 	assert(buf.length == 2 && buf.freeSpace == 3);
534 	buf.read(dst[0 .. 2]); //|. . . . .
535 	assert(dst[0 .. 2] == [1, 2]);
536 
537 	buf.put([0, 0, 0, 1, 2]); //|0 0 0 1 2
538 	buf.popFrontN(2); //. .|0 1 2
539 	buf.put([3, 4]); // 3 4|0 1 2
540 	foreach(i, item; buf)
541 	{
542 		assert(i == item);
543 	}
544 
545 	assert(buf.front == 0);
546 	assert(buf.full);
547 	buf.removeAt(buf[0..1]); //4 .|1 2 3
548 	foreach(i, item; buf)
549 	{
550 		assert(i == item - 1);
551 	}
552 
553 	buf.put(5); // 4 5|1 2 3
554 	buf.removeAt(buf[3..4]); // 5 .|1 2 3
555 	assert(buf.front == 1); buf.popFront();
556 	assert(buf.front == 2); buf.popFront();
557 	assert(buf.front == 3); buf.popFront();
558 	assert(buf.front == 5); buf.popFront();
559 	assert(buf.empty);
560 
561 	buf.put(1);
562 	assert(buf.front == 1);
563 	buf.front = 2;
564 	assert(buf.front == 2);
565 	buf[].front = 3;
566 	assert(buf.front == 3);
567 
568 	buf.putFront(4);
569 	assert(buf.length == 2);
570 	assert(buf[].equal(only(4, 3)));
571 }
572 
573 
574 struct ArraySet(Key)
575 {
576 	import stdx.allocator : makeArray, expandArray, dispose;
577 	import stdx.allocator.building_blocks.affix_allocator : AffixAllocator;
578 
579 	private {
580 		IAllocator AW(IAllocator a) { return a; }
581 		alias AllocatorType = AffixAllocator!(IAllocator, int);
582 		Key[4] m_staticEntries;
583 		Key[] m_entries;
584 		AllocatorType m_allocator;
585 	}
586 
587 	~this()
588 	@trusted {
589 		if (m_entries.ptr) {
590 			if (--allocator.prefix(m_entries) <= 0) {
591 				try allocator.dispose(m_entries);
592 				catch (Exception e) assert(false, e.msg); // should never happen
593 			}
594 		}
595 	}
596 
597 	this(this)
598 	@trusted {
599 		if (m_entries.ptr) {
600 			allocator.prefix(m_entries)++;
601 		}
602 	}
603 
604 	@property ArraySet dup()
605 	{
606 		ArraySet ret;
607 		ret.m_staticEntries = m_staticEntries;
608 		ret.m_allocator = m_allocator;
609 
610 		if (m_entries.length) {
611 			Key[] duped;
612 			() @trusted {
613 				try duped = allocator.makeArray!(Key)(m_entries.length);
614 				catch (Exception e) assert(false, e.msg);
615 				if (!duped.length)
616 					assert(false, "Failed to allocate memory for duplicated "~ArraySet.stringof);
617 				allocator.prefix(duped) = 1;
618 			} ();
619 			duped[] = m_entries[];
620 			ret.m_entries = duped;
621 		}
622 
623 		return ret;
624 	}
625 
626 	void setAllocator(IAllocator allocator)
627 	in { assert(m_entries.ptr is null, "Cannot set allocator after elements have been inserted."); }
628 	do {
629 		m_allocator = AllocatorType(AW(allocator));
630 	}
631 
632 	bool opBinaryRight(string op)(Key key) if (op == "in") { return contains(key); }
633 
634 	int opApply(int delegate(ref Key) @safe del)
635 	{
636 		foreach (ref k; m_staticEntries)
637 			if (k != Key.init)
638 				if (auto ret = del(k))
639 					return ret;
640 		foreach (ref k; m_entries)
641 			if (k != Key.init)
642 				if (auto ret = del(k))
643 					return ret;
644 		return 0;
645 	}
646 
647 	bool contains(Key key)
648 	const {
649 		foreach (ref k; m_staticEntries) if (k == key) return true;
650 		foreach (ref k; m_entries) if (k == key) return true;
651 		return false;
652 	}
653 
654 	void insert(Key key)
655 	{
656 		if (contains(key)) return;
657 
658 		foreach (ref k; m_staticEntries)
659 			if (k == Key.init) {
660 				k = key;
661 				return;
662 			}
663 
664 		foreach (ref k; m_entries)
665 			if (k == Key.init) {
666 				k = key;
667 				return;
668 			}
669 
670 		size_t oldlen = m_entries.length;
671 
672 		() @trusted {
673 			try {
674 				if (!oldlen) {
675 					m_entries = allocator.makeArray!Key(64);
676 					assert(m_entries.length, "Failed to allocate memory for "~ArraySet.stringof);
677 					allocator.prefix(m_entries) = 1;
678 				} else {
679 					int oldrc = allocator.prefix(m_entries);
680 					if (!allocator.expandArray(m_entries, max(64, oldlen * 3 / 4)))
681 						assert(false, "Failed to allocate memory for "~ArraySet.stringof);
682 					allocator.prefix(m_entries) = oldrc;
683 				}
684 			} catch (Exception e) assert(false, e.msg);
685 		} ();
686 
687 		m_entries[oldlen] = key;
688 	}
689 
690 	void remove(Key key)
691 	{
692 		foreach (ref k; m_staticEntries) if (k == key) { k = Key.init; return; }
693 		foreach (ref k; m_entries) if (k == key) { k = Key.init; return; }
694 	}
695 
696 	ref allocator()
697 	nothrow @trusted {
698 		try {
699 			auto palloc = m_allocator._parent;
700 			if (!palloc) {
701 				assert(vibeThreadAllocator !is null, "No theAllocator set!?");
702 				m_allocator = AllocatorType(AW(vibeThreadAllocator));
703 			}
704 		} catch (Exception e) assert(false, e.msg); // should never throw
705 		return m_allocator;
706 	}
707 }
708 
709 @safe nothrow unittest {
710 	import stdx.allocator : allocatorObject;
711 	import stdx.allocator.mallocator : Mallocator;
712 
713 	ArraySet!int s;
714 	s.setAllocator(() @trusted { return Mallocator.instance.allocatorObject; } ());
715 
716 	ArraySet!int t;
717 	t = s;
718 
719 	s.insert(1);
720 	s.insert(2);
721 	s.insert(3);
722 	s.insert(4);
723 	assert(s.contains(1));
724 	assert(s.contains(2));
725 	assert(s.contains(3));
726 	assert(s.contains(4));
727 	assert(!t.contains(1));
728 
729 	s.insert(5);
730 	assert(s.contains(5));
731 
732 	t = s;
733 	assert(t.contains(5));
734 	assert(t.contains(1));
735 
736 	s.insert(6);
737 	assert(s.contains(6));
738 	assert(t.contains(6));
739 
740 	s = ArraySet!int.init;
741 	assert(!s.contains(1));
742 	assert(t.contains(1));
743 	assert(t.contains(6));
744 
745 	s = t.dup;
746 	assert(s.contains(1));
747 	assert(s.contains(6));
748 
749 	t.remove(1);
750 	assert(!t.contains(1));
751 	assert(s.contains(1));
752 	assert(t.contains(2));
753 	assert(t.contains(6));
754 
755 	t.remove(6);
756 	assert(!t.contains(6));
757 	assert(s.contains(6));
758 	assert(t.contains(5));
759 }