/** * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) * which can be found in the file CPL.TXT 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. **/ using System; using System.Collections; namespace clojure.lang { public class PersistentHashtableMap : IPersistentMap { static readonly float FILL_FACTOR = 0.75f; readonly internal PersistentArray array; readonly int _count; readonly int growAtCount; public PersistentHashtableMap(int initialCapacity) { array = new PersistentArray(calcPrimeCapacity(initialCapacity)); _count = 0; this.growAtCount = (int) (this.array.length()*FILL_FACTOR); } /** * @param init {key1,val1,key2,val2,...} */ public PersistentHashtableMap(Object[] init){ //start halfway to a rehash PersistentArray narray = new PersistentArray(calcPrimeCapacity(init.Length)); for(int i=0;i growAtCount) return grow().add(key, val); int i = bucketFor(key,array); int incr = 1; PersistentArray newArray = doAdd(i, key, val, array); return create(_count + incr, newArray, growAtCount); } public IPersistentMap put(Object key, Object val) { if(_count > growAtCount) return grow().put(key, val); int i = bucketFor(key,array); int incr = 1; PersistentArray newArray = doPut(i, key, val, array); if(newArray == array) return this; if(array.get(i) != null && ((IPersistentMap)newArray.get(i)).count() == ((IPersistentMap)array.get(i)).count()) //key already there, no growth incr = 0; return create(_count + incr, newArray, growAtCount); } PersistentArray doPut(int i,Object key,Object val,PersistentArray array){ IPersistentMap entries = (IPersistentMap) array.get(i); IPersistentMap newEntries; if (entries != null) { newEntries = entries.put(key, val); if(newEntries == entries) //already there with same value, no op return array; } else newEntries = createListMap(key, val); //newEntries = createArrayMap(new Object[]{key, val}); return (PersistentArray)array.set(i, newEntries); } PersistentArray doAdd(int i,Object key,Object val,PersistentArray array) { IPersistentMap entries = (IPersistentMap) array.get(i); IPersistentMap newEntries; if (entries != null) { newEntries = entries.add(key, val); } else newEntries = createListMap(key, val); return (PersistentArray)array.set(i, newEntries); } public IPersistentMap remove(Object key) { int i = bucketFor(key,array); IPersistentMap entries = (IPersistentMap) array.get(i); if (entries != null) { IPersistentMap newEntries = entries.remove(key); if (newEntries != entries) return create(_count - 1, (PersistentArray)array.set(i, newEntries)); } //not there, no op return this; } public Object get(Object key) { IPersistentMap entries = entriesFor(key); if(entries != null) return entries.get(key); return null; } public int capacity() { return array.length(); } IPersistentMap grow(){ PersistentArray newArray = new PersistentArray(calcPrimeCapacity(_count * 2)); foreach (IMapEntry e in this) { newArray = doPut(bucketFor(e.key(),newArray),e.key(), e.val(),newArray); } return create(_count,newArray); } public virtual IEnumerator GetEnumerator() { return new Iter(array); } public ISeq seq() { return Seq.create(array); } class Seq : ISeq{ PersistentArray buckets; int b; ISeq e; static public Seq create(PersistentArray buckets) { return next(buckets, -1, null); } static Seq next(PersistentArray buckets, int b, ISeq e) { if(e != null && e.rest() != null) return new Seq(buckets,b,e.rest()); for(b = b+1;b