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}