Browse Source

Add Table Resize Test
Limit Max Array to 1 << 24

moogle 8 months ago
parent
commit
fb5056abd3
3 changed files with 62 additions and 8 deletions
  1. 21 1
      src/Lua/Internal/MathEx.cs
  2. 20 7
      src/Lua/LuaTable.cs
  3. 21 0
      tests/Lua.Tests/TableTests.cs

+ 21 - 1
src/Lua/Internal/MathEx.cs

@@ -1,5 +1,7 @@
 using System.Runtime.CompilerServices;
-
+#if NET6_0_OR_GREATER
+using System.Numerics;
+#endif
 namespace Lua;
 
 internal static class MathEx
@@ -86,4 +88,22 @@ internal static class MathEx
     {
         return ((int)Math.Truncate(d), d % 1.0);
     }
+    
+    /// <summary>Returns the smallest power of two greater than or equal to the input.</summary>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int NextPowerOfTwo(int x)
+    {
+#if NET6_0_OR_GREATER
+        if (x <= 0) return 0;
+        return (int)BitOperations.RoundUpToPowerOf2((uint)x);
+#endif
+        if (x <= 0) return 0;
+        x -= 1;
+        x |= x >> 1;
+        x |= x >> 2;
+        x |= x >> 4;
+        x |= x >> 8;
+        x |= x >> 16;
+        return x + 1;
+    }
 }

+ 20 - 7
src/Lua/LuaTable.cs

@@ -19,6 +19,10 @@ public sealed class LuaTable
     readonly LuaValueDictionary dictionary;
     LuaTable? metatable;
 
+    internal LuaValueDictionary Dictionary => dictionary;
+    private const int MaxArraySize = 1 << 24;
+    private const int MaxDistance = 1 << 12;
+
     public LuaValue this[LuaValue key]
     {
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -50,9 +54,16 @@ public sealed class LuaTable
 
                 if (MathEx.IsInteger(d))
                 {
-                    const int maxArraySize = 0x0f4240;
                     var index = (int)d;
-                    if (0 < index && index < maxArraySize && index <= Math.Max(array.Length * 2, 8))
+                    
+                    var distance = index - array.Length;
+                    if (distance > MaxDistance)
+                    {
+                        dictionary[key] = value;
+                        return;
+                    }
+                    
+                    if (0 < index && index < MaxArraySize && index <= Math.Max(array.Length * 2, 8))
                     {
                         if (array.Length < index)
                             EnsureArrayCapacity(index);
@@ -169,6 +180,12 @@ public sealed class LuaTable
         }
 
         var arrayIndex = index - 1;
+        var distance = index - array.Length;
+        if (distance > MaxDistance)
+        {
+            dictionary[index] = value;
+            return;
+        }
 
         if (index > array.Length || array[^1].Type != LuaValueType.Nil)
         {
@@ -250,11 +267,7 @@ public sealed class LuaTable
         var prevLength = array.Length;
         var newLength = array.Length;
         if (newLength == 0) newLength = 8;
-
-        while (newLength < newCapacity)
-        {
-            newLength *= 2;
-        }
+        newLength = newCapacity <= 8 ? 8 : MathEx.NextPowerOfTwo(newCapacity);
 
         Array.Resize(ref array, newLength);
 

+ 21 - 0
tests/Lua.Tests/TableTests.cs

@@ -41,4 +41,25 @@ public class TableTests
         Assert.That(value, Is.EqualTo(new LuaValue(2)));
         Assert.That(table[2], Is.EqualTo(new LuaValue(3)));
     }
+
+    [Test]
+    public void Test_TableResize()
+    {
+        var table = new LuaTable();
+        int i = 1;
+        int count = 10000;
+        while (count > 0)
+        {
+            var key = i;
+            table[key] = key;
+            table[key * 2 - key / 2] = key;
+            i += key;
+            count--;
+        }
+
+        table[1] = 0;
+        table[int.MaxValue - 1] = 0;
+        Assert.That(table[1], Is.EqualTo(new LuaValue(0)));
+        Assert.That(table[int.MaxValue - 1], Is.EqualTo(new LuaValue(0)));
+    }
 }