/** * Set a hashtable value * * @param key key to set * @param value value to set */ public void hashset(LuaValue key, LuaValue value) { if (value.isnil()) hashRemove(key); else { int index = 0; if (hash.length > 0) { index = hashSlot(key); for (Slot slot = hash[index]; slot != null; slot = slot.rest()) { StrongSlot foundSlot; if ((foundSlot = slot.find(key)) != null) { hash[index] = hash[index].set(foundSlot, value); return; } } } if (checkLoadFactor()) { if (key.isinttype() && key.toint() > 0) { // a rehash might make room in the array portion for this key. rehash(key.toint()); if (arrayset(key.toint(), value)) return; } else { rehash(-1); } index = hashSlot(key); } Slot entry = (m_metatable != null) ? m_metatable.entry(key, value) : defaultEntry(key, value); hash[index] = (hash[index] != null) ? hash[index].add(entry) : entry; ++hashEntries; } }
/* * 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; }