| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- using System.Runtime.CompilerServices;
- using Lua.Internal;
- namespace Lua;
- public sealed class LuaTable
- {
- public LuaTable() : this(8, 8)
- {
- }
- public LuaTable(IEnumerable<LuaValue> values)
- {
- array = values.ToArray();
- dictionary = [];
- }
- public LuaTable(IEnumerable<KeyValuePair<LuaValue, LuaValue>> values)
- {
- array = [];
- dictionary = new Dictionary<LuaValue, LuaValue>(values);
- }
- public LuaTable(int arrayCapacity, int dictionaryCapacity)
- {
- array = new LuaValue[arrayCapacity];
- dictionary = new(dictionaryCapacity);
- }
- LuaValue[] array;
- Dictionary<LuaValue, LuaValue> dictionary;
- LuaTable? metatable;
- public LuaValue this[LuaValue key]
- {
- get
- {
- if (key.Type is LuaValueType.Nil)
- {
- throw new ArgumentException("table index is nil");
- }
- if (TryGetInteger(key, out var index))
- {
- if (index > 0 && index <= array.Length)
- {
- // Arrays in Lua are 1-origin...
- return array[index - 1];
- }
- }
- if (dictionary.TryGetValue(key, out var value)) return value;
- return LuaValue.Nil;
- }
- set
- {
- if (TryGetInteger(key, out var index))
- {
- if (0 < index && index <= array.Length * 2)
- {
- EnsureArrayCapacity(index);
- array[index - 1] = value;
- return;
- }
- }
- if (value.Type is LuaValueType.Nil)
- {
- dictionary.Remove(key);
- }
- else
- {
- dictionary[key] = value;
- }
- }
- }
- public int HashMapCount
- {
- get => dictionary.Count;
- }
- public int ArrayLength
- {
- get
- {
- for (int i = 0; i < array.Length; i++)
- {
- if (array[i].Type is LuaValueType.Nil) return i;
- }
- return array.Length;
- }
- }
- public LuaTable? Metatable
- {
- get => metatable;
- set => metatable = value;
- }
- public bool TryGetValue(LuaValue key, out LuaValue value)
- {
- if (key.Type is LuaValueType.Nil)
- {
- value = default;
- return false;
- }
- if (TryGetInteger(key, out var index))
- {
- if (index > 0 && index <= array.Length)
- {
- value = array[index - 1];
- return value.Type is not LuaValueType.Nil;
- }
- }
- return dictionary.TryGetValue(key, out value);
- }
- public bool ContainsKey(LuaValue key)
- {
- if (key.Type is LuaValueType.Nil)
- {
- return false;
- }
- if (TryGetInteger(key, out var index))
- {
- return index > 0 && index <= array.Length && array[index].Type != LuaValueType.Nil;
- }
- return dictionary.ContainsKey(key);
- }
- public LuaValue RemoveAt(int index)
- {
- if (index <= 0 || index > array.Length)
- {
- throw new IndexOutOfRangeException();
- }
- var arrayIndex = index - 1;
- var value = array[arrayIndex];
- if (arrayIndex < array.Length - 1)
- {
- array.AsSpan(arrayIndex + 1).CopyTo(array.AsSpan(arrayIndex));
- }
- array[^1] = default;
- return value;
- }
- public void Insert(int index, LuaValue value)
- {
- if (index <= 0 || index > array.Length + 1)
- {
- throw new IndexOutOfRangeException();
- }
- var arrayIndex = index - 1;
- EnsureArrayCapacity(array.Length + 1);
- if (arrayIndex != array.Length - 1)
- {
- array.AsSpan(arrayIndex, array.Length - arrayIndex - 1).CopyTo(array.AsSpan(arrayIndex + 1));
- }
-
- array[arrayIndex] = value;
- }
- public KeyValuePair<LuaValue, LuaValue> GetNext(LuaValue key)
- {
- var index = -1;
- if (key.Type is LuaValueType.Nil)
- {
- index = 0;
- }
- else if (TryGetInteger(key, out var integer) && integer > 0 && integer <= array.Length)
- {
- index = integer;
- }
- if (index != -1)
- {
- var span = array.AsSpan(index);
- for (int i = 0; i < span.Length; i++)
- {
- if (span[i].Type is not LuaValueType.Nil)
- {
- return new(index + i + 1, span[i]);
- }
- }
- foreach (var pair in dictionary)
- {
- return pair;
- }
- }
- else
- {
- var foundKey = false;
- foreach (var pair in dictionary)
- {
- if (foundKey) return pair;
- if (pair.Key.Equals(key))
- {
- foundKey = true;
- }
- }
- }
- return default;
- }
- public void Clear()
- {
- dictionary.Clear();
- }
- public Span<LuaValue> GetArraySpan()
- {
- return array.AsSpan();
- }
- internal void EnsureArrayCapacity(int newCapacity)
- {
- if (array.Length >= newCapacity) return;
- var prevLength = array.Length;
- var newLength = array.Length;
- if (newLength == 0) newLength = 8;
- while (newLength < newCapacity)
- {
- newLength *= 2;
- }
- Array.Resize(ref array, newLength);
- using var indexList = new PooledList<(int, LuaValue)>(dictionary.Count);
- // Move some of the elements of the hash part to a newly allocated array
- foreach (var kv in dictionary)
- {
- if (kv.Key.TryRead<double>(out var d) && MathEx.IsInteger(d))
- {
- var index = (int)d;
- if (index > prevLength && index <= newLength)
- {
- indexList.Add((index, kv.Value));
- }
- }
- }
- foreach ((var index, var value) in indexList.AsSpan())
- {
- dictionary.Remove(index);
- array[index - 1] = value;
- }
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static bool TryGetInteger(LuaValue value, out int integer)
- {
- if (value.TryRead<double>(out var num) && MathEx.IsInteger(num))
- {
- integer = (int)num;
- return true;
- }
- integer = default;
- return false;
- }
- }
|