openzeppelin_crypto/
const_helpers.rs

1//! This module contains helpers for functions with constant context, like
2//! [`crate::const_for`] - constant expression time `for` cycle, as well as its
3//! optimized versions like [`crate::const_for_unroll6`], that performs [loop
4//! unroll] optimization and can be used both from compile time and runtime.
5//!
6//! Beware of using an optimized version everywhere, since it can bloat
7//! binary (WASM) size easily.
8//! Measure impact first.
9//!
10//! [loop unroll]: https://en.wikipedia.org/wiki/Loop_unrolling
11
12/// Allows `for` loops in constant context.
13///
14/// ```rust,ignore
15/// // This loop runs from 0 to 10 (not including 10).
16/// const_for!((i in 0..10) {
17///     // loop body here
18/// });
19/// ```
20#[macro_export]
21macro_rules! const_for {
22    (($i:ident in $start:tt.. $end:tt) $code:expr) => {{
23        let mut $i = $start;
24        loop {
25            $crate::cycle!($i, $end, $code);
26        }
27    }};
28}
29
30/// Allows reversed `for` loop in constant context.
31///
32/// Start (`$start`) index is not inclusive.
33///
34/// ```rust,ignore
35/// // This loop:
36/// const_rev_for!((i in 0..10) {
37///     // loop body here
38/// });
39/// // is similar to the following loop:
40/// for i in (0..10).rev() {
41///     // loop body here
42/// }
43/// // Will runs from 9 till 0 (not including 0).
44/// ```
45#[macro_export]
46macro_rules! const_rev_for {
47    (($i:ident in $end:tt.. $start:tt) $code:expr) => {{
48        let mut $i = $start;
49        loop {
50            $crate::rev_cycle!($i, $end, $code);
51        }
52    }};
53}
54
55/// Allows `for` loop in constant context, with 2 stages loop unroll
56/// optimization.
57#[macro_export]
58macro_rules! const_for_unroll2 {
59    (($i:ident in $start:tt.. $end:tt) $code:expr) => {{
60        let mut $i = $start;
61        loop {
62            $crate::cycle!($i, $end, $code);
63            $crate::cycle!($i, $end, $code);
64        }
65    }};
66}
67
68/// Allows `for` loop in constant context, with 4 stages loop unroll
69/// optimization.
70#[macro_export]
71macro_rules! const_for_unroll4 {
72    (($i:ident in $start:tt.. $end:tt) $code:expr) => {{
73        let mut $i = $start;
74        loop {
75            $crate::cycle!($i, $end, $code);
76            $crate::cycle!($i, $end, $code);
77            $crate::cycle!($i, $end, $code);
78            $crate::cycle!($i, $end, $code);
79        }
80    }};
81}
82
83/// Allows `for` loop in constant context, with 6 stages loop unroll
84/// optimization.
85#[macro_export]
86macro_rules! const_for_unroll6 {
87    (($i:ident in $start:tt.. $end:tt) $code:expr) => {{
88        let mut $i = $start;
89        loop {
90            $crate::cycle!($i, $end, $code);
91            $crate::cycle!($i, $end, $code);
92            $crate::cycle!($i, $end, $code);
93            $crate::cycle!($i, $end, $code);
94            $crate::cycle!($i, $end, $code);
95            $crate::cycle!($i, $end, $code);
96        }
97    }};
98}
99
100/// Allows reversed `for` loop in constant context, with 6 stages loop
101/// unroll optimization.
102///
103/// Start (`$start`) index is not inclusive.
104#[macro_export]
105macro_rules! const_rev_for_unroll6 {
106    (($i:ident in $end:tt.. $start:tt) $code:expr) => {{
107        let mut $i = $start;
108        loop {
109            $crate::rev_cycle!($i, $end, $code);
110            $crate::rev_cycle!($i, $end, $code);
111            $crate::rev_cycle!($i, $end, $code);
112            $crate::rev_cycle!($i, $end, $code);
113            $crate::rev_cycle!($i, $end, $code);
114            $crate::rev_cycle!($i, $end, $code);
115        }
116    }};
117}
118
119/// Allows `for` loop in constant context, with 8 stages loop unroll
120/// optimization.
121#[macro_export]
122macro_rules! const_for_unroll8 {
123    (($i:ident in $start:tt.. $end:tt) $code:expr) => {{
124        let mut $i = $start;
125        loop {
126            $crate::cycle!($i, $end, $code);
127            $crate::cycle!($i, $end, $code);
128            $crate::cycle!($i, $end, $code);
129            $crate::cycle!($i, $end, $code);
130            $crate::cycle!($i, $end, $code);
131            $crate::cycle!($i, $end, $code);
132            $crate::cycle!($i, $end, $code);
133            $crate::cycle!($i, $end, $code);
134        }
135    }};
136}
137
138/// Single cycle step in the loop.
139#[macro_export]
140macro_rules! cycle {
141    ($i:ident, $end:tt, $code:expr) => {{
142        if $i < $end {
143            $code
144        } else {
145            break;
146        }
147        $i += 1;
148    }};
149}
150
151/// Single cycle step back in the loop.
152#[macro_export]
153macro_rules! rev_cycle {
154    ($i:ident, $end:tt, $code:expr) => {{
155        if $end < $i {
156            $i -= 1;
157            $code
158        } else {
159            break;
160        }
161    }};
162}