light_array_map/
lib.rs

1#![no_std]
2
3use core::ptr::read_unaligned;
4
5use tinyvec::ArrayVec;
6
7/// A generic tinyvec::ArrayVec backed map with O(n) lookup.
8/// Maintains insertion order and tracks the last updated entry index.
9pub struct ArrayMap<K, V, const N: usize>
10where
11    K: PartialEq + Default,
12    V: Default,
13{
14    entries: ArrayVec<[(K, V); N]>,
15    last_updated_index: Option<usize>,
16}
17
18impl<K, V, const N: usize> ArrayMap<K, V, N>
19where
20    K: PartialEq + Default,
21    V: Default,
22{
23    pub fn new() -> Self {
24        Self {
25            entries: ArrayVec::new(),
26            last_updated_index: None,
27        }
28    }
29
30    pub fn len(&self) -> usize {
31        self.entries.len()
32    }
33
34    pub fn is_empty(&self) -> bool {
35        self.entries.is_empty()
36    }
37
38    pub fn last_updated_index(&self) -> Option<usize> {
39        self.last_updated_index
40    }
41
42    pub fn get(&self, index: usize) -> Option<&(K, V)> {
43        self.entries.get(index)
44    }
45
46    pub fn get_mut(&mut self, index: usize) -> Option<&mut (K, V)> {
47        self.entries.get_mut(index)
48    }
49
50    pub fn get_u8(&self, index: u8) -> Option<&(K, V)> {
51        self.get(index as usize)
52    }
53
54    pub fn get_mut_u8(&mut self, index: u8) -> Option<&mut (K, V)> {
55        self.get_mut(index as usize)
56    }
57
58    pub fn get_by_key(&self, key: &K) -> Option<&V> {
59        self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
60    }
61
62    pub fn get_mut_by_key(&mut self, key: &K) -> Option<&mut V> {
63        self.entries
64            .iter_mut()
65            .find(|(k, _)| k == key)
66            .map(|(_, v)| v)
67    }
68
69    pub fn find(&self, key: &K) -> Option<(usize, &(K, V))> {
70        self.entries.iter().enumerate().find(|(_, (k, _))| k == key)
71    }
72
73    pub fn find_mut(&mut self, key: &K) -> Option<(usize, &mut (K, V))> {
74        self.entries
75            .iter_mut()
76            .enumerate()
77            .find(|(_, (k, _))| k == key)
78    }
79
80    pub fn find_index(&self, key: &K) -> Option<usize> {
81        self.find(key).map(|(idx, _)| idx)
82    }
83
84    pub fn set_last_updated_index<E>(&mut self, index: usize) -> Result<(), E>
85    where
86        E: From<ArrayMapError>,
87    {
88        if index < self.entries.len() {
89            self.last_updated_index = Some(index);
90            Ok(())
91        } else {
92            Err(ArrayMapError::IndexOutOfBounds.into())
93        }
94    }
95
96    pub fn insert<E>(&mut self, key: K, value: V, error: E) -> Result<usize, E> {
97        let new_idx = self.entries.len();
98        // tinyvec's try_push returns Some(item) on failure, None on success
99        if self.entries.try_push((key, value)).is_some() {
100            return Err(error);
101        }
102        self.last_updated_index = Some(new_idx);
103        Ok(new_idx)
104    }
105}
106
107impl<K, V, const N: usize> Default for ArrayMap<K, V, N>
108where
109    K: PartialEq + Default,
110    V: Default,
111{
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117// Optimized [u8; 32] key methods (4x u64 comparison instead of 32x u8).
118impl<V, const N: usize> ArrayMap<[u8; 32], V, N>
119where
120    V: Default,
121{
122    pub fn get_by_pubkey(&self, key: &[u8; 32]) -> Option<&V> {
123        self.entries
124            .iter()
125            .find(|(k, _)| pubkey_eq(k, key))
126            .map(|(_, v)| v)
127    }
128
129    pub fn get_mut_by_pubkey(&mut self, key: &[u8; 32]) -> Option<&mut V> {
130        self.entries
131            .iter_mut()
132            .find(|(k, _)| pubkey_eq(k, key))
133            .map(|(_, v)| v)
134    }
135
136    pub fn find_by_pubkey(&self, key: &[u8; 32]) -> Option<(usize, &([u8; 32], V))> {
137        self.entries
138            .iter()
139            .enumerate()
140            .find(|(_, (k, _))| pubkey_eq(k, key))
141    }
142
143    pub fn find_mut_by_pubkey(&mut self, key: &[u8; 32]) -> Option<(usize, &mut ([u8; 32], V))> {
144        self.entries
145            .iter_mut()
146            .enumerate()
147            .find(|(_, (k, _))| pubkey_eq(k, key))
148    }
149
150    pub fn find_pubkey_index(&self, key: &[u8; 32]) -> Option<usize> {
151        self.find_by_pubkey(key).map(|(idx, _)| idx)
152    }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum ArrayMapError {
157    CapacityExceeded,
158    IndexOutOfBounds,
159}
160
161impl core::fmt::Display for ArrayMapError {
162    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163        match self {
164            ArrayMapError::CapacityExceeded => write!(f, "ArrayMap capacity exceeded"),
165            ArrayMapError::IndexOutOfBounds => write!(f, "ArrayMap index out of bounds"),
166        }
167    }
168}
169
170#[inline(always)]
171pub const fn pubkey_eq(p1: &[u8; 32], p2: &[u8; 32]) -> bool {
172    let p1_ptr = p1.as_ptr() as *const u64;
173    let p2_ptr = p2.as_ptr() as *const u64;
174
175    unsafe {
176        read_unaligned(p1_ptr) == read_unaligned(p2_ptr)
177            && read_unaligned(p1_ptr.add(1)) == read_unaligned(p2_ptr.add(1))
178            && read_unaligned(p1_ptr.add(2)) == read_unaligned(p2_ptr.add(2))
179            && read_unaligned(p1_ptr.add(3)) == read_unaligned(p2_ptr.add(3))
180    }
181}