using_queue/
lib.rs

1// Copyright 2015-2020 Parity Technologies (UK) Ltd.
2// This file is part of Tetsy Vapory.
3
4// Tetsy Vapory is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Tetsy Vapory is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Tetsy Vapory.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Queue-like data structure including notion of usage.
18
19/// Special queue-like data structure that includes the notion of
20/// usage to avoid items that were queued but never used from making it into
21/// the queue.
22pub struct UsingQueue<T> {
23	/// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this.
24	pending: Option<T>,
25	/// Currently being sealed by miners.
26	in_use: Vec<T>,
27	/// The maximum allowable number of items in_use.
28	max_size: usize,
29}
30
31/// Take an item or just clone it?
32pub enum GetAction {
33	/// Remove the item, faster but you can't get it back.
34	Take,
35	/// Clone the item, slower but you can get it again.
36	Clone,
37}
38
39impl<T> UsingQueue<T> {
40	/// Create a new struct with a maximum size of `max_size`.
41	pub fn new(max_size: usize) -> UsingQueue<T> {
42		UsingQueue {
43			pending: None,
44			in_use: vec![],
45			max_size,
46		}
47	}
48
49	/// Return a reference to the item at the top of the queue (or `None` if the queue is empty);
50	/// it doesn't constitute noting that the item is used.
51	pub fn peek_last_ref(&self) -> Option<&T> {
52		self.pending.as_ref().or(self.in_use.last())
53	}
54
55	/// Return a reference to the item at the top of the queue (or `None` if the queue is empty);
56	/// this constitutes using the item and will remain in the queue for at least another
57	/// `max_size` invocations of `set_pending() + use_last_ref()`.
58	pub fn use_last_ref(&mut self) -> Option<&T> {
59		if let Some(x) = self.pending.take() {
60			self.in_use.push(x);
61			if self.in_use.len() > self.max_size {
62				self.in_use.remove(0);
63			}
64		}
65		self.in_use.last()
66	}
67
68	/// Place an item on the end of the queue. The previously pending item will be removed
69	/// if `use_last_ref()` since it was set.
70	pub fn set_pending(&mut self, b: T) {
71		self.pending = Some(b);
72	}
73
74	/// Is there anything in the queue currently?
75	pub fn is_in_use(&self) -> bool { self.in_use.len() > 0 }
76
77	/// Clears everything; the queue is entirely reset.
78	pub fn reset(&mut self) {
79		self.pending = None;
80		self.in_use.clear();
81	}
82
83	/// Returns `Some` item which is the first that `f` returns `true` with a reference to it
84	/// as a parameter or `None` if no such item exists in the queue.
85	fn take_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
86		self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i))
87	}
88
89	/// Returns `Some` item which is the first that `f` returns `true` with a reference to it
90	/// as a parameter or `None` if no such item exists in the queue.
91	fn clone_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool, T: Clone {
92		self.in_use.iter().find(|r| predicate(r)).cloned()
93	}
94
95	/// Fork-function for `take_used_if` and `clone_used_if`.
96	pub fn get_used_if<P>(&mut self, action: GetAction, predicate: P) -> Option<T> where P: Fn(&T) -> bool, T: Clone {
97		match action {
98			GetAction::Take => self.take_used_if(predicate),
99			GetAction::Clone => self.clone_used_if(predicate),
100		}
101	}
102
103	/// Returns a clone of the pending block if `f` returns `true` with a reference to it as
104	/// a parameter, otherwise `None`.
105	///
106	/// If pending block is not available will clone the first of the used blocks that match the predicate.
107	pub fn get_pending_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool, T: Clone {
108		// a bit clumsy - TODO: think about a nicer way of expressing this.
109		if let Some(ref x) = self.pending {
110			if predicate(x) {
111				Some(x.clone())
112			} else {
113				None
114			}
115		} else {
116			self.in_use.last().into_iter().filter(|x| predicate(x)).next().cloned()
117		}
118	}
119}
120
121#[test]
122fn should_not_find_when_pushed() {
123	let mut q = UsingQueue::new(2);
124	q.set_pending(1);
125	assert!(q.take_used_if(|i| i == &1).is_none());
126}
127
128#[test]
129fn should_not_find_when_pushed_with_clone() {
130	let mut q = UsingQueue::new(2);
131	q.set_pending(1);
132	assert!(q.clone_used_if(|i| i == &1).is_none());
133}
134
135#[test]
136fn should_find_when_pushed_and_used() {
137	let mut q = UsingQueue::new(2);
138	q.set_pending(1);
139	q.use_last_ref();
140	assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
141}
142
143#[test]
144fn should_have_same_semantics_for_get_take_clone() {
145	let mut q = UsingQueue::new(2);
146	q.set_pending(1);
147	assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
148	assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
149	q.use_last_ref();
150	assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
151	assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
152	assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1);
153	assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
154	assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
155}
156
157#[test]
158fn should_find_when_pushed_and_used_with_clone() {
159	let mut q = UsingQueue::new(2);
160	q.set_pending(1);
161	q.use_last_ref();
162	assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
163}
164
165#[test]
166fn should_not_find_again_when_pushed_and_taken() {
167	let mut q = UsingQueue::new(2);
168	q.set_pending(1);
169	q.use_last_ref();
170	assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
171	assert!(q.clone_used_if(|i| i == &1).is_none());
172}
173
174#[test]
175fn should_find_again_when_pushed_and_cloned() {
176	let mut q = UsingQueue::new(2);
177	q.set_pending(1);
178	q.use_last_ref();
179	assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
180	assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
181	assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
182}
183
184#[test]
185fn should_find_when_others_used() {
186	let mut q = UsingQueue::new(2);
187	q.set_pending(1);
188	q.use_last_ref();
189	q.set_pending(2);
190	q.use_last_ref();
191	assert!(q.take_used_if(|i| i == &1).is_some());
192}
193
194#[test]
195fn should_not_find_when_too_many_used() {
196	let mut q = UsingQueue::new(1);
197	q.set_pending(1);
198	q.use_last_ref();
199	q.set_pending(2);
200	q.use_last_ref();
201	assert!(q.take_used_if(|i| i == &1).is_none());
202}
203
204#[test]
205fn should_not_find_when_not_used_and_then_pushed() {
206	let mut q = UsingQueue::new(3);
207	q.set_pending(1);
208	q.set_pending(2);
209	q.use_last_ref();
210	assert!(q.take_used_if(|i| i == &1).is_none());
211}
212
213#[test]
214fn should_peek_correctly_after_push() {
215	let mut q = UsingQueue::new(3);
216	q.set_pending(1);
217	assert_eq!(q.peek_last_ref(), Some(&1));
218	q.set_pending(2);
219	assert_eq!(q.peek_last_ref(), Some(&2));
220}
221
222#[test]
223fn should_inspect_correctly() {
224	let mut q = UsingQueue::new(3);
225	q.set_pending(1);
226	assert_eq!(q.use_last_ref(), Some(&1));
227	assert_eq!(q.peek_last_ref(), Some(&1));
228	q.set_pending(2);
229	assert_eq!(q.use_last_ref(), Some(&2));
230	assert_eq!(q.peek_last_ref(), Some(&2));
231}
232
233#[test]
234fn should_not_find_when_not_used_peeked_and_then_pushed() {
235	let mut q = UsingQueue::new(3);
236	q.set_pending(1);
237	q.peek_last_ref();
238	q.set_pending(2);
239	q.use_last_ref();
240	assert!(q.take_used_if(|i| i == &1).is_none());
241}
242
243#[test]
244fn should_pop_used() {
245	let mut q = UsingQueue::new(3);
246	q.set_pending(1);
247	q.use_last_ref();
248	let popped = q.get_pending_if(|i| i == &1);
249	assert_eq!(popped, Some(1));
250}
251
252#[test]
253fn should_not_pop_last_pending() {
254	let mut q = UsingQueue::new(3);
255	q.set_pending(1);
256	assert_eq!(q.get_pending_if(|i| i == &1), Some(1));
257	assert_eq!(q.get_pending_if(|i| i == &1), Some(1));
258}
259
260#[test]
261fn should_not_pop_unused_before_used() {
262	let mut q = UsingQueue::new(3);
263	q.set_pending(1);
264	q.set_pending(2);
265	let popped = q.get_pending_if(|i| i == &1);
266	assert_eq!(popped, None);
267}
268
269#[test]
270fn should_not_remove_used_popped() {
271	let mut q = UsingQueue::new(3);
272	q.set_pending(1);
273	q.use_last_ref();
274	assert_eq!(q.get_pending_if(|i| i == &1), Some(1));
275	assert_eq!(q.get_pending_if(|i| i == &1), Some(1));
276}