use std::{
borrow::{Borrow, BorrowMut},
marker::PhantomData,
os::fd::AsFd as _,
};
use crate::{
maps::{
check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys, PerCpuValues,
},
sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu, SyscallError},
Pod,
};
#[doc(alias = "BPF_MAP_TYPE_LRU_PERCPU_HASH")]
#[doc(alias = "BPF_MAP_TYPE_PERCPU_HASH")]
pub struct PerCpuHashMap<T, K: Pod, V: Pod> {
pub(crate) inner: T,
_k: PhantomData<K>,
_v: PhantomData<V>,
}
impl<T: Borrow<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow();
check_kv_size::<K, V>(data)?;
Ok(Self {
inner: map,
_k: PhantomData,
_v: PhantomData,
})
}
pub fn get(&self, key: &K, flags: u64) -> Result<PerCpuValues<V>, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let values =
bpf_map_lookup_elem_per_cpu(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
values.ok_or(MapError::KeyNotFound)
}
pub fn iter(&self) -> MapIter<'_, K, PerCpuValues<V>, Self> {
MapIter::new(self)
}
pub fn keys(&self) -> MapKeys<'_, K> {
MapKeys::new(self.inner.borrow())
}
}
impl<T: BorrowMut<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
pub fn insert(
&mut self,
key: impl Borrow<K>,
values: PerCpuValues<V>,
flags: u64,
) -> Result<(), MapError> {
let fd = self.inner.borrow_mut().fd().as_fd();
bpf_map_update_elem_per_cpu(fd, key.borrow(), &values, flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
},
)?;
Ok(())
}
pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
hash_map::remove(self.inner.borrow_mut(), key)
}
}
impl<T: Borrow<MapData>, K: Pod, V: Pod> IterableMap<K, PerCpuValues<V>>
for PerCpuHashMap<T, K, V>
{
fn map(&self) -> &MapData {
self.inner.borrow()
}
fn get(&self, key: &K) -> Result<PerCpuValues<V>, MapError> {
Self::get(self, key, 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
generated::bpf_map_type::{BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_HASH},
maps::{test_utils, Map},
};
#[test]
fn test_try_from_ok() {
let map = Map::PerCpuHashMap(test_utils::new_map(test_utils::new_obj_map::<u32>(
BPF_MAP_TYPE_PERCPU_HASH,
)));
assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok())
}
#[test]
fn test_try_from_ok_lru() {
let map_data =
|| test_utils::new_map(test_utils::new_obj_map::<u32>(BPF_MAP_TYPE_LRU_PERCPU_HASH));
let map = Map::PerCpuHashMap(map_data());
assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok());
let map = Map::PerCpuLruHashMap(map_data());
assert!(PerCpuHashMap::<_, u32, u32>::try_from(&map).is_ok())
}
}