| ... | ... | @@ -835,6 +835,75 @@ pub const Mutable = struct { |
| 835 | 835 | r.positive = a.positive; |
| 836 | 836 | } |
| 837 | 837 | |
| 838 | /// r = a <<| shift with 2s-complement saturating semantics. |
| 839 | /// |
| 840 | /// r and a may alias. |
| 841 | /// |
| 842 | /// Asserts there is enough memory to fit the result. The upper bound Limb count is |
| 843 | /// r is `calcTwosCompLimbCount(bit_count)`. |
| 844 | pub fn shiftLeftSat(r: *Mutable, a: Const, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) void { |
| 845 | // Special case: When the argument is negative, but the result is supposed to be unsigned, |
| 846 | // return 0 in all cases. |
| 847 | if (!a.positive and signedness == .unsigned) { |
| 848 | r.set(0); |
| 849 | return; |
| 850 | } |
| 851 | |
| 852 | // Check whether the shift is going to overflow. This is the case |
| 853 | // when (in 2s complement) any bit above `bit_count - shift` is set in the unshifted value. |
| 854 | // Note, the sign bit is not counted here. |
| 855 | |
| 856 | // Handle shifts larger than the target type. This also deals with |
| 857 | // 0-bit integers. |
| 858 | if (bit_count <= shift) { |
| 859 | // In this case, there is only no overflow if `a` is zero. |
| 860 | if (a.eqZero()) { |
| 861 | r.set(0); |
| 862 | } else { |
| 863 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| 864 | } |
| 865 | return; |
| 866 | } |
| 867 | |
| 868 | const checkbit = bit_count - shift - @boolToInt(signedness == .signed); |
| 869 | // If `checkbit` and more significant bits are zero, no overflow will take place. |
| 870 | |
| 871 | if (checkbit >= a.limbs.len * limb_bits) { |
| 872 | // `checkbit` is outside the range of a, so definitely no overflow will take place. We |
| 873 | // can defer to a normal shift. |
| 874 | // Note that if `a` is normalized (which we assume), this checks for set bits in the upper limbs. |
| 875 | |
| 876 | // Note, in this case r should already have enough limbs required to perform the normal shift. |
| 877 | // In this case the shift of the most significant limb may still overflow. |
| 878 | r.shiftLeft(a, shift); |
| 879 | return; |
| 880 | } else if (checkbit < (a.limbs.len - 1) * limb_bits) { |
| 881 | // `checkbit` is not in the most significant limb. If `a` is normalized the most significant |
| 882 | // limb will not be zero, so in this case we need to saturate. Note that `a.limbs.len` must be |
| 883 | // at least one according to normalization rules. |
| 884 | |
| 885 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| 886 | return; |
| 887 | } |
| 888 | |
| 889 | // Generate a mask with the bits to check in the most signficant limb. We'll need to check |
| 890 | // all bits with equal or more significance than checkbit. |
| 891 | // const msb = @truncate(Log2Limb, checkbit); |
| 892 | // const checkmask = (@as(Limb, 1) << msb) -% 1; |
| 893 | |
| 894 | if (a.limbs[a.limbs.len - 1] >> @truncate(Log2Limb, checkbit) != 0) { |
| 895 | // Need to saturate. |
| 896 | r.setTwosCompIntLimit(if (a.positive) .max else .min, signedness, bit_count); |
| 897 | return; |
| 898 | } |
| 899 | |
| 900 | // This shift should not be able to overflow, so invoke llshl and normalize manually |
| 901 | // to avoid the extra required limb. |
| 902 | llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift); |
| 903 | r.normalize(a.limbs.len + (shift / limb_bits)); |
| 904 | r.positive = a.positive; |
| 905 | } |
| 906 | |
| 838 | 907 | /// r = a >> shift |
| 839 | 908 | /// r and a may alias. |
| 840 | 909 | /// |
| ... | ... | @@ -2401,6 +2470,14 @@ pub const Managed = struct { |
| 2401 | 2470 | r.setMetadata(m.positive, m.len); |
| 2402 | 2471 | } |
| 2403 | 2472 | |
| 2473 | /// r = a <<| shift with 2s-complement saturating semantics. |
| 2474 | pub fn shiftLeftSat(r: *Managed, a: Managed, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) !void { |
| 2475 | try r.ensureTwosCompCapacity(bit_count); |
| 2476 | var m = r.toMutable(); |
| 2477 | m.shiftLeftSat(a.toConst(), shift, signedness, bit_count); |
| 2478 | r.setMetadata(m.positive, m.len); |
| 2479 | } |
| 2480 | |
| 2404 | 2481 | /// r = a >> shift |
| 2405 | 2482 | pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void { |
| 2406 | 2483 | if (a.len() <= shift / limb_bits) { |
| ... | ... | @@ -2949,10 +3026,18 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { |
| 2949 | 3026 | fn llshl(r: []Limb, a: []const Limb, shift: usize) void { |
| 2950 | 3027 | @setRuntimeSafety(debug_safety); |
| 2951 | 3028 | assert(a.len >= 1); |
| 2952 | | assert(r.len >= a.len + (shift / limb_bits) + 1); |
| 3029 | |
| 3030 | const interior_limb_shift = @truncate(Log2Limb, shift); |
| 3031 | |
| 3032 | // We only need the extra limb if the shift of the last element overflows. |
| 3033 | // This is useful for the implementation of `shiftLeftSat`. |
| 3034 | if (a[a.len - 1] << interior_limb_shift >> interior_limb_shift != a[a.len - 1]) { |
| 3035 | assert(r.len >= a.len + (shift / limb_bits) + 1); |
| 3036 | } else { |
| 3037 | assert(r.len >= a.len + (shift / limb_bits)); |
| 3038 | } |
| 2953 | 3039 | |
| 2954 | 3040 | const limb_shift = shift / limb_bits + 1; |
| 2955 | | const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits); |
| 2956 | 3041 | |
| 2957 | 3042 | var carry: Limb = 0; |
| 2958 | 3043 | var i: usize = 0; |
| ... | ... | @@ -2979,7 +3064,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2979 | 3064 | assert(r.len >= a.len - (shift / limb_bits)); |
| 2980 | 3065 | |
| 2981 | 3066 | const limb_shift = shift / limb_bits; |
| 2982 | | const interior_limb_shift = @intCast(Log2Limb, shift % limb_bits); |
| 3067 | const interior_limb_shift = @truncate(Log2Limb, shift); |
| 2983 | 3068 | |
| 2984 | 3069 | var carry: Limb = 0; |
| 2985 | 3070 | var i: usize = 0; |