    001package net.minecraft.util;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.HashSet;
    006import java.util.Set;
    007
    008public class IntHashMap
    009{
    010    /** An array of HashEntries representing the heads of hash slot lists */
    011    private transient IntHashMapEntry[] slots = new IntHashMapEntry[16];
    012
    013    /** The number of items stored in this map */
    014    private transient int count;
    015
    016    /** The grow threshold */
    017    private int threshold = 12;
    018
    019    /** The scale factor used to determine when to grow the table */
    020    private final float growFactor = 0.75F;
    021
    022    /** A serial stamp used to mark changes */
    023    private transient volatile int versionStamp;
    024
    025    /** The set of all the keys stored in this MCHash object */
    026    private Set keySet = new HashSet();
    027
    028    /**
    029     * Makes the passed in integer suitable for hashing by a number of shifts
    030     */
    031    private static int computeHash(int par0)
    032    {
    033        par0 ^= par0 >>> 20 ^ par0 >>> 12;
    034        return par0 ^ par0 >>> 7 ^ par0 >>> 4;
    035    }
    036
    037    /**
    038     * Computes the index of the slot for the hash and slot count passed in.
    039     */
    040    private static int getSlotIndex(int par0, int par1)
    041    {
    042        return par0 & par1 - 1;
    043    }
    044
    045    /**
    046     * Returns the object associated to a key
    047     */
    048    public Object lookup(int par1)
    049    {
    050        int j = computeHash(par1);
    051
    052        for (IntHashMapEntry inthashmapentry = this.slots[getSlotIndex(j, this.slots.length)]; inthashmapentry != null; inthashmapentry = inthashmapentry.nextEntry)
    053        {
    054            if (inthashmapentry.hashEntry == par1)
    055            {
    056                return inthashmapentry.valueEntry;
    057            }
    058        }
    059
    060        return null;
    061    }
    062
    063    /**
    064     * Return true if an object is associated with the given key
    065     */
    066    public boolean containsItem(int par1)
    067    {
    068        return this.lookupEntry(par1) != null;
    069    }
    070
    071    /**
    072     * Returns the key/object mapping for a given key as a MCHashEntry
    073     */
    074    final IntHashMapEntry lookupEntry(int par1)
    075    {
    076        int j = computeHash(par1);
    077
    078        for (IntHashMapEntry inthashmapentry = this.slots[getSlotIndex(j, this.slots.length)]; inthashmapentry != null; inthashmapentry = inthashmapentry.nextEntry)
    079        {
    080            if (inthashmapentry.hashEntry == par1)
    081            {
    082                return inthashmapentry;
    083            }
    084        }
    085
    086        return null;
    087    }
    088
    089    /**
    090     * Adds a key and associated value to this map
    091     */
    092    public void addKey(int par1, Object par2Obj)
    093    {
    094        this.keySet.add(Integer.valueOf(par1));
    095        int j = computeHash(par1);
    096        int k = getSlotIndex(j, this.slots.length);
    097
    098        for (IntHashMapEntry inthashmapentry = this.slots[k]; inthashmapentry != null; inthashmapentry = inthashmapentry.nextEntry)
    099        {
    100            if (inthashmapentry.hashEntry == par1)
    101            {
    102                inthashmapentry.valueEntry = par2Obj;
    103                return;
    104            }
    105        }
    106
    107        ++this.versionStamp;
    108        this.insert(j, par1, par2Obj, k);
    109    }
    110
    111    /**
    112     * Increases the number of hash slots
    113     */
    114    private void grow(int par1)
    115    {
    116        IntHashMapEntry[] ainthashmapentry = this.slots;
    117        int j = ainthashmapentry.length;
    118
    119        if (j == 1073741824)
    120        {
    121            this.threshold = Integer.MAX_VALUE;
    122        }
    123        else
    124        {
    125            IntHashMapEntry[] ainthashmapentry1 = new IntHashMapEntry[par1];
    126            this.copyTo(ainthashmapentry1);
    127            this.slots = ainthashmapentry1;
    128            this.threshold = (int)((float)par1 * this.growFactor);
    129        }
    130    }
    131
    132    /**
    133     * Copies the hash slots to a new array
    134     */
    135    private void copyTo(IntHashMapEntry[] par1ArrayOfIntHashMapEntry)
    136    {
    137        IntHashMapEntry[] ainthashmapentry1 = this.slots;
    138        int i = par1ArrayOfIntHashMapEntry.length;
    139
    140        for (int j = 0; j < ainthashmapentry1.length; ++j)
    141        {
    142            IntHashMapEntry inthashmapentry = ainthashmapentry1[j];
    143
    144            if (inthashmapentry != null)
    145            {
    146                ainthashmapentry1[j] = null;
    147                IntHashMapEntry inthashmapentry1;
    148
    149                do
    150                {
    151                    inthashmapentry1 = inthashmapentry.nextEntry;
    152                    int k = getSlotIndex(inthashmapentry.slotHash, i);
    153                    inthashmapentry.nextEntry = par1ArrayOfIntHashMapEntry[k];
    154                    par1ArrayOfIntHashMapEntry[k] = inthashmapentry;
    155                    inthashmapentry = inthashmapentry1;
    156                }
    157                while (inthashmapentry1 != null);
    158            }
    159        }
    160    }
    161
    162    /**
    163     * Removes the specified object from the map and returns it
    164     */
    165    public Object removeObject(int par1)
    166    {
    167        this.keySet.remove(Integer.valueOf(par1));
    168        IntHashMapEntry inthashmapentry = this.removeEntry(par1);
    169        return inthashmapentry == null ? null : inthashmapentry.valueEntry;
    170    }
    171
    172    /**
    173     * Removes the specified entry from the map and returns it
    174     */
    175    final IntHashMapEntry removeEntry(int par1)
    176    {
    177        int j = computeHash(par1);
    178        int k = getSlotIndex(j, this.slots.length);
    179        IntHashMapEntry inthashmapentry = this.slots[k];
    180        IntHashMapEntry inthashmapentry1;
    181        IntHashMapEntry inthashmapentry2;
    182
    183        for (inthashmapentry1 = inthashmapentry; inthashmapentry1 != null; inthashmapentry1 = inthashmapentry2)
    184        {
    185            inthashmapentry2 = inthashmapentry1.nextEntry;
    186
    187            if (inthashmapentry1.hashEntry == par1)
    188            {
    189                ++this.versionStamp;
    190                --this.count;
    191
    192                if (inthashmapentry == inthashmapentry1)
    193                {
    194                    this.slots[k] = inthashmapentry2;
    195                }
    196                else
    197                {
    198                    inthashmapentry.nextEntry = inthashmapentry2;
    199                }
    200
    201                return inthashmapentry1;
    202            }
    203
    204            inthashmapentry = inthashmapentry1;
    205        }
    206
    207        return inthashmapentry1;
    208    }
    209
    210    /**
    211     * Removes all entries from the map
    212     */
    213    public void clearMap()
    214    {
    215        ++this.versionStamp;
    216        IntHashMapEntry[] ainthashmapentry = this.slots;
    217
    218        for (int i = 0; i < ainthashmapentry.length; ++i)
    219        {
    220            ainthashmapentry[i] = null;
    221        }
    222
    223        this.count = 0;
    224    }
    225
    226    /**
    227     * Adds an object to a slot
    228     */
    229    private void insert(int par1, int par2, Object par3Obj, int par4)
    230    {
    231        IntHashMapEntry inthashmapentry = this.slots[par4];
    232        this.slots[par4] = new IntHashMapEntry(par1, par2, par3Obj, inthashmapentry);
    233
    234        if (this.count++ >= this.threshold)
    235        {
    236            this.grow(2 * this.slots.length);
    237        }
    238    }
    239
    240    @SideOnly(Side.CLIENT)
    241
    242    /**
    243     * Return the Set of all keys stored in this MCHash object
    244     */
    245    public Set getKeySet()
    246    {
    247        return this.keySet;
    248    }
    249
    250    /**
    251     * Returns the hash code for a key
    252     */
    253    static int getHash(int par0)
    254    {
    255        return computeHash(par0);
    256    }
    257}