From 458ca73250de360621d9e9287feac97fa883c40f Mon Sep 17 00:00:00 2001 From: Diogo Sousa Date: Tue, 5 Mar 2019 23:53:46 +0000 Subject: [PATCH] Now `List` abstracts from the pointer type (`Rc`/`Arc`). --- Cargo.toml | 6 + benches/rpds_list.rs | 2 +- benches/rpds_list_sync.rs | 170 ++++++++++++++++++++++ src/lib.rs | 1 + src/list/mod.rs | 256 +++++++++++++++++++++++----------- src/list/test.rs | 64 +++++++-- src/map/hash_trie_map/mod.rs | 12 +- src/map/hash_trie_map/test.rs | 41 +++--- src/queue/mod.rs | 42 +++--- src/queue/test.rs | 8 +- src/stack/mod.rs | 11 +- tools/release.sh | 6 +- 12 files changed, 475 insertions(+), 144 deletions(-) create mode 100644 benches/rpds_list_sync.rs diff --git a/Cargo.toml b/Cargo.toml index 45365fb..b8aef87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ travis-ci = { repository = "orium/rpds", branch = "master" } codecov = { repository = "orium/rpds", branch = "master", service = "github" } [dependencies] +archery = "0.2" serde = { version = "1", optional = true } [dev-dependencies] @@ -47,6 +48,7 @@ rand = "0.6" # needed to test serde bincode = "1" pretty_assertions = "0.6" +static_assertions = "0.3" [features] fatal-warnings = [] @@ -62,6 +64,10 @@ harness = false name = "rpds_list" harness = false +[[bench]] +name = "rpds_list_sync" +harness = false + [[bench]] name = "std_vec" harness = false diff --git a/benches/rpds_list.rs b/benches/rpds_list.rs index 011efa6..0e31105 100644 --- a/benches/rpds_list.rs +++ b/benches/rpds_list.rs @@ -142,7 +142,7 @@ fn rpds_list_reverse_mut(c: &mut Criterion) { fn rpds_list_iterate(c: &mut Criterion) { let limit = 10_000; - let mut list: List = List::new(); + let mut list = List::new(); for i in 0..limit { list.push_front_mut(i); diff --git a/benches/rpds_list_sync.rs b/benches/rpds_list_sync.rs new file mode 100644 index 0000000..6069c0b --- /dev/null +++ b/benches/rpds_list_sync.rs @@ -0,0 +1,170 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#![cfg_attr(feature = "fatal-warnings", deny(warnings))] + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rpds::ListSync; + +fn rpds_list_sync_push_front(c: &mut Criterion) { + let limit = 10_000; + + c.bench_function("rpds list sync push front", move |b| { + b.iter(|| { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list = list.push_front(i); + } + + list + }) + }); +} + +fn rpds_list_sync_push_front_mut(c: &mut Criterion) { + let limit = 10_000; + + c.bench_function("rpds list sync push front mut", move |b| { + b.iter(|| { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + list + }) + }); +} + +fn rpds_list_sync_drop_first(c: &mut Criterion) { + let limit = 10_000; + + c.bench_function("rpds list sync drop first", move |b| { + b.iter_with_setup( + || { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + list + }, + |mut list| { + for _ in 0..limit { + list = list.drop_first().unwrap(); + } + + list + }, + ); + }); +} + +fn rpds_list_sync_drop_first_mut(c: &mut Criterion) { + let limit = 10_000; + + c.bench_function("rpds list sync drop first mut", move |b| { + b.iter_with_setup( + || { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + list + }, + |mut list| { + for _ in 0..limit { + list.drop_first_mut(); + } + + list + }, + ); + }); +} + +fn rpds_list_sync_reverse(c: &mut Criterion) { + let limit = 1_000; + + c.bench_function("rpds list sync reverse", move |b| { + b.iter_with_setup( + || { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + list + }, + |mut list| { + for _ in 0..limit { + list = list.reverse(); + } + + list + }, + ); + }); +} + +fn rpds_list_sync_reverse_mut(c: &mut Criterion) { + let limit = 1_000; + + c.bench_function("rpds list sync reverse mut", move |b| { + b.iter_with_setup( + || { + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + list + }, + |mut list| { + for _ in 0..limit { + list.reverse_mut(); + } + + list + }, + ); + }); +} + +fn rpds_list_sync_iterate(c: &mut Criterion) { + let limit = 10_000; + let mut list: ListSync = ListSync::new_sync(); + + for i in 0..limit { + list.push_front_mut(i); + } + + c.bench_function("rpds list sync iterate", move |b| { + b.iter(|| { + for i in list.iter() { + black_box(i); + } + }) + }); +} + +criterion_group!( + benches, + rpds_list_sync_push_front, + rpds_list_sync_push_front_mut, + rpds_list_sync_drop_first, + rpds_list_sync_drop_first_mut, + rpds_list_sync_reverse, + rpds_list_sync_reverse_mut, + rpds_list_sync_iterate +); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index 9a7723f..ea1ec9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -296,6 +296,7 @@ pub mod stack; pub mod vector; pub use crate::list::List; +pub use crate::list::ListSync; pub use crate::map::hash_trie_map::HashTrieMap; pub use crate::map::red_black_tree_map::RedBlackTreeMap; pub use crate::queue::Queue; diff --git a/src/list/mod.rs b/src/list/mod.rs index c8f9a69..ac1b9e1 100644 --- a/src/list/mod.rs +++ b/src/list/mod.rs @@ -3,40 +3,40 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use archery::*; use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::Display; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; -use std::sync::Arc; // TODO Use impl trait instead of this when available. -pub type Iter<'a, T> = std::iter::Map, fn(&Arc) -> &T>; +pub type Iter<'a, T, P> = std::iter::Map, fn(&SharedPointer) -> &T>; #[doc(hidden)] #[macro_export] macro_rules! list_reverse { - ( ; $($reversed:expr),*) => { + ($ptr_kind:ty ; ; $($reversed:expr),*) => { { #[allow(unused_mut)] - let mut l = $crate::List::new(); + let mut l: List<_, $ptr_kind> = $crate::List::new_with_ptr_kind(); $( l.push_front_mut($reversed); )* l } }; - ($h:expr ; $($reversed:expr),*) => { - $crate::list_reverse!( ; $h, $($reversed),*) + ($ptr_kind:ty ; $h:expr ; $($reversed:expr),*) => { + $crate::list_reverse!($ptr_kind ; ; $h, $($reversed),*) }; - ($h:expr, $($t:expr),+ ; $($reversed:expr),*) => { - $crate::list_reverse!($($t),* ; $h, $($reversed),*) + ($ptr_kind:ty ; $h:expr, $($t:expr),+ ; $($reversed:expr),*) => { + $crate::list_reverse!($ptr_kind ; $($t),* ; $h, $($reversed),*) }; // This is just to handle the cases where this macro is called with an extra comma in the // reserve list, which can happen in a recursive call. - ($($t:expr),* ; $($reversed:expr),*,) => { - $crate::list_reverse!($($t),* ; $($reversed),*) + ($ptr_kind:ty ; $($t:expr),* ; $($reversed:expr),*,) => { + $crate::list_reverse!($ptr_kind ; $($t),* ; $($reversed),*) }; } @@ -55,7 +55,31 @@ macro_rules! list_reverse { #[macro_export] macro_rules! list { ($($e:expr),*) => { - $crate::list_reverse!($($e),* ; ) + $crate::list_reverse!(::archery::SharedPointerKindRc ; $($e),* ; ) + }; +} + +/// Creates a [`List`](list/struct.List.html) that implements `Sync`, containing the +/// given arguments: +/// +/// ``` +/// # use rpds::*; +/// # +/// let l = List::new_sync() +/// .push_front(3) +/// .push_front(2) +/// .push_front(1); +/// +/// assert_eq!(list_sync![1, 2, 3], l); +/// +/// fn is_sync() -> impl Sync { +/// list_sync![0, 1, 1, 2, 3, 5, 8] +/// } +/// ``` +#[macro_export] +macro_rules! list_sync { + ($($e:expr),*) => { + $crate::list_reverse!(::archery::SharedPointerKindArc ; $($e),* ; ) }; } @@ -86,30 +110,58 @@ macro_rules! list { /// This is your classic functional list with "cons" and "nil" nodes, with a little extra sauce to /// make some operations more efficient. #[derive(Debug)] -pub struct List { - head: Option>>, - last: Option>, +pub struct List +where + P: SharedPointerKind, +{ + head: Option, P>>, + last: Option>, length: usize, } #[derive(Debug)] -struct Node { - value: Arc, - next: Option>>, +struct Node +where + P: SharedPointerKind, +{ + value: SharedPointer, + next: Option, P>>, } -impl Clone for Node { - fn clone(&self) -> Node { +impl Clone for Node +where + P: SharedPointerKind, +{ + fn clone(&self) -> Node { Node { - value: Arc::clone(&self.value), + value: SharedPointer::clone(&self.value), next: self.next.clone(), } } } +pub type ListSync = List; + +impl ListSync { + #[must_use] + pub fn new_sync() -> ListSync { + List::new_with_ptr_kind() + } +} + impl List { #[must_use] pub fn new() -> List { + List::new_with_ptr_kind() + } +} + +impl List +where + P: SharedPointerKind, +{ + #[must_use] + pub fn new_with_ptr_kind() -> List { List { head: None, last: None, @@ -128,7 +180,7 @@ impl List { } #[must_use] - pub fn drop_first(&self) -> Option> { + pub fn drop_first(&self) -> Option> { let mut new_list = self.clone(); if new_list.drop_first_mut() { @@ -153,9 +205,9 @@ impl List { } } - fn push_front_arc_mut(&mut self, v: Arc) { + fn push_front_ptr_mut(&mut self, v: SharedPointer) { if self.length == 0 { - self.last = Some(Arc::clone(&v)); + self.last = Some(SharedPointer::clone(&v)); } let new_head = Node { @@ -163,12 +215,12 @@ impl List { next: self.head.take(), }; - self.head = Some(Arc::new(new_head)); + self.head = Some(SharedPointer::new(new_head)); self.length += 1; } #[must_use] - pub fn push_front(&self, v: T) -> List { + pub fn push_front(&self, v: T) -> List { let mut new_list = self.clone(); new_list.push_front_mut(v); @@ -177,38 +229,41 @@ impl List { } pub fn push_front_mut(&mut self, v: T) { - self.push_front_arc_mut(Arc::new(v)) + self.push_front_ptr_mut(SharedPointer::new(v)) } #[must_use] - pub fn reverse(&self) -> List { - let mut new_list = List::new(); + pub fn reverse(&self) -> List { + let mut new_list = List::new_with_ptr_kind(); // It is significantly faster to re-implement this here than to clone and call // `reverse_mut()`. The reason is that since this is a linear data structure all nodes will // need to be cloned given that the ref count would be greater than one. - for v in self.iter_arc() { - new_list.push_front_arc_mut(Arc::clone(v)); + for v in self.iter_ptr() { + new_list.push_front_ptr_mut(SharedPointer::clone(v)); } new_list } pub fn reverse_mut(&mut self) { - self.last = self.head.as_ref().map(|next| Arc::clone(&next.value)); + self.last = self + .head + .as_ref() + .map(|next| SharedPointer::clone(&next.value)); - let mut prev: Option>> = None; - let mut current: Option>> = self.head.take(); + let mut prev: Option, P>> = None; + let mut current: Option, P>> = self.head.take(); - while let Some(mut curr_arc) = current { - let curr = Arc::make_mut(&mut curr_arc); + while let Some(mut curr_ptr) = current { + let curr = SharedPointer::make_mut(&mut curr_ptr); let curr_next = curr.next.take(); curr.next = prev.take(); current = curr_next; - prev = Some(curr_arc); + prev = Some(curr_ptr); } self.head = prev; @@ -227,42 +282,59 @@ impl List { } #[must_use] - pub fn iter(&self) -> Iter<'_, T> { - self.iter_arc().map(|v| v.borrow()) + pub fn iter(&self) -> Iter<'_, T, P> { + self.iter_ptr().map(|v| v.borrow()) } - pub(crate) fn iter_arc(&self) -> IterArc<'_, T> { - IterArc::new(self) + pub fn iter_ptr(&self) -> IterPtr<'_, T, P> { + IterPtr::new(self) } } -impl Default for List { - fn default() -> List { - List::new() +impl Default for List +where + P: SharedPointerKind, +{ + fn default() -> List { + List::new_with_ptr_kind() } } -impl PartialEq for List { - fn eq(&self, other: &List) -> bool { +impl PartialEq> for List +where + P: SharedPointerKind, + PO: SharedPointerKind, +{ + fn eq(&self, other: &List) -> bool { self.length == other.length && self.iter().eq(other.iter()) } } -impl Eq for List {} +impl Eq for List where P: SharedPointerKind {} -impl> PartialOrd> for List { - fn partial_cmp(&self, other: &List) -> Option { +impl, P, PO> PartialOrd> for List +where + P: SharedPointerKind, + PO: SharedPointerKind, +{ + fn partial_cmp(&self, other: &List) -> Option { self.iter().partial_cmp(other.iter()) } } -impl Ord for List { - fn cmp(&self, other: &List) -> Ordering { +impl Ord for List +where + P: SharedPointerKind, +{ + fn cmp(&self, other: &List) -> Ordering { self.iter().cmp(other.iter()) } } -impl Hash for List { +impl Hash for List +where + P: SharedPointerKind, +{ fn hash(&self, state: &mut H) { // Add the hash of length so that if two collections are added one after the other it doesn't // hash to the same thing as a single collection with the same elements in the same order. @@ -274,8 +346,11 @@ impl Hash for List { } } -impl Clone for List { - fn clone(&self) -> List { +impl Clone for List +where + P: SharedPointerKind, +{ + fn clone(&self) -> List { List { head: self.head.clone(), last: self.last.clone(), @@ -284,7 +359,10 @@ impl Clone for List { } } -impl Display for List { +impl Display for List +where + P: SharedPointerKind, +{ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut first = true; @@ -302,17 +380,23 @@ impl Display for List { } } -impl<'a, T> IntoIterator for &'a List { +impl<'a, T, P> IntoIterator for &'a List +where + P: SharedPointerKind, +{ type Item = &'a T; - type IntoIter = Iter<'a, T>; + type IntoIter = Iter<'a, T, P>; - fn into_iter(self) -> Iter<'a, T> { + fn into_iter(self) -> Iter<'a, T, P> { self.iter() } } -impl FromIterator for List { - fn from_iter>(into_iter: I) -> List { +impl FromIterator for List +where + P: SharedPointerKind, +{ + fn from_iter>(into_iter: I) -> List { let iter = into_iter.into_iter(); let (min_size, max_size_hint) = iter.size_hint(); let mut vec: Vec = Vec::with_capacity(max_size_hint.unwrap_or(min_size)); @@ -321,7 +405,7 @@ impl FromIterator for List { vec.push(e); } - let mut list: List = List::new(); + let mut list: List = List::new_with_ptr_kind(); for e in vec.into_iter().rev() { list.push_front_mut(e); @@ -332,24 +416,33 @@ impl FromIterator for List { } #[derive(Debug)] -pub struct IterArc<'a, T> { - next: Option<&'a Node>, +pub struct IterPtr<'a, T, P> +where + P: SharedPointerKind, +{ + next: Option<&'a Node>, length: usize, } -impl<'a, T> IterArc<'a, T> { - fn new(list: &List) -> IterArc<'_, T> { - IterArc { +impl<'a, T, P> IterPtr<'a, T, P> +where + P: SharedPointerKind, +{ + fn new(list: &List) -> IterPtr<'_, T, P> { + IterPtr { next: list.head.as_ref().map(|node| node.as_ref()), length: list.len(), } } } -impl<'a, T> Iterator for IterArc<'a, T> { - type Item = &'a Arc; +impl<'a, T, P> Iterator for IterPtr<'a, T, P> +where + P: SharedPointerKind, +{ + type Item = &'a SharedPointer; - fn next(&mut self) -> Option<&'a Arc> { + fn next(&mut self) -> Option<&'a SharedPointer> { match self.next { Some(Node { value: ref v, @@ -368,7 +461,7 @@ impl<'a, T> Iterator for IterArc<'a, T> { } } -impl<'a, T> ExactSizeIterator for IterArc<'a, T> {} +impl<'a, T, P> ExactSizeIterator for IterPtr<'a, T, P> where P: SharedPointerKind {} #[cfg(feature = "serde")] pub mod serde { @@ -378,41 +471,46 @@ pub mod serde { use std::fmt; use std::marker::PhantomData; - impl Serialize for List + impl Serialize for List where T: Serialize, + P: SharedPointerKind, { fn serialize(&self, serializer: S) -> Result { serializer.collect_seq(self) } } - impl<'de, T> Deserialize<'de> for List + impl<'de, T, P> Deserialize<'de> for List where T: Deserialize<'de>, + P: SharedPointerKind, { - fn deserialize>(deserializer: D) -> Result, D::Error> { + fn deserialize>(deserializer: D) -> Result, D::Error> { deserializer.deserialize_seq(ListVisitor { - phantom: PhantomData, + _phantom_t: PhantomData, + _phantom_p: PhantomData, }) } } - struct ListVisitor { - phantom: PhantomData, + struct ListVisitor { + _phantom_t: PhantomData, + _phantom_p: PhantomData

, } - impl<'de, T> Visitor<'de> for ListVisitor + impl<'de, T, P> Visitor<'de> for ListVisitor where T: Deserialize<'de>, + P: SharedPointerKind, { - type Value = List; + type Value = List; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } - fn visit_seq(self, mut seq: A) -> Result, A::Error> + fn visit_seq(self, mut seq: A) -> Result, A::Error> where A: SeqAccess<'de>, { @@ -426,7 +524,7 @@ pub mod serde { vec.push(value); } - let mut list: List = List::new(); + let mut list: List = List::new_with_ptr_kind(); for value in vec.into_iter().rev() { list.push_front_mut(value); diff --git a/src/list/test.rs b/src/list/test.rs index a838a6b..c9a75eb 100644 --- a/src/list/test.rs +++ b/src/list/test.rs @@ -5,6 +5,18 @@ use super::*; use pretty_assertions::assert_eq; +use static_assertions::assert_impl; + +assert_impl!( + list_sync_is_send_and_sync; + ListSync, + Send, Sync +); + +#[allow(dead_code)] +fn compile_time_macro_list_sync_is_send_and_sync() -> impl Send + Sync { + list_sync!(0) +} mod iter { use super::*; @@ -67,20 +79,6 @@ mod iter { } } -mod compile_time { - use super::*; - - #[test] - fn test_is_send() { - let _: Box = Box::new(List::::new()); - } - - #[test] - fn test_is_sync() { - let _: Box = Box::new(List::::new()); - } -} - #[test] fn test_new() { let empty_list: List = List::new(); @@ -248,6 +246,18 @@ fn test_eq() { assert_eq!(list_2, list_2); } +#[test] +fn test_eq_pointer_kind_consistent() { + let list_a = list!["a"]; + let list_a_sync = list_sync!["a"]; + let list_b = list!["b"]; + let list_b_sync = list_sync!["b"]; + + assert!(list_a == list_a_sync); + assert!(list_a != list_b_sync); + assert!(list_b == list_b_sync); +} + #[test] fn test_partial_ord() { let list_1 = list!["a"]; @@ -273,7 +283,23 @@ fn test_ord() { assert_eq!(list_2.cmp(&list_1), Ordering::Greater); } -fn hash(list: &List) -> u64 { +#[test] +fn test_ord_pointer_kind_consistent() { + let list_a = list!["a"]; + let list_a_sync = list_sync!["a"]; + let list_b = list!["b"]; + let list_b_sync = list_sync!["b"]; + + assert!(list_a <= list_a_sync); + assert!(list_a < list_b_sync); + assert!(list_b >= list_b_sync); + + assert!(list_a_sync >= list_a); + assert!(list_b_sync > list_a); + assert!(list_b_sync <= list_b); +} + +fn hash(list: &List) -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); list.hash(&mut hasher); @@ -292,6 +318,14 @@ fn test_hash() { assert_ne!(hash(&list_1), hash(&list_2)); } +#[test] +fn test_hash_pointer_kind_consistent() { + let list = list!["a"]; + let list_sync = list_sync!["a"]; + + assert_eq!(hash(&list), hash(&list_sync)); +} + #[test] fn test_clone() { let list = list!["hello", "there"]; diff --git a/src/map/hash_trie_map/mod.rs b/src/map/hash_trie_map/mod.rs index a688e38..213cdee 100644 --- a/src/map/hash_trie_map/mod.rs +++ b/src/map/hash_trie_map/mod.rs @@ -8,6 +8,8 @@ mod sparse_array_usize; use super::entry::Entry; use crate::list; use crate::List; +use crate::ListSync; +use archery::SharedPointerKindArc; use sparse_array_usize::SparseArrayUsize; use std::borrow::Borrow; use std::collections::hash_map::RandomState; @@ -160,7 +162,7 @@ enum Node { #[derive(Debug, PartialEq, Eq)] enum Bucket { Single(EntryWithHash), - Collision(List>), + Collision(ListSync>), } #[derive(Debug, PartialEq, Eq)] @@ -413,11 +415,11 @@ mod bucket_utils { /// Returns `true` if an element was removed. pub fn list_remove_first bool>( - list: &mut List, + list: &mut ListSync, predicate: F, ) -> bool { let mut before_needle: Vec = Vec::with_capacity(list.len()); - let remaining: &mut List = list; + let remaining: &mut ListSync = list; let mut removed = false; while !remaining.is_empty() { @@ -487,7 +489,7 @@ where } Bucket::Single(existing_entry) => { // TODO In theory we should not need to clone `existing_entry`. - let entries = list!(entry, existing_entry.clone()); + let entries = list_sync!(entry, existing_entry.clone()); *self = Bucket::Collision(entries); @@ -881,7 +883,7 @@ pub struct IterArc<'a, K, V> { enum IterStackElement<'a, K, V> { Branch(Peekable>>>), LeafSingle(&'a EntryWithHash), - LeafCollision(Peekable>>), + LeafCollision(Peekable, SharedPointerKindArc>>), } impl<'a, K, V> IterStackElement<'a, K, V> diff --git a/src/map/hash_trie_map/test.rs b/src/map/hash_trie_map/test.rs index 63d44fa..15680ee 100644 --- a/src/map/hash_trie_map/test.rs +++ b/src/map/hash_trie_map/test.rs @@ -14,10 +14,10 @@ mod bucket { fn test_list_remove_first() { use bucket_utils::list_remove_first; - let list_a_b_c = list!['a', 'b', 'c']; - let list_b_c = list!['b', 'c']; - let list_a_c = list!['a', 'c']; - let list_a_b = list!['a', 'b']; + let list_a_b_c = list_sync!['a', 'b', 'c']; + let list_b_c = list_sync!['b', 'c']; + let list_a_c = list_sync!['a', 'c']; + let list_a_b = list_sync!['a', 'b']; let mut list = list_a_b_c.clone(); assert!(!list_remove_first(&mut list, |_| false)); @@ -45,7 +45,7 @@ mod bucket { let entry_c = EntryWithHash::new(0xCu8, 2, &hash_builder); let bucket_single = Bucket::Single(entry_a.clone()); - let bucket_collision = Bucket::Collision(list![entry_b.clone(), entry_a.clone()]); + let bucket_collision = Bucket::Collision(list_sync![entry_b.clone(), entry_a.clone()]); assert_eq!( bucket_single.get(entry_a.key(), entry_a.key_hash), @@ -77,12 +77,18 @@ mod bucket { let bucket_single_a = Bucket::Single(entry_a.clone()); let bucket_single_a9 = Bucket::Single(entry_a9.clone()); - let bucket_collision_b_a = Bucket::Collision(list![entry_b.clone(), entry_a.clone()]); - let bucket_collision_a_b_c = - Bucket::Collision(list![entry_a.clone(), entry_b.clone(), entry_c.clone()]); - let bucket_collision_b9_a_c = - Bucket::Collision(list![entry_b9.clone(), entry_a.clone(), entry_c.clone()]); - let bucket_collision_d_a_b_c = Bucket::Collision(list![ + let bucket_collision_b_a = Bucket::Collision(list_sync![entry_b.clone(), entry_a.clone()]); + let bucket_collision_a_b_c = Bucket::Collision(list_sync![ + entry_a.clone(), + entry_b.clone(), + entry_c.clone() + ]); + let bucket_collision_b9_a_c = Bucket::Collision(list_sync![ + entry_b9.clone(), + entry_a.clone(), + entry_c.clone() + ]); + let bucket_collision_d_a_b_c = Bucket::Collision(list_sync![ entry_d.clone(), entry_a.clone(), entry_b.clone(), @@ -121,9 +127,12 @@ mod bucket { let entry_d = EntryWithHash::new(0xDu8, 2, &hash_builder); let bucket_single_a = Bucket::Single(entry_a.clone()); - let bucket_collision_b_c = Bucket::Collision(list![entry_b.clone(), entry_c.clone()]); - let bucket_collision_a_b_c = - Bucket::Collision(list![entry_a.clone(), entry_b.clone(), entry_c.clone()]); + let bucket_collision_b_c = Bucket::Collision(list_sync![entry_b.clone(), entry_c.clone()]); + let bucket_collision_a_b_c = Bucket::Collision(list_sync![ + entry_a.clone(), + entry_b.clone(), + entry_c.clone() + ]); let mut bucket_ref: Option<&mut Bucket> = None; assert!(!Bucket::remove( @@ -353,7 +362,7 @@ mod node { let bucket_a = Bucket::Single(entry_a); let bucket_b = Bucket::Single(entry_b); let bucket_c = Bucket::Single(entry_c); - let bucket_de = Bucket::Collision(list![entry_e, entry_d]); + let bucket_de = Bucket::Collision(list_sync![entry_e, entry_d]); let node_depth_1_first = { let mut array = SparseArrayUsize::new(); @@ -472,7 +481,7 @@ mod node { let bucket_a = Bucket::Single(entry_a.clone()); let bucket_b = Bucket::Single(entry_b.clone()); - let bucket_a_b = Bucket::Collision(list![entry_a.clone(), entry_b.clone()]); + let bucket_a_b = Bucket::Collision(list_sync![entry_a.clone(), entry_b.clone()]); let empty_branch = Node::::new_empty_branch(); let branch_with_collision = { diff --git a/src/queue/mod.rs b/src/queue/mod.rs index 8f07c2e..863ead0 100644 --- a/src/queue/mod.rs +++ b/src/queue/mod.rs @@ -3,17 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::List; +use crate::{List, ListSync}; +use archery::SharedPointer; +use archery::SharedPointerKindArc; use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::Display; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; -use std::sync::Arc; // TODO Use impl trait instead of this when available. -type IterArc<'a, T> = std::iter::Chain, LazilyReversedListIter<'a, T>>; -pub type Iter<'a, T> = std::iter::Map, fn(&Arc) -> &T>; +type IterArc<'a, T> = std::iter::Chain< + crate::list::IterPtr<'a, T, SharedPointerKindArc>, + LazilyReversedListIter<'a, T>, +>; +pub type Iter<'a, T> = + std::iter::Map, fn(&SharedPointer) -> &T>; /// Creates a [`Queue`](queue/struct.Queue.html) containing the given arguments: /// @@ -68,16 +73,16 @@ macro_rules! queue { /// [Immutability in C# Part Four: An Immutable Queue](https://goo.gl/hWyMuS). #[derive(Debug)] pub struct Queue { - in_list: List, - out_list: List, + in_list: ListSync, + out_list: ListSync, } impl Queue { #[must_use] pub fn new() -> Queue { Queue { - in_list: List::new(), - out_list: List::new(), + in_list: List::new_with_ptr_kind(), + out_list: List::new_with_ptr_kind(), } } @@ -148,7 +153,7 @@ impl Queue { fn iter_arc(&self) -> IterArc<'_, T> { self.out_list - .iter_arc() + .iter_ptr() .chain(LazilyReversedListIter::new(&self.in_list)) } } @@ -232,37 +237,38 @@ impl FromIterator for Queue { fn from_iter>(into_iter: I) -> Queue { Queue { out_list: List::from_iter(into_iter), - in_list: List::new(), + in_list: List::new_with_ptr_kind(), } } } pub enum LazilyReversedListIter<'a, T: 'a> { Uninitialized { - list: &'a List, + list: &'a ListSync, }, Initialized { - vec: Vec<&'a Arc>, + vec: Vec<&'a SharedPointer>, current: Option, }, } impl<'a, T> LazilyReversedListIter<'a, T> { - fn new(list: &List) -> LazilyReversedListIter<'_, T> { + fn new(list: &ListSync) -> LazilyReversedListIter<'_, T> { LazilyReversedListIter::Uninitialized { list } } } impl<'a, T> Iterator for LazilyReversedListIter<'a, T> { - type Item = &'a Arc; + type Item = &'a SharedPointer; - fn next(&mut self) -> Option<&'a Arc> { + fn next(&mut self) -> Option<&'a SharedPointer> { match self { LazilyReversedListIter::Uninitialized { list } => { let len = list.len(); - let mut vec: Vec<&'a Arc> = Vec::with_capacity(len); + let mut vec: Vec<&'a SharedPointer> = + Vec::with_capacity(len); - for v in list.iter_arc() { + for v in list.iter_ptr() { vec.push(v); } @@ -328,7 +334,7 @@ pub mod serde { fn deserialize>(deserializer: D) -> Result, D::Error> { Deserialize::deserialize(deserializer).map(|list| Queue { out_list: list, - in_list: List::new(), + in_list: List::new_with_ptr_kind(), }) } } diff --git a/src/queue/test.rs b/src/queue/test.rs index 8698d98..593f40b 100644 --- a/src/queue/test.rs +++ b/src/queue/test.rs @@ -12,7 +12,7 @@ mod lazily_reversed_list_iter { #[test] fn test_iter() { - let list = list![0, 1, 2]; + let list = list_sync![0, 1, 2]; let mut iterator = LazilyReversedListIter::new(&list); assert_eq!(iterator.next().map(|v| **v), Some(2)); @@ -20,7 +20,7 @@ mod lazily_reversed_list_iter { assert_eq!(iterator.next().map(|v| **v), Some(0)); assert_eq!(iterator.next(), None); - let empty_list: List = List::new(); + let empty_list: ListSync = ListSync::new_sync(); let mut iterator = LazilyReversedListIter::new(&empty_list); assert_eq!(iterator.next(), None); @@ -28,7 +28,7 @@ mod lazily_reversed_list_iter { #[test] fn test_iter_size_hint() { - let list = list![0, 1, 2]; + let list = list_sync![0, 1, 2]; let mut iterator = LazilyReversedListIter::new(&list); assert_eq!(iterator.size_hint(), (3, Some(3))); @@ -42,7 +42,7 @@ mod lazily_reversed_list_iter { assert_eq!(iterator.size_hint(), (0, Some(0))); - let empty_list: List = List::new(); + let empty_list: ListSync = ListSync::new_sync(); let iterator = LazilyReversedListIter::new(&empty_list); assert_eq!(iterator.size_hint(), (0, Some(0))); diff --git a/src/stack/mod.rs b/src/stack/mod.rs index 6052e44..5726a34 100644 --- a/src/stack/mod.rs +++ b/src/stack/mod.rs @@ -3,14 +3,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::List; +use crate::{List, ListSync}; +use archery::SharedPointerKindArc; use std::cmp::Ordering; use std::fmt::Display; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; // TODO Use impl trait for return value when available -pub type Iter<'a, T> = crate::list::Iter<'a, T>; +pub type Iter<'a, T> = crate::list::Iter<'a, T, SharedPointerKindArc>; /// Creates a [`Stack`](stack/struct.Stack.html) containing the given arguments: /// @@ -63,13 +64,15 @@ macro_rules! stack { /// This is a thin wrapper around a [`List`](../list/struct.List.html). #[derive(Debug)] pub struct Stack { - list: List, + list: ListSync, } impl Stack { #[must_use] pub fn new() -> Stack { - Stack { list: List::new() } + Stack { + list: List::new_with_ptr_kind(), + } } #[must_use] diff --git a/tools/release.sh b/tools/release.sh index 2922c82..b9aac57 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -57,13 +57,15 @@ if [ "$answer" != "yes" ]; then exit 0 fi -echo "Running tests..." +echo -n "Running tests... " -if ! ./tools/check.sh 2>&1 > /dev/null; then +if ! ./tools/check.sh 2>/dev/null > /dev/null; then echo "It failed :(" >&2 exit 0 fi +echo "done." + set_version "$release_version" git commit -am "Release v${release_version}."