/* SnapTree - (c) 2009 Stanford University - PPL */
// SnapReferenceArray
package trees.lockbased.stanfordutils;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReferenceArray;
/** Implements a concurrent fixed-size collection with fast clone. Reads and
* writes have volatile semantics. No provision is provided to grow or shrink
* the collection.
*/
public class SnapDoubleArray implements Iterable<Double>, Cloneable {
// TODO: clean up the internal implementation
private static final int LOG_BF = 5;
private static final int BF = 1 << LOG_BF;
private static final int BF_MASK = BF - 1;
// Internally this is implemented as an external tree with branching factor
// BF. The leaves of the tree are the elements E. Nodes are considered to
// be unshared if they have the same generation as the root node.
private static class Generation {
}
private interface Node {
Node clone();
}
private static final class Leaf extends AtomicLongArray implements Node {
// never modified after initialization of the SnapDoubleArray, but
// convenient to be non-final
Generation gen;
Leaf(final Generation gen, final int length, final double initialValue) {
super(length);
this.gen = gen;
final long bits = Double.doubleToRawLongBits(initialValue);
if (bits != 0L) {
for (int i = 0; i < length; ++i) {
lazySet(i, bits);
}
}
}
Leaf(final Generation gen, final Leaf src) {
super(src.length());
this.gen = gen;
for (int i = 0; i < src.length(); ++i) {
lazySet(i, src.get(i));
}
}
public Leaf clone() {
return new Leaf(new Generation(), this);
}
double getDouble(final int index) {
return Double.longBitsToDouble(get(index));
}
void setDouble(final int index, final double newValue) {
set(index, Double.doubleToRawLongBits(newValue));
}
double getAndSetDouble(final int index, final double newValue) {
return Double.longBitsToDouble(getAndSet(index, Double.doubleToRawLongBits(newValue)));
}
boolean compareAndSetDouble(final int index, final double expected, final double newValue) {
return compareAndSet(index, Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(newValue));
}
}
private static final class Branch extends AtomicReferenceArray<Node> implements Node {
// never modified after initialization of the SnapReferenceArray, but
// convenient to be non-final
Generation gen;
Branch(final Generation gen, final int length, final Node initialValue) {
super(length);
this.gen = gen;
if (initialValue != null) {
for (int i = 0; i < length; ++i) {
lazySet(i, initialValue);
}
}
}
Branch(final Generation gen, final Branch src) {
super(src.length());
this.gen = gen;
for (int i = 0; i < src.length(); ++i) {
lazySet(i, src.get(i));
}
}
public Branch clone() {
return new Branch(new Generation(), this);
}
}
private static class COWMgr extends CopyOnWriteManager<Node> {
private COWMgr(final Node initialValue) {
super(initialValue, 0);
}
protected Node freezeAndClone(final Node value) {
return value.clone();
}
protected Node cloneFrozen(final Node frozenValue) {
return frozenValue.clone();
}
}
/** 0 if _length == 0, otherwise the smallest positive int such that
* (1L << (LOG_BF * _height)) >= _length.
*/
private final int _height;
private final int _length;
private CopyOnWriteManager<Node> _rootRef;
public SnapDoubleArray(final int length) {
this(length, 0.0);
}
public SnapDoubleArray(final int length, final double element) {
int height = 0;
Node partial = null;
if (length > 0) {
// We will insert the gen into all of the partials (since they
// are used exactly once). We reuse the fulls, so we will give
// them a null gen that will cause them to be copied before any
// actual writes.
final Generation gen = new Generation();
Object full = Double.valueOf(element);
do {
++height;
// This is the number of nodes required at this level. They
// are either all full, or all but one full and one partial.
int levelSize = ((length - 1) >> (LOG_BF * (height - 1))) + 1;
// Partial is only present if this level doesn't evenly divide into
// pieces of length BF, or if a lower level didn't divide evenly.
Node newP = null;
if (partial != null || (levelSize & BF_MASK) != 0) {
final int partialBF = ((levelSize - 1) & BF_MASK) + 1;
if (height == 1) {
newP = new Leaf(gen, partialBF, (Double) full);
}
else {
newP = new Branch(gen, partialBF, (Node) full);
if (partial != null) {
((Branch) newP).set(partialBF - 1, partial);
}
}
assert(partial != null || partialBF < BF);
}
Node newF = null;
if (levelSize > BF || newP == null) {
if (height == 1) {
newF = new Leaf(null, BF, (Double) full);
}
else {
newF = new Branch(null, BF, (Node) full);
}
}
if (levelSize <= BF) {
// we're done
if (newP == null) {
// top level is a full, which isn't duplicated
if (height == 1) {
((Leaf) newF).gen = gen;
}
else {
((Branch) newF).gen = gen;
}
partial = newF;
}
else {
// Top level is a partial. If it uses exactly one
// full child, then we can mark that as unshared.
final Branch b = (Branch) newP;
if (b.length() == 2 && b.get(0) != b.get(1)) {
if (height == 2) {
((Leaf) b.get(0)).gen = gen;
}
else {
((Branch) b.get(0)).gen = gen;
}
}
partial = newP;
}
full = null;
}
else {
partial = newP;
full = newF;
assert(full != null);
}
} while (full != null);
}
_height = height;
_length = length;
_rootRef = new COWMgr(partial);
}
@SuppressWarnings("unchecked")
public SnapDoubleArray clone() {
final SnapDoubleArray copy;
try {
copy = (SnapDoubleArray) super.clone();
}
catch (final CloneNotSupportedException xx) {
throw new Error("unexpected", xx);
}
// height and length are done properly by the magic Cloneable.clone()
copy._rootRef = new COWMgr(_rootRef.frozen().clone());
return copy;
}
public int length() {
return _length;
}
public double get(final int index) {
checkBounds(index);
return readableLeaf(_rootRef.read(), index).getDouble(index & BF_MASK);
}
private void checkBounds(final int index) {
if (index < 0 || index >= _length) {
throw new IndexOutOfBoundsException();
}
}
private Leaf readableLeaf(final Node root, final int index) {
Node cur = root;
for (int h = _height - 1; h >= 1; --h) {
cur = ((Branch) cur).get((index >> (LOG_BF * h)) & BF_MASK);
}
return (Leaf) cur;
}
public void set(final int index, final double newValue) {
checkBounds(index);
final Epoch.Ticket ticket = _rootRef.beginMutation();
try {
mutableLeaf(_rootRef.mutable(), index).setDouble(index & BF_MASK, newValue);
}
finally {
ticket.leave(0);
}
}
public double getAndSet(final int index, final double newValue) {
checkBounds(index);
final Epoch.Ticket ticket = _rootRef.beginMutation();
try {
return mutableLeaf(_rootRef.mutable(), index).getAndSetDouble(index & BF_MASK, newValue);
}
finally {
ticket.leave(0);
}
}
public boolean compareAndSet(final int index, final double expected, final double newValue) {
checkBounds(index);
final Epoch.Ticket ticket = _rootRef.beginMutation();
try {
return mutableLeaf(_rootRef.mutable(), index).compareAndSetDouble(index & BF_MASK, expected, newValue);
}
finally {
ticket.leave(0);
}
}
private Leaf mutableLeaf(final Node root, final int index) {
if (_height <= 1) {
return (Leaf) root;
}
else {
Branch cur = (Branch) root;
final Generation gen = cur.gen;
for (int h = _height - 1; h > 1; --h) {
cur = mutableChildBranch(gen, cur, h, index);
}
return mutableChildLeaf(gen, cur, index);
}
}
private Branch mutableChildBranch(final Generation gen, final Branch cur, final int h, final int index) {
final int i = (index >> (LOG_BF * h)) & BF_MASK;
final Branch child = (Branch) cur.get(i);
if (child.gen == gen) {
// easy case
return child;
}
else {
final Branch repl = new Branch(gen, child);
// reread before CAS
Node newChild = cur.get(i);
if (newChild == child) {
cur.compareAndSet(i, child, repl);
newChild = cur.get(i);
}
return (Branch) newChild;
}
}
private Leaf mutableChildLeaf(final Generation gen, final Branch cur, final int index) {
final int i = (index >> LOG_BF) & BF_MASK;
final Leaf child = (Leaf) cur.get(i);
if (child.gen == gen) {
// easy case
return child;
}
else {
final Leaf repl = new Leaf(gen, child);
// reread before CAS
Node newChild = cur.get(i);
if (newChild == child) {
cur.compareAndSet(i, child, repl);
newChild = cur.get(i);
}
return (Leaf) newChild;
}
}
public Iterator<Double> iterator() {
final Node root = _rootRef.frozen();
return new Iterator<Double>() {
private int _index;
private Leaf _leaf;
public boolean hasNext() {
return _index < _length;
}
@SuppressWarnings("unchecked")
public Double next() {
if ((_index & BF_MASK) == 0) {
_leaf = readableLeaf(root, _index);
}
return _leaf.getDouble(_index++ & BF_MASK);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/** Returns a view of this instance as a fixed-size {@link java.util.List}. Reads
* and writes to the list will be propagated to this instance.
*/
public List<Double> asList() {
return new AbstractList<Double>() {
public Double get(final int index) {
return SnapDoubleArray.this.get(index);
}
@Override
public Double set(final int index, final Double element) {
return SnapDoubleArray.this.getAndSet(index, element);
}
public int size() {
return SnapDoubleArray.this.length();
}
@Override
public Iterator<Double> iterator() {
return SnapDoubleArray.this.iterator();
}
};
}
@Override
public String toString() {
return asList().toString();
}
}