]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.procore/src/org/simantics/db/procore/cluster/TableIntHash.java
Fail safe import fixes made by Antti
[simantics/platform.git] / bundles / org.simantics.db.procore / src / org / simantics / db / procore / cluster / TableIntHash.java
index 7d8256b0b3991747bdf26698099ea92f79e8f3e2..286e09a95b062076a40ab66943674d0607e13197 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.db.procore.cluster;\r
-\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.impl.ClusterI.Procedure;\r
-import org.simantics.db.impl.ClusterSupport;\r
-import org.simantics.db.impl.Modifier;\r
-import org.simantics.db.impl.Table;\r
-import org.simantics.db.impl.TableFactory;\r
-import org.simantics.db.impl.TableSizeListener;\r
-\r
-import gnu.trove.impl.PrimeFinder;\r
-\r
-class TableIntHash extends Table<int[]> {\r
-    private static final int INITIAL_SIZE = 1000;\r
-    private static final int ENTRY_SIZE = 3;\r
-    protected static final int HeaderSize = 5;\r
-    private static final int Capacity = -5; // Number of allocated slots.\r
-    private static final int Size = -4; // Number of used slots.\r
-    private static final int Free = -3; // Number of free slots.\r
-    private static final int MaxInsert = -2; // Limit for increasing number of allocated slots.\r
-    private static final int MaxRemove = -1; // Limit for removing removed slots.\r
-    protected int[] ints;\r
-    protected int hashBase;\r
-    TableIntHash(TableSizeListener sizeListener, int[] header, int headerBase) {\r
-        super(TableFactory.getIntFactory(), sizeListener, header, headerBase);\r
-        int capacity = PrimeFinder.nextPrime(INITIAL_SIZE);\r
-        int tableSize = ENTRY_SIZE * capacity + HeaderSize;\r
-        createNewTable(tableSize, tableSize, 1); // size, capacity, count\r
-        ints = getTable();\r
-        hashBase = getTableBase() + HeaderSize;\r
-        capacitySet(capacity);\r
-        sizeSet(0);\r
-        freeSet(capacity);\r
-        computeMaxInsert(capacity);\r
-        computeMaxRemove(maxInsertGet());\r
-    }\r
-    TableIntHash(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {\r
-        super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);\r
-        this.ints = getTable();\r
-        this.hashBase = getTableBase() + HeaderSize;\r
-    }\r
-    static final boolean isFree(int v) {\r
-        return IntHashTrait.isFree(v);\r
-    }\r
-\r
-    static final boolean isFull(int v) {\r
-        return IntHashTrait.isFull(v);\r
-    }\r
-\r
-    final boolean isFullByIndex(int index) {\r
-        return isFull(setGet(index));\r
-    }\r
-\r
-    static final boolean isRemoved(int v) {\r
-        return IntHashTrait.isRemoved(v);\r
-    }\r
-\r
-    static final int setFree() {\r
-        return IntHashTrait.setFree();\r
-    }\r
-\r
-    static final int setFull(int v) {\r
-        return IntHashTrait.setFull(v);\r
-    }\r
-\r
-    static final int setRemoved() {\r
-        return IntHashTrait.setRemoved();\r
-    }\r
-    final int capacityGet() {\r
-        return ints[hashBase + Capacity];\r
-    }\r
-    final void capacitySet(int a) {\r
-        ints[hashBase + Capacity] = a;\r
-    }\r
-    final int sizeGet() {\r
-        return ints[hashBase + Size];\r
-    }\r
-    final void sizeSet(int a) {\r
-        ints[hashBase + Size] = a;\r
-    }\r
-    final int freeGet() {\r
-        return ints[hashBase + Free];\r
-    }\r
-    final void freeSet(int a) {\r
-        ints[hashBase + Free] = a;\r
-    }\r
-    final int maxInsertGet() {\r
-        return ints[hashBase + MaxInsert];\r
-    }\r
-    final void maxInsertSet(int a) {\r
-        ints[hashBase + MaxInsert] = a;\r
-    }\r
-    final int maxRemoveGet() {\r
-        return ints[hashBase + MaxRemove];\r
-    }\r
-    void maxRemoveSet(int a) {\r
-        ints[hashBase + MaxRemove] = a;\r
-    }\r
-    final int setGet(final int index) {\r
-        return ints[hashBase + index * ENTRY_SIZE + 2];\r
-    }\r
-    final void setSet(int index, int value) {\r
-        ints[hashBase + index * ENTRY_SIZE + 2] = value;\r
-    }\r
-    final void setKeyAndValue(int index, int key1, int key2, int value) {\r
-        int realIndex = index * ENTRY_SIZE;\r
-        ints[hashBase + realIndex] = key1;\r
-        ints[hashBase + realIndex + 1] = key2;\r
-        ints[hashBase + realIndex + 2] = value;\r
-    }\r
-    final boolean isFreeByIndex(final int index) {\r
-       return isFree(ints[hashBase + index * ENTRY_SIZE + 2]);\r
-    }\r
-    final boolean isRemovedByIndex(final int index) {\r
-       return isRemoved(ints[hashBase + index * ENTRY_SIZE + 2]);\r
-    }\r
-    final boolean isEqualByIndex(final int index, final int key1, final int key2) {\r
-        int realIndex = index * ENTRY_SIZE;\r
-        return key1 == ints[hashBase + realIndex] && key2 == ints[hashBase + realIndex + 1];\r
-    }\r
-    final void computeMaxInsert(int capacity) {\r
-        // need at least one free slot for open addressing\r
-        int maxSize = Math.min(capacity - 1, capacity >> 1);\r
-        maxInsertSet(maxSize);\r
-        freeSet(capacity - sizeGet()); // reset the free element count\r
-    }\r
-    \r
-    final void computeMaxRemove(int removeCapacity) {\r
-        maxRemoveSet((removeCapacity >> 1) + 1);\r
-    }\r
-    /**\r
-     * retrieves the value for <tt>key</tt>\r
-     * \r
-     * @param key an <code>int</code> value\r
-     * @return the value of <tt>key</tt> or (int)0 if no such mapping exists.\r
-     */\r
-    final int getValue(int key1, int key2) {\r
-        int index = index(key1, key2);\r
-        return index < 0 ?  0 : setGet(index);\r
-    }\r
-    final boolean removeValue(int key1, int key2) {\r
-        int index = index(key1, key2);\r
-        if (index < 0)\r
-            return false;\r
-        setSet(index, setRemoved());\r
-        return true;\r
-        \r
-    }\r
-    final boolean setValue(int key1, int key2, int value) {\r
-       assert(!isFree(value));\r
-       assert(!isRemoved(value));\r
-        int index = insertionIndex(key1, key2);\r
-        boolean added = true;\r
-        boolean isNewMapping = true;\r
-        boolean previousStateWasFree = false;\r
-        if (index < 0) { // old entry\r
-            index = -index - 1;\r
-            setSet(index, value);\r
-            isNewMapping = false;\r
-        } else { // new entry\r
-             if (isFreeByIndex(index))\r
-                 previousStateWasFree = true;\r
-             setKeyAndValue(index, key1, key2, value);\r
-        }\r
-        if (isNewMapping)\r
-            postInsertHook(previousStateWasFree);\r
-        return added;\r
-        \r
-    }\r
-    /**\r
-     * Locates the index of <tt>akey</tt>.\r
-     *\r
-     * @param akey an <code>int</code> value\r
-     * @return the index of <tt>akey</tt> or -1 if it isn't in the set.\r
-     */\r
-    final int index(final int akey1, final int akey2) {\r
-        int hash, probe, index, length;\r
-        //IntArray set = _set;\r
-        length = capacityGet();\r
-        hash = computeHashCode(akey1, akey2);\r
-        index = hash % length;\r
-        if (!isFreeByIndex(index) &&\r
-            (isRemovedByIndex(index) ||\r
-             !isEqualByIndex(index, akey1, akey2))) {\r
-            // see Knuth, p. 529\r
-            probe = 1 + (hash % (length - 2));\r
-            do {\r
-                index -= probe;\r
-                if (index < 0) {\r
-                    index += length;\r
-                }\r
-            } while (!isFreeByIndex(index) &&\r
-                     (isRemovedByIndex(index) ||\r
-                                !isEqualByIndex(index, akey1, akey2)));\r
-        }\r
-\r
-        return isFreeByIndex(index) ? -1 : index;\r
-    }\r
-    /**\r
-     * Locates the index at which <tt>akey</tt> can be inserted.  if\r
-     * there is already a key equaling <tt>akey</tt> in the set,\r
-     * returns that key as a negative integer.\r
-     *\r
-     * @param akey an <code>long</code> value\r
-     * @return an <code>int</code> value\r
-     */\r
-    final int insertionIndex(int akey1, int akey2) {\r
-        int hash, probe, index, length;\r
-        //IntArray set = _set;\r
-        length = capacityGet();\r
-        hash = computeHashCode(akey1, akey2);\r
-        index = hash % length;\r
-        if (isFreeByIndex(index)) {\r
-            return index; // empty, all done\r
-        } else if (isFullByIndex(index) && isEqualByIndex(index, akey1, akey2)) {\r
-            return -index -1; // already stored\r
-        } else { // already FULL or REMOVED, must probe\r
-            // compute the double hash\r
-            probe = 1 + (hash % (length - 2));\r
-\r
-            // if the slot we landed on is FULL (but not removed), probe\r
-            // until we find an empty slot, a REMOVED slot, or an element\r
-            // equal to the one we are trying to insert.\r
-            // finding an empty slot means that the value is not present\r
-            // and that we should use that slot as the insertion point;\r
-            // finding a REMOVED slot means that we need to keep searching,\r
-            // however we want to remember the offset of that REMOVED slot\r
-            // so we can reuse it in case a "new" insertion (i.e. not an update)\r
-            // is possible.\r
-            // finding a matching value means that we've found that our desired\r
-            // key is already in the table\r
-\r
-            if (!isRemovedByIndex(index)) {\r
-                // starting at the natural offset, probe until we find an\r
-                // offset that isn't full.\r
-                do {\r
-                    index -= probe;\r
-                    if (index < 0) {\r
-                        index += length;\r
-                    }\r
-                } while (isFullByIndex(index) && !isEqualByIndex(index, akey1, akey2));\r
-            }\r
-\r
-            // if the index we found was removed: continue probing until we\r
-            // locate a free location or an element which equal()s the\r
-            // one we have.\r
-            if (isRemovedByIndex(index)) {\r
-                int firstRemoved = index;\r
-                while (!isFreeByIndex(index) &&\r
-                       (isRemovedByIndex(index) || !isEqualByIndex(index, akey1, akey2))) {\r
-                    index -= probe;\r
-                    if (index < 0) {\r
-                        index += length;\r
-                    }\r
-                }\r
-                return isFullByIndex(index) ? -index -1 : firstRemoved;\r
-            }\r
-            // if it's full, the key is already stored\r
-            return isFullByIndex(index) ? -index -1 : index;\r
-        }\r
-    }\r
-\r
-    static final int computeHashCode(int key1, int key2) {\r
-        // Multiple by prime to make sure hash can't be negative (see Knuth v3, p. 515-516)\r
-        int hash;\r
-        if (key1 == key2)\r
-            hash = key1 * 31;\r
-        else\r
-            hash = ((int)(key1 ^ key2)) * 31;\r
-        return hash & 0x7fffffff;\r
-    }\r
-    /**\r
-     * After an insert, this hook is called to adjust the size/free\r
-     * values of the set and to perform rehashing if necessary.\r
-     */\r
-    protected final void postInsertHook(boolean usedFreeSlot) {\r
-        if (usedFreeSlot) {\r
-            freeSet(freeGet()-1);\r
-        }\r
-        // rehash whenever we exhaust the available space in the table\r
-        int size = sizeGet() + 1;\r
-        sizeSet(size);\r
-        int maxSize = maxInsertGet();\r
-        int capacity = capacityGet();\r
-        int free = freeGet();\r
-        if (size >  maxSize || free == 0) {\r
-            // choose a new capacity suited to the new state of the table\r
-            // if we've grown beyond our maximum size, double capacity;\r
-            // if we've exhausted the free spots, rehash to the same capacity,\r
-            // which will free up any stale removed slots for reuse.\r
-            int newCapacity = size > maxSize ? PrimeFinder.nextPrime(capacity << 1) : capacity;\r
-            rehash(newCapacity);\r
-        } \r
-    }\r
-    final private void rehash(int newCapacity) {\r
-        int oldCapacity = capacityGet();\r
-        int oldHashBase = hashBase;\r
-        int newTableCapacity = newCapacity*ENTRY_SIZE + HeaderSize;\r
-        int[] oldInts = createNewTable(newTableCapacity, newTableCapacity, 1);\r
-        ints = getTable();\r
-        hashBase = getTableBase() + HeaderSize;\r
-        capacitySet(newCapacity);\r
-        sizeSet(0);\r
-        freeSet(newCapacity);\r
-        computeMaxInsert(newCapacity);\r
-        computeMaxRemove(maxInsertGet());\r
-        for (int i = oldCapacity; i-- > 0;) {\r
-            int realIndex = oldHashBase + i*ENTRY_SIZE;\r
-            if (isFull(oldInts[realIndex+2])) {\r
-                int hashIndex = insertionIndex(oldInts[realIndex], oldInts[realIndex+1]);\r
-                assert(hashIndex >= 0);\r
-                int value = oldInts[realIndex+2];\r
-               assert(!isFree(value));\r
-               assert(!isRemoved(value));\r
-                setKeyAndValue(hashIndex, oldInts[realIndex], oldInts[realIndex+1], value);\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public <Context> boolean foreach(int setIndex, Procedure procedure, Context context, ClusterSupport support, Modifier modifier) throws DatabaseException {\r
-        throw new UnsupportedOperationException();\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.db.procore.cluster;
+
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.impl.ClusterI.Procedure;
+import org.simantics.db.impl.ClusterSupport;
+import org.simantics.db.impl.Modifier;
+import org.simantics.db.impl.Table;
+import org.simantics.db.impl.TableFactory;
+import org.simantics.db.impl.TableSizeListener;
+
+import gnu.trove.impl.PrimeFinder;
+
+class TableIntHash extends Table<int[]> {
+    private static final int INITIAL_SIZE = 1000;
+    private static final int ENTRY_SIZE = 3;
+    protected static final int HeaderSize = 5;
+    private static final int Capacity = -5; // Number of allocated slots.
+    private static final int Size = -4; // Number of used slots.
+    private static final int Free = -3; // Number of free slots.
+    private static final int MaxInsert = -2; // Limit for increasing number of allocated slots.
+    private static final int MaxRemove = -1; // Limit for removing removed slots.
+    protected int[] ints;
+    protected int hashBase;
+    TableIntHash(TableSizeListener sizeListener, int[] header, int headerBase) {
+        super(TableFactory.getIntFactory(), sizeListener, header, headerBase);
+        int capacity = PrimeFinder.nextPrime(INITIAL_SIZE);
+        int tableSize = ENTRY_SIZE * capacity + HeaderSize;
+        createNewTable(tableSize, tableSize, 1); // size, capacity, count
+        ints = getTable();
+        hashBase = getTableBase() + HeaderSize;
+        capacitySet(capacity);
+        sizeSet(0);
+        freeSet(capacity);
+        computeMaxInsert(capacity);
+        computeMaxRemove(maxInsertGet());
+    }
+    TableIntHash(TableSizeListener sizeListener, int[] header, int headerBase, int[] ints) {
+        super(TableFactory.getIntFactory(), sizeListener, header, headerBase, ints);
+        this.ints = getTable();
+        this.hashBase = getTableBase() + HeaderSize;
+    }
+    static final boolean isFree(int v) {
+        return IntHashTrait.isFree(v);
+    }
+
+    static final boolean isFull(int v) {
+        return IntHashTrait.isFull(v);
+    }
+
+    final boolean isFullByIndex(int index) {
+        return isFull(setGet(index));
+    }
+
+    static final boolean isRemoved(int v) {
+        return IntHashTrait.isRemoved(v);
+    }
+
+    static final int setFree() {
+        return IntHashTrait.setFree();
+    }
+
+    static final int setFull(int v) {
+        return IntHashTrait.setFull(v);
+    }
+
+    static final int setRemoved() {
+        return IntHashTrait.setRemoved();
+    }
+    final int capacityGet() {
+        return ints[hashBase + Capacity];
+    }
+    final void capacitySet(int a) {
+        ints[hashBase + Capacity] = a;
+    }
+    final int sizeGet() {
+        return ints[hashBase + Size];
+    }
+    final void sizeSet(int a) {
+        ints[hashBase + Size] = a;
+    }
+    final int freeGet() {
+        return ints[hashBase + Free];
+    }
+    final void freeSet(int a) {
+        ints[hashBase + Free] = a;
+    }
+    final int maxInsertGet() {
+        return ints[hashBase + MaxInsert];
+    }
+    final void maxInsertSet(int a) {
+        ints[hashBase + MaxInsert] = a;
+    }
+    final int maxRemoveGet() {
+        return ints[hashBase + MaxRemove];
+    }
+    void maxRemoveSet(int a) {
+        ints[hashBase + MaxRemove] = a;
+    }
+    final int setGet(final int index) {
+        return ints[hashBase + index * ENTRY_SIZE + 2];
+    }
+    final void setSet(int index, int value) {
+        ints[hashBase + index * ENTRY_SIZE + 2] = value;
+    }
+    final void setKeyAndValue(int index, int key1, int key2, int value) {
+        int realIndex = index * ENTRY_SIZE;
+        ints[hashBase + realIndex] = key1;
+        ints[hashBase + realIndex + 1] = key2;
+        ints[hashBase + realIndex + 2] = value;
+    }
+    final boolean isFreeByIndex(final int index) {
+       return isFree(ints[hashBase + index * ENTRY_SIZE + 2]);
+    }
+    final boolean isRemovedByIndex(final int index) {
+       return isRemoved(ints[hashBase + index * ENTRY_SIZE + 2]);
+    }
+    final boolean isEqualByIndex(final int index, final int key1, final int key2) {
+        int realIndex = index * ENTRY_SIZE;
+        return key1 == ints[hashBase + realIndex] && key2 == ints[hashBase + realIndex + 1];
+    }
+    final void computeMaxInsert(int capacity) {
+        // need at least one free slot for open addressing
+        int maxSize = Math.min(capacity - 1, capacity >> 1);
+        maxInsertSet(maxSize);
+        freeSet(capacity - sizeGet()); // reset the free element count
+    }
+    
+    final void computeMaxRemove(int removeCapacity) {
+        maxRemoveSet((removeCapacity >> 1) + 1);
+    }
+    /**
+     * retrieves the value for <tt>key</tt>
+     * 
+     * @param key an <code>int</code> value
+     * @return the value of <tt>key</tt> or (int)0 if no such mapping exists.
+     */
+    final int getValue(int key1, int key2) {
+        int index = index(key1, key2);
+        return index < 0 ?  0 : setGet(index);
+    }
+    final boolean removeValue(int key1, int key2) {
+        int index = index(key1, key2);
+        if (index < 0)
+            return false;
+        setSet(index, setRemoved());
+        return true;
+        
+    }
+    final boolean setValue(int key1, int key2, int value) {
+       assert(!isFree(value));
+       assert(!isRemoved(value));
+        int index = insertionIndex(key1, key2);
+        boolean added = true;
+        boolean isNewMapping = true;
+        boolean previousStateWasFree = false;
+        if (index < 0) { // old entry
+            index = -index - 1;
+            setSet(index, value);
+            isNewMapping = false;
+        } else { // new entry
+             if (isFreeByIndex(index))
+                 previousStateWasFree = true;
+             setKeyAndValue(index, key1, key2, value);
+        }
+        if (isNewMapping)
+            postInsertHook(previousStateWasFree);
+        return added;
+        
+    }
+    /**
+     * Locates the index of <tt>akey</tt>.
+     *
+     * @param akey an <code>int</code> value
+     * @return the index of <tt>akey</tt> or -1 if it isn't in the set.
+     */
+    final int index(final int akey1, final int akey2) {
+        int hash, probe, index, length;
+        //IntArray set = _set;
+        length = capacityGet();
+        hash = computeHashCode(akey1, akey2);
+        index = hash % length;
+        if (!isFreeByIndex(index) &&
+            (isRemovedByIndex(index) ||
+             !isEqualByIndex(index, akey1, akey2))) {
+            // see Knuth, p. 529
+            probe = 1 + (hash % (length - 2));
+            do {
+                index -= probe;
+                if (index < 0) {
+                    index += length;
+                }
+            } while (!isFreeByIndex(index) &&
+                     (isRemovedByIndex(index) ||
+                                !isEqualByIndex(index, akey1, akey2)));
+        }
+
+        return isFreeByIndex(index) ? -1 : index;
+    }
+    /**
+     * Locates the index at which <tt>akey</tt> can be inserted.  if
+     * there is already a key equaling <tt>akey</tt> in the set,
+     * returns that key as a negative integer.
+     *
+     * @param akey an <code>long</code> value
+     * @return an <code>int</code> value
+     */
+    final int insertionIndex(int akey1, int akey2) {
+        int hash, probe, index, length;
+        //IntArray set = _set;
+        length = capacityGet();
+        hash = computeHashCode(akey1, akey2);
+        index = hash % length;
+        if (isFreeByIndex(index)) {
+            return index; // empty, all done
+        } else if (isFullByIndex(index) && isEqualByIndex(index, akey1, akey2)) {
+            return -index -1; // already stored
+        } else { // already FULL or REMOVED, must probe
+            // compute the double hash
+            probe = 1 + (hash % (length - 2));
+
+            // if the slot we landed on is FULL (but not removed), probe
+            // until we find an empty slot, a REMOVED slot, or an element
+            // equal to the one we are trying to insert.
+            // finding an empty slot means that the value is not present
+            // and that we should use that slot as the insertion point;
+            // finding a REMOVED slot means that we need to keep searching,
+            // however we want to remember the offset of that REMOVED slot
+            // so we can reuse it in case a "new" insertion (i.e. not an update)
+            // is possible.
+            // finding a matching value means that we've found that our desired
+            // key is already in the table
+
+            if (!isRemovedByIndex(index)) {
+                // starting at the natural offset, probe until we find an
+                // offset that isn't full.
+                do {
+                    index -= probe;
+                    if (index < 0) {
+                        index += length;
+                    }
+                } while (isFullByIndex(index) && !isEqualByIndex(index, akey1, akey2));
+            }
+
+            // if the index we found was removed: continue probing until we
+            // locate a free location or an element which equal()s the
+            // one we have.
+            if (isRemovedByIndex(index)) {
+                int firstRemoved = index;
+                while (!isFreeByIndex(index) &&
+                       (isRemovedByIndex(index) || !isEqualByIndex(index, akey1, akey2))) {
+                    index -= probe;
+                    if (index < 0) {
+                        index += length;
+                    }
+                }
+                return isFullByIndex(index) ? -index -1 : firstRemoved;
+            }
+            // if it's full, the key is already stored
+            return isFullByIndex(index) ? -index -1 : index;
+        }
+    }
+
+    static final int computeHashCode(int key1, int key2) {
+        // Multiple by prime to make sure hash can't be negative (see Knuth v3, p. 515-516)
+        int hash;
+        if (key1 == key2)
+            hash = key1 * 31;
+        else
+            hash = ((int)(key1 ^ key2)) * 31;
+        return hash & 0x7fffffff;
+    }
+    /**
+     * After an insert, this hook is called to adjust the size/free
+     * values of the set and to perform rehashing if necessary.
+     */
+    protected final void postInsertHook(boolean usedFreeSlot) {
+        if (usedFreeSlot) {
+            freeSet(freeGet()-1);
+        }
+        // rehash whenever we exhaust the available space in the table
+        int size = sizeGet() + 1;
+        sizeSet(size);
+        int maxSize = maxInsertGet();
+        int capacity = capacityGet();
+        int free = freeGet();
+        if (size >  maxSize || free == 0) {
+            // choose a new capacity suited to the new state of the table
+            // if we've grown beyond our maximum size, double capacity;
+            // if we've exhausted the free spots, rehash to the same capacity,
+            // which will free up any stale removed slots for reuse.
+            int newCapacity = size > maxSize ? PrimeFinder.nextPrime(capacity << 1) : capacity;
+            rehash(newCapacity);
+        } 
+    }
+    final private void rehash(int newCapacity) {
+        int oldCapacity = capacityGet();
+        int oldHashBase = hashBase;
+        int newTableCapacity = newCapacity*ENTRY_SIZE + HeaderSize;
+        int[] oldInts = createNewTable(newTableCapacity, newTableCapacity, 1);
+        ints = getTable();
+        hashBase = getTableBase() + HeaderSize;
+        capacitySet(newCapacity);
+        sizeSet(0);
+        freeSet(newCapacity);
+        computeMaxInsert(newCapacity);
+        computeMaxRemove(maxInsertGet());
+        for (int i = oldCapacity; i-- > 0;) {
+            int realIndex = oldHashBase + i*ENTRY_SIZE;
+            if (isFull(oldInts[realIndex+2])) {
+                int hashIndex = insertionIndex(oldInts[realIndex], oldInts[realIndex+1]);
+                assert(hashIndex >= 0);
+                int value = oldInts[realIndex+2];
+               assert(!isFree(value));
+               assert(!isRemoved(value));
+                setKeyAndValue(hashIndex, oldInts[realIndex], oldInts[realIndex+1], value);
+            }
+        }
+    }
+
+    @Override
+    public <Context> boolean foreach(int setIndex, Procedure procedure, Context context, ClusterSupport support, Modifier modifier) throws DatabaseException {
+        throw new UnsupportedOperationException();
+    }
+
+}