1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Trait and inmemory implementation of [PkarrCache]

use dyn_clone::DynClone;
use lru::LruCache;
use mainline::Id;
use std::fmt::Debug;
use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex};

use crate::SignedPacket;

/// The sha1 hash of the [crate::PublicKey] used as the key in [PkarrCache].
pub type PkarrCacheKey = Id;

pub trait PkarrCache: Debug + Send + Sync + DynClone {
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    /// Puts [SignedPacket] into cache.
    fn put(&self, key: &PkarrCacheKey, signed_packet: &SignedPacket);
    /// Reads [SignedPacket] from cache, while moving it to the head of the LRU list.
    fn get(&self, key: &PkarrCacheKey) -> Option<SignedPacket>;
    /// Reads [SignedPacket] from cache, without changing the LRU list.
    ///
    /// Used for internal reads that are not initiated by the user directly,
    /// like comparing an received signed packet with existing one.
    ///
    /// Useful to implement differently from [PkarrCache::get], if you are implementing
    /// persistent cache where writes are slower than reads.
    ///
    /// Otherwise it will just use [PkarrCache::get].
    fn get_read_only(&self, key: &PkarrCacheKey) -> Option<SignedPacket> {
        self.get(key)
    }
}

dyn_clone::clone_trait_object!(PkarrCache);

/// A thread safe wrapper around `LruCache`
#[derive(Debug, Clone)]
pub struct InMemoryPkarrCache {
    inner: Arc<Mutex<LruCache<Id, SignedPacket>>>,
}

impl InMemoryPkarrCache {
    /// Creats a new `LRU` cache that holds at most `cap` items.
    pub fn new(capacity: NonZeroUsize) -> Self {
        Self {
            inner: Arc::new(Mutex::new(LruCache::new(capacity))),
        }
    }
}

impl PkarrCache for InMemoryPkarrCache {
    fn len(&self) -> usize {
        self.inner.lock().unwrap().len()
    }

    /// Puts [SignedPacket], if a version of the  packet already exists,
    /// and it has the same [SignedPacket::as_bytes], then only [SignedPacket::last_seen] will be
    /// updated, otherwise the input will be cloned.
    fn put(&self, target: &Id, signed_packet: &SignedPacket) {
        let mut lock = self.inner.lock().unwrap();

        match lock.get_mut(target) {
            Some(existing) => {
                if existing.as_bytes() == signed_packet.as_bytes() {
                    // just refresh the last_seen
                    existing.set_last_seen(signed_packet.last_seen())
                } else {
                    lock.put(*target, signed_packet.clone());
                }
            }
            None => {
                lock.put(*target, signed_packet.clone());
            }
        }
    }

    fn get(&self, key: &PkarrCacheKey) -> Option<SignedPacket> {
        self.inner.lock().unwrap().get(key).cloned()
    }
}