From 7c82b476b7b895af29ff469eb00b05a591fe1616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Delabrouille?= <34384633+tdelabro@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:23:45 +0200 Subject: [PATCH] chore: create ScriptNum type and use it instead of i64 (#64) --- src/script/engine.zig | 3 +- src/script/lib.zig | 2 + src/script/opcodes/arithmetic.zig | 167 +++++++++++++++--------------- src/script/stack.zig | 17 +-- 4 files changed, 97 insertions(+), 92 deletions(-) diff --git a/src/script/engine.zig b/src/script/engine.zig index 313f2f1..b561f80 100644 --- a/src/script/engine.zig +++ b/src/script/engine.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const Stack = @import("stack.zig").Stack; const Script = @import("lib.zig").Script; +const ScriptNum = @import("lib.zig").ScriptNum; const ScriptFlags = @import("lib.zig").ScriptFlags; const arithmetic = @import("opcodes/arithmetic.zig"); const Opcode = @import("opcodes/constant.zig").Opcode; @@ -410,7 +411,7 @@ pub const Engine = struct { fn opSize(self: *Engine) !void { const top_value = try self.stack.pop(); const len = top_value.len; - const result: i64 = @intCast(len); + const result: ScriptNum = @intCast(len); try self.stack.pushElement(top_value); try self.stack.pushInt(result); diff --git a/src/script/lib.zig b/src/script/lib.zig index 114a758..879ef08 100644 --- a/src/script/lib.zig +++ b/src/script/lib.zig @@ -3,6 +3,8 @@ pub const stack = @import("stack.zig"); pub const arithmetic = @import("opcodes/arithmetic.zig"); const StackError = @import("stack.zig").StackError; +pub const ScriptNum = i64; + /// Maximum number of bytes pushable to the stack const MAX_SCRIPT_ELEMENT_SIZE = 520; diff --git a/src/script/opcodes/arithmetic.zig b/src/script/opcodes/arithmetic.zig index bd61d8e..9b731e2 100644 --- a/src/script/opcodes/arithmetic.zig +++ b/src/script/opcodes/arithmetic.zig @@ -2,6 +2,7 @@ const std = @import("std"); const testing = std.testing; const Engine = @import("../engine.zig").Engine; const Script = @import("../lib.zig").Script; +const ScriptNum = @import("../lib.zig").ScriptNum; const ScriptFlags = @import("../lib.zig").ScriptFlags; const StackError = @import("../stack.zig").StackError; @@ -22,8 +23,8 @@ pub fn op1Sub(self: *Engine) !void { /// OP_NEGATE: Negate the top stack item pub fn opNegate(self: *Engine) !void { const value = try self.stack.popInt(); - const result = if (value == std.math.minInt(i64)) - std.math.minInt(i64) + const result = if (value == std.math.minInt(ScriptNum)) + std.math.minInt(ScriptNum) else -value; try self.stack.pushInt(result); @@ -32,8 +33,8 @@ pub fn opNegate(self: *Engine) !void { /// Computes the absolute value of the top stack item pub fn opAbs(engine: *Engine) !void { const value = try engine.stack.popInt(); - const result = if (value == std.math.minInt(i64)) - std.math.minInt(i64) // Handle overflow case + const result = if (value == std.math.minInt(ScriptNum)) + std.math.minInt(ScriptNum) // Handle overflow case else if (value < 0) -value else @@ -51,7 +52,7 @@ pub fn opNot(self: *Engine) !void { /// Pushes 1 if the top stack item is not 0, 0 otherwise pub fn op0NotEqual(self: *Engine) !void { const value = try self.stack.popInt(); - const result: i64 = if (value != 0) 1 else 0; + const result: ScriptNum = if (value != 0) 1 else 0; try self.stack.pushInt(result); } @@ -179,15 +180,15 @@ test "OP_1ADD operation" { // Test cases const testCases = [_]struct { - input: i64, - expected: i64, + input: ScriptNum, + expected: ScriptNum, }{ .{ .input = 0, .expected = 1 }, .{ .input = -1, .expected = 0 }, .{ .input = 42, .expected = 43 }, .{ .input = -100, .expected = -99 }, - .{ .input = std.math.maxInt(i64), .expected = std.math.minInt(i64) }, // Overflow case - .{ .input = std.math.minInt(i64), .expected = std.math.minInt(i64) + 1 }, + .{ .input = std.math.maxInt(ScriptNum), .expected = std.math.minInt(ScriptNum) }, // Overflow case + .{ .input = std.math.minInt(ScriptNum), .expected = std.math.minInt(ScriptNum) + 1 }, }; for (testCases) |tc| { @@ -218,16 +219,16 @@ test "OP_1SUB operation" { // Test cases const testCases = [_]struct { - input: i64, - expected: i64, + input: ScriptNum, + expected: ScriptNum, }{ .{ .input = 0, .expected = -1 }, .{ .input = 1, .expected = 0 }, .{ .input = -1, .expected = -2 }, .{ .input = 42, .expected = 41 }, .{ .input = -100, .expected = -101 }, - .{ .input = std.math.maxInt(i64), .expected = std.math.maxInt(i64) - 1 }, - .{ .input = std.math.minInt(i64), .expected = std.math.maxInt(i64) }, // Underflow case + .{ .input = std.math.maxInt(ScriptNum), .expected = std.math.maxInt(ScriptNum) - 1 }, + .{ .input = std.math.minInt(ScriptNum), .expected = std.math.maxInt(ScriptNum) }, // Underflow case }; for (testCases) |tc| { @@ -258,16 +259,16 @@ test "OP_NEGATE operation" { // Test cases const testCases = [_]struct { - input: i64, - expected: i64, + input: ScriptNum, + expected: ScriptNum, }{ .{ .input = 0, .expected = 0 }, .{ .input = 1, .expected = -1 }, .{ .input = -1, .expected = 1 }, .{ .input = 42, .expected = -42 }, .{ .input = -42, .expected = 42 }, - .{ .input = std.math.maxInt(i64), .expected = -std.math.maxInt(i64) }, - .{ .input = std.math.minInt(i64), .expected = std.math.minInt(i64) }, // Special case + .{ .input = std.math.maxInt(ScriptNum), .expected = -std.math.maxInt(ScriptNum) }, + .{ .input = std.math.minInt(ScriptNum), .expected = std.math.minInt(ScriptNum) }, // Special case }; for (testCases) |tc| { @@ -298,16 +299,16 @@ test "OP_ABS operation" { // Test cases const testCases = [_]struct { - input: i64, - expected: i64, + input: ScriptNum, + expected: ScriptNum, }{ .{ .input = 0, .expected = 0 }, .{ .input = 1, .expected = 1 }, .{ .input = -1, .expected = 1 }, .{ .input = 42, .expected = 42 }, .{ .input = -42, .expected = 42 }, - .{ .input = std.math.maxInt(i64), .expected = std.math.maxInt(i64) }, - .{ .input = std.math.minInt(i64), .expected = std.math.minInt(i64) }, // Special case + .{ .input = std.math.maxInt(ScriptNum), .expected = std.math.maxInt(ScriptNum) }, + .{ .input = std.math.minInt(ScriptNum), .expected = std.math.minInt(ScriptNum) }, // Special case }; for (testCases) |tc| { @@ -338,7 +339,7 @@ test "OP_NOT operation" { // Test cases const testCases = [_]struct { - input: i64, + input: ScriptNum, expected: bool, }{ .{ .input = 0, .expected = true }, @@ -346,8 +347,8 @@ test "OP_NOT operation" { .{ .input = -1, .expected = false }, .{ .input = 42, .expected = false }, .{ .input = -42, .expected = false }, - .{ .input = std.math.maxInt(i64), .expected = false }, - .{ .input = std.math.minInt(i64), .expected = false }, // Special case + .{ .input = std.math.maxInt(ScriptNum), .expected = false }, + .{ .input = std.math.minInt(ScriptNum), .expected = false }, // Special case }; for (testCases) |tc| { @@ -378,16 +379,16 @@ test "OP_0NOTEQUAL operation" { // Test cases const testCases = [_]struct { - input: i64, - expected: i64, + input: ScriptNum, + expected: ScriptNum, }{ .{ .input = 0, .expected = 0 }, .{ .input = 1, .expected = 1 }, .{ .input = -1, .expected = 1 }, .{ .input = 42, .expected = 1 }, .{ .input = -42, .expected = 1 }, - .{ .input = std.math.maxInt(i64), .expected = 1 }, - .{ .input = std.math.minInt(i64), .expected = 1 }, // Special case + .{ .input = std.math.maxInt(ScriptNum), .expected = 1 }, + .{ .input = std.math.minInt(ScriptNum), .expected = 1 }, // Special case }; for (testCases) |tc| { @@ -418,9 +419,9 @@ test "OP_ADD operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, - expected: i64, + a: ScriptNum, + b: ScriptNum, + expected: ScriptNum, }{ .{ .a = 0, .b = 0, .expected = 0 }, .{ .a = 0, .b = 1, .expected = 1 }, @@ -429,8 +430,8 @@ test "OP_ADD operation" { .{ .a = -1, .b = 1, .expected = 0 }, .{ .a = 42, .b = 42, .expected = 84 }, .{ .a = -42, .b = 42, .expected = 0 }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = std.math.minInt(i64) }, // Overflow case - .{ .a = std.math.minInt(i64), .b = -1, .expected = std.math.maxInt(i64) }, // Underflow case + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = std.math.minInt(ScriptNum) }, // Overflow case + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = std.math.maxInt(ScriptNum) }, // Underflow case }; for (testCases) |tc| { @@ -462,9 +463,9 @@ test "OP_SUB operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, - expected: i64, + a: ScriptNum, + b: ScriptNum, + expected: ScriptNum, }{ .{ .a = 0, .b = 0, .expected = 0 }, .{ .a = 0, .b = 1, .expected = -1 }, @@ -473,8 +474,8 @@ test "OP_SUB operation" { .{ .a = -1, .b = 1, .expected = -2 }, .{ .a = 42, .b = 42, .expected = 0 }, .{ .a = -42, .b = 42, .expected = -84 }, - .{ .a = std.math.maxInt(i64), .b = -1, .expected = std.math.minInt(i64) }, // Overflow case - .{ .a = std.math.minInt(i64), .b = 1, .expected = std.math.maxInt(i64) }, // Underflow case + .{ .a = std.math.maxInt(ScriptNum), .b = -1, .expected = std.math.minInt(ScriptNum) }, // Overflow case + .{ .a = std.math.minInt(ScriptNum), .b = 1, .expected = std.math.maxInt(ScriptNum) }, // Underflow case }; for (testCases) |tc| { @@ -506,8 +507,8 @@ test "OP_BOOLOR operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = false }, @@ -517,8 +518,8 @@ test "OP_BOOLOR operation" { .{ .a = -1, .b = 1, .expected = true }, .{ .a = 42, .b = 42, .expected = true }, .{ .a = -42, .b = 42, .expected = true }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = true }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = true }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = true }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = true }, }; for (testCases) |tc| { @@ -550,8 +551,8 @@ test "OP_NUMEQUAL operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = true }, @@ -561,8 +562,8 @@ test "OP_NUMEQUAL operation" { .{ .a = -1, .b = 1, .expected = false }, .{ .a = 42, .b = 42, .expected = true }, .{ .a = -42, .b = 42, .expected = false }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = false }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = false }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = false }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = false }, }; for (testCases) |tc| { @@ -594,8 +595,8 @@ test "OP_NUMNOTEQUAL operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = false }, @@ -605,8 +606,8 @@ test "OP_NUMNOTEQUAL operation" { .{ .a = -1, .b = 1, .expected = true }, .{ .a = 42, .b = 42, .expected = false }, .{ .a = -42, .b = 42, .expected = true }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = true }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = true }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = true }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = true }, }; for (testCases) |tc| { @@ -638,8 +639,8 @@ test "OP_LESSTHAN operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = false }, @@ -649,8 +650,8 @@ test "OP_LESSTHAN operation" { .{ .a = -1, .b = 1, .expected = true }, .{ .a = 42, .b = 42, .expected = false }, .{ .a = -42, .b = 42, .expected = true }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = false }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = true }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = false }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = true }, }; for (testCases) |tc| { @@ -682,8 +683,8 @@ test "OP_GREATERTHAN operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = false }, @@ -693,8 +694,8 @@ test "OP_GREATERTHAN operation" { .{ .a = -1, .b = 1, .expected = false }, .{ .a = 42, .b = 42, .expected = false }, .{ .a = -42, .b = 42, .expected = false }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = true }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = false }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = true }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = false }, }; for (testCases) |tc| { @@ -726,8 +727,8 @@ test "OP_LESSTHANOREQUAL operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = true }, @@ -737,8 +738,8 @@ test "OP_LESSTHANOREQUAL operation" { .{ .a = -1, .b = 1, .expected = true }, .{ .a = 42, .b = 42, .expected = true }, .{ .a = -42, .b = 42, .expected = true }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = false }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = true }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = false }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = true }, }; for (testCases) |tc| { @@ -770,8 +771,8 @@ test "OP_GREATERTHANOREQUAL operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, expected: bool, }{ .{ .a = 0, .b = 0, .expected = true }, @@ -781,8 +782,8 @@ test "OP_GREATERTHANOREQUAL operation" { .{ .a = -1, .b = 1, .expected = false }, .{ .a = 42, .b = 42, .expected = true }, .{ .a = -42, .b = 42, .expected = false }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = true }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = false }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = true }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = false }, }; for (testCases) |tc| { @@ -814,9 +815,9 @@ test "OP_MIN operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, - expected: i64, + a: ScriptNum, + b: ScriptNum, + expected: ScriptNum, }{ .{ .a = 0, .b = 0, .expected = 0 }, .{ .a = 0, .b = 1, .expected = 0 }, @@ -825,8 +826,8 @@ test "OP_MIN operation" { .{ .a = -1, .b = 1, .expected = -1 }, .{ .a = 42, .b = 42, .expected = 42 }, .{ .a = -42, .b = 42, .expected = -42 }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = 1 }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = std.math.minInt(i64) }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = 1 }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = std.math.minInt(ScriptNum) }, }; for (testCases) |tc| { @@ -858,9 +859,9 @@ test "OP_MAX operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, - expected: i64, + a: ScriptNum, + b: ScriptNum, + expected: ScriptNum, }{ .{ .a = 0, .b = 0, .expected = 0 }, .{ .a = 0, .b = 1, .expected = 1 }, @@ -869,8 +870,8 @@ test "OP_MAX operation" { .{ .a = -1, .b = 1, .expected = 1 }, .{ .a = 42, .b = 42, .expected = 42 }, .{ .a = -42, .b = 42, .expected = 42 }, - .{ .a = std.math.maxInt(i64), .b = 1, .expected = std.math.maxInt(i64) }, - .{ .a = std.math.minInt(i64), .b = -1, .expected = -1 }, + .{ .a = std.math.maxInt(ScriptNum), .b = 1, .expected = std.math.maxInt(ScriptNum) }, + .{ .a = std.math.minInt(ScriptNum), .b = -1, .expected = -1 }, }; for (testCases) |tc| { @@ -902,9 +903,9 @@ test "OP_WITHIN operation" { // Test cases const testCases = [_]struct { - x: i64, - min: i64, - max: i64, + x: ScriptNum, + min: ScriptNum, + max: ScriptNum, expected: bool, }{ .{ .x = 0, .min = -1, .max = 1, .expected = true }, @@ -945,15 +946,15 @@ test "OP_NUMEQUALVERIFY operation" { // Test cases const testCases = [_]struct { - a: i64, - b: i64, + a: ScriptNum, + b: ScriptNum, shouldVerify: bool, }{ .{ .a = 0, .b = 0, .shouldVerify = true }, .{ .a = 1, .b = 1, .shouldVerify = true }, .{ .a = -1, .b = -1, .shouldVerify = true }, - .{ .a = std.math.maxInt(i64), .b = std.math.maxInt(i64), .shouldVerify = true }, - .{ .a = std.math.minInt(i64), .b = std.math.minInt(i64), .shouldVerify = true }, + .{ .a = std.math.maxInt(ScriptNum), .b = std.math.maxInt(ScriptNum), .shouldVerify = true }, + .{ .a = std.math.minInt(ScriptNum), .b = std.math.minInt(ScriptNum), .shouldVerify = true }, .{ .a = 0, .b = 1, .shouldVerify = false }, .{ .a = 1, .b = 0, .shouldVerify = false }, .{ .a = -1, .b = 1, .shouldVerify = false }, diff --git a/src/script/stack.zig b/src/script/stack.zig index 946fa65..8c9a467 100644 --- a/src/script/stack.zig +++ b/src/script/stack.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const ScriptNum = @import("lib.zig").ScriptNum; const testing = std.testing; const native_endian = @import("builtin").target.cpu.arch.endian(); @@ -70,7 +71,7 @@ pub const Stack = struct { /// /// # Returns /// - `StackError` if out of memory - pub fn pushInt(self: *Stack, value: i64) StackError!void { + pub fn pushInt(self: *Stack, value: ScriptNum) StackError!void { try self.pushByteArray(std.mem.asBytes(&value)); } @@ -91,15 +92,15 @@ pub const Stack = struct { /// Pop an integer from the stack /// /// # Returns - /// - `i64`: The popped integer value + /// - `ScriptNum`: The popped integer value /// - `StackError` if the stack is empty or the value is invalid - pub fn popInt(self: *Stack) StackError!i64 { + pub fn popInt(self: *Stack) StackError!ScriptNum { const value = try self.pop(); defer self.allocator.free(value); if (value.len > 8) return StackError.InvalidValue; - return std.mem.readVarInt(i64, value, native_endian); + return std.mem.readVarInt(ScriptNum, value, native_endian); } /// Pop a boolean value from the stack @@ -301,11 +302,11 @@ test "Stack pushInt and popInt" { try testing.expectEqual(0, try stack.popInt()); // Test pushing and popping large integers - try stack.pushInt(std.math.maxInt(i64)); - try testing.expectEqual(std.math.maxInt(i64), try stack.popInt()); + try stack.pushInt(std.math.maxInt(ScriptNum)); + try testing.expectEqual(std.math.maxInt(ScriptNum), try stack.popInt()); - try stack.pushInt(std.math.minInt(i64)); - try testing.expectEqual(std.math.minInt(i64), try stack.popInt()); + try stack.pushInt(std.math.minInt(ScriptNum)); + try testing.expectEqual(std.math.minInt(ScriptNum), try stack.popInt()); // Test popping from empty stack try testing.expectError(StackError.StackUnderflow, stack.popInt());