Weak table implementation.

This commit is contained in:
James Roseborough
2008-04-08 21:55:16 +00:00
parent 720c6027d6
commit 09aa37a837
14 changed files with 501 additions and 270 deletions

View File

@@ -119,7 +119,7 @@ public class DumpState {
dumpInt(n);
for (i = 0; i < n; i++) {
final LValue o = f.k[i];
if (o == LNil.NIL) {
if (o.isNil()) {
writer.write(Lua.LUA_TNIL);
// do nothing more
} else if (o instanceof LBoolean) {

View File

@@ -55,6 +55,7 @@ public class BaseLib extends LFunction {
"tostring",
"unpack",
"next",
"_inext", // not public
};
private static final int INSTALL = 0;
@@ -81,10 +82,16 @@ public class BaseLib extends LFunction {
private static final int TOSTRING = 21;
private static final int UNPACK = 22;
private static final int NEXT = 23;
private static final int INEXT = 24;
private static LFunction next;
private static LFunction inext;
public static void install(LTable globals) {
for ( int i=1; i<NAMES.length; i++ )
for ( int i=1, n=NAMES.length; i<n; i++ )
globals.put( NAMES[i], new BaseLib(i) );
next = new BaseLib(NEXT);
inext = new BaseLib(INEXT);
globals.put("_VERSION", new LString("Lua 5.1"));
}
@@ -142,13 +149,23 @@ public class BaseLib extends LFunction {
vm.resettop();
break;
}
case PAIRS:
case IPAIRS: {
case IPAIRS:
case PAIRS: {
checkargtype(vm,2,Lua.LUA_TTABLE);
LTable v = vm.totable(2);
LValue r = v.luaPairs(id==PAIRS);
LTable t = vm.totable(2);
vm.resettop();
vm.pushlvalue( r );
vm.pushjavafunction( (id==IPAIRS)? inext: next );
vm.pushlvalue( t );
vm.pushnil();
break;
}
case INEXT:
case NEXT: {
checkargtype(vm,2,Lua.LUA_TTABLE);
LTable t = vm.totable(2);
LValue v = vm.topointer(3);
vm.resettop();
t.next(vm,v,(id==INEXT));
break;
}
case GETMETATABLE: {
@@ -358,14 +375,6 @@ public class BaseLib extends LFunction {
vm.pushlvalue(list.get(k));
break;
}
case NEXT: {
checkargtype(vm,2,Lua.LUA_TTABLE);
LTable t = vm.totable(2);
LValue v = vm.topointer(3);
vm.resettop();
t.next(vm,v);
break;
}
default:
LuaState.vmerror( "bad base id" );
}
@@ -472,7 +481,7 @@ public class BaseLib extends LFunction {
return false;
}
LValue v = vm.topointer(2);
if ( v == LNil.NIL )
if ( v.isNil() )
break;
LString s = v.luaAsString();
s.write(baos, 0, s.length());

View File

@@ -229,7 +229,7 @@ public class PackageLib extends LFunction {
e = fname.m_length;
LString key = fname.substring(b, e);
LValue val = table.get(key);
if ( val == LNil.NIL ) { /* no such field? */
if ( val.isNil() ) { /* no such field? */
LTable field = new LTable(); /* new table for field */
table.put(key, field);
table = field;

View File

@@ -28,6 +28,7 @@ import org.luaj.vm.LFunction;
import org.luaj.vm.LString;
import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.LWeakTable;
import org.luaj.vm.LuaState;
@@ -205,7 +206,7 @@ public class TableLib extends LFunction {
vm.resettop();
break;
}
default:
LuaState.vmerror( "bad table id" );
}

View File

@@ -29,6 +29,10 @@ public final class LNil extends LValue {
return luaGetTypeName();
}
public boolean isNil() {
return true;
}
public boolean toJavaBoolean() {
return false;
}

View File

@@ -57,33 +57,35 @@ public class LTable extends LValue {
* Elements of m_hashKeys are never LNil.NIL - they are null to indicate
* the hash slot is empty and some non-null, non-nil value otherwise.
*/
private LValue[] m_hashKeys;
protected LValue[] m_hashKeys;
/**
* Values in the hash part. Must be null when m_hashKeys is null and equal
* in size otherwise.
*/
private LValue[] m_hashValues;
protected LValue[] m_hashValues;
/**
* m_hashEntries is the number of slots that are used. Must always be less
* than m_hashKeys.length.
*/
private int m_hashEntries;
protected int m_hashEntries;
/**
* Array of values to store the "array part" of the table, that is the
* entries with positive integer keys. Elements must never be null: "empty"
* slots are set to LNil.NIL.
*/
private LValue[] m_vector;
protected LValue[] m_vector;
/**
* Number of values in m_vector that non-nil.
*/
private int m_arrayEntries;
protected int m_arrayEntries;
private LTable m_metatable;
private static final int INVALID_KEY_TO_NEXT = -2;
/** Construct an empty LTable with no initial capacity. */
public LTable() {
@@ -123,7 +125,7 @@ public class LTable extends LValue {
if ( key.isInteger() ) {
// call the integer-specific put method
put( key.toJavaInt(), val );
} else if ( val == null || val == LNil.NIL ) {
} else if ( val == null || val.isNil() ) {
// Remove the key if the value is nil. This comes after the check
// for an integer key so that values are properly removed from
// the array part.
@@ -159,7 +161,7 @@ public class LTable extends LValue {
* any.
*/
public void put( int key, LValue value ) {
if (value == null || value == LNil.NIL) {
if (value == null || value.isNil()) {
remove( key );
return;
}
@@ -167,7 +169,7 @@ public class LTable extends LValue {
final int index = key - 1;
for ( ;; ) {
if ( index < m_vector.length ) {
if ( m_vector[index] == LNil.NIL ) {
if ( m_vector[index].isNil() ) {
++m_arrayEntries;
}
m_vector[index] = value;
@@ -231,8 +233,7 @@ public class LTable extends LValue {
if ( m_vector.length > 0 && key.isInteger() ) {
final int index = key.toJavaInt() - 1;
if ( index >= 0 && index < m_vector.length ) {
final LValue v = m_vector[index];
return v != LNil.NIL;
return ! m_vector[index].isNil();
}
}
if ( m_hashKeys == null )
@@ -243,7 +244,7 @@ public class LTable extends LValue {
public void luaGetTable(LuaState vm, LValue table, LValue key) {
LValue v = get(key);
if ( v == LNil.NIL && m_metatable != null ) {
if ( v.isNil() && m_metatable != null ) {
super.luaGetTable( vm, table, key );
} else {
vm.pushlvalue(v);
@@ -264,8 +265,8 @@ public class LTable extends LValue {
*/
public int luaLength() {
for ( int i = Math.max( 0, m_arrayEntries-1 ); i < m_vector.length; ++i ) {
if ( m_vector[i] != LNil.NIL &&
( i+1 == m_vector.length || m_vector[i+1] == LNil.NIL ) ) {
if ( ! m_vector[i].isNil() &&
( i+1 == m_vector.length || m_vector[i+1].isNil() ) ) {
return i+1;
}
}
@@ -278,15 +279,24 @@ public class LTable extends LValue {
}
/** Valid for tables */
public void luaSetMetatable(LValue metatable) {
if ( m_metatable != null && m_metatable.containsKey(TM_METATABLE) )
public LValue luaSetMetatable(LValue metatable) {
if ( m_metatable != null && m_metatable.containsKey(TM_METATABLE) )
throw new LuaErrorException("cannot change a protected metatable");
if ( metatable == null || metatable == LNil.NIL )
if ( metatable == null || metatable.isNil() )
this.m_metatable = null;
else if ( metatable.luaGetType() == Lua.LUA_TTABLE )
this.m_metatable = (LTable) metatable;
else if ( metatable.luaGetType() == Lua.LUA_TTABLE ) {
LTable t = (LTable) metatable;
LValue m = t.get(TM_MODE);
if ( "v".equals(m.toJavaString()) ) {
LTable n = new LWeakTable(this);
n.m_metatable = t;
return n;
}
this.m_metatable = t;
}
else
throw new LuaErrorException("nil or table expected, got "+metatable.luaGetTypeName());
return null;
}
public String toJavaString() {
@@ -296,47 +306,6 @@ public class LTable extends LValue {
public int luaGetType() {
return Lua.LUA_TTABLE;
}
/** Valid for tables */
public LValue luaPairs(boolean isPairs) {
return new LTableIterator(isPairs);
}
/** Iterator for tables */
private final class LTableIterator extends LFunction {
private int arrayIndex;
private int hashIndex;
private final boolean isPairs;
private LTableIterator(boolean isPairs) {
this.arrayIndex = 0;
this.hashIndex = 0;
this.isPairs = isPairs;
}
// perform a lua call
public boolean luaStackCall(LuaState vm) {
vm.resettop();
int i;
while ( ( i = arrayIndex++ ) < m_vector.length ) {
if ( m_vector[i] != LNil.NIL ) {
vm.pushinteger( arrayIndex );
vm.pushlvalue( m_vector[ i ] );
return false;
}
}
if ( isPairs && (m_hashKeys != null) ) {
while ( ( i = hashIndex++ ) < m_hashKeys.length ) {
if ( m_hashKeys[i] != null ) {
vm.pushlvalue( m_hashKeys[i] );
vm.pushlvalue( m_hashValues[i] );
return false;
}
}
}
return false;
}
}
/**
* Helper method to get all the keys in this table in an array. Meant to be
@@ -349,7 +318,7 @@ public class LTable extends LValue {
int out = 0;
for ( int i = 0; i < m_vector.length; ++i ) {
if ( m_vector[ i ] != LNil.NIL ) {
if ( ! m_vector[ i ].isNil() ) {
keys[ out++ ] = LInteger.valueOf( i + 1 );
}
}
@@ -365,11 +334,11 @@ public class LTable extends LValue {
}
/** Remove the value in the table with the given integer key. */
private void remove( int key ) {
protected void remove( int key ) {
if ( key > 0 ) {
final int index = key - 1;
if ( index < m_vector.length ) {
if ( m_vector[ index ] != LNil.NIL ) {
if ( ! m_vector[ index ].isNil() ) {
m_vector[ index ] = LNil.NIL;
--m_arrayEntries;
}
@@ -383,7 +352,7 @@ public class LTable extends LValue {
}
}
private void remove( LValue key ) {
protected void remove( LValue key ) {
if ( m_hashKeys != null ) {
int slot = findSlot( key );
clearSlot( slot );
@@ -522,7 +491,7 @@ public class LTable extends LValue {
} else {
for ( int i = newCapacity; i < oldCapacity; ++i ) {
LValue v = m_vector[i];
if ( v != LNil.NIL ) {
if ( ! v.isNil() ) {
if (checkLoadFactor())
rehash();
final int slot = findSlot( i+1 );
@@ -557,7 +526,7 @@ public class LTable extends LValue {
final int index = Math.max(0,pos==0? m_arrayEntries: pos-1);
if ( m_arrayEntries + 1 > m_vector.length )
resize( ( m_arrayEntries + 1 ) * 2 );
if ( m_vector[index] != LNil.NIL ) {
if ( ! m_vector[index].isNil() ) {
System.arraycopy(m_vector, index, m_vector, index+1, m_vector.length-1-index);
}
m_vector[index] = value;
@@ -572,7 +541,7 @@ public class LTable extends LValue {
public LValue luaRemovePos(int pos) {
if ( pos > m_arrayEntries ) {
LValue val = get( pos );
if ( val != LNil.NIL )
if ( ! val.isNil() )
put( pos, LNil.NIL );
return val;
} else {
@@ -592,7 +561,7 @@ public class LTable extends LValue {
LValue result = LInteger.valueOf(0);
for ( int i = m_vector.length - 1; i >= 0; i-- ) {
if ( m_vector[i] != LNil.NIL ) {
if ( ! m_vector[i].isNil() ) {
result = LInteger.valueOf(i + 1);
break;
}
@@ -648,7 +617,7 @@ public class LTable extends LValue {
}
private boolean compare(int i, int j, LuaState vm, LValue cmpfunc) {
if ( cmpfunc != LNil.NIL ) {
if ( ! cmpfunc.isNil() ) {
vm.pushlvalue(cmpfunc);
vm.pushlvalue(m_vector[i]);
vm.pushlvalue(m_vector[j]);
@@ -670,65 +639,77 @@ public class LTable extends LValue {
/**
* Leave key,value pair on top, or nil if at end of list.
* @param vm the LuaState to leave the values on
* @param indexedonly TODO
* @param index index to start search
* @return true if next exists, false if at end of list
*/
public void next(LuaState vm, LValue key ) {
public boolean next(LuaState vm, LValue key, boolean indexedonly ) {
// find place to start looking
int start = nextKey2StartIndex( vm, key );
int n = (m_vector != null? m_vector.length: 0);
int i = findindex(key, n, indexedonly);
if ( i == INVALID_KEY_TO_NEXT )
vm.error( "invalid key to 'next'" );
// look in vector part
int n = m_vector.length;
if ( start < n ) {
for ( int i=start; i<n; i++ ) {
if ( m_vector[i] != LNil.NIL ) {
vm.pushinteger(i+1);
vm.pushlvalue(m_vector[i]);
return;
}
// check vector part
for ( ++i; i<n; ++i ) {
if ( ! m_vector[i].isNil() ) {
vm.pushinteger(i+1);
vm.pushlvalue(m_vector[i]);
return true;
} else if ( indexedonly ) {
vm.pushnil();
return false;
}
start = n;
}
// look in hash part
if ( m_hashKeys != null ) {
for ( int i=start-n; i<m_hashKeys.length; i++ ) {
// check hash part
if ( (! indexedonly) && (m_hashKeys != null) ) {
int m = m_hashKeys.length;
for ( i-=n; i<m; ++i ) {
if ( m_hashKeys[i] != null ) {
vm.pushlvalue(m_hashKeys[i]);
vm.pushlvalue(m_hashValues[i]);
return;
return true;
}
}
}
// nothing found, return nil.
// nothing found, push nil, return nil.
vm.pushnil();
}
private int nextKey2StartIndex( LuaState vm, LValue key ) {
if ( key == LNil.NIL )
return 0;
int n = m_vector.length;
if ( n > 0 && key.isInteger() ) {
final int index = key.toJavaInt() - 1;
if ( index >= 0 && index < n ) {
if ( m_vector[index] == LNil.NIL )
vm.error( "invalid key to 'next'" );
return index + 1;
}
}
if ( m_hashKeys == null )
vm.error( "invalid key to 'next'" );
int slot = findSlot( key );
if ( m_hashKeys[slot] == null )
vm.error( "invalid key to 'next'" );
return n + slot + 1;
return false;
}
private int findindex (LValue key, int n, boolean indexedonly) {
// first iteration
if ( key.isNil() )
return -1;
// is `key' inside array part?
if ( key.isInteger() ) {
int i = key.toJavaInt();
if ( (0 < i) && (i <= n) ) {
if ( m_vector[i-1] == LNil.NIL )
return INVALID_KEY_TO_NEXT;
return i-1;
}
}
// vector only?
if ( indexedonly )
return n;
if ( m_hashKeys == null )
return INVALID_KEY_TO_NEXT;
// find slot
int slot = findSlot(key);
if ( m_hashKeys[slot] == null )
return INVALID_KEY_TO_NEXT;
return n + slot;
}
/**
* Executes the given f over all elements of table. For each element, f is
@@ -738,35 +719,26 @@ public class LTable extends LValue {
*
* @param vm
* @param function
* @param isforeachi is a table.foreachi() call, not a table.foreach() call
* @param indexedonly is a table.foreachi() call, not a table.foreach() call
* @return
*/
public LValue foreach(LuaState vm, LFunction function, boolean isforeachi) {
for ( int i = 0; i < m_vector.length; ++i ) {
if ( m_vector[ i ] != LNil.NIL ) {
if ( foreachitem( vm, function, LInteger.valueOf(i+1), m_vector[i] ) )
return vm.topointer(1);
}
}
if ( (! isforeachi) && m_hashKeys != null ) {
for ( int i = 0; i < m_hashKeys.length; ++i ) {
if ( m_hashKeys[ i ] != null ) {
if ( foreachitem( vm, function, m_hashKeys[i], m_hashValues[i] ) )
return vm.topointer(1);
}
}
}
return LNil.NIL;
}
public LValue foreach(LuaState vm, LFunction function, boolean indexedonly) {
private static boolean foreachitem(LuaState vm, LFunction f, LValue key, LValue value) {
vm.resettop();
vm.pushlvalue( f );
vm.pushlvalue( key );
vm.pushlvalue( value );
vm.call(2, 1);
return ! vm.isnil(1);
LValue key = LNil.NIL;
while ( true ) {
// push function onto stack
vm.resettop();
vm.pushlvalue(function);
// get next value
if ( ! next(vm,key,indexedonly) )
return LNil.NIL;
key = vm.topointer(2);
// call function
vm.call(2, 1);
if ( ! vm.isnil(-1) )
return vm.poplvalue();
}
}
}

View File

@@ -34,6 +34,9 @@ public class LValue {
/** Metatable tag for intercepting table sets */
public static final LString TM_METATABLE = new LString("__metatable");
/** Metatable tag for setting table mode */
public static final LString TM_MODE = new LString("__mode");
protected void conversionError(String target) {
throw new LuaErrorException( "bad conversion: "+luaGetTypeName()+" to "+target );
}
@@ -59,6 +62,11 @@ public class LValue {
return false;
}
/** Return true if this value is LNil.NIL, false otherwise */
public boolean isNil() {
return false;
}
// perform a lua call, return true if the call is to a lua function, false
// if it ran to completion.
public boolean luaStackCall(LuaState vm) {
@@ -124,7 +132,7 @@ public class LValue {
LTable mt = luaGetMetatable();
if ( mt != null ) {
LValue event = mt.get( TM_NEWINDEX );
if ( event != null && event != LNil.NIL ) {
if ( event != null && ! event.isNil() ) {
event.luaSetTable( vm, table, key, val );
return;
}
@@ -141,7 +149,7 @@ public class LValue {
LTable mt = luaGetMetatable();
if ( mt != null ) {
LValue event = mt.get( TM_INDEX );
if ( event != null && event != LNil.NIL ) {
if ( event != null && ! event.isNil() ) {
event.luaGetTable( vm, table, key );
return;
}
@@ -184,13 +192,14 @@ public class LValue {
return null;
}
/** Valid for tables */
public void luaSetMetatable(LValue metatable) {
/** Valid for tables
* @return TODO*/
public LValue luaSetMetatable(LValue metatable) {
throw new LuaErrorException( "bad argument #1 to 'setmetatable' (table expected, got "+metatable.luaGetTypeName()+")");
}
/** Valid for all types: return the int value identifying the type of this value */
public abstract int luaGetType();
abstract public int luaGetType();
/** Valid for all types: return the type of this value as an LString */
@@ -309,4 +318,8 @@ public class LValue {
return LNil.NIL;
}
/** Dereference a potentially weak reference, and return the value */
public LValue toStrongReference() {
return this;
}
}

View File

@@ -0,0 +1,110 @@
package org.luaj.vm;
import java.lang.ref.WeakReference;
public class LWeakTable extends LTable {
private static class LWeakValue extends LValue {
private WeakReference ref;
private LWeakValue(LValue v) {
ref = new WeakReference(v);
}
public LValue toStrongReference() {
return (LValue) ref.get();
}
public LValue value() {
LValue v = (LValue) ref.get();
return (v!=null? v: LNil.NIL);
}
public String toJavaString() {
return value().toJavaString();
}
public int luaGetType() {
return value().luaGetType();
}
public boolean isNil() {
return value().isNil();
}
}
private static void makestrong(LuaState vm) {
LValue v = vm.poplvalue();
v = v.toStrongReference();
vm.pushlvalue(v!=null? v: LNil.NIL);
}
/** Construct a new LTable with weak-reference keys */
public LWeakTable() {
}
/** Copy constructor */
public LWeakTable(LTable copy) {
this.m_arrayEntries = copy.m_arrayEntries;
this.m_hashEntries = copy.m_hashEntries;
if ( copy.m_vector != null ) {
int n = copy.m_vector.length;
this.m_vector = new LValue[n];
for ( int i=0; i<n; i++ ) {
this.m_vector[i] = ( copy.m_vector[i] != null?
this.m_vector[i] = new LWeakValue(copy.m_vector[i]):
LNil.NIL );
}
}
if ( copy.m_hashKeys != null ) {
int n = copy.m_hashKeys.length;
this.m_hashKeys = new LValue[n];
this.m_hashValues = new LValue[n];
for ( int i=0; i<n; i++ ) {
if ( copy.m_hashKeys[i] != null ) {
this.m_hashKeys[i] = copy.m_hashKeys[i];
this.m_hashValues[i] = new LWeakValue(copy.m_hashValues[i]);
}
}
}
}
public LValue get(int key) {
LValue v = super.get(key).toStrongReference();
if ( v == null ) {
super.remove(key);
return LNil.NIL;
}
return v;
}
public LValue get(LValue key) {
LValue v = super.get(key).toStrongReference();
if ( v == null ) {
super.remove(key);
return LNil.NIL;
}
return v;
}
public void luaInsertPos(int pos, LValue value) {
super.luaInsertPos(pos, new LWeakValue(value));
}
public void put(int key, LValue value) {
super.put(key, new LWeakValue(value));
}
public void put(LValue key, LValue val) {
super.put(key, new LWeakValue(val));
}
public void put(String key, LValue value) {
super.put(key, new LWeakValue(value));
}
public boolean next(LuaState vm, LValue key, boolean indexedonly) {
while ( super.next(vm, key, indexedonly) ) {
makestrong(vm);
if ( ! vm.isnil(-1) )
return true;
vm.pop(1);
key = vm.poplvalue();
}
return false;
}
}

View File

@@ -810,7 +810,7 @@ public class LuaState extends Lua {
adjustTop( cb + c );
// test for continuation
if (this.stack[cb] != LNil.NIL ) { // continue?
if (!this.stack[cb].isNil() ) { // continue?
this.stack[cb-1] = this.stack[cb]; // save control variable
} else {
ci.pc++; // skip over jump
@@ -1463,7 +1463,7 @@ public class LuaState extends Lua {
* 0&nbsp;otherwise.
*/
public boolean isnil(int index) {
return topointer(index) == LNil.NIL;
return topointer(index).isNil();
}
/**
@@ -1499,7 +1499,7 @@ public class LuaState extends Lua {
* string convertible to a number, and 0&nbsp;otherwise.
*/
public boolean isnumber(int index) {
return tolnumber(index) != LNil.NIL;
return ! tolnumber(index).isNil();
}
/**
@@ -1689,7 +1689,7 @@ public class LuaState extends Lua {
for ( int i=0; i<n; i++ )
stack[--top] = null;
}
private LValue poplvalue() {
public LValue poplvalue() {
LValue p = stack[--top];
stack[top] = null;
return p;
@@ -2034,7 +2034,11 @@ public class LuaState extends Lua {
public void setmetatable(int index) {
LTable t = totable(index);
LValue v = poplvalue();
t.luaSetMetatable(v);
LValue n = t.luaSetMetatable(v);
if ( n != null ) {
pushlvalue(n);
replace(index);
}
}
/**

View File

@@ -50,7 +50,7 @@ public class CoerceLuaToJava {
return value.toJavaBoolean()? Boolean.TRUE: Boolean.FALSE;
}
public int score(LValue value) {
if ( value instanceof LBoolean || value == LNil.NIL )
if ( value instanceof LBoolean || value.isNil() )
return 0;
if ( value instanceof LNumber )
return 1;
@@ -66,7 +66,7 @@ public class CoerceLuaToJava {
return 0;
if ( value instanceof LNumber )
return 1;
if ( value instanceof LBoolean || value == LNil.NIL )
if ( value instanceof LBoolean || value.isNil() )
return 2;
return 4;
}
@@ -80,7 +80,7 @@ public class CoerceLuaToJava {
return 0;
if ( value instanceof LNumber )
return 1;
if ( value instanceof LBoolean || value == LNil.NIL )
if ( value instanceof LBoolean || value.isNil() )
return 2;
return 4;
}
@@ -107,7 +107,7 @@ public class CoerceLuaToJava {
return new Double(value.toJavaDouble());
if ( value instanceof LBoolean )
return Boolean.valueOf(value.toJavaBoolean());
if ( value == LNil.NIL )
if ( value.isNil() )
return null;
return value;
}

View File

@@ -77,6 +77,7 @@ public class LuaRunner {
System.err.println(script+" lua error, "+lee.getMessage() );
} catch ( Throwable t ) {
System.err.println(script+" threw "+t);
t.printStackTrace();
} finally {
System.out.flush();
System.err.flush();

View File

@@ -0,0 +1,53 @@
package org.luaj.vm;
import java.util.Random;
import junit.framework.TestCase;
public class LWeakTableTest extends TestCase {
Random random = new Random(0);
Runtime rt = Runtime.getRuntime();
private void runTest(int n,int i0,int i1,int di) {
System.out.println("------- testing "+n+" keys up to "+i1+" bytes each ("+(n*i1)+" bytes total)");
LTable t = new LWeakTable();
for ( int i=0; i<n; i++ )
t.put(i, new LString(new byte[1]));
for ( int i=i0; i<=i1; i+=di ) {
int hits = 0;
for ( int j=0; j<100; j++ ) {
int k = random.nextInt(n);
LValue v = t.get(k);
if ( v != LNil.NIL )
hits++;
t.put(i, new LString(new byte[i]));
}
long total = rt.totalMemory() / 1000;
long free = rt.freeMemory() / 1000;
long used = (rt.totalMemory() - rt.freeMemory()) / 1000;
System.out.println("keys="+n+" bytes="+i+" mem u(f,t)="+used+"("+free+"/"+total+") hits="+hits+"/100");
}
}
public void testWeakTable5000() {
runTest(100,0,5000,500);
}
public void testWeakTable10000() {
runTest(100,0,10000,1000);
}
public void testWeakTable100() {
runTest(100,0,100,10);
}
public void testWeakTable1000() {
runTest(100,0,1000,100);
}
public void testWeakTable2000() {
runTest(100,0,2000,200);
}
}

View File

@@ -18,69 +18,70 @@ tryconcat( { a='aaa', b='bbb', c='ccc', d='ddd', e='eee' } )
tryconcat( { [501]="one", [502]="two", [503]="three", [504]="four", [505]="five" } )
tryconcat( {} )
-- print the elements of a table in a platform-independent way
function eles(t,f)
f = f or pairs
all = {}
for k,v in f(t) do
table.insert( all, "["..tostring(k).."]="..tostring(v) )
end
table.sort( all )
return "{"..table.concat(all,',').."}"
end
-- insert, maxn
print( '-- insert, maxn tests' )
local t = { "one", "two", "three", a='aaa', b='bbb', c='ccc' }
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
table.insert(t,'six')
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
table.insert(t,1,'seven')
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
table.insert(t,4,'eight')
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
table.insert(t,7,'nine')
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
table.insert(t,10,'ten')
print( table.concat(t,'-'), table.maxn(t), #t, table.getn(t) )
print( t[10] )
print( table.maxn({}), #{} )
print( eles(t) )
table.insert(t,'six'); print( eles(t) )
table.insert(t,1,'seven'); print( eles(t) )
table.insert(t,4,'eight'); print( eles(t) )
table.insert(t,7,'nine'); print( eles(t) )
table.insert(t,10,'ten'); print( eles(t) )
-- remove
print( '-- remove tests' )
t = { "one", "two", "three", "four", "five", "six", "seven", [10]="ten", a='aaa', b='bbb', c='ccc' }
print( table.concat(t,'-'), table.maxn(t), #t )
print( table.remove(t) )
print( table.concat(t,'-'), table.maxn(t) )
print( table.remove(t,1) )
print( table.concat(t,'-'), table.maxn(t) )
print( table.remove(t,3) )
print( table.concat(t,'-'), table.maxn(t) )
print( table.remove(t,5) )
print( table.concat(t,'-'), table.maxn(t), t[10] )
print( table.remove(t,10) )
print( table.concat(t,'-'), table.maxn(t), t[10] )
print( table.remove(t,-1) )
print( table.concat(t,'-'), table.maxn(t), t[10] )
print( table.remove(t,-1) )
print( table.concat(t,'-'), table.maxn(t), t[10] )
print( eles(t) )
print( 'table.remove(t)', table.remove(t) ); print( eles(t) )
print( 'table.remove(t,1)', table.remove(t,1) ); print( eles(t) )
print( 'table.remove(t,3)', table.remove(t,3) ); print( eles(t) )
print( 'table.remove(t,5)', table.remove(t,5) ); print( eles(t) )
print( 'table.remove(t,10)', table.remove(t,10) ); print( eles(t) )
print( 'table.remove(t,-1)', table.remove(t,-1) ); print( eles(t) )
print( 'table.remove(t,-1)', table.remove(t,-1) ) ; print( eles(t) )
-- sort
print( '-- sort tests' )
t = { "one", "two", "three", a='aaa', b='bbb', c='ccc' }
print( table.concat(t,'-'), table.maxn(t), #t )
table.sort(t)
print( table.concat(t,'-'), table.maxn(t), #t )
t = { "zzz", "yyy", "xxx", "www", "vvv", "uuu", "ttt", "sss" }
print( table.concat(t,'-'), table.maxn(t), #t )
table.sort(t)
print( table.concat(t,'-'), table.maxn(t), #t )
table.sort(t,function(a,b) return b<a end)
print( table.concat(t,'-'), table.maxn(t), #t )
function sorttest(t,f)
t = (t)
print( table.concat(t,'-') )
if f then
table.sort(t,f)
else
table.sort(t)
end
print( table.concat(t,'-') )
end
sorttest{ "one", "two", "three" }
sorttest{ "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }
sorttest( { "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }, function(a,b) return b<a end)
-- getn
t0 = {}
t1 = { 'one', 'two', 'three' }
t2 = { a='aa', b='bb', c='cc' }
t3 = { 'one', 'two', 'three', a='aa', b='bb', c='cc' }
print( 'getn(t0)', pcall( table.getn, t0 ) )
print( 'getn(t0)', pcall( table.getn, t1 ) )
print( 'getn(t0)', pcall( table.getn, t2 ) )
print( 'getn(t0)', pcall( table.getn, t3 ) )
print( 'getn('..eles(t0)..')', pcall( table.getn, t0 ) )
print( 'getn('..eles(t1)..')', pcall( table.getn, t1 ) )
print( 'getn('..eles(t2)..')', pcall( table.getn, t2 ) )
print( 'getn('..eles(t3)..')', pcall( table.getn, t3 ) )
-- foreach
function test( f, t, result, name )
status, value = pcall( f, t, function(...)
print(name,...)
print(' -- ',...)
print(' next',next(t,(...)))
return result
end )
print( name, 's,v', status, value )
@@ -90,12 +91,32 @@ function testall( f, t, name )
test( f, t, false, name..'fls' )
test( f, t, 100, name..'100' )
end
testall( table.foreach, t0, 'table.foreach(t0)' )
testall( table.foreach, t1, 'table.foreach(t1)' )
testall( table.foreach, t2, 'table.foreach(t2)' )
testall( table.foreach, t3, 'table.foreach(t3)' )
testall( table.foreachi, t0, 'table.foreachi(t0)' )
testall( table.foreachi, t1, 'table.foreachi(t1)' )
testall( table.foreachi, t2, 'table.foreachi(t2)' )
testall( table.foreachi, t3, 'table.foreachi(t3)' )
testall( table.foreach, t0, 'table.foreach('..eles(t0)..')' )
testall( table.foreach, t1, 'table.foreach('..eles(t1)..')' )
testall( table.foreach, t2, 'table.foreach('..eles(t2)..')' )
testall( table.foreach, t3, 'table.foreach('..eles(t3)..')' )
testall( table.foreachi, t0, 'table.foreachi('..eles(t0)..')' )
testall( table.foreachi, t1, 'table.foreachi('..eles(t1)..')' )
testall( table.foreachi, t2, 'table.foreachi('..eles(t2)..')' )
testall( table.foreachi, t3, 'table.foreachi('..eles(t3)..')' )
-- pairs, ipairs
function testpairs(f, t, name)
print( name )
for a,b in f(t) do
print( ' ', a, b )
end
end
function testbothpairs(t)
testpairs( pairs, t, 'pairs( '..eles(t)..' )' )
testpairs( ipairs, t, 'ipairs( '..eles(t)..' )' )
end
for i,t in ipairs({t0,t1,t2,t3}) do
testbothpairs(t)
end
t = { 'one', 'two', 'three', 'four', 'five' }
testbothpairs(t)
t[6] = 'six'
testbothpairs(t)
t[4] = nil
testbothpairs(t)

View File

@@ -1,12 +1,14 @@
-- concat
print( '-- weak table tests' )
-- construct new weak table
function newweak(t)
return setmetatable(t,{__mode="v"})
function newtable(t)
n = setmetatable(t,{__mode="v"})
for k,v in pairs(t) do
n[k] = v
end
return n;
end
-- print the elements of a table in a platform-independent way
-- normalized printing
function eles(t,f)
f = f or pairs
all = {}
@@ -14,12 +16,47 @@ function eles(t,f)
table.insert( all, "["..tostring(k).."]="..tostring(v) )
end
table.sort( all )
return "{"..table.concat(all,',').."}"
return tostring(t).."{"..table.concat(all,',').."}"
end
-- basic weak-reference table test
local src = "return { 'one', 'two', 'three', 'four', a='aaa', b='bbb', c='ccc', d='ddd'}"
local weak = newtable( loadstring(src)() )
local strong = { weak[1], weak[3], a=weak.a, c=weak.c }
print( 'before, weak:', eles(weak) )
print( 'before, strong:', eles(strong) )
print( 'gc', pcall( collectgarbage, "collect" ) )
print( 'after, weak:', eles(weak) )
print( 'after, strong:', eles(strong) )
print( 'gc', pcall( collectgarbage, "collect" ) )
print( 'after, weak:', eles(weak) )
print( 'after, strong:', eles(strong) )
print( '-- concat tests' )
function tryconcat(t)
print( table.concat(t) )
print( table.concat(t,'--') )
print( table.concat(t,',',2) )
print( table.concat(t,',',2,2) )
print( table.concat(t,',',5,2) )
end
tryconcat( newtable{ "one", "two", "three", a='aaa', b='bbb', c='ccc' } )
tryconcat( newtable{ "one", "two", "three", "four", "five" } )
function tryconcat(t)
print( table.concat(t) )
print( table.concat(t,'--') )
print( table.concat(t,',',2) )
end
tryconcat( newtable{ a='aaa', b='bbb', c='ccc', d='ddd', e='eee' } )
tryconcat( newtable{ [501]="one", [502]="two", [503]="three", [504]="four", [505]="five" } )
tryconcat( newtable{} )
-- insert, maxn
print( '-- insert, maxn tests' )
local t = newweak{ "one", "two", "three", a='aaa', b='bbb', c='ccc' }
local t = newtable{ "one", "two", "three", a='aaa', b='bbb', c='ccc' }
print( eles(t) )
table.insert(t,'six'); print( eles(t) )
table.insert(t,1,'seven'); print( eles(t) )
@@ -29,20 +66,20 @@ table.insert(t,10,'ten'); print( eles(t) )
-- remove
print( '-- remove tests' )
t = newweak{ "one", "two", "three", "four", "five", "six", "seven", [10]="ten", a='aaa', b='bbb', c='ccc' }
t = newtable{ "one", "two", "three", "four", "five", "six", "seven", [10]="ten", a='aaa', b='bbb', c='ccc' }
print( eles(t) )
print( table.remove(t) ); print( eles(t) )
print( table.remove(t,1) ); print( eles(t) )
print( table.remove(t,3) ); print( eles(t) )
print( table.remove(t,5) ); print( eles(t) )
print( table.remove(t,10) ); print( eles(t) )
print( table.remove(t,-1) ); print( eles(t) )
print( table.remove(t,-1) ) ; print( eles(t) )
print( 'table.remove(t)', table.remove(t) ); print( eles(t) )
print( 'table.remove(t,1)', table.remove(t,1) ); print( eles(t) )
print( 'table.remove(t,3)', table.remove(t,3) ); print( eles(t) )
print( 'table.remove(t,5)', table.remove(t,5) ); print( eles(t) )
print( 'table.remove(t,10)', table.remove(t,10) ); print( eles(t) )
print( 'table.remove(t,-1)', table.remove(t,-1) ); print( eles(t) )
print( 'table.remove(t,-1)', table.remove(t,-1) ) ; print( eles(t) )
-- sort
print( '-- sort tests' )
function sorttest(t,f)
t = newweak(t)
t = (t)
print( table.concat(t,'-') )
if f then
table.sort(t,f)
@@ -50,25 +87,28 @@ function sorttest(t,f)
table.sort(t)
end
print( table.concat(t,'-') )
end
sorttest{ "one", "two", "three" }
sorttest{ "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }
sorttest( { "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }, function(a,b) return b<a end)
end
--[[
sorttest( newtable{ "one", "two", "three" } )
sorttest( newtable{ "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" } )
sorttest( newtable{ "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }, function(a,b) return b<a end)
--]]
-- getn
t0 = newweak{}
t1 = newweak{ 'one', 'two', 'three' }
t2 = newweak{ a='aa', b='bb', c='cc' }
t3 = newweak{ 'one', 'two', 'three', a='aa', b='bb', c='cc' }
print( 'getn(t0)', pcall( table.getn, t0 ) )
print( 'getn(t1)', pcall( table.getn, t1 ) )
print( 'getn(t2)', pcall( table.getn, t2 ) )
print( 'getn(t3)', pcall( table.getn, t3 ) )
t0 = newtable{}
t1 = newtable{ 'one', 'two', 'three' }
t2 = newtable{ a='aa', b='bb', c='cc' }
t3 = newtable{ 'one', 'two', 'three', a='aa', b='bb', c='cc' }
print( 'getn('..eles(t0)..')', pcall( table.getn, t0 ) )
print( 'getn('..eles(t1)..')', pcall( table.getn, t1 ) )
print( 'getn('..eles(t2)..')', pcall( table.getn, t2 ) )
print( 'getn('..eles(t3)..')', pcall( table.getn, t3 ) )
-- foreach
function test( f, t, result, name )
status, value = pcall( f, t, function(...)
print(name,...)
print(' -- ',...)
print(' next',next(t,(...)))
return result
end )
print( name, 's,v', status, value )
@@ -78,29 +118,32 @@ function testall( f, t, name )
test( f, t, false, name..'fls' )
test( f, t, 100, name..'100' )
end
testall( table.foreach, t0, 'table.foreach(t0)' )
testall( table.foreach, t1, 'table.foreach(t1)' )
testall( table.foreach, t2, 'table.foreach(t2)' )
testall( table.foreach, t3, 'table.foreach(t3)' )
testall( table.foreachi, t0, 'table.foreachi(t0)' )
testall( table.foreachi, t1, 'table.foreachi(t1)' )
testall( table.foreachi, t2, 'table.foreachi(t2)' )
testall( table.foreachi, t3, 'table.foreachi(t3)' )
testall( table.foreach, t0, 'table.foreach('..eles(t0)..')' )
testall( table.foreach, t1, 'table.foreach('..eles(t1)..')' )
testall( table.foreach, t2, 'table.foreach('..eles(t2)..')' )
testall( table.foreach, t3, 'table.foreach('..eles(t3)..')' )
testall( table.foreachi, t0, 'table.foreachi('..eles(t0)..')' )
testall( table.foreachi, t1, 'table.foreachi('..eles(t1)..')' )
testall( table.foreachi, t2, 'table.foreachi('..eles(t2)..')' )
testall( table.foreachi, t3, 'table.foreachi('..eles(t3)..')' )
-- pairs, ipairs
function testpairs(f, t, name)
print( name, unpack(t) )
print( name )
for a,b in f(t) do
print( a, b )
print( ' ', a, b )
end
end
testpairs( pairs, t0, 'pairs(t0)' )
testpairs( pairs, t1, 'pairs(t1)' )
testpairs( pairs, t2, 'pairs(t2)' )
testpairs( pairs, t3, 'pairs(t3)' )
testpairs( ipairs, t0, 'ipairs(t0)' )
testpairs( ipairs, t1, 'ipairs(t1)' )
testpairs( ipairs, t2, 'ipairs(t2)' )
testpairs( ipairs, t3, 'ipairs(t3)' )
function testbothpairs(t)
testpairs( pairs, t, 'pairs( '..eles(t)..' )' )
testpairs( ipairs, t, 'ipairs( '..eles(t)..' )' )
end
for i,t in ipairs({t0,t1,t2,t3}) do
testbothpairs(t)
end
t = newtable{ 'one', 'two', 'three', 'four', 'five' }
testbothpairs(t)
t[6] = 'six'
testbothpairs(t)
t[4] = nil
testbothpairs(t)