Commit 1e09157b53

Robin Voetter <robin@voetter.nl>
2021-10-16 01:50:59
big ints: Fix set(signed int minimum) panic
1 parent f6bf24b
Changed files (2)
lib
std
lib/std/math/big/int.zig
@@ -18,18 +18,12 @@ const debug_safety = false;
 /// Returns the number of limbs needed to store `scalar`, which must be a
 /// primitive integer value.
 pub fn calcLimbLen(scalar: anytype) usize {
-    const T = @TypeOf(scalar);
-    switch (@typeInfo(T)) {
-        .Int => |info| {
-            const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T;
-            return @sizeOf(UT) / @sizeOf(Limb);
-        },
-        .ComptimeInt => {
-            const w_value = if (scalar < 0) -scalar else scalar;
-            return @divFloor(math.log2(w_value), limb_bits) + 1;
-        },
-        else => @compileError("parameter must be a primitive integer type"),
+    if (scalar == 0) {
+        return 1;
     }
+
+    const w_value = std.math.absCast(scalar);
+    return @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1;
 }
 
 pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
@@ -218,26 +212,22 @@ pub const Mutable = struct {
     /// needs to be to store a specific value.
     pub fn set(self: *Mutable, value: anytype) void {
         const T = @TypeOf(value);
+        const needed_limbs = calcLimbLen(value);
+        assert(needed_limbs <= self.limbs.len); // value too big
+
+        self.len = needed_limbs;
+        self.positive = value >= 0;
 
         switch (@typeInfo(T)) {
             .Int => |info| {
-                const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T;
-
-                const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
-                assert(needed_limbs <= self.limbs.len); // value too big
-                self.len = 0;
-                self.positive = value >= 0;
-
-                var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
+                var w_value = std.math.absCast(value);
 
                 if (info.bits <= limb_bits) {
-                    self.limbs[0] = @as(Limb, w_value);
-                    self.len += 1;
+                    self.limbs[0] = w_value;
                 } else {
                     var i: usize = 0;
                     while (w_value != 0) : (i += 1) {
                         self.limbs[i] = @truncate(Limb, w_value);
-                        self.len += 1;
 
                         // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
                         w_value >>= limb_bits / 2;
@@ -246,13 +236,7 @@ pub const Mutable = struct {
                 }
             },
             .ComptimeInt => {
-                comptime var w_value = if (value < 0) -value else value;
-
-                const req_limbs = @divFloor(math.log2(w_value), limb_bits) + 1;
-                assert(req_limbs <= self.limbs.len); // value too big
-
-                self.len = req_limbs;
-                self.positive = value >= 0;
+                comptime var w_value = std.math.absCast(value);
 
                 if (w_value <= maxInt(Limb)) {
                     self.limbs[0] = w_value;
lib/std/math/big/int_test.zig
@@ -61,6 +61,13 @@ test "big.int sub-limb to" {
     try testing.expect((try a.to(u8)) == 10);
 }
 
+test "big.int set negative minimum" {
+    var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64)));
+    defer a.deinit();
+
+    try testing.expect((try a.to(i64)) == minInt(i64));
+}
+
 test "big.int to target too small error" {
     var a = try Managed.initSet(testing.allocator, 0xffffffff);
     defer a.deinit();