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;
final class CodecOutputList extends AbstractList<Object> implements RandomAccess {
private static final CodecOutputListRecycler NOOP_RECYCLER = new CodecOutputListRecycler() {
@Override
public void recycle(CodecOutputList object) {
}
};
private static final FastThreadLocal<CodecOutputLists> CODEC_OUTPUT_LISTS_POOL =
new FastThreadLocal<CodecOutputLists>() {
@Override
protected CodecOutputLists initialValue() throws Exception {
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) {
elements[i] = new CodecOutputList(this, 16);
}
count = elements.length;
currentIdx = elements.length;
mask = elements.length - 1;
}
public CodecOutputList getOrCreate() {
if (count == 0) {
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) {
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() {
size = 0;
}
boolean insertSinceRecycled() {
return insertSinceRecycled;
}
void recycle() {
for (int i = 0 ; i < size; i ++) {
array[i] = null;
}
size = 0;
insertSinceRecycled = false;
recycler.recycle(this);
}
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() {
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;
}
}