Browse Source

Add: bit32.extract

AnnulusGames 1 year ago
parent
commit
537b9fea17

+ 8 - 0
src/Lua/Exceptions.cs

@@ -73,6 +73,14 @@ public class LuaRuntimeException(Traceback traceback, string message) : LuaExcep
         throw new LuaRuntimeException(traceback, $"bad argument #{argumentId} to '{functionName}' ({expected} expected, got {actual})");
     }
 
+    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaState state, LuaFunction function, int argumentId, double value)
+    {
+        if (!MathEx.IsInteger(value))
+        {
+            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{function.Name}' (number has no integer representation)");
+        }
+    }
+
     public override string Message => $"{LuaTraceback.RootChunkName}:{LuaTraceback.LastPosition.Line}: {base.Message}";
 
     public override string ToString()

+ 29 - 0
src/Lua/Standard/Bitwise/Bit32Helper.cs

@@ -4,6 +4,16 @@ namespace Lua.Standard.Bitwise;
 
 internal static class Bit32Helper
 {
+    static readonly uint[] Masks = [
+        0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF,
+        0x1FF, 0x3FF, 0x7FF, 0xFFF,
+        0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
+        0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
+        0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
+        0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
+        0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
+    ];
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static uint ToUInt32(double d)
     {
@@ -17,4 +27,23 @@ internal static class Bit32Helper
         d = Math.IEEERemainder(d, Math.Pow(2.0, 32.0));
         return (int)d;
     }
+
+    public static uint GetNBitMask(int bits)
+    {
+        if (bits <= 0) return 0;
+        if (bits >= 32) return Masks[31];
+        return Masks[bits - 1];
+    }
+
+    public static void ValidateFieldAndWidth(LuaState state, LuaFunction function, int argumentId, int pos, int width)
+    {
+        if (pos > 31 || (pos + width) > 31)
+            throw new LuaRuntimeException(state.GetTraceback(), "trying to access non-existent bits");
+
+        if (pos < 0)
+            throw new LuaRuntimeException(state.GetTraceback(), $"bad argument #{argumentId} to '{function.Name}' (field cannot be negative)");
+
+        if (width <= 0)
+            throw new LuaRuntimeException(state.GetTraceback(), "bad argument #{argumentId} to '{function.Name}' (width must be positive)");
+    }
 }

+ 30 - 0
src/Lua/Standard/Bitwise/ExtractFunction.cs

@@ -0,0 +1,30 @@
+namespace Lua.Standard.Bitwise;
+
+public sealed class ExtractFunction : LuaFunction
+{
+    public override string Name => "extract";
+    public static readonly ExtractFunction Instance = new();
+
+    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
+        var arg2 = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
+            : 1;
+
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 3, arg2);
+
+        var n = (int)arg0;
+        var field = (int)arg1;
+        var width = (int)arg2;
+
+        Bit32Helper.ValidateFieldAndWidth(context.State, this, 2, field, width);
+
+        var result = (n >> field) & Bit32Helper.GetNBitMask(width);
+        buffer.Span[0] = result;
+        return new(1);
+    }
+}

+ 1 - 0
src/Lua/Standard/OpenLibExtensions.cs

@@ -106,6 +106,7 @@ public static class OpenLibExtensions
         BorFunction.Instance,
         BtestFunction.Instance,
         BxorFunction.Instance,
+        ExtractFunction.Instance,
     ];
 
     public static void OpenBasicLibrary(this LuaState state)