]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.procore/src/org/simantics/db/procore/cluster/TableIntHash.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.db.procore / src / org / simantics / db / procore / cluster / TableIntHash.java
diff --git a/bundles/org.simantics.db.procore/src/org/simantics/db/procore/cluster/TableIntHash.java b/bundles/org.simantics.db.procore/src/org/simantics/db/procore/cluster/TableIntHash.java
new file mode 100644 (file)
index 0000000..7d8256b
--- /dev/null
@@ -0,0 +1,336 @@
+/*******************************************************************************\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