package com.github.fge.grappa.stack; import com.google.common.annotations.VisibleForTesting; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; /** * A {@link ValueStack} implementation using arrays * * <p>This is the default implementation currently used.</p> * * @param <V> type parameter of the stack's element */ @ParametersAreNonnullByDefault public final class ArrayValueStack<V> extends ValueStackBase<V> { @VisibleForTesting static final int INITIAL_SIZE = 16; @VisibleForTesting static final int SIZE_INCREASE = 16; private int arraySize = 0; private V[] array = (V[]) new Object[INITIAL_SIZE]; public ArrayValueStack() { } @VisibleForTesting ArrayValueStack(final V[] values) { System.arraycopy(values, 0, array, 0, values.length); arraySize = values.length; } @VisibleForTesting V[] getArray() { return Arrays.copyOf(array, array.length); } @Override protected void doPush(final int down, final V value) { ensureCapacity(); System.arraycopy(array, down, array, down + 1, arraySize - down); array[down] = value; arraySize++; } @Override protected V doPop(final int down) { final V ret = array[down]; arraySize--; System.arraycopy(array, down + 1, array, down, arraySize - down); array[arraySize] = null; shrinkIfNecessary(); return ret; } @Override protected V doPeek(final int down) { return array[down]; } @Override protected void doPoke(final int down, final V value) { array[down] = value; } @Override protected void doDup() { ensureCapacity(); System.arraycopy(array, 0, array, 1, arraySize); arraySize++; } @Override protected void doSwap(final int n) { V tmp; final int swapIndex = n / 2; // this also works for odd numbers for (int index = 0; index < swapIndex; index++) { tmp = array[index]; array[index] = array[n - index - 1]; array[n - index - 1] = tmp; } } @Override public int size() { return arraySize; } @Override public void clear() { arraySize = 0; array = (V[]) new Object[INITIAL_SIZE]; } @Nonnull @Override public Object takeSnapshot() { final V[] copy = Arrays.copyOf(array, array.length); return new ArrayWithSize<>(copy, arraySize); } @Override public void restoreSnapshot(final Object snapshot) { final ArrayWithSize<V> s = (ArrayWithSize<V>) snapshot; array = s.array; arraySize = s.arraySize; } @Override public Iterator<V> iterator() { return new ArrayIterator<>(array, arraySize); } private void ensureCapacity() { if (arraySize == array.length) array = Arrays.copyOf(array, arraySize + SIZE_INCREASE); } private void shrinkIfNecessary() { final int length = array.length; final int lengthSizeDiff = length - arraySize; if (lengthSizeDiff >= SIZE_INCREASE) array = Arrays.copyOf(array, length - SIZE_INCREASE); } private static final class ArrayWithSize<T> { private final T[] array; private final int arraySize; private ArrayWithSize(final T[] array, final int size) { this.array = array; arraySize = size; } } private static final class ArrayIterator<T> implements Iterator<T> { private final T[] array; private final int arraySize; private int index = 0; private ArrayIterator(final T[] array, final int size) { this.array = array; arraySize = size; } @Override public boolean hasNext() { return index < arraySize; } @Override public T next() { if (!hasNext()) throw new NoSuchElementException(); return array[index++]; } @Override public void remove() { throw new UnsupportedOperationException(); } } }