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 85
// This file is part of Substrate. // Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. use futures::{stream::unfold, FutureExt, Stream, StreamExt}; use futures_timer::Delay; use linked_hash_set::LinkedHashSet; use std::{hash::Hash, num::NonZeroUsize, time::Duration}; /// Creates a stream that returns a new value every `duration`. pub fn interval(duration: Duration) -> impl Stream<Item = ()> + Unpin { unfold((), move |_| Delay::new(duration).map(|_| Some(((), ())))).map(drop) } /// Wrapper around `LinkedHashSet` with bounded growth. /// /// In the limit, for each element inserted the oldest existing element will be removed. #[derive(Debug, Clone)] pub struct LruHashSet<T: Hash + Eq> { set: LinkedHashSet<T>, limit: NonZeroUsize, } impl<T: Hash + Eq> LruHashSet<T> { /// Create a new `LruHashSet` with the given (exclusive) limit. pub fn new(limit: NonZeroUsize) -> Self { Self { set: LinkedHashSet::new(), limit } } /// Insert element into the set. /// /// Returns `true` if this is a new element to the set, `false` otherwise. /// Maintains the limit of the set by removing the oldest entry if necessary. /// Inserting the same element will update its LRU position. pub fn insert(&mut self, e: T) -> bool { if self.set.insert(e) { if self.set.len() == usize::from(self.limit) { self.set.pop_front(); // remove oldest entry } return true } false } } #[cfg(test)] mod tests { use super::*; #[test] fn maintains_limit() { let three = NonZeroUsize::new(3).unwrap(); let mut set = LruHashSet::<u8>::new(three); // First element. assert!(set.insert(1)); assert_eq!(vec![&1], set.set.iter().collect::<Vec<_>>()); // Second element. assert!(set.insert(2)); assert_eq!(vec![&1, &2], set.set.iter().collect::<Vec<_>>()); // Inserting the same element updates its LRU position. assert!(!set.insert(1)); assert_eq!(vec![&2, &1], set.set.iter().collect::<Vec<_>>()); // We reached the limit. The next element forces the oldest one out. assert!(set.insert(3)); assert_eq!(vec![&1, &3], set.set.iter().collect::<Vec<_>>()); } }