1 /*
2  * Kiss - A refined core library for D programming language.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module kiss.container.cirularqueue;
13 
14 import core.memory;
15 import std.experimental.allocator.common;
16 import std.experimental.allocator.gc_allocator;
17 import std.traits;
18 import kiss.container.common;
19 
20 /**
21  * 
22     Queue Struct Template.
23     Params:
24         T         = the element type;
25         autoExten = if the Queue is full, will or not auto expand;
26         addToGC   = if use other Allocator, will or not add to GC scan.
27         Allocator = which type Allocator will used
28 */
29 
30 @trusted struct CirularQueue(T, Allocator = GCAllocator, bool autoExten = false, bool addInGC = true)
31         if (is(T == Unqual!T))
32 {
33     enum TSize = T.sizeof;
34     enum addToGC = addInGC && hasIndirections!T && !is(Unqual!Allocator == GCAllocator);
35     static if (hasIndirections!T)
36         alias InsertT = T;
37     else
38         alias InsertT = const T;
39 
40     /**
41         Params:
42             size        =  the queue init size. 
43     */
44     this(uint size)
45     {
46         assert(size > 3);
47         _size = size;
48         _data = cast(T[]) _alloc.allocate(TSize * size);
49         static if (addToGC)
50             GC.addRange(_data.ptr, len);
51     }
52 
53     static if (!StaticAlloc!Allocator)
54     {
55         this(uint size, Allocator alloc)
56         {
57             this._alloc = alloc;
58             this(size);
59         }
60     }
61     ~this()
62     {
63         if (_data.ptr)
64         {
65             static if (addToGC)
66                 GC.removeRange(_data.ptr);
67             _alloc.deallocate(_data);
68         }
69     }
70 
71     pragma(inline, true) void clear()
72     {
73 
74         _data[] = T.init;
75         _front = _rear = 0;
76     }
77 
78     pragma(inline, true) @property bool empty() const nothrow
79     {
80         return (_rear == _front);
81     }
82 
83     pragma(inline) @property bool full() const
84     {
85         if ((_rear + 1) % _size == _front)
86             return true; //队满
87         else
88             return false;
89     }
90 
91     pragma(inline, true) @property T front()
92     {
93         assert(!empty());
94         return _data[_front];
95     }
96 
97     pragma(inline, true) @property uint length()
98     {
99         return (_rear - _front + _size) % _size;
100     }
101 
102     pragma(inline, true) @property uint maxLength() nothrow
103     {
104         static if (autoExten)
105         {
106             return uint.max;
107         }
108         else
109         {
110             return _size - 1;
111         }
112     }
113 
114     bool enQueue(InsertT x)
115     {
116         if (full())
117         { //队满
118             static if (autoExten)
119                 exten();
120             else
121                 return false;
122         }
123         _data[_rear] = x;
124         _rear = (_rear + 1) % _size; //队尾指针加 1 取模
125         return true;
126     }
127 
128     pragma(inline, true) T deQueue(T v = T.init) nothrow
129     {
130         assert(!empty());
131         auto x = _data[_front];
132         _data[_front] = v;
133         _front = (_front + 1) % _size; //队头指针加 1 取模
134         return x;
135     }
136 
137     static if (autoExten)
138     {
139     protected:
140         void exten()
141         {
142             auto size = _size > 128 ? _size + ((_size / 3) * 2) : _size * 2;
143             auto len = TSize * size;
144             auto data = cast(T[]) _alloc.allocate(TSize * size);
145             static if (addToGC)
146                 GC.addRange(data.ptr, len);
147             uint i = 0;
148             while (!empty)
149             {
150                 data[i] = deQueue();
151                 ++i;
152             }
153             _size = size;
154             _front = 0;
155             _rear = i;
156             static if (addToGC)
157                 GC.removeRange(_data.ptr);
158             _alloc.deallocate(_data);
159             _data = data;
160         }
161 
162     }
163     mixin AllocDefine!Allocator;
164 private:
165     @disable this();
166     @disable this(this);
167     uint _front = 0;
168     uint _rear = 0;
169     T[] _data = null;
170     uint _size;
171 }
172 
173 unittest
174 {
175     import std.stdio;
176 
177     auto myq = CirularQueue!(int)(5);
178     writeln("init is empty = ", myq.empty);
179     foreach (i; 0 .. 13)
180     {
181         writeln("enQueue i =  ", i, "  en value = ", myq.enQueue(i));
182     }
183     writeln("end is empty = ", myq.empty);
184     writeln("end is full = ", myq.full);
185     writeln("size  = ", myq.length);
186     int i = 0;
187     while (!myq.empty)
188     {
189         writeln("\n");
190         writeln("\tstart while! i = ", i);
191         writeln("\tfront is = ", myq.front());
192         writeln("\tdeQueue is = ", myq.deQueue());
193 
194         ++i;
195     }
196     writeln("size  = ", myq.length);
197     int x = 2;
198     myq.enQueue(x);
199     writeln("front is = ", myq.front());
200     writeln("size  = ", myq.length);
201     x = 3;
202     myq.enQueue(x);
203     writeln("size  = ", myq.length);
204     writeln("front is = ", myq.front());
205     writeln("deQueue is = ", myq.deQueue());
206     writeln("size  = ", myq.length);
207     writeln("front is = ", myq.front());
208 }