1 /++
2     Containers and thereto related functionality.
3 
4     Example:
5     ---
6     {
7         Buffer!string buffer;
8 
9         buffer.put("abc");
10         buffer.put("def");
11         assert(!buffer.empty);
12         assert(buffer.front == "abc");
13         buffer.popFront();
14         assert(buffer.front == "def");
15         buffer.popFront();
16         assert(buffer.empty);
17     }
18     {
19         Buffer!(char, Yes.dynamic, 3) buffer;
20 
21         assert(!buffer.buf.length);
22         buffer ~= 'a';
23         assert(buffer.buf.length == 3);
24         buffer ~= 'b';
25         buffer ~= 'c';
26         assert(buffer.length == 3);
27         buffer ~= 'd';
28         assert(buffer.buf.length > 3);
29         assert(buffer[0..5] == "abcd");
30         buffer.clear();
31         assert(buffer.empty);
32     }
33     ---
34  +/
35 module lu.container;
36 
37 private:
38 
39 import std.typecons : Flag, No, Yes;
40 
41 public:
42 
43 @safe:
44 
45 
46 // Buffer
47 /++
48     Simple buffer/queue for storing and fetching items of any type `T`.
49     Does not use manual memory allocation.
50 
51     It can use a static array internally to store elements on the stack, which
52     imposes a hard limit on how many items can be added, or a dynamic heap one
53     with a resizable buffer.
54 
55     Example:
56     ---
57     Buffer!(string, No.dynamic, 16) buffer;
58 
59     buffer.put("abc");
60     buffer ~= "def";
61     assert(!buffer.empty);
62     assert(buffer.front == "abc");
63     buffer.popFront();
64     assert(buffer.front == "def");
65     buffer.popFront();
66     assert(buffer.empty);
67     ---
68 
69     Params:
70         T = Buffer item type.
71         dynamic = Whether to use a dynamic array whose size can be grown at
72             runtime, or to use a static array with a fixed size. Trying to add
73             more elements than there is room for will cause an assert.
74             Defaults to `No.dynamic`; a static bufferCurrent position in the array..
75         originalSize = How many items to allocate space for. If `No.dynamic` was
76             passed it will assert if you attempt to store anything past this amount.
77  +/
78 struct Buffer(T, Flag!"dynamic" dynamic = No.dynamic, size_t originalSize = 128)
79 {
80 pure nothrow:
81     static if (dynamic)
82     {
83         /++
84             By how much to grow the buffer when we reach the end of it.
85          +/
86         private enum growthFactor = 1.5;
87 
88         /++
89             Internal buffer dynamic array.
90          +/
91         T[] buf;
92 
93         /++
94             Variable buffer size.
95          +/
96         size_t bufferSize;
97     }
98     else
99     {
100         /++
101             Internal buffer static array.
102          +/
103         T[bufferSize] buf;
104 
105         /++
106             Static buffer size.
107          +/
108         alias bufferSize = originalSize;
109     }
110 
111     /++
112         Current position in the array.
113      +/
114     ptrdiff_t pos;
115 
116     /++
117         Position of last entry in the array.
118      +/
119     ptrdiff_t end;
120 
121     static if (dynamic)
122     {
123         // put
124         /++
125             Append an item to the end of the buffer.
126 
127             If it would be put beyond the end of the buffer, it will be resized to fit.
128 
129             Params:
130                 more = Item to add.
131          +/
132         void put(/*const*/ T more)
133         {
134             if (end == bufferSize)
135             {
136                 bufferSize = !bufferSize ? originalSize : cast(size_t)(bufferSize * growthFactor);
137                 buf.length = bufferSize;
138             }
139 
140             buf[end++] = more;
141         }
142     }
143     else
144     {
145         // put
146         /++
147             Append an item to the end of the buffer.
148 
149             If it would be put beyond the end of the buffer, it will assert.
150 
151             Params:
152                 more = Item to add.
153          +/
154         void put(/*const*/ T more) @nogc
155         in ((end < bufferSize), '`' ~ typeof(this).stringof ~ "` buffer overflow")
156         {
157             buf[end++] = more;
158         }
159     }
160 
161     static if (dynamic)
162     {
163         // reserve
164         /++
165             Reserves enough room for the specified number of elements. If there
166             is already enough room, nothing is done. Otherwise the buffer is grown.
167 
168             Params:
169                 reserveSize = Number of elements to reserve size for.
170          +/
171         void reserve(const size_t reserveSize)
172         {
173             if (bufferSize < reserveSize)
174             {
175                 bufferSize = reserveSize;
176                 buf.length = bufferSize;
177             }
178         }
179     }
180 
181     // opOpAssign
182     /++
183         Implements `buf ~= someT` (appending) by wrapping `put`.
184 
185         Params:
186             op = Operation type, here specialised to "`~`".
187             more = Item to add.
188      +/
189     void opOpAssign(string op : "~")(const T more)
190     {
191         return put(more);
192     }
193 
194     // front
195     /++
196         Fetches the item at the current position of the buffer.
197 
198         Returns:
199             An item T.
200      +/
201     T front() const @nogc
202     in ((end > 0), '`' ~ typeof(this).stringof ~ "` buffer underrun")
203     {
204         return buf[pos];
205     }
206 
207     // popFront
208     /++
209         Advances the current position to the next item in the buffer.
210      +/
211     void popFront() @nogc
212     {
213         if (++pos == end) reset();
214     }
215 
216     // length
217     /++
218         Returns what amounts to the current length of the buffer; the distance
219         between the current position `pos` and the last element `end`.
220 
221         Returns:
222             The buffer's current length.
223      +/
224     size_t length() const @nogc
225     {
226         return (end - pos);
227     }
228 
229     // empty
230     /++
231         Returns whether or not the container is considered empty.
232 
233         Mind that the buffer may well still contain old contents. Use `clear`
234         to zero it out.
235 
236         Returns:
237             `true` if there are items available to get via `front`,
238             `false` if not.
239      +/
240     bool empty() const @nogc
241     {
242         return (end == 0);
243     }
244 
245     // reset
246     /++
247         Resets the array positions, effectively soft-emptying the buffer.
248 
249         The old elements' values are still there, they will just be overwritten
250         as the buffer is appended to.
251      +/
252     void reset() @nogc
253     {
254         pos = 0;
255         end = 0;
256     }
257 
258     // clear
259     /++
260         Zeroes out the buffer's elements, getting rid of old contents.
261      +/
262     void clear() @nogc
263     {
264         reset();
265         buf[] = T.init;
266     }
267 }
268 
269 ///
270 unittest
271 {
272     {
273         Buffer!(bool, No.dynamic, 4) buffer;
274 
275         assert(buffer.empty);
276         buffer.put(true);
277         buffer.put(false);
278         assert(buffer.length == 2);
279         buffer.put(true);
280         buffer.put(false);
281 
282         assert(!buffer.empty);
283         assert(buffer.front == true);
284         buffer.popFront();
285         assert(buffer.front == false);
286         buffer.popFront();
287         assert(buffer.front == true);
288         buffer.popFront();
289         assert(buffer.front == false);
290         buffer.popFront();
291         assert(buffer.empty);
292         assert(buffer.buf == [ true, false, true, false ]);
293         buffer.put(false);
294         assert(buffer.buf == [ false, false, true, false ]);
295         buffer.reset();
296         assert(buffer.empty);
297         buffer.clear();
298         assert(buffer.buf == [ false, false, false, false ]);
299     }
300     {
301         Buffer!(string, No.dynamic, 4) buffer;
302 
303         assert(buffer.empty);
304         buffer.put("abc");
305         buffer.put("def");
306         buffer.put("ghi");
307 
308         assert(!buffer.empty);
309         assert(buffer.front == "abc");
310         buffer.popFront();
311         assert(buffer.front == "def");
312         buffer.popFront();
313         buffer.put("JKL");
314         assert(buffer.front == "ghi");
315         buffer.popFront();
316         assert(buffer.front == "JKL");
317         buffer.popFront();
318         assert(buffer.empty);
319         assert(buffer.buf == [ "abc", "def", "ghi", "JKL" ]);
320         buffer.put("MNO");
321         assert(buffer.buf == [ "MNO", "def", "ghi", "JKL" ]);
322         buffer.clear();
323         assert(buffer.buf == [ string.init, string.init, string.init, string.init ]);
324     }
325     {
326         Buffer!(char, No.dynamic, 64) buffer;
327         buffer ~= 'a';
328         buffer ~= 'b';
329         buffer ~= 'c';
330         assert(buffer.buf[0..3] == "abc");
331 
332         foreach (char_; buffer)
333         {
334             assert((char_ == 'a') || (char_ == 'b') || (char_ == 'c'));
335         }
336     }
337     {
338         Buffer!(int, Yes.dynamic, 3) buffer;
339         assert(!buffer.buf.length);
340         buffer ~= 1;
341         assert(buffer.buf.length == 3);
342         buffer ~= 2;
343         buffer ~= 3;
344         assert(buffer.front == 1);
345         buffer.popFront();
346         assert(buffer.front == 2);
347         buffer.popFront();
348         assert(buffer.front == 3);
349         buffer.popFront();
350         assert(buffer.empty);
351         buffer.reserve(64);
352         assert(buffer.buf.length == 64);
353         buffer.reserve(63);
354         assert(buffer.buf.length == 64);
355     }
356     {
357         Buffer!(char, No.dynamic, 4) buffer;
358         buffer ~= 'a';
359         buffer ~= 'b';
360         buffer ~= 'c';
361         buffer ~= 'd';
362         assert(buffer.buf == "abcd");
363         assert(buffer.length == 4);
364         buffer.reset();
365         assert(buffer.buf == "abcd");
366         buffer.clear();
367         assert(buffer.buf == typeof(buffer.buf).init);
368     }
369 }
370 
371 
372 // CircularBuffer
373 /++
374     Simple circular-ish buffer for storing items of type `T` that discards elements
375     when the maximum size is reached. Does not use manual memory allocation.
376 
377     It can use a static array internally to store elements on the stack, which
378     imposes a hard limit on how many items can be added, or a dynamic heap one
379     with a resizable buffer.
380 
381     Example:
382     ---
383     CircularBuffer!(int, Yes.dynamic) buf;
384     buf.resize(3);
385     buf.put(1);
386     buf.put(2);
387     buf.put(3);
388     but.put(4);
389     assert(buf.front == 4);
390     assert(buf.buf == [ 4, 2, 3 ]);
391     ---
392 
393     Params:
394         T = Buffer item type.
395         dynamic = Whether to use a dynamic array whose size can be grown at
396             runtime, or to use a static array with a fixed size. Trying to add
397             more elements than there is room for will wrap around and discard elements.
398             Defaults to `No.dynamic`; a static buffer.
399         originalSize = How many items to allocate space for in the case of a
400             static array.
401  +/
402 struct CircularBuffer(T, Flag!"dynamic" dynamic = No.dynamic, size_t originalSize = 16)
403 if (originalSize > 1)
404 {
405 private:
406     static if (dynamic)
407     {
408         // buf
409         /++
410             Internal buffer dynamic array.
411          +/
412         T[] buf;
413     }
414     else
415     {
416         // buf
417         /++
418             Internal buffer static array.
419          +/
420         T[originalSize] buf;
421     }
422 
423     // head
424     /++
425         Head position in the internal buffer.
426      +/
427     size_t head;
428 
429     // tail
430     /++
431         Tail position in the internal buffer.
432      +/
433     size_t tail;
434 
435     // caughtUp
436     /++
437         Whether or not [head] and [tail] point to the same position in the
438         context of a circular array.
439      +/
440     bool caughtUp;
441 
442     // initialised
443     /++
444         Whether or not at least one element has been added.
445      +/
446     bool initialised;
447 
448 public:
449     // front
450     /++
451         Returns the front element.
452 
453         Returns:
454             An item T.
455      +/
456     auto front() const
457     in ((buf.length > 0), "Tried to get `front` from a zero-sized " ~ typeof(this).stringof)
458     {
459         return buf[head];
460     }
461 
462     // put
463     /++
464         Append an item to the buffer.
465 
466         If it would be put beyond the end of the buffer, it will wrap around and
467         truncate old values.
468 
469         Params:
470             item = Item to add.
471      +/
472     void put(T item) pure @safe @nogc nothrow
473     in ((buf.length > 0), "Tried to `put` something into a zero-sized " ~ typeof(this).stringof)
474     {
475         if (initialised) ++head %= buf.length;
476         buf[head] = item;
477         tail = head;
478         caughtUp = true;
479         initialised = true;
480     }
481 
482     // popFront
483     /++
484         Advances the current position to the next item in the buffer.
485      +/
486     void popFront() pure @safe @nogc nothrow
487     in ((buf.length > 0), "Tried to `popFront` a zero-sized " ~ typeof(this).stringof)
488     in (!empty, "Tried to `popFront` an empty " ~ typeof(this).stringof)
489     {
490         if (head == 0)
491         {
492             head = (buf.length + (-1));
493             caughtUp = false;
494         }
495         else
496         {
497             --head;
498         }
499     }
500 
501     static if (dynamic)
502     {
503         // resize
504         /++
505             Resizes the internal buffer to a specified size.
506 
507             Params:
508                 size = New size.
509          +/
510         void resize(const size_t size) pure @safe nothrow
511         {
512             buf.length = size;
513             if (head >= buf.length) head = buf.length +(-1);
514             if (tail >= buf.length) tail = buf.length +(-1);
515         }
516     }
517 
518     // opOpAssign
519     /++
520         Implements `buf ~= someT` (appending) by wrapping `put`.
521 
522         Params:
523             op = Operation type, here specialised to "`~`".
524             more = Item to add.
525      +/
526     void opOpAssign(string op : "~")(const T more) pure @safe @nogc nothrow
527     {
528         return put(more);
529     }
530 
531     // size
532     /++
533         Returns the size of the internal buffer.
534 
535         Returns:
536             Internal buffer size.
537      +/
538     auto size() const
539     {
540         return buf.length;
541     }
542 
543     // empty
544     /++
545         Returns whether or not the container is considered empty.
546 
547         Mind that the buffer may well still contain old contents. Use `clear`
548         to zero it out.
549 
550         Returns:
551             `true` if there are items available to get via `front`,
552             `false` if not.
553      +/
554     auto empty() const
555     {
556         return !caughtUp && (head == tail);
557     }
558 
559     // save
560     /++
561         Implements Range `save`.
562 
563         Returns:
564             A shallow copy of the container.
565      +/
566     auto save()
567     {
568         return this;
569     }
570 
571     // dup
572     /++
573         Makes a deep(er) duplicate of the container.
574 
575         Returns:
576             A copy of the current container with the internal buffer explicitly `.dup`ed.
577      +/
578     auto dup()
579     {
580         auto copy = this;
581 
582         static if (dynamic)
583         {
584             copy.buf = this.buf.dup;
585         }
586 
587         return copy;
588     }
589 
590     // clear
591     /++
592         Resets the buffer pointers but doesn't clear the contents.
593      +/
594     void reset() pure @safe @nogc nothrow
595     {
596         head = 0;
597         tail = 0;
598     }
599 
600     // clear
601     /++
602         Zeroes out the buffer's elements, getting rid of old contents.
603      +/
604     void clear() pure @safe @nogc nothrow
605     {
606         reset();
607         buf[] = T.init;
608     }
609 }
610 
611 ///
612 unittest
613 {
614     import std.conv : text;
615 
616     {
617         CircularBuffer!(int, Yes.dynamic) buf;
618         buf.resize(3);
619 
620         buf.put(1);
621         assert((buf.front == 1), buf.front.text);
622         buf.put(2);
623         assert((buf.front == 2), buf.front.text);
624         buf.put(3);
625         assert((buf.front == 3), buf.front.text);
626         buf ~= 4;
627         assert((buf.front == 4), buf.front.text);
628         assert((buf.buf[] == [ 4, 2, 3 ]), buf.buf.text);
629         buf ~= 5;
630         assert((buf.front == 5), buf.front.text);
631         buf ~= 6;
632         assert((buf.front == 6), buf.front.text);
633         assert((buf.buf[] == [ 4, 5, 6 ]), buf.buf.text);
634         buf.popFront();
635         buf.popFront();
636         buf.popFront();
637         assert(buf.empty);
638     }
639     {
640         CircularBuffer!(int, No.dynamic, 3) buf;
641         //buf.resize(3);
642 
643         buf.put(1);
644         assert((buf.front == 1), buf.front.text);
645         buf.put(2);
646         assert((buf.front == 2), buf.front.text);
647         buf.put(3);
648         assert((buf.front == 3), buf.front.text);
649         buf ~= 4;
650         assert((buf.front == 4), buf.front.text);
651         assert((buf.buf[] == [ 4, 2, 3 ]), buf.buf.text);
652         buf.popFront();
653         buf.popFront();
654         buf.popFront();
655         assert(buf.empty);
656     }
657     {
658         CircularBuffer!(int, No.dynamic, 2) buf;
659         //buf.resize(2);
660 
661         buf.put(1);
662         assert((buf.front == 1), buf.front.text);
663         buf.put(2);
664         assert((buf.front == 2), buf.front.text);
665         buf.put(3);
666         assert((buf.front == 3), buf.front.text);
667         buf ~= 4;
668         assert((buf.front == 4), buf.front.text);
669         assert((buf.buf[] == [ 3, 4 ]), buf.buf.text);
670         buf.popFront();
671         buf.popFront();
672         assert(buf.empty);
673         //buf.popFront();  // AssertError
674     }
675     {
676         CircularBuffer!(int, No.dynamic, 2) buf;
677         //buf.resize(2);
678 
679         buf.put(1);
680         assert((buf.front == 1), buf.front.text);
681         buf.put(2);
682         assert((buf.front == 2), buf.front.text);
683         buf.put(3);
684         assert((buf.front == 3), buf.front.text);
685         buf ~= 4;
686         assert((buf.front == 4), buf.front.text);
687         assert((buf.buf[] == [ 3, 4 ]), buf.buf.text);
688         auto savedBuf = buf.save();
689         buf.popFront();
690         buf.popFront();
691         assert(buf.empty);
692         assert((savedBuf.front == 4), savedBuf.front.text);
693         savedBuf.popFront();
694         auto savedBuf2 = savedBuf.save();
695         savedBuf.popFront();
696         assert(savedBuf.empty);
697         assert((savedBuf2.front == 3), savedBuf2.front.text);
698         savedBuf2.popFront();
699         assert(savedBuf2.empty);
700     }
701 }