Skip to main content

fixed_bigint/fixeduint/
strict_impl.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! `Strict*` implementations for FixedUInt.
16//!
17//! The strict family panics on overflow regardless of debug/release.
18//! That semantic is intrinsically value-dependent — incompatible with
19//! constant-time guarantees — so impls are gated on `P = Nct`. Bodies
20//! delegate to the existing `Overflowing*` / `Checked*` paths and
21//! convert the overflow flag into a `panic!`.
22
23use super::{FixedUInt, MachineWord};
24use crate::machineword::ConstMachineWord;
25use const_num_traits::Nct;
26use const_num_traits::{
27    CheckedPow, OverflowingAdd, OverflowingMul, OverflowingSub, StrictAdd, StrictDiv, StrictMul,
28    StrictPow, StrictRem, StrictShl, StrictShr, StrictSub,
29};
30
31c0nst::c0nst! {
32    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictAdd for FixedUInt<T, N, Nct> {
33        type Output = FixedUInt<T, N, Nct>;
34        fn strict_add(self, v: Self) -> Self {
35            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, v);
36            if overflow {
37                panic!("FixedUInt: strict_add overflowed");
38            }
39            res
40        }
41    }
42
43    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictSub for FixedUInt<T, N, Nct> {
44        type Output = FixedUInt<T, N, Nct>;
45        fn strict_sub(self, v: Self) -> Self {
46            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, v);
47            if overflow {
48                panic!("FixedUInt: strict_sub underflowed");
49            }
50            res
51        }
52    }
53
54    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictMul for FixedUInt<T, N, Nct> {
55        type Output = FixedUInt<T, N, Nct>;
56        fn strict_mul(self, v: Self) -> Self {
57            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, v);
58            if overflow {
59                panic!("FixedUInt: strict_mul overflowed");
60            }
61            res
62        }
63    }
64
65    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictDiv for FixedUInt<T, N, Nct> {
66        type Output = FixedUInt<T, N, Nct>;
67        fn strict_div(self, v: Self) -> Self {
68            // For unsigned `MIN / -1` is N/A; the only overflow mode is `v == 0`,
69            // which `Div<Self>` already panics on. Delegate.
70            self / v
71        }
72    }
73
74    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictRem for FixedUInt<T, N, Nct> {
75        type Output = FixedUInt<T, N, Nct>;
76        fn strict_rem(self, v: Self) -> Self {
77            self % v
78        }
79    }
80
81    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShl for FixedUInt<T, N, Nct> {
82        type Output = FixedUInt<T, N, Nct>;
83        fn strict_shl(self, rhs: u32) -> Self {
84            let shift = rhs as usize;
85            if shift >= Self::BIT_SIZE {
86                panic!("FixedUInt: strict_shl shift exceeds bit width");
87            }
88            self << shift
89        }
90    }
91
92    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShr for FixedUInt<T, N, Nct> {
93        type Output = FixedUInt<T, N, Nct>;
94        fn strict_shr(self, rhs: u32) -> Self {
95            let shift = rhs as usize;
96            if shift >= Self::BIT_SIZE {
97                panic!("FixedUInt: strict_shr shift exceeds bit width");
98            }
99            self >> shift
100        }
101    }
102
103    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictPow for FixedUInt<T, N, Nct> {
104        type Output = FixedUInt<T, N, Nct>;
105        fn strict_pow(self, exp: u32) -> Self {
106            match <Self as CheckedPow>::checked_pow(self, exp) {
107                Some(v) => v,
108                None => panic!("FixedUInt: strict_pow overflowed"),
109            }
110        }
111    }
112
113    // --- Reference-receiver mirrors -----------------------------------------
114
115    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictAdd for &FixedUInt<T, N, Nct> {
116        type Output = FixedUInt<T, N, Nct>;
117        fn strict_add(self, v: Self) -> FixedUInt<T, N, Nct> {
118            <FixedUInt<T, N, Nct> as StrictAdd>::strict_add(FixedUInt::from_array(self.array), FixedUInt::from_array(v.array))
119        }
120    }
121
122    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictSub for &FixedUInt<T, N, Nct> {
123        type Output = FixedUInt<T, N, Nct>;
124        fn strict_sub(self, v: Self) -> FixedUInt<T, N, Nct> {
125            <FixedUInt<T, N, Nct> as StrictSub>::strict_sub(FixedUInt::from_array(self.array), FixedUInt::from_array(v.array))
126        }
127    }
128
129    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictMul for &FixedUInt<T, N, Nct> {
130        type Output = FixedUInt<T, N, Nct>;
131        fn strict_mul(self, v: Self) -> FixedUInt<T, N, Nct> {
132            <FixedUInt<T, N, Nct> as StrictMul>::strict_mul(FixedUInt::from_array(self.array), FixedUInt::from_array(v.array))
133        }
134    }
135
136    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictDiv for &FixedUInt<T, N, Nct> {
137        type Output = FixedUInt<T, N, Nct>;
138        fn strict_div(self, v: Self) -> FixedUInt<T, N, Nct> {
139            <FixedUInt<T, N, Nct> as StrictDiv>::strict_div(FixedUInt::from_array(self.array), FixedUInt::from_array(v.array))
140        }
141    }
142
143    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictRem for &FixedUInt<T, N, Nct> {
144        type Output = FixedUInt<T, N, Nct>;
145        fn strict_rem(self, v: Self) -> FixedUInt<T, N, Nct> {
146            <FixedUInt<T, N, Nct> as StrictRem>::strict_rem(FixedUInt::from_array(self.array), FixedUInt::from_array(v.array))
147        }
148    }
149
150    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShl for &FixedUInt<T, N, Nct> {
151        type Output = FixedUInt<T, N, Nct>;
152        fn strict_shl(self, rhs: u32) -> FixedUInt<T, N, Nct> {
153            <FixedUInt<T, N, Nct> as StrictShl>::strict_shl(FixedUInt::from_array(self.array), rhs)
154        }
155    }
156
157    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShr for &FixedUInt<T, N, Nct> {
158        type Output = FixedUInt<T, N, Nct>;
159        fn strict_shr(self, rhs: u32) -> FixedUInt<T, N, Nct> {
160            <FixedUInt<T, N, Nct> as StrictShr>::strict_shr(FixedUInt::from_array(self.array), rhs)
161        }
162    }
163
164    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictPow for &FixedUInt<T, N, Nct> {
165        type Output = FixedUInt<T, N, Nct>;
166        fn strict_pow(self, exp: u32) -> FixedUInt<T, N, Nct> {
167            <FixedUInt<T, N, Nct> as StrictPow>::strict_pow(FixedUInt::from_array(self.array), exp)
168        }
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    type U16 = FixedUInt<u8, 2, Nct>;
177
178    #[test]
179    fn strict_add_ok() {
180        assert_eq!(
181            StrictAdd::strict_add(U16::from(10u8), U16::from(20u8)),
182            U16::from(30u8)
183        );
184    }
185
186    #[test]
187    #[should_panic(expected = "strict_add overflowed")]
188    fn strict_add_overflows() {
189        let _ = StrictAdd::strict_add(U16::from(0xFFFFu16), U16::from(1u8));
190    }
191
192    #[test]
193    fn strict_sub_ok() {
194        assert_eq!(
195            StrictSub::strict_sub(U16::from(30u8), U16::from(10u8)),
196            U16::from(20u8)
197        );
198    }
199
200    #[test]
201    #[should_panic(expected = "strict_sub underflowed")]
202    fn strict_sub_underflows() {
203        let _ = StrictSub::strict_sub(U16::from(0u8), U16::from(1u8));
204    }
205
206    #[test]
207    fn strict_mul_ok() {
208        assert_eq!(
209            StrictMul::strict_mul(U16::from(7u8), U16::from(13u8)),
210            U16::from(91u8)
211        );
212    }
213
214    #[test]
215    #[should_panic(expected = "strict_mul overflowed")]
216    fn strict_mul_overflows() {
217        let _ = StrictMul::strict_mul(U16::from(0x1000u16), U16::from(0x1000u16));
218    }
219
220    #[test]
221    fn strict_shl_ok() {
222        assert_eq!(StrictShl::strict_shl(U16::from(1u8), 8), U16::from(256u16));
223    }
224
225    #[test]
226    #[should_panic(expected = "strict_shl shift exceeds bit width")]
227    fn strict_shl_too_wide() {
228        let _ = StrictShl::strict_shl(U16::from(1u8), 16);
229    }
230
231    #[test]
232    fn strict_shr_ok() {
233        assert_eq!(StrictShr::strict_shr(U16::from(256u16), 4), U16::from(16u8));
234    }
235
236    #[test]
237    #[should_panic(expected = "strict_shr shift exceeds bit width")]
238    fn strict_shr_too_wide() {
239        let _ = StrictShr::strict_shr(U16::from(1u8), 16);
240    }
241
242    #[test]
243    fn strict_div_ok() {
244        assert_eq!(
245            StrictDiv::strict_div(U16::from(100u8), U16::from(10u8)),
246            U16::from(10u8)
247        );
248    }
249
250    #[test]
251    fn strict_rem_ok() {
252        assert_eq!(
253            StrictRem::strict_rem(U16::from(100u8), U16::from(7u8)),
254            U16::from(2u8)
255        );
256    }
257
258    #[test]
259    fn strict_pow_ok() {
260        assert_eq!(StrictPow::strict_pow(U16::from(2u8), 8), U16::from(256u16));
261    }
262
263    #[test]
264    #[should_panic(expected = "strict_pow overflowed")]
265    fn strict_pow_overflows() {
266        let _ = StrictPow::strict_pow(U16::from(2u8), 16);
267    }
268
269    #[test]
270    fn strict_ref() {
271        let a = U16::from(10u8);
272        let b = U16::from(20u8);
273        assert_eq!(StrictAdd::strict_add(&a, &b), U16::from(30u8));
274    }
275
276    // --- Const-eval smoke ---------------------------------------------------
277    c0nst::c0nst! {
278        pub c0nst fn const_strict_add<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, b: FixedUInt<T, N, Nct>) -> FixedUInt<T, N, Nct> {
279            StrictAdd::strict_add(a, b)
280        }
281        pub c0nst fn const_strict_sub<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, b: FixedUInt<T, N, Nct>) -> FixedUInt<T, N, Nct> {
282            StrictSub::strict_sub(a, b)
283        }
284        pub c0nst fn const_strict_mul<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, b: FixedUInt<T, N, Nct>) -> FixedUInt<T, N, Nct> {
285            StrictMul::strict_mul(a, b)
286        }
287        pub c0nst fn const_strict_div<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, b: FixedUInt<T, N, Nct>) -> FixedUInt<T, N, Nct> {
288            StrictDiv::strict_div(a, b)
289        }
290        pub c0nst fn const_strict_rem<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, b: FixedUInt<T, N, Nct>) -> FixedUInt<T, N, Nct> {
291            StrictRem::strict_rem(a, b)
292        }
293        pub c0nst fn const_strict_shl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, rhs: u32) -> FixedUInt<T, N, Nct> {
294            StrictShl::strict_shl(a, rhs)
295        }
296        pub c0nst fn const_strict_shr<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, rhs: u32) -> FixedUInt<T, N, Nct> {
297            StrictShr::strict_shr(a, rhs)
298        }
299        pub c0nst fn const_strict_pow<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, exp: u32) -> FixedUInt<T, N, Nct> {
300            StrictPow::strict_pow(a, exp)
301        }
302    }
303
304    #[test]
305    fn nightly_const_eval_strict() {
306        assert_eq!(
307            const_strict_add(U16::from(10u8), U16::from(20u8)),
308            U16::from(30u8)
309        );
310        assert_eq!(
311            const_strict_mul(U16::from(6u8), U16::from(7u8)),
312            U16::from(42u8)
313        );
314        assert_eq!(const_strict_shl(U16::from(1u8), 4), U16::from(16u8));
315        assert_eq!(const_strict_pow(U16::from(2u8), 8), U16::from(256u16));
316
317        #[cfg(feature = "nightly")]
318        {
319            const A: U16 = FixedUInt::from_array([10, 0]);
320            const B: U16 = FixedUInt::from_array([20, 0]);
321            const TWO: U16 = FixedUInt::from_array([2, 0]);
322            const THIRTY: U16 = FixedUInt::from_array([30, 0]);
323            const TEN: U16 = FixedUInt::from_array([10, 0]);
324            const TWO_HUNDRED: U16 = FixedUInt::from_array([200, 0]);
325            const SIXTEEN: U16 = FixedUInt::from_array([16, 0]);
326            const TWO_FIFTY_SIX: U16 = FixedUInt::from_array([0, 1]);
327
328            const ADD: U16 = const_strict_add(A, B);
329            const SUB: U16 = const_strict_sub(THIRTY, A);
330            const MUL: U16 = const_strict_mul(TEN, TWO);
331            const DIV: U16 = const_strict_div(TWO_HUNDRED, TEN);
332            const REM: U16 = const_strict_rem(TWO_HUNDRED, FixedUInt::from_array([7, 0]));
333            const SHL: U16 = const_strict_shl(TWO, 3);
334            const SHR: U16 = const_strict_shr(SIXTEEN, 2);
335            const POW: U16 = const_strict_pow(TWO, 8);
336
337            assert_eq!(ADD, FixedUInt::from_array([30, 0]));
338            assert_eq!(SUB, FixedUInt::from_array([20, 0]));
339            assert_eq!(MUL, FixedUInt::from_array([20, 0]));
340            assert_eq!(DIV, FixedUInt::from_array([20, 0]));
341            assert_eq!(REM, FixedUInt::from_array([4, 0]));
342            assert_eq!(SHL, FixedUInt::from_array([16, 0]));
343            assert_eq!(SHR, FixedUInt::from_array([4, 0]));
344            assert_eq!(POW, TWO_FIFTY_SIX);
345        }
346    }
347}