org.luaj.vm2.LuaTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of luaj-jme Show documentation
Show all versions of luaj-jme Show documentation
Luaj 3.0.1 for the jme platform
/*******************************************************************************
* 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.util.Vector;
public class LuaTable extends LuaValue {
private static final int MIN_HASH_CAPACITY = 2;
private static final LuaString N = valueOf("n");
protected LuaValue[] array;
protected LuaValue[] hashKeys;
protected LuaValue[] hashValues;
protected int hashEntries;
protected LuaValue m_metatable;
public LuaTable() {
array = NOVALS;
hashKeys = NOVALS;
hashValues = NOVALS;
}
public LuaTable(int narray, int nhash) {
presize(narray, nhash);
}
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-(nn>>1));
for ( int i=0; i array.length )
array = resize( array, narray );
}
public void presize(int narray, int nhash) {
if ( nhash > 0 && nhash < MIN_HASH_CAPACITY )
nhash = MIN_HASH_CAPACITY;
array = (narray>0? new LuaValue[narray]: NOVALS);
hashKeys = (nhash>0? new LuaValue[nhash]: NOVALS);
hashValues = (nhash>0? new LuaValue[nhash]: NOVALS);
hashEntries = 0;
}
private static LuaValue[] resize( LuaValue[] old, int n ) {
LuaValue[] v = new LuaValue[n];
System.arraycopy(old, 0, v, 0, old.length);
return v;
}
protected int getArrayLength() {
return array.length;
}
protected int getHashLength() {
return hashValues.length;
}
public LuaValue getmetatable() {
if ( m_metatable!=null )
return m_metatable.rawget(METATABLE).optvalue(m_metatable);
return m_metatable;
}
public LuaValue setmetatable(LuaValue metatable) {
if ( m_metatable!=null && !m_metatable.rawget(METATABLE).isnil() )
error("cannot change a protected metatable");
m_metatable = metatable;
LuaValue mode;
if ( m_metatable!=null && (mode=m_metatable.rawget(MODE)).isstring() ) {
String m = mode.tojstring();
boolean k = m.indexOf('k')>=0;
boolean v = m.indexOf('v')>=0;
return changemode(k,v);
}
return this;
}
protected LuaTable changemode(boolean weakkeys, boolean weakvalues) {
if ( weakkeys || weakvalues )
return new WeakTable(weakkeys, weakvalues, this);
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 )
return array[key-1]!=null? array[key-1]: NIL;
return hashget( LuaInteger.valueOf(key) );
}
public LuaValue rawget( LuaValue key ) {
if ( key.isinttype() ) {
int ikey = key.toint();
if ( ikey>0 && ikey<=array.length )
return array[ikey-1]!=null? array[ikey-1]: NIL;
}
return hashget( key );
}
protected LuaValue hashget(LuaValue key) {
if ( hashEntries > 0 ) {
LuaValue v = hashValues[hashFindSlot(key)];
return v!=null? v: NIL;
}
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 ) {
key.checkvalidkey();
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 );
}
private boolean arrayset( int key, LuaValue value ) {
if ( key>0 && key<=array.length ) {
array[key-1] = (value.isnil()? null: value);
return true;
} else if ( key==array.length+1 && !value.isnil() ) {
expandarray();
array[key-1] = value;
return true;
}
return false;
}
private void expandarray() {
int n = array.length;
int m = Math.max(2,n*2);
array = resize(array, m);
for ( int i=n; i0; --n )
if ( !rawget(n).isnil() )
return LuaInteger.valueOf(n);
return ZERO;
}
/**
* Get the length of this table, as lua defines it.
*/
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 maxn() {
int n = 0;
for ( int i=0; i n )
n = key;
}
}
return n;
}
/**
* Get the next element after a particular key in the table
* @return key,value or nil
*/
/**
* 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 ) {
if ( array[i-1] == null )
error( "invalid key to 'next'" );
break;
}
}
if ( hashKeys.length == 0 )
error( "invalid key to 'next'" );
i = hashFindSlot(key);
if ( hashKeys[i] == null )
error( "invalid key to 'next'" );
i += 1+array.length;
}
} while ( false );
// check array part
for ( ; i=array.length || array[i]==null?
NIL:
varargsOf(LuaInteger.valueOf(i+1),array[i]);
}
/**
* Call the supplied function once for each key-value pair
*
* @param func function to call
*/
public LuaValue foreach(LuaValue func) {
Varargs n;
LuaValue k = NIL;
LuaValue v;
while ( !(k = ((n = next(k)).arg1())).isnil() )
if ( ! (v = func.call(k, n.arg(2))).isnil() )
return v;
return NIL;
}
/**
* Call the supplied function once for each key-value pair
* in the contiguous array part
*
* @param func
*/
public LuaValue foreachi(LuaValue func) {
Varargs n;
LuaValue k = NIL;
LuaValue v;
while ( !(k = ((n = inext(k)).arg1())).isnil() )
if ( ! (v = func.call(k, n.arg(2))).isnil() )
return v;
return NIL;
}
// ======================= hashset =================
public void hashset(LuaValue key, LuaValue value) {
if ( value.isnil() )
hashRemove(key);
else {
if ( hashKeys.length == 0 ) {
hashKeys = new LuaValue[ MIN_HASH_CAPACITY ];
hashValues = new LuaValue[ MIN_HASH_CAPACITY ];
}
int slot = hashFindSlot( key );
if ( hashFillSlot( slot, value ) )
return;
hashKeys[slot] = key;
hashValues[slot] = value;
if ( checkLoadFactor() )
rehash();
}
}
public int hashFindSlot(LuaValue key) {
int i = ( key.hashCode() & 0x7FFFFFFF ) % hashKeys.length;
// This loop is guaranteed to terminate as long as we never allow the
// table to get 100% full.
LuaValue k;
while ( ( k = hashKeys[i] ) != null && !k.eq_b(key) ) {
i = ( i + 1 ) % hashKeys.length;
}
return i;
}
private boolean hashFillSlot( int slot, LuaValue value ) {
hashValues[ slot ] = value;
if ( hashKeys[ slot ] != null ) {
return true;
} else {
++hashEntries;
return false;
}
}
private void hashRemove( LuaValue key ) {
if ( hashKeys.length > 0 ) {
int slot = hashFindSlot( key );
hashClearSlot( slot );
}
}
protected void hashClearSlot( int i ) {
if ( hashKeys[ i ] != null ) {
int j = i;
int n = hashKeys.length;
while ( hashKeys[ j = ( ( j + 1 ) % n ) ] != null ) {
final int k = ( ( hashKeys[ j ].hashCode() )& 0x7FFFFFFF ) % n;
if ( ( j > i && ( k <= i || k > j ) ) ||
( j < i && ( k <= i && k > j ) ) ) {
hashKeys[ i ] = hashKeys[ j ];
hashValues[ i ] = hashValues[ j ];
i = j;
}
}
--hashEntries;
hashKeys[ i ] = null;
hashValues[ i ] = null;
if ( hashEntries == 0 ) {
hashKeys = NOVALS;
hashValues = NOVALS;
}
}
}
private boolean checkLoadFactor() {
// Using a load factor of (n+1) >= 7/8 because that is easy to compute without
// overflow or division.
final int hashCapacity = hashKeys.length;
return hashEntries >= (hashCapacity - (hashCapacity>>3));
}
private void rehash() {
final int oldCapacity = hashKeys.length;
final int newCapacity = oldCapacity+(oldCapacity>>2)+MIN_HASH_CAPACITY;
final LuaValue[] oldKeys = hashKeys;
final LuaValue[] oldValues = hashValues;
hashKeys = new LuaValue[ newCapacity ];
hashValues = new LuaValue[ newCapacity ];
for ( int i = 0; i < oldCapacity; ++i ) {
final LuaValue k = oldKeys[i];
if ( k != null ) {
final LuaValue v = oldValues[i];
final int slot = hashFindSlot( k );
hashKeys[slot] = k;
hashValues[slot] = v;
}
}
}
// ----------------- sort support -----------------------------
//
// implemented heap sort from wikipedia
//
// Only sorts the contiguous array part.
//
public void sort(LuaValue comparator) {
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 = array[i];
LuaValue b = 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;
}
/** @deprecated - count via iteration instead */
public int keyCount() {
return keys().length;
}
/** @deprecated - use next() instead */
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;
}
}