From 1700b6a087803467918520a7f5ffe2a6f128735f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 24 Mar 2016 07:49:54 +0000 Subject: [PATCH] Add UsingQueue. --- miner/src/miner.rs | 1 - util/src/lib.rs | 2 + util/src/using_queue.rs | 207 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 util/src/using_queue.rs diff --git a/miner/src/miner.rs b/miner/src/miner.rs index d4f6c8a00..508fcffb4 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -39,7 +39,6 @@ pub struct Miner { gas_floor_target: RwLock, author: RwLock
, extra_data: RwLock, - } impl Default for Miner { diff --git a/util/src/lib.rs b/util/src/lib.rs index 6abf6485d..dfdb300f6 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -124,6 +124,7 @@ pub mod hash; pub mod bytes; pub mod rlp; pub mod misc; +pub mod using_queue; mod json_aid; pub mod vector; pub mod sha3; @@ -149,6 +150,7 @@ pub mod table; pub use common::*; pub use misc::*; +pub use using_queue::*; pub use json_aid::*; pub use rlp::*; pub use hashdb::*; diff --git a/util/src/using_queue.rs b/util/src/using_queue.rs new file mode 100644 index 000000000..0371d3efe --- /dev/null +++ b/util/src/using_queue.rs @@ -0,0 +1,207 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . +//! Queue-like datastructure including notion of usage. + +/// Special queue-like datastructure that includes the notion of +/// usage to avoid items that were queued but never used from making it into +/// the queue. +pub struct UsingQueue where T: Clone { + /// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this. + pending: Option, + /// Currently being sealed by miners. + in_use: Vec, + /// The maximum allowable number of items in_use. + max_size: usize, +} + +impl UsingQueue where T: Clone { + /// Create a new struct with a maximum size of `max_size`. + pub fn new(max_size: usize) -> UsingQueue { + UsingQueue { + pending: None, + in_use: vec![], + max_size: max_size, + } + } + + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); + /// it doesn't constitute noting that the item is used. + pub fn peek_last_ref(&self) -> Option<&T> { + self.pending.as_ref().or(self.in_use.last()) + } + + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); + /// this constitutes using the item and will remain in the queue for at least another + /// `max_size` invocations of `push()`. + pub fn use_last_ref(&mut self) -> Option<&T> { + if let Some(x) = self.pending.take() { + self.in_use.push(x); + if self.in_use.len() > self.max_size { + self.in_use.remove(0); + } + } + self.in_use.last() + } + + /// Place an item on the end of the queue. The previously `push()`ed item will be removed + /// if `use_last_ref()` since it was `push()`ed. + pub fn push(&mut self, b: T) { + self.pending = Some(b); + } + + /// Clears everything; the queue is entirely reset. + pub fn reset(&mut self) { + self.pending = None; + self.in_use.clear(); + } + + /// Returns `Some` reference to first block that `f` returns `true` with it as a parameter + /// or `None` if no such block exists in the queue. + pub fn find_if

(&self, predicate: P) -> Option<&T> where P: Fn(&T) -> bool { + if self.pending.as_ref().map(|r| predicate(r)).unwrap_or(false) { + self.pending.as_ref() + } else { + self.in_use.iter().find(|r| predicate(r)) + } + } + + /// Returns the most recently pushed block if `f` returns `true` with a reference to it as + /// a parameter, otherwise `None`. + /// Will not destroy a block if a reference to it has previously been returned by `use_last_ref`, + /// but rather clone it. + pub fn pop_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { + // a bit clumsy - TODO: think about a nicer way of expressing this. + if let Some(x) = self.pending.take() { + if predicate(&x) { + Some(x) + } else { + self.pending = Some(x); + None + } + } else { + self.in_use.last().into_iter().filter(|x| predicate(x)).next().cloned() + } + } +} + +#[test] +fn should_find_when_pushed() { + let mut q = UsingQueue::new(2); + q.push(1); + assert!(q.find_if(|i| i == &1).is_some()); +} + +#[test] +fn should_find_when_pushed_and_used() { + let mut q = UsingQueue::new(2); + q.push(1); + q.use_last_ref(); + assert!(q.find_if(|i| i == &1).is_some()); +} + +#[test] +fn should_find_when_others_used() { + let mut q = UsingQueue::new(2); + q.push(1); + q.use_last_ref(); + q.push(2); + q.use_last_ref(); + assert!(q.find_if(|i| i == &1).is_some()); +} + +#[test] +fn should_not_find_when_too_many_used() { + let mut q = UsingQueue::new(1); + q.push(1); + q.use_last_ref(); + q.push(2); + q.use_last_ref(); + assert!(q.find_if(|i| i == &1).is_none()); +} + +#[test] +fn should_not_find_when_not_used_and_then_pushed() { + let mut q = UsingQueue::new(3); + q.push(1); + q.push(2); + q.use_last_ref(); + assert!(q.find_if(|i| i == &1).is_none()); +} + +#[test] +fn should_peek_correctly_after_push() { + let mut q = UsingQueue::new(3); + q.push(1); + assert_eq!(q.peek_last_ref(), Some(&1)); + q.push(2); + assert_eq!(q.peek_last_ref(), Some(&2)); +} + +#[test] +fn should_inspect_correctly() { + let mut q = UsingQueue::new(3); + q.push(1); + assert_eq!(q.use_last_ref(), Some(&1)); + assert_eq!(q.peek_last_ref(), Some(&1)); + q.push(2); + assert_eq!(q.use_last_ref(), Some(&2)); + assert_eq!(q.peek_last_ref(), Some(&2)); +} + +#[test] +fn should_not_find_when_not_used_peeked_and_then_pushed() { + let mut q = UsingQueue::new(3); + q.push(1); + q.peek_last_ref(); + q.push(2); + q.use_last_ref(); + assert!(q.find_if(|i| i == &1).is_none()); +} + +#[test] +fn should_pop_used() { + let mut q = UsingQueue::new(3); + q.push(1); + q.use_last_ref(); + let popped = q.pop_if(|i| i == &1); + assert_eq!(popped, Some(1)); +} + +#[test] +fn should_pop_unused() { + let mut q = UsingQueue::new(3); + q.push(1); + assert_eq!(q.pop_if(|i| i == &1), Some(1)); + assert_eq!(q.pop_if(|i| i == &1), None); +} + +#[test] +fn should_not_pop_unused_before_used() { + let mut q = UsingQueue::new(3); + q.push(1); + q.push(2); + let popped = q.pop_if(|i| i == &1); + assert_eq!(popped, None); +} + +#[test] +fn should_not_remove_used_popped() { + let mut q = UsingQueue::new(3); + q.push(1); + q.use_last_ref(); + assert_eq!(q.pop_if(|i| i == &1), Some(1)); + assert_eq!(q.pop_if(|i| i == &1), Some(1)); +}