From fcdcff71581ba2cdecfd2d81fb6c758e0d182f8e Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:07:51 -0400 Subject: [PATCH 01/21] Port some rust-lang/rust performance and safety PRs Port changes from rust-lang/rust related to binary heap performance and use of unsafe: - #81706: Document BinaryHeap unsafe functions - #81127: Improve sift_down performance in BinaryHeap - #58123: Avoid some bounds checks in binary_heap::{PeekMut,Hole} - #72709: #[deny(unsafe_op_in_unsafe_fn)] in liballoc Note that the following related rust-lang/rust PRs were already ported here in earlier PRs: - (in #28) #78857: Improve BinaryHeap performance - (in #27) #75974: Avoid useless sift_down when std::collections::binary_heap::PeekMut is never mutably dereferenced --- src/binary_heap.rs | 209 +++++++++++++++++++++++++++++++-------------- 1 file changed, 143 insertions(+), 66 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 89ca065..4da0c08 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -155,6 +155,7 @@ //! } //! ``` +#![deny(unsafe_op_in_unsafe_fn)] #![allow(clippy::needless_doctest_main)] #![allow(missing_docs)] // #![stable(feature = "rust1", since = "1.0.0")] @@ -319,7 +320,8 @@ impl<'a, T: fmt::Debug, C: Compare> fmt::Debug for PeekMut<'a, T, C> { impl<'a, T, C: Compare> Drop for PeekMut<'a, T, C> { fn drop(&mut self) { if self.sift { - self.heap.sift_down(0); + // SAFETY: PeekMut is only instantiated for non-empty heaps. + unsafe { self.heap.sift_down(0) }; } } } @@ -328,15 +330,19 @@ impl<'a, T, C: Compare> Drop for PeekMut<'a, T, C> { impl<'a, T, C: Compare> Deref for PeekMut<'a, T, C> { type Target = T; fn deref(&self) -> &T { - &self.heap.data[0] + debug_assert!(!self.heap.is_empty()); + // SAFE: PeekMut is only instantiated for non-empty heaps + unsafe { self.heap.data.get_unchecked(0) } } } // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] impl<'a, T, C: Compare> DerefMut for PeekMut<'a, T, C> { fn deref_mut(&mut self) -> &mut T { + debug_assert!(!self.heap.is_empty()); self.sift = true; - &mut self.heap.data[0] + // SAFE: PeekMut is only instantiated for non-empty heaps + unsafe { self.heap.data.get_unchecked_mut(0) } } } @@ -865,7 +871,8 @@ impl> BinaryHeap { self.data.pop().map(|mut item| { if !self.is_empty() { swap(&mut item, &mut self.data[0]); - self.sift_down_to_bottom(0); + // SAFETY: !self.is_empty() means that self.len() > 0 + unsafe { self.sift_down_to_bottom(0) }; } item }) @@ -891,7 +898,9 @@ impl> BinaryHeap { pub fn push(&mut self, item: T) { let old_len = self.len(); self.data.push(item); - self.sift_up(0, old_len); + // SAFETY: Since we pushed a new item it means that + // old_len = self.len() - 1 < self.len() + unsafe { self.sift_up(0, old_len) }; } /// Consumes the `BinaryHeap` and returns the underlying vector @@ -946,7 +955,10 @@ impl> BinaryHeap { let ptr = self.data.as_mut_ptr(); ptr::swap(ptr, ptr.add(end)); } - self.sift_down_range(0, end); + // SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so: + // 0 < 1 <= end <= self.len() - 1 < self.len() + // Which means 0 < end and end < self.len(). + unsafe { self.sift_down_range(0, end) }; } self.into_vec() } @@ -959,53 +971,92 @@ impl> BinaryHeap { // the hole is filled back at the end of its scope, even on panic. // Using a hole reduces the constant factor compared to using swaps, // which involves twice as many moves. - fn sift_up(&mut self, start: usize, pos: usize) -> usize { - unsafe { - // Take out the value at `pos` and create a hole. - let mut hole = Hole::new(&mut self.data, pos); - - while hole.pos() > start { - let parent = (hole.pos() - 1) / 2; - // if hole.element() <= hole.get(parent) { - if self.cmp.compare(hole.element(), hole.get(parent)) != Ordering::Greater { - break; - } - hole.move_to(parent); + + /// # Safety + /// + /// The caller must guarantee that `pos < self.len()`. + unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize { + // Take out the value at `pos` and create a hole. + // SAFETY: The caller guarantees that pos < self.len() + let mut hole = unsafe { Hole::new(&mut self.data, pos) }; + + while hole.pos() > start { + let parent = (hole.pos() - 1) / 2; + + // SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0 + // and so hole.pos() - 1 can't underflow. + // This guarantees that parent < hole.pos() so + // it's a valid index and also != hole.pos(). + if self + .cmp + .compare(hole.element(), unsafe { hole.get(parent) }) + != Ordering::Greater + { + break; } - hole.pos() + + // SAFETY: Same as above + unsafe { hole.move_to(parent) }; } + + hole.pos() } /// Take an element at `pos` and move it down the heap, /// while its children are larger. - fn sift_down_range(&mut self, pos: usize, end: usize) { - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end - 1 { - // compare with the greater of the two children - // if !(hole.get(child) > hole.get(child + 1)) { child += 1 } - child += (self.cmp.compare(hole.get(child), hole.get(child + 1)) - != Ordering::Greater) as usize; - // if we are already in order, stop. - // if hole.element() >= hole.get(child) { - if self.cmp.compare(hole.element(), hole.get(child)) != Ordering::Less { - return; - } - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - if child == end - 1 - && self.cmp.compare(hole.element(), hole.get(child)) == Ordering::Less - { - hole.move_to(child); + /// + /// # Safety + /// + /// The caller must guarantee that `pos < end <= self.len()`. + unsafe fn sift_down_range(&mut self, pos: usize, end: usize) { + // SAFETY: The caller guarantees that pos < end <= self.len(). + let mut hole = unsafe { Hole::new(&mut self.data, pos) }; + let mut child = 2 * hole.pos() + 1; + + // Loop invariant: child == 2 * hole.pos() + 1. + while child <= end.saturating_sub(2) { + // compare with the greater of the two children + // SAFETY: child < end - 1 < self.len() and + // child + 1 < end <= self.len(), so they're valid indexes. + // child == 2 * hole.pos() + 1 != hole.pos() and + // child + 1 == 2 * hole.pos() + 2 != hole.pos(). + // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow + // if T is a ZST + child += unsafe { + self.cmp.compare(hole.get(child), hole.get(child + 1)) != Ordering::Greater + } as usize; + + // if we are already in order, stop. + // SAFETY: child is now either the old child or the old child+1 + // We already proven that both are < self.len() and != hole.pos() + if self.cmp.compare(hole.element(), unsafe { hole.get(child) }) != Ordering::Less { + return; } + + // SAFETY: same as above. + unsafe { hole.move_to(child) }; + child = 2 * hole.pos() + 1; + } + + // SAFETY: && short circuit, which means that in the + // second condition it's already true that child == end - 1 < self.len(). + if child == end - 1 + && self.cmp.compare(hole.element(), unsafe { hole.get(child) }) == Ordering::Less + { + // SAFETY: child is already proven to be a valid index and + // child == 2 * hole.pos() + 1 != hole.pos(). + unsafe { hole.move_to(child) }; } } - fn sift_down(&mut self, pos: usize) { + /// # Safety + /// + /// The caller must guarantee that `pos < self.len()`. + unsafe fn sift_down(&mut self, pos: usize) { let len = self.len(); - self.sift_down_range(pos, len); + // SAFETY: pos < len is guaranteed by the caller and + // obviously len = self.len() <= self.len(). + unsafe { self.sift_down_range(pos, len) }; } /// Take an element at `pos` and move it all the way down the heap, @@ -1013,27 +1064,46 @@ impl> BinaryHeap { /// /// Note: This is faster when the element is known to be large / should /// be closer to the bottom. - fn sift_down_to_bottom(&mut self, mut pos: usize) { + /// + /// # Safety + /// + /// The caller must guarantee that `pos < self.len()`. + unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) { let end = self.len(); let start = pos; - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end - 1 { - let right = child + 1; - // compare with the greater of the two children - // if !(hole.get(child) > hole.get(right)) { child += 1 } - child += (self.cmp.compare(hole.get(child), hole.get(right)) != Ordering::Greater) - as usize; - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - if child == end - 1 { - hole.move_to(child); - } - pos = hole.pos; + + // SAFETY: The caller guarantees that pos < self.len(). + let mut hole = unsafe { Hole::new(&mut self.data, pos) }; + let mut child = 2 * hole.pos() + 1; + + // Loop invariant: child == 2 * hole.pos() + 1. + while child <= end.saturating_sub(2) { + // SAFETY: child < end - 1 < self.len() and + // child + 1 < end <= self.len(), so they're valid indexes. + // child == 2 * hole.pos() + 1 != hole.pos() and + // child + 1 == 2 * hole.pos() + 2 != hole.pos(). + // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow + // if T is a ZST + child += unsafe { + self.cmp.compare(hole.get(child), hole.get(child + 1)) != Ordering::Greater + } as usize; + + // SAFETY: Same as above + unsafe { hole.move_to(child) }; + child = 2 * hole.pos() + 1; + } + + if child == end - 1 { + // SAFETY: child == end - 1 < self.len(), so it's a valid index + // and child == 2 * hole.pos() + 1 != hole.pos(). + unsafe { hole.move_to(child) }; } - self.sift_up(start, pos); + pos = hole.pos(); + drop(hole); + + // SAFETY: pos is the position in the hole and was already proven + // to be a valid index. + unsafe { self.sift_up(start, pos) }; } /// Returns the length of the binary heap. @@ -1129,7 +1199,10 @@ impl> BinaryHeap { let mut n = self.len() / 2; while n > 0 { n -= 1; - self.sift_down(n); + // SAFETY: n starts from self.len() / 2 and goes down to 0. + // The only case when !(n < self.len()) is if + // self.len() == 0, but it's ruled out by the loop condition. + unsafe { self.sift_down(n) }; } } @@ -1205,7 +1278,8 @@ impl<'a, T> Hole<'a, T> { #[inline] unsafe fn new(data: &'a mut [T], pos: usize) -> Self { debug_assert!(pos < data.len()); - let elt = ptr::read(&data[pos]); + // SAFE: pos should be inside the slice + let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; Hole { data, elt: Some(elt), @@ -1231,7 +1305,7 @@ impl<'a, T> Hole<'a, T> { unsafe fn get(&self, index: usize) -> &T { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); - self.data.get_unchecked(index) + unsafe { self.data.get_unchecked(index) } } /// Move hole to new location @@ -1241,9 +1315,12 @@ impl<'a, T> Hole<'a, T> { unsafe fn move_to(&mut self, index: usize) { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); - let index_ptr: *const _ = self.data.get_unchecked(index); - let hole_ptr = self.data.get_unchecked_mut(self.pos); - ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + unsafe { + let ptr = self.data.as_mut_ptr(); + let index_ptr: *const _ = ptr.add(index); + let hole_ptr = ptr.add(self.pos); + ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + } self.pos = index; } } From 478bb1f348cb2d9421ddfef8a774ec064f11f368 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Fri, 22 Jul 2022 20:36:31 -0400 Subject: [PATCH 02/21] Adjust lint levels for using unsafe blocks in unsafe fns If the `unsafe_op_in_unsafe_fn` lint is available (since Rust 1.52.0), set it to "deny", requiring `unsafe { }` blocks in order to perform unsafe operations even in unsafe functions; this silences the otherwise default warning of the `unused_unsafe` lint as well. If the `unsafe_op_in_unsafe_fn` lint is not available, then just supress warnings from the `unused_unsafe` lint. --- Cargo.toml | 3 +++ build.rs | 8 ++++++++ src/binary_heap.rs | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index e361521..12a8604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,9 @@ keywords = ["binary", "heap", "priority", "queue"] categories = ["data-structures", "algorithms", ] edition = "2018" +[build-dependencies] +autocfg = "1" + [dependencies] compare = "0.1.0" serde = { version = "1.0.116", optional = true, features = ["derive"] } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..10a4ea0 --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +fn main() { + let ac = autocfg::new(); + + // Required for stabilization of `unsafe_op_in_unsafe_fn` lint. + ac.emit_rustc_version(1, 52); + + autocfg::rerun_path("build.rs"); +} diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 4da0c08..b87fec3 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -155,7 +155,8 @@ //! } //! ``` -#![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(has_rustc_1_52, deny(unsafe_op_in_unsafe_fn))] +#![cfg_attr(not(has_rustc_1_52), allow(unused_unsafe))] #![allow(clippy::needless_doctest_main)] #![allow(missing_docs)] // #![stable(feature = "rust1", since = "1.0.0")] From 051c6ec44d7a1f59c4ffb18a4b741e7b156fb462 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 24 Jul 2022 11:00:42 -0400 Subject: [PATCH 03/21] Port changes in `BinaryHeap::append` This commit ports the change in the rebuild heuristic used by `BinaryHeap::append()` that was added in rust-lang/rust#77435: "Change BinaryHeap::append rebuild heuristic". See also the discussion in rust-lang/rust#77433: "Suboptimal performance of BinaryHeap::append" for more information on how the new heuristic was chosen. It also ports the new private method `.rebuild_tail()` now used by `std::collections::BinaryHeap::append()` from rust-lang/rust#78681: "Improve rebuilding behaviour of BinaryHeap::retain". Note that Rust 1.60.0 adds the clippy lint `manual_bits` which warns against code used here. We suppress the lint instead of following the upstream patch which now uses `usize::BITS`, since this was stabilized in Rust 1.53.0 and this crate's MSRV is currently 1.36.0. --- src/binary_heap.rs | 65 ++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index b87fec3..62259ef 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1196,6 +1196,45 @@ impl> BinaryHeap { self.drain(); } + /// Rebuild assuming data[0..start] is still a proper heap. + fn rebuild_tail(&mut self, start: usize) { + if start == self.len() { + return; + } + + let tail_len = self.len() - start; + + // `usize::BITS` requires Rust 1.53.0 or greater. + #[allow(clippy::manual_bits)] + #[inline(always)] + fn log2_fast(x: usize) -> usize { + 8 * size_of::() - (x.leading_zeros() as usize) - 1 + } + + // `rebuild` takes O(self.len()) operations + // and about 2 * self.len() comparisons in the worst case + // while repeating `sift_up` takes O(tail_len * log(start)) operations + // and about 1 * tail_len * log_2(start) comparisons in the worst case, + // assuming start >= tail_len. For larger heaps, the crossover point + // no longer follows this reasoning and was determined empirically. + let better_to_rebuild = if start < tail_len { + true + } else if self.len() <= 2048 { + 2 * self.len() < tail_len * log2_fast(start) + } else { + 2 * self.len() < tail_len * 11 + }; + + if better_to_rebuild { + self.rebuild(); + } else { + for i in start..self.len() { + // SAFETY: The index `i` is always less than self.len(). + unsafe { self.sift_up(0, i) }; + } + } + } + fn rebuild(&mut self) { let mut n = self.len() / 2; while n > 0 { @@ -1233,31 +1272,11 @@ impl> BinaryHeap { swap(self, other); } - if other.is_empty() { - return; - } - - #[inline(always)] - fn log2_fast(x: usize) -> usize { - 8 * size_of::() - (x.leading_zeros() as usize) - 1 - } + let start = self.data.len(); - // `rebuild` takes O(len1 + len2) operations - // and about 2 * (len1 + len2) comparisons in the worst case - // while `extend` takes O(len2 * log_2(len1)) operations - // and about 1 * len2 * log_2(len1) comparisons in the worst case, - // assuming len1 >= len2. - #[inline] - fn better_to_rebuild(len1: usize, len2: usize) -> bool { - 2 * (len1 + len2) < len2 * log2_fast(len1) - } + self.data.append(&mut other.data); - if better_to_rebuild(self.len(), other.len()) { - self.data.append(&mut other.data); - self.rebuild(); - } else { - self.extend(other.drain()); - } + self.rebuild_tail(start); } } From d5d672b5a1f87118c9ab954703b0aaf5502689d5 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:06:17 -0400 Subject: [PATCH 04/21] Port rust-lang/rust#50487: Use `ManuallyDrop` instead of `Option` --- src/binary_heap.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 62259ef..07375bd 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -170,7 +170,7 @@ use std::slice; // use std::vec::Drain; use compare::Compare; use core::fmt; -use core::mem::{size_of, swap}; +use core::mem::{size_of, swap, ManuallyDrop}; use core::ptr; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -1286,8 +1286,7 @@ impl> BinaryHeap { /// position with the value that was originally removed. struct Hole<'a, T: 'a> { data: &'a mut [T], - /// `elt` is always `Some` from new until drop. - elt: Option, + elt: ManuallyDrop, pos: usize, } @@ -1302,7 +1301,7 @@ impl<'a, T> Hole<'a, T> { let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; Hole { data, - elt: Some(elt), + elt: ManuallyDrop::new(elt), pos, } } @@ -1315,7 +1314,7 @@ impl<'a, T> Hole<'a, T> { /// Returns a reference to the element removed. #[inline] fn element(&self) -> &T { - self.elt.as_ref().unwrap() + &self.elt } /// Returns a reference to the element at `index`. @@ -1351,7 +1350,7 @@ impl<'a, T> Drop for Hole<'a, T> { // fill the hole again unsafe { let pos = self.pos; - ptr::write(self.data.get_unchecked_mut(pos), self.elt.take().unwrap()); + ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); } } } From 4cbae630312b04b73d15922353421f00ca6fe7c9 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:17:53 -0400 Subject: [PATCH 05/21] Port rust-lang/rust@e70c2fbd: "liballoc: elide some lifetimes" This commit ports rust-lang/rust commit e70c2fbd5cbe8bf176f1ed01ba9a53cec7e842a5 "liballoc: elide some lifetimes". Part of rust-lang/rust#58081: Transition liballoc to Rust 2018. --- src/binary_heap.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 07375bd..94d56fc 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -311,14 +311,14 @@ pub struct PeekMut<'a, T: 'a, C: 'a + Compare> { } // #[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: fmt::Debug, C: Compare> fmt::Debug for PeekMut<'a, T, C> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl> fmt::Debug for PeekMut<'_, T, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish() } } // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T, C: Compare> Drop for PeekMut<'a, T, C> { +impl> Drop for PeekMut<'_, T, C> { fn drop(&mut self) { if self.sift { // SAFETY: PeekMut is only instantiated for non-empty heaps. @@ -328,7 +328,7 @@ impl<'a, T, C: Compare> Drop for PeekMut<'a, T, C> { } // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T, C: Compare> Deref for PeekMut<'a, T, C> { +impl> Deref for PeekMut<'_, T, C> { type Target = T; fn deref(&self) -> &T { debug_assert!(!self.heap.is_empty()); @@ -338,7 +338,7 @@ impl<'a, T, C: Compare> Deref for PeekMut<'a, T, C> { } // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T, C: Compare> DerefMut for PeekMut<'a, T, C> { +impl> DerefMut for PeekMut<'_, T, C> { fn deref_mut(&mut self) -> &mut T { debug_assert!(!self.heap.is_empty()); self.sift = true; @@ -382,7 +382,7 @@ impl Default for BinaryHeap { // #[stable(feature = "binaryheap_debug", since = "1.4.0")] impl> fmt::Debug for BinaryHeap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } } @@ -753,7 +753,7 @@ impl> BinaryHeap { /// assert_eq!(heap.peek(), Some(&2)); /// ``` // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] - pub fn peek_mut(&mut self) -> Option> { + pub fn peek_mut(&mut self) -> Option> { if self.is_empty() { None } else { @@ -1169,7 +1169,7 @@ impl> BinaryHeap { /// ``` #[inline] // #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain { + pub fn drain(&mut self) -> Drain<'_, T> { Drain { iter: self.data.drain(..), } @@ -1344,7 +1344,7 @@ impl<'a, T> Hole<'a, T> { } } -impl<'a, T> Drop for Hole<'a, T> { +impl Drop for Hole<'_, T> { #[inline] fn drop(&mut self) { // fill the hole again @@ -1368,16 +1368,16 @@ pub struct Iter<'a, T: 'a> { } // #[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Iter").field(&self.iter.as_slice()).finish() } } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` // #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Iter<'a, T> { - fn clone(&self) -> Iter<'a, T> { +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { Iter { iter: self.iter.clone(), } @@ -1432,7 +1432,7 @@ pub struct IntoIter { // #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("IntoIter") .field(&self.iter.as_slice()) .finish() @@ -1508,7 +1508,7 @@ pub struct Drain<'a, T: 'a> { } // #[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> Iterator for Drain<'a, T> { +impl Iterator for Drain<'_, T> { type Item = T; #[inline] @@ -1523,7 +1523,7 @@ impl<'a, T: 'a> Iterator for Drain<'a, T> { } // #[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { +impl DoubleEndedIterator for Drain<'_, T> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back() From 3c1b1f5c88784a56d7a35949340183b283514c1a Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:32:23 -0400 Subject: [PATCH 06/21] Add #[must_use] to many methods Port the following rust-lang/rust PRs adding #[must_use] to many methods: - #89726: Add #[must_use] to alloc constructors - #89755: Add #[must_use] to conversions that move self - #89786: Add #[must_use] to len and is_empty - #89899: Add #[must_use] to remaining alloc functions In addition, add #[must_use] to methods unique to `binary-heap-plus` but which are generalizations of methods covered above, such as `new_min()`, `with_capacity_min()`, `new_by()`, `with_capacity_by()`, etc. --- src/binary_heap.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 94d56fc..30bb774 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -440,6 +440,7 @@ impl BinaryHeap { /// assert_eq!(heap.pop(), Some(5)); /// ``` // #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] pub fn new() -> Self { BinaryHeap::from_vec(vec![]) } @@ -465,6 +466,7 @@ impl BinaryHeap { /// assert_eq!(heap.pop(), Some(5)); /// ``` // #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] pub fn with_capacity(capacity: usize) -> Self { BinaryHeap::from_vec(Vec::with_capacity(capacity)) } @@ -487,6 +489,7 @@ impl BinaryHeap { /// heap.push(5); /// assert_eq!(heap.pop(), Some(1)); /// ``` + #[must_use] pub fn new_min() -> Self { BinaryHeap::from_vec(vec![]) } @@ -511,6 +514,7 @@ impl BinaryHeap { /// heap.push(5); /// assert_eq!(heap.pop(), Some(1)); /// ``` + #[must_use] pub fn with_capacity_min(capacity: usize) -> Self { BinaryHeap::from_vec(Vec::with_capacity(capacity)) } @@ -536,6 +540,7 @@ where /// heap.push(5); /// assert_eq!(heap.pop(), Some(1)); /// ``` + #[must_use] pub fn new_by(f: F) -> Self { BinaryHeap::from_vec_cmp(vec![], FnComparator(f)) } @@ -560,6 +565,7 @@ where /// heap.push(5); /// assert_eq!(heap.pop(), Some(1)); /// ``` + #[must_use] pub fn with_capacity_by(capacity: usize, f: F) -> Self { BinaryHeap::from_vec_cmp(Vec::with_capacity(capacity), FnComparator(f)) } @@ -585,6 +591,7 @@ where /// heap.push(5); /// assert_eq!(heap.pop(), Some(3)); /// ``` + #[must_use] pub fn new_by_key(f: F) -> Self { BinaryHeap::from_vec_cmp(vec![], KeyComparator(f)) } @@ -609,6 +616,7 @@ where /// heap.push(5); /// assert_eq!(heap.pop(), Some(3)); /// ``` + #[must_use] pub fn with_capacity_by_key(capacity: usize, f: F) -> Self { BinaryHeap::from_vec_cmp(Vec::with_capacity(capacity), KeyComparator(f)) } @@ -723,6 +731,7 @@ impl> BinaryHeap { /// assert_eq!(heap.peek(), Some(&5)); /// /// ``` + #[must_use] // #[stable(feature = "rust1", since = "1.0.0")] pub fn peek(&self) -> Option<&T> { self.data.get(0) @@ -776,6 +785,7 @@ impl> BinaryHeap { /// assert!(heap.capacity() >= 100); /// heap.push(4); /// ``` + #[must_use] // #[stable(feature = "rust1", since = "1.0.0")] pub fn capacity(&self) -> usize { self.data.capacity() @@ -921,6 +931,7 @@ impl> BinaryHeap { /// println!("{}", x); /// } /// ``` + #[must_use = "`self` will be dropped if the result is not used"] // #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] pub fn into_vec(self) -> Vec { self.into() @@ -943,6 +954,7 @@ impl> BinaryHeap { /// let vec = heap.into_sorted_vec(); /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]); /// ``` + #[must_use = "`self` will be dropped if the result is not used"] // #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] pub fn into_sorted_vec(mut self) -> Vec { let mut end = self.len(); @@ -1119,6 +1131,7 @@ impl> BinaryHeap { /// /// assert_eq!(heap.len(), 2); /// ``` + #[must_use] // #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.data.len() @@ -1142,6 +1155,7 @@ impl> BinaryHeap { /// /// assert!(!heap.is_empty()); /// ``` + #[must_use] // #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { self.len() == 0 @@ -1362,6 +1376,7 @@ impl Drop for Hole<'_, T> { /// /// [`iter`]: struct.BinaryHeap.html#method.iter /// [`BinaryHeap`]: struct.BinaryHeap.html +#[must_use = "iterators are lazy and do nothing unless consumed"] // #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, @@ -1472,6 +1487,7 @@ impl DoubleEndedIterator for IntoIter { // #[stable(feature = "fused", since = "1.26.0")] // impl FusedIterator for IntoIter {} +#[must_use = "iterators are lazy and do nothing unless consumed"] // #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] #[derive(Clone, Debug)] pub struct IntoIterSorted> { From 3fd37161729da57f3d27ca5b3a4954a8c40bdd33 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 17 Jul 2022 20:18:43 -0400 Subject: [PATCH 07/21] Port rust-lang/rust changes for `Iterator`, `Extend` - #62316: When possible without changing semantics, implement Iterator::last in terms of DoubleEndedIterator::next_back for types in liballoc and libcore - #59740: Use for_each to extend collections --- src/binary_heap.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 30bb774..9280c60 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1412,6 +1412,11 @@ impl<'a, T> Iterator for Iter<'a, T> { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn last(self) -> Option<&'a T> { + self.iter.last() + } } // #[stable(feature = "rust1", since = "1.0.0")] @@ -1652,9 +1657,7 @@ impl> BinaryHeap { self.reserve(lower); - for elem in iterator { - self.push(elem); - } + iterator.for_each(move |elem| self.push(elem)); } } From 83265325782e7bc2d08506dbdedd2726efeb79ed Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Mon, 18 Jul 2022 19:27:33 -0400 Subject: [PATCH 08/21] Port rust-lang/rust documentation changes The following are some of the PRs included here: - #92902: Improve the documentation of drain members - #87537: Clarify undefined behaviour in binary heap, btree and hashset docs - #89010: Add some intra doc links - #80681: Clarify what the effects of a 'logic error' are - #77079: Use Self in docs when possible - #75974: Avoid useless sift_down when std::collections::binary_heap::PeekMut is never mutably dereferenced - #76534: Add doc comments for From impls - #75831: doc: Prefer https link for wikipedia URLs - #74010: Use italics for O notation - #71167: big-O notation: parenthesis for function calls, explicit multiplication - #63486: Document From trait for BinaryHeap - #60952: Document BinaryHeap time complexity - #60451: BinaryHeap: add min-heap example Also port the change in rust-lang/rust@99ed06eb88 "libs: doc comments". Note that rust-lang/rust#60451 adds an example of a min-heap. We add a similar example here, although edited to highlight `binary_heap_plus::BinaryHeap`'s ability to implement a min-heap *without* wrapping the items in `std::cmp::Reverse`. Finally, we replace the wildcard import in the documentation examples with a named import of `BinaryHeap`. --- src/binary_heap.rs | 195 +++++++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 58 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 9280c60..8fd8386 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -13,11 +13,11 @@ //! Note: This version is folked from Rust standartd library, which only supports //! max heap. //! -//! Insertion and popping the largest element have `O(log n)` time complexity. -//! Checking the largest element is `O(1)`. Converting a vector to a binary heap -//! can be done in-place, and has `O(n)` complexity. A binary heap can also be -//! converted to a sorted vector in-place, allowing it to be used for an `O(n -//! log n)` in-place heapsort. +//! Insertion and popping the largest element have *O*(log(*n*)) time complexity. +//! Checking the largest element is *O*(1). Converting a vector to a binary heap +//! can be done in-place, and has *O*(*n*) complexity. A binary heap can also be +//! converted to a sorted vector in-place, allowing it to be used for an *O*(*n* * log(*n*)) +//! in-place heapsort. //! //! # Examples //! @@ -25,15 +25,16 @@ //! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. //! It shows how to use [`BinaryHeap`] with custom types. //! -//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm -//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem -//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph +//! [dijkstra]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +//! [sssp]: https://en.wikipedia.org/wiki/Shortest_path_problem +//! [dir_graph]: https://en.wikipedia.org/wiki/Directed_graph //! [`BinaryHeap`]: struct.BinaryHeap.html //! //! ``` //! use std::cmp::Ordering; -//! use binary_heap_plus::*; +//! // Only required for Rust versions prior to 1.43.0. //! use std::usize; +//! use binary_heap_plus::BinaryHeap; //! //! #[derive(Copy, Clone, Eq, PartialEq)] //! struct State { @@ -45,7 +46,7 @@ //! // Explicitly implement the trait so the queue becomes a min-heap //! // instead of a max-heap. //! impl Ord for State { -//! fn cmp(&self, other: &State) -> Ordering { +//! fn cmp(&self, other: &Self) -> Ordering { //! // Notice that the we flip the ordering on costs. //! // In case of a tie we compare positions - this step is necessary //! // to make implementations of `PartialEq` and `Ord` consistent. @@ -56,12 +57,12 @@ //! //! // `PartialOrd` needs to be implemented as well. //! impl PartialOrd for State { -//! fn partial_cmp(&self, other: &State) -> Option { +//! fn partial_cmp(&self, other: &Self) -> Option { //! Some(self.cmp(other)) //! } //! } //! -//! // Each node is represented as an `usize`, for a shorter implementation. +//! // Each node is represented as a `usize`, for a shorter implementation. //! struct Edge { //! node: usize, //! cost: usize, @@ -188,14 +189,17 @@ use std::vec; /// This will be a max-heap. /// /// It is a logic error for an item to be modified in such a way that the -/// item's ordering relative to any other item, as determined by the `Ord` +/// item's ordering relative to any other item, as determined by the [`Ord`] /// trait, changes while it is in the heap. This is normally only possible -/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. +/// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The +/// behavior resulting from such a logic error is not specified (it +/// could include panics, incorrect results, aborts, memory leaks, or +/// non-termination) but will not be undefined behavior. /// /// # Examples /// /// ``` -/// use binary_heap_plus::*; +/// use binary_heap_plus::BinaryHeap; /// /// // Type inference lets us omit an explicit type signature (which /// // would be `BinaryHeap` in this example). @@ -234,6 +238,46 @@ use std::vec; /// // The heap should now be empty. /// assert!(heap.is_empty()) /// ``` +/// +/// ## Min-heap +/// +/// `BinaryHeap` can also act as a min-heap without requiring [`Reverse`] or a custom [`Ord`] +/// implementation. +/// +/// ``` +/// use binary_heap_plus::BinaryHeap; +/// +/// let mut heap = BinaryHeap::new_min(); +/// +/// // There is no need to wrap values in `Reverse` +/// heap.push(1); +/// heap.push(5); +/// heap.push(2); +/// +/// // If we pop these scores now, they should come back in the reverse order. +/// assert_eq!(heap.pop(), Some(1)); +/// assert_eq!(heap.pop(), Some(2)); +/// assert_eq!(heap.pop(), Some(5)); +/// assert_eq!(heap.pop(), None); +/// ``` +/// +/// # Time complexity +/// +/// | [push] | [pop] | [peek]/[peek\_mut] | +/// |---------|---------------|--------------------| +/// | *O*(1)~ | *O*(log(*n*)) | *O*(1) | +/// +/// The value for `push` is an expected cost; the method documentation gives a +/// more detailed analysis. +/// +/// [`Reverse`]: https://doc.rust-lang.org/stable/core/cmp/struct.Reverse.html +/// [`Ord`]: https://doc.rust-lang.org/stable/core/cmp/trait.Ord.html +/// [`Cell`]: https://doc.rust-lang.org/stable/core/cell/struct.Cell.html +/// [`RefCell`]: https://doc.rust-lang.org/stable/core/cell/struct.RefCell.html +/// [push]: #method.push +/// [pop]: #method.pop +/// [peek]: #method.peek +/// [peek\_mut]: #method.peek_mut // #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BinaryHeap @@ -432,7 +476,7 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// heap.push(3); /// heap.push(1); @@ -457,7 +501,7 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::with_capacity(10); /// assert_eq!(heap.capacity(), 10); /// heap.push(3); @@ -482,7 +526,7 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new_min(); /// heap.push(3); /// heap.push(1); @@ -506,7 +550,7 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::with_capacity_min(10); /// assert_eq!(heap.capacity(), 10); /// heap.push(3); @@ -533,7 +577,7 @@ where /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new_by(|a: &i32, b: &i32| b.cmp(a)); /// heap.push(3); /// heap.push(1); @@ -557,7 +601,7 @@ where /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::with_capacity_by(10, |a: &i32, b: &i32| b.cmp(a)); /// assert_eq!(heap.capacity(), 10); /// heap.push(3); @@ -584,7 +628,7 @@ where /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new_by_key(|a: &i32| a % 4); /// heap.push(3); /// heap.push(1); @@ -608,7 +652,7 @@ where /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::with_capacity_by_key(10, |a: &i32| a % 4); /// assert_eq!(heap.capacity(), 10); /// heap.push(3); @@ -630,7 +674,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// use compare::Compare; /// use std::cmp::Ordering; /// @@ -681,7 +725,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); /// /// // Print 1, 2, 3, 4 in arbitrary order @@ -704,7 +748,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); /// /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); @@ -721,7 +765,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// assert_eq!(heap.peek(), None); /// @@ -731,6 +775,10 @@ impl> BinaryHeap { /// assert_eq!(heap.peek(), Some(&5)); /// /// ``` + /// + /// # Time complexity + /// + /// Cost is *O*(1) in the worst case. #[must_use] // #[stable(feature = "rust1", since = "1.0.0")] pub fn peek(&self) -> Option<&T> { @@ -748,7 +796,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// assert!(heap.peek_mut().is_none()); /// @@ -761,6 +809,11 @@ impl> BinaryHeap { /// } /// assert_eq!(heap.peek(), Some(&2)); /// ``` + /// + /// # Time complexity + /// + /// If the item is modified then the worst case time complexity is *O*(log(*n*)), + /// otherwise it's *O*(1). // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] pub fn peek_mut(&mut self) -> Option> { if self.is_empty() { @@ -780,7 +833,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::with_capacity(100); /// assert!(heap.capacity() >= 100); /// heap.push(4); @@ -807,7 +860,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// heap.reserve_exact(100); /// assert!(heap.capacity() >= 100); @@ -832,7 +885,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// heap.reserve(100); /// assert!(heap.capacity() >= 100); @@ -850,7 +903,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); /// /// assert!(heap.capacity() >= 100); @@ -870,13 +923,17 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::from(vec![1, 3]); /// /// assert_eq!(heap.pop(), Some(3)); /// assert_eq!(heap.pop(), Some(1)); /// assert_eq!(heap.pop(), None); /// ``` + /// + /// # Time complexity + /// + /// The worst case cost of `pop` on a heap containing *n* elements is *O*(log(*n*)). // #[stable(feature = "rust1", since = "1.0.0")] pub fn pop(&mut self) -> Option { self.data.pop().map(|mut item| { @@ -896,7 +953,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// heap.push(3); /// heap.push(5); @@ -905,6 +962,22 @@ impl> BinaryHeap { /// assert_eq!(heap.len(), 3); /// assert_eq!(heap.peek(), Some(&5)); /// ``` + /// + /// # Time complexity + /// + /// The expected cost of `push`, averaged over every possible ordering of + /// the elements being pushed, and over a sufficiently large number of + /// pushes, is *O*(1). This is the most meaningful cost metric when pushing + /// elements that are *not* already in any sorted pattern. + /// + /// The time complexity degrades if elements are pushed in predominantly + /// ascending order. In the worst case, elements are pushed in ascending + /// sorted order and the amortized cost per push is *O*(log(*n*)) against a heap + /// containing *n* elements. + /// + /// The worst case cost of a *single* call to `push` is *O*(*n*). The worst case + /// occurs when capacity is exhausted and needs a resize. The resize cost + /// has been amortized in the previous figures. // #[stable(feature = "rust1", since = "1.0.0")] pub fn push(&mut self, item: T) { let old_len = self.len(); @@ -922,7 +995,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); /// let vec = heap.into_vec(); /// @@ -945,7 +1018,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); /// heap.push(6); @@ -1126,7 +1199,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let heap = BinaryHeap::from(vec![1, 3]); /// /// assert_eq!(heap.len(), 2); @@ -1144,7 +1217,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::new(); /// /// assert!(heap.is_empty()); @@ -1161,16 +1234,19 @@ impl> BinaryHeap { self.len() == 0 } - /// Clears the binary heap, returning an iterator over the removed elements. + /// Clears the binary heap, returning an iterator over the removed elements + /// in arbitrary order. If the iterator is dropped before being fully + /// consumed, it drops the remaining elements in arbitrary order. /// - /// The elements are removed in arbitrary order. + /// The returned iterator keeps a mutable borrow on the heap to optimize + /// its implementation. /// /// # Examples /// /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::from(vec![1, 3]); /// /// assert!(!heap.is_empty()); @@ -1196,7 +1272,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let mut heap = BinaryHeap::from(vec![1, 3]); /// /// assert!(!heap.is_empty()); @@ -1267,7 +1343,7 @@ impl> BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// /// let v = vec![-10, 1, 2, 3, 3]; /// let mut a = BinaryHeap::from(v); @@ -1294,7 +1370,7 @@ impl> BinaryHeap { } } -/// Hole represents a hole in a slice i.e. an index without valid value +/// Hole represents a hole in a slice i.e., an index without valid value /// (because it was moved from or duplicated). /// In drop, `Hole` will restore the slice by filling the hole /// position with the value that was originally removed. @@ -1305,7 +1381,7 @@ struct Hole<'a, T: 'a> { } impl<'a, T> Hole<'a, T> { - /// Create a new Hole at index `pos`. + /// Create a new `Hole` at index `pos`. /// /// Unsafe because pos must be within the data slice. #[inline] @@ -1371,11 +1447,10 @@ impl Drop for Hole<'_, T> { /// An iterator over the elements of a `BinaryHeap`. /// -/// This `struct` is created by the [`iter`] method on [`BinaryHeap`]. See its +/// This `struct` is created by [`BinaryHeap::iter()`]. See its /// documentation for more. /// -/// [`iter`]: struct.BinaryHeap.html#method.iter -/// [`BinaryHeap`]: struct.BinaryHeap.html +/// [`BinaryHeap::iter()`]: struct.BinaryHeap.html#method.iter #[must_use = "iterators are lazy and do nothing unless consumed"] // #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { @@ -1439,11 +1514,11 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { /// An owning iterator over the elements of a `BinaryHeap`. /// -/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`][`BinaryHeap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. +/// This `struct` is created by [`BinaryHeap::into_iter()`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. /// -/// [`into_iter`]: struct.BinaryHeap.html#method.into_iter -/// [`BinaryHeap`]: struct.BinaryHeap.html +/// [`BinaryHeap::into_iter()`]: struct.BinaryHeap.html#method.into_iter +/// [`IntoIterator`]: https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html // #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] pub struct IntoIter { @@ -1481,7 +1556,6 @@ impl DoubleEndedIterator for IntoIter { self.iter.next_back() } } - // #[stable(feature = "rust1", since = "1.0.0")] // impl ExactSizeIterator for IntoIter { // fn is_empty(&self) -> bool { @@ -1517,11 +1591,10 @@ impl> Iterator for IntoIterSorted { /// A draining iterator over the elements of a `BinaryHeap`. /// -/// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its +/// This `struct` is created by [`BinaryHeap::drain()`]. See its /// documentation for more. /// -/// [`drain`]: struct.BinaryHeap.html#method.drain -/// [`BinaryHeap`]: struct.BinaryHeap.html +/// [`BinaryHeap::drain()`]: struct.BinaryHeap.html#method.drain // #[stable(feature = "drain", since = "1.6.0")] // #[derive(Debug)] pub struct Drain<'a, T: 'a> { @@ -1563,7 +1636,9 @@ impl DoubleEndedIterator for Drain<'_, T> { // #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] impl From> for BinaryHeap { - /// creates a max heap from a vec + /// Converts a `Vec` into a `BinaryHeap`. + /// + /// This conversion happens in-place, and has *O*(*n*) time complexity. fn from(vec: Vec) -> Self { BinaryHeap::from_vec(vec) } @@ -1577,6 +1652,10 @@ impl From> for BinaryHeap { // } impl> Into> for BinaryHeap { + /// Converts a `BinaryHeap` into a `Vec`. + /// + /// This conversion requires no data movement or allocation, and has + /// constant time complexity. fn into(self) -> Vec { self.data } @@ -1603,7 +1682,7 @@ impl> IntoIterator for BinaryHeap { /// Basic usage: /// /// ``` - /// use binary_heap_plus::*; + /// use binary_heap_plus::BinaryHeap; /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); /// /// // Print 1, 2, 3, 4 in arbitrary order From 848c7845f3920180bd0a4c13882d72bcbf718b46 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Mon, 18 Jul 2022 20:30:39 -0400 Subject: [PATCH 09/21] Add more links in documentation Following rust-lang/rust#89010, add links to referenced items in the parts of this crate's documentation that unique here and not found in `std`. --- README.md | 7 ++-- src/binary_heap.rs | 12 +++++-- src/lib.rs | 84 ++++++++++++++++++++++++++++------------------ 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7bf6f66..2b67890 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ ![Rust](https://github.com/sekineh/binary-heap-plus-rs/workflows/Rust/badge.svg) -Enhancement over Rust's `std::collections::BinaryHeap`. +Enhancement over Rust's +[`std::collections::BinaryHeap`](https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html). It supports the following heaps and still maintains backward compatibility. - Max heap @@ -25,8 +26,8 @@ This crate requires Rust 1.36.0 or later. # Changes -See CHANGELOG.md. -https://github.com/sekineh/binary-heap-plus-rs/blob/master/CHANGELOG.md +See +[CHANGELOG.md](https://github.com/sekineh/binary-heap-plus-rs/blob/master/CHANGELOG.md). # Thanks diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 8fd8386..ce17a50 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -432,31 +432,37 @@ impl> fmt::Debug for BinaryHeap { } impl + Default> BinaryHeap { - /// Generic constructor for `BinaryHeap` from `Vec`. + /// Generic constructor for `BinaryHeap` from [`Vec`]. /// /// Because `BinaryHeap` stores the elements in its internal `Vec`, /// it's natural to construct it from `Vec`. + /// + /// [`Vec`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html pub fn from_vec(vec: Vec) -> Self { BinaryHeap::from_vec_cmp(vec, C::default()) } } impl> BinaryHeap { - /// Generic constructor for `BinaryHeap` from `Vec` and comparator. + /// Generic constructor for `BinaryHeap` from [`Vec`] and comparator. /// /// Because `BinaryHeap` stores the elements in its internal `Vec`, /// it's natural to construct it from `Vec`. + /// + /// [`Vec`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html pub fn from_vec_cmp(vec: Vec, cmp: C) -> Self { unsafe { BinaryHeap::from_vec_cmp_raw(vec, cmp, true) } } - /// Generic constructor for `BinaryHeap` from `Vec` and comparator. + /// Generic constructor for `BinaryHeap` from [`Vec`] and comparator. /// /// Because `BinaryHeap` stores the elements in its internal `Vec`, /// it's natural to construct it from `Vec`. /// /// # Safety /// User is responsible for providing valid `rebuild` value. + /// + /// [`Vec`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html pub unsafe fn from_vec_cmp_raw(vec: Vec, cmp: C, rebuild: bool) -> Self { let mut heap = BinaryHeap { data: vec, cmp }; if rebuild && !heap.data.is_empty() { diff --git a/src/lib.rs b/src/lib.rs index 51d92b0..1157238 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,62 +1,76 @@ -//! This crate provides `BinaryHeap` which is backward-compatible with `std::collections::BinaryHeap`. +//! This crate provides [`BinaryHeap`] which is backward-compatible with +//! [`std::collections::BinaryHeap`]. //! //! Added features include: //! * Heaps other than max heap. -//! * Optional `serde` feature. +//! * Optional [`serde`] feature. +//! +//! [`BinaryHeap`]: struct.BinaryHeap.html +//! [`std::collections::BinaryHeap`]: +//! https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html +//! [`serde`]: https://docs.serde.rs/serde/ //! //! # Quick start //! //! ## Max/Min Heap //! -//! For max heap, `BiaryHeap::from_vec()` is the most versatile way to create a heap. +//! For max heap, [`BinaryHeap::from_vec()`] is the most versatile way to create a heap. //! //! ```rust -//! use binary_heap_plus::*; +//! use binary_heap_plus::*; //! -//! // max heap -//! let mut h: BinaryHeap = BinaryHeap::from_vec(vec![]); -//! // max heap with initial capacity -//! let mut h: BinaryHeap = BinaryHeap::from_vec(Vec::with_capacity(16)); -//! // max heap from iterator -//! let mut h: BinaryHeap = BinaryHeap::from_vec((0..42).collect()); -//! assert_eq!(h.pop(), Some(41)); +//! // max heap +//! let mut h: BinaryHeap = BinaryHeap::from_vec(vec![]); +//! // max heap with initial capacity +//! let mut h: BinaryHeap = BinaryHeap::from_vec(Vec::with_capacity(16)); +//! // max heap from iterator +//! let mut h: BinaryHeap = BinaryHeap::from_vec((0..42).collect()); +//! assert_eq!(h.pop(), Some(41)); //! ``` //! //! Min heap is similar, but requires type annotation. //! //! ```rust -//! use binary_heap_plus::*; +//! use binary_heap_plus::*; //! -//! // min heap -//! let mut h: BinaryHeap = BinaryHeap::from_vec(vec![]); -//! // min heap with initial capacity -//! let mut h: BinaryHeap = BinaryHeap::from_vec(Vec::with_capacity(16)); -//! // min heap from iterator -//! let mut h: BinaryHeap = BinaryHeap::from_vec((0..42).collect()); -//! assert_eq!(h.pop(), Some(0)); +//! // min heap +//! let mut h: BinaryHeap = BinaryHeap::from_vec(vec![]); +//! // min heap with initial capacity +//! let mut h: BinaryHeap = BinaryHeap::from_vec(Vec::with_capacity(16)); +//! // min heap from iterator +//! let mut h: BinaryHeap = BinaryHeap::from_vec((0..42).collect()); +//! assert_eq!(h.pop(), Some(0)); //! ``` //! +//! [`BinaryHeap::from_vec()`]: struct.BinaryHeap.html#method.from_vec +//! //! ## Custom Heap //! -//! For custom heap, `BinaryHeap::from_vec_cmp()` works in a similar way to max/min heap. The only difference is that you add the comparator closure with apropriate signature. +//! For custom heap, [`BinaryHeap::from_vec_cmp()`] works in a similar way to max/min heap. The +//! only difference is that you add the comparator closure with apropriate signature. //! //! ```rust -//! use binary_heap_plus::*; +//! use binary_heap_plus::*; //! -//! // custom heap: ordered by second value (_.1) of the tuples; min first -//! let mut h = BinaryHeap::from_vec_cmp( -//! vec![(1, 5), (3, 2), (2, 3)], -//! |a: &(i32, i32), b: &(i32, i32)| b.1.cmp(&a.1), // comparator closure here -//! ); -//! assert_eq!(h.pop(), Some((3, 2))); +//! // custom heap: ordered by second value (_.1) of the tuples; min first +//! let mut h = BinaryHeap::from_vec_cmp( +//! vec![(1, 5), (3, 2), (2, 3)], +//! |a: &(i32, i32), b: &(i32, i32)| b.1.cmp(&a.1), // comparator closure here +//! ); +//! assert_eq!(h.pop(), Some((3, 2))); //! ``` //! +//! [`BinaryHeap::from_vec_cmp()`]: struct.BinaryHeap.html#method.from_vec_cmp +//! //! # Constructers //! //! ## Generic methods to create different kind of heaps from initial `vec` data. //! -//! * `BinaryHeap::from_vec(vec)` -//! * `BinaryHeap::from_vec_cmp(vec, cmp)` +//! * [`BinaryHeap::from_vec`]`(vec)` +//! * [`BinaryHeap::from_vec_cmp`]`(vec, cmp)` +//! +//! [`BinaryHeap::from_vec`]: struct.BinaryHeap.html#method.from_vec +//! [`BinaryHeap::from_vec_cmp`]: struct.BinaryHeap.html#method.from_vec_cmp //! //! ``` //! use binary_heap_plus::*; @@ -87,11 +101,15 @@ //! //! ## Dedicated methods to create different kind of heaps //! -//! * `BinaryHeap::new()` creates a max heap. -//! * `BinaryHeap::new_min()` creates a min heap. -//! * `BinaryHeap::new_by()` creates a heap sorted by the given closure. -//! * `BinaryHeap::new_by_key()` creates a heap sorted by the key generated by the given closure. +//! * [`BinaryHeap::new()`] creates a max heap. +//! * [`BinaryHeap::new_min()`] creates a min heap. +//! * [`BinaryHeap::new_by()`] creates a heap sorted by the given closure. +//! * [`BinaryHeap::new_by_key()`] creates a heap sorted by the key generated by the given closure. //! +//! [`BinaryHeap::new()`]: struct.BinaryHeap.html#method.new +//! [`BinaryHeap::new_min()`]: struct.BinaryHeap.html#method.new_min +//! [`BinaryHeap::new_by()`]: struct.BinaryHeap.html#method.new_by +//! [`BinaryHeap::new_by_key()`]: struct.BinaryHeap.html#method.new_by_key mod binary_heap; pub use crate::binary_heap::*; From 6e6e2448861b1bee5de6aed6c545602680f67abd Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:11:57 -0400 Subject: [PATCH 10/21] Use provided methods of `Compare` trait --- src/binary_heap.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index ce17a50..7508171 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1081,8 +1081,7 @@ impl> BinaryHeap { // it's a valid index and also != hole.pos(). if self .cmp - .compare(hole.element(), unsafe { hole.get(parent) }) - != Ordering::Greater + .compares_le(hole.element(), unsafe { hole.get(parent) }) { break; } @@ -1114,14 +1113,15 @@ impl> BinaryHeap { // child + 1 == 2 * hole.pos() + 2 != hole.pos(). // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow // if T is a ZST - child += unsafe { - self.cmp.compare(hole.get(child), hole.get(child + 1)) != Ordering::Greater - } as usize; + child += unsafe { self.cmp.compares_le(hole.get(child), hole.get(child + 1)) } as usize; // if we are already in order, stop. // SAFETY: child is now either the old child or the old child+1 // We already proven that both are < self.len() and != hole.pos() - if self.cmp.compare(hole.element(), unsafe { hole.get(child) }) != Ordering::Less { + if self + .cmp + .compares_ge(hole.element(), unsafe { hole.get(child) }) + { return; } @@ -1133,7 +1133,9 @@ impl> BinaryHeap { // SAFETY: && short circuit, which means that in the // second condition it's already true that child == end - 1 < self.len(). if child == end - 1 - && self.cmp.compare(hole.element(), unsafe { hole.get(child) }) == Ordering::Less + && self + .cmp + .compares_lt(hole.element(), unsafe { hole.get(child) }) { // SAFETY: child is already proven to be a valid index and // child == 2 * hole.pos() + 1 != hole.pos(). @@ -1176,9 +1178,7 @@ impl> BinaryHeap { // child + 1 == 2 * hole.pos() + 2 != hole.pos(). // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow // if T is a ZST - child += unsafe { - self.cmp.compare(hole.get(child), hole.get(child + 1)) != Ordering::Greater - } as usize; + child += unsafe { self.cmp.compares_le(hole.get(child), hole.get(child + 1)) } as usize; // SAFETY: Same as above unsafe { hole.move_to(child) }; From b47dbbaf6cdc6912735e66e9467f3b229fc7af6b Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Fri, 22 Jul 2022 20:40:57 -0400 Subject: [PATCH 11/21] Add additional test from `alloc` This test is taken from `library/alloc/src/collections/binary_heap/tests.rs` in Rust 1.62.0. Note that the setup in the test is adapted to this crate by implementing `Ord` instead of `PartialOrd` for `PanicOrd` as is done upstream, since the heap operations here rely on the `Ord` trait whereas upstream they rely on the `PartialOrd` trait. --- src/lib.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1157238..04a3420 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -430,6 +430,93 @@ mod from_liballoc { d } } + + // old binaryheap failed this test + // + // Integrity means that all elements are present after a comparison panics, + // even if the order might not be correct. + // + // Destructors must be called exactly once per element. + // FIXME: re-enable emscripten once it can unwind again + #[test] + #[cfg(not(target_os = "emscripten"))] + fn panic_safe() { + use std::cmp; + use std::panic::{self, AssertUnwindSafe}; + use std::sync::atomic::{AtomicUsize, Ordering}; + + use rand::{seq::SliceRandom, thread_rng}; + + static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); + + #[derive(Eq, PartialEq, PartialOrd, Clone, Debug)] + struct PanicOrd(T, bool); + + impl Drop for PanicOrd { + fn drop(&mut self) { + // update global drop count + DROP_COUNTER.fetch_add(1, Ordering::SeqCst); + } + } + + impl Ord for PanicOrd { + fn cmp(&self, other: &Self) -> cmp::Ordering { + if self.1 || other.1 { + panic!("Panicking comparison"); + } + self.0.cmp(&other.0) + } + } + let mut rng = thread_rng(); + const DATASZ: usize = 32; + // Miri is too slow + let ntest = if cfg!(miri) { 1 } else { 10 }; + + // don't use 0 in the data -- we want to catch the zeroed-out case. + let data = (1..=DATASZ).collect::>(); + + // since it's a fuzzy test, run several tries. + for _ in 0..ntest { + for i in 1..=DATASZ { + DROP_COUNTER.store(0, Ordering::SeqCst); + + let mut panic_ords: Vec<_> = data + .iter() + .filter(|&&x| x != i) + .map(|&x| PanicOrd(x, false)) + .collect(); + let panic_item = PanicOrd(i, true); + + // heapify the sane items + panic_ords.shuffle(&mut rng); + let mut heap = BinaryHeap::from(panic_ords); + let inner_data; + + { + // push the panicking item to the heap and catch the panic + let thread_result = { + let mut heap_ref = AssertUnwindSafe(&mut heap); + panic::catch_unwind(move || { + heap_ref.push(panic_item); + }) + }; + assert!(thread_result.is_err()); + + // Assert no elements were dropped + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert!(drops == 0, "Must not drop items. drops={}", drops); + inner_data = heap.clone().into_vec(); + drop(heap); + } + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert_eq!(drops, DATASZ); + + let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::>(); + data_sorted.sort(); + assert_eq!(data_sorted, data); + } + } + } } #[cfg(feature = "serde")] From 9bd3739781485ef7c7abed5ec601d2c49f9d8154 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sat, 23 Jul 2022 15:15:10 -0400 Subject: [PATCH 12/21] Remove license from within source file The license was removed from `binary_heap.rs` in rust-lang/rust#57108 after it was determined that the per-file license headers were not necessary. See also discussion in rust-lang/rust PRs #53654, #53617, and #43498. --- src/binary_heap.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 7508171..89e1046 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1,13 +1,3 @@ -// Copyright 2013-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - //! A priority queue implemented with a binary heap. //! //! Note: This version is folked from Rust standartd library, which only supports From 61eb0e69379c8a44964d960600a250315ed45131 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:06:04 -0400 Subject: [PATCH 13/21] Add `shrink_to()` method, stabilized in Rust 1.56.0 See rust-lang/rust#86879. --- build.rs | 3 +++ src/binary_heap.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/build.rs b/build.rs index 10a4ea0..a25664d 100644 --- a/build.rs +++ b/build.rs @@ -4,5 +4,8 @@ fn main() { // Required for stabilization of `unsafe_op_in_unsafe_fn` lint. ac.emit_rustc_version(1, 52); + // Required for stabilization of `Vec::shrink_to()`. + ac.emit_rustc_version(1, 56); + autocfg::rerun_path("build.rs"); } diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 89e1046..432d382 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -911,6 +911,33 @@ impl> BinaryHeap { self.data.shrink_to_fit(); } + /// Discards capacity with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to(10); + /// assert!(heap.capacity() >= 10); + /// ``` + /// + /// # Compatibility + /// + /// This feature requires Rust 1.56.0 or greater. + #[inline] + #[cfg(rustc_1_56)] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.data.shrink_to(min_capacity) + } + /// Removes the greatest item from the binary heap and returns it, or `None` if it /// is empty. /// From b3db7f1770e851c0d79a4f66c10fbb392e8ca4bb Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sat, 30 Jul 2022 20:19:24 -0400 Subject: [PATCH 14/21] Implement `From>` for `Vec` when possible This requires Rust 1.41.0 or greater. --- build.rs | 3 +++ src/binary_heap.rs | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/build.rs b/build.rs index a25664d..81fc872 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,9 @@ fn main() { let ac = autocfg::new(); + // Required in order to implement `From>` for `Vec`. + ac.emit_rustc_version(1, 41); + // Required for stabilization of `unsafe_op_in_unsafe_fn` lint. ac.emit_rustc_version(1, 52); diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 432d382..a94c8e7 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1667,13 +1667,22 @@ impl From> for BinaryHeap { } } -// #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -// impl> From> for Vec { -// fn from(heap: BinaryHeap) -> Vec { -// heap.data -// } -// } +/// # Compatibility +/// +/// This trait is only implemented for Rust 1.41.0 or greater. For earlier versions, `Into>` +/// is implemented for `BinaryHeap` instead. +#[cfg(rustc_1_41)] +impl> From> for Vec { + /// Converts a `BinaryHeap` into a `Vec`. + /// + /// This conversion requires no data movement or allocation, and has + /// constant time complexity. + fn from(heap: BinaryHeap) -> Vec { + heap.data + } +} +#[cfg(not(rustc_1_41))] impl> Into> for BinaryHeap { /// Converts a `BinaryHeap` into a `Vec`. /// From 0e5ce1a11036ba476c0a2bf5471b7a9f89027fc5 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 31 Jul 2022 11:36:34 -0400 Subject: [PATCH 15/21] Relax some trait bounds on `BinaryHeap` This is inspired by rust-lang/rust#58421: "Relax some `Ord` bounds on `BinaryHeap`", which split out the methods of `BinaryHeap` which do not require the bound `T: Ord` to a separate impl block. Note that in order to do something similar here, we also have to remove the trait bound `C: Compare` from the definition of the struct `BinaryHeap`; the upstream definition of `BinaryHeap` did not have the analogous bound `T: Ord` on the struct definition in the first place. --- src/binary_heap.rs | 637 ++++++++++++++++++++++----------------------- 1 file changed, 318 insertions(+), 319 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index a94c8e7..319ba6a 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -270,10 +270,7 @@ use std::vec; /// [peek\_mut]: #method.peek_mut // #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct BinaryHeap -where - C: Compare, -{ +pub struct BinaryHeap { data: Vec, cmp: C, } @@ -392,7 +389,7 @@ impl<'a, T, C: Compare> PeekMut<'a, T, C> { } // #[stable(feature = "rust1", since = "1.0.0")] -impl + Clone> Clone for BinaryHeap { +impl Clone for BinaryHeap { fn clone(&self) -> Self { BinaryHeap { data: self.data.clone(), @@ -415,7 +412,7 @@ impl Default for BinaryHeap { } // #[stable(feature = "binaryheap_debug", since = "1.4.0")] -impl> fmt::Debug for BinaryHeap { +impl fmt::Debug for BinaryHeap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } @@ -713,74 +710,6 @@ impl> BinaryHeap { } } - /// Returns an iterator visiting all values in the underlying vector, in - /// arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order - /// for x in heap.iter() { - /// println!("{}", x); - /// } - /// ``` - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { - iter: self.data.iter(), - } - } - - /// Returns an iterator which retrieves elements in heap order. - /// This method consumes the original heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); - /// ``` - // #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] - pub fn into_iter_sorted(self) -> IntoIterSorted { - IntoIterSorted { inner: self } - } - - /// Returns the greatest item in the binary heap, or `None` if it is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// assert_eq!(heap.peek(), None); - /// - /// heap.push(1); - /// heap.push(5); - /// heap.push(2); - /// assert_eq!(heap.peek(), Some(&5)); - /// - /// ``` - /// - /// # Time complexity - /// - /// Cost is *O*(1) in the worst case. - #[must_use] - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn peek(&self) -> Option<&T> { - self.data.get(0) - } - /// Returns a mutable reference to the greatest item in the binary heap, or /// `None` if it is empty. /// @@ -822,122 +751,6 @@ impl> BinaryHeap { } } - /// Returns the number of elements the binary heap can hold without reallocating. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - #[must_use] - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.data.capacity() - } - - /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the - /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it requests. Therefore - /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future - /// insertions are expected. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve_exact(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - /// - /// [`reserve`]: #method.reserve - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.data.reserve_exact(additional); - } - - /// Reserves capacity for at least `additional` more elements to be inserted in the - /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.data.reserve(additional); - } - - /// Discards as much additional capacity as possible. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to_fit(); - /// assert!(heap.capacity() == 0); - /// ``` - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.data.shrink_to_fit(); - } - - /// Discards capacity with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// If the current capacity is less than the lower limit, this is a no-op. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to(10); - /// assert!(heap.capacity() >= 10); - /// ``` - /// - /// # Compatibility - /// - /// This feature requires Rust 1.56.0 or greater. - #[inline] - #[cfg(rustc_1_56)] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.data.shrink_to(min_capacity) - } - /// Removes the greatest item from the binary heap and returns it, or `None` if it /// is empty. /// @@ -1010,29 +823,6 @@ impl> BinaryHeap { unsafe { self.sift_up(0, old_len) }; } - /// Consumes the `BinaryHeap` and returns the underlying vector - /// in arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); - /// let vec = heap.into_vec(); - /// - /// // Will print in some order - /// for x in vec { - /// println!("{}", x); - /// } - /// ``` - #[must_use = "`self` will be dropped if the result is not used"] - // #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_vec(self) -> Vec { - self.into() - } - /// Consumes the `BinaryHeap` and returns a vector in sorted /// (ascending) order. /// @@ -1215,107 +1005,13 @@ impl> BinaryHeap { unsafe { self.sift_up(start, pos) }; } - /// Returns the length of the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert_eq!(heap.len(), 2); - /// ``` - #[must_use] - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.data.len() - } + /// Rebuild assuming data[0..start] is still a proper heap. + fn rebuild_tail(&mut self, start: usize) { + if start == self.len() { + return; + } - /// Checks if the binary heap is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// - /// assert!(heap.is_empty()); - /// - /// heap.push(3); - /// heap.push(5); - /// heap.push(1); - /// - /// assert!(!heap.is_empty()); - /// ``` - #[must_use] - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Clears the binary heap, returning an iterator over the removed elements - /// in arbitrary order. If the iterator is dropped before being fully - /// consumed, it drops the remaining elements in arbitrary order. - /// - /// The returned iterator keeps a mutable borrow on the heap to optimize - /// its implementation. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// for x in heap.drain() { - /// println!("{}", x); - /// } - /// - /// assert!(heap.is_empty()); - /// ``` - #[inline] - // #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { - Drain { - iter: self.data.drain(..), - } - } - - /// Drops all items from the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use binary_heap_plus::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// heap.clear(); - /// - /// assert!(heap.is_empty()); - /// ``` - // #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.drain(); - } - - /// Rebuild assuming data[0..start] is still a proper heap. - fn rebuild_tail(&mut self, start: usize) { - if start == self.len() { - return; - } - - let tail_len = self.len() - start; + let tail_len = self.len() - start; // `usize::BITS` requires Rust 1.53.0 or greater. #[allow(clippy::manual_bits)] @@ -1393,6 +1089,309 @@ impl> BinaryHeap { } } +impl BinaryHeap { + /// Returns an iterator visiting all values in the underlying vector, in + /// arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// + /// // Print 1, 2, 3, 4 in arbitrary order + /// for x in heap.iter() { + /// println!("{}", x); + /// } + /// ``` + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { + iter: self.data.iter(), + } + } + + /// Returns an iterator which retrieves elements in heap order. + /// This method consumes the original heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), vec![5, 4]); + /// ``` + // #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] + pub fn into_iter_sorted(self) -> IntoIterSorted { + IntoIterSorted { inner: self } + } + + /// Returns the greatest item in the binary heap, or `None` if it is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert_eq!(heap.peek(), None); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// assert_eq!(heap.peek(), Some(&5)); + /// + /// ``` + /// + /// # Time complexity + /// + /// Cost is *O*(1) in the worst case. + #[must_use] + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn peek(&self) -> Option<&T> { + self.data.get(0) + } + + /// Returns the number of elements the binary heap can hold without reallocating. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + #[must_use] + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.data.capacity() + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future + /// insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve_exact(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + /// + /// [`reserve`]: #method.reserve + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.data.reserve_exact(additional); + } + + /// Reserves capacity for at least `additional` more elements to be inserted in the + /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.data.reserve(additional); + } + + /// Discards as much additional capacity as possible. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to_fit(); + /// assert!(heap.capacity() == 0); + /// ``` + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.data.shrink_to_fit(); + } + + /// Discards capacity with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to(10); + /// assert!(heap.capacity() >= 10); + /// ``` + /// + /// # Compatibility + /// + /// This feature requires Rust 1.56.0 or greater. + #[inline] + #[cfg(rustc_1_56)] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.data.shrink_to(min_capacity) + } + + /// Consumes the `BinaryHeap` and returns the underlying vector + /// in arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// let vec = heap.into_vec(); + /// + /// // Will print in some order + /// for x in vec { + /// println!("{}", x); + /// } + /// ``` + #[must_use = "`self` will be dropped if the result is not used"] + // #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] + pub fn into_vec(self) -> Vec { + self.into() + } + + /// Returns the length of the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert_eq!(heap.len(), 2); + /// ``` + #[must_use] + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.data.len() + } + + /// Checks if the binary heap is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// + /// assert!(heap.is_empty()); + /// + /// heap.push(3); + /// heap.push(5); + /// heap.push(1); + /// + /// assert!(!heap.is_empty()); + /// ``` + #[must_use] + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Clears the binary heap, returning an iterator over the removed elements + /// in arbitrary order. If the iterator is dropped before being fully + /// consumed, it drops the remaining elements in arbitrary order. + /// + /// The returned iterator keeps a mutable borrow on the heap to optimize + /// its implementation. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// for x in heap.drain() { + /// println!("{}", x); + /// } + /// + /// assert!(heap.is_empty()); + /// ``` + #[inline] + // #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self) -> Drain<'_, T> { + Drain { + iter: self.data.drain(..), + } + } + + /// Drops all items from the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// heap.clear(); + /// + /// assert!(heap.is_empty()); + /// ``` + // #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.drain(); + } +} + /// Hole represents a hole in a slice i.e., an index without valid value /// (because it was moved from or duplicated). /// In drop, `Hole` will restore the slice by filling the hole @@ -1592,7 +1591,7 @@ impl DoubleEndedIterator for IntoIter { #[must_use = "iterators are lazy and do nothing unless consumed"] // #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] #[derive(Clone, Debug)] -pub struct IntoIterSorted> { +pub struct IntoIterSorted { inner: BinaryHeap, } @@ -1619,7 +1618,7 @@ impl> Iterator for IntoIterSorted { /// /// [`BinaryHeap::drain()`]: struct.BinaryHeap.html#method.drain // #[stable(feature = "drain", since = "1.6.0")] -// #[derive(Debug)] +#[derive(Debug)] pub struct Drain<'a, T: 'a> { iter: vec::Drain<'a, T>, } @@ -1672,7 +1671,7 @@ impl From> for BinaryHeap { /// This trait is only implemented for Rust 1.41.0 or greater. For earlier versions, `Into>` /// is implemented for `BinaryHeap` instead. #[cfg(rustc_1_41)] -impl> From> for Vec { +impl From> for Vec { /// Converts a `BinaryHeap` into a `Vec`. /// /// This conversion requires no data movement or allocation, and has @@ -1683,7 +1682,7 @@ impl> From> for Vec { } #[cfg(not(rustc_1_41))] -impl> Into> for BinaryHeap { +impl Into> for BinaryHeap { /// Converts a `BinaryHeap` into a `Vec`. /// /// This conversion requires no data movement or allocation, and has @@ -1701,7 +1700,7 @@ impl FromIterator for BinaryHeap { } // #[stable(feature = "rust1", since = "1.0.0")] -impl> IntoIterator for BinaryHeap { +impl IntoIterator for BinaryHeap { type Item = T; type IntoIter = IntoIter; @@ -1731,7 +1730,7 @@ impl> IntoIterator for BinaryHeap { } // #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, C: Compare> IntoIterator for &'a BinaryHeap { +impl<'a, T, C> IntoIterator for &'a BinaryHeap { type Item = &'a T; type IntoIter = Iter<'a, T>; From 5837ac22681ef931e326161705e1899c0db6ccf0 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sun, 31 Jul 2022 13:50:31 -0400 Subject: [PATCH 16/21] Implement `From<[T; N]>` for `BinaryHeap` Port rust-lang/rust PR's implementing `From<[T; N]>` for `BinaryHeap`: - #84111: "Stabilize `impl From<[(K, V); N]> for HashMap` (and friends)" - #88611: "Deprecate array::IntoIter::new." NOTE: This requires Rust 1.56.0 or greater. --- build.rs | 2 +- src/binary_heap.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 81fc872..7fd5c35 100644 --- a/build.rs +++ b/build.rs @@ -7,7 +7,7 @@ fn main() { // Required for stabilization of `unsafe_op_in_unsafe_fn` lint. ac.emit_rustc_version(1, 52); - // Required for stabilization of `Vec::shrink_to()`. + // Required for stabilization of `Vec::shrink_to()` and `IntoIterator` for arrays. ac.emit_rustc_version(1, 56); autocfg::rerun_path("build.rs"); diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 319ba6a..137afbc 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1666,6 +1666,25 @@ impl From> for BinaryHeap { } } +/// # Compatibility +/// +/// This trait is only implemented for Rust 1.56.0 or greater. +#[cfg(rustc_1_56)] +impl From<[T; N]> for BinaryHeap { + /// ``` + /// use binary_heap_plus::BinaryHeap; + /// + /// let mut h1 = BinaryHeap::from([1, 4, 2, 3]); + /// let mut h2: BinaryHeap<_> = [1, 4, 2, 3].into(); + /// while let Some((a, b)) = h1.pop().zip(h2.pop()) { + /// assert_eq!(a, b); + /// } + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + /// # Compatibility /// /// This trait is only implemented for Rust 1.41.0 or greater. For earlier versions, `Into>` From ae09fe718fcb455f9824317bc442750f9f2512bc Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sat, 6 Aug 2022 16:41:42 -0400 Subject: [PATCH 17/21] Port updated example from rust-lang/rust This commits ports one small part of rust-lang/rust#91861. The main change that PR made to `binary_heap.rs` was to replace vectors with arrays in the examples. We do *not* port such changes as they require Rust 1.56.0, which is greater than this crate's current MSRV. However, it also simplified the setup in the example of `BinaryHeap::append()`, and we repeat that here only with vectors instead of arrays. --- src/binary_heap.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 137afbc..3cd76b5 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -1064,11 +1064,8 @@ impl> BinaryHeap { /// ``` /// use binary_heap_plus::BinaryHeap; /// - /// let v = vec![-10, 1, 2, 3, 3]; - /// let mut a = BinaryHeap::from(v); - /// - /// let v = vec![-20, 5, 43]; - /// let mut b = BinaryHeap::from(v); + /// let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]); + /// let mut b = BinaryHeap::from(vec![-20, 5, 43]); /// /// a.append(&mut b); /// From 722ec58caa42ff4908b6d35029e232ab4dfeee00 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:27:23 -0400 Subject: [PATCH 18/21] Update CHANGELOG.md and document compatibility --- CHANGELOG.md | 22 ++++++++++++++++++++++ README.md | 10 ++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf6db5..06ff294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +* `#[must_use]` attribute to many methods, porting and extending several + rust-lang/rust PRs +* Method `shrink_to()` for Rust 1.56.0 and greater, ported from rust-lang/rust +* Implementation of `From>` for `Vec` for Rust 1.41.0 or + greater +* Implementation of `From<[T; N]>` for `BinaryHeap` for Rust 1.56.0 or + greater, ported from rust-lang/rust#84111 +* Links to referenced items in the documenation +* Example of a min-heap, ported from rust-lang/rust#60451 +* Documentation of time complexities of several methods, ported from + rust-lang/rust#60952 + ### Changed * Bump MSRV (minimum supported rust version) to rust 1.36.0. +* Port rust-lang/rust#77435 improvement to rebuild heuristic of + `BinaryHeap::append()` +* Use italics with big-O notation in documentation, ported from + rust-lang/rust#71167 +* Relax trait bound `C: Compare` on `BinaryHeap` struct and certain + methods, in part ported from rust-lang/rust#58421 +* Synchronize internal implementation details with + `std::collections::BinaryHeap` in Rust 1.62.0 ## [0.4.1] - 2021-01-06 diff --git a/README.md b/README.md index 2b67890..3559354 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,15 @@ Other notable added methods are: - `.into_iter_sorted()` which is less-surprising version of `.into_iter()`. The implementation is backported from `std`. - `.replace_cmp()` which replace the comparator of the existing heap. -## MSRV (Minimum Supported Rust Version) +## Compatibility and MSRV (Minimum Supported Rust Version) -This crate requires Rust 1.36.0 or later. +This crate is based on the standard library's implementation of +[`BinaryHeap`](https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html) +from Rust 1.62.0. + +This crate requires Rust 1.36.0 or later. A few of its APIs are only available +for more recent versions of Rust where they have been stabilized in the +standard library; see the documentation for specifics. # Changes From 9845c7cb14c8d50a3b7dad6a824876265f797793 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:07:30 -0400 Subject: [PATCH 19/21] Fix name of `cfg` value --- src/binary_heap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/binary_heap.rs b/src/binary_heap.rs index 3cd76b5..b820c8c 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -146,8 +146,8 @@ //! } //! ``` -#![cfg_attr(has_rustc_1_52, deny(unsafe_op_in_unsafe_fn))] -#![cfg_attr(not(has_rustc_1_52), allow(unused_unsafe))] +#![cfg_attr(rustc_1_52, deny(unsafe_op_in_unsafe_fn))] +#![cfg_attr(not(rustc_1_52), allow(unused_unsafe))] #![allow(clippy::needless_doctest_main)] #![allow(missing_docs)] // #![stable(feature = "rust1", since = "1.0.0")] From 851b5f65ea38c017e5efcef690417f9f41253ffc Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sat, 17 Sep 2022 11:32:58 -0400 Subject: [PATCH 20/21] Run CI on Rust versions with conditional compilation --- .github/workflows/rust.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index bf15857..186deaa 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,9 +21,20 @@ jobs: - "" - --features serde include: + # Also test on nightly and Rust versions between MSRV and stable + # where there is conditional compilation - os: ubuntu-latest rust: nightly cargo_args: "" + - os: ubuntu-latest + rust: 1.41.0 + cargo_args: "" + - os: ubuntu-latest + rust: 1.52.0 + cargo_args: "" + - os: ubuntu-latest + rust: 1.56.0 + cargo_args: "" runs-on: ${{ matrix.os }} From 8f153a375b0c0782127bed15c9de6f7355bda7d8 Mon Sep 17 00:00:00 2001 From: Clint White <104277906+clint-white@users.noreply.github.com> Date: Sat, 17 Sep 2022 16:41:00 -0400 Subject: [PATCH 21/21] Increase MSRV to 1.52.0 - Unconditionally set `deny(unsafe_op_in_unsafe_fn)` - Unconditionally implement `From>` for `Vec` and remove conditional implementation of `Into>` for `BinaryHeap` - Use intra-doc links --- .github/workflows/rust.yml | 8 +------- CHANGELOG.md | 6 +++--- README.md | 2 +- build.rs | 6 ------ src/binary_heap.rs | 38 +++++++------------------------------- 5 files changed, 12 insertions(+), 48 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 186deaa..32ed9ee 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,7 @@ jobs: - macos-latest rust: - stable - - 1.36.0 # MSRV (Rust 2018 and dev-dependency `serde_json`) + - 1.52.0 # MSRV cargo_args: - "" - --features serde @@ -26,12 +26,6 @@ jobs: - os: ubuntu-latest rust: nightly cargo_args: "" - - os: ubuntu-latest - rust: 1.41.0 - cargo_args: "" - - os: ubuntu-latest - rust: 1.52.0 - cargo_args: "" - os: ubuntu-latest rust: 1.56.0 cargo_args: "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ff294..baac573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `#[must_use]` attribute to many methods, porting and extending several rust-lang/rust PRs * Method `shrink_to()` for Rust 1.56.0 and greater, ported from rust-lang/rust -* Implementation of `From>` for `Vec` for Rust 1.41.0 or - greater * Implementation of `From<[T; N]>` for `BinaryHeap` for Rust 1.56.0 or greater, ported from rust-lang/rust#84111 * Links to referenced items in the documenation @@ -22,7 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -* Bump MSRV (minimum supported rust version) to rust 1.36.0. +* Bump MSRV (minimum supported rust version) to rust 1.52.0. +* Implement `From>` for `Vec` instead of `Into>` for + `BinaryHeap` * Port rust-lang/rust#77435 improvement to rebuild heuristic of `BinaryHeap::append()` * Use italics with big-O notation in documentation, ported from diff --git a/README.md b/README.md index 3559354..070c6cf 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ This crate is based on the standard library's implementation of [`BinaryHeap`](https://doc.rust-lang.org/stable/std/collections/struct.BinaryHeap.html) from Rust 1.62.0. -This crate requires Rust 1.36.0 or later. A few of its APIs are only available +This crate requires Rust 1.52.0 or later. A few of its APIs are only available for more recent versions of Rust where they have been stabilized in the standard library; see the documentation for specifics. diff --git a/build.rs b/build.rs index 7fd5c35..e50a3cf 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,6 @@ fn main() { let ac = autocfg::new(); - // Required in order to implement `From>` for `Vec`. - ac.emit_rustc_version(1, 41); - - // Required for stabilization of `unsafe_op_in_unsafe_fn` lint. - ac.emit_rustc_version(1, 52); - // Required for stabilization of `Vec::shrink_to()` and `IntoIterator` for arrays. ac.emit_rustc_version(1, 56); diff --git a/src/binary_heap.rs b/src/binary_heap.rs index b820c8c..73baae6 100644 --- a/src/binary_heap.rs +++ b/src/binary_heap.rs @@ -18,7 +18,6 @@ //! [dijkstra]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm //! [sssp]: https://en.wikipedia.org/wiki/Shortest_path_problem //! [dir_graph]: https://en.wikipedia.org/wiki/Directed_graph -//! [`BinaryHeap`]: struct.BinaryHeap.html //! //! ``` //! use std::cmp::Ordering; @@ -146,8 +145,7 @@ //! } //! ``` -#![cfg_attr(rustc_1_52, deny(unsafe_op_in_unsafe_fn))] -#![cfg_attr(not(rustc_1_52), allow(unused_unsafe))] +#![deny(unsafe_op_in_unsafe_fn)] #![allow(clippy::needless_doctest_main)] #![allow(missing_docs)] // #![stable(feature = "rust1", since = "1.0.0")] @@ -264,10 +262,10 @@ use std::vec; /// [`Ord`]: https://doc.rust-lang.org/stable/core/cmp/trait.Ord.html /// [`Cell`]: https://doc.rust-lang.org/stable/core/cell/struct.Cell.html /// [`RefCell`]: https://doc.rust-lang.org/stable/core/cell/struct.RefCell.html -/// [push]: #method.push -/// [pop]: #method.pop -/// [peek]: #method.peek -/// [peek\_mut]: #method.peek_mut +/// [push]: BinaryHeap::push +/// [pop]: BinaryHeap::pop +/// [peek]: BinaryHeap::peek +/// [peek\_mut]: BinaryHeap::peek_mut // #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BinaryHeap { @@ -333,8 +331,7 @@ where /// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See /// its documentation for more. /// -/// [`peek_mut`]: struct.BinaryHeap.html#method.peek_mut -/// [`BinaryHeap`]: struct.BinaryHeap.html +/// [`peek_mut`]: BinaryHeap::peek_mut // #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] pub struct PeekMut<'a, T: 'a, C: 'a + Compare> { heap: &'a mut BinaryHeap, @@ -1196,7 +1193,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` /// - /// [`reserve`]: #method.reserve + /// [`reserve`]: BinaryHeap::reserve // #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve_exact(&mut self, additional: usize) { self.data.reserve_exact(additional); @@ -1468,8 +1465,6 @@ impl Drop for Hole<'_, T> { /// /// This `struct` is created by [`BinaryHeap::iter()`]. See its /// documentation for more. -/// -/// [`BinaryHeap::iter()`]: struct.BinaryHeap.html#method.iter #[must_use = "iterators are lazy and do nothing unless consumed"] // #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { @@ -1536,7 +1531,6 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { /// This `struct` is created by [`BinaryHeap::into_iter()`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. /// -/// [`BinaryHeap::into_iter()`]: struct.BinaryHeap.html#method.into_iter /// [`IntoIterator`]: https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html // #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] @@ -1612,8 +1606,6 @@ impl> Iterator for IntoIterSorted { /// /// This `struct` is created by [`BinaryHeap::drain()`]. See its /// documentation for more. -/// -/// [`BinaryHeap::drain()`]: struct.BinaryHeap.html#method.drain // #[stable(feature = "drain", since = "1.6.0")] #[derive(Debug)] pub struct Drain<'a, T: 'a> { @@ -1682,11 +1674,6 @@ impl From<[T; N]> for BinaryHeap { } } -/// # Compatibility -/// -/// This trait is only implemented for Rust 1.41.0 or greater. For earlier versions, `Into>` -/// is implemented for `BinaryHeap` instead. -#[cfg(rustc_1_41)] impl From> for Vec { /// Converts a `BinaryHeap` into a `Vec`. /// @@ -1697,17 +1684,6 @@ impl From> for Vec { } } -#[cfg(not(rustc_1_41))] -impl Into> for BinaryHeap { - /// Converts a `BinaryHeap` into a `Vec`. - /// - /// This conversion requires no data movement or allocation, and has - /// constant time complexity. - fn into(self) -> Vec { - self.data - } -} - // #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator for BinaryHeap { fn from_iter>(iter: I) -> Self {