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-jse Show documentation
Show all versions of luaj-jse Show documentation
Luaj 2.0 for the jse 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() {
return m_metatable;
}
public LuaValue setmetatable(LuaValue 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 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;
}
/** This may be deprecated in a future release.
* It is recommended to count via iteration over next() instead */
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 */
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, val, valmt);
}
}