1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.util;
20
21import java.util.AbstractList;
22import java.util.NoSuchElementException;
23import java.util.Queue;
24
25/* ------------------------------------------------------------ */
26/**
27 * Queue backed by circular array.
28 * <p/>
29 * This partial Queue implementation (also with {@link #remove()} for stack operation)
30 * is backed by a growable circular array.
31 *
32 * @param <E>
33 */
34public class ArrayQueue<E> extends AbstractList<E> implements Queue<E>
35{
36    public static final int DEFAULT_CAPACITY = 64;
37    public static final int DEFAULT_GROWTH = 32;
38
39    protected final Object _lock;
40    protected final int _growCapacity;
41    protected Object[] _elements;
42    protected int _nextE;
43    protected int _nextSlot;
44    protected int _size;
45
46    /* ------------------------------------------------------------ */
47    public ArrayQueue()
48    {
49        this(DEFAULT_CAPACITY, -1);
50    }
51
52    /* ------------------------------------------------------------ */
53    public ArrayQueue(int capacity)
54    {
55        this(capacity, -1);
56    }
57
58    /* ------------------------------------------------------------ */
59    public ArrayQueue(int initCapacity, int growBy)
60    {
61        this(initCapacity, growBy, null);
62    }
63
64    /* ------------------------------------------------------------ */
65    public ArrayQueue(int initCapacity, int growBy, Object lock)
66    {
67        _lock = lock == null ? this : lock;
68        _growCapacity = growBy;
69        _elements = new Object[initCapacity];
70    }
71
72    /* ------------------------------------------------------------ */
73    public int getCapacity()
74    {
75        synchronized (_lock)
76        {
77            return _elements.length;
78        }
79    }
80
81    /* ------------------------------------------------------------ */
82    @Override
83    public boolean add(E e)
84    {
85        if (!offer(e))
86            throw new IllegalStateException("Full");
87        return true;
88    }
89
90    /* ------------------------------------------------------------ */
91    public boolean offer(E e)
92    {
93        synchronized (_lock)
94        {
95            return enqueue(e);
96        }
97    }
98
99    /* ------------------------------------------------------------ */
100    private boolean enqueue(E e)
101    {
102        if (_size == _elements.length && !grow())
103            return false;
104
105        _size++;
106        _elements[_nextSlot++] = e;
107        if (_nextSlot == _elements.length)
108            _nextSlot = 0;
109
110        return true;
111    }
112
113    /* ------------------------------------------------------------ */
114    /**
115     * Add without synchronization or bounds checking
116     *
117     * @param e the element to add
118     * @see #add(Object)
119     */
120    public void addUnsafe(E e)
121    {
122        if (!enqueue(e))
123            throw new IllegalStateException("Full");
124    }
125
126    /* ------------------------------------------------------------ */
127    public E element()
128    {
129        synchronized (_lock)
130        {
131            if (isEmpty())
132                throw new NoSuchElementException();
133            return at(_nextE);
134        }
135    }
136
137    @SuppressWarnings("unchecked")
138    private E at(int index)
139    {
140        return (E)_elements[index];
141    }
142
143    /* ------------------------------------------------------------ */
144    public E peek()
145    {
146        synchronized (_lock)
147        {
148            if (isEmpty())
149                return null;
150            return at(_nextE);
151        }
152    }
153
154    /* ------------------------------------------------------------ */
155    public E poll()
156    {
157        synchronized (_lock)
158        {
159            if (_size == 0)
160                return null;
161            return dequeue();
162        }
163    }
164
165    /* ------------------------------------------------------------ */
166    private E dequeue()
167    {
168        E e = at(_nextE);
169        _elements[_nextE] = null;
170        _size--;
171        if (++_nextE == _elements.length)
172            _nextE = 0;
173        return e;
174    }
175
176    /* ------------------------------------------------------------ */
177    public E remove()
178    {
179        synchronized (_lock)
180        {
181            if (_size == 0)
182                throw new NoSuchElementException();
183            return dequeue();
184        }
185    }
186
187    /* ------------------------------------------------------------ */
188    @Override
189    public void clear()
190    {
191        synchronized (_lock)
192        {
193            _size = 0;
194            _nextE = 0;
195            _nextSlot = 0;
196        }
197    }
198
199    /* ------------------------------------------------------------ */
200    @Override
201    public boolean isEmpty()
202    {
203        synchronized (_lock)
204        {
205            return _size == 0;
206        }
207    }
208
209    /* ------------------------------------------------------------ */
210    @Override
211    public int size()
212    {
213        synchronized (_lock)
214        {
215            return _size;
216        }
217    }
218
219    /* ------------------------------------------------------------ */
220    @Override
221    public E get(int index)
222    {
223        synchronized (_lock)
224        {
225            if (index < 0 || index >= _size)
226                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
227            return getUnsafe(index);
228        }
229    }
230
231    /* ------------------------------------------------------------ */
232    /**
233     * Get without synchronization or bounds checking.
234     *
235     * @param  index index of the element to return
236     * @return the element at the specified index
237     * @see #get(int)
238     */
239    public E getUnsafe(int index)
240    {
241        int i = (_nextE + index) % _elements.length;
242        return at(i);
243    }
244
245    /* ------------------------------------------------------------ */
246    @Override
247    public E remove(int index)
248    {
249        synchronized (_lock)
250        {
251            if (index < 0 || index >= _size)
252                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
253
254            int i = (_nextE + index) % _elements.length;
255            E old = at(i);
256
257            if (i < _nextSlot)
258            {
259                // 0                         _elements.length
260                //       _nextE........._nextSlot
261                System.arraycopy(_elements, i + 1, _elements, i, _nextSlot - i);
262                _nextSlot--;
263                _size--;
264            }
265            else
266            {
267                // 0                         _elements.length
268                // ......_nextSlot   _nextE..........
269                System.arraycopy(_elements, i + 1, _elements, i, _elements.length - i - 1);
270                if (_nextSlot > 0)
271                {
272                    _elements[_elements.length - 1] = _elements[0];
273                    System.arraycopy(_elements, 1, _elements, 0, _nextSlot - 1);
274                    _nextSlot--;
275                }
276                else
277                    _nextSlot = _elements.length - 1;
278
279                _size--;
280            }
281
282            return old;
283        }
284    }
285
286    /* ------------------------------------------------------------ */
287    @Override
288    public E set(int index, E element)
289    {
290        synchronized (_lock)
291        {
292            if (index < 0 || index >= _size)
293                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
294
295            int i = _nextE + index;
296            if (i >= _elements.length)
297                i -= _elements.length;
298            E old = at(i);
299            _elements[i] = element;
300            return old;
301        }
302    }
303
304    /* ------------------------------------------------------------ */
305    @Override
306    public void add(int index, E element)
307    {
308        synchronized (_lock)
309        {
310            if (index < 0 || index > _size)
311                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
312
313            if (_size == _elements.length && !grow())
314                throw new IllegalStateException("Full");
315
316            if (index == _size)
317            {
318                add(element);
319            }
320            else
321            {
322                int i = _nextE + index;
323                if (i >= _elements.length)
324                    i -= _elements.length;
325
326                _size++;
327                _nextSlot++;
328                if (_nextSlot == _elements.length)
329                    _nextSlot = 0;
330
331                if (i < _nextSlot)
332                {
333                    // 0                         _elements.length
334                    //       _nextE.....i..._nextSlot
335                    // 0                         _elements.length
336                    // ..i..._nextSlot   _nextE..........
337                    System.arraycopy(_elements, i, _elements, i + 1, _nextSlot - i);
338                    _elements[i] = element;
339                }
340                else
341                {
342                    // 0                         _elements.length
343                    // ......_nextSlot   _nextE.....i....
344                    if (_nextSlot > 0)
345                    {
346                        System.arraycopy(_elements, 0, _elements, 1, _nextSlot);
347                        _elements[0] = _elements[_elements.length - 1];
348                    }
349
350                    System.arraycopy(_elements, i, _elements, i + 1, _elements.length - i - 1);
351                    _elements[i] = element;
352                }
353            }
354        }
355    }
356
357    /* ------------------------------------------------------------ */
358    protected boolean grow()
359    {
360        synchronized (_lock)
361        {
362            if (_growCapacity <= 0)
363                return false;
364
365            Object[] elements = new Object[_elements.length + _growCapacity];
366
367            int split = _elements.length - _nextE;
368            if (split > 0)
369                System.arraycopy(_elements, _nextE, elements, 0, split);
370            if (_nextE != 0)
371                System.arraycopy(_elements, 0, elements, split, _nextSlot);
372
373            _elements = elements;
374            _nextE = 0;
375            _nextSlot = _size;
376            return true;
377        }
378    }
379}
380