    001package net.minecraft.util;
    002
    003public class LongHashMap
    004{
    005    /** the array of all elements in the hash */
    006    private transient LongHashMapEntry[] hashArray = new LongHashMapEntry[16];
    007
    008    /** the number of elements in the hash array */
    009    private transient int numHashElements;
    010
    011    /**
    012     * the maximum amount of elements in the hash (probably 3/4 the size due to meh hashing function)
    013     */
    014    private int capacity = 12;
    015
    016    /**
    017     * percent of the hasharray that can be used without hash colliding probably
    018     */
    019    private final float percentUseable = 0.75F;
    020
    021    /** count of times elements have been added/removed */
    022    private transient volatile int modCount;
    023
    024    /**
    025     * returns the hashed key given the original key
    026     */
    027    private static int getHashedKey(long par0)
    028    {
    029        return hash((int)(par0 ^ par0 >>> 32));
    030    }
    031
    032    /**
    033     * the hash function
    034     */
    035    private static int hash(int par0)
    036    {
    037        par0 ^= par0 >>> 20 ^ par0 >>> 12;
    038        return par0 ^ par0 >>> 7 ^ par0 >>> 4;
    039    }
    040
    041    /**
    042     * gets the index in the hash given the array length and the hashed key
    043     */
    044    private static int getHashIndex(int par0, int par1)
    045    {
    046        return par0 & par1 - 1;
    047    }
    048
    049    public int getNumHashElements()
    050    {
    051        return this.numHashElements;
    052    }
    053
    054    /**
    055     * get the value from the map given the key
    056     */
    057    public Object getValueByKey(long par1)
    058    {
    059        int j = getHashedKey(par1);
    060
    061        for (LongHashMapEntry longhashmapentry = this.hashArray[getHashIndex(j, this.hashArray.length)]; longhashmapentry != null; longhashmapentry = longhashmapentry.nextEntry)
    062        {
    063            if (longhashmapentry.key == par1)
    064            {
    065                return longhashmapentry.value;
    066            }
    067        }
    068
    069        return null;
    070    }
    071
    072    public boolean containsItem(long par1)
    073    {
    074        return this.getEntry(par1) != null;
    075    }
    076
    077    final LongHashMapEntry getEntry(long par1)
    078    {
    079        int j = getHashedKey(par1);
    080
    081        for (LongHashMapEntry longhashmapentry = this.hashArray[getHashIndex(j, this.hashArray.length)]; longhashmapentry != null; longhashmapentry = longhashmapentry.nextEntry)
    082        {
    083            if (longhashmapentry.key == par1)
    084            {
    085                return longhashmapentry;
    086            }
    087        }
    088
    089        return null;
    090    }
    091
    092    /**
    093     * Add a key-value pair.
    094     */
    095    public void add(long par1, Object par3Obj)
    096    {
    097        int j = getHashedKey(par1);
    098        int k = getHashIndex(j, this.hashArray.length);
    099
    100        for (LongHashMapEntry longhashmapentry = this.hashArray[k]; longhashmapentry != null; longhashmapentry = longhashmapentry.nextEntry)
    101        {
    102            if (longhashmapentry.key == par1)
    103            {
    104                longhashmapentry.value = par3Obj;
    105                return;
    106            }
    107        }
    108
    109        ++this.modCount;
    110        this.createKey(j, par1, par3Obj, k);
    111    }
    112
    113    /**
    114     * resizes the table
    115     */
    116    private void resizeTable(int par1)
    117    {
    118        LongHashMapEntry[] alonghashmapentry = this.hashArray;
    119        int j = alonghashmapentry.length;
    120
    121        if (j == 1073741824)
    122        {
    123            this.capacity = Integer.MAX_VALUE;
    124        }
    125        else
    126        {
    127            LongHashMapEntry[] alonghashmapentry1 = new LongHashMapEntry[par1];
    128            this.copyHashTableTo(alonghashmapentry1);
    129            this.hashArray = alonghashmapentry1;
    130            this.capacity = (int)((float)par1 * this.percentUseable);
    131        }
    132    }
    133
    134    /**
    135     * copies the hash table to the specified array
    136     */
    137    private void copyHashTableTo(LongHashMapEntry[] par1ArrayOfLongHashMapEntry)
    138    {
    139        LongHashMapEntry[] alonghashmapentry1 = this.hashArray;
    140        int i = par1ArrayOfLongHashMapEntry.length;
    141
    142        for (int j = 0; j < alonghashmapentry1.length; ++j)
    143        {
    144            LongHashMapEntry longhashmapentry = alonghashmapentry1[j];
    145
    146            if (longhashmapentry != null)
    147            {
    148                alonghashmapentry1[j] = null;
    149                LongHashMapEntry longhashmapentry1;
    150
    151                do
    152                {
    153                    longhashmapentry1 = longhashmapentry.nextEntry;
    154                    int k = getHashIndex(longhashmapentry.hash, i);
    155                    longhashmapentry.nextEntry = par1ArrayOfLongHashMapEntry[k];
    156                    par1ArrayOfLongHashMapEntry[k] = longhashmapentry;
    157                    longhashmapentry = longhashmapentry1;
    158                }
    159                while (longhashmapentry1 != null);
    160            }
    161        }
    162    }
    163
    164    /**
    165     * calls the removeKey method and returns removed object
    166     */
    167    public Object remove(long par1)
    168    {
    169        LongHashMapEntry longhashmapentry = this.removeKey(par1);
    170        return longhashmapentry == null ? null : longhashmapentry.value;
    171    }
    172
    173    /**
    174     * removes the key from the hash linked list
    175     */
    176    final LongHashMapEntry removeKey(long par1)
    177    {
    178        int j = getHashedKey(par1);
    179        int k = getHashIndex(j, this.hashArray.length);
    180        LongHashMapEntry longhashmapentry = this.hashArray[k];
    181        LongHashMapEntry longhashmapentry1;
    182        LongHashMapEntry longhashmapentry2;
    183
    184        for (longhashmapentry1 = longhashmapentry; longhashmapentry1 != null; longhashmapentry1 = longhashmapentry2)
    185        {
    186            longhashmapentry2 = longhashmapentry1.nextEntry;
    187
    188            if (longhashmapentry1.key == par1)
    189            {
    190                ++this.modCount;
    191                --this.numHashElements;
    192
    193                if (longhashmapentry == longhashmapentry1)
    194                {
    195                    this.hashArray[k] = longhashmapentry2;
    196                }
    197                else
    198                {
    199                    longhashmapentry.nextEntry = longhashmapentry2;
    200                }
    201
    202                return longhashmapentry1;
    203            }
    204
    205            longhashmapentry = longhashmapentry1;
    206        }
    207
    208        return longhashmapentry1;
    209    }
    210
    211    /**
    212     * creates the key in the hash table
    213     */
    214    private void createKey(int par1, long par2, Object par4Obj, int par5)
    215    {
    216        LongHashMapEntry longhashmapentry = this.hashArray[par5];
    217        this.hashArray[par5] = new LongHashMapEntry(par1, par2, par4Obj, longhashmapentry);
    218
    219        if (this.numHashElements++ >= this.capacity)
    220        {
    221            this.resizeTable(2 * this.hashArray.length);
    222        }
    223    }
    224
    225    /**
    226     * public method to get the hashed key(hashCode)
    227     */
    228    static int getHashCode(long par0)
    229    {
    230        return getHashedKey(par0);
    231    }
    232}