1use std::{
2 borrow::{Borrow, BorrowMut},
3 marker::PhantomData,
4};
5
6use crate::{
7 Pod,
8 maps::{IterableMap, MapData, MapError, MapIter, MapKeys, check_kv_size, hash_map},
9};
10
11#[doc(alias = "BPF_MAP_TYPE_HASH")]
32#[doc(alias = "BPF_MAP_TYPE_LRU_HASH")]
33#[derive(Debug)]
34pub struct HashMap<T, K, V> {
35 pub(crate) inner: T,
36 _kv: PhantomData<(K, V)>,
37}
38
39impl<T: Borrow<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
40 pub(crate) fn new(map: T) -> Result<Self, MapError> {
41 let data = map.borrow();
42 check_kv_size::<K, V>(data)?;
43
44 Ok(Self {
45 inner: map,
46 _kv: PhantomData,
47 })
48 }
49
50 pub fn get(&self, key: &K, flags: u64) -> Result<V, MapError> {
52 hash_map::get(self.inner.borrow(), key, flags)
53 }
54
55 pub fn iter(&self) -> MapIter<'_, K, V, Self> {
58 MapIter::new(self)
59 }
60
61 pub fn keys(&self) -> MapKeys<'_, K> {
64 MapKeys::new(self.inner.borrow())
65 }
66}
67
68impl<'a, T: Borrow<MapData>, K: Pod, V: Pod> IntoIterator for &'a HashMap<T, K, V> {
69 type Item = Result<(K, V), MapError>;
70 type IntoIter = MapIter<'a, K, V, HashMap<T, K, V>>;
71
72 fn into_iter(self) -> Self::IntoIter {
73 self.iter()
74 }
75}
76
77impl<T: BorrowMut<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
78 pub fn insert(
80 &mut self,
81 key: impl Borrow<K>,
82 value: impl Borrow<V>,
83 flags: u64,
84 ) -> Result<(), MapError> {
85 hash_map::insert(self.inner.borrow_mut(), key.borrow(), value.borrow(), flags)
86 }
87
88 pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
90 hash_map::remove(self.inner.borrow_mut(), key)
91 }
92}
93
94impl<T: Borrow<MapData>, K: Pod, V: Pod> IterableMap<K, V> for HashMap<T, K, V> {
95 fn map(&self) -> &MapData {
96 self.inner.borrow()
97 }
98
99 fn get(&self, key: &K) -> Result<V, MapError> {
100 Self::get(self, key, 0)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use std::io;
107
108 use assert_matches::assert_matches;
109 use aya_obj::generated::{bpf_attr, bpf_cmd, bpf_map_type};
110 use libc::{EFAULT, ENOENT};
111
112 use super::*;
113 use crate::{
114 maps::{
115 Map,
116 test_utils::{self, new_map},
117 },
118 sys::{SysResult, Syscall, SyscallError, override_syscall},
119 };
120
121 fn new_obj_map() -> aya_obj::Map {
122 test_utils::new_obj_map::<u32>(bpf_map_type::BPF_MAP_TYPE_HASH)
123 }
124
125 fn sys_error(value: i32) -> SysResult {
126 Err((-1, io::Error::from_raw_os_error(value)))
127 }
128
129 #[test]
130 fn test_wrong_key_size() {
131 let map = new_map(new_obj_map());
132 assert_matches!(
133 HashMap::<_, u8, u32>::new(&map),
134 Err(MapError::InvalidKeySize {
135 size: 1,
136 expected: 4
137 })
138 );
139 }
140
141 #[test]
142 fn test_wrong_value_size() {
143 let map = new_map(new_obj_map());
144 assert_matches!(
145 HashMap::<_, u32, u16>::new(&map),
146 Err(MapError::InvalidValueSize {
147 size: 2,
148 expected: 4
149 })
150 );
151 }
152
153 #[test]
154 fn test_try_from_wrong_map() {
155 let map = new_map(new_obj_map());
156 let map = Map::Array(map);
157 assert_matches!(
158 HashMap::<_, u8, u32>::try_from(&map),
159 Err(MapError::InvalidMapType { .. })
160 );
161 }
162
163 #[test]
164 fn test_try_from_wrong_map_values() {
165 let map = new_map(new_obj_map());
166 let map = Map::HashMap(map);
167 assert_matches!(
168 HashMap::<_, u32, u16>::try_from(&map),
169 Err(MapError::InvalidValueSize {
170 size: 2,
171 expected: 4
172 })
173 );
174 }
175
176 #[test]
177 fn test_new_ok() {
178 let map = new_map(new_obj_map());
179 let _: HashMap<_, u32, u32> = HashMap::new(&map).unwrap();
180 }
181
182 #[test]
183 fn test_try_from_ok() {
184 let map = new_map(new_obj_map());
185 let map = Map::HashMap(map);
186 let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
187 }
188
189 #[test]
190 fn test_try_from_ok_lru() {
191 let map_data = || {
192 new_map(test_utils::new_obj_map::<u32>(
193 bpf_map_type::BPF_MAP_TYPE_LRU_HASH,
194 ))
195 };
196 let map = Map::HashMap(map_data());
197 let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
198 let map = Map::LruHashMap(map_data());
199 let _unused: HashMap<_, u32, u32> = map.try_into().unwrap();
200 }
201
202 #[test]
203 fn test_insert_syscall_error() {
204 let mut map = new_map(new_obj_map());
205 let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
206
207 override_syscall(|_| sys_error(EFAULT));
208
209 assert_matches!(
210 hm.insert(1, 42, 0),
211 Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
212 );
213 }
214
215 #[test]
216 fn test_insert_ok() {
217 let mut map = new_map(new_obj_map());
218 let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
219
220 override_syscall(|call| match call {
221 Syscall::Ebpf {
222 cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
223 ..
224 } => Ok(0),
225 _ => sys_error(EFAULT),
226 });
227
228 assert_matches!(hm.insert(1, 42, 0), Ok(()));
229 }
230
231 #[test]
232 fn test_insert_boxed_ok() {
233 let mut map = new_map(new_obj_map());
234 let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
235
236 override_syscall(|call| match call {
237 Syscall::Ebpf {
238 cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
239 ..
240 } => Ok(0),
241 _ => sys_error(EFAULT),
242 });
243
244 assert_matches!(hm.insert(Box::new(1), Box::new(42), 0), Ok(()));
245 }
246
247 #[test]
248 fn test_remove_syscall_error() {
249 let mut map = new_map(new_obj_map());
250 let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
251
252 override_syscall(|_| sys_error(EFAULT));
253
254 assert_matches!(
255 hm.remove(&1),
256 Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
257 );
258 }
259
260 #[test]
261 fn test_remove_ok() {
262 let mut map = new_map(new_obj_map());
263 let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
264
265 override_syscall(|call| match call {
266 Syscall::Ebpf {
267 cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
268 ..
269 } => Ok(0),
270 _ => sys_error(EFAULT),
271 });
272
273 assert_matches!(hm.remove(&1), Ok(()));
274 }
275
276 #[test]
277 fn test_get_syscall_error() {
278 let map = new_map(new_obj_map());
279 override_syscall(|_| sys_error(EFAULT));
280 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
281
282 assert_matches!(
283 hm.get(&1, 0),
284 Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
285 );
286 }
287
288 #[test]
289 fn test_get_not_found() {
290 let map = new_map(new_obj_map());
291 override_syscall(|call| match call {
292 Syscall::Ebpf {
293 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
294 ..
295 } => sys_error(ENOENT),
296 _ => sys_error(EFAULT),
297 });
298 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
299
300 assert_matches!(hm.get(&1, 0), Err(MapError::KeyNotFound));
301 }
302
303 fn bpf_key<T: Copy>(attr: &bpf_attr) -> Option<T> {
304 match unsafe { attr.__bindgen_anon_2.key } as *const T {
305 p if p.is_null() => None,
306 p => Some(unsafe { *p }),
307 }
308 }
309
310 #[expect(
311 clippy::unnecessary_wraps,
312 reason = "mock syscall handlers use SysResult"
313 )]
314 fn set_next_key<T: Copy>(attr: &bpf_attr, next: T) -> SysResult {
315 let key =
316 (unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.next_key } as *const T).cast_mut();
317 unsafe { *key = next }
318 Ok(0)
319 }
320
321 #[expect(
322 clippy::unnecessary_wraps,
323 reason = "mock syscall handlers use SysResult"
324 )]
325 fn set_ret<T: Copy>(attr: &bpf_attr, ret: T) -> SysResult {
326 let value =
327 (unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.value } as *const T).cast_mut();
328 unsafe { *value = ret }
329 Ok(0)
330 }
331
332 #[test]
333 fn test_keys_empty() {
334 let map = new_map(new_obj_map());
335 override_syscall(|call| match call {
336 Syscall::Ebpf {
337 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
338 ..
339 } => sys_error(ENOENT),
340 _ => sys_error(EFAULT),
341 });
342 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
343 let keys = hm.keys().collect::<Result<Vec<_>, _>>();
344 assert_matches!(keys, Ok(ks) if ks.is_empty())
345 }
346
347 fn get_next_key(attr: &bpf_attr) -> SysResult {
348 match bpf_key(attr) {
349 None => set_next_key(attr, 10),
350 Some(10) => set_next_key(attr, 20),
351 Some(20) => set_next_key(attr, 30),
352 Some(30) => sys_error(ENOENT),
353 Some(_) => sys_error(EFAULT),
354 }
355 }
356
357 fn lookup_elem(attr: &bpf_attr) -> SysResult {
358 match bpf_key(attr) {
359 Some(10) => set_ret(attr, 100),
360 Some(20) => set_ret(attr, 200),
361 Some(30) => set_ret(attr, 300),
362 Some(_) => sys_error(ENOENT),
363 None => sys_error(EFAULT),
364 }
365 }
366
367 #[test]
368 fn test_keys() {
369 let map = new_map(new_obj_map());
370
371 override_syscall(|call| match call {
372 Syscall::Ebpf {
373 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
374 attr,
375 } => get_next_key(attr),
376 _ => sys_error(EFAULT),
377 });
378
379 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
380
381 let keys = hm.keys().collect::<Result<Vec<_>, _>>().unwrap();
382 assert_eq!(&keys, &[10, 20, 30])
383 }
384
385 #[test]
386 fn test_keys_error() {
387 let map = new_map(new_obj_map());
388 override_syscall(|call| match call {
389 Syscall::Ebpf {
390 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
391 attr,
392 } => match bpf_key(attr) {
393 None => set_next_key(attr, 10),
394 Some(10) => set_next_key(attr, 20),
395 Some(_) => sys_error(EFAULT),
396 },
397 _ => sys_error(EFAULT),
398 });
399 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
400
401 let mut keys = hm.keys();
402 assert_matches!(keys.next(), Some(Ok(10)));
403 assert_matches!(keys.next(), Some(Ok(20)));
404 assert_matches!(
405 keys.next(),
406 Some(Err(MapError::SyscallError(SyscallError {
407 call: "bpf_map_get_next_key",
408 io_error: _
409 })))
410 );
411 assert_matches!(keys.next(), None);
412 }
413
414 #[test]
415 fn test_iter() {
416 let map = new_map(new_obj_map());
417 override_syscall(|call| match call {
418 Syscall::Ebpf {
419 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
420 attr,
421 } => get_next_key(attr),
422 Syscall::Ebpf {
423 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
424 attr,
425 } => lookup_elem(attr),
426 _ => sys_error(EFAULT),
427 });
428 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
429 let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
430 assert_eq!(&items, &[(10, 100), (20, 200), (30, 300)])
431 }
432
433 #[test]
434 fn test_iter_key_deleted() {
435 let map = new_map(new_obj_map());
436 override_syscall(|call| match call {
437 Syscall::Ebpf {
438 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
439 attr,
440 } => get_next_key(attr),
441 Syscall::Ebpf {
442 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
443 attr,
444 } => match bpf_key(attr) {
445 Some(10) => set_ret(attr, 100),
446 Some(20) => sys_error(ENOENT),
447 Some(30) => set_ret(attr, 300),
448 Some(_) => sys_error(ENOENT),
449 None => sys_error(EFAULT),
450 },
451 _ => sys_error(EFAULT),
452 });
453 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
454
455 let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
456 assert_eq!(&items, &[(10, 100), (30, 300)])
457 }
458
459 #[test]
460 fn test_iter_key_error() {
461 let map = new_map(new_obj_map());
462 override_syscall(|call| match call {
463 Syscall::Ebpf {
464 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
465 attr,
466 } => match bpf_key(attr) {
467 None => set_next_key(attr, 10),
468 Some(10) => set_next_key(attr, 20),
469 Some(20) => sys_error(EFAULT),
470 Some(30) => sys_error(ENOENT),
471 Some(i) => panic!("invalid key {i}"),
472 },
473 Syscall::Ebpf {
474 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
475 attr,
476 } => lookup_elem(attr),
477 _ => sys_error(EFAULT),
478 });
479 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
480
481 let mut iter = hm.iter();
482 assert_matches!(iter.next(), Some(Ok((10, 100))));
483 assert_matches!(iter.next(), Some(Ok((20, 200))));
484 assert_matches!(
485 iter.next(),
486 Some(Err(MapError::SyscallError(SyscallError {
487 call: "bpf_map_get_next_key",
488 io_error: _
489 })))
490 );
491 assert_matches!(iter.next(), None);
492 }
493
494 #[test]
495 fn test_iter_value_error() {
496 let map = new_map(new_obj_map());
497 override_syscall(|call| match call {
498 Syscall::Ebpf {
499 cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
500 attr,
501 } => get_next_key(attr),
502 Syscall::Ebpf {
503 cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
504 attr,
505 } => match bpf_key(attr) {
506 Some(10) => set_ret(attr, 100),
507 Some(20) => sys_error(EFAULT),
508 Some(30) => set_ret(attr, 300),
509 Some(_) => sys_error(ENOENT),
510 None => sys_error(EFAULT),
511 },
512 _ => sys_error(EFAULT),
513 });
514 let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
515
516 let mut iter = hm.iter();
517 assert_matches!(iter.next(), Some(Ok((10, 100))));
518 assert_matches!(
519 iter.next(),
520 Some(Err(MapError::SyscallError(SyscallError {
521 call: "bpf_map_lookup_elem",
522 io_error: _
523 })))
524 );
525 assert_matches!(iter.next(), Some(Ok((30, 300))));
526 assert_matches!(iter.next(), None);
527 }
528}