authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-16 01:50:59+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-16 11:32:05+02:00
log1e09157b53441d06cd1f49b9c3917a58ee244cb1
treebee7820a937e242eca9a014d51406b73f699de0f
parentf6bf24b2f3e1d65ce66625c4466aa2af9edff41c

big ints: Fix set(signed int minimum) panic


2 files changed, 20 insertions(+), 29 deletions(-)

lib/std/math/big/int.zig+13-29
...@@ -18,18 +18,12 @@ const debug_safety = false;...@@ -18,18 +18,12 @@ const debug_safety = false;
18/// Returns the number of limbs needed to store `scalar`, which must be a18/// Returns the number of limbs needed to store `scalar`, which must be a
19/// primitive integer value.19/// primitive integer value.
20pub fn calcLimbLen(scalar: anytype) usize {20pub fn calcLimbLen(scalar: anytype) usize {
21 const T = @TypeOf(scalar);21 if (scalar == 0) {
22 switch (@typeInfo(T)) {22 return 1;
23 .Int => |info| {
24 const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T;
25 return @sizeOf(UT) / @sizeOf(Limb);
26 },
27 .ComptimeInt => {
28 const w_value = if (scalar < 0) -scalar else scalar;
29 return @divFloor(math.log2(w_value), limb_bits) + 1;
30 },
31 else => @compileError("parameter must be a primitive integer type"),
32 }23 }
24
25 const w_value = std.math.absCast(scalar);
26 return @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1;
33}27}
3428
35pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {29pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
...@@ -218,26 +212,22 @@ pub const Mutable = struct {...@@ -218,26 +212,22 @@ pub const Mutable = struct {
218 /// needs to be to store a specific value.212 /// needs to be to store a specific value.
219 pub fn set(self: *Mutable, value: anytype) void {213 pub fn set(self: *Mutable, value: anytype) void {
220 const T = @TypeOf(value);214 const T = @TypeOf(value);
215 const needed_limbs = calcLimbLen(value);
216 assert(needed_limbs <= self.limbs.len); // value too big
217
218 self.len = needed_limbs;
219 self.positive = value >= 0;
221220
222 switch (@typeInfo(T)) {221 switch (@typeInfo(T)) {
223 .Int => |info| {222 .Int => |info| {
224 const UT = if (info.signedness == .signed) std.meta.Int(.unsigned, info.bits - 1) else T;223 var w_value = std.math.absCast(value);
225
226 const needed_limbs = @sizeOf(UT) / @sizeOf(Limb);
227 assert(needed_limbs <= self.limbs.len); // value too big
228 self.len = 0;
229 self.positive = value >= 0;
230
231 var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
232224
233 if (info.bits <= limb_bits) {225 if (info.bits <= limb_bits) {
234 self.limbs[0] = @as(Limb, w_value);226 self.limbs[0] = w_value;
235 self.len += 1;
236 } else {227 } else {
237 var i: usize = 0;228 var i: usize = 0;
238 while (w_value != 0) : (i += 1) {229 while (w_value != 0) : (i += 1) {
239 self.limbs[i] = @truncate(Limb, w_value);230 self.limbs[i] = @truncate(Limb, w_value);
240 self.len += 1;
241231
242 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.232 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
243 w_value >>= limb_bits / 2;233 w_value >>= limb_bits / 2;
...@@ -246,13 +236,7 @@ pub const Mutable = struct {...@@ -246,13 +236,7 @@ pub const Mutable = struct {
246 }236 }
247 },237 },
248 .ComptimeInt => {238 .ComptimeInt => {
249 comptime var w_value = if (value < 0) -value else value;239 comptime var w_value = std.math.absCast(value);
250
251 const req_limbs = @divFloor(math.log2(w_value), limb_bits) + 1;
252 assert(req_limbs <= self.limbs.len); // value too big
253
254 self.len = req_limbs;
255 self.positive = value >= 0;
256240
257 if (w_value <= maxInt(Limb)) {241 if (w_value <= maxInt(Limb)) {
258 self.limbs[0] = w_value;242 self.limbs[0] = w_value;
lib/std/math/big/int_test.zig+7
...@@ -61,6 +61,13 @@ test "big.int sub-limb to" {...@@ -61,6 +61,13 @@ test "big.int sub-limb to" {
61 try testing.expect((try a.to(u8)) == 10);61 try testing.expect((try a.to(u8)) == 10);
62}62}
6363
64test "big.int set negative minimum" {
65 var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64)));
66 defer a.deinit();
67
68 try testing.expect((try a.to(i64)) == minInt(i64));
69}
70
64test "big.int to target too small error" {71test "big.int to target too small error" {
65 var a = try Managed.initSet(testing.allocator, 0xffffffff);72 var a = try Managed.initSet(testing.allocator, 0xffffffff);
66 defer a.deinit();73 defer a.deinit();