use rand::distr::Alphanumeric;
use rand::{Rng, rng};
use std::collections::{HashMap, VecDeque};
pub fn random_string(len: usize) -> String {
rng()
.sample_iter(&Alphanumeric)
.take(len)
.map(char::from)
.collect()
}
pub struct BoundedHashMap<K, V> {
map: HashMap<K, V>,
queue: VecDeque<K>,
max_entries: usize,
}
impl<K: Clone + std::hash::Hash + std::cmp::Eq, V> BoundedHashMap<K, V> {
pub fn new(max_entries: usize) -> Self {
BoundedHashMap {
map: HashMap::new(),
queue: VecDeque::new(),
max_entries,
}
}
pub fn insert(&mut self, key: K, value: V) {
if self.max_entries == 0 {
return;
}
if self.queue.len() >= self.max_entries {
if let Some(removed) = self.queue.pop_back() {
self.map.remove(&removed);
}
}
if !self.map.contains_key(&key) {
self.queue.push_front(key.clone());
}
self.map.insert(key, value);
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self.map.get_mut(key)
}
#[allow(dead_code)] pub fn get(&self, key: &K) -> Option<&V> {
self.map.get(key)
}
#[allow(dead_code)] pub fn len(&self) -> usize {
self.map.len()
}
#[allow(dead_code)] pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[allow(dead_code)] pub fn capacity(&self) -> usize {
self.max_entries
}
pub fn take(&mut self, key: &K) -> Option<V> {
self.queue.retain(|k| k != key);
self.map.remove(key)
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.map.iter()
}
}
impl<K: Clone + std::hash::Hash + std::cmp::Eq, V> Default for BoundedHashMap<K, V> {
fn default() -> Self {
Self::new(1024)
}
}
use crate::message::Message;
use crate::metrics::Metrics;
pub(crate) fn try_send_or_log(
addr: &crate::actor::Addr,
msg: Message,
metrics: &Metrics,
ctx: &'static str,
) {
if addr.send(msg).is_err() {
metrics.record_dropped_send();
log::debug!(target: "beam::send", "actor mailbox full or closed, dropped message (context={})", ctx);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_string_length() {
let s = random_string(32);
assert_eq!(s.len(), 32);
}
#[test]
fn test_random_string_empty() {
let s = random_string(0);
assert_eq!(s.len(), 0);
}
#[test]
fn test_random_string_alphanumeric() {
let s = random_string(100);
assert!(s.chars().all(|c| c.is_ascii_alphanumeric()));
}
#[test]
fn test_random_string_uniqueness() {
let a = random_string(32);
let b = random_string(32);
assert_ne!(a, b);
}
#[test]
fn test_bounded_insert_and_get() {
let mut map = BoundedHashMap::new(10);
map.insert("a", 1);
map.insert("b", 2);
assert_eq!(map.get(&"a"), Some(&1));
assert_eq!(map.get(&"b"), Some(&2));
assert_eq!(map.len(), 2);
}
#[test]
fn test_bounded_eviction_fifo() {
let mut map = BoundedHashMap::new(2);
map.insert("a", 1);
map.insert("b", 2);
assert_eq!(map.len(), 2);
map.insert("c", 3); assert_eq!(map.get(&"a"), None);
assert_eq!(map.get(&"b"), Some(&2));
assert_eq!(map.get(&"c"), Some(&3));
assert_eq!(map.len(), 2);
}
#[test]
fn test_bounded_update_existing_key() {
let mut map = BoundedHashMap::new(2);
map.insert("a", 1);
map.insert("a", 99);
assert_eq!(map.get(&"a"), Some(&99));
assert_eq!(map.len(), 1);
}
#[test]
fn test_bounded_get_mut() {
let mut map = BoundedHashMap::new(10);
map.insert("a", 1);
if let Some(v) = map.get_mut(&"a") {
*v = 42;
}
assert_eq!(map.get(&"a"), Some(&42));
}
#[test]
fn test_bounded_is_empty() {
let map: BoundedHashMap<&str, i32> = BoundedHashMap::new(10);
assert!(map.is_empty());
}
#[test]
fn test_bounded_capacity() {
let map: BoundedHashMap<&str, i32> = BoundedHashMap::new(42);
assert_eq!(map.capacity(), 42);
}
#[test]
fn test_bounded_default() {
let map: BoundedHashMap<&str, i32> = BoundedHashMap::default();
assert_eq!(map.capacity(), 1024);
}
#[test]
fn test_bounded_zero_capacity() {
let mut map = BoundedHashMap::new(0);
map.insert("a", 1);
assert_eq!(map.get(&"a"), None); }
}