/*
* 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.Recycler;
import java.util.AbstractList;
import java.util.RandomAccess;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Special {@link AbstractList} implementation which is used within our codec base classes.
*/
final class CodecOutputList extends AbstractList<Object> implements RandomAccess {
private static final Recycler<CodecOutputList> RECYCLER = new Recycler<CodecOutputList>() {
@Override
protected CodecOutputList newObject(Handle<CodecOutputList> handle) {
return new CodecOutputList(handle);
}
};
static CodecOutputList newInstance() {
return RECYCLER.get();
}
private final Recycler.Handle<CodecOutputList> handle;
private int size;
// Size of 16 should be good enough for 99 % of all users.
private Object[] array = new Object[16];
private boolean insertSinceRecycled;
private CodecOutputList(Recycler.Handle<CodecOutputList> handle) {
this.handle = handle;
}
@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 {@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.
*/
void recycle() {
for (int i = 0 ; i < size; i ++) {
array[i] = null;
}
clear();
insertSinceRecycled = false;
handle.recycle(this);
}
/**
* 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;
}
}