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