Skip to content

Commit 0a55350

Browse files
authored
Merge pull request #443 from cuviper/separate-disjoint
Separate the implementations of `get_disjoint_mut`
2 parents 1073259 + ddb0316 commit 0a55350

3 files changed

Lines changed: 70 additions & 48 deletions

File tree

‎src/map.rs‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! [`IndexMap`] is a hash table where the iteration order of the key-value
22
//! pairs is independent of the hash values of the keys.
33
4+
mod disjoint;
45
mod entry;
56
mod iter;
67
mod mutable;
@@ -952,25 +953,19 @@ where
952953
///
953954
/// ```
954955
/// let mut map = indexmap::IndexMap::from([(1, 'a'), (3, 'b'), (2, 'c')]);
955-
/// assert_eq!(map.get_disjoint_mut([&2, &1]), [Some(&mut 'c'), Some(&mut 'a')]);
956+
/// assert_eq!(
957+
/// map.get_disjoint_mut([&2, &1, &0]),
958+
/// [Some(&mut 'c'), Some(&mut 'a'), None],
959+
/// );
956960
/// ```
957961
#[track_caller]
958962
pub fn get_disjoint_mut<Q, const N: usize>(&mut self, keys: [&Q; N]) -> [Option<&mut V>; N]
959963
where
960964
Q: ?Sized + Hash + Equivalent<K>,
961965
{
962966
let indices = keys.map(|key| self.get_index_of(key));
963-
match self.as_mut_slice().get_disjoint_opt_mut(indices) {
964-
Err(GetDisjointMutError::IndexOutOfBounds) => {
965-
unreachable!(
966-
"Internal error: indices should never be OOB as we got them from get_index_of"
967-
);
968-
}
969-
Err(GetDisjointMutError::OverlappingIndices) => {
970-
panic!("duplicate keys found");
971-
}
972-
Ok(key_values) => key_values.map(|kv_opt| kv_opt.map(|kv| kv.1)),
973-
}
967+
disjoint::get_disjoint_opt_mut(self.as_entries_mut(), indices)
968+
.map(|opt| opt.map(Bucket::value_mut))
974969
}
975970

976971
/// Remove the key-value pair equivalent to `key` and return

‎src/map/disjoint.rs‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![allow(unsafe_code)]
2+
3+
use crate::GetDisjointMutError;
4+
5+
/// Like `slice::get_disjoint_mut`, although we're not dealing with ranges (yet).
6+
// TODO(MSRV 1.86): remove this in favor of the standard library's method.
7+
pub(super) fn get_disjoint_mut<T, const N: usize>(
8+
entries: &mut [T],
9+
indices: [usize; N],
10+
) -> Result<[&mut T; N], GetDisjointMutError> {
11+
// SAFETY: Can't allow duplicate indices as we would return mutable refs to the same data.
12+
let len = entries.len();
13+
for i in 0..N {
14+
let idx = indices[i];
15+
if idx >= len {
16+
return Err(GetDisjointMutError::IndexOutOfBounds);
17+
} else if indices[..i].contains(&idx) {
18+
return Err(GetDisjointMutError::OverlappingIndices);
19+
}
20+
}
21+
22+
let entries_ptr = entries.as_mut_ptr();
23+
Ok(indices.map(move |idx| {
24+
// SAFETY: The base pointer is valid as it comes from a slice and the reference is always
25+
// in-bounds & unique as we've already checked the indices above.
26+
unsafe { &mut *(entries_ptr.add(idx)) }
27+
}))
28+
}
29+
30+
/// Like `slice::get_disjoint_mut` but with optional indices,
31+
/// allowing for absent keys from the user's original request.
32+
#[track_caller]
33+
pub(super) fn get_disjoint_opt_mut<T, const N: usize>(
34+
entries: &mut [T],
35+
indices: [Option<usize>; N],
36+
) -> [Option<&mut T>; N] {
37+
// SAFETY: Can't allow duplicate indices as we would return mutable refs to the same data.
38+
let len = entries.len();
39+
for i in 0..N {
40+
if let Some(idx) = indices[i] {
41+
if idx >= len {
42+
unreachable!("`get_index_of` returned an out-of-bounds index");
43+
} else if indices[..i].contains(&Some(idx)) {
44+
panic!("duplicate keys found");
45+
}
46+
}
47+
}
48+
49+
let entries_ptr = entries.as_mut_ptr();
50+
indices.map(move |idx_opt| {
51+
match idx_opt {
52+
Some(idx) => {
53+
// SAFETY: The base pointer is valid as it comes from a slice and the reference is always
54+
// in-bounds & unique as we've already checked the indices above.
55+
Some(unsafe { &mut *entries_ptr.add(idx) })
56+
}
57+
None => None,
58+
}
59+
})
60+
}

‎src/map/slice.rs‎

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -350,42 +350,9 @@ impl<K, V> Slice<K, V> {
350350
&mut self,
351351
indices: [usize; N],
352352
) -> Result<[(&K, &mut V); N], GetDisjointMutError> {
353-
let indices = indices.map(Some);
354-
let key_values = self.get_disjoint_opt_mut(indices)?;
355-
Ok(key_values.map(Option::unwrap))
356-
}
357-
358-
#[allow(unsafe_code)]
359-
pub(crate) fn get_disjoint_opt_mut<const N: usize>(
360-
&mut self,
361-
indices: [Option<usize>; N],
362-
) -> Result<[Option<(&K, &mut V)>; N], GetDisjointMutError> {
363-
// SAFETY: Can't allow duplicate indices as we would return several mutable refs to the same data.
364-
let len = self.len();
365-
for i in 0..N {
366-
if let Some(idx) = indices[i] {
367-
if idx >= len {
368-
return Err(GetDisjointMutError::IndexOutOfBounds);
369-
} else if indices[..i].contains(&Some(idx)) {
370-
return Err(GetDisjointMutError::OverlappingIndices);
371-
}
372-
}
373-
}
374-
375-
let entries_ptr = self.entries.as_mut_ptr();
376-
let out = indices.map(|idx_opt| {
377-
match idx_opt {
378-
Some(idx) => {
379-
// SAFETY: The base pointer is valid as it comes from a slice and the reference is always
380-
// in-bounds & unique as we've already checked the indices above.
381-
let kv = unsafe { (*(entries_ptr.add(idx))).ref_mut() };
382-
Some(kv)
383-
}
384-
None => None,
385-
}
386-
});
387-
388-
Ok(out)
353+
// TODO(MSRV 1.86): use the standard library's `slice::get_disjoint_mut`
354+
let entries = super::disjoint::get_disjoint_mut(&mut self.entries, indices)?;
355+
Ok(entries.map(Bucket::ref_mut))
389356
}
390357
}
391358

0 commit comments

Comments
 (0)