mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-03-03 15:30:27 +00:00
Lua 5.2支持
This commit is contained in:
581
UniLua/LuaTable.cs
Normal file
581
UniLua/LuaTable.cs
Normal file
@@ -0,0 +1,581 @@
|
||||
|
||||
// #define DEBUG_DUMMY_TVALUE_MODIFY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
public class LuaTable {
|
||||
public LuaTable MetaTable;
|
||||
public uint NoTagMethodFlags;
|
||||
|
||||
public LuaTable(LuaState l) {
|
||||
InitLuaTable(l);
|
||||
}
|
||||
|
||||
~LuaTable()
|
||||
{
|
||||
Recycle();
|
||||
}
|
||||
|
||||
public StkId Get(ref TValue key)
|
||||
{
|
||||
if(key.Tt == (int)LuaType.LUA_TNIL) { return TheNilValue; }
|
||||
|
||||
if(IsPositiveInteger(ref key))
|
||||
{ return GetInt((int)key.NValue); }
|
||||
|
||||
if(key.Tt == (int)LuaType.LUA_TSTRING)
|
||||
{ return GetStr(key.SValue()); }
|
||||
|
||||
var h = key.GetHashCode();
|
||||
for(var node = GetHashNode(h); node != null; node = node.Next) {
|
||||
if(node.Key.V == key) {
|
||||
{ return node.Val; }
|
||||
}
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public StkId GetInt(int key)
|
||||
{
|
||||
if(0 < key && key-1 < ArrayPart.Length)
|
||||
{ return ArrayPart[key-1]; }
|
||||
|
||||
var k = new TValue();
|
||||
k.SetNValue(key);
|
||||
for(var node = GetHashNode(ref k); node != null; node = node.Next) {
|
||||
if(node.Key.V.TtIsNumber() && node.Key.V.NValue == (double)key) {
|
||||
return node.Val;
|
||||
}
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public StkId GetStr(string key)
|
||||
{
|
||||
var h = key.GetHashCode();
|
||||
for(var node = GetHashNode(h); node != null; node = node.Next) {
|
||||
if(node.Key.V.TtIsString() && node.Key.V.SValue() == key)
|
||||
{ return node.Val; }
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public void Set(ref TValue key, ref TValue val)
|
||||
{
|
||||
var cell = Get(ref key);
|
||||
if(cell == TheNilValue) {
|
||||
cell = NewTableKey(ref key);
|
||||
}
|
||||
cell.V.SetObj(ref val);
|
||||
}
|
||||
|
||||
public void SetInt(int key, ref TValue val)
|
||||
{
|
||||
var cell = GetInt(key);
|
||||
if(cell == TheNilValue) {
|
||||
var k = new TValue();
|
||||
k.SetNValue(key);
|
||||
cell = NewTableKey(ref k);
|
||||
}
|
||||
cell.V.SetObj(ref val);
|
||||
// Console.WriteLine(string.Format("---------------- SetInt {0} -> {1}", key, val));
|
||||
// DumpParts();
|
||||
}
|
||||
|
||||
/*
|
||||
** returns the index of a `key' for table traversals. First goes all
|
||||
** elements in the array part, then elements in the hash part. The
|
||||
** beginning of a traversal is signaled by -1.
|
||||
*/
|
||||
private int FindIndex(StkId key)
|
||||
{
|
||||
if(key.V.TtIsNil())
|
||||
{ return -1; }
|
||||
|
||||
// is `key' inside array part?
|
||||
int i = ArrayIndex(ref key.V);
|
||||
if(0 < i && i <= ArrayPart.Length)
|
||||
{ return i-1; }
|
||||
|
||||
var n = GetHashNode(ref key.V);
|
||||
// check whether `key' is somewhere in the chain
|
||||
for(;;) {
|
||||
if(L.V_RawEqualObj(ref n.Key.V, ref key.V))
|
||||
{ return ArrayPart.Length + n.Index; }
|
||||
n = n.Next;
|
||||
|
||||
// key not found
|
||||
if(n == null) { L.G_RunError("invalid key to 'next'"); }
|
||||
}
|
||||
}
|
||||
|
||||
public bool Next(StkId key, StkId val)
|
||||
{
|
||||
// find original element
|
||||
int i = FindIndex(key);
|
||||
|
||||
// try first array part
|
||||
for(i++; i<ArrayPart.Length; ++i) {
|
||||
if(!ArrayPart[i].V.TtIsNil()) {
|
||||
key.V.SetNValue(i+1);
|
||||
val.V.SetObj(ref ArrayPart[i].V);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then hash part
|
||||
for(i-=ArrayPart.Length; i<HashPart.Length; ++i) {
|
||||
if(!HashPart[i].Val.V.TtIsNil()) {
|
||||
key.V.SetObj(ref HashPart[i].Key.V);
|
||||
val.V.SetObj(ref HashPart[i].Val.V);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int Length
|
||||
{ get {
|
||||
uint j = (uint)ArrayPart.Length;
|
||||
if(j > 0 && ArrayPart[j-1].V.TtIsNil()) {
|
||||
/* there is a boundary in the array part: (binary) search for it */
|
||||
uint i = 0;
|
||||
while(j - i > 1) {
|
||||
uint m = (i+j)/2;
|
||||
if(ArrayPart[m-1].V.TtIsNil()) { j = m; }
|
||||
else { i = m; }
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
/* else must find a boundary in hash part */
|
||||
else if(HashPart == DummyHashPart)
|
||||
return (int)j;
|
||||
else return UnboundSearch(j);
|
||||
} }
|
||||
|
||||
public void Resize(int nasize, int nhsize)
|
||||
{
|
||||
int oasize = ArrayPart.Length;
|
||||
var oldHashPart = HashPart;
|
||||
if(nasize > oasize) // array part must grow?
|
||||
SetArraryVector(nasize);
|
||||
|
||||
// create new hash part with appropriate size
|
||||
SetNodeVector(nhsize);
|
||||
|
||||
// array part must shrink?
|
||||
if(nasize < oasize) {
|
||||
var oldArrayPart = ArrayPart;
|
||||
ArrayPart = DummyArrayPart;
|
||||
// re-insert elements from vanishing slice
|
||||
for(int i=nasize; i<oasize; ++i) {
|
||||
if(!oldArrayPart[i].V.TtIsNil())
|
||||
{ SetInt(i+1, ref oldArrayPart[i].V); }
|
||||
}
|
||||
// shrink array
|
||||
var newArrayPart = new StkId[nasize];
|
||||
for(int i=0; i<nasize; ++i) {
|
||||
newArrayPart[i] = oldArrayPart[i];
|
||||
}
|
||||
ArrayPart = newArrayPart;
|
||||
}
|
||||
|
||||
// re-insert elements from hash part
|
||||
for(int i=0; i<oldHashPart.Length; ++i) {
|
||||
var node = oldHashPart[i];
|
||||
if(!node.Val.V.TtIsNil()) {
|
||||
Set(ref node.Key.V, ref node.Val.V);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldHashPart != DummyHashPart)
|
||||
RecycleHNode(oldHashPart);
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
//
|
||||
// **** PRIVATE below ****
|
||||
//
|
||||
//-----------------------------------------
|
||||
|
||||
private class HNode
|
||||
{
|
||||
public int Index;
|
||||
public StkId Key;
|
||||
public StkId Val;
|
||||
public HNode Next;
|
||||
|
||||
public void CopyFrom(HNode o)
|
||||
{
|
||||
Key.V.SetObj(ref o.Key.V);
|
||||
Val.V.SetObj(ref o.Val.V);
|
||||
Next = o.Next;
|
||||
}
|
||||
}
|
||||
|
||||
private LuaState L;
|
||||
|
||||
private StkId[] ArrayPart;
|
||||
private HNode[] HashPart;
|
||||
private int LastFree;
|
||||
|
||||
private static StkId TheNilValue;
|
||||
private static StkId[] DummyArrayPart;
|
||||
private static HNode DummyNode;
|
||||
private static HNode[] DummyHashPart;
|
||||
|
||||
private const int MAXBITS = 30;
|
||||
private const int MAXASIZE = (1 << MAXBITS);
|
||||
|
||||
static LuaTable()
|
||||
{
|
||||
TheNilValue = new StkId();
|
||||
TheNilValue.V.SetNilValue();
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
TheNilValue.V.Lock_ = true;
|
||||
#endif
|
||||
|
||||
DummyArrayPart = new StkId[0];
|
||||
|
||||
DummyNode = new HNode();
|
||||
DummyNode.Key = TheNilValue;
|
||||
DummyNode.Val = TheNilValue;
|
||||
DummyNode.Next = null;
|
||||
|
||||
DummyHashPart = new HNode[1];
|
||||
DummyHashPart[0] = DummyNode;
|
||||
DummyHashPart[0].Index = 0;
|
||||
}
|
||||
|
||||
#region Small Object Cache
|
||||
private static HNode CacheHead = null;
|
||||
private static object CacheHeadLock = new Object();
|
||||
|
||||
private void Recycle()
|
||||
{
|
||||
if (HashPart != null && HashPart != DummyHashPart)
|
||||
{
|
||||
RecycleHNode(HashPart);
|
||||
HashPart = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void RecycleHNode(HNode[] garbage)
|
||||
{
|
||||
if (garbage == null || garbage.Length == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < garbage.Length-1; i++)
|
||||
{
|
||||
garbage[i].Next = garbage[i + 1];
|
||||
}
|
||||
|
||||
lock(CacheHeadLock) {
|
||||
garbage[garbage.Length - 1].Next = CacheHead;
|
||||
CacheHead = garbage[0];
|
||||
}
|
||||
}
|
||||
|
||||
private HNode NewHNode()
|
||||
{
|
||||
HNode ret;
|
||||
if (CacheHead == null)
|
||||
{
|
||||
ret = new HNode();
|
||||
ret.Key = new StkId();
|
||||
ret.Val = new StkId();
|
||||
}
|
||||
else
|
||||
{
|
||||
lock(CacheHeadLock) {
|
||||
ret = CacheHead;
|
||||
CacheHead = CacheHead.Next;
|
||||
}
|
||||
ret.Next = null;
|
||||
ret.Index = 0;
|
||||
ret.Key.V.SetNilValue();
|
||||
ret.Val.V.SetNilValue();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void InitLuaTable(LuaState lua)
|
||||
{
|
||||
L = lua;
|
||||
ArrayPart = DummyArrayPart;
|
||||
SetNodeVector(0);
|
||||
}
|
||||
|
||||
private bool IsPositiveInteger(ref TValue v)
|
||||
{
|
||||
return (v.TtIsNumber() && v.NValue > 0 && (v.NValue % 1) == 0 && v.NValue <= int.MaxValue); //fix large number key bug
|
||||
}
|
||||
|
||||
private HNode GetHashNode(int hashcode)
|
||||
{
|
||||
uint n = (uint)hashcode;
|
||||
return HashPart[n % HashPart.Length];
|
||||
}
|
||||
|
||||
private HNode GetHashNode(ref TValue v)
|
||||
{
|
||||
if(IsPositiveInteger(ref v)) { return GetHashNode((int)v.NValue); }
|
||||
|
||||
if(v.TtIsString()) { return GetHashNode(v.SValue().GetHashCode()); }
|
||||
|
||||
return GetHashNode(v.GetHashCode());
|
||||
}
|
||||
|
||||
private void SetArraryVector(int size)
|
||||
{
|
||||
Utl.Assert(size >= ArrayPart.Length);
|
||||
|
||||
var newArrayPart = new StkId[size];
|
||||
int i = 0;
|
||||
for( ; i<ArrayPart.Length; ++i) {
|
||||
newArrayPart[i] = ArrayPart[i];
|
||||
}
|
||||
for( ; i<size; ++i) {
|
||||
newArrayPart[i] = new StkId();
|
||||
newArrayPart[i].V.SetNilValue();
|
||||
}
|
||||
ArrayPart = newArrayPart;
|
||||
}
|
||||
|
||||
private void SetNodeVector(int size)
|
||||
{
|
||||
if(size == 0) {
|
||||
HashPart = DummyHashPart;
|
||||
LastFree = size;
|
||||
return;
|
||||
}
|
||||
|
||||
int lsize = CeilLog2(size);
|
||||
if(lsize > MAXBITS) { L.G_RunError("table overflow"); }
|
||||
|
||||
size = (1 << lsize);
|
||||
HashPart = new HNode[size];
|
||||
for(int i=0; i<size; ++i) {
|
||||
HashPart[i] = NewHNode();
|
||||
HashPart[i].Index = i;
|
||||
}
|
||||
LastFree = size;
|
||||
}
|
||||
|
||||
private HNode GetFreePos()
|
||||
{
|
||||
while(LastFree > 0) {
|
||||
var node = HashPart[--LastFree];
|
||||
if(node.Key.V.TtIsNil()) { return node; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
** returns the index for `key' if `key' is an appropriate key to live in
|
||||
** the array part of the table, -1 otherwise.
|
||||
*/
|
||||
private int ArrayIndex(ref TValue k)
|
||||
{
|
||||
if(IsPositiveInteger(ref k))
|
||||
return (int)k.NValue;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static readonly byte[] Log2_ = new byte[] {
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
|
||||
};
|
||||
private int CeilLog2(int x)
|
||||
{
|
||||
Utl.Assert(x > 0);
|
||||
int l = 0;
|
||||
x--;
|
||||
while(x >= 256) { l+=8; x>>=8; }
|
||||
return l + Log2_[x];
|
||||
}
|
||||
|
||||
private int CountInt(ref TValue key, ref int[] nums)
|
||||
{
|
||||
int k = ArrayIndex(ref key);
|
||||
if(0 < k && k <= MAXASIZE) {
|
||||
nums[CeilLog2(k)]++;
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
private int NumUseArray(ref int[] nums)
|
||||
{
|
||||
int ause = 0;
|
||||
int i = 1;
|
||||
for(int lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {
|
||||
int lc = 0; // counter
|
||||
int lim = ttlg;
|
||||
if(lim > ArrayPart.Length) {
|
||||
lim = ArrayPart.Length;
|
||||
if(i > lim) { break; } // no more elements to count
|
||||
}
|
||||
|
||||
// count elements in range (2^(lg-1), 2^lg]
|
||||
for(; i<=lim; ++i) {
|
||||
if(!ArrayPart[i-1].V.TtIsNil()) { lc++; }
|
||||
}
|
||||
nums[lg] += lc;
|
||||
ause += lc;
|
||||
}
|
||||
return ause;
|
||||
}
|
||||
|
||||
private int NumUseHash(ref int[] nums, ref int nasize)
|
||||
{
|
||||
int totaluse = 0;
|
||||
int ause = 0;
|
||||
int i = HashPart.Length;
|
||||
while(i-- > 0) {
|
||||
var n = HashPart[i];
|
||||
if(!n.Val.V.TtIsNil()) {
|
||||
ause += CountInt(ref n.Key.V, ref nums);
|
||||
totaluse++;
|
||||
}
|
||||
}
|
||||
nasize += ause;
|
||||
return totaluse;
|
||||
}
|
||||
|
||||
private int ComputeSizes(ref int[] nums, ref int nasize)
|
||||
{
|
||||
int a = 0;
|
||||
int na = 0;
|
||||
int n = 0;
|
||||
for(int i=0, tti=1; tti/2<nasize; ++i, tti*=2) {
|
||||
if(nums[i] > 0) {
|
||||
a += nums[i];
|
||||
if(a > tti/2) {
|
||||
n = tti;
|
||||
na = a;
|
||||
}
|
||||
}
|
||||
if(a == nasize) { break; } // all elements already conted
|
||||
}
|
||||
nasize = n;
|
||||
Utl.Assert(nasize/2 <= na && na <= nasize);
|
||||
return na;
|
||||
}
|
||||
|
||||
private static int[] Nums = new int[MAXBITS + 1];
|
||||
private void Rehash(ref TValue k)
|
||||
{
|
||||
for(int i=0; i<=MAXBITS; ++i) { Nums[i] = 0; }
|
||||
|
||||
int nasize = NumUseArray(ref Nums);
|
||||
int totaluse = nasize;
|
||||
totaluse += NumUseHash(ref Nums, ref nasize);
|
||||
nasize += CountInt(ref k, ref Nums);
|
||||
totaluse++;
|
||||
int na = ComputeSizes(ref Nums, ref nasize);
|
||||
Resize(nasize, totaluse-na);
|
||||
}
|
||||
|
||||
private void DumpParts()
|
||||
{
|
||||
Console.WriteLine("------------------ [DumpParts] enter -----------------------");
|
||||
Console.WriteLine("<< Array Part >>");
|
||||
for(var i=0; i<ArrayPart.Length; ++i) {
|
||||
var n = ArrayPart[i];
|
||||
Console.WriteLine(string.Format("i:{0} val:{1}", i, n.V));
|
||||
}
|
||||
Console.WriteLine("<< Hash Part >>");
|
||||
for(var i=0; i<HashPart.Length; ++i) {
|
||||
var n = HashPart[i];
|
||||
var next = (n.Next == null) ? -1 : n.Next.Index;
|
||||
Console.WriteLine(string.Format("i:{0} index:{1} key:{2} val:{3} next:{4}", i, n.Index, n.Key.V, n.Val.V, next));
|
||||
}
|
||||
Console.WriteLine("++++++++++++++++++ [DumpParts] leave +++++++++++++++++++++++");
|
||||
}
|
||||
|
||||
private StkId NewTableKey(ref TValue k)
|
||||
{
|
||||
if(k.TtIsNil()) { L.G_RunError("table index is nil"); }
|
||||
|
||||
if(k.TtIsNumber() && System.Double.IsNaN(k.NValue))
|
||||
{ L.G_RunError("table index is NaN"); }
|
||||
|
||||
var mp = GetHashNode(ref k);
|
||||
|
||||
// if main position is taken
|
||||
if(!mp.Val.V.TtIsNil() || mp == DummyNode) {
|
||||
var n = GetFreePos();
|
||||
if(n == null) {
|
||||
Rehash(ref k);
|
||||
var cell = Get(ref k);
|
||||
if(cell != TheNilValue) { return cell; }
|
||||
return NewTableKey(ref k);
|
||||
}
|
||||
|
||||
Utl.Assert(n != DummyNode);
|
||||
var othern = GetHashNode(ref mp.Key.V);
|
||||
// is colliding node out of its main position?
|
||||
if(othern != mp) {
|
||||
while(othern.Next != mp) { othern = othern.Next; }
|
||||
othern.Next = n;
|
||||
n.CopyFrom(mp);
|
||||
mp.Next = null;
|
||||
mp.Val.V.SetNilValue();
|
||||
}
|
||||
// colliding node is in its own main position
|
||||
else {
|
||||
n.Next = mp.Next;
|
||||
mp.Next = n;
|
||||
mp = n;
|
||||
}
|
||||
}
|
||||
|
||||
mp.Key.V.SetObj(ref k);
|
||||
Utl.Assert(mp.Val.V.TtIsNil());
|
||||
return mp.Val;
|
||||
}
|
||||
|
||||
private int UnboundSearch(uint j)
|
||||
{
|
||||
uint i = j;
|
||||
j++;
|
||||
while(!GetInt((int)j).V.TtIsNil()) {
|
||||
i = j;
|
||||
j *= 2;
|
||||
|
||||
// overflow?
|
||||
if(j > LuaLimits.MAX_INT) {
|
||||
/* table was built with bad purposes: resort to linear search */
|
||||
i = 1;
|
||||
while(!GetInt((int)i).V.TtIsNil()) { i++; }
|
||||
return (int)(i-1);
|
||||
}
|
||||
}
|
||||
/* now do a binary search between them */
|
||||
while(j - i > 1) {
|
||||
uint m = (i + j) / 2;
|
||||
if(GetInt((int)m).V.TtIsNil()) { j = m; }
|
||||
else { i = m; }
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user