renet/src/iface/arp_cache.rs

160 lines
5.3 KiB
Rust

use managed::ManagedSlice;
use wire::{EthernetAddress, IpAddress};
/// An Address Resolution Protocol cache.
///
/// This interface maps protocol addresses to hardware addresses.
pub trait Cache {
/// Update the cache to map given protocol address to given hardware address.
fn fill(&mut self, protocol_addr: &IpAddress, hardware_addr: &EthernetAddress);
/// Look up the hardware address corresponding for the given protocol address.
fn lookup(&mut self, protocol_addr: &IpAddress) -> Option<EthernetAddress>;
}
/// An Address Resolution Protocol cache backed by a slice.
///
/// This cache uses a fixed-size storage, binary search, and a least recently used
/// eviction strategy.
///
/// # Examples
///
/// On systems with heap, this cache can be created with:
/// ```rust
/// use smoltcp::iface::SliceArpCache;
/// let mut arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
/// ```
///
/// On systems without heap, use:
/// ```rust
/// use smoltcp::iface::SliceArpCache;
/// let mut arp_cache_storage = [Default::default(); 8]
/// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage[..]);
/// ```
pub struct SliceCache<'a> {
storage: ManagedSlice<'a, (IpAddress, EthernetAddress, usize)>,
counter: usize
}
impl<'a> SliceCache<'a> {
/// Create a cache. The backing storage is cleared upon creation.
///
/// # Panics
/// This function panics if `storage.len() == 0`.
pub fn new<T>(storage: T) -> SliceCache<'a>
where T: Into<ManagedSlice<'a, (IpAddress, EthernetAddress, usize)>> {
let mut storage = storage.into();
if storage.len() == 0 {
panic!("ARP slice cache created with empty storage")
}
for elem in storage.iter_mut() {
*elem = Default::default()
}
SliceCache {
storage: storage,
counter: 0
}
}
/// Find an entry for the given protocol address, if any.
fn find(&self, protocol_addr: &IpAddress) -> Option<usize> {
// The order of comparison is important: any valid IpAddress should
// sort before IpAddress::Invalid.
self.storage.binary_search_by_key(protocol_addr, |&(key, _, _)| key).ok()
}
/// Sort entries in an order suitable for `find`.
fn sort(&mut self) {
#[cfg(feature = "std")]
fn sort(data: &mut [(IpAddress, EthernetAddress, usize)]) {
data.sort_by_key(|&(key, _, _)| key)
}
#[cfg(not(feature = "std"))]
fn sort(data: &mut [(IpAddress, EthernetAddress, usize)]) {
// Use an insertion sort, which performs best on 10 elements and less.
for i in 1..data.len() {
let mut j = i;
while j > 0 && data[j-1].0 > data[j].0 {
data.swap(j, j - 1);
j = j - 1;
}
}
}
sort(&mut self.storage)
}
/// Find the least recently used entry.
fn lru(&self) -> usize {
self.storage.iter().enumerate().min_by_key(|&(_, &(_, _, counter))| counter).unwrap().0
}
}
impl<'a> Cache for SliceCache<'a> {
fn fill(&mut self, protocol_addr: &IpAddress, hardware_addr: &EthernetAddress) {
if let None = self.find(protocol_addr) {
let lru_index = self.lru();
self.storage[lru_index] =
(*protocol_addr, *hardware_addr, self.counter);
self.sort()
}
}
fn lookup(&mut self, protocol_addr: &IpAddress) -> Option<EthernetAddress> {
if let Some(index) = self.find(protocol_addr) {
let (_protocol_addr, hardware_addr, ref mut counter) =
self.storage[index];
self.counter += 1;
*counter = self.counter;
Some(hardware_addr)
} else {
None
}
}
}
#[cfg(test)]
mod test {
use wire::Ipv4Address;
use super::*;
const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
const PADDR_A: IpAddress = IpAddress::Ipv4(Ipv4Address([0, 0, 0, 0]));
const PADDR_B: IpAddress = IpAddress::Ipv4(Ipv4Address([0, 0, 0, 1]));
const PADDR_C: IpAddress = IpAddress::Ipv4(Ipv4Address([0, 0, 0, 2]));
const PADDR_D: IpAddress = IpAddress::Ipv4(Ipv4Address([0, 0, 0, 3]));
#[test]
fn test_slice_cache() {
let mut cache_storage = [Default::default(); 3];
let mut cache = SliceCache::new(&mut cache_storage[..]);
cache.fill(&PADDR_A, &HADDR_A);
assert_eq!(cache.lookup(&PADDR_A), Some(HADDR_A));
assert_eq!(cache.lookup(&PADDR_B), None);
cache.fill(&PADDR_B, &HADDR_B);
cache.fill(&PADDR_C, &HADDR_C);
assert_eq!(cache.lookup(&PADDR_A), Some(HADDR_A));
assert_eq!(cache.lookup(&PADDR_B), Some(HADDR_B));
assert_eq!(cache.lookup(&PADDR_C), Some(HADDR_C));
cache.lookup(&PADDR_B);
cache.lookup(&PADDR_A);
cache.lookup(&PADDR_C);
cache.fill(&PADDR_D, &HADDR_D);
assert_eq!(cache.lookup(&PADDR_A), Some(HADDR_A));
assert_eq!(cache.lookup(&PADDR_B), None);
assert_eq!(cache.lookup(&PADDR_C), Some(HADDR_C));
assert_eq!(cache.lookup(&PADDR_D), Some(HADDR_D));
}
}