auto merge of #11064 : huonw/rust/vec-sort, r=alexcrichton
This uses quite a bit of unsafe code for speed and failure safety, and allocates `2*n` temporary storage. [Performance](https://gist.github.com/huonw/5547f2478380288a28c2): | n | new | priority_queue | quick3 | |-------:|---------:|---------------:|---------:| | 5 | 200 | 155 | 106 | | 100 | 6490 | 8750 | 5810 | | 10000 | 1300000 | 1790000 | 1060000 | | 100000 | 16700000 | 23600000 | 12700000 | | sorted | 520000 | 1380000 | 53900000 | | trend | 1310000 | 1690000 | 1100000 | (The times are in nanoseconds, having subtracted the set-up time (i.e. the `just_generate` bench target).) I imagine that there is still significant room for improvement, particularly because both priority_queue and quick3 are doing a static call via `Ord` or `TotalOrd` for the comparisons, while this is using a (boxed) closure. Also, this code does not `clone`, unlike `quick_sort3`; and is stable, unlike both of the others.
This commit is contained in:
commit
55cbef611a
19 changed files with 493 additions and 1303 deletions
|
@ -28,8 +28,6 @@ use std::io;
|
|||
use std::io::fs;
|
||||
use std::path::is_sep;
|
||||
|
||||
use sort;
|
||||
|
||||
/**
|
||||
* An iterator that yields Paths from the filesystem that match a particular
|
||||
* pattern - see the `glob` function for more details.
|
||||
|
@ -149,9 +147,8 @@ impl Iterator<Path> for GlobIterator {
|
|||
|
||||
fn list_dir_sorted(path: &Path) -> ~[Path] {
|
||||
match io::result(|| fs::readdir(path)) {
|
||||
Ok(children) => {
|
||||
let mut children = children;
|
||||
sort::quick_sort(children, |p1, p2| p2.filename() <= p1.filename());
|
||||
Ok(mut children) => {
|
||||
children.sort_by(|p1, p2| p2.filename().cmp(&p1.filename()));
|
||||
children
|
||||
}
|
||||
Err(..) => ~[]
|
||||
|
@ -771,4 +768,3 @@ mod test {
|
|||
assert!(Pattern::new("a/b").matches_path(&Path::new("a/b")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,8 +61,6 @@ pub mod ringbuf;
|
|||
pub mod priority_queue;
|
||||
pub mod smallintmap;
|
||||
|
||||
pub mod sort;
|
||||
|
||||
pub mod dlist;
|
||||
pub mod treemap;
|
||||
pub mod btree;
|
||||
|
|
|
@ -213,7 +213,6 @@ impl<T: Ord> Extendable<T> for PriorityQueue<T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sort::merge_sort;
|
||||
use priority_queue::PriorityQueue;
|
||||
|
||||
#[test]
|
||||
|
@ -231,7 +230,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_top_and_pop() {
|
||||
let data = ~[2u, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
|
||||
let mut sorted = merge_sort(data, |x, y| x.le(y));
|
||||
let mut sorted = data.clone();
|
||||
sorted.sort();
|
||||
let mut heap = PriorityQueue::from_vec(data);
|
||||
while !heap.is_empty() {
|
||||
assert_eq!(heap.top(), sorted.last());
|
||||
|
@ -311,11 +311,14 @@ mod tests {
|
|||
assert_eq!(heap.len(), 5);
|
||||
}
|
||||
|
||||
fn check_to_vec(data: ~[int]) {
|
||||
fn check_to_vec(mut data: ~[int]) {
|
||||
let heap = PriorityQueue::from_vec(data.clone());
|
||||
assert_eq!(merge_sort(heap.clone().to_vec(), |x, y| x.le(y)),
|
||||
merge_sort(data, |x, y| x.le(y)));
|
||||
assert_eq!(heap.to_sorted_vec(), merge_sort(data, |x, y| x.le(y)));
|
||||
let mut v = heap.clone().to_vec();
|
||||
v.sort();
|
||||
data.sort();
|
||||
|
||||
assert_eq!(v, data);
|
||||
assert_eq!(heap.to_sorted_vec(), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
1181
src/libextra/sort.rs
1181
src/libextra/sort.rs
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,6 @@
|
|||
|
||||
#[allow(missing_doc)];
|
||||
|
||||
use sort;
|
||||
use std::cmp;
|
||||
use std::hashmap;
|
||||
use std::io;
|
||||
|
@ -20,6 +19,25 @@ use std::util;
|
|||
// NB: this can probably be rewritten in terms of num::Num
|
||||
// to be less f64-specific.
|
||||
|
||||
fn f64_cmp(x: f64, y: f64) -> Ordering {
|
||||
// arbitrarily decide that NaNs are larger than everything.
|
||||
if y.is_nan() {
|
||||
Less
|
||||
} else if x.is_nan() {
|
||||
Greater
|
||||
} else if x < y {
|
||||
Less
|
||||
} else if x == y {
|
||||
Equal
|
||||
} else {
|
||||
Greater
|
||||
}
|
||||
}
|
||||
|
||||
fn f64_sort(v: &mut [f64]) {
|
||||
v.sort_by(|x: &f64, y: &f64| f64_cmp(*x, *y));
|
||||
}
|
||||
|
||||
/// Trait that provides simple descriptive statistics on a univariate set of numeric samples.
|
||||
pub trait Stats {
|
||||
|
||||
|
@ -240,13 +258,13 @@ impl<'a> Stats for &'a [f64] {
|
|||
|
||||
fn percentile(self, pct: f64) -> f64 {
|
||||
let mut tmp = self.to_owned();
|
||||
sort::tim_sort(tmp);
|
||||
f64_sort(tmp);
|
||||
percentile_of_sorted(tmp, pct)
|
||||
}
|
||||
|
||||
fn quartiles(self) -> (f64,f64,f64) {
|
||||
let mut tmp = self.to_owned();
|
||||
sort::tim_sort(tmp);
|
||||
f64_sort(tmp);
|
||||
let a = percentile_of_sorted(tmp, 25.0);
|
||||
let b = percentile_of_sorted(tmp, 50.0);
|
||||
let c = percentile_of_sorted(tmp, 75.0);
|
||||
|
@ -291,7 +309,7 @@ fn percentile_of_sorted(sorted_samples: &[f64],
|
|||
/// See: http://en.wikipedia.org/wiki/Winsorising
|
||||
pub fn winsorize(samples: &mut [f64], pct: f64) {
|
||||
let mut tmp = samples.to_owned();
|
||||
sort::tim_sort(tmp);
|
||||
f64_sort(tmp);
|
||||
let lo = percentile_of_sorted(tmp, pct);
|
||||
let hi = percentile_of_sorted(tmp, 100.0-pct);
|
||||
for samp in samples.mut_iter() {
|
||||
|
|
|
@ -21,7 +21,6 @@ use getopts::groups;
|
|||
use json::ToJson;
|
||||
use json;
|
||||
use serialize::Decodable;
|
||||
use sort;
|
||||
use stats::Stats;
|
||||
use stats;
|
||||
use term;
|
||||
|
@ -38,7 +37,6 @@ use std::to_str::ToStr;
|
|||
use std::f64;
|
||||
use std::os;
|
||||
|
||||
|
||||
// The name of a test. By convention this follows the rules for rust
|
||||
// paths; i.e. it should be a series of identifiers separated by double
|
||||
// colons. This way if some test runner wants to arrange the tests
|
||||
|
@ -488,7 +486,7 @@ impl<T: Writer> ConsoleTestState<T> {
|
|||
for f in self.failures.iter() {
|
||||
failures.push(f.name.to_str());
|
||||
}
|
||||
sort::tim_sort(failures);
|
||||
failures.sort();
|
||||
for name in failures.iter() {
|
||||
self.write_plain(format!(" {}\n", name.to_str()));
|
||||
}
|
||||
|
@ -839,10 +837,7 @@ pub fn filter_tests(
|
|||
};
|
||||
|
||||
// Sort the tests alphabetically
|
||||
fn lteq(t1: &TestDescAndFn, t2: &TestDescAndFn) -> bool {
|
||||
t1.desc.name.to_str() < t2.desc.name.to_str()
|
||||
}
|
||||
sort::quick_sort(filtered, lteq);
|
||||
filtered.sort_by(|t1, t2| t1.desc.name.to_str().cmp(&t2.desc.name.to_str()));
|
||||
|
||||
// Shard the remaining tests, if sharding requested.
|
||||
match opts.test_shard {
|
||||
|
|
|
@ -151,7 +151,6 @@ Additional help:
|
|||
}
|
||||
|
||||
pub fn describe_warnings() {
|
||||
use extra::sort::Sort;
|
||||
println("
|
||||
Available lint options:
|
||||
-W <foo> Warn about <foo>
|
||||
|
@ -164,7 +163,7 @@ Available lint options:
|
|||
let mut lint_dict = lint_dict.move_iter()
|
||||
.map(|(k, v)| (v, k))
|
||||
.collect::<~[(lint::LintSpec, &'static str)]>();
|
||||
lint_dict.qsort();
|
||||
lint_dict.sort();
|
||||
|
||||
let mut max_key = 0;
|
||||
for &(_, name) in lint_dict.iter() {
|
||||
|
|
|
@ -18,7 +18,6 @@ use metadata::decoder;
|
|||
use metadata::loader;
|
||||
|
||||
use std::hashmap::HashMap;
|
||||
use extra;
|
||||
use syntax::ast;
|
||||
use syntax::parse::token::ident_interner;
|
||||
|
||||
|
@ -174,7 +173,7 @@ pub fn find_extern_mod_stmt_cnum(cstore: &CStore,
|
|||
cstore.extern_mod_crate_map.find(&emod_id).map(|x| *x)
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
#[deriving(Clone, TotalEq, TotalOrd)]
|
||||
struct crate_hash {
|
||||
name: @str,
|
||||
vers: @str,
|
||||
|
@ -198,16 +197,14 @@ pub fn get_dep_hashes(cstore: &CStore) -> ~[@str] {
|
|||
});
|
||||
}
|
||||
|
||||
let sorted = extra::sort::merge_sort(result, |a, b| {
|
||||
(a.name, a.vers, a.hash) <= (b.name, b.vers, b.hash)
|
||||
});
|
||||
result.sort();
|
||||
|
||||
debug!("sorted:");
|
||||
for x in sorted.iter() {
|
||||
for x in result.iter() {
|
||||
debug!(" hash[{}]: {}", x.name, x.hash);
|
||||
}
|
||||
|
||||
sorted.map(|ch| ch.hash)
|
||||
result.map(|ch| ch.hash)
|
||||
}
|
||||
|
||||
impl crate_metadata {
|
||||
|
|
|
@ -30,7 +30,6 @@ use std::util;
|
|||
use std::vec;
|
||||
|
||||
use extra::serialize::Encodable;
|
||||
use extra;
|
||||
|
||||
use syntax::abi::AbiSet;
|
||||
use syntax::ast::*;
|
||||
|
@ -1532,7 +1531,7 @@ fn encode_crate_deps(ecx: &EncodeContext,
|
|||
});
|
||||
|
||||
// Sort by cnum
|
||||
extra::sort::quick_sort(deps, |kv1, kv2| kv1.cnum <= kv2.cnum);
|
||||
deps.sort_by(|kv1, kv2| kv1.cnum.cmp(&kv2.cnum));
|
||||
|
||||
// Sanity-check the crate numbers
|
||||
let mut expected_cnum = 1;
|
||||
|
|
|
@ -21,7 +21,6 @@ use util::ppaux::ty_to_str;
|
|||
use std::iter;
|
||||
use std::num;
|
||||
use std::vec;
|
||||
use extra::sort;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::{unguarded_pat, walk_pat};
|
||||
use syntax::codemap::{Span, dummy_sp, Spanned};
|
||||
|
@ -454,7 +453,7 @@ fn missing_ctor(cx: &MatchCheckCtxt,
|
|||
ty::ty_unboxed_vec(..) | ty::ty_evec(..) => {
|
||||
|
||||
// Find the lengths and slices of all vector patterns.
|
||||
let vec_pat_lens = m.iter().filter_map(|r| {
|
||||
let mut vec_pat_lens = m.iter().filter_map(|r| {
|
||||
match r[0].node {
|
||||
PatVec(ref before, ref slice, ref after) => {
|
||||
Some((before.len() + after.len(), slice.is_some()))
|
||||
|
@ -465,21 +464,19 @@ fn missing_ctor(cx: &MatchCheckCtxt,
|
|||
|
||||
// Sort them by length such that for patterns of the same length,
|
||||
// those with a destructured slice come first.
|
||||
let mut sorted_vec_lens = sort::merge_sort(vec_pat_lens,
|
||||
|&(len1, slice1), &(len2, slice2)| {
|
||||
if len1 == len2 {
|
||||
slice1 > slice2
|
||||
} else {
|
||||
len1 <= len2
|
||||
}
|
||||
}
|
||||
);
|
||||
sorted_vec_lens.dedup();
|
||||
vec_pat_lens.sort_by(|&(len1, slice1), &(len2, slice2)| {
|
||||
if len1 == len2 {
|
||||
slice2.cmp(&slice1)
|
||||
} else {
|
||||
len1.cmp(&len2)
|
||||
}
|
||||
});
|
||||
vec_pat_lens.dedup();
|
||||
|
||||
let mut found_slice = false;
|
||||
let mut next = 0;
|
||||
let mut missing = None;
|
||||
for &(length, slice) in sorted_vec_lens.iter() {
|
||||
for &(length, slice) in vec_pat_lens.iter() {
|
||||
if length != next {
|
||||
missing = Some(next);
|
||||
break;
|
||||
|
|
|
@ -63,7 +63,7 @@ use syntax::{ast, ast_util, visit};
|
|||
use syntax::ast_util::IdVisitingOperation;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
|
||||
pub enum lint {
|
||||
ctypes,
|
||||
unused_imports,
|
||||
|
@ -110,20 +110,16 @@ pub fn level_to_str(lv: level) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq, Ord)]
|
||||
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
|
||||
pub enum level {
|
||||
allow, warn, deny, forbid
|
||||
}
|
||||
|
||||
#[deriving(Clone, Eq)]
|
||||
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
|
||||
pub struct LintSpec {
|
||||
default: level,
|
||||
lint: lint,
|
||||
desc: &'static str,
|
||||
default: level
|
||||
}
|
||||
|
||||
impl Ord for LintSpec {
|
||||
fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
|
||||
}
|
||||
|
||||
pub type LintDict = HashMap<&'static str, LintSpec>;
|
||||
|
|
|
@ -73,7 +73,6 @@ use std::libc::c_uint;
|
|||
use std::vec;
|
||||
use std::local_data;
|
||||
use extra::time;
|
||||
use extra::sort;
|
||||
use syntax::ast::Name;
|
||||
use syntax::ast_map::{path, path_elt_to_str, path_name, path_pretty_name};
|
||||
use syntax::ast_util::{local_def, is_local};
|
||||
|
@ -3163,10 +3162,9 @@ pub fn trans_crate(sess: session::Session,
|
|||
println!("n_inlines: {}", ccx.stats.n_inlines);
|
||||
println!("n_closures: {}", ccx.stats.n_closures);
|
||||
println("fn stats:");
|
||||
sort::quick_sort(ccx.stats.fn_stats,
|
||||
|&(_, _, insns_a), &(_, _, insns_b)| {
|
||||
insns_a > insns_b
|
||||
});
|
||||
|
||||
ccx.stats.fn_stats.sort_by(|&(_, _, insns_a), &(_, _, insns_b)| insns_b.cmp(&insns_a));
|
||||
|
||||
for tuple in ccx.stats.fn_stats.iter() {
|
||||
match *tuple {
|
||||
(ref name, ms, insns) => {
|
||||
|
|
|
@ -45,8 +45,6 @@ use std::vec;
|
|||
|
||||
use extra::arc::Arc;
|
||||
use extra::json::ToJson;
|
||||
use extra::sort;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
|
||||
|
@ -900,44 +898,44 @@ fn item_module(w: &mut Writer, cx: &Context,
|
|||
debug!("{:?}", items);
|
||||
let mut indices = vec::from_fn(items.len(), |i| i);
|
||||
|
||||
fn lt(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> bool {
|
||||
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering {
|
||||
if shortty(i1) == shortty(i2) {
|
||||
return i1.name < i2.name;
|
||||
return i1.name.cmp(&i2.name);
|
||||
}
|
||||
match (&i1.inner, &i2.inner) {
|
||||
(&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => {
|
||||
match (&a.inner, &b.inner) {
|
||||
(&clean::ExternMod(..), _) => true,
|
||||
(_, &clean::ExternMod(..)) => false,
|
||||
_ => idx1 < idx2,
|
||||
(&clean::ExternMod(..), _) => Less,
|
||||
(_, &clean::ExternMod(..)) => Greater,
|
||||
_ => idx1.cmp(&idx2),
|
||||
}
|
||||
}
|
||||
(&clean::ViewItemItem(..), _) => true,
|
||||
(_, &clean::ViewItemItem(..)) => false,
|
||||
(&clean::ModuleItem(..), _) => true,
|
||||
(_, &clean::ModuleItem(..)) => false,
|
||||
(&clean::StructItem(..), _) => true,
|
||||
(_, &clean::StructItem(..)) => false,
|
||||
(&clean::EnumItem(..), _) => true,
|
||||
(_, &clean::EnumItem(..)) => false,
|
||||
(&clean::StaticItem(..), _) => true,
|
||||
(_, &clean::StaticItem(..)) => false,
|
||||
(&clean::ForeignFunctionItem(..), _) => true,
|
||||
(_, &clean::ForeignFunctionItem(..)) => false,
|
||||
(&clean::ForeignStaticItem(..), _) => true,
|
||||
(_, &clean::ForeignStaticItem(..)) => false,
|
||||
(&clean::TraitItem(..), _) => true,
|
||||
(_, &clean::TraitItem(..)) => false,
|
||||
(&clean::FunctionItem(..), _) => true,
|
||||
(_, &clean::FunctionItem(..)) => false,
|
||||
(&clean::TypedefItem(..), _) => true,
|
||||
(_, &clean::TypedefItem(..)) => false,
|
||||
_ => idx1 < idx2,
|
||||
(&clean::ViewItemItem(..), _) => Less,
|
||||
(_, &clean::ViewItemItem(..)) => Greater,
|
||||
(&clean::ModuleItem(..), _) => Less,
|
||||
(_, &clean::ModuleItem(..)) => Greater,
|
||||
(&clean::StructItem(..), _) => Less,
|
||||
(_, &clean::StructItem(..)) => Greater,
|
||||
(&clean::EnumItem(..), _) => Less,
|
||||
(_, &clean::EnumItem(..)) => Greater,
|
||||
(&clean::StaticItem(..), _) => Less,
|
||||
(_, &clean::StaticItem(..)) => Greater,
|
||||
(&clean::ForeignFunctionItem(..), _) => Less,
|
||||
(_, &clean::ForeignFunctionItem(..)) => Greater,
|
||||
(&clean::ForeignStaticItem(..), _) => Less,
|
||||
(_, &clean::ForeignStaticItem(..)) => Greater,
|
||||
(&clean::TraitItem(..), _) => Less,
|
||||
(_, &clean::TraitItem(..)) => Greater,
|
||||
(&clean::FunctionItem(..), _) => Less,
|
||||
(_, &clean::FunctionItem(..)) => Greater,
|
||||
(&clean::TypedefItem(..), _) => Less,
|
||||
(_, &clean::TypedefItem(..)) => Greater,
|
||||
_ => idx1.cmp(&idx2),
|
||||
}
|
||||
}
|
||||
|
||||
debug!("{:?}", indices);
|
||||
sort::quick_sort(indices, |&i1, &i2| lt(&items[i1], &items[i2], i1, i2));
|
||||
indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
|
||||
|
||||
debug!("{:?}", indices);
|
||||
let mut curty = "";
|
||||
|
@ -1532,7 +1530,7 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
|
|||
}
|
||||
|
||||
for (_, items) in map.mut_iter() {
|
||||
sort::quick_sort(*items, |i1, i2| i1 < i2);
|
||||
items.sort();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,8 @@ pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4};
|
|||
pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8};
|
||||
pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12};
|
||||
pub use vec::{ImmutableEqVector, ImmutableTotalOrdVector, ImmutableCopyableVector};
|
||||
pub use vec::{OwnedVector, OwnedCopyableVector,OwnedEqVector, MutableVector};
|
||||
pub use vec::{OwnedVector, OwnedCopyableVector,OwnedEqVector};
|
||||
pub use vec::{MutableVector, MutableTotalOrdVector};
|
||||
pub use vec::{Vector, VectorVector, CopyableVector, ImmutableVector};
|
||||
|
||||
// Reexported runtime types
|
||||
|
|
|
@ -1921,6 +1921,151 @@ impl<T:Eq> OwnedEqVector<T> for ~[T] {
|
|||
}
|
||||
}
|
||||
|
||||
fn merge_sort<T>(v: &mut [T], compare: |&T, &T| -> Ordering) {
|
||||
// warning: this wildly uses unsafe.
|
||||
static INSERTION: uint = 8;
|
||||
|
||||
let len = v.len();
|
||||
|
||||
// allocate some memory to use as scratch memory, we keep the
|
||||
// length 0 so we can keep shallow copies of the contents of `v`
|
||||
// without risking the dtors running on an object twice if
|
||||
// `compare` fails.
|
||||
let mut working_space = with_capacity(2 * len);
|
||||
// these both are buffers of length `len`.
|
||||
let mut buf_dat = working_space.as_mut_ptr();
|
||||
let mut buf_tmp = unsafe {buf_dat.offset(len as int)};
|
||||
|
||||
// length `len`.
|
||||
let buf_v = v.as_ptr();
|
||||
|
||||
// step 1. sort short runs with insertion sort. This takes the
|
||||
// values from `v` and sorts them into `buf_dat`, leaving that
|
||||
// with sorted runs of length INSERTION.
|
||||
|
||||
// We could hardcode the sorting comparisons here, and we could
|
||||
// manipulate/step the pointers themselves, rather than repeatedly
|
||||
// .offset-ing.
|
||||
for start in range_step(0, len, INSERTION) {
|
||||
// start <= i <= len;
|
||||
for i in range(start, cmp::min(start + INSERTION, len)) {
|
||||
// j satisfies: start <= j <= i;
|
||||
let mut j = i as int;
|
||||
unsafe {
|
||||
// `i` is in bounds.
|
||||
let read_ptr = buf_v.offset(i as int);
|
||||
|
||||
// find where to insert, we need to do strict <,
|
||||
// rather than <=, to maintain stability.
|
||||
|
||||
// start <= j - 1 < len, so .offset(j - 1) is in
|
||||
// bounds.
|
||||
while j > start as int &&
|
||||
compare(&*read_ptr, &*buf_dat.offset(j - 1)) == Less {
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
// shift everything to the right, to make space to
|
||||
// insert this value.
|
||||
|
||||
// j + 1 could be `len` (for the last `i`), but in
|
||||
// that case, `i == j` so we don't copy. The
|
||||
// `.offset(j)` is always in bounds.
|
||||
ptr::copy_memory(buf_dat.offset(j + 1),
|
||||
buf_dat.offset(j),
|
||||
i - j as uint);
|
||||
ptr::copy_nonoverlapping_memory(buf_dat.offset(j), read_ptr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 2. merge the sorted runs.
|
||||
let mut width = INSERTION;
|
||||
while width < len {
|
||||
// merge the sorted runs of length `width` in `buf_dat` two at
|
||||
// a time, placing the result in `buf_tmp`.
|
||||
|
||||
// 0 <= start <= len.
|
||||
for start in range_step(0, len, 2 * width) {
|
||||
// manipulate pointers directly for speed (rather than
|
||||
// using a `for` loop with `range` and `.offset` inside
|
||||
// that loop).
|
||||
unsafe {
|
||||
// the end of the first run & start of the
|
||||
// second. Offset of `len` is defined, since this is
|
||||
// precisely one byte past the end of the object.
|
||||
let right_start = buf_dat.offset(cmp::min(start + width, len) as int);
|
||||
// end of the second. Similar reasoning to the above re safety.
|
||||
let right_end_idx = cmp::min(start + 2 * width, len);
|
||||
let right_end = buf_dat.offset(right_end_idx as int);
|
||||
|
||||
// the pointers to the elements under consideration
|
||||
// from the two runs.
|
||||
|
||||
// both of these are in bounds.
|
||||
let mut left = buf_dat.offset(start as int);
|
||||
let mut right = right_start;
|
||||
|
||||
// where we're putting the results, it is a run of
|
||||
// length `2*width`, so we step it once for each step
|
||||
// of either `left` or `right`. `buf_tmp` has length
|
||||
// `len`, so these are in bounds.
|
||||
let mut out = buf_tmp.offset(start as int);
|
||||
let out_end = buf_tmp.offset(right_end_idx as int);
|
||||
|
||||
while out < out_end {
|
||||
// Either the left or the right run are exhausted,
|
||||
// so just copy the remainder from the other run
|
||||
// and move on; this gives a huge speed-up (order
|
||||
// of 25%) for mostly sorted vectors (the best
|
||||
// case).
|
||||
if left == right_start {
|
||||
// the number remaining in this run.
|
||||
let elems = (right_end as uint - right as uint) / mem::size_of::<T>();
|
||||
ptr::copy_nonoverlapping_memory(out, right, elems);
|
||||
break;
|
||||
} else if right == right_end {
|
||||
let elems = (right_start as uint - left as uint) / mem::size_of::<T>();
|
||||
ptr::copy_nonoverlapping_memory(out, left, elems);
|
||||
break;
|
||||
}
|
||||
|
||||
// check which side is smaller, and that's the
|
||||
// next element for the new run.
|
||||
|
||||
// `left < right_start` and `right < right_end`,
|
||||
// so these are valid.
|
||||
let to_copy = if compare(&*left, &*right) == Greater {
|
||||
step(&mut right)
|
||||
} else {
|
||||
step(&mut left)
|
||||
};
|
||||
ptr::copy_nonoverlapping_memory(out, to_copy, 1);
|
||||
step(&mut out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::swap(&mut buf_dat, &mut buf_tmp);
|
||||
|
||||
width *= 2;
|
||||
}
|
||||
|
||||
// write the result to `v` in one go, so that there are never two copies
|
||||
// of the same object in `v`.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping_memory(v.as_mut_ptr(), buf_dat, len);
|
||||
}
|
||||
|
||||
// increment the pointer, returning the old pointer.
|
||||
#[inline(always)]
|
||||
unsafe fn step<T>(ptr: &mut *mut T) -> *mut T {
|
||||
let old = *ptr;
|
||||
*ptr = ptr.offset(1);
|
||||
old
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for vectors such that their elements are
|
||||
/// mutable.
|
||||
pub trait MutableVector<'a, T> {
|
||||
|
@ -2020,6 +2165,25 @@ pub trait MutableVector<'a, T> {
|
|||
/// Reverse the order of elements in a vector, in place
|
||||
fn reverse(self);
|
||||
|
||||
/// Sort the vector, in place, using `compare` to compare
|
||||
/// elements.
|
||||
///
|
||||
/// This sort is `O(n log n)` worst-case and stable, but allocates
|
||||
/// approximately `2 * n`, where `n` is the length of `self`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut v = [5, 4, 1, 3, 2];
|
||||
/// v.sort(|a, b| a.cmp(b));
|
||||
/// assert_eq!(v, [1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// // reverse sorting
|
||||
/// v.sort(|a, b| b.cmp(a));
|
||||
/// assert_eq!(v, [5, 4, 3, 2, 1]);
|
||||
/// ```
|
||||
fn sort_by(self, compare: |&T, &T| -> Ordering);
|
||||
|
||||
/**
|
||||
* Consumes `src` and moves as many elements as it can into `self`
|
||||
* from the range [start,end).
|
||||
|
@ -2163,6 +2327,11 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_by(self, compare: |&T, &T| -> Ordering) {
|
||||
merge_sort(self, compare)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn move_from(self, mut src: ~[T], start: uint, end: uint) -> uint {
|
||||
for (a, b) in self.mut_iter().zip(src.mut_slice(start, end).mut_iter()) {
|
||||
|
@ -2216,6 +2385,32 @@ impl<'a, T:Clone> MutableCloneableVector<T> for &'a mut [T] {
|
|||
}
|
||||
}
|
||||
|
||||
/// Methods for mutable vectors with orderable elements, such as
|
||||
/// in-place sorting.
|
||||
pub trait MutableTotalOrdVector<T> {
|
||||
/// Sort the vector, in place.
|
||||
///
|
||||
/// This is equivalent to `self.sort_by(std::vec::SortForward)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::vec;
|
||||
///
|
||||
/// let mut v = [-5, 4, 1, -3, 2];
|
||||
///
|
||||
/// v.sort();
|
||||
/// assert_eq!(v, [-5, -3, 1, 2, 4]);
|
||||
/// ```
|
||||
fn sort(self);
|
||||
}
|
||||
impl<'a, T: TotalOrd> MutableTotalOrdVector<T> for &'a mut [T] {
|
||||
#[inline]
|
||||
fn sort(self) {
|
||||
self.sort_by(|a,b| a.cmp(b))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a vector from an unsafe pointer to a buffer
|
||||
*
|
||||
|
@ -2686,6 +2881,7 @@ mod tests {
|
|||
use vec::*;
|
||||
use cmp::*;
|
||||
use prelude::*;
|
||||
use rand::{Rng, task_rng};
|
||||
|
||||
fn square(n: uint) -> uint { n * n }
|
||||
|
||||
|
@ -3279,6 +3475,64 @@ mod tests {
|
|||
assert!(v3.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort() {
|
||||
for len in range(4u, 25) {
|
||||
for _ in range(0, 100) {
|
||||
let mut v = task_rng().gen_vec::<uint>(len);
|
||||
let mut v1 = v.clone();
|
||||
|
||||
v.sort();
|
||||
assert!(v.windows(2).all(|w| w[0] <= w[1]));
|
||||
|
||||
v1.sort_by(|a, b| a.cmp(b));
|
||||
assert!(v1.windows(2).all(|w| w[0] <= w[1]));
|
||||
|
||||
v1.sort_by(|a, b| b.cmp(a));
|
||||
assert!(v1.windows(2).all(|w| w[0] >= w[1]));
|
||||
}
|
||||
}
|
||||
|
||||
// shouldn't fail/crash
|
||||
let mut v: [uint, .. 0] = [];
|
||||
v.sort();
|
||||
|
||||
let mut v = [0xDEADBEEF];
|
||||
v.sort();
|
||||
assert_eq!(v, [0xDEADBEEF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_stability() {
|
||||
for len in range(4, 25) {
|
||||
for _ in range(0 , 10) {
|
||||
let mut counts = [0, .. 10];
|
||||
|
||||
// create a vector like [(6, 1), (5, 1), (6, 2), ...],
|
||||
// where the first item of each tuple is random, but
|
||||
// the second item represents which occurrence of that
|
||||
// number this element is, i.e. the second elements
|
||||
// will occur in sorted order.
|
||||
let mut v = range(0, len).map(|_| {
|
||||
let n = task_rng().gen::<uint>() % 10;
|
||||
counts[n] += 1;
|
||||
(n, counts[n])
|
||||
}).to_owned_vec();
|
||||
|
||||
// only sort on the first element, so an unstable sort
|
||||
// may mix up the counts.
|
||||
v.sort_by(|&(a,_), &(b,_)| a.cmp(&b));
|
||||
|
||||
// this comparison includes the count (the second item
|
||||
// of the tuple), so elements with equal first items
|
||||
// will need to be ordered with increasing
|
||||
// counts... i.e. exactly asserting that this sort is
|
||||
// stable.
|
||||
assert!(v.windows(2).all(|w| w[0] <= w[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partition() {
|
||||
assert_eq!((~[]).partition(|x: &int| *x < 3), (~[], ~[]));
|
||||
|
@ -4102,10 +4356,11 @@ mod bench {
|
|||
use extra::test::BenchHarness;
|
||||
use iter::range;
|
||||
use vec;
|
||||
use vec::VectorVector;
|
||||
use vec::{VectorVector, MutableTotalOrdVector};
|
||||
use option::*;
|
||||
use ptr;
|
||||
use rand::{weak_rng, Rng};
|
||||
use mem;
|
||||
|
||||
#[bench]
|
||||
fn iterator(bh: &mut BenchHarness) {
|
||||
|
@ -4306,4 +4561,43 @@ mod bench {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn sort_random_small(bh: &mut BenchHarness) {
|
||||
let mut rng = weak_rng();
|
||||
bh.iter(|| {
|
||||
let mut v: ~[u64] = rng.gen_vec(5);
|
||||
v.sort();
|
||||
});
|
||||
bh.bytes = 5 * mem::size_of::<u64>() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn sort_random_medium(bh: &mut BenchHarness) {
|
||||
let mut rng = weak_rng();
|
||||
bh.iter(|| {
|
||||
let mut v: ~[u64] = rng.gen_vec(100);
|
||||
v.sort();
|
||||
});
|
||||
bh.bytes = 100 * mem::size_of::<u64>() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn sort_random_large(bh: &mut BenchHarness) {
|
||||
let mut rng = weak_rng();
|
||||
bh.iter(|| {
|
||||
let mut v: ~[u64] = rng.gen_vec(10000);
|
||||
v.sort();
|
||||
});
|
||||
bh.bytes = 10000 * mem::size_of::<u64>() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn sort_sorted(bh: &mut BenchHarness) {
|
||||
let mut v = vec::from_fn(10000, |i| i);
|
||||
bh.iter(|| {
|
||||
v.sort();
|
||||
});
|
||||
bh.bytes = (v.len() * mem::size_of_val(&v[0])) as u64;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
// Functions dealing with attributes and meta items
|
||||
|
||||
use extra;
|
||||
|
||||
use ast;
|
||||
use ast::{Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
|
||||
use codemap::{Span, Spanned, spanned, dummy_spanned};
|
||||
|
@ -205,7 +203,7 @@ pub fn sort_meta_items(items: &[@MetaItem]) -> ~[@MetaItem] {
|
|||
.map(|&mi| (mi.name(), mi))
|
||||
.collect::<~[(@str, @MetaItem)]>();
|
||||
|
||||
extra::sort::quick_sort(v, |&(a, _), &(b, _)| a <= b);
|
||||
v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
|
||||
|
||||
// There doesn't seem to be a more optimal way to do this
|
||||
v.move_iter().map(|(_, m)| {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
extern mod extra;
|
||||
|
||||
use extra::sort;
|
||||
use std::cmp::Ord;
|
||||
use std::comm;
|
||||
use std::hashmap::HashMap;
|
||||
|
@ -27,35 +26,32 @@ use std::task;
|
|||
use std::util;
|
||||
use std::vec;
|
||||
|
||||
fn f64_cmp(x: f64, y: f64) -> Ordering {
|
||||
// arbitrarily decide that NaNs are larger than everything.
|
||||
if y.is_nan() {
|
||||
Less
|
||||
} else if x.is_nan() {
|
||||
Greater
|
||||
} else if x < y {
|
||||
Less
|
||||
} else if x == y {
|
||||
Equal
|
||||
} else {
|
||||
Greater
|
||||
}
|
||||
}
|
||||
|
||||
// given a map, print a sorted version of it
|
||||
fn sort_and_fmt(mm: &HashMap<~[u8], uint>, total: uint) -> ~str {
|
||||
fn pct(xx: uint, yy: uint) -> f64 {
|
||||
return (xx as f64) * 100.0 / (yy as f64);
|
||||
}
|
||||
|
||||
fn le_by_val<TT:Clone,
|
||||
UU:Clone + Ord>(
|
||||
kv0: &(TT,UU),
|
||||
kv1: &(TT,UU))
|
||||
-> bool {
|
||||
let (_, v0) = (*kv0).clone();
|
||||
let (_, v1) = (*kv1).clone();
|
||||
return v0 >= v1;
|
||||
}
|
||||
|
||||
fn le_by_key<TT:Clone + Ord,
|
||||
UU:Clone>(
|
||||
kv0: &(TT,UU),
|
||||
kv1: &(TT,UU))
|
||||
-> bool {
|
||||
let (k0, _) = (*kv0).clone();
|
||||
let (k1, _) = (*kv1).clone();
|
||||
return k0 <= k1;
|
||||
}
|
||||
|
||||
// sort by key, then by value
|
||||
fn sortKV<TT:Clone + Ord, UU:Clone + Ord>(orig: ~[(TT,UU)]) -> ~[(TT,UU)] {
|
||||
return sort::merge_sort(sort::merge_sort(orig, le_by_key), le_by_val);
|
||||
fn sortKV(mut orig: ~[(~[u8],f64)]) -> ~[(~[u8],f64)] {
|
||||
orig.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
|
||||
orig.sort_by(|&(_, a), &(_, b)| f64_cmp(b, a));
|
||||
orig
|
||||
}
|
||||
|
||||
let mut pairs = ~[];
|
||||
|
|
|
@ -9,7 +9,6 @@ use std::libc::{stat, strlen};
|
|||
use std::ptr::null;
|
||||
use std::unstable::intrinsics::init;
|
||||
use std::vec::{reverse};
|
||||
use extra::sort::quick_sort3;
|
||||
|
||||
static LINE_LEN: uint = 80;
|
||||
static TABLE: [u8, ..4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ];
|
||||
|
@ -267,7 +266,7 @@ fn print_frequencies(frequencies: &Table, frame: i32) {
|
|||
for frequencies.each |entry| {
|
||||
vector.push((entry.code, entry.count));
|
||||
}
|
||||
quick_sort3(vector);
|
||||
vector.sort();
|
||||
|
||||
let mut total_count = 0;
|
||||
for vector.each |&(_, count)| {
|
||||
|
|
89
src/test/run-pass/vector-sort-failure-safe.rs
Normal file
89
src/test/run-pass/vector-sort-failure-safe.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::task;
|
||||
use std::rand::{task_rng, Rng};
|
||||
|
||||
static MAX_LEN: uint = 20;
|
||||
static mut drop_counts: [uint, .. MAX_LEN] = [0, .. MAX_LEN];
|
||||
static mut clone_count: uint = 0;
|
||||
|
||||
#[deriving(Rand, Ord, TotalEq, TotalOrd)]
|
||||
struct DropCounter { x: uint, clone_num: uint }
|
||||
|
||||
impl Clone for DropCounter {
|
||||
fn clone(&self) -> DropCounter {
|
||||
let num = unsafe { clone_count };
|
||||
unsafe { clone_count += 1; }
|
||||
DropCounter {
|
||||
x: self.x,
|
||||
clone_num: num
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropCounter {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Rand creates some with arbitrary clone_nums
|
||||
if self.clone_num < MAX_LEN {
|
||||
drop_counts[self.clone_num] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// len can't go above 64.
|
||||
for len in range(2u, MAX_LEN) {
|
||||
for _ in range(0, 10) {
|
||||
let main = task_rng().gen_vec::<DropCounter>(len);
|
||||
|
||||
// work out the total number of comparisons required to sort
|
||||
// this array...
|
||||
let mut count = 0;
|
||||
main.clone().sort_by(|a, b| { count += 1; a.cmp(b) });
|
||||
|
||||
// ... and then fail on each and every single one.
|
||||
for fail_countdown in range(0, count) {
|
||||
// refresh the counters.
|
||||
unsafe {
|
||||
drop_counts = [0, .. MAX_LEN];
|
||||
clone_count = 0;
|
||||
}
|
||||
|
||||
let v = main.clone();
|
||||
|
||||
task::try(proc() {
|
||||
let mut v = v;
|
||||
let mut fail_countdown = fail_countdown;
|
||||
v.sort_by(|a, b| {
|
||||
if fail_countdown == 0 {
|
||||
fail!()
|
||||
}
|
||||
fail_countdown -= 1;
|
||||
a.cmp(b)
|
||||
})
|
||||
});
|
||||
|
||||
// check that the number of things dropped is exactly
|
||||
// what we expect (i.e. the contents of `v`).
|
||||
unsafe {
|
||||
for (i, &c) in drop_counts.iter().enumerate() {
|
||||
let expected = if i < len {1} else {0};
|
||||
assert!(c == expected,
|
||||
"found drop count == {} for i == {}, len == {}",
|
||||
c, i, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue