use std::collections::HashMap;
use std::collections::LinkedList;
use std::hash::Hash;
use std::sync::Arc;
mod timeoutset;
pub use timeoutset::TimeoutSet;
pub enum MemCacheMode {
None,
TryClearAtUpdate,
}
impl MemCacheMode {
pub fn is_try_clear(&self) -> bool {
match self {
MemCacheMode::TryClearAtUpdate => true,
_ => false,
}
}
}
impl Default for MemCacheMode {
fn default() -> Self {
Self::TryClearAtUpdate
}
}
#[derive(Default)]
pub struct MemCache<K, T>
where
K: Eq + Hash,
{
map: HashMap<K, (u64, T)>,
time_set: TimeoutSet<K>,
last_clear_time: u64,
try_clear_interval: u64,
pub mode: MemCacheMode,
pub time_out_fn: Option<Arc<dyn Fn(K, T) + Send + Sync>>,
}
fn now_millis() -> u64 {
use std::time::SystemTime;
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
impl<K, T> MemCache<K, T>
where
K: Eq + Hash + Clone,
T: Clone,
{
pub fn new() -> Self {
Self::new_with_clear_interval(10000)
}
pub fn new_with_clear_interval(interval_millis: u64) -> Self {
Self {
map: Default::default(),
time_set: Default::default(),
last_clear_time: 0u64,
try_clear_interval: interval_millis,
mode: MemCacheMode::TryClearAtUpdate,
time_out_fn: None,
}
}
fn build_last_time(cache_sec: i32) -> u64 {
if cache_sec == -1i32 {
0
} else {
((cache_sec * 1000) as u64) + now_millis()
}
}
fn try_clear(&mut self) {
if !self.mode.is_try_clear() {
return;
}
let current_time = now_millis();
if current_time - self.try_clear_interval > self.last_clear_time {
self.clear_time_out();
}
}
pub fn get_time_out_keys(&self) -> LinkedList<&K> {
let current_time = now_millis();
self.time_set.get_timeout_values(current_time)
}
pub fn clear_time_out(&mut self) {
let current_time = now_millis();
let list = self.time_set.timeout(current_time);
for item in list {
if let Some((t, _)) = self.map.get(&item) {
if *t > current_time {
continue;
}
let (_, v) = self.map.remove(&item).unwrap();
if let Some(f) = &self.time_out_fn {
f(item, v);
}
}
}
self.last_clear_time = current_time;
}
pub fn set(&mut self, key: K, val: T, cache_sec: i32) {
self.try_clear();
if cache_sec == 0i32 || cache_sec < -1i32 {
return;
}
let last_time = Self::build_last_time(cache_sec);
self.time_set.add(last_time, key.clone());
self.map.insert(key, (last_time, val));
}
pub fn update_time_out(&mut self, key: &K, cache_sec: i32) {
self.try_clear();
if cache_sec == 0i32 || cache_sec < -1i32 {
return;
}
match self.get(key) {
Ok(_) => {
let last_time = Self::build_last_time(cache_sec);
self.time_set.add(last_time, key.clone());
}
Err(_) => {}
};
}
pub fn get(&self, key: &K) -> Result<T, String> {
match self.map.get(key) {
Some((t, v)) => {
if *t == 0 {
return Ok(v.clone());
} else if now_millis() > *t {
return Err("Expried".to_owned());
}
Ok(v.clone())
}
None => Err("NOT FOUND".to_owned()),
}
}
pub fn time_to_live(&self, key: &K) -> i32 {
match self.map.get(key) {
Some((t, _)) => {
let now = now_millis();
if *t > now {
((*t - now) / 1000) as i32
} else {
-1
}
}
None => -1,
}
}
pub fn remove(&mut self, key: &K) -> Option<T> {
self.try_clear();
match self.map.remove(key) {
Some((_, v)) => Some(v),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn time_out_print(k: String, v: String) {
println!("============ time out,{}:{}", &k, &v);
}
#[test]
fn test01() {
let mut m = MemCache::new_with_clear_interval(100);
let ref_info = Arc::new(1);
m.time_out_fn = Some(Arc::new(move |k, v| {
println!("============ time out,{}:{}", &k, &v);
println!("ref_info ,{}", &ref_info)
}));
let name = "name".to_owned();
m.set("name".to_owned(), "abc".to_owned(), 1);
println!("1.{:?}", m.get(&name));
assert!(m.get(&name).is_ok());
std::thread::sleep(Duration::from_millis(1001));
println!("2:{:?}", m.get(&name));
assert!(m.get(&name).is_err());
m.set("name".to_owned(), "abc".to_owned(), 1);
println!("3:{:?}", m.get(&name));
assert!(m.get(&name).is_ok());
std::thread::sleep(Duration::from_millis(1001));
assert!(m.get(&name).is_err());
println!("4:{:?}", m.get(&name));
std::thread::sleep(Duration::from_millis(1001));
m.clear_time_out()
}
}