View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
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   * A unbounded circular queue based on array.
31   * 
32   * @author The Apache Directory Project (mina-dev@directory.apache.org)
33   * @version $Rev: 593416 $, $Date: 2007-11-08 20:42:48 -0700 (Thu, 08 Nov 2007) $
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      private static final int DEFAULT_CAPACITY = 4;
40  
41      private final int initialCapacity;
42      private Object[] items;
43      private int mask;
44      private int first = 0;
45      private int last = 0;
46      private boolean full;
47      private int shrinkThreshold;
48  
49      /**
50       * Construct a new, empty queue.
51       */
52      public CircularQueue() {
53          this(DEFAULT_CAPACITY);
54      }
55      
56      public CircularQueue(int initialCapacity) {
57          int actualCapacity = normalizeCapacity(initialCapacity);
58          items = new Object[actualCapacity];
59          mask = actualCapacity - 1;
60          this.initialCapacity = actualCapacity;
61          this.shrinkThreshold = 0;
62      }
63  
64      private static int normalizeCapacity(int initialCapacity) {
65          int actualCapacity = 1;
66          while (actualCapacity < initialCapacity) {
67              actualCapacity <<= 1;
68              if (actualCapacity < 0) {
69                  actualCapacity = 1 << 30;
70                  break;
71              }
72          }
73          return actualCapacity;
74      }
75  
76      /**
77       * Returns the capacity of this queue.
78       */
79      public int capacity() {
80          return items.length;
81      }
82  
83      @Override
84      public void clear() {
85          if (!isEmpty()) {
86              Arrays.fill(items, null);
87              first = 0;
88              last = 0;
89              full = false;
90              shrinkIfNeeded();
91          }
92      }
93  
94      @SuppressWarnings("unchecked")
95      public E poll() {
96          if (isEmpty()) {
97              return null;
98          }
99  
100         Object ret = items[first];
101         items[first] = null;
102         decreaseSize();
103         
104         if (first == last) {
105             first = last = 0;
106         }
107 
108         shrinkIfNeeded();
109         return (E) ret;
110     }
111 
112     public boolean offer(E item) {
113         if (item == null) {
114             throw new NullPointerException("item");
115         }
116         expandIfNeeded();
117         items[last] = item;
118         increaseSize();
119         return true;
120     }
121 
122     @SuppressWarnings("unchecked")
123     public E peek() {
124         if (isEmpty()) {
125             return null;
126         }
127 
128         return (E) items[first];
129     }
130 
131     @SuppressWarnings("unchecked")
132     @Override
133     public E get(int idx) {
134         checkIndex(idx);
135         return (E) items[getRealIndex(idx)];
136     }
137 
138     @Override
139     public boolean isEmpty() {
140         return (first == last) && !full;
141     }
142 
143     @Override
144     public int size() {
145         if (full) {
146             return capacity();
147         }
148         
149         if (last >= first) {
150             return last - first;
151         } else {
152             return last - first + capacity();
153         }
154     }
155     
156     @Override
157     public String toString() {
158         return "first=" + first + ", last=" + last + ", size=" + size()
159                 + ", mask = " + mask;
160     }
161 
162     private void checkIndex(int idx) {
163         if (idx < 0 || idx >= size()) {
164             throw new IndexOutOfBoundsException(String.valueOf(idx));
165         }
166     }
167 
168     private int getRealIndex(int idx) {
169         return (first + idx) & mask;
170     }
171 
172     private void increaseSize() {
173         last = (last + 1) & mask;
174         full = first == last;
175     }
176 
177     private void decreaseSize() {
178         first = (first + 1) & mask;
179         full = false;
180     }
181 
182     private void expandIfNeeded() {
183         if (full) {
184             // expand queue
185             final int oldLen = items.length;
186             final int newLen = oldLen << 1;
187             Object[] tmp = new Object[newLen];
188     
189             if (first < last) {
190                 System.arraycopy(items, first, tmp, 0, last - first);
191             } else {
192                 System.arraycopy(items, first, tmp, 0, oldLen - first);
193                 System.arraycopy(items, 0, tmp, oldLen - first, last);
194             }
195     
196             first = 0;
197             last = oldLen;
198             items = tmp;
199             mask = tmp.length - 1;
200             if (newLen >>> 3 > initialCapacity) {
201                 shrinkThreshold = newLen >>> 3;
202             }
203         }
204     }
205     
206     private void shrinkIfNeeded() {
207         int size = size();
208         if (size < shrinkThreshold) {
209             // shrink queue
210             final int oldLen = items.length;
211             int newLen = normalizeCapacity(size);
212             if (size == newLen) {
213                 newLen <<= 1;
214             }
215             
216             if (newLen >= oldLen) {
217                 return;
218             }
219             
220             if (newLen < initialCapacity) {
221                 if (oldLen == initialCapacity) {
222                     return;
223                 } else {
224                     newLen = initialCapacity;
225                 }
226             }
227             
228             Object[] tmp = new Object[newLen];
229     
230             if (first < last) {
231                 System.arraycopy(items, first, tmp, 0, last - first);
232             } else {
233                 System.arraycopy(items, first, tmp, 0, oldLen - first);
234                 System.arraycopy(items, 0, tmp, oldLen - first, last);
235             }
236     
237             first = 0;
238             last = size;
239             items = tmp;
240             mask = tmp.length - 1;
241             shrinkThreshold = 0;
242         }
243     }
244 
245     @Override
246     public boolean add(E o) {
247         return offer(o);
248     }
249 
250     @SuppressWarnings("unchecked")
251     @Override
252     public E set(int idx, E o) {
253         checkIndex(idx);
254 
255         int realIdx = getRealIndex(idx);
256         Object old = items[realIdx];
257         items[realIdx] = o;
258         return (E) old;
259     }
260 
261     @Override
262     public void add(int idx, E o) {
263         if (idx == size()) {
264             offer(o);
265             return;
266         }
267 
268         checkIndex(idx);
269         expandIfNeeded();
270 
271         int realIdx = getRealIndex(idx);
272 
273         // Make a room for a new element.
274         if (first < last) {
275             System
276                     .arraycopy(items, realIdx, items, realIdx + 1, last
277                             - realIdx);
278         } else {
279             if (realIdx >= first) {
280                 System.arraycopy(items, 0, items, 1, last);
281                 items[0] = items[items.length - 1];
282                 System.arraycopy(items, realIdx, items, realIdx + 1,
283                         items.length - realIdx - 1);
284             } else {
285                 System.arraycopy(items, realIdx, items, realIdx + 1, last
286                         - realIdx);
287             }
288         }
289 
290         items[realIdx] = o;
291         increaseSize();
292     }
293 
294     @SuppressWarnings("unchecked")
295     @Override
296     public E remove(int idx) {
297         if (idx == 0) {
298             return poll();
299         }
300 
301         checkIndex(idx);
302 
303         int realIdx = getRealIndex(idx);
304         Object removed = items[realIdx];
305 
306         // Remove a room for the removed element.
307         if (first < last) {
308             System.arraycopy(items, first, items, first + 1, realIdx - first);
309         } else {
310             if (realIdx >= first) {
311                 System.arraycopy(items, first, items, first + 1, realIdx
312                         - first);
313             } else {
314                 System.arraycopy(items, 0, items, 1, realIdx);
315                 items[0] = items[items.length - 1];
316                 System.arraycopy(items, first, items, first + 1, items.length
317                         - first - 1);
318             }
319         }
320 
321         items[first] = null;
322         decreaseSize();
323 
324         shrinkIfNeeded();
325         return (E) removed;
326     }
327 
328     public E remove() {
329         if (isEmpty()) {
330             throw new NoSuchElementException();
331         }
332         return poll();
333     }
334 
335     public E element() {
336         if (isEmpty()) {
337             throw new NoSuchElementException();
338         }
339         return peek();
340     }
341 }