1 module modbus.cbuffer;
2 
3 import std.exception;
4 import std.algorithm : equal;
5 
6 struct CBuffer
7 {
8     private ubyte[] buf;
9     private size_t s, e;
10 
11     private this(inout(ubyte)[] buf, size_t s, size_t e) pure inout
12     {
13         this.buf = buf;
14         this.s = s;
15         this.e = e;
16     }
17 
18     this(size_t n) { buf = new ubyte[](n+1); }
19 
20     invariant
21     {
22         assert(s >= 0);
23         assert(e >= 0);
24         assert(s < buf.length);
25         assert(e < buf.length);
26     }
27 
28     @nogc pure
29     {
30         const @property
31         {
32             bool empty() { return s == e; }
33             bool full() { return e == (capacity + s - 1) % capacity; }
34 
35             size_t length()
36             {
37                 if (s <= e) return e - s;
38                 else return capacity - (s - e);
39             }
40 
41             size_t capacity() { return buf.length; }
42         }
43 
44         void put(ubyte val)
45         {
46             assert(!full, "no space");
47             buf[e] = val;
48             e = (e + 1) % capacity;
49         }
50 
51         void put(const(void)[] data)
52         {
53             assert(data.length < capacity - length, "no space");
54             foreach (i, v; cast(ubyte[])data) put(v);
55         }
56 
57         void clear() { s = e; }
58 
59         @property inout 
60         {
61             ref inout(ubyte) front()
62             {
63                 assert(!empty, "empty");
64                 return buf[s];
65             }
66 
67             ref inout(ubyte) back()
68             {
69                 assert(!empty, "empty");
70                 return buf[e-1];
71             }
72         }
73 
74         void popFront() { popFrontN(1); }
75 
76         size_t popFrontN(size_t n)
77         {
78             assert(!empty, "empty");
79             long ns = s + n;
80             long el = e < s ? e + capacity : e;
81             auto df = el - ns;
82             if (df < 0) n += df;
83             s = (s + n) % capacity;
84             return n;
85         }
86 
87         void popBack()
88         {
89             assert(!empty, "empty");
90             e = (capacity + (cast(ptrdiff_t)e) - 1) % capacity;
91         }
92 
93         ref inout(ubyte) opIndex(size_t n) inout
94         {
95             auto idx = (s+n) % capacity;
96             return buf[idx];
97         }
98 
99         void[] fill(void[] buffer)
100         {
101             assert(buffer.length >= length, "no space");
102 
103             if (empty) return buffer[0..0];
104 
105             if (s < e)
106             {
107                 buffer[0..length] = buf[s..e];
108                 return buffer[0..length];
109             }
110             else
111             {
112                 buffer[0..capacity-s] = buf[s..$];
113                 buffer[capacity-s..length] = buf[0..e];
114                 return buffer[0..length];
115             }
116         }
117     }
118 
119     inout(CBuffer) opSlice(size_t a, size_t b) inout
120     {
121         enforce(a <= b, new Exception("range: a must be <= b"));
122         enforce(b-a <= length, new Exception("range: b-a must be <= length"));
123 
124         auto idx_s = (s + a) % capacity;
125         auto idx_e = (s + b) % capacity;
126 
127         return inout CBuffer(buf, idx_s, idx_e);
128     }
129 
130     CBuffer dup() const @property
131     { return CBuffer(buf.dup, s, e); }
132 
133     ubyte[] getData(size_t a, size_t b) inout
134     {
135         enforce(a <= b, new Exception("range: a must be <= b"));
136         enforce(b-a <= length, new Exception("range: b-a must be <= length"));
137 
138         if (a == b) return [];
139 
140         auto idx_s = (s + a) % capacity;
141         auto idx_e = (s + b) % capacity;
142 
143         if (idx_s < idx_e) return buf[idx_s..idx_e].dup;
144         else return buf[idx_s..$].dup ~ buf[0..idx_e];
145     }
146 
147     ubyte[] getData() inout { return getData(0, length); }
148 }
149 
150 class CBufferCls
151 {
152     CBuffer s;
153     alias s this;
154     this(size_t n) { s = CBuffer(n); }
155 }
156 
157 unittest
158 {
159     auto buf = CBuffer(10);
160     assert(buf.empty);
161     assert(!buf.full);
162     assert(buf.length == 0);
163     buf.put(12);
164     assert(!buf.empty);
165     assert(!buf.full);
166     assert(buf.length == 1);
167     assert(buf.front == 12);
168     assert(buf.back == 12);
169     buf.put(cast(ubyte[])[42, 15]);
170     assert(buf.length == 3);
171     assert(buf.front == 12);
172     assert(buf.back == 15);
173     buf.popFront();
174     assert(buf.length == 2);
175     assert(buf.front == 42);
176     assert(buf.back == 15);
177     buf.popBack();
178     assert(buf.length == 1);
179     assert(buf.front == 42);
180     assert(buf.back == 42);
181     assert(buf.s == 1);
182     assert(buf.e == 2);
183     buf.put(cast(ubyte[])[1,2,3,4,5]);
184     assert(!buf.empty);
185     assert(!buf.full);
186     assert(buf.length == 6);
187     assert(buf.s == 1);
188     assert(buf.e == 7);
189     buf.popFront();
190     buf.popFront();
191     buf.popFront();
192     assert(!buf.empty);
193     assert(!buf.full);
194     assert(buf.length == 3);
195     buf.put(cast(ubyte[])[11,12,13,14]);
196     assert(!buf.empty);
197     assert(!buf.full);
198     assert(buf.length == 7);
199     buf.put(cast(ubyte[])[21,22]);
200     assert(!buf.empty);
201     assert(!buf.full);
202     assert(buf.length == 9);
203     buf.put(cast(ubyte[])[31]);
204     assert(buf.back == 31);
205     assert(!buf.empty);
206     assert(buf.full);
207     assert(buf.length == 10);
208     assertThrown!Throwable(buf.put(42));
209     buf.popBack;
210     assert(buf.back == 22);
211     assert(!buf.empty);
212     assert(!buf.full);
213     assert(buf.length == 9);
214     foreach (i; 0 .. 5) buf.popFront;
215     assert(buf.back == 22);
216     assert(buf.front == 13);
217     assert(!buf.empty);
218     assert(!buf.full);
219     assert(buf.length == 4);
220     buf.popFront;
221     buf.popFront;
222     buf.popBack;
223     buf.popBack;
224     assert(buf.empty);
225     assert(!buf.full);
226     assert(buf.length == 0);
227     buf.put(cast(ubyte[])[1,2,3,4,5,6,7,8,9,10]);
228     assert(!buf.empty);
229     assert(buf.full);
230     assert(buf.length == 10);
231     foreach (i; 0 .. 5) buf.popFront;
232     foreach (i; 0 .. 5) buf.popBack;
233     assert(buf.empty);
234     assert(!buf.full);
235     assert(buf.length == 0);
236     buf.put(200);
237     assert(buf.front == 200);
238     assert(buf.back == 200);
239     assert(!buf.empty);
240     assert(!buf.full);
241     assert(buf.length == 1);
242     assert(buf.s == 5);
243     assert(buf.e == 6);
244 }
245 
246 unittest
247 {
248     auto buf = CBuffer(10);
249     buf.put(cast(ubyte[])[1,2,3,4,5,6,8,9]);
250     foreach (i; 0..7) buf.popFront;
251     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
252     assert(buf.getData() == [9, 1, 2, 3, 4, 5, 6]);
253     auto buf2 = buf.dup;
254     foreach (i; 0..5) buf.popFront;
255     assert(buf.length == 2);
256     assert(buf.front == 5);
257     assert(buf.back == 6);
258     assert(buf.getData() == [5, 6]);
259     foreach (i; 0..5) buf2.popBack;
260     assert(buf2.length == 2);
261     assert(buf2.front == 9);
262     assert(buf2.back == 1);
263     assert(buf2.getData() == [9, 1]);
264 }
265 
266 unittest
267 {
268     auto buf = CBuffer(10);
269     buf.put(cast(ubyte[])[1,2,3,4,5,6,8,9]);
270     foreach (i; 0..7) buf.popFront;
271     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
272     assert(buf.getData() == [9, 1, 2, 3, 4, 5, 6]);
273     auto buf2 = buf[2..6];
274     assert(buf2.getData() == [2, 3, 4, 5]);
275     assert(buf.buf == buf2.buf);
276     buf2.clear();
277     assert(buf2.empty);
278     assert(buf2.length == 0);
279     assert(buf2.getData() == []);
280     assert(buf.getData() == [9, 1, 2, 3, 4, 5, 6]);
281 }
282 
283 unittest
284 {
285     auto buf = CBuffer(10);
286     buf.put(cast(ubyte[])[1,2,3,4,5,6,8,9]);
287     foreach (i; 0..7) buf.popFront;
288     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
289     ubyte[] res;
290     foreach (v; buf) res ~= v;
291     assert(res == [9, 1, 2, 3, 4, 5, 6]);
292 }
293 
294 unittest
295 {
296     auto buf = new CBufferCls(10);
297     buf.put(cast(ubyte[])[1,2,3,4,5,6,8,9]);
298     foreach (i; 0..7) buf.popFront;
299     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
300     ubyte[] res;
301     buf[0] = 12;
302     buf[5] = 55;
303     assert(buf[0] == 12);
304     foreach (v; buf) res ~= v;
305     assert(res == [12, 1, 2, 3, 4, 55, 6]);
306 }
307 
308 unittest
309 {
310     auto buf = new CBufferCls(10);
311     buf.put(cast(ubyte[])[1,2,3,4,5,6,8,9]);
312     buf.popFrontN(7);
313     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
314     ubyte[] res;
315     buf[0] = 12;
316     buf[5] = 55;
317     assert(buf[0] == 12);
318     foreach (v; buf) res ~= v;
319     assert(res == [12, 1, 2, 3, 4, 55, 6]);
320 }
321 
322 unittest
323 {
324     auto buf = new CBufferCls(10);
325     buf.put(cast(ubyte[])[1,2,3,4,5,6,7,8]);
326     assert(buf.popFrontN(2) == 2);
327     assert(equal(buf.dup, [3,4,5,6,7,8]));
328     assert(buf.popFrontN(2) == 2);
329     assert(equal(buf.dup, [5,6,7,8]));
330     assert(buf.popFrontN(2) == 2);
331     assert(equal(buf.dup, [7,8]));
332     assert(buf.popFrontN(2) == 2);
333     assert(equal(buf.dup, cast(ubyte[])[]));
334     assert(buf.empty);
335 }
336 
337 unittest
338 {
339     auto buf = new CBufferCls(10);
340     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
341     assert(buf.popFrontN(10) == 6);
342     buf.put(cast(ubyte[])[1,2,3,4,5,6,7,8]);
343     assert(buf.popFrontN(6) == 6);
344     assert(equal(buf.dup, [7,8]));
345 }
346 
347 unittest
348 {
349     auto buf = new CBufferCls(10);
350     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
351     assert(buf.popFrontN(10) == 6);
352     buf.put(cast(ubyte[])[1,2,3,4,5,6,7,8]);
353     assert(buf.popFrontN(10) == 8);
354     assert(buf.empty);
355 }
356 
357 unittest
358 {
359     auto buf = CBuffer(6);
360     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
361     assert(buf.popFrontN(10) == 6);
362     buf.put(cast(ubyte[])[1,2,3,4]);
363     assert(buf.popFrontN(10) == 4);
364     assert(buf.empty);
365 }
366 
367 unittest
368 {
369     auto buf = new CBufferCls(10);
370     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
371     assert(buf.popFrontN(10) == 6);
372     buf.put(cast(ubyte[])[1,2,3,4,5,6,7,8]);
373     ubyte[12] data;
374     auto res = buf.fill(data[]);
375     assert(res.ptr == data[].ptr);
376     assert(res.length == buf.length);
377     assert(equal(cast(ubyte[])res, [1,2,3,4,5,6,7,8]));
378 }
379 
380 unittest
381 {
382     auto buf = new CBufferCls(10);
383     buf.put(cast(ubyte[])[1,2,3,4,5,6]);
384     assert(buf.popFrontN(2) == 2);
385     ubyte[12] data;
386     auto res = buf.fill(data[]);
387     assert(res.length == buf.length);
388     assert(equal(cast(ubyte[])res, [3,4,5,6]));
389 }