// #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 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 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 MAXBITS) { L.G_RunError("table overflow"); } size = (1 << lsize); HashPart = new HNode[size]; for(int i=0; i 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 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>"); for(var i=0; i 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; } } }