1 /**
2 	Utility functions for circular array processing
3 	Copyright: © 2012 RejectedSoftware e.K., © 2014-2015 Etienne Cimon
4 	License: Subject to the terms of the MIT license, as written in the included LICENSE file.
5 	Authors: Sönke Ludwig, Etienne Cimon
6 */
7 module memutils.circularbuffer;
8 
9 import memutils.allocators;
10 import memutils.constants;
11 import std.traits : hasElaborateDestructor, isBasicType, isPointer;
12 import memutils.utils;
13 import memutils.helpers;
14 
15 @trusted nothrow:
16 
17 struct CircularBuffer(T, size_t N = 0, ALLOC = void) {
18     @trusted nothrow:
19 	@disable this(this);
20 
21 	private {
22 		static if( N > 0 ) T[N] m_buffer;
23 		else T[] m_buffer;
24 		size_t m_start = 0;
25 		size_t m_fill = 0;
26 	}
27 	static if( N == 0 ){
28 		this(size_t capacity) { m_buffer = allocArray!(T, ALLOC)(capacity); }
29 		~this() { if (m_buffer.length > 0) freeArray!(T, ALLOC)(m_buffer, m_fill, m_start); m_buffer = null; }
30 	} 
31 	else {
32 		// clear ring buffer static fields upon removal (to run struct destructors, if T is a struct)
33 		~this() 
34 		{ 
35 			// TODO: Test this
36 			// destroy(m_buffer[m_start .. m_fill]); 
37 		}
38 	}
39 	@property bool empty() const { return m_fill == 0; }
40 	@property bool full() const { return m_fill == m_buffer.length; }
41 	@property size_t length() const { return m_fill; }
42 	@property size_t freeSpace() const { return m_buffer.length - m_fill; }
43 	@property size_t capacity() const { return m_buffer.length; }
44 	static if( N == 0 ){
45 		@property void capacity(size_t new_size)
46 		{
47 			if (new_size <= length || new_size == capacity) return;
48 			if( m_buffer.length ){
49 				auto temp = allocArray!(T, ALLOC)(new_size);
50 				size_t tmp_fill = m_fill;
51 				read(temp[0 .. m_fill]);
52 				m_start = 0;
53 				m_fill = tmp_fill;
54 				freeArray!(T, ALLOC)(m_buffer, m_fill, m_start);
55 				m_buffer = temp;
56 			} else m_buffer = allocArray!(T, ALLOC)(new_size);
57 		}
58 	}
59 	@property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
60 	@property ref inout(T) back() inout { assert(!empty); return m_buffer[mod(m_start+m_fill-1)]; }
61 	void clear()
62 	{
63 		popFrontN(length);
64 		assert(m_fill == 0);
65 		m_start = 0;
66 	}
67 	void put()(T itm) { assert(m_fill < m_buffer.length); m_buffer[mod(m_start + m_fill++)] = itm; }
68 	void forcePut()(T itm) { if (m_fill >= m_buffer.length) popBack(); m_buffer[mod(m_start + m_fill++)] = itm; }
69 	void put(TC : T)(TC[] itms)
70 	{
71 		if( !itms.length ) return;
72 		static if( N == 0 ) {
73 			if (m_fill+itms.length > m_buffer.length)
74 				capacity = capacity*2;
75 		} else assert(m_fill+itms.length <= m_buffer.length, "Cannot write to buffer, it is full.");
76 		if( mod(m_start+m_fill) >= mod(m_start+m_fill+itms.length) ){
77 			size_t chunk1 = m_buffer.length - (m_start+m_fill);
78 			size_t chunk2 = itms.length - chunk1;
79 			m_buffer[m_start+m_fill .. m_buffer.length] = itms[0 .. chunk1];
80 			m_buffer[0 .. chunk2] = itms[chunk1 .. $];
81 		} else {
82 			m_buffer[mod(m_start+m_fill) .. mod(m_start+m_fill)+itms.length] = itms[];
83 		}
84 		m_fill += itms.length;
85 	}
86 	void putN(size_t n) { assert(m_fill+n <= m_buffer.length); m_fill += n; }
87 	void popFront() { assert(!empty); m_start = mod(m_start+1); m_fill--; }
88 	void popFrontN(size_t n) { 
89 		import core.stdc.string : memset; 
90 		assert(length >= n); 
91 		m_start = mod(m_start + n);
92 		m_fill -= n;
93 	}
94 	void popBack() { assert(!empty); m_fill--; }
95 	void popBackN(size_t n) { assert(length >= n); m_fill -= n; }
96 
97 	// moves all the values from the buffer one step down at start of the reference range
98 	void removeAt(Range r)
99 	{
100 		assert(r.m_buffer is m_buffer);
101 		if( m_start + m_fill > m_buffer.length ){
102 			assert(r.m_start >= m_start && r.m_start < m_buffer.length || r.m_start < mod(m_start+m_fill));
103 			if( r.m_start > m_start ){
104 				foreach(i; r.m_start .. m_buffer.length-1)
105 					m_buffer[i] = m_buffer[i+1];
106 				m_buffer[$-1] = m_buffer[0];
107 				foreach(i; 0 .. mod(m_start + m_fill - 1))
108 					m_buffer[i] = m_buffer[i+1];
109 			} else {
110 				foreach(i; r.m_start .. mod(m_start + m_fill - 1))
111 					m_buffer[i] = m_buffer[i+1];
112 			}
113 		} else {
114 			assert(r.m_start >= m_start && r.m_start < m_start+m_fill);
115 			foreach(i; r.m_start .. m_start+m_fill-1)
116 				m_buffer[i] = m_buffer[i+1];
117 		}
118 		m_fill--;
119 		static if (hasElaborateDestructor!T) { // calls destructors
120 			static if (is(T == struct) && isPointer!T) .destroy(*m_buffer[mod(m_start+m_fill)]);
121 			else .destroy(m_buffer[mod(m_start+m_fill)]);
122 		}
123 	}
124 	inout(T)[] peek() inout { return m_buffer[m_start .. min(cast(size_t)(m_start+m_fill), m_buffer.length)]; }
125 	T[] peekDst() {
126 		if( m_start + m_fill < m_buffer.length ) return m_buffer[m_start+m_fill .. $];
127 		else return m_buffer[mod(m_start+m_fill) .. m_start];
128 	}
129 	void read(T[] dst)
130 	{
131 		import core.stdc.string : memset;
132 		assert(dst.length <= length);
133 		if( !dst.length ) return;
134 		if( mod(m_start) >= mod(m_start+dst.length) ){
135 			size_t chunk1 = m_buffer.length - m_start;
136 			size_t chunk2 = dst.length - chunk1;
137 			dst[0 .. chunk1] = m_buffer[m_start .. $];
138 			dst[chunk1 .. $] = m_buffer[0 .. chunk2];
139 			//static if (is(ALLOC == SecureMem)) 
140 			//{
141 			//	memset(m_buffer.ptr + m_start, 0, chunk1);
142 			//	memset(m_buffer.ptr, 0, chunk2);
143 			//}
144 		} else {
145 			dst[] = m_buffer[m_start .. m_start+dst.length];
146 			//static if (is(ALLOC == SecureMem)) 
147 			//	memset(m_buffer.ptr + m_start, 0, dst.length);
148 		}
149 		popFrontN(dst.length);
150 	}
151 	int opApply(scope int delegate(ref T itm) nothrow del)
152 	{
153 		if( m_start+m_fill > m_buffer.length ){
154 			foreach(i; m_start .. m_buffer.length)
155 				if( auto ret = del(m_buffer[i]) )
156 					return ret;
157 			foreach(i; 0 .. mod(m_start+m_fill))
158 				if( auto ret = del(m_buffer[i]) )
159 					return ret;
160 		} else {
161 			foreach(i; m_start .. m_start+m_fill)
162 				if( auto ret = del(m_buffer[i]) )
163 					return ret;
164 		}
165 		return 0;
166 	}
167 	ref inout(T) opIndex(size_t idx) inout { assert(idx < length); return m_buffer[mod(m_start+idx)]; }
168 	Range opSlice() { return Range(m_buffer, m_start, m_fill); }
169 	Range opSlice(size_t from, size_t to)
170 	{
171 		assert(from <= to);
172 		assert(to <= m_fill);
173 		return Range(m_buffer, mod(m_start+from), to-from);
174 	}
175 	size_t opDollar(size_t dim)() const if(dim == 0) { return length; }
176 	private size_t mod(size_t n)
177 	const {
178 		static if( N == 0 ){
179 			/*static if(PotOnly){
180             return x & (m_buffer.length-1);
181             } else {*/
182 			return n % m_buffer.length;
183 			//}
184 		} else static if( ((N - 1) & N) == 0 ){
185 			return n & (N - 1);
186 		} else return n % N;
187 	}
188 	static struct Range {
189 		private {
190 			T[] m_buffer;
191 			size_t m_start;
192 			size_t m_length;
193 		}
194 		private this(T[] buffer, size_t start, size_t length)
195 		{
196 			m_buffer = buffer;
197 			m_start = start;
198 			m_length = length;
199 		}
200 		@property bool empty() const { return m_length == 0; }
201 		@property inout(T) front() inout { assert(!empty); return m_buffer[m_start]; }
202 		void popFront()
203 		{
204 			assert(!empty);
205 			m_start++;
206 			m_length--;
207 			if( m_start >= m_buffer.length )
208 				m_start = 0;
209 		}
210 	}
211 }
212 /*
213 unittest {
214 	import std.range : isInputRange, isOutputRange;
215 	static assert(isInputRange!(CircularBuffer!int) && isOutputRange!(CircularBuffer!int, int));
216 
217 	// test static buffer
218 	CircularBuffer!(int, 5) buf;
219 	assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . .
220 	assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . .
221 	assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . .
222 	assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 .
223 	assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5
224 	assert(buf.length == 5 && buf.freeSpace == 0);
225 	assert(buf.front == 1);
226 	buf.popFront(); // .|2 3 4 5
227 	assert(buf.front == 2);
228 	buf.popFrontN(2); // . . .|4 5
229 	assert(buf.front == 4);
230 	assert(buf.length == 2 && buf.freeSpace == 3);
231 	buf.put([6, 7, 8]); // 6 7 8|4 5
232 	assert(buf.length == 5 && buf.freeSpace == 0);
233 	int[5] dst;
234 	buf.read(dst); // . . .|. .
235 	assert(dst == [4, 5, 6, 7, 8]);
236 	assert(buf.length == 0 && buf.freeSpace == 5);
237 	buf.put([1, 2]); // . . .|1 2
238 	assert(buf.length == 2 && buf.freeSpace == 3);
239 	buf.read(dst[0 .. 2]); //|. . . . .
240 	assert(dst[0 .. 2] == [1, 2]);
241 }
242 */