/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/licenses/publicdomain
 */

package java.util.concurrent.atomic;

import sun.misc.Unsafe;

/**
 * An array of object references in which elements may be updated atomically.
 * See the {@link java.util.concurrent.atomic} package specification for
 * description of the properties of atomic variables.
 * 
 * @since 1.5
 * @author Doug Lea
 * @param <E>
 *            The base class of elements held in this array
 */
public class AtomicReferenceArray<E> implements java.io.Serializable {
	private static final long serialVersionUID = -6209656149925076980L;

	private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed
	private static final int base = unsafe.arrayBaseOffset(Object[].class);
	private static final int scale = unsafe.arrayIndexScale(Object[].class);
	private final Object[] array;

	private long checkedByteOffset(int i) {
		if (i < 0 || i >= array.length)
			throw new IndexOutOfBoundsException("index " + i);

		return byteOffset(i);
	}

	private static long byteOffset(int i) {
		return base + (long) i * scale;
	}

	/**
	 * Creates a new AtomicReferenceArray of the given length, with all elements
	 * initially zero.
	 * 
	 * @param length
	 *            the length of the array
	 */
	public AtomicReferenceArray(int length) {
		array = new Object[length];
	}

	/**
	 * Creates a new AtomicReferenceArray with the same length as, and all
	 * elements copied from, the given array.
	 * 
	 * @param array
	 *            the array to copy elements from
	 * @throws NullPointerException
	 *             if array is null
	 */
	public AtomicReferenceArray(E[] array) {
		// Visibility guaranteed by final field guarantees
		this.array = array.clone();
	}

	/**
	 * Returns the length of the array.
	 * 
	 * @return the length of the array
	 */
	public final int length() {
		return array.length;
	}

	/**
	 * Gets the current value at position {@code i}.
	 * 
	 * @param i
	 *            the index
	 * @return the current value
	 */
	public final E get(int i) {
		return getRaw(checkedByteOffset(i));
	}

	private E getRaw(long offset) {
		return (E) unsafe.getObjectVolatile(array, offset);
	}

	/**
	 * Sets the element at position {@code i} to the given value.
	 * 
	 * @param i
	 *            the index
	 * @param newValue
	 *            the new value
	 */
	public final void set(int i, E newValue) {
		unsafe.putObjectVolatile(array, checkedByteOffset(i), newValue);
	}

	/**
	 * Eventually sets the element at position {@code i} to the given value.
	 * 
	 * @param i
	 *            the index
	 * @param newValue
	 *            the new value
	 * @since 1.6
	 */
	public final void lazySet(int i, E newValue) {
		unsafe.putOrderedObject(array, checkedByteOffset(i), newValue);
	}

	/**
	 * Atomically sets the element at position {@code i} to the given value and
	 * returns the old value.
	 * 
	 * @param i
	 *            the index
	 * @param newValue
	 *            the new value
	 * @return the previous value
	 */
	public final E getAndSet(int i, E newValue) {
		long offset = checkedByteOffset(i);
		while (true) {
			E current = (E) getRaw(offset);
			if (compareAndSetRaw(offset, current, newValue))
				return current;
		}
	}

	/**
	 * Atomically sets the element at position {@code i} to the given updated
	 * value if the current value {@code ==} the expected value.
	 * 
	 * @param i
	 *            the index
	 * @param expect
	 *            the expected value
	 * @param update
	 *            the new value
	 * @return true if successful. False return indicates that the actual value
	 *         was not equal to the expected value.
	 */
	public final boolean compareAndSet(int i, E expect, E update) {
		return compareAndSetRaw(checkedByteOffset(i), expect, update);
	}

	private boolean compareAndSetRaw(long offset, E expect, E update) {
		return unsafe.compareAndSwapObject(array, offset, expect, update);
	}

	/**
	 * Atomically sets the element at position {@code i} to the given updated
	 * value if the current value {@code ==} the expected value.
	 * 
	 * <p>
	 * May <a href="package-summary.html#Spurious">fail spuriously</a> and does
	 * not provide ordering guarantees, so is only rarely an appropriate
	 * alternative to {@code compareAndSet}.
	 * 
	 * @param i
	 *            the index
	 * @param expect
	 *            the expected value
	 * @param update
	 *            the new value
	 * @return true if successful.
	 */
	public final boolean weakCompareAndSet(int i, E expect, E update) {
		return compareAndSet(i, expect, update);
	}

	/**
	 * Returns the String representation of the current values of array.
	 * 
	 * @return the String representation of the current values of array
	 */
	public String toString() {
		int iMax = array.length - 1;
		if (iMax == -1)
			return "[]";

		StringBuilder b = new StringBuilder();
		b.append('[');
		for (int i = 0;; i++) {
			b.append(getRaw(byteOffset(i)));
			if (i == iMax)
				return b.append(']').toString();
			b.append(", ");
		}
	}

}
