/*
* JVSTM: a Java library for Software Transactional Memory
* Copyright (C) 2005 INESC-ID Software Engineering Group
* http://www.esw.inesc-id.pt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author's contact:
* INESC-ID Software Engineering Group
* Rua Alves Redol 9
* 1000 - 029 Lisboa
* Portugal
*/
package jvstm.util;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.concurrent.atomic.AtomicReferenceArray;
import jvstm.Atomic;
import jvstm.VArray;
import jvstm.VBox;
import jvstm.VBoxInt;
/** Versioned ArrayList implementation. **/
/* Lots of optimizations can still be done. Wherever a method's implementation
* is reused from its superclass, there might be a potential optimization.
*/
public class VArrayList<E> extends AbstractList<E> implements RandomAccess, Deque<E> {
private final VBox<VArray<E>> array;
private final VArray<E> array() {
return array.get();
}
private final VBoxInt size;
public VArrayList() {
this(10);
}
public VArrayList(Collection<? extends E> c) {
this(c.size());
addAll(c);
}
public VArrayList(int initialCapacity) {
array = new VBox<VArray<E>>(new VArray<E>(initialCapacity));
size = new VBoxInt();
}
// List / ArrayList methods
@Override
@Atomic(speculativeReadOnly = true)
public boolean add(E e) {
int size = size();
ensureCapacity(size + 1);
array().put(size, e);
this.size.putInt(size + 1);
return true;
}
@Override
@Atomic(speculativeReadOnly = false)
public void add(int index, E element) {
int size = size();
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(size + 1);
VArray<E> array = array(); // Must be done AFTER ensureCapacity!
for (int i = size - 1; i >= index; i--) {
array.put(i + 1, array.get(i));
}
array.put(index, element);
this.size.put(size + 1);
}
@Override
@Atomic(speculativeReadOnly = false)
public boolean addAll(Collection<? extends E> c) {
if (c.size() == 0) {
return false;
}
int size = size();
ensureCapacity(size + c.size());
VArray<E> array = array(); // Must be done AFTER ensureCapacity!
for (E element : c) {
array.put(size++, element);
}
this.size.putInt(size);
return true;
}
@Override
@Atomic(speculativeReadOnly = false)
public boolean addAll(int index, Collection<? extends E> c) {
int size = size();
if (index > size) {
throw new IndexOutOfBoundsException();
}
ensureCapacity(size + c.size());
return super.addAll(index, c);
}
@Override
@Atomic(speculativeReadOnly = false)
public void clear() {
// Let the GC do its job
array.put(new VArray<E>(array().length));
size.putInt(0);
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
@Override
@Atomic(readOnly = true)
public boolean containsAll(Collection<?> c) {
return super.containsAll(c);
}
@Atomic
public void ensureCapacity(int minCapacity) {
if (minCapacity <= array().length) {
return;
}
// Calculate new size for array
int minCapHighBit = Integer.highestOneBit(minCapacity);
int newCapacity = minCapHighBit << 1;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if ((minCapacity & (minCapHighBit >> 1)) > 0) {
newCapacity += minCapHighBit;
}
resize(newCapacity);
}
@Atomic(speculativeReadOnly = false)
public void trimToSize() {
int size = size();
if (array().length > size) {
resize(size);
}
}
/* Should only be called from an Atomic context */
private void resize(int newCapacity) {
// To resize the array, we create a new one and copy over the elements from the old one
// As this operation is very time-sensitive, this implementation uses inside knowledge of
// VArray to go faster
int size = size();
VArray<E> oldVArray = array();
VArray<E> newVArray = new VArray<E>(newCapacity);
// Get the AtomicReferenceArray from the underlying VArray
AtomicReferenceArray<E> realArray = newVArray.values;
// Copy elements over
for (int i = 0; i < size; i++) {
realArray.lazySet(i, oldVArray.get(i));
}
// Update VBox
this.array.put(newVArray);
}
@Override
@Atomic(readOnly = true)
/** Note that this operation is only transactional if both lists are transactional. **/
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public E get(int index) {
if (index >= size()) {
throw new IndexOutOfBoundsException();
}
return array().get(index);
}
@Override
@Atomic(readOnly = true)
public int hashCode() {
return super.hashCode();
}
@Override
@Atomic(readOnly = true)
public int indexOf(Object o) {
return super.indexOf(o);
}
@Override
@Atomic(readOnly = true)
public int lastIndexOf(Object o) {
return super.lastIndexOf(o);
}
@Override
@Atomic(speculativeReadOnly = false)
public E remove(int index) {
int size = size();
if (index >= size) {
throw new IndexOutOfBoundsException();
}
VArray<E> array = array();
int newSize = size - 1;
E oldValue = array.get(index);
for (int i = index; i < newSize; i++) {
array.put(i, array.get(i + 1));
}
array.put(newSize, null);
this.size.put(newSize);
return oldValue;
}
@Override
@Atomic
public boolean remove(Object o) {
int index = indexOf(o);
if (index < 0) {
return false;
} else {
remove(index);
return true;
}
}
@Override
@Atomic
public boolean removeAll(Collection<?> c) {
return super.removeAll(c);
}
@Override
@Atomic(speculativeReadOnly = false)
protected void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex, toIndex);
}
@Override
@Atomic
public boolean retainAll(Collection<?> c) {
return super.retainAll(c);
}
@Override
@Atomic(speculativeReadOnly = false)
public E set(int index, E element) {
if (index >= size()) {
throw new IndexOutOfBoundsException();
}
VArray<E> array = array();
E oldValue = array.get(index);
array.put(index, element);
return oldValue;
}
@Override
public final int size() {
return size.getInt();
}
@Override
@Atomic(readOnly = true)
public Object[] toArray() {
return toArray(new Object[size()]);
}
@SuppressWarnings("unchecked")
@Override
@Atomic(readOnly = true)
public <T> T[] toArray(T[] a) {
VArray<E> array = array();
int size = size();
if (a.length < size) {
// Need to create a bigger array
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
}
for (int i = 0; i < size; i++) {
a[i] = (T) array.get(i);
}
if (a.length > size) {
a[size] = null;
}
return a;
}
@Override
@Atomic(readOnly = true)
public String toString() {
return super.toString();
}
// Deque methods
@Override
public void addFirst(E e) {
add(0, e);
}
@Override
public void addLast(E e) {
add(e);
}
@Override
public Iterator<E> descendingIterator() {
return new ListIteratorReverser<E>(listIterator(size()));
}
@Override
public E element() {
return getFirst();
}
@Override
@Atomic(readOnly = true)
public E getFirst() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return peekFirst();
}
@Override
@Atomic(readOnly = true)
public E getLast() {
if (isEmpty()) {
throw new NoSuchElementException();
}
return peekLast();
}
@Override
public boolean offer(E e) {
return offerLast(e);
}
@Override
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
@Override
public boolean offerLast(E e) {
addLast(e);
return true;
}
@Override
public E peek() {
return peekFirst();
}
@Override
public E peekFirst() {
try {
return get(0);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
@Override
@Atomic(readOnly = true)
public E peekLast() {
try {
return get(size() - 1);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
@Override
public E poll() {
return pollFirst();
}
@Override
public E pollFirst() {
try {
return remove(0);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
@Override
@Atomic(speculativeReadOnly = false)
public E pollLast() {
try {
return remove(size() - 1);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
@Override
public E pop() {
return removeFirst();
}
@Override
public void push(E e) {
addFirst(e);
}
@Override
public E remove() {
return removeFirst();
}
@Override
public E removeFirst() {
try {
return remove(0);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
@Override
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
@Override
@Atomic(speculativeReadOnly = false)
public E removeLast() {
try {
return remove(size() - 1);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
@Override
@Atomic
public boolean removeLastOccurrence(Object o) {
try {
remove(lastIndexOf(o));
return true;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
// Extra utility stuff
private static final class ListIteratorReverser<E> implements Iterator<E>, Iterable<E> {
final ListIterator<E> listIterator;
ListIteratorReverser(ListIterator<E> listIterator) {
this.listIterator = listIterator;
}
@Override
public boolean hasNext() {
return listIterator.hasPrevious();
}
@Override
public E next() {
return listIterator.previous();
}
@Override
public void remove() {
listIterator.remove();
}
@Override
public Iterator<E> iterator() {
return this;
}
}
public VArrayList(Iterator<? extends E> it) {
this();
while (it.hasNext()) {
add(it.next());
}
}
public E first() {
return getFirst();
}
public E last() {
return getLast();
}
/**
* Returns an Iterable object, suitable for using with foreach to iterate
* over the current list in reverse order.
**/
public Iterable<E> reverseIteration() {
return new ListIteratorReverser<E>(listIterator(size()));
}
/**
* Returns a new list, with the same elements as the current list, but with
* a reversed order.
**/
@Atomic(readOnly = true)
public VArrayList<E> reversed() {
return new VArrayList<E>(descendingIterator());
}
}