diff --git a/jode/jode/util/Makefile.am b/jode/jode/util/Makefile.am index b8f8d89..c61a05e 100644 --- a/jode/jode/util/Makefile.am +++ b/jode/jode/util/Makefile.am @@ -9,9 +9,10 @@ CLASSLIB = @CLASSLIB@ BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB) MY_JAVA_FILES = \ + ArrayEnum.java \ SimpleMap.java \ SimpleSet.java \ - ArrayEnum.java + UnifyHash.java noinst_DATA = $(MY_JAVA_FILES:.java=.class) EXTRA_DIST = $(MY_JAVA_FILES) diff --git a/jode/jode/util/UnifyHash.java.in b/jode/jode/util/UnifyHash.java.in new file mode 100644 index 0000000..c06ed3b --- /dev/null +++ b/jode/jode/util/UnifyHash.java.in @@ -0,0 +1,240 @@ +package jode.util; +///#ifdef JDK12 +///import java.lang.ref.WeakReference; +///import java.lang.ref.ReferenceQueue; +///#endif + +import @COLLECTIONS@.Comparator; +import @COLLECTIONS@.AbstractCollection; +import @COLLECTIONS@.Iterator; +import @COLLECTIONS@.NoSuchElementException; +import @COLLECTIONS@.ConcurrentModificationException; +import @COLLECTIONEXTRA@.UnsupportedOperationException; + +public class UnifyHash extends AbstractCollection { + /** + * the default capacity + */ + private static final int DEFAULT_CAPACITY = 11; + + /** the default load factor of a HashMap */ + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + +///#ifdef JDK12 +/// private ReferenceQueue queue = new ReferenceQueue(); +///#endif + + static class Bucket +///#ifdef JDK12 +/// extends WeakReference +///#endif + { +///#ifdef JDK12 +/// public Bucket(Object o, ReferenceQueue q) { +/// super(o, q); +/// } +///#else + public Bucket(Object o) { + this.obj = o; + } + + Object obj; + + public Object get() { + return obj; + } +///#endif + + int hash; + Bucket next; + } + + private Bucket[] buckets; + int modCount = 0; + int size = 0; + int threshold; + float loadFactor; + + public UnifyHash(int initialCapacity, float loadFactor) { + this.loadFactor = loadFactor; + buckets = new Bucket[initialCapacity]; + threshold = (int) (loadFactor * initialCapacity); + } + + public UnifyHash(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + public UnifyHash() { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + private void grow() { + Bucket[] oldBuckets = buckets; + int newCap = buckets.length * 2 + 1; + threshold = (int) (loadFactor * newCap); + buckets = new Bucket[newCap]; + for (int i = 0; i < oldBuckets.length; i++) { + Bucket nextBucket; + for (Bucket b = oldBuckets[i]; b != null; b = nextBucket) { + if (i != Math.abs(b.hash % oldBuckets.length)) + throw new RuntimeException(""+i+", hash: "+b.hash+", oldlength: "+oldBuckets.length); + int newSlot = Math.abs(b.hash % newCap); + nextBucket = b.next; + b.next = buckets[newSlot]; + buckets[newSlot] = b; + } + } + } + +///#ifdef JDK12 +/// public final void cleanUp() { +/// Bucket died; +/// while ((died = (Bucket)queue.poll()) != null) { +/// int diedSlot = Math.abs(died.hash % buckets.length); +/// if (buckets[diedSlot] == died) +/// buckets[diedSlot] = died.next; +/// else { +/// Bucket b = buckets[diedSlot] +/// while (b.next != died) +/// b = b.next; +/// b.next = died.next; +/// } +/// size--; +/// } +/// } +///#endif + + + public int size() { + return size; + } + + public Iterator iterator() { +///#ifdef JDK12 +/// cleanUp(); +///#endif + + return new Iterator() { + private int bucket = 0; + private int known = modCount; + private Bucket nextBucket; + private Object nextVal; + + { + internalNext(); + } + + private void internalNext() { + while (true) { + while (nextBucket == null) { + if (bucket == buckets.length) + return; + nextBucket = buckets[bucket++]; + } + + nextVal = nextBucket.get(); + if (nextVal != null) + return; + + nextBucket = nextBucket.next; + } + } + + public boolean hasNext() { + return nextBucket != null; + } + + public Object next() { + if (known != modCount) + throw new ConcurrentModificationException(); + if (nextBucket == null) + throw new NoSuchElementException(); + Object result = nextVal; + nextBucket = nextBucket.next; + internalNext(); + return result; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public Iterator iterateHashCode(final int hash) { + return new Iterator() { + private int known = modCount; + private Bucket nextBucket + = buckets[Math.abs(hash % buckets.length)]; + private Object nextVal; + + { + internalNext(); + } + + private void internalNext() { + while (nextBucket != null) { + if (nextBucket.hash == hash) { + nextVal = nextBucket.get(); + if (nextVal != null) + return; + } + + nextBucket = nextBucket.next; + } + } + + public boolean hasNext() { + return nextBucket != null; + } + + public Object next() { + if (known != modCount) + throw new ConcurrentModificationException(); + if (nextBucket == null) + throw new NoSuchElementException(); + Object result = nextVal; + nextBucket = nextBucket.next; + internalNext(); + return result; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public void put(int hash, Object o) { + if (size++ > threshold) + grow(); + modCount++; + + int slot = Math.abs(hash % buckets.length); +///#ifdef JDK12 +/// Bucket b = new Bucket(o, queue); +///#else + Bucket b = new Bucket(o); +///#endif + b.hash = hash; + b.next = buckets[slot]; + buckets[slot] = b; + } + + public Object unify(Object o, int hash, Comparator comparator) { +///#ifdef JDK12 +/// cleanUp(); +///#endif + int slot = Math.abs(hash % buckets.length); + for (Bucket b = buckets[slot]; b != null; b = b.next) { + Object old = b.get(); + if (old != null && comparator.compare(o, old) == 0) + return old; + } + + put(hash, o); + return o; + } +} +