1 /**
2   A FIFO queue implementation.
3 */
4 module dscord.util.queue;
5 
6 import vibe.core.sync;
7 
8 import std.container.dlist,
9        std.array,
10        core.time;
11 
12 /**
13   A simple Queue of type T.
14 */
15 class Queue(T) {
16   private DList!(T) data;
17 
18   /// Returns the size of the array
19   private size_t _size;
20 
21   void setSize(size_t size) {
22     this._size = size;
23   }
24 
25   @property size_t size() {
26     return this._size;
27   }
28 
29   /// Returns true if the queue is empty
30   @property bool empty() {
31     return this.data.empty;
32   }
33 
34   /// Raw array access
35   @property T[] array() {
36     return this.data.array;
37   }
38 
39   /// Clear the entire queue
40   void clear() {
41     this.data.removeFront(this.size);
42     this.setSize(0);
43   }
44 
45   /// Push an item to the back of the queue. Returns false if the queue is full.
46   bool push(T item) {
47     this.data.insertBack(item);
48     this.setSize(this.size + 1);
49     return true;
50   }
51 
52   /// Push multiple items to the back of the queue. Returns false if the queue is
53   //   full.
54   bool push(T[] arr) {
55     this.data.insertBack(arr);
56     this.setSize(this.size + 1);
57     return true;
58   }
59 
60   /// Pops a single item off the front of the queue. Throws an exception if the
61   //   queue is empty.
62   T pop() {
63     if (this.data.empty) {
64       throw new Exception("Cannot pop from empty Queue");
65     }
66 
67     T v = this.data.front;
68     this.data.removeFront();
69     this.setSize(this.size - 1);
70     return v;
71   }
72 
73   /// Peaks at the last item in the queue.
74   T peakBack() {
75     if (this.data.empty) {
76       throw new Exception("Cannot peak into empty Queue");
77     }
78 
79     return this.data.back;
80   }
81 
82   /// Peaks at the first item in the queue.
83   T peakFront() {
84     if (this.data.empty) {
85       throw new Exception("Cannot peak into empty Queue");
86     }
87 
88     return this.data.front;
89   }
90 }
91 
92 /**
93   A blocking queue of type T.
94 */
95 class BlockingQueue(T) : Queue!T {
96   private ManualEvent onInsert;
97   private ManualEvent onModify;
98 
99   this() {
100     this.onInsert = createManualEvent();
101     this.onModify = createManualEvent();
102   }
103 
104   override void setSize(size_t size) {
105     if (this._size < size) {
106       this.onInsert.emit();
107     }
108 
109     this.onModify.emit();
110     this._size = size;
111   }
112 
113   bool wait(Duration timeout) {
114     if (this.onModify.wait(timeout, 0) != 0) {
115       return false;
116     }
117     return true;
118   }
119 
120   void wait() {
121     this.onModify.wait();
122   }
123 
124   T pop(Duration timeout) {
125     if (this.onInsert.wait(timeout, 0) != 0) {
126       return null;
127     }
128 
129     return this.pop(false);
130   }
131 
132   /// Pops a single item off the front of the queue. Throws an exception if the
133   //   queue is empty.
134   T pop(bool block=true) {
135     if (this.data.empty) {
136       if (block) {
137         this.onInsert.wait();
138       } else {
139         return null;
140       }
141     }
142 
143     return this.pop();
144   }
145 
146   override T pop() {
147     T v = this.data.front;
148     this.data.removeFront();
149     this.setSize(this.size - 1);
150     return v;
151   }
152 }
153 
154 /**
155   A SizedQueue of type T.
156 */
157 class SizedQueue(T) : Queue!T {
158   /// Maximum size of the queue
159   size_t     maxSize;
160 
161   this(size_t maxSize) {
162     this.maxSize = maxSize;
163   }
164 
165   /// True if the queue is full
166   @property bool full() {
167     return this.size == this.maxSize;
168   }
169 
170   override bool push(T item) {
171     if (this.size == this.maxSize) return false;
172     return super.push(item);
173   }
174 
175   override bool push(T[] arr) {
176     if (arr.length + this.size > this.maxSize) return false;
177     return super.push(arr);
178   }
179 }