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    fn ct_lookup(&self, index: Idx) -> CtOption<T> {
28        let mut ret = CtOption::none();
29        let mut i = Idx::default();
30
31        for item in self {
32            ret.insert_if(item, i.ct_eq(&index));
33
34            // TODO(tarcieri): ideally we'd prevent overflow here but there's no core `CheckedAdd`
35            i += Idx::from(1u8);
36        }
37
38        ret
39    }
40}
41
42impl<T, Idx, const N: usize> CtLookup<Idx> for [T; N]
43where
44    T: CtAssign + Default,
45    Idx: AddAssign + CtEq + Default + From<u8>,
46{
47    type Output = T;
48
49    #[inline]
50    fn ct_lookup(&self, index: Idx) -> CtOption<T> {
51        self.as_slice().ct_lookup(index)
52    }
53}
54
55#[cfg(feature = "alloc")]
56mod alloc {
57    use super::{AddAssign, CtAssign, CtEq, CtLookup, CtOption};
58    use ::alloc::{boxed::Box, vec::Vec};
59
60    impl<T, Idx> CtLookup<Idx> for Box<[T]>
61    where
62        T: CtAssign + Default,
63        Idx: AddAssign + CtEq + Default + From<u8>,
64    {
65        type Output = T;
66
67        #[inline]
68        fn ct_lookup(&self, index: Idx) -> CtOption<T> {
69            (**self).ct_lookup(index)
70        }
71    }
72
73    impl<T, Idx> CtLookup<Idx> for Vec<T>
74    where
75        T: CtAssign + Default,
76        Idx: AddAssign + CtEq + Default + From<u8>,
77    {
78        type Output = T;
79
80        #[inline]
81        fn ct_lookup(&self, index: Idx) -> CtOption<T> {
82            self.as_slice().ct_lookup(index)
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    mod array {
90        use crate::CtLookup;
91
92        const EXAMPLE: [u8; 3] = [1, 2, 3];
93
94        #[test]
95        fn ct_lookup_u32() {
96            assert_eq!(EXAMPLE.ct_lookup(0u32).unwrap(), 1);
97            assert_eq!(EXAMPLE.ct_lookup(1u32).unwrap(), 2);
98            assert_eq!(EXAMPLE.ct_lookup(2u32).unwrap(), 3);
99            assert!(EXAMPLE.ct_lookup(3u32).is_none().to_bool());
100            assert!(EXAMPLE.ct_lookup(4u32).is_none().to_bool());
101        }
102
103        #[test]
104        fn ct_lookup_usize() {
105            assert_eq!(EXAMPLE.ct_lookup(0usize).unwrap(), 1);
106            assert_eq!(EXAMPLE.ct_lookup(1usize).unwrap(), 2);
107            assert_eq!(EXAMPLE.ct_lookup(2usize).unwrap(), 3);
108            assert!(EXAMPLE.ct_lookup(3usize).is_none().to_bool());
109            assert!(EXAMPLE.ct_lookup(4usize).is_none().to_bool());
110        }
111    }
112
113    mod slice {
114        use crate::CtLookup;
115
116        const EXAMPLE: &[u8] = &[1, 2, 3];
117
118        #[test]
119        fn ct_lookup_u32() {
120            assert_eq!(EXAMPLE.ct_lookup(0u32).unwrap(), 1);
121            assert_eq!(EXAMPLE.ct_lookup(1u32).unwrap(), 2);
122            assert_eq!(EXAMPLE.ct_lookup(2u32).unwrap(), 3);
123            assert!(EXAMPLE.ct_lookup(3u32).is_none().to_bool());
124            assert!(EXAMPLE.ct_lookup(4u32).is_none().to_bool());
125        }
126
127        #[test]
128        fn ct_lookup_usize() {
129            assert_eq!(EXAMPLE.ct_lookup(0usize).unwrap(), 1);
130            assert_eq!(EXAMPLE.ct_lookup(1usize).unwrap(), 2);
131            assert_eq!(EXAMPLE.ct_lookup(2usize).unwrap(), 3);
132            assert!(EXAMPLE.ct_lookup(3usize).is_none().to_bool());
133            assert!(EXAMPLE.ct_lookup(4usize).is_none().to_bool());
134        }
135    }
136}