/* * newKey > 0 is next key to insert * newKey == 0 means number of keys not changing (__mode changed) * newKey < 0 next key will go in hash part */ private void rehash(int newKey) { if (m_metatable != null && (m_metatable.useWeakKeys() || m_metatable.useWeakValues())) { // If this table has weak entries, hashEntries is just an upper bound. hashEntries = countHashKeys(); if (m_metatable.useWeakValues()) { dropWeakArrayValues(); } } int[] nums = new int[32]; int total = countIntKeys(nums); if (newKey > 0) { total++; nums[log2(newKey)]++; } // Choose N such that N <= sum(nums[0..log(N)]) < 2N int keys = nums[0]; int newArraySize = 0; for (int log = 1; log < 32; ++log) { keys += nums[log]; if (total * 2 < 1 << log) { // Not enough integer keys. break; } else if (keys >= (1 << (log - 1))) { newArraySize = 1 << log; } } final LuaValue[] oldArray = array; final Slot[] oldHash = hash; final LuaValue[] newArray; final Slot[] newHash; // Copy existing array entries and compute number of moving entries. int movingToArray = 0; if (newKey > 0 && newKey <= newArraySize) { movingToArray--; } if (newArraySize != oldArray.length) { newArray = new LuaValue[newArraySize]; if (newArraySize > oldArray.length) { for (int i = log2(oldArray.length + 1), j = log2(newArraySize) + 1; i < j; ++i) { movingToArray += nums[i]; } } else if (oldArray.length > newArraySize) { for (int i = log2(newArraySize + 1), j = log2(oldArray.length) + 1; i < j; ++i) { movingToArray -= nums[i]; } } System.arraycopy(oldArray, 0, newArray, 0, Math.min(oldArray.length, newArraySize)); } else { newArray = array; } final int newHashSize = hashEntries - movingToArray + ((newKey < 0 || newKey > newArraySize) ? 1 : 0); // Make room for the new entry final int oldCapacity = oldHash.length; final int newCapacity; final int newHashMask; if (newHashSize > 0) { // round up to next power of 2. newCapacity = (newHashSize < MIN_HASH_CAPACITY) ? MIN_HASH_CAPACITY : 1 << log2(newHashSize); newHashMask = newCapacity - 1; newHash = new Slot[newCapacity]; } else { newCapacity = 0; newHashMask = 0; newHash = NOBUCKETS; } // Move hash buckets for (int i = 0; i < oldCapacity; ++i) { for (Slot slot = oldHash[i]; slot != null; slot = slot.rest()) { int k; if ((k = slot.arraykey(newArraySize)) > 0) { StrongSlot entry = slot.first(); if (entry != null) newArray[k - 1] = entry.value(); } else { int j = slot.keyindex(newHashMask); newHash[j] = slot.relink(newHash[j]); } } } // Move array values into hash portion for (int i = newArraySize; i < oldArray.length; ) { LuaValue v; if ((v = oldArray[i++]) != null) { int slot = hashmod(LuaInteger.hashCode(i), newHashMask); Slot newEntry; if (m_metatable != null) { newEntry = m_metatable.entry(valueOf(i), v); if (newEntry == null) continue; } else { newEntry = defaultEntry(valueOf(i), v); } newHash[slot] = (newHash[slot] != null) ? newHash[slot].add(newEntry) : newEntry; } } hash = newHash; array = newArray; hashEntries -= movingToArray; }
public int keyindex(int mask) { return hashmod(LuaInteger.hashCode(key), mask); }