/*
 * Copyright 2016 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.handler.codec;

import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.MathUtil;

import java.util.AbstractList;
import java.util.RandomAccess;

import static io.netty.util.internal.ObjectUtil.checkNotNull;

Special AbstractList implementation which is used within our codec base classes.
/** * Special {@link AbstractList} implementation which is used within our codec base classes. */
final class CodecOutputList extends AbstractList<Object> implements RandomAccess { private static final CodecOutputListRecycler NOOP_RECYCLER = new CodecOutputListRecycler() { @Override public void recycle(CodecOutputList object) { // drop on the floor and let the GC handle it. } }; private static final FastThreadLocal<CodecOutputLists> CODEC_OUTPUT_LISTS_POOL = new FastThreadLocal<CodecOutputLists>() { @Override protected CodecOutputLists initialValue() throws Exception { // 16 CodecOutputList per Thread are cached. return new CodecOutputLists(16); } }; private interface CodecOutputListRecycler { void recycle(CodecOutputList codecOutputList); } private static final class CodecOutputLists implements CodecOutputListRecycler { private final CodecOutputList[] elements; private final int mask; private int currentIdx; private int count; CodecOutputLists(int numElements) { elements = new CodecOutputList[MathUtil.safeFindNextPositivePowerOfTwo(numElements)]; for (int i = 0; i < elements.length; ++i) { // Size of 16 should be good enough for the majority of all users as an initial capacity. elements[i] = new CodecOutputList(this, 16); } count = elements.length; currentIdx = elements.length; mask = elements.length - 1; } public CodecOutputList getOrCreate() { if (count == 0) { // Return a new CodecOutputList which will not be cached. We use a size of 4 to keep the overhead // low. return new CodecOutputList(NOOP_RECYCLER, 4); } --count; int idx = (currentIdx - 1) & mask; CodecOutputList list = elements[idx]; currentIdx = idx; return list; } @Override public void recycle(CodecOutputList codecOutputList) { int idx = currentIdx; elements[idx] = codecOutputList; currentIdx = (idx + 1) & mask; ++count; assert count <= elements.length; } } static CodecOutputList newInstance() { return CODEC_OUTPUT_LISTS_POOL.get().getOrCreate(); } private final CodecOutputListRecycler recycler; private int size; private Object[] array; private boolean insertSinceRecycled; private CodecOutputList(CodecOutputListRecycler recycler, int size) { this.recycler = recycler; array = new Object[size]; } @Override public Object get(int index) { checkIndex(index); return array[index]; } @Override public int size() { return size; } @Override public boolean add(Object element) { checkNotNull(element, "element"); try { insert(size, element); } catch (IndexOutOfBoundsException ignore) { // This should happen very infrequently so we just catch the exception and try again. expandArray(); insert(size, element); } ++ size; return true; } @Override public Object set(int index, Object element) { checkNotNull(element, "element"); checkIndex(index); Object old = array[index]; insert(index, element); return old; } @Override public void add(int index, Object element) { checkNotNull(element, "element"); checkIndex(index); if (size == array.length) { expandArray(); } if (index != size - 1) { System.arraycopy(array, index, array, index + 1, size - index); } insert(index, element); ++ size; } @Override public Object remove(int index) { checkIndex(index); Object old = array[index]; int len = size - index - 1; if (len > 0) { System.arraycopy(array, index + 1, array, index, len); } array[-- size] = null; return old; } @Override public void clear() { // We only set the size to 0 and not null out the array. Null out the array will explicit requested by // calling recycle() size = 0; }
Returns true if any elements where added or set. This will be reset once recycle() was called.
/** * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called. */
boolean insertSinceRecycled() { return insertSinceRecycled; }
Recycle the array which will clear it and null out all entries in the internal storage.
/** * Recycle the array which will clear it and null out all entries in the internal storage. */
void recycle() { for (int i = 0 ; i < size; i ++) { array[i] = null; } size = 0; insertSinceRecycled = false; recycler.recycle(this); }
Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe.
/** * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe. */
Object getUnsafe(int index) { return array[index]; } private void checkIndex(int index) { if (index >= size) { throw new IndexOutOfBoundsException(); } } private void insert(int index, Object element) { array[index] = element; insertSinceRecycled = true; } private void expandArray() { // double capacity int newCapacity = array.length << 1; if (newCapacity < 0) { throw new OutOfMemoryError(); } Object[] newArray = new Object[newCapacity]; System.arraycopy(array, 0, newArray, 0, array.length); array = newArray; } }