package org.simantics.scl.compiler.internal.types; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class HashConsing { private static class Entry extends WeakReference { private final int hash; private Entry next; Entry(T value, ReferenceQueue queue, int hash, Entry next) { super(value, queue); this.hash = hash; this.next = next; } } private double loadFactor = 0.75; @SuppressWarnings("unchecked") private Entry[] table = new Entry[16]; private int size = 0; private int threshold = (int)(table.length * loadFactor); private final ReferenceQueue queue = new ReferenceQueue(); public synchronized T canonical(T candidate) { int h = hashCode(candidate); expungeStaleEntries(); Entry[] tab = table; int i = indexFor(h, tab.length); for (Entry e = tab[i]; e != null; e = e.next) { T cur = e.get(); if (h == e.hash && cur != null && equals(cur, candidate)) return cur; } Entry e = tab[i]; tab[i] = new Entry(candidate, queue, h, e); if (++size >= threshold) resize(tab.length * 2); return candidate; } private void resize(int newCapacity) { expungeStaleEntries(); Entry[] oldTable = table; @SuppressWarnings("unchecked") Entry[] newTable = new Entry[newCapacity]; transfer(oldTable, newTable); table = newTable; if (size >= threshold / 2) { threshold = (int)(newCapacity * loadFactor); } else { expungeStaleEntries(); transfer(newTable, oldTable); table = oldTable; } } private void transfer(Entry[] src, Entry[] dest) { for (int j = 0; j < src.length; ++j) { Entry e = src[j]; src[j] = null; while (e != null) { Entry next = e.next; Object key = e.get(); if (key == null) { e.next = null; size--; } else { int i = indexFor(e.hash, dest.length); e.next = dest[i]; dest[i] = e; } e = next; } } } @SuppressWarnings("unchecked") private void expungeStaleEntries() { Entry e; while ( (e = (Entry) queue.poll()) != null) { int h = e.hash; int i = indexFor(h, table.length); Entry prev = table[i]; Entry p = prev; while (p != null) { Entry next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; e.next = null; size--; break; } prev = p; p = next; } } } private static int indexFor(int h, int length) { return h & (length-1); } protected int hashCode(T obj) { return obj.hashCode(); } protected boolean equals(T a, T b) { return a.equals(b); } }