1 /**
2 	Utility functions for array processing
3 
4 	Copyright: © 2012 RejectedSoftware e.K.
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 vutil.array;
9 
10 import vutil.memory;
11 
12 import std.algorithm;
13 import std.range : isInputRange, isOutputRange;
14 import std.traits;
15 static import std.utf;
16 
17 
18 void removeFromArray(T)(ref T[] array, T item)
19 {
20 	foreach( i; 0 .. array.length )
21 		if( array[i] is item ){
22 			removeFromArrayIdx(array, i);
23 			return;
24 		}
25 }
26 
27 void removeFromArrayIdx(T)(ref T[] array, size_t idx)
28 {
29 	foreach( j; idx+1 .. array.length)
30 		array[j-1] = array[j];
31 	array.length = array.length-1;
32 }
33 
34 enum AppenderResetMode {
35 	keepData,
36 	freeData,
37 	reuseData
38 }
39 
40 struct AllocAppender(ArrayType : E[], E) {
41 	alias ElemType = Unqual!E;
42 
43 	static assert(!hasIndirections!E && !hasElaborateDestructor!E);
44 
45 	private {
46 		ElemType[] m_data;
47 		ElemType[] m_remaining;
48 		Allocator m_alloc;
49 		bool m_allocatedBuffer = false;
50 	}
51 
52 	this(Allocator alloc, ElemType[] initial_buffer = null)
53 	{
54 		m_alloc = alloc;
55 		m_data = initial_buffer;
56 		m_remaining = initial_buffer;
57 	}
58 
59 	@disable this(this);
60 
61 	@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_data.length - m_remaining.length]; }
62 
63 	void reset(AppenderResetMode reset_mode = AppenderResetMode.keepData)
64 	{
65 		if (reset_mode == AppenderResetMode.keepData) m_data = null;
66 		else if (reset_mode == AppenderResetMode.freeData) { if (m_allocatedBuffer) m_alloc.free(m_data); m_data = null; }
67 		m_remaining = m_data;
68 	}
69 
70 	void reserve(size_t amt)
71 	{
72 		size_t nelems = m_data.length - m_remaining.length;
73 		if (!m_data.length) {
74 			m_data = cast(ElemType[])m_alloc.alloc(amt*E.sizeof);
75 			m_remaining = m_data;
76 			m_allocatedBuffer = true;
77 		}
78 		if (m_remaining.length < amt) {
79 			debug {
80 				import std.digest.crc;
81 				auto checksum = crc32Of(m_data[0 .. nelems]);
82 			}
83 			if (m_allocatedBuffer) m_data = cast(ElemType[])m_alloc.realloc(m_data, (nelems+amt)*E.sizeof);
84 			else {
85 				auto newdata = cast(ElemType[])m_alloc.alloc((nelems+amt)*E.sizeof);
86 				newdata[0 .. nelems] = m_data[0 .. nelems];
87 				m_data = newdata;
88 				m_allocatedBuffer = true;
89 			}
90 			debug assert(crc32Of(m_data[0 .. nelems]) == checksum);
91 		}
92 		m_remaining = m_data[nelems .. m_data.length];
93 	}
94 
95 	void put(E el)
96 	{
97 		if( m_remaining.length == 0 ) grow(1);
98 		m_remaining[0] = el;
99 		m_remaining = m_remaining[1 .. $];
100 	}
101 
102 	void put(ArrayType arr)
103 	{
104 		if (m_remaining.length < arr.length) grow(arr.length);
105 		m_remaining[0 .. arr.length] = arr[];
106 		m_remaining = m_remaining[arr.length .. $];
107 	}
108 
109 	static if( !hasAliasing!E ){
110 		void put(in ElemType[] arr){
111 			put(cast(ArrayType)arr);
112 		}
113 	}
114 
115 	static if( is(ElemType == char) ){
116 		void put(dchar el)
117 		{
118 			if( el < 128 ) put(cast(char)el);
119 			else {
120 				char[4] buf;
121 				auto len = std.utf.encode(buf, el);
122 				put(cast(ArrayType)buf[0 .. len]);
123 			}
124 		}
125 	}
126 
127 	static if( is(ElemType == wchar) ){
128 		void put(dchar el)
129 		{
130 			if( el < 128 ) put(cast(wchar)el);
131 			else {
132 				wchar[3] buf;
133 				auto len = std.utf.encode(buf, el);
134 				put(cast(ArrayType)buf[0 .. len]);
135 			}
136 		}
137 	}
138 
139 	void grow(size_t min_free)
140 	{
141 		if( !m_data.length && min_free < 16 ) min_free = 16;
142 
143 		auto min_size = m_data.length + min_free - m_remaining.length;
144 		auto new_size = max(m_data.length, 16);
145 		while( new_size < min_size )
146 			new_size = (new_size * 3) / 2;
147 		reserve(new_size - m_data.length + m_remaining.length);
148 	}
149 }
150 
151 unittest {
152 	auto a = AllocAppender!string(defaultAllocator());
153 	a.put("Hello");
154 	a.put(' ');
155 	a.put("World");
156 	assert(a.data == "Hello World");
157 	a.reset();
158 	assert(a.data == "");
159 }
160 
161 unittest {
162 	char[4] buf;
163 	auto a = AllocAppender!string(defaultAllocator(), buf);
164 	a.put("He");
165 	assert(a.data == "He");
166 	assert(a.data.ptr == buf.ptr);
167 	a.put("ll");
168 	assert(a.data == "Hell");
169 	assert(a.data.ptr == buf.ptr);
170 	a.put('o');
171 	assert(a.data == "Hello");
172 	assert(a.data.ptr != buf.ptr);
173 }
174 
175 unittest {
176 	char[4] buf;
177 	auto a = AllocAppender!string(defaultAllocator(), buf);
178 	a.put("Hello");
179 	assert(a.data == "Hello");
180 	assert(a.data.ptr != buf.ptr);
181 }
182 
183 struct FixedAppender(ArrayType : E[], size_t NELEM, E) {
184 	alias ElemType = Unqual!E;
185 	private {
186 		ElemType[NELEM] m_data;
187 		size_t m_fill;
188 	}
189 
190 	void clear()
191 	{
192 		m_fill = 0;
193 	}
194 
195 	void put(E el)
196 	{
197 		m_data[m_fill++] = el;
198 	}
199 
200 	static if( is(ElemType == char) ){
201 		void put(dchar el)
202 		{
203 			if( el < 128 ) put(cast(char)el);
204 			else {
205 				char[4] buf;
206 				auto len = std.utf.encode(buf, el);
207 				put(cast(ArrayType)buf[0 .. len]);
208 			}
209 		}
210 	}
211 
212 	static if( is(ElemType == wchar) ){
213 		void put(dchar el)
214 		{
215 			if( el < 128 ) put(cast(wchar)el);
216 			else {
217 				wchar[3] buf;
218 				auto len = std.utf.encode(buf, el);
219 				put(cast(ArrayType)buf[0 .. len]);
220 			}
221 		}
222 	}
223 
224 	void put(ArrayType arr)
225 	{
226 		m_data[m_fill .. m_fill+arr.length] = (cast(ElemType[])arr)[];
227 		m_fill += arr.length;
228 	}
229 
230 	@property ArrayType data() { return cast(ArrayType)m_data[0 .. m_fill]; }
231 
232 	static if (!is(E == immutable)) {
233 		void reset() { m_fill = 0; }
234 	}
235 }
236 
237 
238 /**
239 	TODO: clear ring buffer fields upon removal (to run struct destructors, if T is a struct)
240 */
241 struct FixedRingBuffer(T, size_t N = 0) {
242 	private {
243 		static if( N > 0 ) T[N] m_buffer;
244 		else T[] m_buffer;
245 		size_t m_start = 0;
246 		size_t m_fill = 0;
247 	}
248 
249 	static if( N == 0 ){
250 		bool m_freeOnDestruct;
251 		this(size_t capacity) { m_buffer = new T[capacity]; }
252 		~this() { if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer; }
253 	}
254 
255 	@property bool empty() const { return m_fill == 0; }
256 
257 	@property bool full() const { return m_fill == m_buffer.length; }
258 
259 	@property size_t length() const { return m_fill; }
260 
261 	@property size_t freeSpace() const { return m_buffer.length - m_fill; }
262 
263 	@property size_t capacity() const { return m_buffer.length; }
264 
265 	static if( N == 0 ){
266 		@property void freeOnDestruct(bool b) { m_freeOnDestruct = b; }
267 		@property void capacity(size_t new_size)
268 		{
269 			if( m_buffer.length ){
270 				auto newbuffer = new T[new_size];
271 				auto dst = newbuffer;
272 				auto newfill = min(m_fill, new_size);
273 				read(dst[0 .. newfill]);
274 				if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer;
275 				m_buffer = newbuffer;
276 				m_start = 0;
277 				m_fill = newfill;
278 			} else {
279 				if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer;
280 				m_buffer = new T[new_size];
281 			}
282 		}
283 	}
284 	
285 	@property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
286 
287 	@property ref inout(T) back() inout { assert(!empty); return m_buffer[mod(m_start+m_fill-1)]; }
288 
289 	void clear()
290 	{
291 		popFrontN(length);
292 		assert(m_fill == 0);
293 		m_start = 0;
294 	}
295 
296 	void put()(T itm) { assert(m_fill < m_buffer.length); m_buffer[mod(m_start + m_fill++)] = itm; }
297 	void put(TC : T)(TC[] itms)
298 	{
299 		if( !itms.length ) return;
300 		assert(m_fill+itms.length <= m_buffer.length);
301 		if( mod(m_start+m_fill) >= mod(m_start+m_fill+itms.length) ){
302 			size_t chunk1 = m_buffer.length - (m_start+m_fill);
303 			size_t chunk2 = itms.length - chunk1;
304 			m_buffer[m_start+m_fill .. m_buffer.length] = itms[0 .. chunk1];
305 			m_buffer[0 .. chunk2] = itms[chunk1 .. $];
306 		} else {
307 			m_buffer[mod(m_start+m_fill) .. mod(m_start+m_fill)+itms.length] = itms[];
308 		}
309 		m_fill += itms.length;
310 	}
311 	void putN(size_t n) { assert(m_fill+n <= m_buffer.length); m_fill += n; }
312 
313 	void popFront() { assert(!empty); m_start = mod(m_start+1); m_fill--; }
314 	void popFrontN(size_t n) { assert(length >= n); m_start = mod(m_start + n); m_fill -= n; }
315 
316 	void popBack() { assert(!empty); m_fill--; }
317 	void popBackN(size_t n) { assert(length >= n); m_fill -= n; }
318 
319 	void removeAt(Range r)
320 	{
321 		assert(r.m_buffer is m_buffer);
322 		if( m_start + m_fill > m_buffer.length ){
323 			assert(r.m_start >= m_start && r.m_start < m_buffer.length || r.m_start < mod(m_start+m_fill));
324 			if( r.m_start > m_start ){
325 				foreach(i; r.m_start .. m_buffer.length-1)
326 					m_buffer[i] = m_buffer[i+1];
327 				m_buffer[$-1] = m_buffer[0];
328 				foreach(i; 0 .. mod(m_start + m_fill - 1))
329 					m_buffer[i] = m_buffer[i+1];
330 			} else {
331 				foreach(i; r.m_start .. mod(m_start + m_fill - 1))
332 					m_buffer[i] = m_buffer[i+1];
333 			}
334 		} else {
335 			assert(r.m_start >= m_start && r.m_start < m_start+m_fill);
336 			foreach(i; r.m_start .. m_start+m_fill-1)
337 				m_buffer[i] = m_buffer[i+1];
338 		}
339 		m_fill--;
340 		destroy(m_buffer[mod(m_start+m_fill)]); // TODO: only call destroy for non-POD T
341 	}
342 
343 	inout(T)[] peek() inout { return m_buffer[m_start .. min(m_start+m_fill, m_buffer.length)]; }
344 	T[] peekDst() {
345 		if( m_start + m_fill < m_buffer.length ) return m_buffer[m_start+m_fill .. $];
346 		else return m_buffer[mod(m_start+m_fill) .. m_start];
347 	}
348 
349 	void read(T[] dst)
350 	{
351 		assert(dst.length <= length);
352 		if( !dst.length ) return;
353 		if( mod(m_start) >= mod(m_start+dst.length) ){
354 			size_t chunk1 = m_buffer.length - m_start;
355 			size_t chunk2 = dst.length - chunk1;
356 			dst[0 .. chunk1] = m_buffer[m_start .. $];
357 			dst[chunk1 .. $] = m_buffer[0 .. chunk2];
358 		} else {
359 			dst[] = m_buffer[m_start .. m_start+dst.length];
360 		}
361 		popFrontN(dst.length);
362 	}
363 
364 	int opApply(scope int delegate(ref T itm) del)
365 	{
366 		if( m_start+m_fill > m_buffer.length ){
367 			foreach(i; m_start .. m_buffer.length)
368 				if( auto ret = del(m_buffer[i]) )
369 					return ret;
370 			foreach(i; 0 .. mod(m_start+m_fill))
371 				if( auto ret = del(m_buffer[i]) )
372 					return ret;
373 		} else {
374 			foreach(i; m_start .. m_start+m_fill)
375 				if( auto ret = del(m_buffer[i]) )
376 					return ret;
377 		}
378 		return 0;
379 	}
380 
381 	ref inout(T) opIndex(size_t idx) inout { assert(idx < length); return m_buffer[mod(m_start+idx)]; }
382 
383 	Range opSlice() { return Range(m_buffer, m_start, m_fill); }
384 
385 	Range opSlice(size_t from, size_t to)
386 	{
387 		assert(from <= to);
388 		assert(to <= m_fill);
389 		return Range(m_buffer, mod(m_start+from), to-from);
390 	}
391 
392 	size_t opDollar(size_t dim)() const if(dim == 0) { return length; }
393 
394 	private size_t mod(size_t n)
395 	const {
396 		static if( N == 0 ){
397 			/*static if(PotOnly){
398 				return x & (m_buffer.length-1);
399 			} else {*/
400 				return n % m_buffer.length;
401 			//}
402 		} else static if( ((N - 1) & N) == 0 ){
403 			return n & (N - 1);
404 		} else return n % N;
405 	}
406 
407 	static struct Range {
408 		private {
409 			T[] m_buffer;
410 			size_t m_start;
411 			size_t m_length;
412 		}
413 
414 		private this(T[] buffer, size_t start, size_t length)
415 		{
416 			m_buffer = buffer;
417 			m_start = start;
418 			m_length = length;
419 		}
420 
421 		@property bool empty() const { return m_length == 0; }
422 
423 		@property inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
424 
425 		void popFront()
426 		{
427 			assert(!empty);
428 			m_start++;
429 			m_length--;
430 			if( m_start >= m_buffer.length )
431 				m_start = 0;
432 		}
433 	}
434 }
435 
436 unittest {
437 	static assert(isInputRange!(FixedRingBuffer!int) && isOutputRange!(FixedRingBuffer!int, int));
438 
439 	FixedRingBuffer!(int, 5) buf;
440 	assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . .
441 	assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . .
442 	assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . .
443 	assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 .
444 	assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5
445 	assert(buf.length == 5 && buf.freeSpace == 0);
446 	assert(buf.front == 1);
447 	buf.popFront(); // .|2 3 4 5
448 	assert(buf.front == 2);
449 	buf.popFrontN(2); // . . .|4 5
450 	assert(buf.front == 4);
451 	assert(buf.length == 2 && buf.freeSpace == 3);
452 	buf.put([6, 7, 8]); // 6 7 8|4 5
453 	assert(buf.length == 5 && buf.freeSpace == 0);
454 	int[5] dst;
455 	buf.read(dst); // . . .|. .
456 	assert(dst == [4, 5, 6, 7, 8]);
457 	assert(buf.length == 0 && buf.freeSpace == 5);
458 	buf.put([1, 2]); // . . .|1 2
459 	assert(buf.length == 2 && buf.freeSpace == 3);
460 	buf.read(dst[0 .. 2]); //|. . . . .
461 	assert(dst[0 .. 2] == [1, 2]);
462 }
463 
464 
465 struct ArraySet(Key)
466 {
467 	private {
468 		Key[4] m_staticEntries;
469 		Key[] m_entries;
470 	}
471 
472 	@property ArraySet dup()
473 	{
474 		return ArraySet(m_staticEntries, m_entries.dup);
475 	}
476 
477 	bool opBinaryRight(string op)(Key key) if (op == "in") { return contains(key); }
478 
479 	int opApply(int delegate(ref Key) del)
480 	{
481 		foreach (ref k; m_staticEntries)
482 			if (k != Key.init)
483 				if (auto ret = del(k))
484 					return ret;
485 		foreach (ref k; m_entries)
486 			if (k != Key.init)
487 				if (auto ret = del(k))
488 					return ret;
489 		return 0;
490 	}
491 
492 	bool contains(Key key)
493 	const {
494 		foreach (ref k; m_staticEntries) if (k == key) return true;
495 		foreach (ref k; m_entries) if (k == key) return true;
496 		return false;
497 	}
498 
499 	void insert(Key key)
500 	{
501 		if (contains(key)) return;
502 		foreach (ref k; m_staticEntries)
503 			if (k == Key.init) {
504 				k = key;
505 				return;
506 			}
507 		foreach (ref k; m_entries)
508 			if (k == Key.init) {
509 				k = key;
510 				return;
511 			}
512 		m_entries ~= key;
513 	}
514 
515 	void remove(Key key)
516 	{
517 		foreach (ref k; m_staticEntries) if (k == key) { k = Key.init; return; }
518 		foreach (ref k; m_entries) if (k == key) { k = Key.init; return; }
519 	}
520 }