1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.util;
21
22 import java.io.Serializable;
23 import java.util.AbstractList;
24 import java.util.Arrays;
25 import java.util.NoSuchElementException;
26 import java.util.Queue;
27
28
29
30
31
32
33 public class CircularQueue<E> extends AbstractList<E> implements Queue<E>, Serializable {
34
35 private static final long serialVersionUID = 3993421269224511264L;
36
37
38 private static final int DEFAULT_CAPACITY = 4;
39
40
41 private final int initialCapacity;
42
43
44
45
46 private volatile Object[] items;
47 private int mask;
48 private int first = 0;
49 private int last = 0;
50 private boolean full;
51 private int shrinkThreshold;
52
53
54
55
56 public CircularQueue() {
57 this(DEFAULT_CAPACITY);
58 }
59
60 public CircularQueue(int initialCapacity) {
61 int actualCapacity = normalizeCapacity(initialCapacity);
62 items = new Object[actualCapacity];
63 mask = actualCapacity - 1;
64 this.initialCapacity = actualCapacity;
65 this.shrinkThreshold = 0;
66 }
67
68
69
70
71 private static int normalizeCapacity(int initialCapacity) {
72 int actualCapacity = 1;
73
74 while (actualCapacity < initialCapacity) {
75 actualCapacity <<= 1;
76 if (actualCapacity < 0) {
77 actualCapacity = 1 << 30;
78 break;
79 }
80 }
81 return actualCapacity;
82 }
83
84
85
86
87 public int capacity() {
88 return items.length;
89 }
90
91 @Override
92 public void clear() {
93 if (!isEmpty()) {
94 Arrays.fill(items, null);
95 first = 0;
96 last = 0;
97 full = false;
98 shrinkIfNeeded();
99 }
100 }
101
102 @SuppressWarnings("unchecked")
103 public E poll() {
104 if (isEmpty()) {
105 return null;
106 }
107
108 Object ret = items[first];
109 items[first] = null;
110 decreaseSize();
111
112 if (first == last) {
113 first = last = 0;
114 }
115
116 shrinkIfNeeded();
117 return (E) ret;
118 }
119
120 public boolean offer(E item) {
121 if (item == null) {
122 throw new NullPointerException("item");
123 }
124
125 expandIfNeeded();
126 items[last] = item;
127 increaseSize();
128 return true;
129 }
130
131 @SuppressWarnings("unchecked")
132 public E peek() {
133 if (isEmpty()) {
134 return null;
135 }
136
137 return (E) items[first];
138 }
139
140 @SuppressWarnings("unchecked")
141 @Override
142 public E get(int idx) {
143 checkIndex(idx);
144 return (E) items[getRealIndex(idx)];
145 }
146
147 @Override
148 public boolean isEmpty() {
149 return (first == last) && !full;
150 }
151
152 @Override
153 public int size() {
154 if (full) {
155 return capacity();
156 }
157
158 if (last >= first) {
159 return last - first;
160 }
161
162 return last - first + capacity();
163 }
164
165 @Override
166 public String toString() {
167 return "first=" + first + ", last=" + last + ", size=" + size()
168 + ", mask = " + mask;
169 }
170
171 private void checkIndex(int idx) {
172 if (idx < 0 || idx >= size()) {
173 throw new IndexOutOfBoundsException(String.valueOf(idx));
174 }
175 }
176
177 private int getRealIndex(int idx) {
178 return (first + idx) & mask;
179 }
180
181 private void increaseSize() {
182 last = (last + 1) & mask;
183 full = first == last;
184 }
185
186 private void decreaseSize() {
187 first = (first + 1) & mask;
188 full = false;
189 }
190
191 private void expandIfNeeded() {
192 if (full) {
193
194 final int oldLen = items.length;
195 final int newLen = oldLen << 1;
196 Object[] tmp = new Object[newLen];
197
198 if (first < last) {
199 System.arraycopy(items, first, tmp, 0, last - first);
200 } else {
201 System.arraycopy(items, first, tmp, 0, oldLen - first);
202 System.arraycopy(items, 0, tmp, oldLen - first, last);
203 }
204
205 first = 0;
206 last = oldLen;
207 items = tmp;
208 mask = tmp.length - 1;
209 if (newLen >>> 3 > initialCapacity) {
210 shrinkThreshold = newLen >>> 3;
211 }
212 }
213 }
214
215 private void shrinkIfNeeded() {
216 int size = size();
217 if (size <= shrinkThreshold) {
218
219 final int oldLen = items.length;
220 int newLen = normalizeCapacity(size);
221 if (size == newLen) {
222 newLen <<= 1;
223 }
224
225 if (newLen >= oldLen) {
226 return;
227 }
228
229 if (newLen < initialCapacity) {
230 if (oldLen == initialCapacity) {
231 return;
232 }
233
234 newLen = initialCapacity;
235 }
236
237 Object[] tmp = new Object[newLen];
238
239
240 if (size > 0) {
241 if (first < last) {
242 System.arraycopy(items, first, tmp, 0, last - first);
243 } else {
244 System.arraycopy(items, first, tmp, 0, oldLen - first);
245 System.arraycopy(items, 0, tmp, oldLen - first, last);
246 }
247 }
248
249 first = 0;
250 last = size;
251 items = tmp;
252 mask = tmp.length - 1;
253 shrinkThreshold = 0;
254 }
255 }
256
257 @Override
258 public boolean add(E o) {
259 return offer(o);
260 }
261
262 @SuppressWarnings("unchecked")
263 @Override
264 public E set(int idx, E o) {
265 checkIndex(idx);
266
267 int realIdx = getRealIndex(idx);
268 Object old = items[realIdx];
269 items[realIdx] = o;
270 return (E) old;
271 }
272
273 @Override
274 public void add(int idx, E o) {
275 if (idx == size()) {
276 offer(o);
277 return;
278 }
279
280 checkIndex(idx);
281 expandIfNeeded();
282
283 int realIdx = getRealIndex(idx);
284
285
286 if (first < last) {
287 System
288 .arraycopy(items, realIdx, items, realIdx + 1, last
289 - realIdx);
290 } else {
291 if (realIdx >= first) {
292 System.arraycopy(items, 0, items, 1, last);
293 items[0] = items[items.length - 1];
294 System.arraycopy(items, realIdx, items, realIdx + 1,
295 items.length - realIdx - 1);
296 } else {
297 System.arraycopy(items, realIdx, items, realIdx + 1, last
298 - realIdx);
299 }
300 }
301
302 items[realIdx] = o;
303 increaseSize();
304 }
305
306 @SuppressWarnings("unchecked")
307 @Override
308 public E remove(int idx) {
309 if (idx == 0) {
310 return poll();
311 }
312
313 checkIndex(idx);
314
315 int realIdx = getRealIndex(idx);
316 Object removed = items[realIdx];
317
318
319 if (first < last) {
320 System.arraycopy(items, first, items, first + 1, realIdx - first);
321 } else {
322 if (realIdx >= first) {
323 System.arraycopy(items, first, items, first + 1, realIdx
324 - first);
325 } else {
326 System.arraycopy(items, 0, items, 1, realIdx);
327 items[0] = items[items.length - 1];
328 System.arraycopy(items, first, items, first + 1, items.length
329 - first - 1);
330 }
331 }
332
333 items[first] = null;
334 decreaseSize();
335
336 shrinkIfNeeded();
337 return (E) removed;
338 }
339
340 public E remove() {
341 if (isEmpty()) {
342 throw new NoSuchElementException();
343 }
344 return poll();
345 }
346
347 public E element() {
348 if (isEmpty()) {
349 throw new NoSuchElementException();
350 }
351 return peek();
352 }
353 }