aoqi@0: /* aoqi@0: * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.util; aoqi@0: aoqi@0: /** aoqi@0: * A hash table that maps Object to int. aoqi@0: * aoqi@0: * This is a custom hash table optimised for the Object -> int aoqi@0: * maps. This is done to avoid unnecessary object allocation in the image set. aoqi@0: * aoqi@0: * @author Charles Turner aoqi@0: * @author Per Bothner aoqi@0: */ aoqi@0: public class IntHashTable { aoqi@0: private static final int DEFAULT_INITIAL_SIZE = 64; aoqi@0: protected Object[] objs; // the domain set aoqi@0: protected int[] ints; // the image set aoqi@0: protected int mask; // used to clip int's into the domain aoqi@0: protected int num_bindings; // the number of mappings (including DELETED) aoqi@0: private final static Object DELETED = new Object(); aoqi@0: aoqi@0: /** aoqi@0: * Construct an Object -> int hash table. aoqi@0: * aoqi@0: * The default size of the hash table is 64 mappings. aoqi@0: */ aoqi@0: public IntHashTable() { aoqi@0: objs = new Object[DEFAULT_INITIAL_SIZE]; aoqi@0: ints = new int[DEFAULT_INITIAL_SIZE]; aoqi@0: mask = DEFAULT_INITIAL_SIZE - 1; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Construct an Object -> int hash table with a specified amount of mappings. aoqi@0: * @param capacity The number of default mappings in this hash table. aoqi@0: */ aoqi@0: public IntHashTable(int capacity) { aoqi@0: int log2Size = 4; aoqi@0: while (capacity > (1 << log2Size)) { aoqi@0: log2Size++; aoqi@0: } aoqi@0: capacity = 1 << log2Size; aoqi@0: objs = new Object[capacity]; aoqi@0: ints = new int[capacity]; aoqi@0: mask = capacity - 1; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Compute the hash code of a given object. aoqi@0: * aoqi@0: * @param key The object whose hash code is to be computed. aoqi@0: * @return zero if the object is null, otherwise the identityHashCode aoqi@0: */ aoqi@0: public int hash(Object key) { aoqi@0: return System.identityHashCode(key); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Find either the index of a key's value, or the index of an available space. aoqi@0: * aoqi@0: * @param key The key to whose value you want to find. aoqi@0: * @param hash The hash code of this key. aoqi@0: * @return Either the index of the key's value, or an index pointing to aoqi@0: * unoccupied space. aoqi@0: */ aoqi@0: public int lookup(Object key, int hash) { aoqi@0: Object node; aoqi@0: int hash1 = hash ^ (hash >>> 15); aoqi@0: int hash2 = (hash ^ (hash << 6)) | 1; //ensure coprimeness aoqi@0: int deleted = -1; aoqi@0: for (int i = hash1 & mask;; i = (i + hash2) & mask) { aoqi@0: node = objs[i]; aoqi@0: if (node == key) aoqi@0: return i; aoqi@0: if (node == null) aoqi@0: return deleted >= 0 ? deleted : i; aoqi@0: if (node == DELETED && deleted < 0) aoqi@0: deleted = i; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Lookup a given key's value in the hash table. aoqi@0: * aoqi@0: * @param key The key whose value you want to find. aoqi@0: * @return Either the index of the key's value, or an index pointing to aoqi@0: * unoccupied space. aoqi@0: */ aoqi@0: public int lookup(Object key) { aoqi@0: return lookup(key, hash(key)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the value stored at the specified index in the table. aoqi@0: * aoqi@0: * @param index The index to inspect, as returned from {@link #lookup} aoqi@0: * @return A non-negative integer if the index contains a non-null aoqi@0: * value, or -1 if it does. aoqi@0: */ aoqi@0: public int getFromIndex(int index) { aoqi@0: Object node = objs[index]; aoqi@0: return node == null || node == DELETED ? -1 : ints[index]; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Associates the specified key with the specified value in this map. aoqi@0: * aoqi@0: * @param key key with which the specified value is to be associated. aoqi@0: * @param value value to be associated with the specified key. aoqi@0: * @param index the index at which to place this binding, as returned aoqi@0: * from {@link #lookup}. aoqi@0: * @return previous value associated with specified key, or -1 if there was aoqi@0: * no mapping for key. aoqi@0: */ aoqi@0: public int putAtIndex(Object key, int value, int index) { aoqi@0: Object old = objs[index]; aoqi@0: if (old == null || old == DELETED) { aoqi@0: objs[index] = key; aoqi@0: ints[index] = value; aoqi@0: if (old != DELETED) aoqi@0: num_bindings++; aoqi@0: if (3 * num_bindings >= 2 * objs.length) aoqi@0: rehash(); aoqi@0: return -1; aoqi@0: } else { // update existing mapping aoqi@0: int oldValue = ints[index]; aoqi@0: ints[index] = value; aoqi@0: return oldValue; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public int remove(Object key) { aoqi@0: int index = lookup(key); aoqi@0: Object old = objs[index]; aoqi@0: if (old == null || old == DELETED) aoqi@0: return -1; aoqi@0: objs[index] = DELETED; aoqi@0: return ints[index]; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Expand the hash table when it exceeds the load factor. aoqi@0: * aoqi@0: * Rehash the existing objects. aoqi@0: */ aoqi@0: protected void rehash() { aoqi@0: Object[] oldObjsTable = objs; aoqi@0: int[] oldIntsTable = ints; aoqi@0: int oldCapacity = oldObjsTable.length; aoqi@0: int newCapacity = oldCapacity << 1; aoqi@0: Object[] newObjTable = new Object[newCapacity]; aoqi@0: int[] newIntTable = new int[newCapacity]; aoqi@0: int newMask = newCapacity - 1; aoqi@0: objs = newObjTable; aoqi@0: ints = newIntTable; aoqi@0: mask = newMask; aoqi@0: num_bindings = 0; // this is recomputed below aoqi@0: Object key; aoqi@0: for (int i = oldIntsTable.length; --i >= 0;) { aoqi@0: key = oldObjsTable[i]; aoqi@0: if (key != null && key != DELETED) aoqi@0: putAtIndex(key, oldIntsTable[i], lookup(key, hash(key))); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Removes all mappings from this map. aoqi@0: */ aoqi@0: public void clear() { aoqi@0: for (int i = objs.length; --i >= 0;) { aoqi@0: objs[i] = null; aoqi@0: } aoqi@0: num_bindings = 0; aoqi@0: } aoqi@0: }