annotate src/clojure/lang/PersistentHashMap.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
rlm@10 1 /**
rlm@10 2 * Copyright (c) Rich Hickey. All rights reserved.
rlm@10 3 * The use and distribution terms for this software are covered by the
rlm@10 4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
rlm@10 5 * which can be found in the file epl-v10.html at the root of this distribution.
rlm@10 6 * By using this software in any fashion, you are agreeing to be bound by
rlm@10 7 * the terms of this license.
rlm@10 8 * You must not remove this notice, or any other, from this software.
rlm@10 9 **/
rlm@10 10
rlm@10 11 package clojure.lang;
rlm@10 12
rlm@10 13 import java.io.Serializable;
rlm@10 14 import java.util.Iterator;
rlm@10 15 import java.util.List;
rlm@10 16 import java.util.Map;
rlm@10 17 import java.util.concurrent.atomic.AtomicReference;
rlm@10 18
rlm@10 19 /*
rlm@10 20 A persistent rendition of Phil Bagwell's Hash Array Mapped Trie
rlm@10 21
rlm@10 22 Uses path copying for persistence
rlm@10 23 HashCollision leaves vs. extended hashing
rlm@10 24 Node polymorphism vs. conditionals
rlm@10 25 No sub-tree pools or root-resizing
rlm@10 26 Any errors are my own
rlm@10 27 */
rlm@10 28
rlm@10 29 public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj {
rlm@10 30
rlm@10 31 final int count;
rlm@10 32 final INode root;
rlm@10 33 final boolean hasNull;
rlm@10 34 final Object nullValue;
rlm@10 35 final IPersistentMap _meta;
rlm@10 36
rlm@10 37 final public static PersistentHashMap EMPTY = new PersistentHashMap(0, null, false, null);
rlm@10 38 final private static Object NOT_FOUND = new Object();
rlm@10 39
rlm@10 40 static public IPersistentMap create(Map other){
rlm@10 41 ITransientMap ret = EMPTY.asTransient();
rlm@10 42 for(Object o : other.entrySet())
rlm@10 43 {
rlm@10 44 Map.Entry e = (Entry) o;
rlm@10 45 ret = ret.assoc(e.getKey(), e.getValue());
rlm@10 46 }
rlm@10 47 return ret.persistent();
rlm@10 48 }
rlm@10 49
rlm@10 50 /*
rlm@10 51 * @param init {key1,val1,key2,val2,...}
rlm@10 52 */
rlm@10 53 public static PersistentHashMap create(Object... init){
rlm@10 54 ITransientMap ret = EMPTY.asTransient();
rlm@10 55 for(int i = 0; i < init.length; i += 2)
rlm@10 56 {
rlm@10 57 ret = ret.assoc(init[i], init[i + 1]);
rlm@10 58 }
rlm@10 59 return (PersistentHashMap) ret.persistent();
rlm@10 60 }
rlm@10 61
rlm@10 62 public static PersistentHashMap createWithCheck(Object... init){
rlm@10 63 ITransientMap ret = EMPTY.asTransient();
rlm@10 64 for(int i = 0; i < init.length; i += 2)
rlm@10 65 {
rlm@10 66 ret = ret.assoc(init[i], init[i + 1]);
rlm@10 67 if(ret.count() != i/2 + 1)
rlm@10 68 throw new IllegalArgumentException("Duplicate key: " + init[i]);
rlm@10 69 }
rlm@10 70 return (PersistentHashMap) ret.persistent();
rlm@10 71 }
rlm@10 72
rlm@10 73 static public PersistentHashMap create(ISeq items){
rlm@10 74 ITransientMap ret = EMPTY.asTransient();
rlm@10 75 for(; items != null; items = items.next().next())
rlm@10 76 {
rlm@10 77 if(items.next() == null)
rlm@10 78 throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
rlm@10 79 ret = ret.assoc(items.first(), RT.second(items));
rlm@10 80 }
rlm@10 81 return (PersistentHashMap) ret.persistent();
rlm@10 82 }
rlm@10 83
rlm@10 84 static public PersistentHashMap createWithCheck(ISeq items){
rlm@10 85 ITransientMap ret = EMPTY.asTransient();
rlm@10 86 for(int i=0; items != null; items = items.next().next(), ++i)
rlm@10 87 {
rlm@10 88 if(items.next() == null)
rlm@10 89 throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
rlm@10 90 ret = ret.assoc(items.first(), RT.second(items));
rlm@10 91 if(ret.count() != i + 1)
rlm@10 92 throw new IllegalArgumentException("Duplicate key: " + items.first());
rlm@10 93 }
rlm@10 94 return (PersistentHashMap) ret.persistent();
rlm@10 95 }
rlm@10 96
rlm@10 97 /*
rlm@10 98 * @param init {key1,val1,key2,val2,...}
rlm@10 99 */
rlm@10 100 public static PersistentHashMap create(IPersistentMap meta, Object... init){
rlm@10 101 return create(init).withMeta(meta);
rlm@10 102 }
rlm@10 103
rlm@10 104 PersistentHashMap(int count, INode root, boolean hasNull, Object nullValue){
rlm@10 105 this.count = count;
rlm@10 106 this.root = root;
rlm@10 107 this.hasNull = hasNull;
rlm@10 108 this.nullValue = nullValue;
rlm@10 109 this._meta = null;
rlm@10 110 }
rlm@10 111
rlm@10 112 public PersistentHashMap(IPersistentMap meta, int count, INode root, boolean hasNull, Object nullValue){
rlm@10 113 this._meta = meta;
rlm@10 114 this.count = count;
rlm@10 115 this.root = root;
rlm@10 116 this.hasNull = hasNull;
rlm@10 117 this.nullValue = nullValue;
rlm@10 118 }
rlm@10 119
rlm@10 120 public boolean containsKey(Object key){
rlm@10 121 if(key == null)
rlm@10 122 return hasNull;
rlm@10 123 return (root != null) ? root.find(0, Util.hash(key), key, NOT_FOUND) != NOT_FOUND : false;
rlm@10 124 }
rlm@10 125
rlm@10 126 public IMapEntry entryAt(Object key){
rlm@10 127 if(key == null)
rlm@10 128 return hasNull ? new MapEntry(null, nullValue) : null;
rlm@10 129 return (root != null) ? root.find(0, Util.hash(key), key) : null;
rlm@10 130 }
rlm@10 131
rlm@10 132 public IPersistentMap assoc(Object key, Object val){
rlm@10 133 if(key == null) {
rlm@10 134 if(hasNull && val == nullValue)
rlm@10 135 return this;
rlm@10 136 return new PersistentHashMap(meta(), hasNull ? count : count + 1, root, true, val);
rlm@10 137 }
rlm@10 138 Box addedLeaf = new Box(null);
rlm@10 139 INode newroot = (root == null ? BitmapIndexedNode.EMPTY : root)
rlm@10 140 .assoc(0, Util.hash(key), key, val, addedLeaf);
rlm@10 141 if(newroot == root)
rlm@10 142 return this;
rlm@10 143 return new PersistentHashMap(meta(), addedLeaf.val == null ? count : count + 1, newroot, hasNull, nullValue);
rlm@10 144 }
rlm@10 145
rlm@10 146 public Object valAt(Object key, Object notFound){
rlm@10 147 if(key == null)
rlm@10 148 return hasNull ? nullValue : notFound;
rlm@10 149 return root != null ? root.find(0, Util.hash(key), key, notFound) : notFound;
rlm@10 150 }
rlm@10 151
rlm@10 152 public Object valAt(Object key){
rlm@10 153 return valAt(key, null);
rlm@10 154 }
rlm@10 155
rlm@10 156 public IPersistentMap assocEx(Object key, Object val) throws Exception{
rlm@10 157 if(containsKey(key))
rlm@10 158 throw new Exception("Key already present");
rlm@10 159 return assoc(key, val);
rlm@10 160 }
rlm@10 161
rlm@10 162 public IPersistentMap without(Object key){
rlm@10 163 if(key == null)
rlm@10 164 return hasNull ? new PersistentHashMap(meta(), count - 1, root, false, null) : this;
rlm@10 165 if(root == null)
rlm@10 166 return this;
rlm@10 167 INode newroot = root.without(0, Util.hash(key), key);
rlm@10 168 if(newroot == root)
rlm@10 169 return this;
rlm@10 170 return new PersistentHashMap(meta(), count - 1, newroot, hasNull, nullValue);
rlm@10 171 }
rlm@10 172
rlm@10 173 public Iterator iterator(){
rlm@10 174 return new SeqIterator(seq());
rlm@10 175 }
rlm@10 176
rlm@10 177 public int count(){
rlm@10 178 return count;
rlm@10 179 }
rlm@10 180
rlm@10 181 public ISeq seq(){
rlm@10 182 ISeq s = root != null ? root.nodeSeq() : null;
rlm@10 183 return hasNull ? new Cons(new MapEntry(null, nullValue), s) : s;
rlm@10 184 }
rlm@10 185
rlm@10 186 public IPersistentCollection empty(){
rlm@10 187 return EMPTY.withMeta(meta());
rlm@10 188 }
rlm@10 189
rlm@10 190 static int mask(int hash, int shift){
rlm@10 191 //return ((hash << shift) >>> 27);// & 0x01f;
rlm@10 192 return (hash >>> shift) & 0x01f;
rlm@10 193 }
rlm@10 194
rlm@10 195 public PersistentHashMap withMeta(IPersistentMap meta){
rlm@10 196 return new PersistentHashMap(meta, count, root, hasNull, nullValue);
rlm@10 197 }
rlm@10 198
rlm@10 199 public TransientHashMap asTransient() {
rlm@10 200 return new TransientHashMap(this);
rlm@10 201 }
rlm@10 202
rlm@10 203 public IPersistentMap meta(){
rlm@10 204 return _meta;
rlm@10 205 }
rlm@10 206
rlm@10 207 static final class TransientHashMap extends ATransientMap {
rlm@10 208 AtomicReference<Thread> edit;
rlm@10 209 INode root;
rlm@10 210 int count;
rlm@10 211 boolean hasNull;
rlm@10 212 Object nullValue;
rlm@10 213 final Box leafFlag = new Box(null);
rlm@10 214
rlm@10 215
rlm@10 216 TransientHashMap(PersistentHashMap m) {
rlm@10 217 this(new AtomicReference<Thread>(Thread.currentThread()), m.root, m.count, m.hasNull, m.nullValue);
rlm@10 218 }
rlm@10 219
rlm@10 220 TransientHashMap(AtomicReference<Thread> edit, INode root, int count, boolean hasNull, Object nullValue) {
rlm@10 221 this.edit = edit;
rlm@10 222 this.root = root;
rlm@10 223 this.count = count;
rlm@10 224 this.hasNull = hasNull;
rlm@10 225 this.nullValue = nullValue;
rlm@10 226 }
rlm@10 227
rlm@10 228 ITransientMap doAssoc(Object key, Object val) {
rlm@10 229 if (key == null) {
rlm@10 230 if (this.nullValue != val)
rlm@10 231 this.nullValue = val;
rlm@10 232 if (!hasNull) {
rlm@10 233 this.count++;
rlm@10 234 this.hasNull = true;
rlm@10 235 }
rlm@10 236 return this;
rlm@10 237 }
rlm@10 238 // Box leafFlag = new Box(null);
rlm@10 239 leafFlag.val = null;
rlm@10 240 INode n = (root == null ? BitmapIndexedNode.EMPTY : root)
rlm@10 241 .assoc(edit, 0, Util.hash(key), key, val, leafFlag);
rlm@10 242 if (n != this.root)
rlm@10 243 this.root = n;
rlm@10 244 if(leafFlag.val != null) this.count++;
rlm@10 245 return this;
rlm@10 246 }
rlm@10 247
rlm@10 248 ITransientMap doWithout(Object key) {
rlm@10 249 if (key == null) {
rlm@10 250 if (!hasNull) return this;
rlm@10 251 hasNull = false;
rlm@10 252 nullValue = null;
rlm@10 253 this.count--;
rlm@10 254 return this;
rlm@10 255 }
rlm@10 256 if (root == null) return this;
rlm@10 257 // Box leafFlag = new Box(null);
rlm@10 258 leafFlag.val = null;
rlm@10 259 INode n = root.without(edit, 0, Util.hash(key), key, leafFlag);
rlm@10 260 if (n != root)
rlm@10 261 this.root = n;
rlm@10 262 if(leafFlag.val != null) this.count--;
rlm@10 263 return this;
rlm@10 264 }
rlm@10 265
rlm@10 266 IPersistentMap doPersistent() {
rlm@10 267 edit.set(null);
rlm@10 268 return new PersistentHashMap(count, root, hasNull, nullValue);
rlm@10 269 }
rlm@10 270
rlm@10 271 Object doValAt(Object key, Object notFound) {
rlm@10 272 if (key == null)
rlm@10 273 if (hasNull)
rlm@10 274 return nullValue;
rlm@10 275 else
rlm@10 276 return notFound;
rlm@10 277 if (root == null)
rlm@10 278 return null;
rlm@10 279 return root.find(0, Util.hash(key), key, notFound);
rlm@10 280 }
rlm@10 281
rlm@10 282 int doCount() {
rlm@10 283 return count;
rlm@10 284 }
rlm@10 285
rlm@10 286 void ensureEditable(){
rlm@10 287 Thread owner = edit.get();
rlm@10 288 if(owner == Thread.currentThread())
rlm@10 289 return;
rlm@10 290 if(owner != null)
rlm@10 291 throw new IllegalAccessError("Transient used by non-owner thread");
rlm@10 292 throw new IllegalAccessError("Transient used after persistent! call");
rlm@10 293 }
rlm@10 294 }
rlm@10 295
rlm@10 296 static interface INode extends Serializable {
rlm@10 297 INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf);
rlm@10 298
rlm@10 299 INode without(int shift, int hash, Object key);
rlm@10 300
rlm@10 301 IMapEntry find(int shift, int hash, Object key);
rlm@10 302
rlm@10 303 Object find(int shift, int hash, Object key, Object notFound);
rlm@10 304
rlm@10 305 ISeq nodeSeq();
rlm@10 306
rlm@10 307 INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf);
rlm@10 308
rlm@10 309 INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf);
rlm@10 310 }
rlm@10 311
rlm@10 312 final static class ArrayNode implements INode{
rlm@10 313 int count;
rlm@10 314 final INode[] array;
rlm@10 315 final AtomicReference<Thread> edit;
rlm@10 316
rlm@10 317 ArrayNode(AtomicReference<Thread> edit, int count, INode[] array){
rlm@10 318 this.array = array;
rlm@10 319 this.edit = edit;
rlm@10 320 this.count = count;
rlm@10 321 }
rlm@10 322
rlm@10 323 public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 324 int idx = mask(hash, shift);
rlm@10 325 INode node = array[idx];
rlm@10 326 if(node == null)
rlm@10 327 return new ArrayNode(null, count + 1, cloneAndSet(array, idx, BitmapIndexedNode.EMPTY.assoc(shift + 5, hash, key, val, addedLeaf)));
rlm@10 328 INode n = node.assoc(shift + 5, hash, key, val, addedLeaf);
rlm@10 329 if(n == node)
rlm@10 330 return this;
rlm@10 331 return new ArrayNode(null, count, cloneAndSet(array, idx, n));
rlm@10 332 }
rlm@10 333
rlm@10 334 public INode without(int shift, int hash, Object key){
rlm@10 335 int idx = mask(hash, shift);
rlm@10 336 INode node = array[idx];
rlm@10 337 if(node == null)
rlm@10 338 return this;
rlm@10 339 INode n = node.without(shift + 5, hash, key);
rlm@10 340 if(n == node)
rlm@10 341 return this;
rlm@10 342 if (n == null) {
rlm@10 343 if (count <= 8) // shrink
rlm@10 344 return pack(null, idx);
rlm@10 345 return new ArrayNode(null, count - 1, cloneAndSet(array, idx, n));
rlm@10 346 } else
rlm@10 347 return new ArrayNode(null, count, cloneAndSet(array, idx, n));
rlm@10 348 }
rlm@10 349
rlm@10 350 public IMapEntry find(int shift, int hash, Object key){
rlm@10 351 int idx = mask(hash, shift);
rlm@10 352 INode node = array[idx];
rlm@10 353 if(node == null)
rlm@10 354 return null;
rlm@10 355 return node.find(shift + 5, hash, key);
rlm@10 356 }
rlm@10 357
rlm@10 358 public Object find(int shift, int hash, Object key, Object notFound){
rlm@10 359 int idx = mask(hash, shift);
rlm@10 360 INode node = array[idx];
rlm@10 361 if(node == null)
rlm@10 362 return notFound;
rlm@10 363 return node.find(shift + 5, hash, key, notFound);
rlm@10 364 }
rlm@10 365
rlm@10 366 public ISeq nodeSeq(){
rlm@10 367 return Seq.create(array);
rlm@10 368 }
rlm@10 369
rlm@10 370 private ArrayNode ensureEditable(AtomicReference<Thread> edit){
rlm@10 371 if(this.edit == edit)
rlm@10 372 return this;
rlm@10 373 return new ArrayNode(edit, count, this.array.clone());
rlm@10 374 }
rlm@10 375
rlm@10 376 private ArrayNode editAndSet(AtomicReference<Thread> edit, int i, INode n){
rlm@10 377 ArrayNode editable = ensureEditable(edit);
rlm@10 378 editable.array[i] = n;
rlm@10 379 return editable;
rlm@10 380 }
rlm@10 381
rlm@10 382
rlm@10 383 private INode pack(AtomicReference<Thread> edit, int idx) {
rlm@10 384 Object[] newArray = new Object[2*(count - 1)];
rlm@10 385 int j = 1;
rlm@10 386 int bitmap = 0;
rlm@10 387 for(int i = 0; i < idx; i++)
rlm@10 388 if (array[i] != null) {
rlm@10 389 newArray[j] = array[i];
rlm@10 390 bitmap |= 1 << i;
rlm@10 391 j += 2;
rlm@10 392 }
rlm@10 393 for(int i = idx + 1; i < array.length; i++)
rlm@10 394 if (array[i] != null) {
rlm@10 395 newArray[j] = array[i];
rlm@10 396 bitmap |= 1 << i;
rlm@10 397 j += 2;
rlm@10 398 }
rlm@10 399 return new BitmapIndexedNode(edit, bitmap, newArray);
rlm@10 400 }
rlm@10 401
rlm@10 402 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 403 int idx = mask(hash, shift);
rlm@10 404 INode node = array[idx];
rlm@10 405 if(node == null) {
rlm@10 406 ArrayNode editable = editAndSet(edit, idx, BitmapIndexedNode.EMPTY.assoc(edit, shift + 5, hash, key, val, addedLeaf));
rlm@10 407 editable.count++;
rlm@10 408 return editable;
rlm@10 409 }
rlm@10 410 INode n = node.assoc(edit, shift + 5, hash, key, val, addedLeaf);
rlm@10 411 if(n == node)
rlm@10 412 return this;
rlm@10 413 return editAndSet(edit, idx, n);
rlm@10 414 }
rlm@10 415
rlm@10 416 public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){
rlm@10 417 int idx = mask(hash, shift);
rlm@10 418 INode node = array[idx];
rlm@10 419 if(node == null)
rlm@10 420 return this;
rlm@10 421 INode n = node.without(edit, shift + 5, hash, key, removedLeaf);
rlm@10 422 if(n == node)
rlm@10 423 return this;
rlm@10 424 if(n == null) {
rlm@10 425 if (count <= 8) // shrink
rlm@10 426 return pack(edit, idx);
rlm@10 427 ArrayNode editable = editAndSet(edit, idx, n);
rlm@10 428 editable.count--;
rlm@10 429 return editable;
rlm@10 430 }
rlm@10 431 return editAndSet(edit, idx, n);
rlm@10 432 }
rlm@10 433
rlm@10 434 static class Seq extends ASeq {
rlm@10 435 final INode[] nodes;
rlm@10 436 final int i;
rlm@10 437 final ISeq s;
rlm@10 438
rlm@10 439 static ISeq create(INode[] nodes) {
rlm@10 440 return create(null, nodes, 0, null);
rlm@10 441 }
rlm@10 442
rlm@10 443 private static ISeq create(IPersistentMap meta, INode[] nodes, int i, ISeq s) {
rlm@10 444 if (s != null)
rlm@10 445 return new Seq(meta, nodes, i, s);
rlm@10 446 for(int j = i; j < nodes.length; j++)
rlm@10 447 if (nodes[j] != null) {
rlm@10 448 ISeq ns = nodes[j].nodeSeq();
rlm@10 449 if (ns != null)
rlm@10 450 return new Seq(meta, nodes, j + 1, ns);
rlm@10 451 }
rlm@10 452 return null;
rlm@10 453 }
rlm@10 454
rlm@10 455 private Seq(IPersistentMap meta, INode[] nodes, int i, ISeq s) {
rlm@10 456 super(meta);
rlm@10 457 this.nodes = nodes;
rlm@10 458 this.i = i;
rlm@10 459 this.s = s;
rlm@10 460 }
rlm@10 461
rlm@10 462 public Obj withMeta(IPersistentMap meta) {
rlm@10 463 return new Seq(meta, nodes, i, s);
rlm@10 464 }
rlm@10 465
rlm@10 466 public Object first() {
rlm@10 467 return s.first();
rlm@10 468 }
rlm@10 469
rlm@10 470 public ISeq next() {
rlm@10 471 return create(null, nodes, i, s.next());
rlm@10 472 }
rlm@10 473
rlm@10 474 }
rlm@10 475 }
rlm@10 476
rlm@10 477 final static class BitmapIndexedNode implements INode{
rlm@10 478 static final BitmapIndexedNode EMPTY = new BitmapIndexedNode(null, 0, new Object[0]);
rlm@10 479
rlm@10 480 int bitmap;
rlm@10 481 Object[] array;
rlm@10 482 final AtomicReference<Thread> edit;
rlm@10 483
rlm@10 484 final int index(int bit){
rlm@10 485 return Integer.bitCount(bitmap & (bit - 1));
rlm@10 486 }
rlm@10 487
rlm@10 488 BitmapIndexedNode(AtomicReference<Thread> edit, int bitmap, Object[] array){
rlm@10 489 this.bitmap = bitmap;
rlm@10 490 this.array = array;
rlm@10 491 this.edit = edit;
rlm@10 492 }
rlm@10 493
rlm@10 494 public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 495 int bit = bitpos(hash, shift);
rlm@10 496 int idx = index(bit);
rlm@10 497 if((bitmap & bit) != 0) {
rlm@10 498 Object keyOrNull = array[2*idx];
rlm@10 499 Object valOrNode = array[2*idx+1];
rlm@10 500 if(keyOrNull == null) {
rlm@10 501 INode n = ((INode) valOrNode).assoc(shift + 5, hash, key, val, addedLeaf);
rlm@10 502 if(n == valOrNode)
rlm@10 503 return this;
rlm@10 504 return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, n));
rlm@10 505 }
rlm@10 506 if(Util.equals(key, keyOrNull)) {
rlm@10 507 if(val == valOrNode)
rlm@10 508 return this;
rlm@10 509 return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, val));
rlm@10 510 }
rlm@10 511 addedLeaf.val = addedLeaf;
rlm@10 512 return new BitmapIndexedNode(null, bitmap,
rlm@10 513 cloneAndSet(array,
rlm@10 514 2*idx, null,
rlm@10 515 2*idx+1, createNode(shift + 5, keyOrNull, valOrNode, hash, key, val)));
rlm@10 516 } else {
rlm@10 517 int n = Integer.bitCount(bitmap);
rlm@10 518 if(n >= 16) {
rlm@10 519 INode[] nodes = new INode[32];
rlm@10 520 int jdx = mask(hash, shift);
rlm@10 521 nodes[jdx] = EMPTY.assoc(shift + 5, hash, key, val, addedLeaf);
rlm@10 522 int j = 0;
rlm@10 523 for(int i = 0; i < 32; i++)
rlm@10 524 if(((bitmap >>> i) & 1) != 0) {
rlm@10 525 if (array[j] == null)
rlm@10 526 nodes[i] = (INode) array[j+1];
rlm@10 527 else
rlm@10 528 nodes[i] = EMPTY.assoc(shift + 5, Util.hash(array[j]), array[j], array[j+1], addedLeaf);
rlm@10 529 j += 2;
rlm@10 530 }
rlm@10 531 return new ArrayNode(null, n + 1, nodes);
rlm@10 532 } else {
rlm@10 533 Object[] newArray = new Object[2*(n+1)];
rlm@10 534 System.arraycopy(array, 0, newArray, 0, 2*idx);
rlm@10 535 newArray[2*idx] = key;
rlm@10 536 addedLeaf.val = addedLeaf;
rlm@10 537 newArray[2*idx+1] = val;
rlm@10 538 System.arraycopy(array, 2*idx, newArray, 2*(idx+1), 2*(n-idx));
rlm@10 539 return new BitmapIndexedNode(null, bitmap | bit, newArray);
rlm@10 540 }
rlm@10 541 }
rlm@10 542 }
rlm@10 543
rlm@10 544 public INode without(int shift, int hash, Object key){
rlm@10 545 int bit = bitpos(hash, shift);
rlm@10 546 if((bitmap & bit) == 0)
rlm@10 547 return this;
rlm@10 548 int idx = index(bit);
rlm@10 549 Object keyOrNull = array[2*idx];
rlm@10 550 Object valOrNode = array[2*idx+1];
rlm@10 551 if(keyOrNull == null) {
rlm@10 552 INode n = ((INode) valOrNode).without(shift + 5, hash, key);
rlm@10 553 if (n == valOrNode)
rlm@10 554 return this;
rlm@10 555 if (n != null)
rlm@10 556 return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, n));
rlm@10 557 if (bitmap == bit)
rlm@10 558 return null;
rlm@10 559 return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx));
rlm@10 560 }
rlm@10 561 if(Util.equals(key, keyOrNull))
rlm@10 562 // TODO: collapse
rlm@10 563 return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx));
rlm@10 564 return this;
rlm@10 565 }
rlm@10 566
rlm@10 567 public IMapEntry find(int shift, int hash, Object key){
rlm@10 568 int bit = bitpos(hash, shift);
rlm@10 569 if((bitmap & bit) == 0)
rlm@10 570 return null;
rlm@10 571 int idx = index(bit);
rlm@10 572 Object keyOrNull = array[2*idx];
rlm@10 573 Object valOrNode = array[2*idx+1];
rlm@10 574 if(keyOrNull == null)
rlm@10 575 return ((INode) valOrNode).find(shift + 5, hash, key);
rlm@10 576 if(Util.equals(key, keyOrNull))
rlm@10 577 return new MapEntry(keyOrNull, valOrNode);
rlm@10 578 return null;
rlm@10 579 }
rlm@10 580
rlm@10 581 public Object find(int shift, int hash, Object key, Object notFound){
rlm@10 582 int bit = bitpos(hash, shift);
rlm@10 583 if((bitmap & bit) == 0)
rlm@10 584 return notFound;
rlm@10 585 int idx = index(bit);
rlm@10 586 Object keyOrNull = array[2*idx];
rlm@10 587 Object valOrNode = array[2*idx+1];
rlm@10 588 if(keyOrNull == null)
rlm@10 589 return ((INode) valOrNode).find(shift + 5, hash, key, notFound);
rlm@10 590 if(Util.equals(key, keyOrNull))
rlm@10 591 return valOrNode;
rlm@10 592 return notFound;
rlm@10 593 }
rlm@10 594
rlm@10 595 public ISeq nodeSeq(){
rlm@10 596 return NodeSeq.create(array);
rlm@10 597 }
rlm@10 598
rlm@10 599 private BitmapIndexedNode ensureEditable(AtomicReference<Thread> edit){
rlm@10 600 if(this.edit == edit)
rlm@10 601 return this;
rlm@10 602 int n = Integer.bitCount(bitmap);
rlm@10 603 Object[] newArray = new Object[n >= 0 ? 2*(n+1) : 4]; // make room for next assoc
rlm@10 604 System.arraycopy(array, 0, newArray, 0, 2*n);
rlm@10 605 return new BitmapIndexedNode(edit, bitmap, newArray);
rlm@10 606 }
rlm@10 607
rlm@10 608 private BitmapIndexedNode editAndSet(AtomicReference<Thread> edit, int i, Object a) {
rlm@10 609 BitmapIndexedNode editable = ensureEditable(edit);
rlm@10 610 editable.array[i] = a;
rlm@10 611 return editable;
rlm@10 612 }
rlm@10 613
rlm@10 614 private BitmapIndexedNode editAndSet(AtomicReference<Thread> edit, int i, Object a, int j, Object b) {
rlm@10 615 BitmapIndexedNode editable = ensureEditable(edit);
rlm@10 616 editable.array[i] = a;
rlm@10 617 editable.array[j] = b;
rlm@10 618 return editable;
rlm@10 619 }
rlm@10 620
rlm@10 621 private BitmapIndexedNode editAndRemovePair(AtomicReference<Thread> edit, int bit, int i) {
rlm@10 622 if (bitmap == bit)
rlm@10 623 return null;
rlm@10 624 BitmapIndexedNode editable = ensureEditable(edit);
rlm@10 625 editable.bitmap ^= bit;
rlm@10 626 System.arraycopy(editable.array, 2*(i+1), editable.array, 2*i, editable.array.length - 2*(i+1));
rlm@10 627 editable.array[editable.array.length - 2] = null;
rlm@10 628 editable.array[editable.array.length - 1] = null;
rlm@10 629 return editable;
rlm@10 630 }
rlm@10 631
rlm@10 632 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 633 int bit = bitpos(hash, shift);
rlm@10 634 int idx = index(bit);
rlm@10 635 if((bitmap & bit) != 0) {
rlm@10 636 Object keyOrNull = array[2*idx];
rlm@10 637 Object valOrNode = array[2*idx+1];
rlm@10 638 if(keyOrNull == null) {
rlm@10 639 INode n = ((INode) valOrNode).assoc(edit, shift + 5, hash, key, val, addedLeaf);
rlm@10 640 if(n == valOrNode)
rlm@10 641 return this;
rlm@10 642 return editAndSet(edit, 2*idx+1, n);
rlm@10 643 }
rlm@10 644 if(Util.equals(key, keyOrNull)) {
rlm@10 645 if(val == valOrNode)
rlm@10 646 return this;
rlm@10 647 return editAndSet(edit, 2*idx+1, val);
rlm@10 648 }
rlm@10 649 addedLeaf.val = addedLeaf;
rlm@10 650 return editAndSet(edit, 2*idx, null, 2*idx+1,
rlm@10 651 createNode(edit, shift + 5, keyOrNull, valOrNode, hash, key, val));
rlm@10 652 } else {
rlm@10 653 int n = Integer.bitCount(bitmap);
rlm@10 654 if(n*2 < array.length) {
rlm@10 655 addedLeaf.val = addedLeaf;
rlm@10 656 BitmapIndexedNode editable = ensureEditable(edit);
rlm@10 657 System.arraycopy(editable.array, 2*idx, editable.array, 2*(idx+1), 2*(n-idx));
rlm@10 658 editable.array[2*idx] = key;
rlm@10 659 editable.array[2*idx+1] = val;
rlm@10 660 editable.bitmap |= bit;
rlm@10 661 return editable;
rlm@10 662 }
rlm@10 663 if(n >= 16) {
rlm@10 664 INode[] nodes = new INode[32];
rlm@10 665 int jdx = mask(hash, shift);
rlm@10 666 nodes[jdx] = EMPTY.assoc(edit, shift + 5, hash, key, val, addedLeaf);
rlm@10 667 int j = 0;
rlm@10 668 for(int i = 0; i < 32; i++)
rlm@10 669 if(((bitmap >>> i) & 1) != 0) {
rlm@10 670 if (array[j] == null)
rlm@10 671 nodes[i] = (INode) array[j+1];
rlm@10 672 else
rlm@10 673 nodes[i] = EMPTY.assoc(edit, shift + 5, Util.hash(array[j]), array[j], array[j+1], addedLeaf);
rlm@10 674 j += 2;
rlm@10 675 }
rlm@10 676 return new ArrayNode(edit, n + 1, nodes);
rlm@10 677 } else {
rlm@10 678 Object[] newArray = new Object[2*(n+4)];
rlm@10 679 System.arraycopy(array, 0, newArray, 0, 2*idx);
rlm@10 680 newArray[2*idx] = key;
rlm@10 681 addedLeaf.val = addedLeaf;
rlm@10 682 newArray[2*idx+1] = val;
rlm@10 683 System.arraycopy(array, 2*idx, newArray, 2*(idx+1), 2*(n-idx));
rlm@10 684 BitmapIndexedNode editable = ensureEditable(edit);
rlm@10 685 editable.array = newArray;
rlm@10 686 editable.bitmap |= bit;
rlm@10 687 return editable;
rlm@10 688 }
rlm@10 689 }
rlm@10 690 }
rlm@10 691
rlm@10 692 public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){
rlm@10 693 int bit = bitpos(hash, shift);
rlm@10 694 if((bitmap & bit) == 0)
rlm@10 695 return this;
rlm@10 696 int idx = index(bit);
rlm@10 697 Object keyOrNull = array[2*idx];
rlm@10 698 Object valOrNode = array[2*idx+1];
rlm@10 699 if(keyOrNull == null) {
rlm@10 700 INode n = ((INode) valOrNode).without(edit, shift + 5, hash, key, removedLeaf);
rlm@10 701 if (n == valOrNode)
rlm@10 702 return this;
rlm@10 703 if (n != null)
rlm@10 704 return editAndSet(edit, 2*idx+1, n);
rlm@10 705 if (bitmap == bit)
rlm@10 706 return null;
rlm@10 707 removedLeaf.val = removedLeaf;
rlm@10 708 return editAndRemovePair(edit, bit, idx);
rlm@10 709 }
rlm@10 710 if(Util.equals(key, keyOrNull)) {
rlm@10 711 removedLeaf.val = removedLeaf;
rlm@10 712 // TODO: collapse
rlm@10 713 return editAndRemovePair(edit, bit, idx);
rlm@10 714 }
rlm@10 715 return this;
rlm@10 716 }
rlm@10 717 }
rlm@10 718
rlm@10 719 final static class HashCollisionNode implements INode{
rlm@10 720
rlm@10 721 final int hash;
rlm@10 722 int count;
rlm@10 723 Object[] array;
rlm@10 724 final AtomicReference<Thread> edit;
rlm@10 725
rlm@10 726 HashCollisionNode(AtomicReference<Thread> edit, int hash, int count, Object... array){
rlm@10 727 this.edit = edit;
rlm@10 728 this.hash = hash;
rlm@10 729 this.count = count;
rlm@10 730 this.array = array;
rlm@10 731 }
rlm@10 732
rlm@10 733 public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 734 if(hash == this.hash) {
rlm@10 735 int idx = findIndex(key);
rlm@10 736 if(idx != -1) {
rlm@10 737 if(array[idx + 1] == val)
rlm@10 738 return this;
rlm@10 739 return new HashCollisionNode(null, hash, count, cloneAndSet(array, idx + 1, val));
rlm@10 740 }
rlm@10 741 Object[] newArray = new Object[array.length + 2];
rlm@10 742 System.arraycopy(array, 0, newArray, 0, array.length);
rlm@10 743 newArray[array.length] = key;
rlm@10 744 newArray[array.length + 1] = val;
rlm@10 745 addedLeaf.val = addedLeaf;
rlm@10 746 return new HashCollisionNode(edit, hash, count + 1, newArray);
rlm@10 747 }
rlm@10 748 // nest it in a bitmap node
rlm@10 749 return new BitmapIndexedNode(null, bitpos(this.hash, shift), new Object[] {null, this})
rlm@10 750 .assoc(shift, hash, key, val, addedLeaf);
rlm@10 751 }
rlm@10 752
rlm@10 753 public INode without(int shift, int hash, Object key){
rlm@10 754 int idx = findIndex(key);
rlm@10 755 if(idx == -1)
rlm@10 756 return this;
rlm@10 757 if(count == 1)
rlm@10 758 return null;
rlm@10 759 return new HashCollisionNode(null, hash, count - 1, removePair(array, idx/2));
rlm@10 760 }
rlm@10 761
rlm@10 762 public IMapEntry find(int shift, int hash, Object key){
rlm@10 763 int idx = findIndex(key);
rlm@10 764 if(idx < 0)
rlm@10 765 return null;
rlm@10 766 if(Util.equals(key, array[idx]))
rlm@10 767 return new MapEntry(array[idx], array[idx+1]);
rlm@10 768 return null;
rlm@10 769 }
rlm@10 770
rlm@10 771 public Object find(int shift, int hash, Object key, Object notFound){
rlm@10 772 int idx = findIndex(key);
rlm@10 773 if(idx < 0)
rlm@10 774 return notFound;
rlm@10 775 if(Util.equals(key, array[idx]))
rlm@10 776 return array[idx+1];
rlm@10 777 return notFound;
rlm@10 778 }
rlm@10 779
rlm@10 780 public ISeq nodeSeq(){
rlm@10 781 return NodeSeq.create(array);
rlm@10 782 }
rlm@10 783
rlm@10 784 public int findIndex(Object key){
rlm@10 785 for(int i = 0; i < 2*count; i+=2)
rlm@10 786 {
rlm@10 787 if(Util.equals(key, array[i]))
rlm@10 788 return i;
rlm@10 789 }
rlm@10 790 return -1;
rlm@10 791 }
rlm@10 792
rlm@10 793 private HashCollisionNode ensureEditable(AtomicReference<Thread> edit){
rlm@10 794 if(this.edit == edit)
rlm@10 795 return this;
rlm@10 796 return new HashCollisionNode(edit, hash, count, array);
rlm@10 797 }
rlm@10 798
rlm@10 799 private HashCollisionNode ensureEditable(AtomicReference<Thread> edit, int count, Object[] array){
rlm@10 800 if(this.edit == edit) {
rlm@10 801 this.array = array;
rlm@10 802 this.count = count;
rlm@10 803 return this;
rlm@10 804 }
rlm@10 805 return new HashCollisionNode(edit, hash, count, array);
rlm@10 806 }
rlm@10 807
rlm@10 808 private HashCollisionNode editAndSet(AtomicReference<Thread> edit, int i, Object a) {
rlm@10 809 HashCollisionNode editable = ensureEditable(edit);
rlm@10 810 editable.array[i] = a;
rlm@10 811 return editable;
rlm@10 812 }
rlm@10 813
rlm@10 814 private HashCollisionNode editAndSet(AtomicReference<Thread> edit, int i, Object a, int j, Object b) {
rlm@10 815 HashCollisionNode editable = ensureEditable(edit);
rlm@10 816 editable.array[i] = a;
rlm@10 817 editable.array[j] = b;
rlm@10 818 return editable;
rlm@10 819 }
rlm@10 820
rlm@10 821
rlm@10 822 public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){
rlm@10 823 if(hash == this.hash) {
rlm@10 824 int idx = findIndex(key);
rlm@10 825 if(idx != -1) {
rlm@10 826 if(array[idx + 1] == val)
rlm@10 827 return this;
rlm@10 828 return editAndSet(edit, idx+1, val);
rlm@10 829 }
rlm@10 830 if (array.length > 2*count) {
rlm@10 831 addedLeaf.val = addedLeaf;
rlm@10 832 HashCollisionNode editable = editAndSet(edit, 2*count, key, 2*count+1, val);
rlm@10 833 editable.count++;
rlm@10 834 return editable;
rlm@10 835 }
rlm@10 836 Object[] newArray = new Object[array.length + 2];
rlm@10 837 System.arraycopy(array, 0, newArray, 0, array.length);
rlm@10 838 newArray[array.length] = key;
rlm@10 839 newArray[array.length + 1] = val;
rlm@10 840 addedLeaf.val = addedLeaf;
rlm@10 841 return ensureEditable(edit, count + 1, newArray);
rlm@10 842 }
rlm@10 843 // nest it in a bitmap node
rlm@10 844 return new BitmapIndexedNode(edit, bitpos(this.hash, shift), new Object[] {null, this, null, null})
rlm@10 845 .assoc(edit, shift, hash, key, val, addedLeaf);
rlm@10 846 }
rlm@10 847
rlm@10 848 public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){
rlm@10 849 int idx = findIndex(key);
rlm@10 850 if(idx == -1)
rlm@10 851 return this;
rlm@10 852 if(count == 1)
rlm@10 853 return null;
rlm@10 854 HashCollisionNode editable = ensureEditable(edit);
rlm@10 855 editable.array[idx] = editable.array[2*count-2];
rlm@10 856 editable.array[idx+1] = editable.array[2*count-1];
rlm@10 857 editable.array[2*count-2] = editable.array[2*count-1] = null;
rlm@10 858 editable.count--;
rlm@10 859 return editable;
rlm@10 860 }
rlm@10 861 }
rlm@10 862
rlm@10 863 /*
rlm@10 864 public static void main(String[] args){
rlm@10 865 try
rlm@10 866 {
rlm@10 867 ArrayList words = new ArrayList();
rlm@10 868 Scanner s = new Scanner(new File(args[0]));
rlm@10 869 s.useDelimiter(Pattern.compile("\\W"));
rlm@10 870 while(s.hasNext())
rlm@10 871 {
rlm@10 872 String word = s.next();
rlm@10 873 words.add(word);
rlm@10 874 }
rlm@10 875 System.out.println("words: " + words.size());
rlm@10 876 IPersistentMap map = PersistentHashMap.EMPTY;
rlm@10 877 //IPersistentMap map = new PersistentTreeMap();
rlm@10 878 //Map ht = new Hashtable();
rlm@10 879 Map ht = new HashMap();
rlm@10 880 Random rand;
rlm@10 881
rlm@10 882 System.out.println("Building map");
rlm@10 883 long startTime = System.nanoTime();
rlm@10 884 for(Object word5 : words)
rlm@10 885 {
rlm@10 886 map = map.assoc(word5, word5);
rlm@10 887 }
rlm@10 888 rand = new Random(42);
rlm@10 889 IPersistentMap snapshotMap = map;
rlm@10 890 for(int i = 0; i < words.size() / 200; i++)
rlm@10 891 {
rlm@10 892 map = map.without(words.get(rand.nextInt(words.size() / 2)));
rlm@10 893 }
rlm@10 894 long estimatedTime = System.nanoTime() - startTime;
rlm@10 895 System.out.println("count = " + map.count() + ", time: " + estimatedTime / 1000000);
rlm@10 896
rlm@10 897 System.out.println("Building ht");
rlm@10 898 startTime = System.nanoTime();
rlm@10 899 for(Object word1 : words)
rlm@10 900 {
rlm@10 901 ht.put(word1, word1);
rlm@10 902 }
rlm@10 903 rand = new Random(42);
rlm@10 904 for(int i = 0; i < words.size() / 200; i++)
rlm@10 905 {
rlm@10 906 ht.remove(words.get(rand.nextInt(words.size() / 2)));
rlm@10 907 }
rlm@10 908 estimatedTime = System.nanoTime() - startTime;
rlm@10 909 System.out.println("count = " + ht.size() + ", time: " + estimatedTime / 1000000);
rlm@10 910
rlm@10 911 System.out.println("map lookup");
rlm@10 912 startTime = System.nanoTime();
rlm@10 913 int c = 0;
rlm@10 914 for(Object word2 : words)
rlm@10 915 {
rlm@10 916 if(!map.contains(word2))
rlm@10 917 ++c;
rlm@10 918 }
rlm@10 919 estimatedTime = System.nanoTime() - startTime;
rlm@10 920 System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
rlm@10 921 System.out.println("ht lookup");
rlm@10 922 startTime = System.nanoTime();
rlm@10 923 c = 0;
rlm@10 924 for(Object word3 : words)
rlm@10 925 {
rlm@10 926 if(!ht.containsKey(word3))
rlm@10 927 ++c;
rlm@10 928 }
rlm@10 929 estimatedTime = System.nanoTime() - startTime;
rlm@10 930 System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
rlm@10 931 System.out.println("snapshotMap lookup");
rlm@10 932 startTime = System.nanoTime();
rlm@10 933 c = 0;
rlm@10 934 for(Object word4 : words)
rlm@10 935 {
rlm@10 936 if(!snapshotMap.contains(word4))
rlm@10 937 ++c;
rlm@10 938 }
rlm@10 939 estimatedTime = System.nanoTime() - startTime;
rlm@10 940 System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000);
rlm@10 941 }
rlm@10 942 catch(FileNotFoundException e)
rlm@10 943 {
rlm@10 944 e.printStackTrace();
rlm@10 945 }
rlm@10 946
rlm@10 947 }
rlm@10 948 */
rlm@10 949
rlm@10 950 private static INode[] cloneAndSet(INode[] array, int i, INode a) {
rlm@10 951 INode[] clone = array.clone();
rlm@10 952 clone[i] = a;
rlm@10 953 return clone;
rlm@10 954 }
rlm@10 955
rlm@10 956 private static Object[] cloneAndSet(Object[] array, int i, Object a) {
rlm@10 957 Object[] clone = array.clone();
rlm@10 958 clone[i] = a;
rlm@10 959 return clone;
rlm@10 960 }
rlm@10 961
rlm@10 962 private static Object[] cloneAndSet(Object[] array, int i, Object a, int j, Object b) {
rlm@10 963 Object[] clone = array.clone();
rlm@10 964 clone[i] = a;
rlm@10 965 clone[j] = b;
rlm@10 966 return clone;
rlm@10 967 }
rlm@10 968
rlm@10 969 private static Object[] removePair(Object[] array, int i) {
rlm@10 970 Object[] newArray = new Object[array.length - 2];
rlm@10 971 System.arraycopy(array, 0, newArray, 0, 2*i);
rlm@10 972 System.arraycopy(array, 2*(i+1), newArray, 2*i, newArray.length - 2*i);
rlm@10 973 return newArray;
rlm@10 974 }
rlm@10 975
rlm@10 976 private static INode createNode(int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
rlm@10 977 int key1hash = Util.hash(key1);
rlm@10 978 if(key1hash == key2hash)
rlm@10 979 return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
rlm@10 980 Box _ = new Box(null);
rlm@10 981 AtomicReference<Thread> edit = new AtomicReference<Thread>();
rlm@10 982 return BitmapIndexedNode.EMPTY
rlm@10 983 .assoc(edit, shift, key1hash, key1, val1, _)
rlm@10 984 .assoc(edit, shift, key2hash, key2, val2, _);
rlm@10 985 }
rlm@10 986
rlm@10 987 private static INode createNode(AtomicReference<Thread> edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
rlm@10 988 int key1hash = Util.hash(key1);
rlm@10 989 if(key1hash == key2hash)
rlm@10 990 return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
rlm@10 991 Box _ = new Box(null);
rlm@10 992 return BitmapIndexedNode.EMPTY
rlm@10 993 .assoc(edit, shift, key1hash, key1, val1, _)
rlm@10 994 .assoc(edit, shift, key2hash, key2, val2, _);
rlm@10 995 }
rlm@10 996
rlm@10 997 private static int bitpos(int hash, int shift){
rlm@10 998 return 1 << mask(hash, shift);
rlm@10 999 }
rlm@10 1000
rlm@10 1001 static final class NodeSeq extends ASeq {
rlm@10 1002 final Object[] array;
rlm@10 1003 final int i;
rlm@10 1004 final ISeq s;
rlm@10 1005
rlm@10 1006 NodeSeq(Object[] array, int i) {
rlm@10 1007 this(null, array, i, null);
rlm@10 1008 }
rlm@10 1009
rlm@10 1010 static ISeq create(Object[] array) {
rlm@10 1011 return create(array, 0, null);
rlm@10 1012 }
rlm@10 1013
rlm@10 1014 private static ISeq create(Object[] array, int i, ISeq s) {
rlm@10 1015 if(s != null)
rlm@10 1016 return new NodeSeq(null, array, i, s);
rlm@10 1017 for(int j = i; j < array.length; j+=2) {
rlm@10 1018 if(array[j] != null)
rlm@10 1019 return new NodeSeq(null, array, j, null);
rlm@10 1020 INode node = (INode) array[j+1];
rlm@10 1021 if (node != null) {
rlm@10 1022 ISeq nodeSeq = node.nodeSeq();
rlm@10 1023 if(nodeSeq != null)
rlm@10 1024 return new NodeSeq(null, array, j + 2, nodeSeq);
rlm@10 1025 }
rlm@10 1026 }
rlm@10 1027 return null;
rlm@10 1028 }
rlm@10 1029
rlm@10 1030 NodeSeq(IPersistentMap meta, Object[] array, int i, ISeq s) {
rlm@10 1031 super(meta);
rlm@10 1032 this.array = array;
rlm@10 1033 this.i = i;
rlm@10 1034 this.s = s;
rlm@10 1035 }
rlm@10 1036
rlm@10 1037 public Obj withMeta(IPersistentMap meta) {
rlm@10 1038 return new NodeSeq(meta, array, i, s);
rlm@10 1039 }
rlm@10 1040
rlm@10 1041 public Object first() {
rlm@10 1042 if(s != null)
rlm@10 1043 return s.first();
rlm@10 1044 return new MapEntry(array[i], array[i+1]);
rlm@10 1045 }
rlm@10 1046
rlm@10 1047 public ISeq next() {
rlm@10 1048 if(s != null)
rlm@10 1049 return create(array, i, s.next());
rlm@10 1050 return create(array, i + 2, null);
rlm@10 1051 }
rlm@10 1052 }
rlm@10 1053
rlm@10 1054 }