| ... | @@ -102,14 +102,15 @@ pub fn Uint(comptime max_bits: comptime_int) type { | ... | @@ -102,14 +102,15 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 102 | /// Creates a new big integer from a primitive type. | 102 | /// Creates a new big integer from a primitive type. |
| 103 | /// This function may not run in constant time. | 103 | /// This function may not run in constant time. |
| 104 | pub fn fromPrimitive(comptime T: type, init_value: T) OverflowError!Self { | 104 | pub fn fromPrimitive(comptime T: type, init_value: T) OverflowError!Self { |
| 105 | var x = init_value; | 105 | const U = @Int(.unsigned, @bitSizeOf(T)); |
| | 106 | var x = math.cast(U, init_value) orelse return error.Overflow; |
| 106 | var out: Self = .{ | 107 | var out: Self = .{ |
| 107 | .limbs_buffer = undefined, | 108 | .limbs_buffer = undefined, |
| 108 | .limbs_len = max_limbs_count, | 109 | .limbs_len = max_limbs_count, |
| 109 | }; | 110 | }; |
| 110 | for (&out.limbs_buffer) |*limb| { | 111 | for (&out.limbs_buffer) |*limb| { |
| 111 | limb.* = if (@bitSizeOf(T) > t_bits) @as(TLimb, @truncate(x)) else x; | 112 | limb.* = if (@bitSizeOf(T) > t_bits) @as(TLimb, @truncate(x)) else x; |
| 112 | x = math.shr(T, x, t_bits); | 113 | x = math.shr(U, x, t_bits); |
| 113 | } | 114 | } |
| 114 | if (x != 0) { | 115 | if (x != 0) { |
| 115 | return error.Overflow; | 116 | return error.Overflow; |
| ... | @@ -120,18 +121,21 @@ pub fn Uint(comptime max_bits: comptime_int) type { | ... | @@ -120,18 +121,21 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 120 | /// Converts a big integer to a primitive type. | 121 | /// Converts a big integer to a primitive type. |
| 121 | /// This function may not run in constant time. | 122 | /// This function may not run in constant time. |
| 122 | pub fn toPrimitive(self: Self, comptime T: type) OverflowError!T { | 123 | pub fn toPrimitive(self: Self, comptime T: type) OverflowError!T { |
| 123 | var x: T = 0; | 124 | const U = @Int(.unsigned, @bitSizeOf(T)); |
| | 125 | var x: U = 0; |
| 124 | var i = self.limbs_len - 1; | 126 | var i = self.limbs_len - 1; |
| 125 | while (true) : (i -= 1) { | 127 | while (true) : (i -= 1) { |
| 126 | if (@bitSizeOf(T) >= t_bits and math.shr(T, x, @bitSizeOf(T) - t_bits) != 0) { | 128 | // Check for overflow before shifting, even when the destination is narrower than a limb. |
| | 129 | const discarded = if (@bitSizeOf(U) >= t_bits) math.shr(U, x, @bitSizeOf(U) - t_bits) else x; |
| | 130 | if (discarded != 0) { |
| 127 | return error.Overflow; | 131 | return error.Overflow; |
| 128 | } | 132 | } |
| 129 | x = math.shl(T, x, t_bits); | 133 | x = math.shl(U, x, t_bits); |
| 130 | const v = math.cast(T, self.limbsConst()[i]) orelse return error.Overflow; | 134 | const v = math.cast(U, self.limbsConst()[i]) orelse return error.Overflow; |
| 131 | x |= v; | 135 | x |= v; |
| 132 | if (i == 0) break; | 136 | if (i == 0) break; |
| 133 | } | 137 | } |
| 134 | return x; | 138 | return math.cast(T, x) orelse error.Overflow; |
| 135 | } | 139 | } |
| 136 | | 140 | |
| 137 | /// Encodes a big integer into a byte array. | 141 | /// Encodes a big integer into a byte array. |
| ... | @@ -1085,3 +1089,20 @@ test ct { | ... | @@ -1085,3 +1089,20 @@ test ct { |
| 1085 | try testCt(ct_protected); | 1089 | try testCt(ct_protected); |
| 1086 | try testCt(ct_unprotected); | 1090 | try testCt(ct_unprotected); |
| 1087 | } | 1091 | } |
| | 1092 | |
| | 1093 | test "Uint serialization and predicates" { |
| | 1094 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| | 1095 | |
| | 1096 | const U = Uint(256); |
| | 1097 | const x = try U.fromPrimitive(u128, (1 << t_bits) + 5); |
| | 1098 | try testing.expectError(error.Overflow, Uint(64).fromPrimitive(u256, 1 << 200)); |
| | 1099 | try testing.expectError(error.Overflow, x.toPrimitive(u8)); |
| | 1100 | try testing.expectError(error.Overflow, x.toPrimitive(@Int(.unsigned, t_bits))); |
| | 1101 | try testing.expectEqual((1 << t_bits) + 5, try x.toPrimitive(@Int(.unsigned, t_bits + 1))); |
| | 1102 | const max = try U.fromPrimitive(u128, (1 << t_bits) - 1); |
| | 1103 | try testing.expectEqual((1 << t_bits) - 1, try max.toPrimitive(@Int(.unsigned, t_bits))); |
| | 1104 | try testing.expectError(error.Overflow, max.toPrimitive(@Int(.unsigned, t_bits - 1))); |
| | 1105 | const signed_max = try U.fromPrimitive(u128, math.maxInt(i128)); |
| | 1106 | try testing.expectEqual(math.maxInt(i128), try signed_max.toPrimitive(i128)); |
| | 1107 | try testing.expectError(error.Overflow, (try U.fromPrimitive(u128, 1 << 127)).toPrimitive(i128)); |
| | 1108 | } |