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