1 module ikod.containers.ring;
2 
3 private import std.typecons;
4 private import core.bitop;
5 
6 private import std.experimental.allocator;
7 private import std.experimental.allocator.mallocator : Mallocator;
8 private import std.experimental.allocator.gc_allocator;
9 
10 version(TestingContainers) {
11     import ut;
12 }
13 
14 struct Ring(T, alias N, Allocator = Mallocator, bool GCRangesAllowed = true)
15 {
16     static if (popcnt(N) == 1)
17     {
18         enum _useMask = true;
19         enum _mask = N - 1;
20     }
21     else
22     {
23         enum _useMask = false;
24     }
25     public
26     {
27         enum OverflowPolicy
28         {
29             DROP,
30             OVERWRITE,
31             ERROR
32         }
33         enum PutStatus
34         {
35             OK,
36             DROPPPED,
37             OVERWRITTEN,
38             ERROR
39         }
40         enum GetStatus
41         {
42             OK,
43             EMPTY
44         }
45     }
46     private
47     {
48         alias allocator = Allocator.instance;
49         T[]                 _buffer;
50         immutable size_t    _size = N;
51         size_t              _head, _tail, _length; // write to head, read from tail
52         OverflowPolicy      _overflow_policy = OverflowPolicy.ERROR;
53     }
54     void init(OverflowPolicy p = OverflowPolicy.ERROR)
55     {
56         _buffer = makeArray!(T)(allocator, _size);
57         _overflow_policy = p;
58     }
59     ~this() @trusted
60     {
61         if (_buffer)
62         {
63             dispose(allocator, &_buffer[0]);
64         }
65     }
66     auto put(T)(T item)
67     {
68         if (_length == _size)
69         {
70             final switch(_overflow_policy)
71             {
72                 case OverflowPolicy.ERROR:
73                     return PutStatus.ERROR;
74                 case OverflowPolicy.DROP:
75                     return PutStatus.DROPPPED;
76                 case OverflowPolicy.OVERWRITE:
77                     _buffer[_head] = item;
78                     static if(_useMask)
79                     {
80                         _head = (_head + 1) & _mask;
81                         _tail = (_tail + 1) & _mask;
82                     }
83                     else
84                     {
85                         _head = (_head+1) % N;
86                         _tail = (_tail+1) % N;
87                     }
88                     return PutStatus.OVERWRITTEN;
89             }
90         }
91         _buffer[_head] = item;
92         _length++;
93         static if (_useMask)
94         {
95             _head = (_head + 1) & _mask;
96         }
97         else
98         {
99             _head = (_head+1) % N;
100         }
101         return PutStatus.OK;
102     }
103     auto get()
104     {
105         if (_length == 0)
106         {
107             return tuple!("status", "value")(GetStatus.EMPTY, T.init);
108         }
109         auto r = tuple!("status", "value")(GetStatus.OK, _buffer[_tail]);
110         static if(_useMask)
111         {
112             _tail = (_tail + 1) & _mask;
113         }
114         else
115         {
116             _tail = (_tail+1) % N;
117         }
118         _length--;
119         return r;
120     }
121     auto length()
122     {
123         return _length;
124     }
125     auto empty()
126     {
127         return _length == 0;
128     }
129     auto full()
130     {
131         return _length == N;
132     }
133 }
134 
135 @("basic")
136 @safe unittest
137 {
138     import std.algorithm;
139     import std.range;
140 
141     Ring!(int, 10) ring;
142     ring.init();
143     assert(ring.empty);
144     auto p = ring.put(1);
145     assert(p == ring.PutStatus.OK);
146     assert(!ring.empty);
147     assert(!ring.full);
148     auto g = ring.get();
149     assert(g.status == ring.GetStatus.OK);
150     assert(g.value == 1);
151     assert(ring.empty);
152     foreach(i;0..10)
153     {
154         p = ring.put(i);
155         assert(p == ring.PutStatus.OK);
156     }
157     assert(ring.full);
158     assert(ring.length == 10);
159     p = ring.put(10);
160     assert(p == ring.PutStatus.ERROR);
161     int[] a;
162     while(!ring.empty)
163     {
164         a ~= ring.get().value;
165     }
166     assert(equal(a, iota(10)));
167     assert(ring.empty);
168 }
169 @("class")
170 @safe
171 unittest
172 {
173     class C
174     {
175         int _i;
176         this(int i)
177         {
178             _i = i;
179         }
180     }
181     Ring!(C, 32) ring;
182     ring.init();
183     ring.put(new C(1));
184 }
185 @("N=32")
186 unittest
187 {
188     import std.algorithm;
189     import std.range;
190     enum N = 32;
191     Ring!(int, N) ring;
192     ring.init();
193     assert(ring.empty);
194     auto p = ring.put(1);
195     assert(p == ring.PutStatus.OK);
196     assert(!ring.empty);
197     assert(!ring.full);
198     auto g = ring.get();
199     assert(g.status == ring.GetStatus.OK);
200     assert(g.value == 1);
201     assert(ring.empty);
202     foreach(i;0..N)
203     {
204         p = ring.put(i);
205         assert(p == ring.PutStatus.OK);
206     }
207     assert(ring.full);
208     assert(ring.length == N);
209     p = ring.put(N);
210     assert(p == ring.PutStatus.ERROR);
211     int[] a;
212     while(!ring.empty)
213     {
214         a ~= ring.get().value;
215     }
216     assert(equal(a, iota(N)));
217     assert(ring.empty);
218 }