serde is no longer util dependency (#1534)

* removed old json-tests

* simplify folds in triehash.rs

* removed unused json_aid

* removed unused squeeze.rs

* json branching tests for trie

* removing todos from util

* separated UsingQueue and Table

* further cleanup, removing unused code

* serde serialization of hash moved to rpc module

* uint wrapper for rpc in progress

* serialization of uint moved to rpc module

* updated eth-secp256k1

* updated igd, serde is no longer dependency of util

* loading trie consensus tests

* renamed aliases in rpc imports
This commit is contained in:
Marek Kotewicz
2016-07-06 11:23:29 +02:00
committed by Gav Wood
parent cb1808d53d
commit bcb63bce12
55 changed files with 854 additions and 717 deletions

View File

@@ -24,7 +24,7 @@
//! use util::bytes::BytesConvertable;
//!
//! let arr = [0; 5];
//! let slice: &[u8] = arr.bytes();
//! let slice: &[u8] = arr.as_slice();
//! }
//!
//! fn main() {
@@ -120,12 +120,12 @@ impl<'a> ToPretty for &'a [u8] {
impl<'a> ToPretty for &'a Bytes {
fn pretty(&self) -> PrettySlice {
PrettySlice(self.bytes())
PrettySlice(self.as_slice())
}
}
impl ToPretty for Bytes {
fn pretty(&self) -> PrettySlice {
PrettySlice(self.bytes())
PrettySlice(self.as_slice())
}
}
@@ -162,23 +162,19 @@ pub type Bytes = Vec<u8>;
/// Slice of bytes to underlying memory
pub trait BytesConvertable {
// TODO: rename to as_slice
/// Get the underlying byte-wise representation of the value.
/// Deprecated - use `as_slice` instead.
fn bytes(&self) -> &[u8];
/// Get the underlying byte-wise representation of the value.
fn as_slice(&self) -> &[u8] { self.bytes() }
fn as_slice(&self) -> &[u8];
/// Get a copy of the underlying byte-wise representation.
fn to_bytes(&self) -> Bytes { self.as_slice().to_vec() }
}
impl<T> BytesConvertable for T where T: AsRef<[u8]> {
fn bytes(&self) -> &[u8] { self.as_ref() }
fn as_slice(&self) -> &[u8] { self.as_ref() }
}
#[test]
fn bytes_convertable() {
assert_eq!(vec![0x12u8, 0x34].bytes(), &[0x12u8, 0x34]);
assert_eq!(vec![0x12u8, 0x34].as_slice(), &[0x12u8, 0x34]);
assert!([0u8; 0].as_slice().is_empty());
}

View File

@@ -16,16 +16,18 @@
//! General hash types, a fixed-size raw-data type used as the output of hash functions.
use standard::*;
use rustc_serialize::hex::FromHex;
use std::{ops, fmt, cmp};
use std::cmp::*;
use std::ops::*;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use math::log2;
use error::UtilError;
use rand::Rng;
use rand::os::OsRng;
use bytes::{BytesConvertable,Populatable};
use from_json::*;
use bigint::uint::{Uint, U256};
use rustc_serialize::hex::ToHex;
use serde;
/// Trait for a fixed-size byte array to be used as the output of hash functions.
///
@@ -228,55 +230,6 @@ macro_rules! impl_hash {
}
}
impl serde::Serialize for $from {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: serde::Serializer {
let mut hex = "0x".to_owned();
hex.push_str(self.to_hex().as_ref());
serializer.serialize_str(hex.as_ref())
}
}
impl serde::Deserialize for $from {
fn deserialize<D>(deserializer: &mut D) -> Result<$from, D::Error>
where D: serde::Deserializer {
struct HashVisitor;
impl serde::de::Visitor for HashVisitor {
type Value = $from;
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: serde::Error {
// 0x + len
if value.len() != 2 + $size * 2 {
return Err(serde::Error::custom("Invalid length."));
}
value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::custom("Invalid hex value."))
}
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: serde::Error {
self.visit_str(value.as_ref())
}
}
deserializer.deserialize(HashVisitor)
}
}
impl FromJson for $from {
fn from_json(json: &Json) -> Self {
match *json {
Json::String(ref s) => {
match s.len() % 2 {
0 => FromStr::from_str(clean_0x(s)).unwrap(),
_ => FromStr::from_str(&("0".to_owned() + &(clean_0x(s).to_owned()))[..]).unwrap()
}
},
_ => Default::default(),
}
}
}
impl fmt::Debug for $from {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for i in &self.0[..] {
@@ -285,6 +238,7 @@ macro_rules! impl_hash {
Ok(())
}
}
impl fmt::Display for $from {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for i in &self.0[0..2] {
@@ -506,13 +460,13 @@ impl<'a> From<&'a U256> for H256 {
impl From<H256> for U256 {
fn from(value: H256) -> U256 {
U256::from(value.bytes())
U256::from(value.as_slice())
}
}
impl<'a> From<&'a H256> for U256 {
fn from(value: &'a H256) -> U256 {
U256::from(value.bytes())
U256::from(value.as_slice())
}
}
@@ -532,17 +486,6 @@ impl From<H256> for H64 {
}
}
/*
impl<'a> From<&'a H256> for Address {
fn from(value: &'a H256) -> Address {
let mut ret = Address::new();
ret.0.copy_from_slice(&value[12..32]);
ret
}
}
}
*/
impl From<Address> for H256 {
fn from(value: Address) -> H256 {
let mut ret = H256::new();
@@ -596,11 +539,6 @@ impl_hash!(H520, 65);
impl_hash!(H1024, 128);
impl_hash!(H2048, 256);
/// Constant address for point 0. Often used as a default.
pub static ZERO_ADDRESS: Address = Address([0x00; 20]);
/// Constant 256-bit datum for 0. Often used as a default.
pub static ZERO_H256: H256 = H256([0x00; 32]);
#[cfg(test)]
mod tests {
use hash::*;

View File

@@ -79,7 +79,7 @@ impl ArchiveDB {
}
fn payload(&self, key: &H256) -> Option<Bytes> {
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
}
}
@@ -177,7 +177,7 @@ impl JournalDB for ArchiveDB {
let (key, (value, rc)) = i;
if rc > 0 {
assert!(rc == 1);
batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?");
batch.put(&key, &value).expect("Low-level database error. Some issue with your hard disk?");
inserts += 1;
}
if rc < 0 {
@@ -202,7 +202,7 @@ impl JournalDB for ArchiveDB {
fn latest_era(&self) -> Option<u64> { self.latest_era }
fn state(&self, id: &H256) -> Option<Bytes> {
self.backing.get_by_prefix(&id.bytes()[0..12]).and_then(|b| Some(b.to_vec()))
self.backing.get_by_prefix(&id[0..12]).and_then(|b| Some(b.to_vec()))
}
fn is_pruned(&self) -> bool { false }

View File

@@ -106,7 +106,7 @@ impl EarlyMergeDB {
}
fn morph_key(key: &H256, index: u8) -> Bytes {
let mut ret = key.bytes().to_owned();
let mut ret = key.to_bytes();
ret.push(index);
ret
}
@@ -130,7 +130,7 @@ impl EarlyMergeDB {
}
// this is the first entry for this node in the journal.
if backing.get(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?").is_some() {
if backing.get(h).expect("Low-level database error. Some issue with your hard disk?").is_some() {
// already in the backing DB. start counting, and remember it was already in.
Self::set_already_in(batch, &h);
refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: true});
@@ -143,7 +143,7 @@ impl EarlyMergeDB {
// Gets removed when a key leaves the journal, so should never be set when we're placing a new key.
//Self::reset_already_in(&h);
assert!(!Self::is_already_in(backing, &h));
batch.put(&h.bytes(), d).expect("Low-level database error. Some issue with your hard disk?");
batch.put(h, d).expect("Low-level database error. Some issue with your hard disk?");
refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: false});
if trace {
trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h);
@@ -204,7 +204,7 @@ impl EarlyMergeDB {
}
Some(RefInfo{queue_refs: 1, in_archive: false}) => {
refs.remove(h);
batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?");
batch.delete(h).expect("Low-level database error. Some issue with your hard disk?");
if trace {
trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h);
}
@@ -212,7 +212,7 @@ impl EarlyMergeDB {
None => {
// Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs.
//assert!(!Self::is_already_in(db, &h));
batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?");
batch.delete(h).expect("Low-level database error. Some issue with your hard disk?");
if trace {
trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h);
}
@@ -237,7 +237,7 @@ impl EarlyMergeDB {
}
fn payload(&self, key: &H256) -> Option<Bytes> {
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
}
fn read_refs(db: &Database) -> (Option<u64>, HashMap<H256, RefInfo>) {

View File

@@ -141,7 +141,7 @@ impl OverlayRecentDB {
}
fn payload(&self, key: &H256) -> Option<Bytes> {
self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
}
fn read_overlay(db: &Database) -> JournalOverlay {

View File

@@ -108,7 +108,6 @@ extern crate secp256k1;
extern crate arrayvec;
extern crate elastic_array;
extern crate crossbeam;
extern crate serde;
#[macro_use]
extern crate log as rlog;
extern crate igd;
@@ -117,6 +116,8 @@ extern crate libc;
extern crate target_info;
extern crate bigint;
extern crate chrono;
pub extern crate using_queue;
pub extern crate table;
extern crate ansi_term;
pub mod standard;
@@ -130,7 +131,6 @@ pub mod hash;
pub mod bytes;
pub mod rlp;
pub mod misc;
pub mod using_queue;
pub mod vector;
pub mod sha3;
pub mod hashdb;
@@ -151,14 +151,12 @@ pub mod io;
pub mod network;
pub mod log;
pub mod panics;
pub mod table;
pub mod network_settings;
pub mod path;
mod timer;
pub use common::*;
pub use misc::*;
pub use using_queue::*;
pub use rlp::*;
pub use hashdb::*;
pub use memorydb::*;

View File

@@ -198,14 +198,14 @@ impl HashDB for MemoryDB {
let key = value.sha3();
if match self.data.get_mut(&key) {
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => {
*old_value = From::from(value.bytes());
*old_value = From::from(value);
*rc += 1;
false
},
Some(&mut (_, ref mut x)) => { *x += 1; false } ,
None => true,
}{ // ... None falls through into...
self.data.insert(key.clone(), (From::from(value.bytes()), 1));
self.data.insert(key.clone(), (From::from(value), 1));
}
key
}
@@ -262,8 +262,8 @@ fn memorydb_denote() {
for _ in 0..1000 {
let r = H256::random();
let k = r.sha3();
let &(ref v, ref rc) = m.denote(&k, r.bytes().to_vec());
assert_eq!(v, &r.bytes());
let &(ref v, ref rc) = m.denote(&k, r.to_bytes());
assert_eq!(v.as_slice(), r.as_slice());
assert_eq!(*rc, 0);
}

View File

@@ -172,10 +172,10 @@ impl OverlayDB {
/// Get the refs and value of the given key.
fn payload(&self, key: &H256) -> Option<(Bytes, u32)> {
self.backing.get(&key.bytes())
self.backing.get(key)
.expect("Low-level database error. Some issue with your hard disk?")
.map(|d| {
let r = Rlp::new(d.deref());
let r = Rlp::new(&d);
(r.at(1).as_val(), r.at(0).as_val())
})
}
@@ -186,10 +186,10 @@ impl OverlayDB {
let mut s = RlpStream::new_list(2);
s.append(&payload.1);
s.append(&payload.0);
batch.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?");
batch.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?");
false
} else {
batch.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?");
batch.delete(key).expect("Low-level database error. Some issue with your hard disk?");
true
}
}
@@ -200,10 +200,10 @@ impl OverlayDB {
let mut s = RlpStream::new_list(2);
s.append(&payload.1);
s.append(&payload.0);
self.backing.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?");
self.backing.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?");
false
} else {
self.backing.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?");
self.backing.delete(key).expect("Low-level database error. Some issue with your hard disk?");
true
}
}

View File

@@ -148,9 +148,9 @@ impl_uint_to_bytes!(U128);
impl <T>ToBytes for T where T: FixedHash {
fn to_bytes<V: VecLike<u8>>(&self, out: &mut V) {
out.vec_extend(self.bytes());
out.vec_extend(self.as_slice());
}
fn to_bytes_len(&self) -> usize { self.bytes().len() }
fn to_bytes_len(&self) -> usize { self.as_slice().len() }
}
/// Error returned when `FromBytes` conversation goes wrong

View File

@@ -58,7 +58,7 @@ impl<T> Hashable for T where T: BytesConvertable {
}
fn sha3_into(&self, dest: &mut [u8]) {
unsafe {
let input: &[u8] = self.bytes();
let input: &[u8] = self.as_slice();
sha3_256(dest.as_mut_ptr(), dest.len(), input.as_ptr(), input.len());
}
}

View File

@@ -1,265 +0,0 @@
// 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 <http://www.gnu.org/licenses/>.
//! A collection associating pair of keys (row and column) with a single value.
use std::default::Default;
use std::hash::Hash;
use std::collections::HashMap;
/// Structure to hold double-indexed values
///
/// You can obviously use `HashMap<(Row,Col), Val>`, but this structure gives
/// you better access to all `Columns` in Specific `Row`. Namely you can get sub-hashmap
/// `HashMap<Col, Val>` for specific `Row`
pub struct Table<Row, Col, Val>
where Row: Eq + Hash + Clone,
Col: Eq + Hash {
map: HashMap<Row, HashMap<Col, Val>>,
}
impl<Row, Col, Val> Default for Table<Row, Col, Val>
where Row: Eq + Hash + Clone,
Col: Eq + Hash {
fn default() -> Self {
Table::new()
}
}
// There is default but clippy does not detect it?
#[cfg_attr(feature="dev", allow(new_without_default))]
impl<Row, Col, Val> Table<Row, Col, Val>
where Row: Eq + Hash + Clone,
Col: Eq + Hash {
/// Creates new Table
pub fn new() -> Self {
Table {
map: HashMap::new(),
}
}
/// Removes all elements from this Table
pub fn clear(&mut self) {
self.map.clear();
}
/// Returns length of the Table (number of (row, col, val) tuples)
pub fn len(&self) -> usize {
self.map.values().fold(0, |acc, v| acc + v.len())
}
/// Check if there is any element in this Table
pub fn is_empty(&self) -> bool {
self.map.is_empty() || self.map.values().all(|v| v.is_empty())
}
/// Get mutable reference for single Table row.
pub fn row_mut(&mut self, row: &Row) -> Option<&mut HashMap<Col, Val>> {
self.map.get_mut(row)
}
/// Checks if row is defined for that table (note that even if defined it might be empty)
pub fn has_row(&self, row: &Row) -> bool {
self.map.contains_key(row)
}
/// Get immutable reference for single row in this Table
pub fn row(&self, row: &Row) -> Option<&HashMap<Col, Val>> {
self.map.get(row)
}
/// Get element in cell described by `(row, col)`
pub fn get(&self, row: &Row, col: &Col) -> Option<&Val> {
self.map.get(row).and_then(|r| r.get(col))
}
/// Remove value from specific cell
///
/// It will remove the row if it's the last value in it
pub fn remove(&mut self, row: &Row, col: &Col) -> Option<Val> {
let (val, is_empty) = {
let row_map = self.map.get_mut(row);
if let None = row_map {
return None;
}
let mut row_map = row_map.unwrap();
let val = row_map.remove(col);
(val, row_map.is_empty())
};
// Clean row
if is_empty {
self.map.remove(row);
}
val
}
/// Remove given row from Table if there are no values defined in it
///
/// When using `#row_mut` it may happen that all values from some row are drained.
/// Table however will not be aware that row is empty.
/// You can use this method to explicitly remove row entry from the Table.
pub fn clear_if_empty(&mut self, row: &Row) {
let is_empty = self.map.get(row).map_or(false, |m| m.is_empty());
if is_empty {
self.map.remove(row);
}
}
/// Inserts new value to specified cell
///
/// Returns previous value (if any)
pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option<Val> {
self.map.entry(row).or_insert_with(HashMap::new).insert(col, val)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn should_create_empty_table() {
// when
let table : Table<usize, usize, bool> = Table::new();
// then
assert!(table.is_empty());
assert_eq!(table.len(), 0);
}
#[test]
fn should_insert_elements_and_return_previous_if_any() {
// given
let mut table = Table::new();
// when
let r1 = table.insert(5, 4, true);
let r2 = table.insert(10, 4, true);
let r3 = table.insert(10, 10, true);
let r4 = table.insert(10, 10, false);
// then
assert!(r1.is_none());
assert!(r2.is_none());
assert!(r3.is_none());
assert!(r4.is_some());
assert!(!table.is_empty());
assert_eq!(r4.unwrap(), true);
assert_eq!(table.len(), 3);
}
#[test]
fn should_remove_element() {
// given
let mut table = Table::new();
table.insert(5, 4, true);
assert!(!table.is_empty());
assert_eq!(table.len(), 1);
// when
let r = table.remove(&5, &4);
// then
assert!(table.is_empty());
assert_eq!(table.len() ,0);
assert_eq!(r.unwrap(), true);
}
#[test]
fn should_return_none_if_trying_to_remove_non_existing_element() {
// given
let mut table : Table<usize, usize, usize> = Table::new();
assert!(table.is_empty());
// when
let r = table.remove(&5, &4);
// then
assert!(r.is_none());
}
#[test]
fn should_clear_row_if_removing_last_element() {
// given
let mut table = Table::new();
table.insert(5, 4, true);
assert!(table.has_row(&5));
// when
let r = table.remove(&5, &4);
// then
assert!(r.is_some());
assert!(!table.has_row(&5));
}
#[test]
fn should_return_element_given_row_and_col() {
// given
let mut table = Table::new();
table.insert(1551, 1234, 123);
// when
let r1 = table.get(&1551, &1234);
let r2 = table.get(&5, &4);
// then
assert!(r1.is_some());
assert!(r2.is_none());
assert_eq!(r1.unwrap(), &123);
}
#[test]
fn should_clear_table() {
// given
let mut table = Table::new();
table.insert(1, 1, true);
table.insert(1, 2, false);
table.insert(2, 2, false);
assert_eq!(table.len(), 3);
// when
table.clear();
// then
assert!(table.is_empty());
assert_eq!(table.len(), 0);
assert_eq!(table.has_row(&1), false);
assert_eq!(table.has_row(&2), false);
}
#[test]
fn should_return_mutable_row() {
// given
let mut table = Table::new();
table.insert(1, 1, true);
table.insert(1, 2, false);
table.insert(2, 2, false);
// when
{
let mut row = table.row_mut(&1).unwrap();
row.remove(&1);
row.remove(&2);
}
assert!(table.has_row(&1));
table.clear_if_empty(&1);
// then
assert!(!table.has_row(&1));
assert_eq!(table.len(), 1);
}
}

View File

@@ -64,16 +64,16 @@ impl StandardMap {
fn random_bytes(min_count: usize, journal_count: usize, seed: &mut H256) -> Vec<u8> {
assert!(min_count + journal_count <= 32);
*seed = seed.sha3();
let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1));
seed.bytes()[0..r].to_vec()
let r = min_count + (seed[31] as usize % (journal_count + 1));
seed[0..r].to_vec()
}
/// Get a random value. Equal chance of being 1 byte as of 32. `seed` is mutated pseudoramdonly and used.
fn random_value(seed: &mut H256) -> Bytes {
*seed = seed.sha3();
match seed.bytes()[0] % 2 {
1 => vec![seed.bytes()[31];1],
_ => seed.bytes().to_vec(),
match seed[0] % 2 {
1 => vec![seed[31];1],
_ => seed.to_vec(),
}
}
@@ -82,10 +82,10 @@ impl StandardMap {
fn random_word(alphabet: &[u8], min_count: usize, journal_count: usize, seed: &mut H256) -> Vec<u8> {
assert!(min_count + journal_count <= 32);
*seed = seed.sha3();
let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1));
let r = min_count + (seed[31] as usize % (journal_count + 1));
let mut ret: Vec<u8> = Vec::with_capacity(r);
for i in 0..r {
ret.push(alphabet[seed.bytes()[i] as usize % alphabet.len()]);
ret.push(alphabet[seed[i] as usize % alphabet.len()]);
}
ret
}

View File

@@ -1,273 +0,0 @@
// 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 <http://www.gnu.org/licenses/>.
//! 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<T> where T: Clone {
/// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this.
pending: Option<T>,
/// Currently being sealed by miners.
in_use: Vec<T>,
/// The maximum allowable number of items in_use.
max_size: usize,
}
/// Take an item or just clone it?
pub enum GetAction {
/// Remove the item, faster but you can't get it back.
Take,
/// Clone the item, slower but you can get it again.
Clone,
}
impl<T> UsingQueue<T> where T: Clone {
/// Create a new struct with a maximum size of `max_size`.
pub fn new(max_size: usize) -> UsingQueue<T> {
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` item which is the first that `f` returns `true` with a reference to it
/// as a parameter or `None` if no such item exists in the queue.
pub fn take_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i))
}
/// Returns `Some` item which is the first that `f` returns `true` with a reference to it
/// as a parameter or `None` if no such item exists in the queue.
pub fn clone_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
self.in_use.iter().find(|r| predicate(r)).cloned()
}
/// Fork-function for `take_used_if` and `clone_used_if`.
pub fn get_used_if<P>(&mut self, action: GetAction, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
match action {
GetAction::Take => self.take_used_if(predicate),
GetAction::Clone => self.clone_used_if(predicate),
}
}
/// 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<P>(&mut self, predicate: P) -> Option<T> 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_not_find_when_pushed() {
let mut q = UsingQueue::new(2);
q.push(1);
assert!(q.take_used_if(|i| i == &1).is_none());
}
#[test]
fn should_not_find_when_pushed_with_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
assert!(q.clone_used_if(|i| i == &1).is_none());
}
#[test]
fn should_find_when_pushed_and_used() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
}
#[test]
fn should_have_same_semantics_for_get_take_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
q.use_last_ref();
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1);
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
}
#[test]
fn should_find_when_pushed_and_used_with_clone() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
}
#[test]
fn should_not_find_again_when_pushed_and_taken() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
assert!(q.clone_used_if(|i| i == &1).is_none());
}
#[test]
fn should_find_again_when_pushed_and_cloned() {
let mut q = UsingQueue::new(2);
q.push(1);
q.use_last_ref();
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
}
#[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.take_used_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.take_used_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.take_used_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.take_used_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));
}