From 0f01c3b22cb885e4b98cb9574036899ed0e64859 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 14 Oct 2017 16:30:33 +0200 Subject: [PATCH] [Experiment] Replace HashMap with OrderMap --- src/librustc/Cargo.toml | 1 + src/librustc/dep_graph/query.rs | 4 +- src/librustc/ich/hcx.rs | 4 +- src/librustc/infer/freshen.rs | 2 +- .../infer/region_inference/graphviz.rs | 2 +- src/librustc/lib.rs | 1 + src/librustc/traits/select.rs | 2 +- src/librustc/ty/context.rs | 7 +- src/librustc_data_structures/Cargo.toml | 1 + src/librustc_data_structures/fx.rs | 9 +- src/librustc_data_structures/lib.rs | 1 + .../obligation_forest/mod.rs | 2 +- src/librustc_data_structures/stable_hasher.rs | 5 +- src/librustc_lint/Cargo.toml | 1 + src/librustc_lint/lib.rs | 1 + src/librustc_lint/unused.rs | 2 +- src/librustc_metadata/Cargo.toml | 1 + src/librustc_metadata/cstore_impl.rs | 4 +- src/librustc_metadata/lib.rs | 1 + src/librustc_mir/Cargo.toml | 1 + src/librustc_mir/dataflow/impls/borrows.rs | 2 +- .../dataflow/move_paths/builder.rs | 2 +- src/librustc_mir/lib.rs | 1 + src/librustc_passes/Cargo.toml | 1 + src/librustc_passes/consts.rs | 2 +- src/librustc_passes/lib.rs | 1 + src/librustc_trans/Cargo.toml | 1 + src/librustc_trans/lib.rs | 1 + src/librustc_trans/partitioning.rs | 4 +- src/librustc_typeck/Cargo.toml | 1 + src/librustc_typeck/check/_match.rs | 2 +- .../check/generator_interior.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/check/upvar.rs | 2 +- src/librustc_typeck/impl_wf_check.rs | 2 +- src/librustc_typeck/lib.rs | 1 + src/libserialize/Cargo.toml | 3 + src/libserialize/collection_impls.rs | 38 + src/libserialize/lib.rs | 2 + src/ordermap/Cargo.toml | 8 + src/ordermap/equivalent.rs | 27 + src/ordermap/lib.rs | 1778 +++++++++++++++++ src/ordermap/macros.rs | 1 + src/ordermap/mutable_keys.rs | 88 + src/ordermap/serde.rs | 67 + src/ordermap/util.rs | 17 + 46 files changed, 2079 insertions(+), 29 deletions(-) create mode 100644 src/ordermap/Cargo.toml create mode 100644 src/ordermap/equivalent.rs create mode 100644 src/ordermap/lib.rs create mode 100644 src/ordermap/macros.rs create mode 100644 src/ordermap/mutable_keys.rs create mode 100644 src/ordermap/serde.rs create mode 100644 src/ordermap/util.rs diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 0b62e1bd5afbf..1aa13ff31c4b5 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -23,6 +23,7 @@ rustc_errors = { path = "../librustc_errors" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +ordermap = { path = "../ordermap" } # Note that these dependencies are a lie, they're just here to get linkage to # work. diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs index ea83a4f8b3104..97ab2ae4532ed 100644 --- a/src/librustc/dep_graph/query.rs +++ b/src/librustc/dep_graph/query.rs @@ -41,7 +41,7 @@ impl DepGraphQuery { } pub fn contains_node(&self, node: &DepNode) -> bool { - self.indices.contains_key(&node) + self.indices.contains_key(node) } pub fn nodes(&self) -> Vec<&DepNode> { @@ -83,7 +83,7 @@ impl DepGraphQuery { /// Just the outgoing edges from `node`. pub fn immediate_successors(&self, node: &DepNode) -> Vec<&DepNode> { - if let Some(&index) = self.indices.get(&node) { + if let Some(&index) = self.indices.get(node) { self.graph.successor_nodes(index) .map(|s| self.graph.node_data(s)) .collect() diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index e7a26e14db5bb..3cf2c869b99c3 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -21,7 +21,7 @@ use session::Session; use std::cmp::Ord; use std::hash as std_hash; use std::cell::RefCell; -use std::collections::HashMap; +use ordermap::OrderMap; use syntax::ast; use syntax::attr; @@ -426,7 +426,7 @@ pub fn hash_stable_trait_impls<'gcx, W, R>( hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher, blanket_impls: &Vec, - non_blanket_impls: &HashMap, R>) + non_blanket_impls: &OrderMap, R>) where W: StableHasherResult, R: std_hash::BuildHasher, { diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index c274f8bda9fb0..6e9f0ac4ffc4e 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -47,7 +47,7 @@ use ty::subst::Substs; use util::nodemap::FxHashMap; use hir::def_id::DefId; -use std::collections::hash_map::Entry; +use ordermap::Entry; use super::InferCtxt; use super::unify_key::ToType; diff --git a/src/librustc/infer/region_inference/graphviz.rs b/src/librustc/infer/region_inference/graphviz.rs index 5cf6aa350bdd5..bd56ab8e743b1 100644 --- a/src/librustc/infer/region_inference/graphviz.rs +++ b/src/librustc/infer/region_inference/graphviz.rs @@ -28,7 +28,7 @@ use infer::region_inference::RegionVarBindings; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; -use std::collections::hash_map::Entry::Vacant; +use ordermap::Entry::Vacant; use std::env; use std::fs::File; use std::io; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 015dbbb7affa9..96429fbd6bfdd 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -62,6 +62,7 @@ #![recursion_limit="256"] +extern crate ordermap; extern crate arena; #[macro_use] extern crate bitflags; extern crate core; diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 00f0672822fc1..41cf5df68a9ce 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1263,7 +1263,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let trait_ref = &cache_fresh_trait_pred.0.trait_ref; if self.can_use_global_caches(param_env) { let cache = tcx.selection_cache.hashmap.borrow(); - if let Some(cached) = cache.get(&trait_ref) { + if let Some(cached) = cache.get(trait_ref) { return Some(cached.get(tcx)); } } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 740299b91f118..a2726e38f6ca2 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -53,6 +53,7 @@ use rustc_data_structures::accumulate_vec::AccumulateVec; use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableHasher, StableHasherResult, StableVec}; +use rustc_data_structures; use arena::{TypedArena, DroplessArena}; use rustc_const_math::{ConstInt, ConstUsize}; use rustc_data_structures::indexed_vec::IndexVec; @@ -60,7 +61,7 @@ use std::any::Any; use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::cmp::Ordering; -use std::collections::hash_map::{self, Entry}; +use ordermap::{self, Entry}; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; @@ -275,7 +276,7 @@ impl<'a, V> LocalTableInContext<'a, V> { self.data.get(&id.local_id) } - pub fn iter(&self) -> hash_map::Iter { + pub fn iter(&self) -> ordermap::Iter { self.data.iter() } } @@ -299,7 +300,7 @@ impl<'a, V> LocalTableInContextMut<'a, V> { self.data.get_mut(&id.local_id) } - pub fn entry(&mut self, id: hir::HirId) -> Entry { + pub fn entry(&mut self, id: hir::HirId) -> Entry> { validate_hir_id_for_typeck_tables(self.local_id_root, id, true); self.data.entry(id.local_id) } diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 343b1ed68b804..a8da9d38e9e02 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["dylib"] [dependencies] log = "0.3" serialize = { path = "../libserialize" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_data_structures/fx.rs b/src/librustc_data_structures/fx.rs index 5bf25437763cc..6263929a3d4d9 100644 --- a/src/librustc_data_structures/fx.rs +++ b/src/librustc_data_structures/fx.rs @@ -8,17 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::default::Default; use std::hash::{Hasher, Hash, BuildHasherDefault}; use std::ops::BitXor; +use ordermap::OrderMap; -pub type FxHashMap = HashMap>; +// pub type FxHashMap = HashMap>; pub type FxHashSet = HashSet>; +pub type FxHashMap = OrderMap>; + #[allow(non_snake_case)] pub fn FxHashMap() -> FxHashMap { - HashMap::default() + Default::default() } #[allow(non_snake_case)] diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 47061883425e2..74bcd20c3dee0 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -40,6 +40,7 @@ extern crate log; extern crate serialize as rustc_serialize; // used by deriving #[cfg(unix)] extern crate libc; +extern crate ordermap; pub use rustc_serialize::hex::ToHex; diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 02cae52166ac3..e3d8173f1e473 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -18,7 +18,7 @@ use fx::{FxHashMap, FxHashSet}; use std::cell::Cell; -use std::collections::hash_map::Entry; +use super::ordermap::Entry; use std::fmt::Debug; use std::hash; use std::marker::PhantomData; diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 9aba48c5bef07..22138ca6b0a6e 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -13,6 +13,7 @@ use std::marker::PhantomData; use std::mem; use blake2b::Blake2bHasher; use rustc_serialize::leb128; +use ordermap::OrderMap; fn write_unsigned_leb128_to_buf(buf: &mut [u8; 16], value: u64) -> usize { leb128::write_unsigned_leb128_to(value as u128, |i, v| buf[i] = v) @@ -482,7 +483,7 @@ impl HashStable for ::indexed_set::IdxSetBuf impl_stable_hash_via_hash!(::std::path::Path); impl_stable_hash_via_hash!(::std::path::PathBuf); -impl HashStable for ::std::collections::HashMap +impl HashStable for OrderMap where K: ToStableHashKey + Eq + Hash, V: HashStable, R: BuildHasher, @@ -542,7 +543,7 @@ impl HashStable for ::std::collections::BTreeSet pub fn hash_stable_hashmap( hcx: &mut HCX, hasher: &mut StableHasher, - map: &::std::collections::HashMap, + map: &OrderMap, to_stable_hash_key: F) where K: Eq + Hash, V: HashStable, diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index c3c5461ff7c50..90f6cdae66022 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -16,3 +16,4 @@ rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index fbf993f45576c..d7bf77baf5b2f 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -41,6 +41,7 @@ extern crate log; extern crate rustc_back; extern crate rustc_const_eval; extern crate syntax_pos; +extern crate ordermap; use rustc::lint; use rustc::middle; diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index e2ade19b6e285..6f8b318d79376 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -15,7 +15,7 @@ use util::nodemap::FxHashMap; use lint::{LateContext, EarlyContext, LintContext, LintArray}; use lint::{LintPass, EarlyLintPass, LateLintPass}; -use std::collections::hash_map::Entry::{Occupied, Vacant}; +use ordermap::Entry::{Occupied, Vacant}; use syntax::ast; use syntax::attr; diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index 40b75be36fefb..00f00eca80d06 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -21,3 +21,4 @@ serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_ext = { path = "../libsyntax_ext" } syntax_pos = { path = "../libsyntax_pos" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 8eacc21ab003b..b491b3811cf40 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -290,7 +290,7 @@ pub fn provide_local<'tcx>(providers: &mut Providers<'tcx>) { // external item to be parents). visible_parent_map: |tcx, cnum| { use std::collections::vec_deque::VecDeque; - use std::collections::hash_map::Entry; + use ordermap::Entry; assert_eq!(cnum, LOCAL_CRATE); let mut visible_parent_map: DefIdMap = DefIdMap(); @@ -313,7 +313,7 @@ pub fn provide_local<'tcx>(providers: &mut Providers<'tcx>) { } match visible_parent_map.entry(child) { - Entry::Occupied(mut entry) => { + Entry::Occupied(entry) => { // If `child` is defined in crate `cnum`, ensure // that it is mapped to a parent in `cnum`. if child.krate == cnum && entry.get().krate != cnum { diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 54dbb68667b3a..ac734a0613ba5 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -39,6 +39,7 @@ extern crate proc_macro; extern crate rustc; extern crate rustc_back; extern crate rustc_data_structures; +extern crate ordermap; mod diagnostics; diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index b7a576babeb67..24177c4dd72c3 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -20,3 +20,4 @@ rustc_errors = { path = "../librustc_errors" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 9321121fe1550..05c778465a8b5 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -117,7 +117,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } pub fn region_span(&self, region: &Region) -> Span { - let opt_span = self.region_span_map.get(region); + let opt_span = self.region_span_map.get(*region); assert!(opt_span.is_some(), "end region not found for {:?}", region); *opt_span.unwrap() } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 0790d937cebf0..ecea9c04d3fd2 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -16,7 +16,7 @@ use rustc_data_structures::indexed_vec::{IndexVec}; use syntax::codemap::DUMMY_SP; -use std::collections::hash_map::Entry; +use ordermap::Entry; use std::mem; use super::abs_domain::Lift; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 7e4206e14c561..7acba65ef2ef7 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -41,6 +41,7 @@ extern crate syntax_pos; extern crate rustc_const_math; extern crate rustc_const_eval; extern crate core; // for NonZero +extern crate ordermap; mod diagnostics; diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index d2560c2f8203f..7a3f1cc084c7b 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -16,3 +16,4 @@ rustc_const_math = { path = "../librustc_const_math" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } rustc_errors = { path = "../librustc_errors" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 547d63fc3d4aa..8a3dd233653a5 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -50,7 +50,7 @@ use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use std::collections::hash_map::Entry; +use ordermap::Entry; use std::cmp::Ordering; struct CheckCrateVisitor<'a, 'tcx: 'a> { diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 28b99e1185bd2..52d9e331cf5e6 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -32,6 +32,7 @@ extern crate log; extern crate syntax; extern crate syntax_pos; extern crate rustc_errors as errors; +extern crate ordermap; mod diagnostics; diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 482350d04b5a7..856103c2ad786 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -30,6 +30,7 @@ rustc_trans_utils = { path = "../librustc_trans_utils" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +ordermap = { path = "../ordermap" } [target."cfg(windows)".dependencies] cc = "1.0" diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 2b1c62c7f1c76..21ac6a40c69e4 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -54,6 +54,7 @@ extern crate rustc_trans_utils; extern crate rustc_demangle; extern crate jobserver; extern crate num_cpus; +extern crate ordermap; #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 386806e4c9c9f..36565ec1151c7 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -111,7 +111,7 @@ use rustc::middle::trans::{Linkage, Visibility}; use rustc::ty::{self, TyCtxt, InstanceDef}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use std::collections::hash_map::Entry; +use ordermap::Entry; use syntax::ast::NodeId; use syntax::symbol::{Symbol, InternedString}; use trans_item::{TransItem, TransItemExt, InstantiationMode}; @@ -398,7 +398,7 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< let mut smallest = codegen_units.pop().unwrap(); let second_smallest = codegen_units.last_mut().unwrap(); - for (k, v) in smallest.items_mut().drain() { + for (k, v) in smallest.items_mut().drain(..) { second_smallest.items_mut().insert(k, v); } } diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index 194d37dcb81c2..c2ce996bd7d1e 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -21,3 +21,4 @@ rustc_data_structures = { path = "../librustc_data_structures" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } syntax_pos = { path = "../libsyntax_pos" } rustc_errors = { path = "../librustc_errors" } +ordermap = { path = "../ordermap" } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index ab8994bcae253..a7d37e8c4ffda 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -19,7 +19,7 @@ use check::{FnCtxt, Expectation, Diverges}; use check::coercion::CoerceMany; use util::nodemap::FxHashMap; -use std::collections::hash_map::Entry::{Occupied, Vacant}; +use ordermap::Entry::{Occupied, Vacant}; use std::cmp; use syntax::ast; use syntax::codemap::Spanned; diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index af1297697c241..9c05ee52e1e7c 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -81,7 +81,7 @@ pub fn resolve_interior<'a, 'gcx, 'tcx>(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); assert_eq!(region_expr_count, visitor.expr_count); - let mut types: Vec<_> = visitor.types.drain().collect(); + let mut types: Vec<_> = visitor.types.drain(..).collect(); // Sort types by insertion order types.sort_by_key(|t| t.1); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9c6a4abfbd7c0..d7df3b710dbd9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -108,7 +108,7 @@ use util::common::{ErrorReported, indenter}; use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; use std::cell::{Cell, RefCell, Ref, RefMut}; -use std::collections::hash_map::Entry; +use ordermap::Entry; use std::cmp; use std::fmt::Display; use std::mem::replace; diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index d179b390a2918..e58df9cbcc469 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -54,7 +54,7 @@ use rustc::hir::def_id::DefIndex; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::util::nodemap::FxHashMap; -use std::collections::hash_map::Entry; +use ordermap::Entry; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn closure_analyze(&self, body: &'gcx hir::Body) { diff --git a/src/librustc_typeck/impl_wf_check.rs b/src/librustc_typeck/impl_wf_check.rs index 15708ab766ae8..4a26300d8577d 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/src/librustc_typeck/impl_wf_check.rs @@ -24,7 +24,7 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::def_id::DefId; use rustc::ty::{self, TyCtxt}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; -use std::collections::hash_map::Entry::{Occupied, Vacant}; +use ordermap::Entry::{Occupied, Vacant}; use syntax_pos::Span; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 7a6ee73b9b9e9..a86cb011c11dc 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -90,6 +90,7 @@ extern crate rustc_back; extern crate rustc_const_math; extern crate rustc_data_structures; extern crate rustc_errors as errors; +extern crate ordermap; use rustc::hir; use rustc::lint; diff --git a/src/libserialize/Cargo.toml b/src/libserialize/Cargo.toml index a896c4a634c36..5afc1f6bce968 100644 --- a/src/libserialize/Cargo.toml +++ b/src/libserialize/Cargo.toml @@ -7,3 +7,6 @@ version = "0.0.0" name = "serialize" path = "lib.rs" crate-type = ["dylib", "rlib"] + +[dependencies] +ordermap = { path = "../ordermap" } diff --git a/src/libserialize/collection_impls.rs b/src/libserialize/collection_impls.rs index 1a995276931d8..6885fa8d11335 100644 --- a/src/libserialize/collection_impls.rs +++ b/src/libserialize/collection_impls.rs @@ -14,6 +14,7 @@ use std::hash::{Hash, BuildHasher}; use {Decodable, Encodable, Decoder, Encoder}; use std::collections::{LinkedList, VecDeque, BTreeMap, BTreeSet, HashMap, HashSet}; +use ordermap::OrderMap; impl< T: Encodable @@ -163,6 +164,43 @@ impl Decodable for HashMap } } +impl Encodable for OrderMap + where K: Encodable + Hash + Eq, + V: Encodable, + S: BuildHasher, +{ + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + e.emit_map(self.len(), |e| { + let mut i = 0; + for (key, val) in self { + e.emit_map_elt_key(i, |e| key.encode(e))?; + e.emit_map_elt_val(i, |e| val.encode(e))?; + i += 1; + } + Ok(()) + }) + } +} + +impl Decodable for OrderMap + where K: Decodable + Hash + Eq, + V: Decodable, + S: BuildHasher + Default, +{ + fn decode(d: &mut D) -> Result, D::Error> { + d.read_map(|d, len| { + let state = Default::default(); + let mut map = OrderMap::with_capacity_and_hasher(len, state); + for i in 0..len { + let key = d.read_map_elt_key(i, |d| Decodable::decode(d))?; + let val = d.read_map_elt_val(i, |d| Decodable::decode(d))?; + map.insert(key, val); + } + Ok(map) + }) + } +} + impl Encodable for HashSet where T: Encodable + Hash + Eq, S: BuildHasher, diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index 2e354252c1573..43b7bef755be5 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -27,6 +27,8 @@ Core encoding and decoding interfaces. #![feature(specialization)] #![cfg_attr(test, feature(test))] +extern crate ordermap; + pub use self::serialize::{Decoder, Encoder, Decodable, Encodable}; pub use self::serialize::{SpecializationError, SpecializedEncoder, SpecializedDecoder}; diff --git a/src/ordermap/Cargo.toml b/src/ordermap/Cargo.toml new file mode 100644 index 0000000000000..6acd9cd1dc294 --- /dev/null +++ b/src/ordermap/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ordermap" +version = "0.0.0" + +[lib] +name = "ordermap" +path = "lib.rs" +crate-type = ["dylib"] diff --git a/src/ordermap/equivalent.rs b/src/ordermap/equivalent.rs new file mode 100644 index 0000000000000..d72b2ef3a202c --- /dev/null +++ b/src/ordermap/equivalent.rs @@ -0,0 +1,27 @@ + +use std::borrow::Borrow; + +/// Key equivalence trait. +/// +/// This trait allows hash table lookup to be customized. +/// It has one blanket implementation that uses the regular `Borrow` solution, +/// just like `HashMap` and `BTreeMap` do, so that you can pass `&str` to lookup +/// into a map with `String` keys and so on. +/// +/// # Contract +/// +/// The implementor **must** hash like `K`, if it is hashable. +pub trait Equivalent { + /// Compare self to `key` and return `true` if they are equal. + fn equivalent(&self, key: &K) -> bool; +} + +impl Equivalent for Q + where Q: Eq, + K: Borrow, +{ + #[inline] + fn equivalent(&self, key: &K) -> bool { + *self == *key.borrow() + } +} diff --git a/src/ordermap/lib.rs b/src/ordermap/lib.rs new file mode 100644 index 0000000000000..96813fe1f5e8a --- /dev/null +++ b/src/ordermap/lib.rs @@ -0,0 +1,1778 @@ + +// #![doc(html_root_url = "https://docs.rs/ordermap/0.3/")] + +//! [`OrderMap`] is a hash table where the iteration order of the key-value +//! pairs is independent of the hash values of the keys. +//! +//! [`OrderMap`]: struct.OrderMap.html + +mod macros; +mod util; +mod equivalent; +mod mutable_keys; + +use std::hash::Hash; +use std::hash::BuildHasher; +use std::hash::Hasher; +use std::collections::hash_map::RandomState; +use std::ops::RangeFull; + +use std::cmp::{max, Ordering}; +use std::fmt; +use std::mem::{replace}; +use std::marker::PhantomData; + +use self::util::{third, ptrdistance, enumerate}; +pub use self::equivalent::Equivalent; +pub use self::mutable_keys::MutableKeys; + +fn hash_elem_using(build: &B, k: &K) -> HashValue { + let mut h = build.build_hasher(); + k.hash(&mut h); + HashValue(h.finish() as usize) +} + +/// Hash value newtype. Not larger than usize, since anything larger +/// isn't used for selecting position anyway. +#[derive(Copy, Debug)] +struct HashValue(usize); + +impl Clone for HashValue { + #[inline] + fn clone(&self) -> Self { *self } +} +impl PartialEq for HashValue { + #[inline] + fn eq(&self, rhs: &Self) -> bool { + self.0 == rhs.0 + } +} + +/// A possibly truncated hash value. +/// +#[derive(Debug)] +struct ShortHash(usize, PhantomData); + +impl ShortHash { + /// Pretend this is a full HashValue, which + /// is completely ok w.r.t determining bucket index + /// + /// - Sz = u32: 32-bit hash is enough to select bucket index + /// - Sz = u64: hash is not truncated + fn into_hash(self) -> HashValue { + HashValue(self.0) + } +} + +impl Copy for ShortHash { } +impl Clone for ShortHash { + #[inline] + fn clone(&self) -> Self { *self } +} + +impl PartialEq for ShortHash { + #[inline] + fn eq(&self, rhs: &Self) -> bool { + self.0 == rhs.0 + } +} + +// Compare ShortHash == HashValue by truncating appropriately +// if applicable before the comparison +impl PartialEq for ShortHash where Sz: Size { + #[inline] + fn eq(&self, rhs: &HashValue) -> bool { + if Sz::is_64_bit() { + self.0 == rhs.0 + } else { + lo32(self.0 as u64) == lo32(rhs.0 as u64) + } + } +} +impl From> for HashValue { + fn from(x: ShortHash) -> Self { HashValue(x.0) } +} + +/// `Pos` is stored in the `indices` array and it points to the index of a +/// `Bucket` in self.entries. +/// +/// Pos can be interpreted either as a 64-bit index, or as a 32-bit index and +/// a 32-bit hash. +/// +/// Storing the truncated hash next to the index saves loading the hash from the +/// entry, increasing the cache efficiency. +/// +/// Note that the lower 32 bits of the hash is enough to compute desired +/// position and probe distance in a hash map with less than 2**32 buckets. +/// +/// The OrderMap will simply query its **current raw capacity** to see what its +/// current size class is, and dispatch to the 32-bit or 64-bit lookup code as +/// appropriate. Only the growth code needs some extra logic to handle the +/// transition from one class to another +#[derive(Copy)] +struct Pos { + index: u64, +} + +impl Clone for Pos { + #[inline(always)] + fn clone(&self) -> Self { *self } +} + +impl fmt::Debug for Pos { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.pos() { + Some(i) => write!(f, "Pos({} / {:x})", i, self.index), + None => write!(f, "Pos(None)"), + } + } +} + +impl Pos { + #[inline] + fn none() -> Self { Pos { index: !0 } } + + #[inline] + fn is_none(&self) -> bool { self.index == !0 } + + #[inline] + fn pos(&self) -> Option { + if self.index == !0 { None } else { Some(lo32(self.index as u64)) } + } + + #[inline] + fn with_hash(i: usize, hash: HashValue) -> Self + where Sz: Size + { + if Sz::is_64_bit() { + Pos { + index: i as u64, + } + } else { + Pos { + index: i as u64 | ((hash.0 as u64) << 32) + } + } + } + + #[inline] + fn resolve(&self) -> Option<(usize, ShortHashProxy)> + where Sz: Size + { + if Sz::is_64_bit() { + if !self.is_none() { + Some((self.index as usize, ShortHashProxy::new(0))) + } else { + None + } + } else { + if !self.is_none() { + let (i, hash) = split_lo_hi(self.index); + Some((i as usize, ShortHashProxy::new(hash as usize))) + } else { + None + } + } + } +} + +#[inline] +fn lo32(x: u64) -> usize { (x & 0xFFFF_FFFF) as usize } + +// split into low, hi parts +#[inline] +fn split_lo_hi(x: u64) -> (u32, u32) { (x as u32, (x >> 32) as u32) } + +// Possibly contains the truncated hash value for an entry, depending on +// the size class. +struct ShortHashProxy(usize, PhantomData); + +impl ShortHashProxy + where Sz: Size +{ + fn new(x: usize) -> Self { + ShortHashProxy(x, PhantomData) + } + + /// Get the hash from either `self` or from a lookup into `entries`, + /// depending on `Sz`. + fn get_short_hash(&self, entries: &[Bucket], index: usize) -> ShortHash { + if Sz::is_64_bit() { + ShortHash(entries[index].hash.0, PhantomData) + } else { + ShortHash(self.0, PhantomData) + } + } +} + +/// A hash table where the iteration order of the key-value pairs is independent +/// of the hash values of the keys. +/// +/// The interface is closely compatible with the standard `HashMap`, but also +/// has additional features. +/// +/// # Order +/// +/// The key-value pairs have a consistent order that is determined by +/// the sequence of insertion and removal calls on the map. The order does +/// not depend on the keys or the hash function at all. +/// +/// All iterators traverse the map in *the order*. +/// +/// # Indices +/// +/// The key-value pairs are indexed in a compact range without holes in the +/// range `0..self.len()`. For example, the method `.get_full` looks up the +/// index for a key, and the method `.get_index` looks up the key-value pair by +/// index. +/// +/// # Examples +/// +/// ``` +/// use ordermap::OrderMap; +/// +/// // count the frequency of each letter in a sentence. +/// let mut letters = OrderMap::new(); +/// for ch in "a short treatise on fungi".chars() { +/// *letters.entry(ch).or_insert(0) += 1; +/// } +/// +/// assert_eq!(letters[&'s'], 2); +/// assert_eq!(letters[&'t'], 3); +/// assert_eq!(letters[&'u'], 1); +/// assert_eq!(letters.get(&'y'), None); +/// ``` +#[derive(Clone)] +pub struct OrderMap { + mask: usize, + /// indices are the buckets. indices.len() == raw capacity + indices: Box<[Pos]>, + /// entries is a dense vec of entries in their order. entries.len() == len + entries: Vec>, + hash_builder: S, +} + +#[derive(Copy, Clone, Debug)] +struct Bucket { + hash: HashValue, + key: K, + value: V, +} + +#[inline(always)] +fn desired_pos(mask: usize, hash: HashValue) -> usize { + hash.0 & mask +} + +/// The number of steps that `current` is forward of the desired position for hash +#[inline(always)] +fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize { + current.wrapping_sub(desired_pos(mask, hash)) & mask +} + +enum Inserted { + Done, + Swapped { prev_value: V }, + RobinHood { + probe: usize, + old_pos: Pos, + } +} + +impl fmt::Debug for OrderMap + where K: fmt::Debug + Hash + Eq, + V: fmt::Debug, + S: BuildHasher, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(f.debug_map().entries(self.iter()).finish()); + try!(writeln!(f, "")); + for (i, index) in enumerate(&*self.indices) { + try!(write!(f, "{}: {:?}", i, index)); + if let Some(pos) = index.pos() { + let hash = self.entries[pos].hash; + let key = &self.entries[pos].key; + let desire = desired_pos(self.mask, hash); + try!(writeln!(f, ", desired={}, probe_distance={}, key={:?}", + desire, + probe_distance(self.mask, hash, i), + key)); + } + try!(writeln!(f, "")); + } + try!(writeln!(f, "cap={}, raw_cap={}, entries.cap={}", + self.capacity(), + self.raw_capacity(), + self.entries.capacity())); + Ok(()) + } +} + +#[inline] +fn usable_capacity(cap: usize) -> usize { + cap - cap / 4 +} + +#[inline] +fn to_raw_capacity(n: usize) -> usize { + n + n / 3 +} + +// this could not be captured in an efficient iterator +macro_rules! probe_loop { + ($probe_var: ident < $len: expr, $body: expr) => { + loop { + if $probe_var < $len { + $body + $probe_var += 1; + } else { + $probe_var = 0; + } + } + } +} + +impl OrderMap { + /// Create a new map. (Does not allocate.) + pub fn new() -> Self { + Self::with_capacity(0) + } + + /// Create a new map with capacity for `n` key-value pairs. (Does not + /// allocate if `n` is zero.) + /// + /// Computes in **O(n)** time. + pub fn with_capacity(n: usize) -> Self { + Self::with_capacity_and_hasher(n, <_>::default()) + } +} + +impl OrderMap +{ + /// Create a new map with capacity for `n` key-value pairs. (Does not + /// allocate if `n` is zero.) + /// + /// Computes in **O(n)** time. + pub fn with_capacity_and_hasher(n: usize, hash_builder: S) -> Self + where S: BuildHasher + { + if n == 0 { + OrderMap { + mask: 0, + indices: Box::new([]), + entries: Vec::new(), + hash_builder: hash_builder, + } + } else { + let raw = to_raw_capacity(n); + let raw_cap = max(raw.next_power_of_two(), 8); + OrderMap { + mask: raw_cap.wrapping_sub(1), + indices: vec![Pos::none(); raw_cap].into_boxed_slice(), + entries: Vec::with_capacity(usable_capacity(raw_cap)), + hash_builder: hash_builder, + } + } + } + + /// Return the number of key-value pairs in the map. + /// + /// Computes in **O(1)** time. + pub fn len(&self) -> usize { self.entries.len() } + + /// Returns true if the map contains no elements. + /// + /// Computes in **O(1)** time. + pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Create a new map with `hash_builder` + pub fn with_hasher(hash_builder: S) -> Self + where S: BuildHasher + { + Self::with_capacity_and_hasher(0, hash_builder) + } + + /// Return a reference to the map's `BuildHasher`. + pub fn hasher(&self) -> &S + where S: BuildHasher + { + &self.hash_builder + } + + // Return whether we need 32 or 64 bits to specify a bucket or entry index + // #[cfg(not(feature = "test_low_transition_point"))] + fn size_class_is_64bit(&self) -> bool { + usize::max_value() > u32::max_value() as usize && + self.raw_capacity() >= u32::max_value() as usize + } + + // for testing + // #[cfg(feature = "test_low_transition_point")] + // fn size_class_is_64bit(&self) -> bool { + // self.raw_capacity() >= 64 + // } + + #[inline(always)] + fn raw_capacity(&self) -> usize { + self.indices.len() + } + + /// Computes in **O(1)** time. + pub fn capacity(&self) -> usize { + usable_capacity(self.raw_capacity()) + } +} + +/// Trait for the "size class". Either u32 or u64 depending on the index +/// size needed to address an entry's indes in self.entries. +trait Size { + fn is_64_bit() -> bool; + fn is_same_size() -> bool { + Self::is_64_bit() == T::is_64_bit() + } +} + +impl Size for u32 { + #[inline] + fn is_64_bit() -> bool { false } +} + +impl Size for u64 { + #[inline] + fn is_64_bit() -> bool { true } +} + +/// Call self.method(args) with `::` or `::` depending on `self` +/// size class. +/// +/// The u32 or u64 is *prepended* to the type parameter list! +macro_rules! dispatch_32_vs_64 { + ($self_:ident . $method:ident::<$($t:ty),*>($($arg:expr),*)) => { + if $self_.size_class_is_64bit() { + $self_.$method::($($arg),*) + } else { + $self_.$method::($($arg),*) + } + }; + ($self_:ident . $method:ident ($($arg:expr),*)) => { + if $self_.size_class_is_64bit() { + $self_.$method::($($arg),*) + } else { + $self_.$method::($($arg),*) + } + }; +} + +/// FIXME: Remove dependence on the `S` parameter +/// (to match HashMap). +pub enum Entry<'a, K: 'a, V: 'a, S: 'a = RandomState> { + Occupied(OccupiedEntry<'a, K, V, S>), + Vacant(VacantEntry<'a, K, V, S>), +} + +impl<'a, K, V, S> Entry<'a, K, V, S> { + /// Computes in **O(1)** time (amortized average). + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default), + } + } + + /// Computes in **O(1)** time (amortized average). + pub fn or_insert_with(self, call: F) -> &'a mut V + where F: FnOnce() -> V, + { + match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(call()), + } + } + + pub fn key(&self) -> &K { + match *self { + Entry::Occupied(ref entry) => entry.key(), + Entry::Vacant(ref entry) => entry.key(), + } + } + + pub fn index(&self) -> usize { + match *self { + Entry::Occupied(ref entry) => entry.index(), + Entry::Vacant(ref entry) => entry.index(), + } + } +} + +pub struct OccupiedEntry<'a, K: 'a, V: 'a, S: 'a = RandomState> { + map: &'a mut OrderMap, + key: K, + probe: usize, + index: usize, +} + +impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> { + pub fn key(&self) -> &K { &self.key } + pub fn get(&self) -> &V { + &self.map.entries[self.index].value + } + pub fn get_mut(&mut self) -> &mut V { + &mut self.map.entries[self.index].value + } + + pub fn index(&self) -> usize { + self.index + } + pub fn into_mut(self) -> &'a mut V { + &mut self.map.entries[self.index].value + } + + pub fn insert(self, value: V) -> V { + replace(&mut self.into_mut(), value) + } + + pub fn remove(self) -> V { + self.remove_entry().1 + } + + /// Remove and return the key, value pair stored in the map for this entry + pub fn remove_entry(self) -> (K, V) { + self.map.remove_found(self.probe, self.index) + } +} + + +pub struct VacantEntry<'a, K: 'a, V: 'a, S: 'a = RandomState> { + map: &'a mut OrderMap, + key: K, + hash: HashValue, + probe: usize, +} + +impl<'a, K, V, S> VacantEntry<'a, K, V, S> { + pub fn key(&self) -> &K { &self.key } + pub fn into_key(self) -> K { self.key } + pub fn index(&self) -> usize { self.map.len() } + pub fn insert(self, value: V) -> &'a mut V { + if self.map.size_class_is_64bit() { + self.insert_impl::(value) + } else { + self.insert_impl::(value) + } + } + + fn insert_impl(self, value: V) -> &'a mut V + where Sz: Size + { + let index = self.map.entries.len(); + self.map.entries.push(Bucket { hash: self.hash, key: self.key, value: value }); + let old_pos = Pos::with_hash::(index, self.hash); + self.map.insert_phase_2::(self.probe, old_pos); + &mut {self.map}.entries[index].value + } +} + +impl OrderMap + where K: Hash + Eq, + S: BuildHasher, +{ + /// Get the given key’s corresponding entry in the map for in-place manipulation. + /// + /// Computes in **O(1)** time (amortized average). + pub fn entry(&mut self, key: K) -> Entry { + self.reserve_one(); + dispatch_32_vs_64!(self.entry_phase_1(key)) + } + + // Warning, this is a code duplication zone Entry is not yet finished + fn entry_phase_1(&mut self, key: K) -> Entry + where Sz: Size + { + let hash = hash_elem_using(&self.hash_builder, &key); + let mut probe = desired_pos(self.mask, hash); + let mut dist = 0; + debug_assert!(self.len() < self.raw_capacity()); + probe_loop!(probe < self.indices.len(), { + if let Some((i, hash_proxy)) = self.indices[probe].resolve::() { + let entry_hash = hash_proxy.get_short_hash(&self.entries, i); + // if existing element probed less than us, swap + let their_dist = probe_distance(self.mask, entry_hash.into_hash(), probe); + if their_dist < dist { + // robin hood: steal the spot if it's better for us + return Entry::Vacant(VacantEntry { + map: self, + hash: hash, + key: key, + probe: probe, + }); + } else if entry_hash == hash && self.entries[i].key == key { + return Entry::Occupied(OccupiedEntry { + map: self, + key: key, + probe: probe, + index: i, + }); + } + } else { + // empty bucket, insert here + return Entry::Vacant(VacantEntry { + map: self, + hash: hash, + key: key, + probe: probe, + }); + } + dist += 1; + }); + } + + /// Remove all key-value pairs in the map, while preserving its capacity. + /// + /// Computes in **O(n)** time. + pub fn clear(&mut self) { + self.entries.clear(); + for pos in &mut *self.indices { + *pos = Pos::none(); + } + } + + /// FIXME Not implemented fully yet + pub fn reserve(&mut self, additional: usize) { + if additional > 0 { + self.reserve_one(); + } + } + + // First phase: Look for the preferred location for key. + // + // We will know if `key` is already in the map, before we need to insert it. + // When we insert they key, it might be that we need to continue displacing + // entries (robin hood hashing), in which case Inserted::RobinHood is returned + fn insert_phase_1(&mut self, key: K, value: V) -> Inserted + where Sz: Size + { + let hash = hash_elem_using(&self.hash_builder, &key); + let mut probe = desired_pos(self.mask, hash); + let mut dist = 0; + let insert_kind; + debug_assert!(self.len() < self.raw_capacity()); + probe_loop!(probe < self.indices.len(), { + let pos = &mut self.indices[probe]; + if let Some((i, hash_proxy)) = pos.resolve::() { + let entry_hash = hash_proxy.get_short_hash(&self.entries, i); + // if existing element probed less than us, swap + let their_dist = probe_distance(self.mask, entry_hash.into_hash(), probe); + if their_dist < dist { + // robin hood: steal the spot if it's better for us + let index = self.entries.len(); + insert_kind = Inserted::RobinHood { + probe: probe, + old_pos: Pos::with_hash::(index, hash), + }; + break; + } else if entry_hash == hash && self.entries[i].key == key { + return Inserted::Swapped { + prev_value: replace(&mut self.entries[i].value, value), + }; + } + } else { + // empty bucket, insert here + let index = self.entries.len(); + *pos = Pos::with_hash::(index, hash); + insert_kind = Inserted::Done; + break; + } + dist += 1; + }); + self.entries.push(Bucket { hash: hash, key: key, value: value }); + insert_kind + } + + fn first_allocation(&mut self) { + debug_assert_eq!(self.len(), 0); + let raw_cap = 8usize; + self.mask = raw_cap.wrapping_sub(1); + self.indices = vec![Pos::none(); raw_cap].into_boxed_slice(); + self.entries = Vec::with_capacity(usable_capacity(raw_cap)); + } + + #[inline(never)] + // `Sz` is *current* Size class, before grow + fn double_capacity(&mut self) + where Sz: Size + { + debug_assert!(self.raw_capacity() == 0 || self.len() > 0); + if self.raw_capacity() == 0 { + return self.first_allocation(); + } + + // find first ideally placed element -- start of cluster + let mut first_ideal = 0; + for (i, index) in enumerate(&*self.indices) { + if let Some(pos) = index.pos() { + if 0 == probe_distance(self.mask, self.entries[pos].hash, i) { + first_ideal = i; + break; + } + } + } + + // visit the entries in an order where we can simply reinsert them + // into self.indices without any bucket stealing. + let new_raw_cap = self.indices.len() * 2; + let old_indices = replace(&mut self.indices, vec![Pos::none(); new_raw_cap].into_boxed_slice()); + self.mask = new_raw_cap.wrapping_sub(1); + + // `Sz` is the old size class, and either u32 or u64 is the new + for &pos in &old_indices[first_ideal..] { + dispatch_32_vs_64!(self.reinsert_entry_in_order::(pos)); + } + + for &pos in &old_indices[..first_ideal] { + dispatch_32_vs_64!(self.reinsert_entry_in_order::(pos)); + } + let more = self.capacity() - self.len(); + self.entries.reserve_exact(more); + } + + // write to self.indices + // read from self.entries at `pos` + // + // reinserting rewrites all `Pos` entries anyway. This handles transitioning + // from u32 to u64 size class if needed by using the two type parameters. + fn reinsert_entry_in_order(&mut self, pos: Pos) + where SzNew: Size, + SzOld: Size, + { + if let Some((i, hash_proxy)) = pos.resolve::() { + // only if the size class is conserved can we use the short hash + let entry_hash = if SzOld::is_same_size::() { + hash_proxy.get_short_hash(&self.entries, i).into_hash() + } else { + self.entries[i].hash + }; + // find first empty bucket and insert there + let mut probe = desired_pos(self.mask, entry_hash); + probe_loop!(probe < self.indices.len(), { + if let Some(_) = self.indices[probe].resolve::() { + /* nothing */ + } else { + // empty bucket, insert here + self.indices[probe] = Pos::with_hash::(i, entry_hash); + return; + } + }); + } + } + + fn reserve_one(&mut self) { + if self.len() == self.capacity() { + dispatch_32_vs_64!(self.double_capacity()); + } + } + + /// Insert they key-value pair into the map. + /// + /// If a value already existed for `key`, that old value is returned + /// in `Some`; otherwise, return `None`. + /// + /// Computes in **O(1)** time (amortized average). + pub fn insert(&mut self, key: K, value: V) -> Option { + self.reserve_one(); + if self.size_class_is_64bit() { + match self.insert_phase_1::(key, value) { + Inserted::Swapped { prev_value } => Some(prev_value), + Inserted::Done => None, + Inserted::RobinHood { probe, old_pos } => { + self.insert_phase_2::(probe, old_pos); + None + } + } + } else { + match self.insert_phase_1::(key, value) { + Inserted::Swapped { prev_value } => Some(prev_value), + Inserted::Done => None, + Inserted::RobinHood { probe, old_pos } => { + self.insert_phase_2::(probe, old_pos); + None + } + } + } + } + + /// Return an iterator over the key-value pairs of the map, in their order + pub fn iter(&self) -> Iter { + Iter { + iter: self.entries.iter() + } + } + + /// Return an iterator over the key-value pairs of the map, in their order + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + iter: self.entries.iter_mut() + } + } + + /// Return an iterator over the keys of the map, in their order + pub fn keys(&self) -> Keys { + Keys { + iter: self.entries.iter() + } + } + + /// Return an iterator over the values of the map, in their order + pub fn values(&self) -> Values { + Values { + iter: self.entries.iter() + } + } + + /// Return an iterator over mutable references to the the values of the map, + /// in their order + pub fn values_mut(&mut self) -> ValuesMut { + ValuesMut { + iter: self.entries.iter_mut() + } + } + + /// Return `true` if and equivalent to `key` exists in the map. + /// + /// Computes in **O(1)** time (average). + pub fn contains_key(&self, key: &Q) -> bool + where Q: Hash + Equivalent, + { + self.find(key).is_some() + } + + /// Return a reference to the value stored for `key`, if it is present, + /// else `None`. + /// + /// Computes in **O(1)** time (average). + pub fn get(&self, key: &Q) -> Option<&V> + where Q: Hash + Equivalent, + { + self.get_full(key).map(third) + } + + /// Return item index, key and value + pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> + where Q: Hash + Equivalent, + { + if let Some((_, found)) = self.find(key) { + let entry = &self.entries[found]; + Some((found, &entry.key, &entry.value)) + } else { + None + } + } + + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where Q: Hash + Equivalent, + { + self.get_full_mut(key).map(third) + } + + pub fn get_full_mut(&mut self, key: &Q) + -> Option<(usize, &K, &mut V)> + where Q: Hash + Equivalent, + { + self.get_full_mut2(key).map(|(i, k, v)| (i, &*k, v)) + } + + /// Return probe (indices) and position (entries) + fn find(&self, key: &Q) -> Option<(usize, usize)> + where Q: Hash + Equivalent, + { + if self.len() == 0 { return None; } + let h = hash_elem_using(&self.hash_builder, key); + self.find_using(h, move |entry| { Q::equivalent(key, &entry.key) }) + } + + /// FIXME Same as .swap_remove + /// + /// Computes in **O(1)** time (average). + pub fn remove(&mut self, key: &Q) -> Option + where Q: Hash + Equivalent, + { + self.swap_remove(key) + } + + /// Remove the key-value pair equivalent to `key` and return + /// its value. + /// + /// Like `Vec::swap_remove`, the pair is removed by swapping it with the + /// last element of the map and popping it off. **This perturbs + /// the postion of what used to be the last element!** + /// + /// Return `None` if `key` is not in map. + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove(&mut self, key: &Q) -> Option + where Q: Hash + Equivalent, + { + self.swap_remove_full(key).map(third) + } + + /// Remove the key-value pair equivalent to `key` and return it and + /// the index it had. + /// + /// Like `Vec::swap_remove`, the pair is removed by swapping it with the + /// last element of the map and popping it off. **This perturbs + /// the postion of what used to be the last element!** + /// + /// Return `None` if `key` is not in map. + pub fn swap_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> + where Q: Hash + Equivalent, + { + let (probe, found) = match self.find(key) { + None => return None, + Some(t) => t, + }; + let (k, v) = self.remove_found(probe, found); + Some((found, k, v)) + } + + /// Remove the last key-value pair + /// + /// Computes in **O(1)** time (average). + pub fn pop(&mut self) -> Option<(K, V)> { + self.pop_impl() + } + + /// Scan through each key-value pair in the map and keep those where the + /// closure `keep` returns `true`. + /// + /// The order the elements are visited is not specified. + /// + /// Computes in **O(n)** time (average). + pub fn retain(&mut self, mut keep: F) + where F: FnMut(&K, &mut V) -> bool, + { + self.retain2(move |k, v| keep(&*k, v)) + } + + /// Sort the key-value pairs of the map and return a by value iterator of + /// the key-value pairs with the result. + /// + /// The sort is stable. + pub fn sorted_by(mut self, mut cmp: F) -> IntoIter + where F: FnMut(&K, &V, &K, &V) -> Ordering + { + self.entries.sort_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)); + self.into_iter() + } + /// Clears the `OrderMap`, returning all key-value pairs as a drain iterator. + /// Keeps the allocated memory for reuse. + pub fn drain(&mut self, range: RangeFull) -> Drain { + for i in &mut *self.indices { + *i = Pos::none(); + } + + Drain { + inner: self.entries.drain(range), + } + } +} + +impl OrderMap { + /// Get a key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_index(&self, index: usize) -> Option<(&K, &V)> { + self.entries.get(index).map(|ent| (&ent.key, &ent.value)) + } + + /// Get a key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + pub fn get_index_mut(&mut self, index: usize) -> Option<(&mut K, &mut V)> { + self.entries.get_mut(index).map(|ent| (&mut ent.key, &mut ent.value)) + } + + /// Remove the key-value pair by index + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time (average). + pub fn swap_remove_index(&mut self, index: usize) -> Option<(K, V)> { + let (probe, found) = match self.entries.get(index) + .map(|e| self.find_existing_entry(e)) + { + None => return None, + Some(t) => t, + }; + Some(self.remove_found(probe, found)) + } +} + +// Methods that don't use any properties (Hash / Eq) of K. +// +// It's cleaner to separate them out, then the compiler checks that we are not +// using Hash + Eq at all in these methods. +// +// However, we should probably not let this show in the public API or docs. +impl OrderMap { + fn pop_impl(&mut self) -> Option<(K, V)> { + let (probe, found) = match self.entries.last() + .map(|e| self.find_existing_entry(e)) + { + None => return None, + Some(t) => t, + }; + debug_assert_eq!(found, self.entries.len() - 1); + Some(self.remove_found(probe, found)) + } + + /// phase 2 is post-insert where we forward-shift `Pos` in the indices. + fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) + where Sz: Size + { + probe_loop!(probe < self.indices.len(), { + let pos = &mut self.indices[probe]; + if pos.is_none() { + *pos = old_pos; + break; + } else { + old_pos = replace(pos, old_pos); + } + }); + } + + + /// Return probe (indices) and position (entries) + fn find_using(&self, hash: HashValue, key_eq: F) -> Option<(usize, usize)> + where F: Fn(&Bucket) -> bool, + { + dispatch_32_vs_64!(self.find_using_impl::<_>(hash, key_eq)) + } + + fn find_using_impl(&self, hash: HashValue, key_eq: F) -> Option<(usize, usize)> + where F: Fn(&Bucket) -> bool, + Sz: Size, + { + debug_assert!(self.len() > 0); + let mut probe = desired_pos(self.mask, hash); + let mut dist = 0; + probe_loop!(probe < self.indices.len(), { + if let Some((i, hash_proxy)) = self.indices[probe].resolve::() { + let entry_hash = hash_proxy.get_short_hash(&self.entries, i); + if dist > probe_distance(self.mask, entry_hash.into_hash(), probe) { + // give up when probe distance is too long + return None; + } else if entry_hash == hash && key_eq(&self.entries[i]) { + return Some((probe, i)); + } + } else { + return None; + } + dist += 1; + }); + } + + /// Find `entry` which is already placed inside self.entries; + /// return its probe and entry index. + fn find_existing_entry(&self, entry: &Bucket) -> (usize, usize) + { + dispatch_32_vs_64!(self.find_existing_entry_impl(entry)) + } + + fn find_existing_entry_impl(&self, entry: &Bucket) -> (usize, usize) + where Sz: Size, + { + debug_assert!(self.len() > 0); + let hash = entry.hash; + let actual_pos = ptrdistance(&self.entries[0], entry); + let mut probe = desired_pos(self.mask, hash); + probe_loop!(probe < self.indices.len(), { + if let Some((i, _)) = self.indices[probe].resolve::() { + if i == actual_pos { + return (probe, actual_pos); + } + } else { + debug_assert!(false, "the entry does not exist"); + } + }); + } + + fn remove_found(&mut self, probe: usize, found: usize) -> (K, V) { + dispatch_32_vs_64!(self.remove_found_impl(probe, found)) + } + + fn remove_found_impl(&mut self, probe: usize, found: usize) -> (K, V) + where Sz: Size + { + // index `probe` and entry `found` is to be removed + // use swap_remove, but then we need to update the index that points + // to the other entry that has to move + self.indices[probe] = Pos::none(); + let entry = self.entries.swap_remove(found); + + // correct index that points to the entry that had to swap places + if let Some(entry) = self.entries.get(found) { + // was not last element + // examine new element in `found` and find it in indices + let mut probe = desired_pos(self.mask, entry.hash); + probe_loop!(probe < self.indices.len(), { + if let Some((i, _)) = self.indices[probe].resolve::() { + if i >= self.entries.len() { + // found it + self.indices[probe] = Pos::with_hash::(found, entry.hash); + break; + } + } + }); + } + // backward shift deletion in self.indices + // after probe, shift all non-ideally placed indices backward + if self.len() > 0 { + let mut last_probe = probe; + let mut probe = probe + 1; + probe_loop!(probe < self.indices.len(), { + if let Some((i, hash_proxy)) = self.indices[probe].resolve::() { + let entry_hash = hash_proxy.get_short_hash(&self.entries, i); + if probe_distance(self.mask, entry_hash.into_hash(), probe) > 0 { + self.indices[last_probe] = self.indices[probe]; + self.indices[probe] = Pos::none(); + } else { + break; + } + } else { + break; + } + last_probe = probe; + }); + } + + (entry.key, entry.value) + } + +} + +use std::slice::Iter as SliceIter; +use std::slice::IterMut as SliceIterMut; +use std::vec::IntoIter as VecIntoIter; + +pub struct Keys<'a, K: 'a, V: 'a> { + iter: SliceIter<'a, Bucket>, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + fn next(&mut self) -> Option<&'a K> { + self.iter.next().map(|ent| &ent.key) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|ent| &ent.key) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { + fn next_back(&mut self) -> Option<&'a K> { + self.iter.next_back().map(|ent| &ent.key) + } +} + +impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct Values<'a, K: 'a, V: 'a> { + iter: SliceIter<'a, Bucket>, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + fn next(&mut self) -> Option { + self.iter.next().map(|ent| &ent.value) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|ent| &ent.value) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|ent| &ent.value) + } +} + +impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct ValuesMut<'a, K: 'a, V: 'a> { + iter: SliceIterMut<'a, Bucket>, +} + +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + fn next(&mut self) -> Option { + self.iter.next().map(|ent| &mut ent.value) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|ent| &mut ent.value) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|ent| &mut ent.value) + } +} + +impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct Iter<'a, K: 'a, V: 'a> { + iter: SliceIter<'a, Bucket>, +} + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + self.iter.next().map(|e| (&e.key, &e.value)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|e| (&e.key, &e.value)) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|e| (&e.key, &e.value)) + } +} + +impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct IterMut<'a, K: 'a, V: 'a> { + iter: SliceIterMut<'a, Bucket>, +} + +impl<'a, K, V> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option { + self.iter.next().map(|e| (&e.key, &mut e.value)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|e| (&e.key, &mut e.value)) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|e| (&e.key, &mut e.value)) + } +} + +impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +pub struct IntoIter { + iter: VecIntoIter>, +} + +impl Iterator for IntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option { + self.iter.next().map(|e| (e.key, e.value)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.len() + } + + fn nth(&mut self, n: usize) -> Option { + self.iter.nth(n).map(|e| (e.key, e.value)) + } + + fn last(mut self) -> Option { + self.next_back() + } +} + +impl<'a, K, V> DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|e| (e.key, e.value)) + } +} + +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.iter.len() + } +} + + +impl<'a, K, V, S> IntoIterator for &'a OrderMap + where K: Hash + Eq, + S: BuildHasher, +{ + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a mut OrderMap + where K: Hash + Eq, + S: BuildHasher, +{ + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl IntoIterator for OrderMap + where K: Hash + Eq, + S: BuildHasher, +{ + type Item = (K, V); + type IntoIter = IntoIter; + fn into_iter(self) -> Self::IntoIter { + IntoIter { + iter: self.entries.into_iter(), + } + } +} + +use std::ops::{Index, IndexMut}; + +impl<'a, K, V, Q: ?Sized, S> Index<&'a Q> for OrderMap + where Q: Hash + Equivalent, + K: Hash + Eq, + S: BuildHasher, +{ + type Output = V; + + /// ***Panics*** if `key` is not present in the map. + fn index(&self, key: &'a Q) -> &V { + if let Some(v) = self.get(key) { + v + } else { + panic!("OrderMap: key not found") + } + } +} + +/// Mutable indexing allows changing / updating values of key-value +/// pairs that are already present. +/// +/// You can **not** insert new pairs with index syntax, use `.insert()`. +impl<'a, K, V, Q: ?Sized, S> IndexMut<&'a Q> for OrderMap + where Q: Hash + Equivalent, + K: Hash + Eq, + S: BuildHasher, +{ + /// ***Panics*** if `key` is not present in the map. + fn index_mut(&mut self, key: &'a Q) -> &mut V { + if let Some(v) = self.get_mut(key) { + v + } else { + panic!("OrderMap: key not found") + } + } +} + +use std::iter::FromIterator; + +impl FromIterator<(K, V)> for OrderMap + where K: Hash + Eq, + S: BuildHasher + Default, +{ + fn from_iter>(iterable: I) -> Self { + let iter = iterable.into_iter(); + let (low, _) = iter.size_hint(); + let mut map = Self::with_capacity_and_hasher(low, <_>::default()); + for (k, v) in iter { map.insert(k, v); } + map + } +} + +impl Extend<(K, V)> for OrderMap + where K: Hash + Eq, + S: BuildHasher, +{ + fn extend>(&mut self, iterable: I) { + for (k, v) in iterable { self.insert(k, v); } + } +} + +impl<'a, K, V, S> Extend<(&'a K, &'a V)> for OrderMap + where K: Hash + Eq + Copy, + V: Copy, + S: BuildHasher, +{ + fn extend>(&mut self, iterable: I) { + self.extend(iterable.into_iter().map(|(&key, &value)| (key, value))); + } +} + +impl Default for OrderMap + where S: BuildHasher + Default, +{ + fn default() -> Self { + Self::with_capacity_and_hasher(0, S::default()) + } +} + +impl PartialEq> for OrderMap + where K: Hash + Eq, + V1: PartialEq, + S1: BuildHasher, + S2: BuildHasher +{ + fn eq(&self, other: &OrderMap) -> bool { + if self.len() != other.len() { + return false; + } + + self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) + } +} + +impl Eq for OrderMap + where K: Eq + Hash, + V: Eq, + S: BuildHasher +{ +} + +pub struct Drain<'a, K, V> where K: 'a, V: 'a { + inner: ::std::vec::Drain<'a, Bucket> +} + +impl<'a, K, V> Iterator for Drain<'a, K, V> { + type Item = (K, V); + fn next(&mut self) -> Option { + self.inner.next().map(|bucket| (bucket.key, bucket.value)) + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use util::enumerate; +// +// #[test] +// fn it_works() { +// let mut map = OrderMap::new(); +// assert_eq!(map.is_empty(), true); +// map.insert(1, ()); +// map.insert(1, ()); +// assert_eq!(map.len(), 1); +// assert!(map.get(&1).is_some()); +// assert_eq!(map.is_empty(), false); +// } +// +// #[test] +// fn new() { +// let map = OrderMap::::new(); +// println!("{:?}", map); +// assert_eq!(map.capacity(), 0); +// assert_eq!(map.len(), 0); +// assert_eq!(map.is_empty(), true); +// } +// +// #[test] +// fn insert() { +// let insert = [0, 4, 2, 12, 8, 7, 11, 5]; +// let not_present = [1, 3, 6, 9, 10]; +// let mut map = OrderMap::with_capacity(insert.len()); +// +// for (i, &elt) in enumerate(&insert) { +// assert_eq!(map.len(), i); +// map.insert(elt, elt); +// assert_eq!(map.len(), i + 1); +// assert_eq!(map.get(&elt), Some(&elt)); +// assert_eq!(map[&elt], elt); +// } +// println!("{:?}", map); +// +// for &elt in ¬_present { +// assert!(map.get(&elt).is_none()); +// } +// } +// +// #[test] +// fn insert_2() { +// let mut map = OrderMap::with_capacity(16); +// +// let mut keys = vec![]; +// keys.extend(0..16); +// keys.extend(128..267); +// +// for &i in &keys { +// let old_map = map.clone(); +// map.insert(i, ()); +// for key in old_map.keys() { +// if !map.get(key).is_some() { +// println!("old_map: {:?}", old_map); +// println!("map: {:?}", map); +// panic!("did not find {} in map", key); +// } +// } +// } +// +// for &i in &keys { +// assert!(map.get(&i).is_some(), "did not find {}", i); +// } +// } +// +// #[test] +// fn insert_order() { +// let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; +// let mut map = OrderMap::new(); +// +// for &elt in &insert { +// map.insert(elt, ()); +// } +// +// assert_eq!(map.keys().count(), map.len()); +// assert_eq!(map.keys().count(), insert.len()); +// for (a, b) in insert.iter().zip(map.keys()) { +// assert_eq!(a, b); +// } +// for (i, k) in (0..insert.len()).zip(map.keys()) { +// assert_eq!(map.get_index(i).unwrap().0, k); +// } +// } +// +// #[test] +// fn grow() { +// let insert = [0, 4, 2, 12, 8, 7, 11]; +// let not_present = [1, 3, 6, 9, 10]; +// let mut map = OrderMap::with_capacity(insert.len()); +// +// +// for (i, &elt) in enumerate(&insert) { +// assert_eq!(map.len(), i); +// map.insert(elt, elt); +// assert_eq!(map.len(), i + 1); +// assert_eq!(map.get(&elt), Some(&elt)); +// assert_eq!(map[&elt], elt); +// } +// +// println!("{:?}", map); +// for &elt in &insert { +// map.insert(elt * 10, elt); +// } +// for &elt in &insert { +// map.insert(elt * 100, elt); +// } +// for (i, &elt) in insert.iter().cycle().enumerate().take(100) { +// map.insert(elt * 100 + i as i32, elt); +// } +// println!("{:?}", map); +// for &elt in ¬_present { +// assert!(map.get(&elt).is_none()); +// } +// } +// +// #[test] +// fn remove() { +// let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; +// let mut map = OrderMap::new(); +// +// for &elt in &insert { +// map.insert(elt, elt); +// } +// +// assert_eq!(map.keys().count(), map.len()); +// assert_eq!(map.keys().count(), insert.len()); +// for (a, b) in insert.iter().zip(map.keys()) { +// assert_eq!(a, b); +// } +// +// let remove_fail = [99, 77]; +// let remove = [4, 12, 8, 7]; +// +// for &key in &remove_fail { +// assert!(map.swap_remove_full(&key).is_none()); +// } +// println!("{:?}", map); +// for &key in &remove { +// //println!("{:?}", map); +// let index = map.get_full(&key).unwrap().0; +// assert_eq!(map.swap_remove_full(&key), Some((index, key, key))); +// } +// println!("{:?}", map); +// +// for key in &insert { +// assert_eq!(map.get(key).is_some(), !remove.contains(key)); +// } +// assert_eq!(map.len(), insert.len() - remove.len()); +// assert_eq!(map.keys().count(), insert.len() - remove.len()); +// } +// +// #[test] +// fn swap_remove_index() { +// let insert = [0, 4, 2, 12, 8, 7, 11, 5, 3, 17, 19, 22, 23]; +// let mut map = OrderMap::new(); +// +// for &elt in &insert { +// map.insert(elt, elt * 2); +// } +// +// let mut vector = insert.to_vec(); +// let remove_sequence = &[3, 3, 10, 4, 5, 4, 3, 0, 1]; +// +// // check that the same swap remove sequence on vec and map +// // have the same result. +// for &rm in remove_sequence { +// let out_vec = vector.swap_remove(rm); +// let (out_map, _) = map.swap_remove_index(rm).unwrap(); +// assert_eq!(out_vec, out_map); +// } +// assert_eq!(vector.len(), map.len()); +// for (a, b) in vector.iter().zip(map.keys()) { +// assert_eq!(a, b); +// } +// } +// +// #[test] +// fn partial_eq_and_eq() { +// let mut map_a = OrderMap::new(); +// map_a.insert(1, "1"); +// map_a.insert(2, "2"); +// let mut map_b = map_a.clone(); +// assert_eq!(map_a, map_b); +// map_b.remove(&1); +// assert_ne!(map_a, map_b); +// +// let map_c: OrderMap<_, String> = map_b.into_iter().map(|(k, v)| (k, v.to_owned())).collect(); +// assert_ne!(map_a, map_c); +// assert_ne!(map_c, map_a); +// } +// +// #[test] +// fn extend() { +// let mut map = OrderMap::new(); +// map.extend(vec![(&1, &2), (&3, &4)]); +// map.extend(vec![(5, 6)]); +// assert_eq!(map.into_iter().collect::>(), vec![(1, 2), (3, 4), (5, 6)]); +// } +// +// #[test] +// fn entry() { +// let mut map = OrderMap::new(); +// +// map.insert(1, "1"); +// map.insert(2, "2"); +// { +// let e = map.entry(3); +// assert_eq!(e.index(), 2); +// let e = e.or_insert("3"); +// assert_eq!(e, &"3"); +// } +// +// let e = map.entry(2); +// assert_eq!(e.index(), 1); +// assert_eq!(e.key(), &2); +// match e { +// Entry::Occupied(ref e) => assert_eq!(e.get(), &"2"), +// Entry::Vacant(_) => panic!() +// } +// assert_eq!(e.or_insert("4"), &"2"); +// } +// } diff --git a/src/ordermap/macros.rs b/src/ordermap/macros.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/src/ordermap/macros.rs @@ -0,0 +1 @@ + diff --git a/src/ordermap/mutable_keys.rs b/src/ordermap/mutable_keys.rs new file mode 100644 index 0000000000000..389014dcf73cc --- /dev/null +++ b/src/ordermap/mutable_keys.rs @@ -0,0 +1,88 @@ + +use std::hash::Hash; +use std::hash::BuildHasher; + +use super::{OrderMap, Equivalent}; + +pub struct PrivateMarker { } + +/// Opt-in mutable access to keys. +/// +/// These methods expose `&mut K`, mutable references to the key as it is stored +/// in the map. +/// You are allowed to modify the keys in the hashmap **if the modifcation +/// does not change the key’s hash and equality**. +/// +/// If keys are modified erronously, you can no longer look them up. +/// This is sound (memory safe) but a logical error hazard (just like +/// implementing PartialEq, Eq, or Hash incorrectly would be). +/// +/// `use` this trait to enable its methods for `OrderMap`. +pub trait MutableKeys { + type Key; + type Value; + + /// Return item index, mutable reference to key and value + fn get_full_mut2(&mut self, key: &Q) + -> Option<(usize, &mut Self::Key, &mut Self::Value)> + where Q: Hash + Equivalent; + + /// Scan through each key-value pair in the map and keep those where the + /// closure `keep` returns `true`. + /// + /// The order the elements are visited is not specified. + /// + /// Computes in **O(n)** time (average). + fn retain2(&mut self, keep: F) + where F: FnMut(&mut Self::Key, &mut Self::Value) -> bool; + + /// This method is not useful in itself – it is there to “seal” the trait + /// for external implementation, so that we can add methods without + /// causing breaking changes. + fn __private_marker(&self) -> PrivateMarker; +} + +/// Opt-in mutable access to keys. +/// +/// See [`MutableKeys`](trait.MutableKeys.html) for more information. +impl MutableKeys for OrderMap + where K: Eq + Hash, + S: BuildHasher, +{ + type Key = K; + type Value = V; + fn get_full_mut2(&mut self, key: &Q) + -> Option<(usize, &mut K, &mut V)> + where Q: Hash + Equivalent, + { + if let Some((_, found)) = self.find(key) { + let entry = &mut self.entries[found]; + Some((found, &mut entry.key, &mut entry.value)) + } else { + None + } + } + + fn retain2(&mut self, mut keep: F) + where F: FnMut(&mut K, &mut V) -> bool, + { + // We can use either forward or reverse scan, but forward was + // faster in a microbenchmark + let mut i = 0; + while i < self.len() { + { + let entry = &mut self.entries[i]; + if keep(&mut entry.key, &mut entry.value) { + i += 1; + continue; + } + } + self.swap_remove_index(i); + // skip increment on remove + } + } + + fn __private_marker(&self) -> PrivateMarker { + PrivateMarker { } + } +} diff --git a/src/ordermap/serde.rs b/src/ordermap/serde.rs new file mode 100644 index 0000000000000..3dc32b606511d --- /dev/null +++ b/src/ordermap/serde.rs @@ -0,0 +1,67 @@ + +extern crate serde; + +use self::serde::ser::{Serialize, Serializer, SerializeMap}; +use self::serde::de::{Deserialize, Deserializer, MapAccess, Visitor}; + +use std::fmt::{self, Formatter}; +use std::hash::{BuildHasher, Hash}; +use std::marker::PhantomData; + +use OrderMap; + +/// Requires crate feature `"serde-1"` +impl Serialize for OrderMap + where K: Serialize + Hash + Eq, + V: Serialize, + S: BuildHasher +{ + fn serialize(&self, serializer: T) -> Result + where T: Serializer + { + let mut map_serializer = try!(serializer.serialize_map(Some(self.len()))); + for (key, value) in self { + try!(map_serializer.serialize_entry(key, value)); + } + map_serializer.end() + } +} + +struct OrderMapVisitor(PhantomData<(K, V, S)>); + +impl<'de, K, V, S> Visitor<'de> for OrderMapVisitor + where K: Deserialize<'de> + Eq + Hash, + V: Deserialize<'de>, + S: Default + BuildHasher +{ + type Value = OrderMap; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "a map") + } + + fn visit_map(self, mut map: A) -> Result + where A: MapAccess<'de> + { + let mut values = OrderMap::with_capacity_and_hasher(map.size_hint().unwrap_or(0), Default::default()); + + while let Some((key, value)) = try!(map.next_entry()) { + values.insert(key, value); + } + + Ok(values) + } +} + +/// Requires crate feature `"serde-1"` +impl<'de, K, V, S> Deserialize<'de> for OrderMap + where K: Deserialize<'de> + Eq + Hash, + V: Deserialize<'de>, + S: Default + BuildHasher +{ + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + deserializer.deserialize_map(OrderMapVisitor(PhantomData)) + } +} diff --git a/src/ordermap/util.rs b/src/ordermap/util.rs new file mode 100644 index 0000000000000..90d3e7e334919 --- /dev/null +++ b/src/ordermap/util.rs @@ -0,0 +1,17 @@ + +use std::iter::Enumerate; +use std::mem::size_of; + +pub fn third(t: (A, B, C)) -> C { t.2 } + +pub fn enumerate(iterable: I) -> Enumerate + where I: IntoIterator +{ + iterable.into_iter().enumerate() +} + +/// return the number of steps from a to b +pub fn ptrdistance(a: *const T, b: *const T) -> usize { + debug_assert!(a as usize <= b as usize); + (b as usize - a as usize) / size_of::() +}