Skip to main content

ctutils/traits/
ct_lookup.rs

1use crate::{CtAssign, CtEq, CtOption};
2use core::ops::AddAssign;
3
4#[cfg(doc)]
5use core::ops::Index;
6
7/// Constant-time lookup by index, similar to the [`Index`] trait, but returning an owned result in
8/// constant-time.
9pub trait CtLookup<Idx> {
10    /// Output type returned by the lookup operation.
11    type Output: CtAssign;
12
13    /// Attempt to retrieve the item at the given `index`, either returning it or the [`CtOption`]
14    /// equivalent of [`None`] if the `index` was out-of-bounds.
15    #[must_use]
16    fn ct_lookup(&self, index: Idx) -> CtOption<Self::Output>;
17}
18
19impl<T, Idx> CtLookup<Idx> for [T]
20where
21    T: CtAssign + Default,
22    Idx: AddAssign + CtEq + Default + From<u8>,
23{
24    type Output = T;
25
26    #[inline]
27    #[allow(clippy::arithmetic_side_effects)]
28    fn ct_lookup(&self, index: Idx) -> CtOption<T> {
29        let mut ret = CtOption::none();
30        let mut i = Idx::default();
31
32        for item in self {
33            ret.insert_if(item, i.ct_eq(&index));
34
35            // TODO(tarcieri): ideally we'd prevent overflow here but there's no core `CheckedAdd`
36            i += Idx::from(1u8);
37        }
38
39        ret
40    }
41}
42
43impl<T, Idx, const N: usize> CtLookup<Idx> for [T; N]
44where
45    T: CtAssign + Default,
46    Idx: AddAssign + CtEq + Default + From<u8>,
47{
48    type Output = T;
49
50    #[inline]
51    fn ct_lookup(&self, index: Idx) -> CtOption<T> {
52        self.as_slice().ct_lookup(index)
53    }
54}
55
56#[cfg(feature = "alloc")]
57mod alloc {
58    use super::{AddAssign, CtAssign, CtEq, CtLookup, CtOption};
59    use ::alloc::{boxed::Box, vec::Vec};
60
61    impl<T, Idx> CtLookup<Idx> for Box<[T]>
62    where
63        T: CtAssign + Default,
64        Idx: AddAssign + CtEq + Default + From<u8>,
65    {
66        type Output = T;
67
68        #[inline]
69        fn ct_lookup(&self, index: Idx) -> CtOption<T> {
70            (**self).ct_lookup(index)
71        }
72    }
73
74    impl<T, Idx> CtLookup<Idx> for Vec<T>
75    where
76        T: CtAssign + Default,
77        Idx: AddAssign + CtEq + Default + From<u8>,
78    {
79        type Output = T;
80
81        #[inline]
82        fn ct_lookup(&self, index: Idx) -> CtOption<T> {
83            self.as_slice().ct_lookup(index)
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    mod array {
91        use crate::CtLookup;
92
93        const EXAMPLE: [u8; 3] = [1, 2, 3];
94
95        #[test]
96        fn ct_lookup_u32() {
97            assert_eq!(EXAMPLE.ct_lookup(0u32).unwrap(), 1);
98            assert_eq!(EXAMPLE.ct_lookup(1u32).unwrap(), 2);
99            assert_eq!(EXAMPLE.ct_lookup(2u32).unwrap(), 3);
100            assert!(EXAMPLE.ct_lookup(3u32).is_none().to_bool());
101            assert!(EXAMPLE.ct_lookup(4u32).is_none().to_bool());
102        }
103
104        #[test]
105        fn ct_lookup_usize() {
106            assert_eq!(EXAMPLE.ct_lookup(0usize).unwrap(), 1);
107            assert_eq!(EXAMPLE.ct_lookup(1usize).unwrap(), 2);
108            assert_eq!(EXAMPLE.ct_lookup(2usize).unwrap(), 3);
109            assert!(EXAMPLE.ct_lookup(3usize).is_none().to_bool());
110            assert!(EXAMPLE.ct_lookup(4usize).is_none().to_bool());
111        }
112    }
113
114    mod slice {
115        use crate::CtLookup;
116
117        const EXAMPLE: &[u8] = &[1, 2, 3];
118
119        #[test]
120        fn ct_lookup_u32() {
121            assert_eq!(EXAMPLE.ct_lookup(0u32).unwrap(), 1);
122            assert_eq!(EXAMPLE.ct_lookup(1u32).unwrap(), 2);
123            assert_eq!(EXAMPLE.ct_lookup(2u32).unwrap(), 3);
124            assert!(EXAMPLE.ct_lookup(3u32).is_none().to_bool());
125            assert!(EXAMPLE.ct_lookup(4u32).is_none().to_bool());
126        }
127
128        #[test]
129        fn ct_lookup_usize() {
130            assert_eq!(EXAMPLE.ct_lookup(0usize).unwrap(), 1);
131            assert_eq!(EXAMPLE.ct_lookup(1usize).unwrap(), 2);
132            assert_eq!(EXAMPLE.ct_lookup(2usize).unwrap(), 3);
133            assert!(EXAMPLE.ct_lookup(3usize).is_none().to_bool());
134            assert!(EXAMPLE.ct_lookup(4usize).is_none().to_bool());
135        }
136    }
137}