Mercurial > lasercutter
view src/clojure/lang/PersistentVector.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 /**2 * Copyright (c) Rich Hickey. All rights reserved.3 * The use and distribution terms for this software are covered by the4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)5 * which can be found in the file epl-v10.html at the root of this distribution.6 * By using this software in any fashion, you are agreeing to be bound by7 * the terms of this license.8 * You must not remove this notice, or any other, from this software.9 **/11 /* rich Jul 5, 2007 */13 package clojure.lang;15 import java.io.Serializable;16 import java.util.List;17 import java.util.concurrent.atomic.AtomicReference;19 public class PersistentVector extends APersistentVector implements IObj, IEditableCollection{21 static class Node implements Serializable {22 transient final AtomicReference<Thread> edit;23 final Object[] array;25 Node(AtomicReference<Thread> edit, Object[] array){26 this.edit = edit;27 this.array = array;28 }30 Node(AtomicReference<Thread> edit){31 this.edit = edit;32 this.array = new Object[32];33 }34 }36 final static AtomicReference<Thread> NOEDIT = new AtomicReference<Thread>(null);37 final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);39 final int cnt;40 final int shift;41 final Node root;42 final Object[] tail;43 final IPersistentMap _meta;46 public final static PersistentVector EMPTY = new PersistentVector(0, 5, EMPTY_NODE, new Object[]{});48 static public PersistentVector create(ISeq items){49 TransientVector ret = EMPTY.asTransient();50 for(; items != null; items = items.next())51 ret = ret.conj(items.first());52 return ret.persistent();53 }55 static public PersistentVector create(List items){56 TransientVector ret = EMPTY.asTransient();57 for(Object item : items)58 ret = ret.conj(item);59 return ret.persistent();60 }62 static public PersistentVector create(Object... items){63 TransientVector ret = EMPTY.asTransient();64 for(Object item : items)65 ret = ret.conj(item);66 return ret.persistent();67 }69 PersistentVector(int cnt, int shift, Node root, Object[] tail){70 this._meta = null;71 this.cnt = cnt;72 this.shift = shift;73 this.root = root;74 this.tail = tail;75 }78 PersistentVector(IPersistentMap meta, int cnt, int shift, Node root, Object[] tail){79 this._meta = meta;80 this.cnt = cnt;81 this.shift = shift;82 this.root = root;83 this.tail = tail;84 }86 public TransientVector asTransient(){87 return new TransientVector(this);88 }90 final int tailoff(){91 if(cnt < 32)92 return 0;93 return ((cnt - 1) >>> 5) << 5;94 }96 public Object[] arrayFor(int i){97 if(i >= 0 && i < cnt)98 {99 if(i >= tailoff())100 return tail;101 Node node = root;102 for(int level = shift; level > 0; level -= 5)103 node = (Node) node.array[(i >>> level) & 0x01f];104 return node.array;105 }106 throw new IndexOutOfBoundsException();107 }109 public Object nth(int i){110 Object[] node = arrayFor(i);111 return node[i & 0x01f];112 }114 public Object nth(int i, Object notFound){115 if(i >= 0 && i < cnt)116 return nth(i);117 return notFound;118 }120 public PersistentVector assocN(int i, Object val){121 if(i >= 0 && i < cnt)122 {123 if(i >= tailoff())124 {125 Object[] newTail = new Object[tail.length];126 System.arraycopy(tail, 0, newTail, 0, tail.length);127 newTail[i & 0x01f] = val;129 return new PersistentVector(meta(), cnt, shift, root, newTail);130 }132 return new PersistentVector(meta(), cnt, shift, doAssoc(shift, root, i, val), tail);133 }134 if(i == cnt)135 return cons(val);136 throw new IndexOutOfBoundsException();137 }139 private static Node doAssoc(int level, Node node, int i, Object val){140 Node ret = new Node(node.edit,node.array.clone());141 if(level == 0)142 {143 ret.array[i & 0x01f] = val;144 }145 else146 {147 int subidx = (i >>> level) & 0x01f;148 ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);149 }150 return ret;151 }153 public int count(){154 return cnt;155 }157 public PersistentVector withMeta(IPersistentMap meta){158 return new PersistentVector(meta, cnt, shift, root, tail);159 }161 public IPersistentMap meta(){162 return _meta;163 }166 public PersistentVector cons(Object val){167 int i = cnt;168 //room in tail?169 // if(tail.length < 32)170 if(cnt - tailoff() < 32)171 {172 Object[] newTail = new Object[tail.length + 1];173 System.arraycopy(tail, 0, newTail, 0, tail.length);174 newTail[tail.length] = val;175 return new PersistentVector(meta(), cnt + 1, shift, root, newTail);176 }177 //full tail, push into tree178 Node newroot;179 Node tailnode = new Node(root.edit,tail);180 int newshift = shift;181 //overflow root?182 if((cnt >>> 5) > (1 << shift))183 {184 newroot = new Node(root.edit);185 newroot.array[0] = root;186 newroot.array[1] = newPath(root.edit,shift, tailnode);187 newshift += 5;188 }189 else190 newroot = pushTail(shift, root, tailnode);191 return new PersistentVector(meta(), cnt + 1, newshift, newroot, new Object[]{val});192 }194 private Node pushTail(int level, Node parent, Node tailnode){195 //if parent is leaf, insert node,196 // else does it map to an existing child? -> nodeToInsert = pushNode one more level197 // else alloc new path198 //return nodeToInsert placed in copy of parent199 int subidx = ((cnt - 1) >>> level) & 0x01f;200 Node ret = new Node(parent.edit, parent.array.clone());201 Node nodeToInsert;202 if(level == 5)203 {204 nodeToInsert = tailnode;205 }206 else207 {208 Node child = (Node) parent.array[subidx];209 nodeToInsert = (child != null)?210 pushTail(level-5,child, tailnode)211 :newPath(root.edit,level-5, tailnode);212 }213 ret.array[subidx] = nodeToInsert;214 return ret;215 }217 private static Node newPath(AtomicReference<Thread> edit,int level, Node node){218 if(level == 0)219 return node;220 Node ret = new Node(edit);221 ret.array[0] = newPath(edit, level - 5, node);222 return ret;223 }225 public IChunkedSeq chunkedSeq(){226 if(count() == 0)227 return null;228 return new ChunkedSeq(this,0,0);229 }231 public ISeq seq(){232 return chunkedSeq();233 }235 static public final class ChunkedSeq extends ASeq implements IChunkedSeq{237 public final PersistentVector vec;238 final Object[] node;239 final int i;240 public final int offset;242 public ChunkedSeq(PersistentVector vec, int i, int offset){243 this.vec = vec;244 this.i = i;245 this.offset = offset;246 this.node = vec.arrayFor(i);247 }249 ChunkedSeq(IPersistentMap meta, PersistentVector vec, Object[] node, int i, int offset){250 super(meta);251 this.vec = vec;252 this.node = node;253 this.i = i;254 this.offset = offset;255 }257 ChunkedSeq(PersistentVector vec, Object[] node, int i, int offset){258 this.vec = vec;259 this.node = node;260 this.i = i;261 this.offset = offset;262 }264 public IChunk chunkedFirst() throws Exception{265 return new ArrayChunk(node, offset);266 }268 public ISeq chunkedNext(){269 if(i + node.length < vec.cnt)270 return new ChunkedSeq(vec,i+ node.length,0);271 return null;272 }274 public ISeq chunkedMore(){275 ISeq s = chunkedNext();276 if(s == null)277 return PersistentList.EMPTY;278 return s;279 }281 public Obj withMeta(IPersistentMap meta){282 if(meta == this._meta)283 return this;284 return new ChunkedSeq(meta, vec, node, i, offset);285 }287 public Object first(){288 return node[offset];289 }291 public ISeq next(){292 if(offset + 1 < node.length)293 return new ChunkedSeq(vec, node, i, offset + 1);294 return chunkedNext();295 }296 }298 public IPersistentCollection empty(){299 return EMPTY.withMeta(meta());300 }302 //private Node pushTail(int level, Node node, Object[] tailNode, Box expansion){303 // Object newchild;304 // if(level == 0)305 // {306 // newchild = tailNode;307 // }308 // else309 // {310 // newchild = pushTail(level - 5, (Object[]) arr[arr.length - 1], tailNode, expansion);311 // if(expansion.val == null)312 // {313 // Object[] ret = arr.clone();314 // ret[arr.length - 1] = newchild;315 // return ret;316 // }317 // else318 // newchild = expansion.val;319 // }320 // //expansion321 // if(arr.length == 32)322 // {323 // expansion.val = new Object[]{newchild};324 // return arr;325 // }326 // Object[] ret = new Object[arr.length + 1];327 // System.arraycopy(arr, 0, ret, 0, arr.length);328 // ret[arr.length] = newchild;329 // expansion.val = null;330 // return ret;331 //}333 public PersistentVector pop(){334 if(cnt == 0)335 throw new IllegalStateException("Can't pop empty vector");336 if(cnt == 1)337 return EMPTY.withMeta(meta());338 //if(tail.length > 1)339 if(cnt-tailoff() > 1)340 {341 Object[] newTail = new Object[tail.length - 1];342 System.arraycopy(tail, 0, newTail, 0, newTail.length);343 return new PersistentVector(meta(), cnt - 1, shift, root, newTail);344 }345 Object[] newtail = arrayFor(cnt - 2);347 Node newroot = popTail(shift, root);348 int newshift = shift;349 if(newroot == null)350 {351 newroot = EMPTY_NODE;352 }353 if(shift > 5 && newroot.array[1] == null)354 {355 newroot = (Node) newroot.array[0];356 newshift -= 5;357 }358 return new PersistentVector(meta(), cnt - 1, newshift, newroot, newtail);359 }361 private Node popTail(int level, Node node){362 int subidx = ((cnt-2) >>> level) & 0x01f;363 if(level > 5)364 {365 Node newchild = popTail(level - 5, (Node) node.array[subidx]);366 if(newchild == null && subidx == 0)367 return null;368 else369 {370 Node ret = new Node(root.edit, node.array.clone());371 ret.array[subidx] = newchild;372 return ret;373 }374 }375 else if(subidx == 0)376 return null;377 else378 {379 Node ret = new Node(root.edit, node.array.clone());380 ret.array[subidx] = null;381 return ret;382 }383 }385 static final class TransientVector extends AFn implements ITransientVector, Counted{386 int cnt;387 int shift;388 Node root;389 Object[] tail;391 TransientVector(int cnt, int shift, Node root, Object[] tail){392 this.cnt = cnt;393 this.shift = shift;394 this.root = root;395 this.tail = tail;396 }398 TransientVector(PersistentVector v){399 this(v.cnt, v.shift, editableRoot(v.root), editableTail(v.tail));400 }402 public int count(){403 ensureEditable();404 return cnt;405 }407 Node ensureEditable(Node node){408 if(node.edit == root.edit)409 return node;410 return new Node(root.edit, node.array.clone());411 }413 void ensureEditable(){414 Thread owner = root.edit.get();415 if(owner == Thread.currentThread())416 return;417 if(owner != null)418 throw new IllegalAccessError("Transient used by non-owner thread");419 throw new IllegalAccessError("Transient used after persistent! call");421 // root = editableRoot(root);422 // tail = editableTail(tail);423 }425 static Node editableRoot(Node node){426 return new Node(new AtomicReference<Thread>(Thread.currentThread()), node.array.clone());427 }429 public PersistentVector persistent(){430 ensureEditable();431 // Thread owner = root.edit.get();432 // if(owner != null && owner != Thread.currentThread())433 // {434 // throw new IllegalAccessError("Mutation release by non-owner thread");435 // }436 root.edit.set(null);437 Object[] trimmedTail = new Object[cnt-tailoff()];438 System.arraycopy(tail,0,trimmedTail,0,trimmedTail.length);439 return new PersistentVector(cnt, shift, root, trimmedTail);440 }442 static Object[] editableTail(Object[] tl){443 Object[] ret = new Object[32];444 System.arraycopy(tl,0,ret,0,tl.length);445 return ret;446 }448 public TransientVector conj(Object val){449 ensureEditable();450 int i = cnt;451 //room in tail?452 if(i - tailoff() < 32)453 {454 tail[i & 0x01f] = val;455 ++cnt;456 return this;457 }458 //full tail, push into tree459 Node newroot;460 Node tailnode = new Node(root.edit, tail);461 tail = new Object[32];462 tail[0] = val;463 int newshift = shift;464 //overflow root?465 if((cnt >>> 5) > (1 << shift))466 {467 newroot = new Node(root.edit);468 newroot.array[0] = root;469 newroot.array[1] = newPath(root.edit,shift, tailnode);470 newshift += 5;471 }472 else473 newroot = pushTail(shift, root, tailnode);474 root = newroot;475 shift = newshift;476 ++cnt;477 return this;478 }480 private Node pushTail(int level, Node parent, Node tailnode){481 //if parent is leaf, insert node,482 // else does it map to an existing child? -> nodeToInsert = pushNode one more level483 // else alloc new path484 //return nodeToInsert placed in parent485 parent = ensureEditable(parent);486 int subidx = ((cnt - 1) >>> level) & 0x01f;487 Node ret = parent;488 Node nodeToInsert;489 if(level == 5)490 {491 nodeToInsert = tailnode;492 }493 else494 {495 Node child = (Node) parent.array[subidx];496 nodeToInsert = (child != null) ?497 pushTail(level - 5, child, tailnode)498 : newPath(root.edit, level - 5, tailnode);499 }500 ret.array[subidx] = nodeToInsert;501 return ret;502 }504 final private int tailoff(){505 if(cnt < 32)506 return 0;507 return ((cnt-1) >>> 5) << 5;508 }510 private Object[] arrayFor(int i){511 if(i >= 0 && i < cnt)512 {513 if(i >= tailoff())514 return tail;515 Node node = root;516 for(int level = shift; level > 0; level -= 5)517 node = (Node) node.array[(i >>> level) & 0x01f];518 return node.array;519 }520 throw new IndexOutOfBoundsException();521 }523 public Object valAt(Object key){524 //note - relies on ensureEditable in 2-arg valAt525 return valAt(key, null);526 }528 public Object valAt(Object key, Object notFound){529 ensureEditable();530 if(Util.isInteger(key))531 {532 int i = ((Number) key).intValue();533 if(i >= 0 && i < cnt)534 return nth(i);535 }536 return notFound;537 }539 public Object invoke(Object arg1) throws Exception{540 //note - relies on ensureEditable in nth541 if(Util.isInteger(arg1))542 return nth(((Number) arg1).intValue());543 throw new IllegalArgumentException("Key must be integer");544 }546 public Object nth(int i){547 ensureEditable();548 Object[] node = arrayFor(i);549 return node[i & 0x01f];550 }552 public Object nth(int i, Object notFound){553 if(i >= 0 && i < count())554 return nth(i);555 return notFound;556 }558 public TransientVector assocN(int i, Object val){559 ensureEditable();560 if(i >= 0 && i < cnt)561 {562 if(i >= tailoff())563 {564 tail[i & 0x01f] = val;565 return this;566 }568 root = doAssoc(shift, root, i, val);569 return this;570 }571 if(i == cnt)572 return conj(val);573 throw new IndexOutOfBoundsException();574 }576 public TransientVector assoc(Object key, Object val){577 //note - relies on ensureEditable in assocN578 if(Util.isInteger(key))579 {580 int i = ((Number) key).intValue();581 return assocN(i, val);582 }583 throw new IllegalArgumentException("Key must be integer");584 }586 private Node doAssoc(int level, Node node, int i, Object val){587 node = ensureEditable(node);588 Node ret = node;589 if(level == 0)590 {591 ret.array[i & 0x01f] = val;592 }593 else594 {595 int subidx = (i >>> level) & 0x01f;596 ret.array[subidx] = doAssoc(level - 5, (Node) node.array[subidx], i, val);597 }598 return ret;599 }601 public TransientVector pop(){602 ensureEditable();603 if(cnt == 0)604 throw new IllegalStateException("Can't pop empty vector");605 if(cnt == 1)606 {607 cnt = 0;608 return this;609 }610 int i = cnt - 1;611 //pop in tail?612 if((i & 0x01f) > 0)613 {614 --cnt;615 return this;616 }618 Object[] newtail = arrayFor(cnt - 2);620 Node newroot = popTail(shift, root);621 int newshift = shift;622 if(newroot == null)623 {624 newroot = new Node(root.edit);625 }626 if(shift > 5 && newroot.array[1] == null)627 {628 newroot = ensureEditable((Node) newroot.array[0]);629 newshift -= 5;630 }631 root = newroot;632 shift = newshift;633 --cnt;634 tail = newtail;635 return this;636 }638 private Node popTail(int level, Node node){639 node = ensureEditable(node);640 int subidx = ((cnt - 2) >>> level) & 0x01f;641 if(level > 5)642 {643 Node newchild = popTail(level - 5, (Node) node.array[subidx]);644 if(newchild == null && subidx == 0)645 return null;646 else647 {648 Node ret = node;649 ret.array[subidx] = newchild;650 return ret;651 }652 }653 else if(subidx == 0)654 return null;655 else656 {657 Node ret = node;658 ret.array[subidx] = null;659 return ret;660 }661 }662 }663 /*664 static public void main(String[] args){665 if(args.length != 3)666 {667 System.err.println("Usage: PersistentVector size writes reads");668 return;669 }670 int size = Integer.parseInt(args[0]);671 int writes = Integer.parseInt(args[1]);672 int reads = Integer.parseInt(args[2]);673 // Vector v = new Vector(size);674 ArrayList v = new ArrayList(size);675 // v.setSize(size);676 //PersistentArray p = new PersistentArray(size);677 PersistentVector p = PersistentVector.EMPTY;678 // MutableVector mp = p.mutable();680 for(int i = 0; i < size; i++)681 {682 v.add(i);683 // v.set(i, i);684 //p = p.set(i, 0);685 p = p.cons(i);686 // mp = mp.conj(i);687 }689 Random rand;691 rand = new Random(42);692 long tv = 0;693 System.out.println("ArrayList");694 long startTime = System.nanoTime();695 for(int i = 0; i < writes; i++)696 {697 v.set(rand.nextInt(size), i);698 }699 for(int i = 0; i < reads; i++)700 {701 tv += (Integer) v.get(rand.nextInt(size));702 }703 long estimatedTime = System.nanoTime() - startTime;704 System.out.println("time: " + estimatedTime / 1000000);705 System.out.println("PersistentVector");706 rand = new Random(42);707 startTime = System.nanoTime();708 long tp = 0;710 // PersistentVector oldp = p;711 //Random rand2 = new Random(42);713 MutableVector mp = p.mutable();714 for(int i = 0; i < writes; i++)715 {716 // p = p.assocN(rand.nextInt(size), i);717 mp = mp.assocN(rand.nextInt(size), i);718 // mp = mp.assoc(rand.nextInt(size), i);719 //dummy set to force perverse branching720 //oldp = oldp.assocN(rand2.nextInt(size), i);721 }722 for(int i = 0; i < reads; i++)723 {724 // tp += (Integer) p.nth(rand.nextInt(size));725 tp += (Integer) mp.nth(rand.nextInt(size));726 }727 // p = mp.immutable();728 //mp.cons(42);729 estimatedTime = System.nanoTime() - startTime;730 System.out.println("time: " + estimatedTime / 1000000);731 for(int i = 0; i < size / 2; i++)732 {733 mp = mp.pop();734 // p = p.pop();735 v.remove(v.size() - 1);736 }737 p = (PersistentVector) mp.immutable();738 //mp.pop(); //should fail739 for(int i = 0; i < size / 2; i++)740 {741 tp += (Integer) p.nth(i);742 tv += (Integer) v.get(i);743 }744 System.out.println("Done: " + tv + ", " + tp);746 }747 // */748 }