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