blob: aace1ed0ba3e9202f0552cc6cd014caef669ead1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.util;
import java.util.Arrays;
/**
* Lightweight identity hash table which maps objects to integer handles,
* assigned in ascending order.
*/
public class GridHandleTable {
/** Number of mappings in table/next available handle. */
private int size;
/** Size threshold determining when to expand hash spine. */
private int threshold;
/** Factor for computing size threshold. */
private final float loadFactor;
/** Maps hash value -> candidate handle value. */
private int[] spine;
/** Maps handle value -> next candidate handle value. */
private int[] next;
/** Maps handle value -> associated object. */
private Object[] objs;
/** */
private int[] spineEmpty;
/** */
private int[] nextEmpty;
/**
* Creates new HandleTable with given capacity and load factor.
*
* @param initCap Initial capacity.
* @param loadFactor Load factor.
*/
public GridHandleTable(int initCap, float loadFactor) {
this.loadFactor = loadFactor;
spine = new int[initCap];
next = new int[initCap];
objs = new Object[initCap];
spineEmpty = new int[initCap];
nextEmpty = new int[initCap];
Arrays.fill(spineEmpty, -1);
Arrays.fill(nextEmpty, -1);
threshold = (int)(initCap * loadFactor);
clear();
}
/**
* Looks up and returns handle associated with given object, or -1 if
* no mapping found.
*
* @param obj Object.
* @return Handle.
*/
public int lookup(Object obj) {
int idx = hash(obj) % spine.length;
if (size > 0) {
for (int i = spine[idx]; i >= 0; i = next[i])
if (objs[i] == obj)
return i;
}
if (size >= next.length)
growEntries();
if (size >= threshold) {
growSpine();
idx = hash(obj) % spine.length;
}
insert(obj, size, idx);
size++;
return -1;
}
/**
* Resets table to its initial (empty) state.
*/
public void clear() {
System.arraycopy(spineEmpty, 0, spine, 0, spineEmpty.length);
System.arraycopy(nextEmpty, 0, next, 0, nextEmpty.length);
Arrays.fill(objs, null);
size = 0;
}
/**
* @return Returns objects that were added to handles table.
*/
public Object[] objects() {
return objs;
}
/**
* Inserts mapping object -> handle mapping into table. Assumes table
* is large enough to accommodate new mapping.
*
* @param obj Object.
* @param handle Handle.
* @param idx Index.
*/
private void insert(Object obj, int handle, int idx) {
objs[handle] = obj;
next[handle] = spine[idx];
spine[idx] = handle;
}
/**
* Expands the hash "spine" - equivalent to increasing the number of
* buckets in a conventional hash table.
*/
private void growSpine() {
int size = (spine.length << 1) + 1;
spine = new int[size];
spineEmpty = new int[size];
threshold = (int)(spine.length * loadFactor);
Arrays.fill(spineEmpty, -1);
System.arraycopy(spineEmpty, 0, spine, 0, spineEmpty.length);
for (int i = 0; i < this.size; i++) {
Object obj = objs[i];
int idx = hash(obj) % spine.length;
insert(objs[i], i, idx);
}
}
/**
* Increases hash table capacity by lengthening entry arrays.
*/
private void growEntries() {
int newLen = (next.length << 1) + 1;
int[] newNext = new int[newLen];
System.arraycopy(next, 0, newNext, 0, size);
next = newNext;
nextEmpty = new int[newLen];
Arrays.fill(nextEmpty, -1);
Object[] newObjs = new Object[newLen];
System.arraycopy(objs, 0, newObjs, 0, size);
objs = newObjs;
}
/**
* Returns hash value for given object.
*
* @param obj Object.
* @return Hash value.
*/
private int hash(Object obj) {
return System.identityHashCode(obj) & 0x7FFFFFFF;
}
}