/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 5, 2007 */
package clojure.lang;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class PersistentVector extends APersistentVector implements IEditableCollection{
static class Node{
final AtomicReference<Thread> edit;
final Object[] array;
Node(AtomicReference<Thread> edit, Object[] array){
this.edit = edit;
this.array = array;
}
Node(AtomicReference<Thread> edit){
this.edit = edit;
this.array = new Object[32];
}
}
final static AtomicReference<Thread> NOEDIT = new AtomicReference<Thread>(null);
final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);
final int cnt;
final int shift;
final Node root;
final Object[] tail;
public final static PersistentVector EMPTY = new PersistentVector(0, 5, EMPTY_NODE, new Object[]{});
static public PersistentVector create(ISeq items){
TransientVector ret = EMPTY.asTransient();
for(; items != null; items = items.next())
ret = ret.conj(items.first());
return ret.persistent();
}
static public PersistentVector create(List items){
TransientVector ret = EMPTY.asTransient();
for(Object item : items)
ret = ret.conj(item);
return ret.persistent();
}
static public PersistentVector create(Object... items){
TransientVector ret = EMPTY.asTransient();
for(Object item : items)
ret = ret.conj(item);
return ret.persistent();
}
PersistentVector(int cnt, int shift, Node root, Object[] tail){
super(null);
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
PersistentVector(IPersistentMap meta, int cnt, int shift, Node root, Object[] tail){
super(meta);
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
public TransientVector asTransient(){
return new TransientVector(this);
}
final int tailoff(){
if(cnt < 32)
return 0;
return ((cnt - 1) >>> 5) << 5;
}
public Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
public Object nth(int i){
Object[] node = arrayFor(i);
return node[i & 0x01f];
}
public PersistentVector assocN(int i, Object val){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
Object[] newTail = new Object[tail.length];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[i & 0x01f] = val;
return new PersistentVector(meta(), cnt, shift, root, newTail);
}
return new PersistentVector(meta(), cnt, shift, doAssoc(shift, root, i, val), tail);
}
if(i == cnt)
return cons(val);
throw new IndexOutOfBoundsException();
}
private static Node doAssoc(int level, Node node, int i, Object val){
Node ret = new Node(node.edit,node.array.clone());
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public int count(){
return cnt;
}
public PersistentVector withMeta(IPersistentMap meta){
return new PersistentVector(meta, cnt, shift, root, tail);
}
public PersistentVector cons(Object val){
int i = cnt;
//room in tail?
// if(tail.length < 32)
if(cnt - tailoff() < 32)
{
Object[] newTail = new Object[tail.length + 1];
System.arraycopy(tail, 0, newTail, 0, tail.length);
newTail[tail.length] = val;
return new PersistentVector(meta(), cnt + 1, shift, root, newTail);
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit,tail);
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
return new PersistentVector(meta(), cnt + 1, newshift, newroot, new Object[]{val});
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in copy of parent
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = new Node(parent.edit, parent.array.clone());
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null)?
pushTail(level-5,child, tailnode)
:newPath(root.edit,level-5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
private static Node newPath(AtomicReference<Thread> edit,int level, Node node){
if(level == 0)
return node;
Node ret = new Node(edit);
ret.array[0] = newPath(edit, level - 5, node);
return ret;
}
public IChunkedSeq chunkedSeq(){
if(count() == 0)
return null;
return new ChunkedSeq(this,0,0);
}
public ISeq seq(){
return chunkedSeq();
}
static public final class ChunkedSeq extends ASeq implements IChunkedSeq{
final PersistentVector vec;
final Object[] node;
final int i;
final int offset;
public ChunkedSeq(PersistentVector vec, int i, int offset){
this.vec = vec;
this.i = i;
this.offset = offset;
this.node = vec.arrayFor(i);
}
ChunkedSeq(IPersistentMap meta, PersistentVector vec, Object[] node, int i, int offset){
super(meta);
this.vec = vec;
this.node = node;
this.i = i;
this.offset = offset;
}
ChunkedSeq(PersistentVector vec, Object[] node, int i, int offset){
this.vec = vec;
this.node = node;
this.i = i;
this.offset = offset;
}
public IChunk chunkedFirst() throws Exception{
return new ArrayChunk(node, offset);
}
public ISeq chunkedNext(){
if(i + node.length < vec.cnt)
return new ChunkedSeq(vec,i+ node.length,0);
return null;
}
public ISeq chunkedMore(){
ISeq s = chunkedNext();
if(s == null)
return PersistentList.EMPTY;
return s;
}
public Obj withMeta(IPersistentMap meta){
if(meta == this._meta)
return this;
return new ChunkedSeq(meta, vec, node, i, offset);
}
public Object first(){
return node[offset];
}
public ISeq next(){
if(offset + 1 < node.length)
return new ChunkedSeq(vec, node, i, offset + 1);
return chunkedNext();
}
}
public IPersistentCollection empty(){
return EMPTY.withMeta(meta());
}
//private Node pushTail(int level, Node node, Object[] tailNode, Box expansion){
// Object newchild;
// if(level == 0)
// {
// newchild = tailNode;
// }
// else
// {
// newchild = pushTail(level - 5, (Object[]) arr[arr.length - 1], tailNode, expansion);
// if(expansion.val == null)
// {
// Object[] ret = arr.clone();
// ret[arr.length - 1] = newchild;
// return ret;
// }
// else
// newchild = expansion.val;
// }
// //expansion
// if(arr.length == 32)
// {
// expansion.val = new Object[]{newchild};
// return arr;
// }
// Object[] ret = new Object[arr.length + 1];
// System.arraycopy(arr, 0, ret, 0, arr.length);
// ret[arr.length] = newchild;
// expansion.val = null;
// return ret;
//}
public PersistentVector pop(){
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
if(cnt == 1)
return EMPTY.withMeta(meta());
//if(tail.length > 1)
if(cnt-tailoff() > 1)
{
Object[] newTail = new Object[tail.length - 1];
System.arraycopy(tail, 0, newTail, 0, newTail.length);
return new PersistentVector(meta(), cnt - 1, shift, root, newTail);
}
Object[] newtail = arrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = EMPTY_NODE;
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = (Node) newroot.array[0];
newshift -= 5;
}
return new PersistentVector(meta(), cnt - 1, newshift, newroot, newtail);
}
private Node popTail(int level, Node node){
int subidx = ((cnt-2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = new Node(root.edit, node.array.clone());
ret.array[subidx] = null;
return ret;
}
}
static final class TransientVector extends AFn implements ITransientVector, Counted{
int cnt;
int shift;
Node root;
Object[] tail;
TransientVector(int cnt, int shift, Node root, Object[] tail){
this.cnt = cnt;
this.shift = shift;
this.root = root;
this.tail = tail;
}
TransientVector(PersistentVector v){
this(v.cnt, v.shift, editableRoot(v.root), editableTail(v.tail));
}
public int count(){
ensureEditable();
return cnt;
}
Node ensureEditable(Node node){
if(node.edit == root.edit)
return node;
return new Node(root.edit, node.array.clone());
}
void ensureEditable(){
Thread owner = root.edit.get();
if(owner == Thread.currentThread())
return;
if(owner != null)
throw new IllegalAccessError("Transient used by non-owner thread");
throw new IllegalAccessError("Transient used after persistent! call");
// root = editableRoot(root);
// tail = editableTail(tail);
}
static Node editableRoot(Node node){
return new Node(new AtomicReference<Thread>(Thread.currentThread()), node.array.clone());
}
public PersistentVector persistent(){
ensureEditable();
// Thread owner = root.edit.get();
// if(owner != null && owner != Thread.currentThread())
// {
// throw new IllegalAccessError("Mutation release by non-owner thread");
// }
root.edit.set(null);
Object[] trimmedTail = new Object[cnt-tailoff()];
System.arraycopy(tail,0,trimmedTail,0,trimmedTail.length);
return new PersistentVector(cnt, shift, root, trimmedTail);
}
static Object[] editableTail(Object[] tl){
Object[] ret = new Object[32];
System.arraycopy(tl,0,ret,0,tl.length);
return ret;
}
public TransientVector conj(Object val){
ensureEditable();
int i = cnt;
//room in tail?
if(i - tailoff() < 32)
{
tail[i & 0x01f] = val;
++cnt;
return this;
}
//full tail, push into tree
Node newroot;
Node tailnode = new Node(root.edit, tail);
tail = new Object[32];
tail[0] = val;
int newshift = shift;
//overflow root?
if((cnt >>> 5) > (1 << shift))
{
newroot = new Node(root.edit);
newroot.array[0] = root;
newroot.array[1] = newPath(root.edit,shift, tailnode);
newshift += 5;
}
else
newroot = pushTail(shift, root, tailnode);
root = newroot;
shift = newshift;
++cnt;
return this;
}
private Node pushTail(int level, Node parent, Node tailnode){
//if parent is leaf, insert node,
// else does it map to an existing child? -> nodeToInsert = pushNode one more level
// else alloc new path
//return nodeToInsert placed in parent
parent = ensureEditable(parent);
int subidx = ((cnt - 1) >>> level) & 0x01f;
Node ret = parent;
Node nodeToInsert;
if(level == 5)
{
nodeToInsert = tailnode;
}
else
{
Node child = (Node) parent.array[subidx];
nodeToInsert = (child != null) ?
pushTail(level - 5, child, tailnode)
: newPath(root.edit, level - 5, tailnode);
}
ret.array[subidx] = nodeToInsert;
return ret;
}
final private int tailoff(){
if(cnt < 32)
return 0;
return ((cnt-1) >>> 5) << 5;
}
private Object[] arrayFor(int i){
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
return tail;
Node node = root;
for(int level = shift; level > 0; level -= 5)
node = (Node) node.array[(i >>> level) & 0x01f];
return node.array;
}
throw new IndexOutOfBoundsException();
}
public Object valAt(Object key){
//note - relies on ensureEditable in 2-arg valAt
return valAt(key, null);
}
public Object valAt(Object key, Object notFound){
ensureEditable();
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < cnt)
return nth(i);
}
return notFound;
}
public Object invoke(Object arg1) throws Exception{
//note - relies on ensureEditable in nth
if(Util.isInteger(arg1))
return nth(((Number) arg1).intValue());
throw new IllegalArgumentException("Key must be integer");
}
public Object nth(int i){
ensureEditable();
Object[] node = arrayFor(i);
return node[i & 0x01f];
}
public TransientVector assocN(int i, Object val){
ensureEditable();
if(i >= 0 && i < cnt)
{
if(i >= tailoff())
{
tail[i & 0x01f] = val;
return this;
}
root = doAssoc(shift, root, i, val);
return this;
}
if(i == cnt)
return conj(val);
throw new IndexOutOfBoundsException();
}
public TransientVector assoc(Object key, Object val){
//note - relies on ensureEditable in assocN
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
return assocN(i, val);
}
throw new IllegalArgumentException("Key must be integer");
}
private Node doAssoc(int level, Node node, int i, Object val){
node = ensureEditable(node);
Node ret = node;
if(level == 0)
{
ret.array[i & 0x01f] = val;
}
else
{
int subidx = (i >>> level) & 0x01f;
ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);
}
return ret;
}
public TransientVector pop(){
ensureEditable();
if(cnt == 0)
throw new IllegalStateException("Can't pop empty vector");
if(cnt == 1)
{
cnt = 0;
return this;
}
int i = cnt - 1;
//pop in tail?
if((i & 0x01f) > 0)
{
--cnt;
return this;
}
Object[] newtail = arrayFor(cnt - 2);
Node newroot = popTail(shift, root);
int newshift = shift;
if(newroot == null)
{
newroot = EMPTY_NODE;
}
if(shift > 5 && newroot.array[1] == null)
{
newroot = (Node) newroot.array[0];
newshift -= 5;
}
root = newroot;
shift = newshift;
--cnt;
tail = newtail;
return this;
}
private Node popTail(int level, Node node){
node = ensureEditable(node);
int subidx = ((cnt - 2) >>> level) & 0x01f;
if(level > 5)
{
Node newchild = popTail(level - 5, (Node) node.array[subidx]);
if(newchild == null && subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = newchild;
return ret;
}
}
else if(subidx == 0)
return null;
else
{
Node ret = node;
ret.array[subidx] = null;
return ret;
}
}
}
/*
static public void main(String[] args){
if(args.length != 3)
{
System.err.println("Usage: PersistentVector size writes reads");
return;
}
int size = Integer.parseInt(args[0]);
int writes = Integer.parseInt(args[1]);
int reads = Integer.parseInt(args[2]);
// Vector v = new Vector(size);
ArrayList v = new ArrayList(size);
// v.setSize(size);
//PersistentArray p = new PersistentArray(size);
PersistentVector p = PersistentVector.EMPTY;
// MutableVector mp = p.mutable();
for(int i = 0; i < size; i++)
{
v.add(i);
// v.set(i, i);
//p = p.set(i, 0);
p = p.cons(i);
// mp = mp.conj(i);
}
Random rand;
rand = new Random(42);
long tv = 0;
System.out.println("ArrayList");
long startTime = System.nanoTime();
for(int i = 0; i < writes; i++)
{
v.set(rand.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
tv += (Integer) v.get(rand.nextInt(size));
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
System.out.println("PersistentVector");
rand = new Random(42);
startTime = System.nanoTime();
long tp = 0;
// PersistentVector oldp = p;
//Random rand2 = new Random(42);
MutableVector mp = p.mutable();
for(int i = 0; i < writes; i++)
{
// p = p.assocN(rand.nextInt(size), i);
mp = mp.assocN(rand.nextInt(size), i);
// mp = mp.assoc(rand.nextInt(size), i);
//dummy set to force perverse branching
//oldp = oldp.assocN(rand2.nextInt(size), i);
}
for(int i = 0; i < reads; i++)
{
// tp += (Integer) p.nth(rand.nextInt(size));
tp += (Integer) mp.nth(rand.nextInt(size));
}
// p = mp.immutable();
//mp.cons(42);
estimatedTime = System.nanoTime() - startTime;
System.out.println("time: " + estimatedTime / 1000000);
for(int i = 0; i < size / 2; i++)
{
mp = mp.pop();
// p = p.pop();
v.remove(v.size() - 1);
}
p = (PersistentVector) mp.immutable();
//mp.pop(); //should fail
for(int i = 0; i < size / 2; i++)
{
tp += (Integer) p.nth(i);
tv += (Integer) v.get(i);
}
System.out.println("Done: " + tv + ", " + tp);
}
// */
}