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        fn strict_add(self, v: Self) -> Self {
34            let (res, overflow) = <Self as OverflowingAdd>::overflowing_add(self, v);
35            if overflow {
36                panic!("FixedUInt: strict_add overflowed");
37            }
38            res
39        }
40    }
41
42    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictSub for FixedUInt<T, N, Nct> {
43        fn strict_sub(self, v: Self) -> Self {
44            let (res, overflow) = <Self as OverflowingSub>::overflowing_sub(self, v);
45            if overflow {
46                panic!("FixedUInt: strict_sub underflowed");
47            }
48            res
49        }
50    }
51
52    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictMul for FixedUInt<T, N, Nct> {
53        fn strict_mul(self, v: Self) -> Self {
54            let (res, overflow) = <Self as OverflowingMul>::overflowing_mul(self, v);
55            if overflow {
56                panic!("FixedUInt: strict_mul overflowed");
57            }
58            res
59        }
60    }
61
62    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictDiv for FixedUInt<T, N, Nct> {
63        fn strict_div(self, v: Self) -> Self {
64            // For unsigned `MIN / -1` is N/A; the only overflow mode is `v == 0`,
65            // which `Div<Self>` already panics on. Delegate.
66            self / v
67        }
68    }
69
70    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictRem for FixedUInt<T, N, Nct> {
71        fn strict_rem(self, v: Self) -> Self {
72            self % v
73        }
74    }
75
76    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShl for FixedUInt<T, N, Nct> {
77        fn strict_shl(self, rhs: u32) -> Self {
78            let shift = rhs as usize;
79            if shift >= Self::BIT_SIZE {
80                panic!("FixedUInt: strict_shl shift exceeds bit width");
81            }
82            self << shift
83        }
84    }
85
86    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShr for FixedUInt<T, N, Nct> {
87        fn strict_shr(self, rhs: u32) -> Self {
88            let shift = rhs as usize;
89            if shift >= Self::BIT_SIZE {
90                panic!("FixedUInt: strict_shr shift exceeds bit width");
91            }
92            self >> shift
93        }
94    }
95
96    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictPow for FixedUInt<T, N, Nct> {
97        fn strict_pow(self, exp: u32) -> Self {
98            match <Self as CheckedPow>::checked_pow(self, exp) {
99                Some(v) => v,
100                None => panic!("FixedUInt: strict_pow overflowed"),
101            }
102        }
103    }
104
105    // --- Reference-receiver mirrors -----------------------------------------
106
107    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictAdd for &FixedUInt<T, N, Nct> {
108        fn strict_add(self, v: Self) -> FixedUInt<T, N, Nct> {
109            <FixedUInt<T, N, Nct> as StrictAdd>::strict_add(*self, *v)
110        }
111    }
112
113    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictSub for &FixedUInt<T, N, Nct> {
114        fn strict_sub(self, v: Self) -> FixedUInt<T, N, Nct> {
115            <FixedUInt<T, N, Nct> as StrictSub>::strict_sub(*self, *v)
116        }
117    }
118
119    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictMul for &FixedUInt<T, N, Nct> {
120        fn strict_mul(self, v: Self) -> FixedUInt<T, N, Nct> {
121            <FixedUInt<T, N, Nct> as StrictMul>::strict_mul(*self, *v)
122        }
123    }
124
125    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictDiv for &FixedUInt<T, N, Nct> {
126        fn strict_div(self, v: Self) -> FixedUInt<T, N, Nct> {
127            <FixedUInt<T, N, Nct> as StrictDiv>::strict_div(*self, *v)
128        }
129    }
130
131    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictRem for &FixedUInt<T, N, Nct> {
132        fn strict_rem(self, v: Self) -> FixedUInt<T, N, Nct> {
133            <FixedUInt<T, N, Nct> as StrictRem>::strict_rem(*self, *v)
134        }
135    }
136
137    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShl for &FixedUInt<T, N, Nct> {
138        fn strict_shl(self, rhs: u32) -> FixedUInt<T, N, Nct> {
139            <FixedUInt<T, N, Nct> as StrictShl>::strict_shl(*self, rhs)
140        }
141    }
142
143    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictShr for &FixedUInt<T, N, Nct> {
144        fn strict_shr(self, rhs: u32) -> FixedUInt<T, N, Nct> {
145            <FixedUInt<T, N, Nct> as StrictShr>::strict_shr(*self, rhs)
146        }
147    }
148
149    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> StrictPow for &FixedUInt<T, N, Nct> {
150        fn strict_pow(self, exp: u32) -> FixedUInt<T, N, Nct> {
151            <FixedUInt<T, N, Nct> as StrictPow>::strict_pow(*self, exp)
152        }
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    type U16 = FixedUInt<u8, 2, Nct>;
161
162    #[test]
163    fn strict_add_ok() {
164        assert_eq!(
165            StrictAdd::strict_add(U16::from(10u8), U16::from(20u8)),
166            U16::from(30u8)
167        );
168    }
169
170    #[test]
171    #[should_panic(expected = "strict_add overflowed")]
172    fn strict_add_overflows() {
173        let _ = StrictAdd::strict_add(U16::from(0xFFFFu16), U16::from(1u8));
174    }
175
176    #[test]
177    fn strict_sub_ok() {
178        assert_eq!(
179            StrictSub::strict_sub(U16::from(30u8), U16::from(10u8)),
180            U16::from(20u8)
181        );
182    }
183
184    #[test]
185    #[should_panic(expected = "strict_sub underflowed")]
186    fn strict_sub_underflows() {
187        let _ = StrictSub::strict_sub(U16::from(0u8), U16::from(1u8));
188    }
189
190    #[test]
191    fn strict_mul_ok() {
192        assert_eq!(
193            StrictMul::strict_mul(U16::from(7u8), U16::from(13u8)),
194            U16::from(91u8)
195        );
196    }
197
198    #[test]
199    #[should_panic(expected = "strict_mul overflowed")]
200    fn strict_mul_overflows() {
201        let _ = StrictMul::strict_mul(U16::from(0x1000u16), U16::from(0x1000u16));
202    }
203
204    #[test]
205    fn strict_shl_ok() {
206        assert_eq!(StrictShl::strict_shl(U16::from(1u8), 8), U16::from(256u16));
207    }
208
209    #[test]
210    #[should_panic(expected = "strict_shl shift exceeds bit width")]
211    fn strict_shl_too_wide() {
212        let _ = StrictShl::strict_shl(U16::from(1u8), 16);
213    }
214
215    #[test]
216    fn strict_shr_ok() {
217        assert_eq!(StrictShr::strict_shr(U16::from(256u16), 4), U16::from(16u8));
218    }
219
220    #[test]
221    #[should_panic(expected = "strict_shr shift exceeds bit width")]
222    fn strict_shr_too_wide() {
223        let _ = StrictShr::strict_shr(U16::from(1u8), 16);
224    }
225
226    #[test]
227    fn strict_div_ok() {
228        assert_eq!(
229            StrictDiv::strict_div(U16::from(100u8), U16::from(10u8)),
230            U16::from(10u8)
231        );
232    }
233
234    #[test]
235    fn strict_rem_ok() {
236        assert_eq!(
237            StrictRem::strict_rem(U16::from(100u8), U16::from(7u8)),
238            U16::from(2u8)
239        );
240    }
241
242    #[test]
243    fn strict_pow_ok() {
244        assert_eq!(StrictPow::strict_pow(U16::from(2u8), 8), U16::from(256u16));
245    }
246
247    #[test]
248    #[should_panic(expected = "strict_pow overflowed")]
249    fn strict_pow_overflows() {
250        let _ = StrictPow::strict_pow(U16::from(2u8), 16);
251    }
252
253    #[test]
254    fn strict_ref() {
255        let a = U16::from(10u8);
256        let b = U16::from(20u8);
257        assert_eq!(StrictAdd::strict_add(&a, &b), U16::from(30u8));
258    }
259
260    // --- Const-eval smoke ---------------------------------------------------
261    c0nst::c0nst! {
262        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> {
263            StrictAdd::strict_add(a, b)
264        }
265        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> {
266            StrictSub::strict_sub(a, b)
267        }
268        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> {
269            StrictMul::strict_mul(a, b)
270        }
271        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> {
272            StrictDiv::strict_div(a, b)
273        }
274        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> {
275            StrictRem::strict_rem(a, b)
276        }
277        pub c0nst fn const_strict_shl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, rhs: u32) -> FixedUInt<T, N, Nct> {
278            StrictShl::strict_shl(a, rhs)
279        }
280        pub c0nst fn const_strict_shr<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, rhs: u32) -> FixedUInt<T, N, Nct> {
281            StrictShr::strict_shr(a, rhs)
282        }
283        pub c0nst fn const_strict_pow<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(a: FixedUInt<T, N, Nct>, exp: u32) -> FixedUInt<T, N, Nct> {
284            StrictPow::strict_pow(a, exp)
285        }
286    }
287
288    #[test]
289    fn nightly_const_eval_strict() {
290        assert_eq!(
291            const_strict_add(U16::from(10u8), U16::from(20u8)),
292            U16::from(30u8)
293        );
294        assert_eq!(
295            const_strict_mul(U16::from(6u8), U16::from(7u8)),
296            U16::from(42u8)
297        );
298        assert_eq!(const_strict_shl(U16::from(1u8), 4), U16::from(16u8));
299        assert_eq!(const_strict_pow(U16::from(2u8), 8), U16::from(256u16));
300
301        #[cfg(feature = "nightly")]
302        {
303            const A: U16 = FixedUInt::from_array([10, 0]);
304            const B: U16 = FixedUInt::from_array([20, 0]);
305            const TWO: U16 = FixedUInt::from_array([2, 0]);
306            const THIRTY: U16 = FixedUInt::from_array([30, 0]);
307            const TEN: U16 = FixedUInt::from_array([10, 0]);
308            const TWO_HUNDRED: U16 = FixedUInt::from_array([200, 0]);
309            const SIXTEEN: U16 = FixedUInt::from_array([16, 0]);
310            const TWO_FIFTY_SIX: U16 = FixedUInt::from_array([0, 1]);
311
312            const ADD: U16 = const_strict_add(A, B);
313            const SUB: U16 = const_strict_sub(THIRTY, A);
314            const MUL: U16 = const_strict_mul(TEN, TWO);
315            const DIV: U16 = const_strict_div(TWO_HUNDRED, TEN);
316            const REM: U16 = const_strict_rem(TWO_HUNDRED, FixedUInt::from_array([7, 0]));
317            const SHL: U16 = const_strict_shl(TWO, 3);
318            const SHR: U16 = const_strict_shr(SIXTEEN, 2);
319            const POW: U16 = const_strict_pow(TWO, 8);
320
321            assert_eq!(ADD, FixedUInt::from_array([30, 0]));
322            assert_eq!(SUB, FixedUInt::from_array([20, 0]));
323            assert_eq!(MUL, FixedUInt::from_array([20, 0]));
324            assert_eq!(DIV, FixedUInt::from_array([20, 0]));
325            assert_eq!(REM, FixedUInt::from_array([4, 0]));
326            assert_eq!(SHL, FixedUInt::from_array([16, 0]));
327            assert_eq!(SHR, FixedUInt::from_array([4, 0]));
328            assert_eq!(POW, TWO_FIFTY_SIX);
329        }
330    }
331}