/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.internal; import java.util.*; /** * List implementation with three fixed slots to avoid initial array allocation. */ public final class InlineArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess { private static final int INITIAL_CAPACITY = 3; private static final int INITIAL_SIZE = 10; private int capacity = INITIAL_CAPACITY; private int size = 0; private E fst = null; private E snd = null; private E thd = null; private E[] extended = null; @SuppressWarnings("unchecked") private static <T> T[] newArray(int len) { return (T[]) new Object[len]; } private static boolean eq(Object a, Object b) { return Objects.equals(a, b); } private void ensureCapacity(int c) { if (c > capacity) { grow(c); } } private void grow(int c) { E[] ext = this.extended; if (ext == null) { int len = Math.max(c, INITIAL_SIZE); E[] array = newArray(len); array[0] = fst; array[1] = snd; array[2] = thd; capacity = len; extended = array; fst = snd = thd = null; } else { int len = Math.max(c, ext.length + (ext.length >> 1)); E[] array = Arrays.copyOf(ext, len); capacity = len; extended = array; } } private E uncheckedGet(int index) { E[] ext = this.extended; if (ext != null) { return ext[index]; } switch (index) { case 0: return fst; case 1: return snd; case 2: return thd; default: throw new AssertionError(); } } private void uncheckedSet(int index, E e) { E[] ext = this.extended; if (ext != null) { ext[index] = e; return; } switch (index) { case 0: fst = e; return; case 1: snd = e; return; case 2: thd = e; return; default: throw new AssertionError(); } } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public void clear() { modCount++; size = 0; fst = null; snd = null; thd = null; extended = null; } @Override public boolean contains(Object o) { return indexOf(o) != -1; } @Override public boolean remove(Object o) { int i = indexOf(o); if (i == -1) { return false; } remove(i); return true; } @Override public boolean add(E e) { int size = this.size; ensureCapacity(size + 1); uncheckedSet(size, e); modCount++; this.size += 1; return true; } @Override public E get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException(); return uncheckedGet(index); } @Override public E set(int index, E element) { E prev = get(index); uncheckedSet(index, element); return prev; } @Override public void add(int index, E element) { int size = this.size; if (index < 0 || index > size) throw new IndexOutOfBoundsException(); ensureCapacity(size + 1); E[] ext = this.extended; if (ext != null) { System.arraycopy(ext, index, ext, index + 1, size - index); } else { switch (index) { case 0: thd = snd; snd = fst; break; case 1: thd = snd; break; case 2: break; default: throw new AssertionError(); } } uncheckedSet(index, element); modCount++; this.size += 1; } @Override public E remove(int index) { E prev = get(index); int size = this.size; E[] ext = this.extended; if (ext != null) { int shift = size - index - 1; if (shift > 0) { System.arraycopy(ext, index + 1, ext, index, shift); } ext[size - 1] = null; } else { switch (index) { case 0: fst = snd; case 1: snd = thd; case 2: thd = null; break; default: throw new AssertionError(); } } modCount++; this.size -= 1; return prev; } @Override public int indexOf(Object o) { int size = this.size; E[] ext = this.extended; if (ext != null) { for (int i = 0; i < size; ++i) { if (eq(ext[i], o)) return i; } } else { if (size > 0 && eq(fst, o)) return 0; if (size > 1 && eq(snd, o)) return 1; if (size > 2 && eq(thd, o)) return 2; } return -1; } @Override public int lastIndexOf(Object o) { int size = this.size; E[] ext = this.extended; if (ext != null) { for (int i = size - 1; i >= 0; --i) { if (eq(ext[i], o)) return i; } } else { if (size > 2 && eq(thd, o)) return 2; if (size > 1 && eq(snd, o)) return 1; if (size > 0 && eq(fst, o)) return 0; } return -1; } @Override public Object[] toArray() { int size = this.size; E[] ext = this.extended; if (ext != null) { return Arrays.copyOf(ext, size); } Object[] array = new Object[size]; switch (size) { default: throw new AssertionError(); case 3: array[2] = thd; case 2: array[1] = snd; case 1: array[0] = fst; case 0: return array; } } @Override public Iterator<E> iterator() { return new IteratorImpl(); } @Override public ListIterator<E> listIterator() { return new ListIteratorImpl(0); } @Override public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException(); return new ListIteratorImpl(index); } /** * @see ArrayList#trimToSize() */ public void trimToSize() { E[] ext = this.extended; if (ext != null && size < ext.length) { modCount++; extended = Arrays.copyOf(ext, size); } } private final class IteratorImpl implements Iterator<E> { private final int expectedModCount = InlineArrayList.this.modCount; private int cursor = 0; @Override public boolean hasNext() { return (cursor < size); } @Override public E next() { if (expectedModCount != InlineArrayList.this.modCount) { throw new ConcurrentModificationException(); } if (cursor >= size) { throw new NoSuchElementException(); } return uncheckedGet(cursor++); } @Override public void remove() { throw new UnsupportedOperationException(); } } private final class ListIteratorImpl implements ListIterator<E> { private final int expectedModCount = InlineArrayList.this.modCount; private int cursor; ListIteratorImpl(int cursor) { this.cursor = cursor; } @Override public boolean hasNext() { return (cursor < size); } @Override public E next() { if (expectedModCount != InlineArrayList.this.modCount) { throw new ConcurrentModificationException(); } if (cursor >= size) { throw new NoSuchElementException(); } return uncheckedGet(cursor++); } @Override public boolean hasPrevious() { return (cursor > 0); } @Override public E previous() { if (expectedModCount != InlineArrayList.this.modCount) { throw new ConcurrentModificationException(); } if (cursor <= 0) { throw new NoSuchElementException(); } return uncheckedGet(--cursor); } @Override public int nextIndex() { return cursor; } @Override public int previousIndex() { return cursor - 1; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void set(E e) { throw new UnsupportedOperationException(); } @Override public void add(E e) { throw new UnsupportedOperationException(); } } }