From 92bfef62ae418fa6c8a63adc19021971ee769159 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 22 Aug 2025 10:40:38 -0700 Subject: [PATCH 1/8] Merge pull request indexmap#405 from cuviper/replace_index Add methods to replace the key at a given index (cherry picked from commit a168b1736d91b873b7eec3ed960a0f72b40ad3ee) --- Cargo.toml | 2 +- src/map.rs | 20 ++++++++++++++++++ src/map/entry.rs | 12 +++++++++++ src/set.rs | 16 ++++++++++++++ tests/quick.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 82d846e..c9d1169 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ rust-version = "1.63" bench = false [dependencies] -indexmap = { version = "2.10.0", default-features = false } +indexmap = { version = "2.11.0", default-features = false } arbitrary = { version = "1.0", optional = true, default-features = false } quickcheck = { version = "1.0", optional = true, default-features = false } diff --git a/src/map.rs b/src/map.rs index ae64ca9..91e2a4b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -581,6 +581,26 @@ where self.inner.shift_insert(index, key, value) } + /// Replaces the key at the given index. The new key does not need to be + /// equivalent to the one it is replacing, but it must be unique to the rest + /// of the map. + /// + /// Returns `Ok(old_key)` if successful, or `Err((other_index, key))` if an + /// equivalent key already exists at a different index. The map will be + /// unchanged in the error case. + /// + /// Direct indexing can be used to change the corresponding value: simply + /// `map[index] = value`, or `mem::replace(&mut map[index], value)` to + /// retrieve the old value as well. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(1)** time (average). + #[track_caller] + pub fn replace_index(&mut self, index: usize, key: K) -> Result { + self.inner.replace_index(index, key) + } + /// Get the given key’s corresponding entry in the map for insertion and/or /// in-place manipulation. /// diff --git a/src/map/entry.rs b/src/map/entry.rs index 7bf1533..def5b7a 100644 --- a/src/map/entry.rs +++ b/src/map/entry.rs @@ -336,6 +336,18 @@ impl<'a, K, V> VacantEntry<'a, K, V> { pub fn shift_insert(self, index: usize, value: V) -> &'a mut V { self.inner.shift_insert(index, value) } + + /// Replaces the key at the given index with this entry's key, returning the + /// old key and an `OccupiedEntry` for that index. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(1)** time (average). + #[track_caller] + pub fn replace_index(self, index: usize) -> (K, OccupiedEntry<'a, K, V>) { + let (old_key, inner) = self.inner.replace_index(index); + (old_key, OccupiedEntry { inner }) + } } impl fmt::Debug for VacantEntry<'_, K, V> { diff --git a/src/set.rs b/src/set.rs index e6c7747..8a5acab 100644 --- a/src/set.rs +++ b/src/set.rs @@ -528,6 +528,22 @@ where self.inner.replace_full(value) } + /// Replaces the value at the given index. The new value does not need to be + /// equivalent to the one it is replacing, but it must be unique to the rest + /// of the set. + /// + /// Returns `Ok(old_value)` if successful, or `Err((other_index, value))` if + /// an equivalent value already exists at a different index. The set will be + /// unchanged in the error case. + /// + /// ***Panics*** if `index` is out of bounds. + /// + /// Computes in **O(1)** time (average). + #[track_caller] + pub fn replace_index(&mut self, index: usize, value: T) -> Result { + self.inner.replace_index(index, value) + } + /// Return an iterator over the values that are in `self` but not `other`. /// /// Values are produced in the same order that they appear in `self`. diff --git a/tests/quick.rs b/tests/quick.rs index 3c86255..900397e 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -130,6 +130,60 @@ quickcheck_limit! { true } + fn replace_index(insert: Vec, index: u8, new_key: u8) -> TestResult { + if insert.is_empty() { + return TestResult::discard(); + } + let mut map = OrderMap::new(); + for &key in &insert { + map.insert(key, ()); + } + let mut index = usize::from(index); + if index < map.len() { + match map.replace_index(index, new_key) { + Ok(old_key) => { + assert!(old_key == new_key || !map.contains_key(&old_key)); + } + Err((i, key)) => { + assert_eq!(key, new_key); + index = i; + } + } + assert_eq!(map.get_index_of(&new_key), Some(index)); + assert_eq!(map.get_index(index), Some((&new_key, &()))); + TestResult::passed() + } else { + TestResult::must_fail(move || map.replace_index(index, new_key)) + } + } + + fn vacant_replace_index(insert: Vec, index: u8, new_key: u8) -> TestResult { + if insert.is_empty() { + return TestResult::discard(); + } + let mut map = OrderMap::new(); + for &key in &insert { + map.insert(key, ()); + } + let index = usize::from(index); + if let Some((&old_key, &())) = map.get_index(index) { + match map.entry(new_key) { + Entry::Occupied(_) => return TestResult::discard(), + Entry::Vacant(entry) => { + let (replaced_key, entry) = entry.replace_index(index); + assert_eq!(old_key, replaced_key); + assert_eq!(*entry.key(), new_key); + } + }; + assert!(!map.contains_key(&old_key)); + assert_eq!(map.get_index_of(&new_key), Some(index)); + assert_eq!(map.get_index(index), Some((&new_key, &()))); + TestResult::passed() + } else { + TestResult::must_fail(move || map.replace_index(index, new_key)) + } + } + fn pop(insert: Vec) -> bool { let mut map = OrderMap::new(); for &key in &insert { From a9a5036f41ba928996c5d5763c2254528e8f4fdb Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 19:21:19 +0000 Subject: [PATCH 2/8] Merge pull request indexmap#402 from 4e554c4c/insert_by Add `insert_sorted_by{,_key}` methods for map/set (cherry picked from commit 1201c549758898e7d4b06944a3ef738476eca099) Co-authored-by: Calvin Lee --- src/map.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/map/tests.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/set.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/src/map.rs b/src/map.rs index 91e2a4b..20d44b8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -466,6 +466,52 @@ where self.inner.insert_sorted(key, value) } + /// Insert a key-value pair in the map at its ordered position among keys + /// sorted by `cmp`. + /// + /// This is equivalent to finding the position with + /// [`binary_search_by`][Self::binary_search_by], then calling + /// [`insert_before`][Self::insert_before] with the given key and value. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by(&mut self, cmp: F, key: K, value: V) -> (usize, Option) + where + K: Ord, + F: FnMut(&K, &V) -> Ordering, + { + let (Ok(i) | Err(i)) = self.binary_search_by(cmp); + self.insert_before(i, key, value) + } + + /// Insert a key-value pair in the map at its ordered position + /// using a sort-key extraction function. + /// + /// This is equivalent to finding the position with + /// [`binary_search_by_key`][Self::binary_search_by_key] with `sort_key(key)`, then + /// calling [`insert_before`][Self::insert_before] with the given key and value. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by_key( + &mut self, + sort_key: F, + key: K, + value: V, + ) -> (usize, Option) + where + B: Ord, + F: FnMut(&K, &V) -> B, + { + self.inner.insert_sorted_by_key(key, value, sort_key) + } + /// Insert a key-value pair in the map before the entry at the given index, or at the end. /// /// If an equivalent key already exists in the map: the key remains and diff --git a/src/map/tests.rs b/src/map/tests.rs index a1899c8..36611b9 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -1208,3 +1208,41 @@ fn disjoint_indices_mut_fail_duplicate() { Err(crate::GetDisjointMutError::OverlappingIndices) ); } + +#[test] +fn insert_sorted_by_key() { + let mut values = [(-1, 8), (3, 18), (-27, 2), (-2, 5)]; + let mut map: OrderMap = OrderMap::new(); + for (key, value) in values { + let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), key, value); + assert_eq!(old, None); + } + values.sort_by_key(|(key, _)| key.abs()); + assert_eq!(values, *map.as_slice()); + + for (key, value) in &mut values { + let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), *key, -*value); + assert_eq!(old, Some(*value)); + *value = -*value; + } + assert_eq!(values, *map.as_slice()); +} + +#[test] +fn insert_sorted_by() { + let mut values = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]; + let mut map: OrderMap = OrderMap::new(); + for (key, value) in values { + let (_, old) = map.insert_sorted_by(|probe, _| key.cmp(probe), key, value); + assert_eq!(old, None); + } + values.reverse(); + assert_eq!(values, *map.as_slice()); + + for (key, value) in &mut values { + let (_, old) = map.insert_sorted_by(|probe, _| (*key).cmp(probe), *key, -*value); + assert_eq!(old, Some(*value)); + *value = -*value; + } + assert_eq!(values, *map.as_slice()); +} diff --git a/src/set.rs b/src/set.rs index 8a5acab..e42f8e9 100644 --- a/src/set.rs +++ b/src/set.rs @@ -405,6 +405,47 @@ where self.inner.insert_sorted(value) } + /// Insert the value into the set at its ordered position among values + /// sorted by `cmp`. + /// + /// This is equivalent to finding the position with + /// [`binary_search_by`][Self::binary_search_by], then calling + /// [`insert_before`][Self::insert_before]. + /// + /// If the existing items are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the value + /// is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by(&mut self, cmp: F, value: T) -> (usize, bool) + where + T: Ord, + F: FnMut(&T) -> Ordering, + { + let (Ok(i) | Err(i)) = self.binary_search_by(cmp); + self.insert_before(i, value) + } + + /// Insert the value into the set at its ordered position among values + /// using a sort-key extraction function. + /// + /// This is equivalent to finding the position with + /// [`binary_search_by_key`][Self::binary_search_by_key] with `sort_key(key)`, + /// then calling [`insert_before`][Self::insert_before]. + /// + /// If the existing items are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the value + /// is moved to or inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by_key(&mut self, sort_key: F, value: T) -> (usize, bool) + where + B: Ord, + F: FnMut(&T) -> B, + { + self.inner.insert_sorted_by_key(value, sort_key) + } + /// Insert the value into the set before the value at the given index, or at the end. /// /// If an equivalent item already exists in the set, it returns `false` leaving the From d2c63dd0fb662bb83278d222758001145383f959 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 21:35:08 +0000 Subject: [PATCH 3/8] Merge pull request indexmap#406 from cuviper/more-insert_sorted_by Tweak and extend `insert sorted by`{,`_key`} methods (cherry picked from commit 7939ae9c21b12aa5386cb9d3ecb4f7bb598341a6) --- src/map.rs | 11 +++++------ src/map/entry.rs | 35 +++++++++++++++++++++++++++++++++++ src/map/tests.rs | 8 ++++---- src/set.rs | 9 ++++----- tests/quick.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/map.rs b/src/map.rs index 20d44b8..c8d689e 100644 --- a/src/map.rs +++ b/src/map.rs @@ -478,13 +478,12 @@ where /// pair is moved to or inserted at that position regardless. /// /// Computes in **O(n)** time (average). - pub fn insert_sorted_by(&mut self, cmp: F, key: K, value: V) -> (usize, Option) + pub fn insert_sorted_by(&mut self, key: K, value: V, cmp: F) -> (usize, Option) where K: Ord, - F: FnMut(&K, &V) -> Ordering, + F: FnMut(&K, &V, &K, &V) -> Ordering, { - let (Ok(i) | Err(i)) = self.binary_search_by(cmp); - self.insert_before(i, key, value) + self.inner.insert_sorted_by(key, value, cmp) } /// Insert a key-value pair in the map at its ordered position @@ -499,11 +498,11 @@ where /// pair is moved to or inserted at that position regardless. /// /// Computes in **O(n)** time (average). - pub fn insert_sorted_by_key( + pub fn insert_sorted_by_key( &mut self, - sort_key: F, key: K, value: V, + sort_key: F, ) -> (usize, Option) where B: Ord, diff --git a/src/map/entry.rs b/src/map/entry.rs index def5b7a..759083e 100644 --- a/src/map/entry.rs +++ b/src/map/entry.rs @@ -1,3 +1,4 @@ +use core::cmp::Ordering; use core::fmt; use indexmap::map as ix; @@ -326,6 +327,40 @@ impl<'a, K, V> VacantEntry<'a, K, V> { self.inner.insert_sorted(value) } + /// Inserts the entry's key and the given value into the map at its ordered + /// position among keys sorted by `cmp`, and returns the new index and a + /// mutable reference to the value. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by(self, value: V, cmp: F) -> (usize, &'a mut V) + where + K: Ord, + F: FnMut(&K, &V, &K, &V) -> Ordering, + { + self.inner.insert_sorted_by(value, cmp) + } + + /// Inserts the entry's key and the given value into the map at its ordered + /// position using a sort-key extraction function, and returns the new index + /// and a mutable reference to the value. + /// + /// If the existing keys are **not** already sorted, then the insertion + /// index is unspecified (like [`slice::binary_search`]), but the key-value + /// pair is inserted at that position regardless. + /// + /// Computes in **O(n)** time (average). + pub fn insert_sorted_by_key(self, value: V, sort_key: F) -> (usize, &'a mut V) + where + B: Ord, + F: FnMut(&K, &V) -> B, + { + self.inner.insert_sorted_by_key(value, sort_key) + } + /// Inserts the entry's key and the given value into the map at the given index, /// shifting others to the right, and returns a mutable reference to the value. /// diff --git a/src/map/tests.rs b/src/map/tests.rs index 36611b9..ff4039d 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -1214,14 +1214,14 @@ fn insert_sorted_by_key() { let mut values = [(-1, 8), (3, 18), (-27, 2), (-2, 5)]; let mut map: OrderMap = OrderMap::new(); for (key, value) in values { - let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), key, value); + let (_, old) = map.insert_sorted_by_key(key, value, |k, _| k.abs()); assert_eq!(old, None); } values.sort_by_key(|(key, _)| key.abs()); assert_eq!(values, *map.as_slice()); for (key, value) in &mut values { - let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), *key, -*value); + let (_, old) = map.insert_sorted_by_key(*key, -*value, |k, _| k.abs()); assert_eq!(old, Some(*value)); *value = -*value; } @@ -1233,14 +1233,14 @@ fn insert_sorted_by() { let mut values = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]; let mut map: OrderMap = OrderMap::new(); for (key, value) in values { - let (_, old) = map.insert_sorted_by(|probe, _| key.cmp(probe), key, value); + let (_, old) = map.insert_sorted_by(key, value, |key1, _, key2, _| key2.cmp(key1)); assert_eq!(old, None); } values.reverse(); assert_eq!(values, *map.as_slice()); for (key, value) in &mut values { - let (_, old) = map.insert_sorted_by(|probe, _| (*key).cmp(probe), *key, -*value); + let (_, old) = map.insert_sorted_by(*key, -*value, |key1, _, key2, _| key2.cmp(key1)); assert_eq!(old, Some(*value)); *value = -*value; } diff --git a/src/set.rs b/src/set.rs index e42f8e9..62ea6c5 100644 --- a/src/set.rs +++ b/src/set.rs @@ -417,13 +417,12 @@ where /// is moved to or inserted at that position regardless. /// /// Computes in **O(n)** time (average). - pub fn insert_sorted_by(&mut self, cmp: F, value: T) -> (usize, bool) + pub fn insert_sorted_by(&mut self, value: T, cmp: F) -> (usize, bool) where T: Ord, - F: FnMut(&T) -> Ordering, + F: FnMut(&T, &T) -> Ordering, { - let (Ok(i) | Err(i)) = self.binary_search_by(cmp); - self.insert_before(i, value) + self.inner.insert_sorted_by(value, cmp) } /// Insert the value into the set at its ordered position among values @@ -438,7 +437,7 @@ where /// is moved to or inserted at that position regardless. /// /// Computes in **O(n)** time (average). - pub fn insert_sorted_by_key(&mut self, sort_key: F, value: T) -> (usize, bool) + pub fn insert_sorted_by_key(&mut self, value: T, sort_key: F) -> (usize, bool) where B: Ord, F: FnMut(&T) -> B, diff --git a/tests/quick.rs b/tests/quick.rs index 900397e..75bfa25 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -130,6 +130,46 @@ quickcheck_limit! { true } + fn insert_sorted_by(insert: Vec<(u32, u32)>) -> bool { + let mut hmap = HashMap::new(); + let mut map = OrderMap::new(); + let mut map2 = OrderMap::new(); + for &(key, value) in &insert { + hmap.insert(key, value); + map.insert_sorted_by(key, value, |key1, _, key2, _| key2.cmp(key1)); + match map2.entry(key) { + Entry::Occupied(e) => *e.into_mut() = value, + Entry::Vacant(e) => { + e.insert_sorted_by(value, |key1, _, key2, _| key2.cmp(key1)); + } + } + } + let hsorted = hmap.iter().sorted_by(|(key1, _), (key2, _)| key2.cmp(key1)); + itertools::assert_equal(hsorted, &map); + itertools::assert_equal(&map, &map2); + true + } + + fn insert_sorted_by_key(insert: Vec<(i32, u32)>) -> bool { + let mut hmap = HashMap::new(); + let mut map = OrderMap::new(); + let mut map2 = OrderMap::new(); + for &(key, value) in &insert { + hmap.insert(key, value); + map.insert_sorted_by_key(key, value, |&k, _| (k.unsigned_abs(), k)); + match map2.entry(key) { + Entry::Occupied(e) => *e.into_mut() = value, + Entry::Vacant(e) => { + e.insert_sorted_by_key(value, |&k, _| (k.unsigned_abs(), k)); + } + } + } + let hsorted = hmap.iter().sorted_by_key(|(&k, _)| (k.unsigned_abs(), k)); + itertools::assert_equal(hsorted, &map); + itertools::assert_equal(&map, &map2); + true + } + fn replace_index(insert: Vec, index: u8, new_key: u8) -> TestResult { if insert.is_empty() { return TestResult::discard(); From ff2e7359c68a181d45ad2d4a1606e339dd770914 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 19:29:34 +0000 Subject: [PATCH 4/8] Merge pull request indexmap#400 from Techcable/feature/sval2 Implement sval2 support (cherry picked from commit c0542d740417245ec34152355fd1e429b9cdf9f0) Co-authored-by: Techcable --- .github/workflows/ci.yml | 8 ++- Cargo.toml | 6 ++- src/lib.rs | 2 + src/sval.rs | 16 ++++++ test-sval/Cargo.toml | 13 +++++ test-sval/src/lib.rs | 104 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 src/sval.rs create mode 100644 test-sval/Cargo.toml create mode 100644 test-sval/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87ca19b..e166236 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,8 @@ jobs: features: rayon - rust: stable features: serde + - rust: stable + features: sval - rust: stable features: borsh - rust: stable @@ -62,6 +64,10 @@ jobs: if: matrix.features == 'serde' run: | cargo test --verbose -p test-serde + - name: Tests (sval) + if: matrix.features == 'sval' + run: | + cargo test --verbose -p test-sval - name: Test run benchmarks if: matrix.bench != '' run: cargo test -v --benches @@ -141,7 +147,7 @@ jobs: - name: Build (nightly) run: cargo +nightly build --verbose --all-features - name: Build (MSRV) - run: cargo build --verbose --features arbitrary,quickcheck,serde,rayon + run: cargo build --verbose --features arbitrary,quickcheck,serde,sval,rayon # One job that "summarizes" the success state of this pipeline. This can then be added to branch # protection, rather than having to add each job separately. diff --git a/Cargo.toml b/Cargo.toml index c9d1169..9afba7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ quickcheck = { version = "1.0", optional = true, default-features = false } serde = { version = "1.0", optional = true, default-features = false } borsh = { version = "1.5.6", optional = true, default-features = false } rayon = { version = "1.9", optional = true } +sval = { version = "2", optional = true, default-features = false } [dev-dependencies] itertools = "0.14" @@ -37,6 +38,7 @@ arbitrary = ["dep:arbitrary", "indexmap/arbitrary"] quickcheck = ["dep:quickcheck", "indexmap/quickcheck"] rayon = ["dep:rayon", "indexmap/rayon"] serde = ["dep:serde", "indexmap/serde"] +sval = ["dep:sval", "indexmap/sval"] borsh = ["dep:borsh", "borsh/indexmap"] [profile.bench] @@ -48,11 +50,11 @@ sign-tag = true tag-name = "{{version}}" [package.metadata.docs.rs] -features = ["arbitrary", "quickcheck", "serde", "borsh", "rayon"] +features = ["arbitrary", "quickcheck", "serde", "borsh", "rayon", "sval"] rustdoc-args = ["--cfg", "docsrs"] [workspace] -members = ["test-nostd", "test-serde"] +members = ["test-nostd", "test-serde", "test-sval"] [lints.clippy] style = "allow" diff --git a/src/lib.rs b/src/lib.rs index e50038c..658e75a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,8 @@ mod macros; mod borsh; #[cfg(feature = "serde")] mod serde; +#[cfg(feature = "sval")] +mod sval; pub mod map; pub mod set; diff --git a/src/sval.rs b/src/sval.rs new file mode 100644 index 0000000..0f406f3 --- /dev/null +++ b/src/sval.rs @@ -0,0 +1,16 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "sval")))] + +use crate::{OrderMap, OrderSet}; +use sval::{Stream, Value}; + +impl Value for OrderMap { + fn stream<'sval, ST: Stream<'sval> + ?Sized>(&'sval self, stream: &mut ST) -> sval::Result { + self.inner.stream(stream) + } +} + +impl Value for OrderSet { + fn stream<'sval, ST: Stream<'sval> + ?Sized>(&'sval self, stream: &mut ST) -> sval::Result { + self.inner.stream(stream) + } +} diff --git a/test-sval/Cargo.toml b/test-sval/Cargo.toml new file mode 100644 index 0000000..f581704 --- /dev/null +++ b/test-sval/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test-sval" +version = "0.1.0" +publish = false +edition = "2021" + +[dependencies] + +[dev-dependencies] +fnv = "1.0" +ordermap = { path = "..", features = ["sval"] } +sval = { version = "2", features = ["derive"] } +sval_test = "2" diff --git a/test-sval/src/lib.rs b/test-sval/src/lib.rs new file mode 100644 index 0000000..164cd8d --- /dev/null +++ b/test-sval/src/lib.rs @@ -0,0 +1,104 @@ +#![cfg(test)] + +use fnv::FnvBuildHasher; +use ordermap::{ordermap, orderset, OrderMap, OrderSet}; +use sval_test::{assert_tokens, Token}; + +#[test] +fn test_sval_map() { + let map = ordermap! { 1 => 2, 3 => 4 }; + assert_tokens( + &map, + &[ + Token::MapBegin(Some(2)), + Token::MapKeyBegin, + Token::I32(1), + Token::MapKeyEnd, + Token::MapValueBegin, + Token::I32(2), + Token::MapValueEnd, + Token::MapKeyBegin, + Token::I32(3), + Token::MapKeyEnd, + Token::MapValueBegin, + Token::I32(4), + Token::MapValueEnd, + Token::MapEnd, + ], + ); +} + +#[test] +fn test_sval_set() { + let set = orderset! { 1, 2, 3, 4 }; + assert_tokens( + &set, + &[ + Token::SeqBegin(Some(4)), + Token::SeqValueBegin, + Token::I32(1), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(2), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(3), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(4), + Token::SeqValueEnd, + Token::SeqEnd, + ], + ); +} + +#[test] +fn test_sval_map_fnv_hasher() { + let mut map: OrderMap = Default::default(); + map.insert(1, 2); + map.insert(3, 4); + assert_tokens( + &map, + &[ + Token::MapBegin(Some(2)), + Token::MapKeyBegin, + Token::I32(1), + Token::MapKeyEnd, + Token::MapValueBegin, + Token::I32(2), + Token::MapValueEnd, + Token::MapKeyBegin, + Token::I32(3), + Token::MapKeyEnd, + Token::MapValueBegin, + Token::I32(4), + Token::MapValueEnd, + Token::MapEnd, + ], + ); +} + +#[test] +fn test_sval_set_fnv_hasher() { + let mut set: OrderSet = Default::default(); + set.extend(1..5); + assert_tokens( + &set, + &[ + Token::SeqBegin(Some(4)), + Token::SeqValueBegin, + Token::I32(1), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(2), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(3), + Token::SeqValueEnd, + Token::SeqValueBegin, + Token::I32(4), + Token::SeqValueEnd, + Token::SeqEnd, + ], + ); +} From 608de24858ddd503169ab423c0d4236164a15419 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 14:52:57 -0700 Subject: [PATCH 5/8] Normalize to ASCII apostrophes (cherry picked from commit a468ca41bbbc04afd293e00ad57d7a8ed60cb89e) --- src/map.rs | 10 +++++----- src/map/mutable.rs | 4 ++-- src/map/rayon.rs | 6 +++--- src/set.rs | 8 ++++---- src/set/mutable.rs | 2 +- src/set/rayon.rs | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/map.rs b/src/map.rs index c8d689e..669bc71 100644 --- a/src/map.rs +++ b/src/map.rs @@ -646,7 +646,7 @@ where self.inner.replace_index(index, key) } - /// Get the given key’s corresponding entry in the map for insertion and/or + /// Get the given key's corresponding entry in the map for insertion and/or /// in-place manipulation. /// /// Computes in **O(1)** time (amortized average). @@ -934,7 +934,7 @@ impl OrderMap { self.inner.retain(keep); } - /// Sort the map’s key-value pairs by the default ordering of the keys. + /// Sort the map's key-value pairs by the default ordering of the keys. /// /// This is a stable sort -- but equivalent keys should not normally coexist in /// a map at all, so [`sort_unstable_keys`][Self::sort_unstable_keys] is preferred @@ -948,7 +948,7 @@ impl OrderMap { self.inner.sort_keys(); } - /// Sort the map’s key-value pairs in place using the comparison + /// Sort the map's key-value pairs in place using the comparison /// function `cmp`. /// /// The comparison function receives two key and value pairs to compare (you @@ -1012,7 +1012,7 @@ impl OrderMap { self.inner.sorted_unstable_by(cmp) } - /// Sort the map’s key-value pairs in place using a sort-key extraction function. + /// Sort the map's key-value pairs in place using a sort-key extraction function. /// /// During sorting, the function is called at most once per entry, by using temporary storage /// to remember the results of its evaluation. The order of calls to the function is @@ -1085,7 +1085,7 @@ impl OrderMap { self.inner.partition_point(pred) } - /// Reverses the order of the map’s key-value pairs in place. + /// Reverses the order of the map's key-value pairs in place. /// /// Computes in **O(n)** time and **O(1)** space. pub fn reverse(&mut self) { diff --git a/src/map/mutable.rs b/src/map/mutable.rs index 5f59dcf..c81bdf3 100644 --- a/src/map/mutable.rs +++ b/src/map/mutable.rs @@ -8,7 +8,7 @@ use indexmap::map::MutableKeys as _; /// These methods expose `&mut K`, mutable references to the key as it is stored /// in the map. /// You are allowed to modify the keys in the map **if the modification -/// does not change the key’s hash and equality**. +/// does not change the key's hash and equality**. /// /// If keys are modified erroneously, you can no longer look them up. /// This is sound (memory safe) but a logical error hazard (just like @@ -88,7 +88,7 @@ where /// These methods expose `&mut K`, mutable references to the key as it is stored /// in the map. /// You are allowed to modify the keys in the map **if the modification -/// does not change the key’s hash and equality**. +/// does not change the key's hash and equality**. /// /// If keys are modified erroneously, you can no longer look them up. /// This is sound (memory safe) but a logical error hazard (just like diff --git a/src/map/rayon.rs b/src/map/rayon.rs index 7381320..6dca6ca 100644 --- a/src/map/rayon.rs +++ b/src/map/rayon.rs @@ -126,7 +126,7 @@ where K: Send, V: Send, { - /// Sort the map’s key-value pairs in parallel, by the default ordering of the keys. + /// Sort the map's key-value pairs in parallel, by the default ordering of the keys. pub fn par_sort_keys(&mut self) where K: Ord, @@ -134,7 +134,7 @@ where self.inner.par_sort_keys(); } - /// Sort the map’s key-value pairs in place and in parallel, using the comparison + /// Sort the map's key-value pairs in place and in parallel, using the comparison /// function `cmp`. /// /// The comparison function receives two key and value pairs to compare (you @@ -184,7 +184,7 @@ where self.inner.par_sorted_unstable_by(cmp) } - /// Sort the map’s key-value pairs in place and in parallel, using a sort-key extraction + /// Sort the map's key-value pairs in place and in parallel, using a sort-key extraction /// function. pub fn par_sort_by_cached_key(&mut self, sort_key: F) where diff --git a/src/set.rs b/src/set.rs index 62ea6c5..aeaac71 100644 --- a/src/set.rs +++ b/src/set.rs @@ -865,7 +865,7 @@ impl OrderSet { self.inner.retain(keep) } - /// Sort the set’s values by their default ordering. + /// Sort the set's values by their default ordering. /// /// This is a stable sort -- but equivalent values should not normally coexist in /// a set at all, so [`sort_unstable`][Self::sort_unstable] is preferred @@ -879,7 +879,7 @@ impl OrderSet { self.inner.sort() } - /// Sort the set’s values in place using the comparison function `cmp`. + /// Sort the set's values in place using the comparison function `cmp`. /// /// Computes in **O(n log n)** time and **O(n)** space. The sort is stable. pub fn sort_by(&mut self, cmp: F) @@ -929,7 +929,7 @@ impl OrderSet { self.inner.sorted_unstable_by(cmp) } - /// Sort the set’s values in place using a key extraction function. + /// Sort the set's values in place using a key extraction function. /// /// During sorting, the function is called at most once per entry, by using temporary storage /// to remember the results of its evaluation. The order of calls to the function is @@ -1002,7 +1002,7 @@ impl OrderSet { self.inner.partition_point(pred) } - /// Reverses the order of the set’s values in place. + /// Reverses the order of the set's values in place. /// /// Computes in **O(n)** time and **O(1)** space. pub fn reverse(&mut self) { diff --git a/src/set/mutable.rs b/src/set/mutable.rs index 85bff26..3256924 100644 --- a/src/set/mutable.rs +++ b/src/set/mutable.rs @@ -7,7 +7,7 @@ use indexmap::set::MutableValues as _; /// These methods expose `&mut T`, mutable references to the value as it is stored /// in the set. /// You are allowed to modify the values in the set **if the modification -/// does not change the value’s hash and equality**. +/// does not change the value's hash and equality**. /// /// If values are modified erroneously, you can no longer look them up. /// This is sound (memory safe) but a logical error hazard (just like diff --git a/src/set/rayon.rs b/src/set/rayon.rs index 1e9e2db..238f42e 100644 --- a/src/set/rayon.rs +++ b/src/set/rayon.rs @@ -176,7 +176,7 @@ impl OrderSet where T: Send, { - /// Sort the set’s values in parallel by their default ordering. + /// Sort the set's values in parallel by their default ordering. pub fn par_sort(&mut self) where T: Ord, @@ -184,7 +184,7 @@ where self.inner.par_sort(); } - /// Sort the set’s values in place and in parallel, using the comparison function `cmp`. + /// Sort the set's values in place and in parallel, using the comparison function `cmp`. pub fn par_sort_by(&mut self, cmp: F) where F: Fn(&T, &T) -> Ordering + Sync, @@ -209,7 +209,7 @@ where self.inner.par_sort_unstable(); } - /// Sort the set’s values in place and in parallel, using the comparison function `cmp`. + /// Sort the set's values in place and in parallel, using the comparison function `cmp`. pub fn par_sort_unstable_by(&mut self, cmp: F) where F: Fn(&T, &T) -> Ordering + Sync, @@ -226,7 +226,7 @@ where self.inner.par_sorted_unstable_by(cmp) } - /// Sort the set’s values in place and in parallel, using a key extraction function. + /// Sort the set's values in place and in parallel, using a key extraction function. pub fn par_sort_by_cached_key(&mut self, sort_key: F) where K: Ord + Send, From 8e73584c93f2ed64c9a536feb603bcfbfe1675c6 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 15:05:01 -0700 Subject: [PATCH 6/8] Add `sort_by_key` and `sort_unstable_by_key` (cherry picked from commit ab9e461b895184bd4926a8d4555f05d1a6c50666) --- src/map.rs | 24 ++++++++++++++++++++++++ src/map/rayon.rs | 20 ++++++++++++++++++++ src/set.rs | 22 ++++++++++++++++++++++ src/set/rayon.rs | 18 ++++++++++++++++++ src/set/tests.rs | 22 ++++++++++++++++++++++ 5 files changed, 106 insertions(+) diff --git a/src/map.rs b/src/map.rs index 669bc71..1330e24 100644 --- a/src/map.rs +++ b/src/map.rs @@ -974,6 +974,18 @@ impl OrderMap { self.inner.sorted_by(cmp) } + /// Sort the map's key-value pairs in place using a sort-key extraction function. + /// + /// Computes in **O(n log n + c)** time and **O(n)** space where *n* is + /// the length of the map and *c* the capacity. The sort is stable. + pub fn sort_by_key(&mut self, sort_key: F) + where + T: Ord, + F: FnMut(&K, &V) -> T, + { + self.inner.sort_by_key(sort_key) + } + /// Sort the map's key-value pairs by the default ordering of the keys, but /// may not preserve the order of equal elements. /// @@ -1012,6 +1024,18 @@ impl OrderMap { self.inner.sorted_unstable_by(cmp) } + /// Sort the map's key-value pairs in place using a sort-key extraction function. + /// + /// Computes in **O(n log n + c)** time where *n* is + /// the length of the map and *c* is the capacity. The sort is unstable. + pub fn sort_unstable_by_key(&mut self, sort_key: F) + where + T: Ord, + F: FnMut(&K, &V) -> T, + { + self.inner.sort_unstable_by_key(sort_key) + } + /// Sort the map's key-value pairs in place using a sort-key extraction function. /// /// During sorting, the function is called at most once per entry, by using temporary storage diff --git a/src/map/rayon.rs b/src/map/rayon.rs index 6dca6ca..a0d0c75 100644 --- a/src/map/rayon.rs +++ b/src/map/rayon.rs @@ -155,6 +155,16 @@ where self.inner.par_sorted_by(cmp) } + /// Sort the map's key-value pairs in place and in parallel, using a sort-key extraction + /// function. + pub fn par_sort_by_key(&mut self, sort_key: F) + where + T: Ord, + F: Fn(&K, &V) -> T + Sync, + { + self.inner.par_sort_by_key(sort_key) + } + /// Sort the map's key-value pairs in parallel, by the default ordering of the keys. pub fn par_sort_unstable_keys(&mut self) where @@ -184,6 +194,16 @@ where self.inner.par_sorted_unstable_by(cmp) } + /// Sort the map's key-value pairs in place and in parallel, using a sort-key extraction + /// function. + pub fn par_sort_unstable_by_key(&mut self, sort_key: F) + where + T: Ord, + F: Fn(&K, &V) -> T + Sync, + { + self.inner.par_sort_unstable_by_key(sort_key) + } + /// Sort the map's key-value pairs in place and in parallel, using a sort-key extraction /// function. pub fn par_sort_by_cached_key(&mut self, sort_key: F) diff --git a/src/set.rs b/src/set.rs index aeaac71..2bb175d 100644 --- a/src/set.rs +++ b/src/set.rs @@ -900,6 +900,17 @@ impl OrderSet { self.inner.sorted_by(cmp) } + /// Sort the set's values in place using a key extraction function. + /// + /// Computes in **O(n log n)** time and **O(n)** space. The sort is stable. + pub fn sort_by_key(&mut self, sort_key: F) + where + K: Ord, + F: FnMut(&T) -> K, + { + self.inner.sort_by_key(sort_key) + } + /// Sort the set's values by their default ordering. /// /// See [`sort_unstable_by`](Self::sort_unstable_by) for details. @@ -929,6 +940,17 @@ impl OrderSet { self.inner.sorted_unstable_by(cmp) } + /// Sort the set's values in place using a key extraction function. + /// + /// Computes in **O(n log n)** time. The sort is unstable. + pub fn sort_unstable_by_key(&mut self, sort_key: F) + where + K: Ord, + F: FnMut(&T) -> K, + { + self.inner.sort_unstable_by_key(sort_key) + } + /// Sort the set's values in place using a key extraction function. /// /// During sorting, the function is called at most once per entry, by using temporary storage diff --git a/src/set/rayon.rs b/src/set/rayon.rs index 238f42e..e565214 100644 --- a/src/set/rayon.rs +++ b/src/set/rayon.rs @@ -201,6 +201,15 @@ where self.inner.par_sorted_by(cmp) } + /// Sort the set's values in place and in parallel, using a key extraction function. + pub fn par_sort_by_key(&mut self, sort_key: F) + where + K: Ord, + F: Fn(&T) -> K + Sync, + { + self.inner.par_sort_by_key(sort_key) + } + /// Sort the set's values in parallel by their default ordering. pub fn par_sort_unstable(&mut self) where @@ -226,6 +235,15 @@ where self.inner.par_sorted_unstable_by(cmp) } + /// Sort the set's values in place and in parallel, using a key extraction function. + pub fn par_sort_unstable_by_key(&mut self, sort_key: F) + where + K: Ord, + F: Fn(&T) -> K + Sync, + { + self.inner.par_sort_unstable_by_key(sort_key) + } + /// Sort the set's values in place and in parallel, using a key extraction function. pub fn par_sort_by_cached_key(&mut self, sort_key: F) where diff --git a/src/set/tests.rs b/src/set/tests.rs index 855058f..0d824b6 100644 --- a/src/set/tests.rs +++ b/src/set/tests.rs @@ -768,6 +768,28 @@ fn first() { assert!(result.is_none()); } +#[test] +fn sort_by_key() { + let mut index_set: OrderSet = OrderSet::new(); + index_set.insert(3); + index_set.insert(1); + index_set.insert(2); + index_set.insert(0); + index_set.sort_by_key(|&x| -x); + assert_eq!(index_set.as_slice(), &[3, 2, 1, 0]); +} + +#[test] +fn sort_unstable_by_key() { + let mut index_set: OrderSet = OrderSet::new(); + index_set.insert(3); + index_set.insert(1); + index_set.insert(2); + index_set.insert(0); + index_set.sort_unstable_by_key(|&x| -x); + assert_eq!(index_set.as_slice(), &[3, 2, 1, 0]); +} + #[test] fn sort_by_cached_key() { let mut index_set: OrderSet = OrderSet::new(); From ac2ec3cd6c1a3c2ec665608bfd8085bce9fa9327 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 21 Aug 2025 16:51:43 -0700 Subject: [PATCH 7/8] Add `is_sorted`{,`_by`,`_by_key`} (cherry picked from commit e4bb7d03b92ad7768fb26b5f062fefda6af0dc72) --- src/map.rs | 28 ++++++++++++++++++++++++++++ src/map/tests.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/set.rs | 28 ++++++++++++++++++++++++++++ src/set/tests.rs | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/src/map.rs b/src/map.rs index 1330e24..8a057b6 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1095,6 +1095,34 @@ impl OrderMap { self.inner.binary_search_by_key(b, f) } + /// Checks if the keys of this map are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + K: PartialOrd, + { + self.inner.is_sorted() + } + + /// Checks if this map is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, cmp: F) -> bool + where + F: FnMut(&'a K, &'a V, &'a K, &'a V) -> bool, + { + self.inner.is_sorted_by(cmp) + } + + /// Checks if this map is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, T>(&'a self, sort_key: F) -> bool + where + F: FnMut(&'a K, &'a V) -> T, + T: PartialOrd, + { + self.inner.is_sorted_by_key(sort_key) + } + /// Returns the index of the partition point of a sorted map according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/map/tests.rs b/src/map/tests.rs index ff4039d..0341155 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -1246,3 +1246,49 @@ fn insert_sorted_by() { } assert_eq!(values, *map.as_slice()); } + +#[test] +fn is_sorted() { + fn expect(map: &OrderMap, e: [bool; 7]) { + assert_eq!(e[0], map.is_sorted()); + assert_eq!(e[1], map.is_sorted_by(|k1, _, k2, _| k1 < k2)); + assert_eq!(e[2], map.is_sorted_by(|k1, _, k2, _| k1 > k2)); + assert_eq!(e[3], map.is_sorted_by(|_, v1, _, v2| v1 < v2)); + assert_eq!(e[4], map.is_sorted_by(|_, v1, _, v2| v1 > v2)); + assert_eq!(e[5], map.is_sorted_by_key(|k, _| k)); + assert_eq!(e[6], map.is_sorted_by_key(|_, v| v)); + } + + let mut map = OrderMap::from_iter((0..10).map(|i| (i, i * i))); + expect(&map, [true, true, false, true, false, true, true]); + + map[5] = -1; + expect(&map, [true, true, false, false, false, true, false]); + + map[5] = 25; + map.replace_index(5, -1).unwrap(); + expect(&map, [false, false, false, true, false, false, true]); +} + +#[test] +fn is_sorted_trivial() { + fn expect(map: &OrderMap, e: [bool; 5]) { + assert_eq!(e[0], map.is_sorted()); + assert_eq!(e[1], map.is_sorted_by(|_, _, _, _| true)); + assert_eq!(e[2], map.is_sorted_by(|_, _, _, _| false)); + assert_eq!(e[3], map.is_sorted_by_key(|_, _| 0f64)); + assert_eq!(e[4], map.is_sorted_by_key(|_, _| f64::NAN)); + } + + let mut map = OrderMap::new(); + expect(&map, [true, true, true, true, true]); + + map.insert(0, 0); + expect(&map, [true, true, true, true, true]); + + map.insert(1, 1); + expect(&map, [true, true, false, true, false]); + + map.reverse(); + expect(&map, [false, true, false, true, false]); +} diff --git a/src/set.rs b/src/set.rs index 2bb175d..705e384 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1010,6 +1010,34 @@ impl OrderSet { self.inner.binary_search_by_key(b, f) } + /// Checks if the values of this set are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + T: PartialOrd, + { + self.inner.is_sorted() + } + + /// Checks if this set is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, cmp: F) -> bool + where + F: FnMut(&'a T, &'a T) -> bool, + { + self.inner.is_sorted_by(cmp) + } + + /// Checks if this set is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, K>(&'a self, sort_key: F) -> bool + where + F: FnMut(&'a T) -> K, + K: PartialOrd, + { + self.inner.is_sorted_by_key(sort_key) + } + /// Returns the index of the partition point of a sorted set according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/set/tests.rs b/src/set/tests.rs index 0d824b6..db4597d 100644 --- a/src/set/tests.rs +++ b/src/set/tests.rs @@ -1026,3 +1026,42 @@ fn test_partition_point() { assert_eq!(b.partition_point(|&x| x < 7), 2); assert_eq!(b.partition_point(|&x| x < 8), 3); } + +#[test] +fn is_sorted() { + fn expect(set: &OrderSet, e: [bool; 4]) { + assert_eq!(e[0], set.is_sorted()); + assert_eq!(e[1], set.is_sorted_by(|v1, v2| v1 < v2)); + assert_eq!(e[2], set.is_sorted_by(|v1, v2| v1 > v2)); + assert_eq!(e[3], set.is_sorted_by_key(|v| v)); + } + + let mut set = OrderSet::::from_iter(0..10); + expect(&set, [true, true, false, true]); + + set.replace_index(5, -1).unwrap(); + expect(&set, [false, false, false, false]); +} + +#[test] +fn is_sorted_trivial() { + fn expect(set: &OrderSet, e: [bool; 5]) { + assert_eq!(e[0], set.is_sorted()); + assert_eq!(e[1], set.is_sorted_by(|_, _| true)); + assert_eq!(e[2], set.is_sorted_by(|_, _| false)); + assert_eq!(e[3], set.is_sorted_by_key(|_| 0f64)); + assert_eq!(e[4], set.is_sorted_by_key(|_| f64::NAN)); + } + + let mut set = OrderSet::::default(); + expect(&set, [true, true, true, true, true]); + + set.insert(0); + expect(&set, [true, true, true, true, true]); + + set.insert(1); + expect(&set, [true, true, false, true, false]); + + set.reverse(); + expect(&set, [false, true, false, true, false]); +} From 110acd9d2af2a41f3deb562cb6b5158cc699e6dc Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 22 Aug 2025 13:40:01 -0700 Subject: [PATCH 8/8] Release 0.5.9 --- Cargo.toml | 2 +- RELEASES.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9afba7b..3f4951d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ordermap" edition = "2021" -version = "0.5.8" +version = "0.5.9" documentation = "https://docs.rs/ordermap/" repository = "https://github.com/indexmap-rs/ordermap" license = "Apache-2.0 OR MIT" diff --git a/RELEASES.md b/RELEASES.md index 405baa9..6732bab 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,17 @@ # Releases +## 0.5.9 (2025-08-22) + +- Added `insert_sorted_by` and `insert_sorted_by_key` methods to `OrderMap`, + `OrderSet`, and `VacantEntry`, like customizable versions of `insert_sorted`. +- Added `is_sorted`, `is_sorted_by`, and `is_sorted_by_key` methods to + `OrderMap` and `OrderSet`, as well as their `Slice` counterparts. +- Added `sort_by_key` and `sort_unstable_by_key` methods to `OrderMap` and + `OrderSet`, as well as parallel counterparts. +- Added `replace_index` methods to `OrderMap`, `OrderSet`, and `VacantEntry` + to replace the key (or set value) at a given index. +- Added optional `sval` serialization support. + ## 0.5.8 (2025-06-26) - Added `extract_if` methods to `OrderMap` and `OrderSet`, similar to the