|  | /******************************************************************************* | 
|  | * Copyright (c) 2009 Luaj.org. All rights reserved. | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
|  | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | ******************************************************************************/ | 
|  | package org.luaj.vm2; | 
|  |  | 
|  | import java.lang.ref.WeakReference; | 
|  | import java.util.Vector; | 
|  |  | 
|  | /** | 
|  | * Subclass of {@link LuaValue} for representing lua tables. | 
|  | * <p> | 
|  | * Almost all API's implemented in {@link LuaTable} are defined and documented in {@link LuaValue}. | 
|  | * <p> | 
|  | * If a table is needed, the one of the type-checking functions can be used such as | 
|  | * {@link #istable()}, | 
|  | * {@link #checktable()}, or | 
|  | * {@link #opttable(LuaTable)} | 
|  | * <p> | 
|  | * The main table operations are defined on {@link LuaValue} | 
|  | * for getting and setting values with and without metatag processing: | 
|  | * <ul> | 
|  | * <li>{@link #get(LuaValue)}</li> | 
|  | * <li>{@link #set(LuaValue,LuaValue)}</li> | 
|  | * <li>{@link #rawget(LuaValue)}</li> | 
|  | * <li>{@link #rawset(LuaValue,LuaValue)}</li> | 
|  | * <li>plus overloads such as {@link #get(String)}, {@link #get(int)}, and so on</li> | 
|  | * </ul> | 
|  | * <p> | 
|  | * To iterate over key-value pairs from Java, use | 
|  | * <pre> {@code | 
|  | * LuaValue k = LuaValue.NIL; | 
|  | * while ( true ) { | 
|  | *    Varargs n = table.next(k); | 
|  | *    if ( (k = n.arg1()).isnil() ) | 
|  | *       break; | 
|  | *    LuaValue v = n.arg(2) | 
|  | *    process( k, v ) | 
|  | * }}</pre> | 
|  | * | 
|  | * <p> | 
|  | * As with other types, {@link LuaTable} instances should be constructed via one of the table constructor | 
|  | * methods on {@link LuaValue}: | 
|  | * <ul> | 
|  | * <li>{@link LuaValue#tableOf()} empty table</li> | 
|  | * <li>{@link LuaValue#tableOf(int, int)} table with capacity</li> | 
|  | * <li>{@link LuaValue#listOf(LuaValue[])} initialize array part</li> | 
|  | * <li>{@link LuaValue#listOf(LuaValue[], Varargs)} initialize array part</li> | 
|  | * <li>{@link LuaValue#tableOf(LuaValue[])} initialize named hash part</li> | 
|  | * <li>{@link LuaValue#tableOf(Varargs, int)} initialize named hash part</li> | 
|  | * <li>{@link LuaValue#tableOf(LuaValue[], LuaValue[])} initialize array and named parts</li> | 
|  | * <li>{@link LuaValue#tableOf(LuaValue[], LuaValue[], Varargs)} initialize array and named parts</li> | 
|  | * </ul> | 
|  | * @see LuaValue | 
|  | */ | 
|  | public class LuaTable extends LuaValue implements Metatable { | 
|  | private static final int      MIN_HASH_CAPACITY = 2; | 
|  | private static final LuaString N = valueOf("n"); | 
|  |  | 
|  | /** the array values */ | 
|  | protected LuaValue[] array; | 
|  |  | 
|  | /** the hash part */ | 
|  | protected Slot[] hash; | 
|  |  | 
|  | /** the number of hash entries */ | 
|  | protected int hashEntries; | 
|  |  | 
|  | /** metatable for this table, or null */ | 
|  | protected Metatable m_metatable; | 
|  |  | 
|  | /** Construct empty table */ | 
|  | public LuaTable() { | 
|  | array = NOVALS; | 
|  | hash = NOBUCKETS; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct table with preset capacity. | 
|  | * @param narray capacity of array part | 
|  | * @param nhash capacity of hash part | 
|  | */ | 
|  | public LuaTable(int narray, int nhash) { | 
|  | presize(narray, nhash); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct table with named and unnamed parts. | 
|  | * @param named Named elements in order {@code key-a, value-a, key-b, value-b, ... } | 
|  | * @param unnamed Unnamed elements in order {@code value-1, value-2, ... } | 
|  | * @param lastarg Additional unnamed values beyond {@code unnamed.length} | 
|  | */ | 
|  | public LuaTable(LuaValue[] named, LuaValue[] unnamed, Varargs lastarg) { | 
|  | int nn = (named!=null? named.length: 0); | 
|  | int nu = (unnamed!=null? unnamed.length: 0); | 
|  | int nl = (lastarg!=null? lastarg.narg(): 0); | 
|  | presize(nu+nl, nn>>1); | 
|  | for ( int i=0; i<nu; i++ ) | 
|  | rawset(i+1,unnamed[i]); | 
|  | if ( lastarg != null ) | 
|  | for ( int i=1,n=lastarg.narg(); i<=n; ++i ) | 
|  | rawset(nu+i,lastarg.arg(i)); | 
|  | for ( int i=0; i<nn; i+=2 ) | 
|  | if (!named[i+1].isnil()) | 
|  | rawset(named[i], named[i+1]); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct table of unnamed elements. | 
|  | * @param varargs Unnamed elements in order {@code value-1, value-2, ... } | 
|  | */ | 
|  | public LuaTable(Varargs varargs) { | 
|  | this(varargs,1); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct table of unnamed elements. | 
|  | * @param varargs Unnamed elements in order {@code value-1, value-2, ... } | 
|  | * @param firstarg the index in varargs of the first argument to include in the table | 
|  | */ | 
|  | public LuaTable(Varargs varargs, int firstarg) { | 
|  | int nskip = firstarg-1; | 
|  | int n = Math.max(varargs.narg()-nskip,0); | 
|  | presize( n, 1 ); | 
|  | set(N, valueOf(n)); | 
|  | for ( int i=1; i<=n; i++ ) | 
|  | set(i, varargs.arg(i+nskip)); | 
|  | } | 
|  |  | 
|  | public int type() { | 
|  | return LuaValue.TTABLE; | 
|  | } | 
|  |  | 
|  | public String typename() { | 
|  | return "table"; | 
|  | } | 
|  |  | 
|  | public boolean istable() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public LuaTable checktable() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public LuaTable opttable(LuaTable defval)  { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public void presize( int narray ) { | 
|  | if ( narray > array.length ) | 
|  | array = resize( array, 1 << log2(narray) ); | 
|  | } | 
|  |  | 
|  | public void presize(int narray, int nhash) { | 
|  | if ( nhash > 0 && nhash < MIN_HASH_CAPACITY ) | 
|  | nhash = MIN_HASH_CAPACITY; | 
|  | // Size of both parts must be a power of two. | 
|  | array = (narray>0? new LuaValue[1 << log2(narray)]: NOVALS); | 
|  | hash = (nhash>0? new Slot[1 << log2(nhash)]: NOBUCKETS); | 
|  | hashEntries = 0; | 
|  | } | 
|  |  | 
|  | /** Resize the table */ | 
|  | private static LuaValue[] resize( LuaValue[] old, int n ) { | 
|  | LuaValue[] v = new LuaValue[n]; | 
|  | System.arraycopy(old, 0, v, 0, old.length); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the length of the array part of the table. | 
|  | * @return length of the array part, does not relate to count of objects in the table. | 
|  | */ | 
|  | protected int getArrayLength() { | 
|  | return array.length; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the length of the hash part of the table. | 
|  | * @return length of the hash part, does not relate to count of objects in the table. | 
|  | */ | 
|  | protected int getHashLength() { | 
|  | return hash.length; | 
|  | } | 
|  |  | 
|  | public LuaValue getmetatable() { | 
|  | return ( m_metatable != null ) ? m_metatable.toLuaValue() : null; | 
|  | } | 
|  |  | 
|  | public LuaValue setmetatable(LuaValue metatable) { | 
|  | boolean hadWeakKeys = m_metatable != null && m_metatable.useWeakKeys(); | 
|  | boolean hadWeakValues = m_metatable != null && m_metatable.useWeakValues(); | 
|  | m_metatable = metatableOf( metatable ); | 
|  | if ( ( hadWeakKeys != ( m_metatable != null && m_metatable.useWeakKeys() )) || | 
|  | ( hadWeakValues != ( m_metatable != null && m_metatable.useWeakValues() ))) { | 
|  | // force a rehash | 
|  | rehash( 0 ); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public LuaValue get( int key ) { | 
|  | LuaValue v = rawget(key); | 
|  | return v.isnil() && m_metatable!=null? gettable(this,valueOf(key)): v; | 
|  | } | 
|  |  | 
|  | public LuaValue get( LuaValue key ) { | 
|  | LuaValue v = rawget(key); | 
|  | return v.isnil() && m_metatable!=null? gettable(this,key): v; | 
|  | } | 
|  |  | 
|  | public LuaValue rawget( int key ) { | 
|  | if ( key>0 && key<=array.length ) { | 
|  | LuaValue v = m_metatable == null ? array[key-1] : m_metatable.arrayget(array, key-1); | 
|  | return v != null ? v : NIL; | 
|  | } | 
|  | return hashget( LuaInteger.valueOf(key) ); | 
|  | } | 
|  |  | 
|  | public LuaValue rawget( LuaValue key ) { | 
|  | if ( key.isinttype() ) { | 
|  | int ikey = key.toint(); | 
|  | if ( ikey>0 && ikey<=array.length ) { | 
|  | LuaValue v = m_metatable == null | 
|  | ? array[ikey-1] : m_metatable.arrayget(array, ikey-1); | 
|  | return v != null ? v : NIL; | 
|  | } | 
|  | } | 
|  | return hashget( key ); | 
|  | } | 
|  |  | 
|  | protected LuaValue hashget(LuaValue key) { | 
|  | if ( hashEntries > 0 ) { | 
|  | for ( Slot slot = hash[ hashSlot(key) ]; slot != null; slot = slot.rest() ) { | 
|  | StrongSlot foundSlot; | 
|  | if ( ( foundSlot = slot.find(key) ) != null ) { | 
|  | return foundSlot.value(); | 
|  | } | 
|  | } | 
|  | } | 
|  | return NIL; | 
|  | } | 
|  |  | 
|  | public void set( int key, LuaValue value ) { | 
|  | if ( m_metatable==null || ! rawget(key).isnil() || ! settable(this,LuaInteger.valueOf(key),value) ) | 
|  | rawset(key, value); | 
|  | } | 
|  |  | 
|  | /** caller must ensure key is not nil */ | 
|  | public void set( LuaValue key, LuaValue value ) { | 
|  | if (!key.isvalidkey() && !metatag(NEWINDEX).isfunction()) | 
|  | typerror("table index"); | 
|  | if ( m_metatable==null || ! rawget(key).isnil() ||  ! settable(this,key,value) ) | 
|  | rawset(key, value); | 
|  | } | 
|  |  | 
|  | public void rawset( int key, LuaValue value ) { | 
|  | if ( ! arrayset(key, value) ) | 
|  | hashset( LuaInteger.valueOf(key), value ); | 
|  | } | 
|  |  | 
|  | /** caller must ensure key is not nil */ | 
|  | public void rawset( LuaValue key, LuaValue value ) { | 
|  | if ( !key.isinttype() || !arrayset(key.toint(), value) ) | 
|  | hashset( key, value ); | 
|  | } | 
|  |  | 
|  | /** Set an array element */ | 
|  | private boolean arrayset( int key, LuaValue value ) { | 
|  | if ( key>0 && key<=array.length ) { | 
|  | array[key - 1] = value.isnil() ? null : | 
|  | (m_metatable != null ? m_metatable.wrap(value) : value); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** Remove the element at a position in a list-table | 
|  | * | 
|  | * @param pos the position to remove | 
|  | * @return The removed item, or {@link #NONE} if not removed | 
|  | */ | 
|  | public LuaValue remove(int pos) { | 
|  | int n = length(); | 
|  | if ( pos == 0 ) | 
|  | pos = n; | 
|  | else if (pos > n) | 
|  | return NONE; | 
|  | LuaValue v = rawget(pos); | 
|  | for ( LuaValue r=v; !r.isnil(); ) { | 
|  | r = rawget(pos+1); | 
|  | rawset(pos++, r); | 
|  | } | 
|  | return v.isnil()? NONE: v; | 
|  | } | 
|  |  | 
|  | /** Insert an element at a position in a list-table | 
|  | * | 
|  | * @param pos the position to remove | 
|  | * @param value The value to insert | 
|  | */ | 
|  | public void insert(int pos, LuaValue value) { | 
|  | if ( pos == 0 ) | 
|  | pos = length()+1; | 
|  | while ( ! value.isnil() ) { | 
|  | LuaValue v = rawget( pos ); | 
|  | rawset(pos++, value); | 
|  | value = v; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Concatenate the contents of a table efficiently, using {@link Buffer} | 
|  | * | 
|  | * @param sep {@link LuaString} separater to apply between elements | 
|  | * @param i the first element index | 
|  | * @param j the last element index, inclusive | 
|  | * @return {@link LuaString} value of the concatenation | 
|  | */ | 
|  | public LuaValue concat(LuaString sep, int i, int j) { | 
|  | Buffer  sb = new Buffer (); | 
|  | if ( i<=j ) { | 
|  | sb.append( get(i).checkstring() ); | 
|  | while ( ++i<=j ) { | 
|  | sb.append( sep ); | 
|  | sb.append( get(i).checkstring() ); | 
|  | } | 
|  | } | 
|  | return sb.tostring(); | 
|  | } | 
|  |  | 
|  | public int length() { | 
|  | int a = getArrayLength(); | 
|  | int n = a+1,m=0; | 
|  | while ( !rawget(n).isnil() ) { | 
|  | m = n; | 
|  | n += a+getHashLength()+1; | 
|  | } | 
|  | while ( n > m+1 ) { | 
|  | int k = (n+m) / 2; | 
|  | if ( !rawget(k).isnil() ) | 
|  | m = k; | 
|  | else | 
|  | n = k; | 
|  | } | 
|  | return m; | 
|  | } | 
|  |  | 
|  | public LuaValue len()  { | 
|  | return LuaInteger.valueOf(length()); | 
|  | } | 
|  |  | 
|  | public int rawlen() { | 
|  | return length(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the next element after a particular key in the table | 
|  | * @return key,value or nil | 
|  | */ | 
|  | public Varargs next( LuaValue key ) { | 
|  | int i = 0; | 
|  | do { | 
|  | // find current key index | 
|  | if ( ! key.isnil() ) { | 
|  | if ( key.isinttype() ) { | 
|  | i = key.toint(); | 
|  | if ( i>0 && i<=array.length ) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | if ( hash.length == 0 ) | 
|  | error( "invalid key to 'next'" ); | 
|  | i = hashSlot( key ); | 
|  | boolean found = false; | 
|  | for ( Slot slot = hash[i]; slot != null; slot = slot.rest() ) { | 
|  | if ( found ) { | 
|  | StrongSlot nextEntry = slot.first(); | 
|  | if ( nextEntry != null ) { | 
|  | return nextEntry.toVarargs(); | 
|  | } | 
|  | } else if ( slot.keyeq( key ) ) { | 
|  | found = true; | 
|  | } | 
|  | } | 
|  | if ( !found ) { | 
|  | error( "invalid key to 'next'" ); | 
|  | } | 
|  | i += 1+array.length; | 
|  | } | 
|  | } while ( false ); | 
|  |  | 
|  | // check array part | 
|  | for ( ; i<array.length; ++i ) { | 
|  | if ( array[i] != null ) { | 
|  | LuaValue value = m_metatable == null ? array[i] : m_metatable.arrayget(array, i); | 
|  | if (value != null) { | 
|  | return varargsOf(LuaInteger.valueOf(i+1),value); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // check hash part | 
|  | for ( i -= array.length; i < hash.length; ++i ) { | 
|  | Slot slot = hash[i]; | 
|  | while ( slot != null ) { | 
|  | StrongSlot first = slot.first(); | 
|  | if ( first != null ) | 
|  | return first.toVarargs(); | 
|  | slot = slot.rest(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // nothing found, push nil, return nil. | 
|  | return NIL; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the next element after a particular key in the | 
|  | * contiguous array part of a table | 
|  | * @return key,value or none | 
|  | */ | 
|  | public Varargs inext(LuaValue key) { | 
|  | int k = key.checkint() + 1; | 
|  | LuaValue v = rawget(k); | 
|  | return v.isnil()? NONE: varargsOf(LuaInteger.valueOf(k),v); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static int hashpow2( int hashCode, int mask ) { | 
|  | return hashCode & mask; | 
|  | } | 
|  |  | 
|  | public static int hashmod( int hashCode, int mask ) { | 
|  | return ( hashCode & 0x7FFFFFFF ) % mask; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Find the hashtable slot index to use. | 
|  | * @param key the key to look for | 
|  | * @param hashMask N-1 where N is the number of hash slots (must be power of 2) | 
|  | * @return the slot index | 
|  | */ | 
|  | public static int hashSlot( LuaValue key, int hashMask ) { | 
|  | switch ( key.type() ) { | 
|  | case TNUMBER: | 
|  | case TTABLE: | 
|  | case TTHREAD: | 
|  | case TLIGHTUSERDATA: | 
|  | case TUSERDATA: | 
|  | return hashmod( key.hashCode(), hashMask ); | 
|  | default: | 
|  | return hashpow2( key.hashCode(), hashMask ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Find the hashtable slot to use | 
|  | * @param key key to look for | 
|  | * @return slot to use | 
|  | */ | 
|  | private int hashSlot(LuaValue key) { | 
|  | return hashSlot( key, hash.length - 1 ); | 
|  | } | 
|  |  | 
|  | private void hashRemove( LuaValue key ) { | 
|  | if ( hash.length > 0 ) { | 
|  | int 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].remove( foundSlot ); | 
|  | --hashEntries; | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private boolean checkLoadFactor() { | 
|  | return hashEntries >= hash.length; | 
|  | } | 
|  |  | 
|  | private int countHashKeys() { | 
|  | int keys = 0; | 
|  | for ( int i = 0; i < hash.length; ++i ) { | 
|  | for ( Slot slot = hash[i]; slot != null; slot = slot.rest() ) { | 
|  | if ( slot.first() != null ) | 
|  | keys++; | 
|  | } | 
|  | } | 
|  | return keys; | 
|  | } | 
|  |  | 
|  | private void dropWeakArrayValues() { | 
|  | for ( int i = 0; i < array.length; ++i ) { | 
|  | m_metatable.arrayget(array, i); | 
|  | } | 
|  | } | 
|  |  | 
|  | private int countIntKeys(int[] nums) { | 
|  | int total = 0; | 
|  | int i = 1; | 
|  |  | 
|  | // Count integer keys in array part | 
|  | for ( int bit = 0; bit < 31; ++bit ) { | 
|  | if ( i > array.length ) | 
|  | break; | 
|  | int j = Math.min(array.length, 1 << bit); | 
|  | int c = 0; | 
|  | while ( i <= j ) { | 
|  | if ( array[ i++ - 1 ] != null ) | 
|  | c++; | 
|  | } | 
|  | nums[bit] = c; | 
|  | total += c; | 
|  | } | 
|  |  | 
|  | // Count integer keys in hash part | 
|  | for ( i = 0; i < hash.length; ++i ) { | 
|  | for ( Slot s = hash[i]; s != null; s = s.rest() ) { | 
|  | int k; | 
|  | if ( ( k = s.arraykey(Integer.MAX_VALUE) ) > 0 ) { | 
|  | nums[log2(k)]++; | 
|  | total++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return total; | 
|  | } | 
|  |  | 
|  | // Compute ceil(log2(x)) | 
|  | static int log2(int x) { | 
|  | int lg = 0; | 
|  | x -= 1; | 
|  | if ( x < 0 ) | 
|  | // 2^(-(2^31)) is approximately 0 | 
|  | return Integer.MIN_VALUE; | 
|  | if ( ( x & 0xFFFF0000 ) != 0 ) { | 
|  | lg = 16; | 
|  | x >>>= 16; | 
|  | } | 
|  | if ( ( x & 0xFF00 ) != 0 ) { | 
|  | lg += 8; | 
|  | x >>>= 8; | 
|  | } | 
|  | if ( ( x & 0xF0 ) != 0 ) { | 
|  | lg += 4; | 
|  | x >>>= 4; | 
|  | } | 
|  | switch (x) { | 
|  | case 0x0: return 0; | 
|  | case 0x1: lg += 1; break; | 
|  | case 0x2: lg += 2; break; | 
|  | case 0x3: lg += 2; break; | 
|  | case 0x4: lg += 3; break; | 
|  | case 0x5: lg += 3; break; | 
|  | case 0x6: lg += 3; break; | 
|  | case 0x7: lg += 3; break; | 
|  | case 0x8: lg += 4; break; | 
|  | case 0x9: lg += 4; break; | 
|  | case 0xA: lg += 4; break; | 
|  | case 0xB: lg += 4; break; | 
|  | case 0xC: lg += 4; break; | 
|  | case 0xD: lg += 4; break; | 
|  | case 0xE: lg += 4; break; | 
|  | case 0xF: lg += 4; break; | 
|  | } | 
|  | return lg; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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 Slot entry( LuaValue key, LuaValue value ) { | 
|  | return defaultEntry( key, value ); | 
|  | } | 
|  |  | 
|  | protected static boolean isLargeKey(LuaValue key) { | 
|  | switch (key.type()) { | 
|  | case TSTRING: | 
|  | return key.rawlen() > LuaString.RECENT_STRINGS_MAX_LENGTH; | 
|  | case TNUMBER: | 
|  | case TBOOLEAN: | 
|  | return false; | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | protected static Entry defaultEntry(LuaValue key, LuaValue value) { | 
|  | if ( key.isinttype() ) { | 
|  | return new IntKeyEntry( key.toint(), value ); | 
|  | } else if (value.type() == TNUMBER) { | 
|  | return new NumberValueEntry( key, value.todouble() ); | 
|  | } else { | 
|  | return new NormalEntry( key, value ); | 
|  | } | 
|  | } | 
|  |  | 
|  | // ----------------- sort support ----------------------------- | 
|  | // | 
|  | // implemented heap sort from wikipedia | 
|  | // | 
|  | // Only sorts the contiguous array part. | 
|  | // | 
|  | /** Sort the table using a comparator. | 
|  | * @param comparator {@link LuaValue} to be called to compare elements. | 
|  | */ | 
|  | public void sort(LuaValue comparator) { | 
|  | if (m_metatable != null && m_metatable.useWeakValues()) { | 
|  | dropWeakArrayValues(); | 
|  | } | 
|  | int n = array.length; | 
|  | while ( n > 0 && array[n-1] == null ) | 
|  | --n; | 
|  | if ( n > 1 ) | 
|  | heapSort(n, comparator); | 
|  | } | 
|  |  | 
|  | private void heapSort(int count, LuaValue cmpfunc) { | 
|  | heapify(count, cmpfunc); | 
|  | for ( int end=count-1; end>0; ) { | 
|  | swap(end, 0); | 
|  | siftDown(0, --end, cmpfunc); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void heapify(int count, LuaValue cmpfunc) { | 
|  | for ( int start=count/2-1; start>=0; --start ) | 
|  | siftDown(start, count - 1, cmpfunc); | 
|  | } | 
|  |  | 
|  | private void siftDown(int start, int end, LuaValue cmpfunc) { | 
|  | for ( int root=start; root*2+1 <= end; ) { | 
|  | int child = root*2+1; | 
|  | if (child < end && compare(child, child + 1, cmpfunc)) | 
|  | ++child; | 
|  | if (compare(root, child, cmpfunc)) { | 
|  | swap(root, child); | 
|  | root = child; | 
|  | } else | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | private boolean compare(int i, int j, LuaValue cmpfunc) { | 
|  | LuaValue a, b; | 
|  | if (m_metatable == null) { | 
|  | a = array[i]; | 
|  | b = array[j]; | 
|  | } else { | 
|  | a = m_metatable.arrayget(array, i); | 
|  | b = m_metatable.arrayget(array, j); | 
|  | } | 
|  | if ( a == null || b == null ) | 
|  | return false; | 
|  | if ( ! cmpfunc.isnil() ) { | 
|  | return cmpfunc.call(a,b).toboolean(); | 
|  | } else { | 
|  | return a.lt_b(b); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void swap(int i, int j) { | 
|  | LuaValue a = array[i]; | 
|  | array[i] = array[j]; | 
|  | array[j] = a; | 
|  | } | 
|  |  | 
|  | /** This may be deprecated in a future release. | 
|  | * It is recommended to count via iteration over next() instead | 
|  | * @return count of keys in the table | 
|  | * */ | 
|  | public int keyCount() { | 
|  | LuaValue k = LuaValue.NIL; | 
|  | for ( int i=0; true; i++ ) { | 
|  | Varargs n = next(k); | 
|  | if ( (k = n.arg1()).isnil() ) | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** This may be deprecated in a future release. | 
|  | * It is recommended to use next() instead | 
|  | * @return array of keys in the table | 
|  | * */ | 
|  | public LuaValue[] keys() { | 
|  | Vector l = new Vector(); | 
|  | LuaValue k = LuaValue.NIL; | 
|  | while ( true ) { | 
|  | Varargs n = next(k); | 
|  | if ( (k = n.arg1()).isnil() ) | 
|  | break; | 
|  | l.addElement( k ); | 
|  | } | 
|  | LuaValue[] a = new LuaValue[l.size()]; | 
|  | l.copyInto(a); | 
|  | return a; | 
|  | } | 
|  |  | 
|  | // equality w/ metatable processing | 
|  | public LuaValue eq( LuaValue val ) {  return eq_b(val)? TRUE: FALSE; } | 
|  | public boolean eq_b( LuaValue val )  { | 
|  | if ( this == val ) return true; | 
|  | if ( m_metatable == null || !val.istable() ) return false; | 
|  | LuaValue valmt = val.getmetatable(); | 
|  | return valmt!=null && LuaValue.eqmtcall(this, m_metatable.toLuaValue(), val, valmt); | 
|  | } | 
|  |  | 
|  | /** Unpack all the elements of this table */ | 
|  | public Varargs unpack() { | 
|  | return unpack(1, this.length()); | 
|  | } | 
|  |  | 
|  | /** Unpack all the elements of this table from element i */ | 
|  | public Varargs unpack(int i) { | 
|  | return unpack(i, this.length()); | 
|  | } | 
|  |  | 
|  | /** Unpack the elements from i to j inclusive */ | 
|  | public Varargs unpack(int i, int j) { | 
|  | int n = j + 1 - i; | 
|  | switch (n) { | 
|  | case 0: return NONE; | 
|  | case 1: return get(i); | 
|  | case 2: return varargsOf(get(i), get(i+1)); | 
|  | default: | 
|  | if (n < 0) | 
|  | return NONE; | 
|  | LuaValue[] v = new LuaValue[n]; | 
|  | while (--n >= 0) | 
|  | v[n] = get(i+n); | 
|  | return varargsOf(v); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Represents a slot in the hash table. | 
|  | */ | 
|  | interface Slot { | 
|  |  | 
|  | /** Return hash{pow2,mod}( first().key().hashCode(), sizeMask ) */ | 
|  | int keyindex( int hashMask ); | 
|  |  | 
|  | /** Return first Entry, if still present, or null. */ | 
|  | StrongSlot first(); | 
|  |  | 
|  | /** Compare given key with first()'s key; return first() if equal. */ | 
|  | StrongSlot find( LuaValue key ); | 
|  |  | 
|  | /** | 
|  | * Compare given key with first()'s key; return true if equal. May | 
|  | * return true for keys no longer present in the table. | 
|  | */ | 
|  | boolean keyeq( LuaValue key ); | 
|  |  | 
|  | /** Return rest of elements */ | 
|  | Slot rest(); | 
|  |  | 
|  | /** | 
|  | * Return first entry's key, iff it is an integer between 1 and max, | 
|  | * inclusive, or zero otherwise. | 
|  | */ | 
|  | int arraykey( int max ); | 
|  |  | 
|  | /** | 
|  | * Set the value of this Slot's first Entry, if possible, or return a | 
|  | * new Slot whose first entry has the given value. | 
|  | */ | 
|  | Slot set( StrongSlot target, LuaValue value ); | 
|  |  | 
|  | /** | 
|  | * Link the given new entry to this slot. | 
|  | */ | 
|  | Slot add( Slot newEntry ); | 
|  |  | 
|  | /** | 
|  | * Return a Slot with the given value set to nil; must not return null | 
|  | * for next() to behave correctly. | 
|  | */ | 
|  | Slot remove( StrongSlot target ); | 
|  |  | 
|  | /** | 
|  | * Return a Slot with the same first key and value (if still present) | 
|  | * and rest() equal to rest. | 
|  | */ | 
|  | Slot relink( Slot rest ); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Subclass of Slot guaranteed to have a strongly-referenced key and value, | 
|  | * to support weak tables. | 
|  | */ | 
|  | interface StrongSlot extends Slot { | 
|  | /** Return first entry's key */ | 
|  | LuaValue key(); | 
|  |  | 
|  | /** Return first entry's value */ | 
|  | LuaValue value(); | 
|  |  | 
|  | /** Return varargsOf(key(), value()) or equivalent */ | 
|  | Varargs toVarargs(); | 
|  | } | 
|  |  | 
|  | private static class LinkSlot implements StrongSlot { | 
|  | private Entry entry; | 
|  | private Slot next; | 
|  |  | 
|  | LinkSlot( Entry entry, Slot next ) { | 
|  | this.entry = entry; | 
|  | this.next = next; | 
|  | } | 
|  |  | 
|  | public LuaValue key() { | 
|  | return entry.key(); | 
|  | } | 
|  |  | 
|  | public int keyindex( int hashMask ) { | 
|  | return entry.keyindex( hashMask ); | 
|  | } | 
|  |  | 
|  | public LuaValue value() { | 
|  | return entry.value(); | 
|  | } | 
|  |  | 
|  | public Varargs toVarargs() { | 
|  | return entry.toVarargs(); | 
|  | } | 
|  |  | 
|  | public StrongSlot first() { | 
|  | return entry; | 
|  | } | 
|  |  | 
|  | public StrongSlot find(LuaValue key) { | 
|  | return entry.keyeq(key) ? this : null; | 
|  | } | 
|  |  | 
|  | public boolean keyeq(LuaValue key) { | 
|  | return entry.keyeq(key); | 
|  | } | 
|  |  | 
|  | public Slot rest() { | 
|  | return next; | 
|  | } | 
|  |  | 
|  | public int arraykey( int max ) { | 
|  | return entry.arraykey( max ); | 
|  | } | 
|  |  | 
|  | public Slot set(StrongSlot target, LuaValue value) { | 
|  | if ( target == this ) { | 
|  | entry = entry.set( value ); | 
|  | return this; | 
|  | } else { | 
|  | return setnext(next.set( target, value )); | 
|  | } | 
|  | } | 
|  |  | 
|  | public Slot add( Slot entry ) { | 
|  | return setnext(next.add( entry )); | 
|  | } | 
|  |  | 
|  | public Slot remove( StrongSlot target ) { | 
|  | if ( this == target ) { | 
|  | return new DeadSlot( key(), next ); | 
|  | } else { | 
|  | this.next = next.remove( target ); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Slot relink(Slot rest) { | 
|  | // This method is (only) called during rehash, so it must not change this.next. | 
|  | return ( rest != null ) ? new LinkSlot(entry, rest) : (Slot)entry; | 
|  | } | 
|  |  | 
|  | // this method ensures that this.next is never set to null. | 
|  | private Slot setnext(Slot next) { | 
|  | if ( next != null ) { | 
|  | this.next = next; | 
|  | return this; | 
|  | } else { | 
|  | return entry; | 
|  | } | 
|  | } | 
|  |  | 
|  | public String toString() { | 
|  | return entry + "; " + next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Base class for regular entries. | 
|  | * | 
|  | * <p> | 
|  | * If the key may be an integer, the {@link #arraykey(int)} method must be | 
|  | * overridden to handle that case. | 
|  | */ | 
|  | static abstract class Entry extends Varargs implements StrongSlot { | 
|  | public abstract LuaValue key(); | 
|  | public abstract LuaValue value(); | 
|  | abstract Entry set(LuaValue value); | 
|  |  | 
|  | public int arraykey( int max ) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public LuaValue arg(int i) { | 
|  | switch (i) { | 
|  | case 1: return key(); | 
|  | case 2: return value(); | 
|  | } | 
|  | return NIL; | 
|  | } | 
|  |  | 
|  | public int narg() { | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Subclasses should redefine as "return this;" whenever possible. | 
|  | */ | 
|  | public Varargs toVarargs() { | 
|  | return varargsOf(key(), value()); | 
|  | } | 
|  |  | 
|  | public LuaValue arg1() { | 
|  | return key(); | 
|  | } | 
|  |  | 
|  | public Varargs subargs(int start) { | 
|  | switch (start) { | 
|  | case 1: return this; | 
|  | case 2: return value(); | 
|  | } | 
|  | return NONE; | 
|  | } | 
|  |  | 
|  | public StrongSlot first() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Slot rest() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public StrongSlot find(LuaValue key) { | 
|  | return keyeq(key) ? this : null; | 
|  | } | 
|  |  | 
|  | public Slot set(StrongSlot target, LuaValue value) { | 
|  | return set( value ); | 
|  | } | 
|  |  | 
|  | public Slot add( Slot entry ) { | 
|  | return new LinkSlot( this, entry ); | 
|  | } | 
|  |  | 
|  | public Slot remove(StrongSlot target) { | 
|  | return new DeadSlot( key(), null ); | 
|  | } | 
|  |  | 
|  | public Slot relink( Slot rest ) { | 
|  | return ( rest != null ) ? new LinkSlot( this, rest ) : (Slot)this; | 
|  | } | 
|  | } | 
|  |  | 
|  | static class NormalEntry extends Entry { | 
|  | private final LuaValue key; | 
|  | private LuaValue value; | 
|  |  | 
|  | NormalEntry( LuaValue key, LuaValue value ) { | 
|  | this.key = key; | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public LuaValue key() { | 
|  | return key; | 
|  | } | 
|  |  | 
|  | public LuaValue value() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | public Entry set(LuaValue value) { | 
|  | this.value = value; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public Varargs toVarargs() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public int keyindex( int hashMask ) { | 
|  | return hashSlot( key, hashMask ); | 
|  | } | 
|  |  | 
|  | public boolean keyeq(LuaValue key) { | 
|  | return key.raweq(this.key); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static class IntKeyEntry extends Entry { | 
|  | private final int key; | 
|  | private LuaValue value; | 
|  |  | 
|  | IntKeyEntry(int key, LuaValue value) { | 
|  | this.key = key; | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public LuaValue key() { | 
|  | return valueOf( key ); | 
|  | } | 
|  |  | 
|  | public int arraykey(int max) { | 
|  | return ( key >= 1 && key <= max ) ? key : 0; | 
|  | } | 
|  |  | 
|  | public LuaValue value() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | public Entry set(LuaValue value) { | 
|  | this.value = value; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public int keyindex( int mask ) { | 
|  | return hashmod( LuaInteger.hashCode( key ), mask ); | 
|  | } | 
|  |  | 
|  | public boolean keyeq(LuaValue key) { | 
|  | return key.raweq( this.key ); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Entry class used with numeric values, but only when the key is not an integer. | 
|  | */ | 
|  | private static class NumberValueEntry extends Entry { | 
|  | private double value; | 
|  | private final LuaValue key; | 
|  |  | 
|  | NumberValueEntry(LuaValue key, double value) { | 
|  | this.key = key; | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public LuaValue key() { | 
|  | return key; | 
|  | } | 
|  |  | 
|  | public LuaValue value() { | 
|  | return valueOf(value); | 
|  | } | 
|  |  | 
|  | public Entry set(LuaValue value) { | 
|  | LuaValue n = value.tonumber(); | 
|  | if ( !n.isnil() ) { | 
|  | this.value = n.todouble(); | 
|  | return this; | 
|  | } else { | 
|  | return new NormalEntry( this.key, value ); | 
|  | } | 
|  | } | 
|  |  | 
|  | public int keyindex( int mask ) { | 
|  | return hashSlot( key, mask ); | 
|  | } | 
|  |  | 
|  | public boolean keyeq(LuaValue key) { | 
|  | return key.raweq(this.key); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A Slot whose value has been set to nil. The key is kept in a weak reference so that | 
|  | * it can be found by next(). | 
|  | */ | 
|  | private static class DeadSlot implements Slot { | 
|  |  | 
|  | private final Object key; | 
|  | private Slot next; | 
|  |  | 
|  | private DeadSlot( LuaValue key, Slot next ) { | 
|  | this.key = isLargeKey(key) ? new WeakReference( key ) : (Object)key; | 
|  | this.next = next; | 
|  | } | 
|  |  | 
|  | private LuaValue key() { | 
|  | return (LuaValue) (key instanceof WeakReference ? ((WeakReference) key).get() : key); | 
|  | } | 
|  |  | 
|  | public int keyindex(int hashMask) { | 
|  | // Not needed: this entry will be dropped during rehash. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public StrongSlot first() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public StrongSlot find(LuaValue key) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean keyeq(LuaValue key) { | 
|  | LuaValue k = key(); | 
|  | return k != null && key.raweq(k); | 
|  | } | 
|  |  | 
|  | public Slot rest() { | 
|  | return next; | 
|  | } | 
|  |  | 
|  | public int arraykey(int max) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | public Slot set(StrongSlot target, LuaValue value) { | 
|  | Slot next = ( this.next != null ) ? this.next.set( target, value ) : null; | 
|  | if ( key() != null ) { | 
|  | // if key hasn't been garbage collected, it is still potentially a valid argument | 
|  | // to next(), so we can't drop this entry yet. | 
|  | this.next = next; | 
|  | return this; | 
|  | } else { | 
|  | return next; | 
|  | } | 
|  | } | 
|  |  | 
|  | public Slot add(Slot newEntry) { | 
|  | return ( next != null ) ? next.add(newEntry) : newEntry; | 
|  | } | 
|  |  | 
|  | public Slot remove(StrongSlot target) { | 
|  | if ( key() != null ) { | 
|  | next = next.remove(target); | 
|  | return this; | 
|  | } else { | 
|  | return next; | 
|  | } | 
|  | } | 
|  |  | 
|  | public Slot relink(Slot rest) { | 
|  | return rest; | 
|  | } | 
|  |  | 
|  | public String toString() { | 
|  | StringBuffer buf = new StringBuffer(); | 
|  | buf.append("<dead"); | 
|  | LuaValue k = key(); | 
|  | if (k != null) { | 
|  | buf.append(": "); | 
|  | buf.append(k.toString()); | 
|  | } | 
|  | buf.append('>'); | 
|  | if (next != null) { | 
|  | buf.append("; "); | 
|  | buf.append(next.toString()); | 
|  | } | 
|  | return buf.toString(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | private static final Slot[] NOBUCKETS = {}; | 
|  |  | 
|  | // Metatable operations | 
|  |  | 
|  | public boolean useWeakKeys() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean useWeakValues() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public LuaValue toLuaValue() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public LuaValue wrap(LuaValue value) { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | public LuaValue arrayget(LuaValue[] array, int index) { | 
|  | return array[index]; | 
|  | } | 
|  | } |