Skip to content

Commit a168b17

Browse files
authored
Merge pull request #405 from cuviper/replace_index
Add methods to replace the key at a given index
2 parents 91dbcc5 + a9a7490 commit a168b17

File tree

5 files changed

+141
-0
lines changed

5 files changed

+141
-0
lines changed

src/map.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,37 @@ where
652652
}
653653
}
654654

655+
/// Replaces the key at the given index. The new key does not need to be
656+
/// equivalent to the one it is replacing, but it must be unique to the rest
657+
/// of the map.
658+
///
659+
/// Returns `Ok(old_key)` if successful, or `Err((other_index, key))` if an
660+
/// equivalent key already exists at a different index. The map will be
661+
/// unchanged in the error case.
662+
///
663+
/// Direct indexing can be used to change the corresponding value: simply
664+
/// `map[index] = value`, or `mem::replace(&mut map[index], value)` to
665+
/// retrieve the old value as well.
666+
///
667+
/// ***Panics*** if `index` is out of bounds.
668+
///
669+
/// Computes in **O(1)** time (average).
670+
#[track_caller]
671+
pub fn replace_index(&mut self, index: usize, key: K) -> Result<K, (usize, K)> {
672+
// If there's a direct match, we don't even need to hash it.
673+
let entry = &mut self.as_entries_mut()[index];
674+
if key == entry.key {
675+
return Ok(mem::replace(&mut entry.key, key));
676+
}
677+
678+
let hash = self.hash(&key);
679+
if let Some(i) = self.core.get_index_of(hash, &key) {
680+
debug_assert_ne!(i, index);
681+
return Err((i, key));
682+
}
683+
Ok(self.core.replace_index_unique(index, hash, key))
684+
}
685+
655686
/// Get the given key’s corresponding entry in the map for insertion and/or
656687
/// in-place manipulation.
657688
///

src/map/core.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,13 @@ impl<K, V> IndexMapCore<K, V> {
385385
}
386386
}
387387

388+
/// Replaces the key at the given index,
389+
/// *without* checking whether it already exists.
390+
#[track_caller]
391+
pub(crate) fn replace_index_unique(&mut self, index: usize, hash: HashValue, key: K) -> K {
392+
self.borrow_mut().replace_index_unique(index, hash, key).0
393+
}
394+
388395
/// Remove an entry by shifting all entries that follow it
389396
pub(crate) fn shift_remove_full<Q>(&mut self, hash: HashValue, key: &Q) -> Option<(usize, K, V)>
390397
where
@@ -561,6 +568,28 @@ impl<'a, K, V> RefMut<'a, K, V> {
561568
OccupiedEntry::new(self.entries, entry)
562569
}
563570

571+
/// Replaces the key at the given index,
572+
/// *without* checking whether it already exists.
573+
#[track_caller]
574+
fn replace_index_unique(
575+
self,
576+
index: usize,
577+
hash: HashValue,
578+
key: K,
579+
) -> (K, OccupiedEntry<'a, K, V>) {
580+
// NB: This removal and insertion isn't "no grow" (with unreachable hasher)
581+
// because hashbrown's tombstones might force a resize anyway.
582+
erase_index(self.indices, self.entries[index].hash, index);
583+
let table_entry = self
584+
.indices
585+
.insert_unique(hash.get(), index, get_hash(&self.entries));
586+
587+
let entry = &mut self.entries[index];
588+
entry.hash = hash;
589+
let old_key = mem::replace(&mut entry.key, key);
590+
(old_key, OccupiedEntry::new(self.entries, table_entry))
591+
}
592+
564593
/// Insert a key-value pair in `entries` at a particular index,
565594
/// *without* checking whether it already exists.
566595
fn shift_insert_unique(&mut self, index: usize, hash: HashValue, key: K, value: V) {

src/map/core/entry.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,17 @@ impl<'a, K, V> VacantEntry<'a, K, V> {
414414
.shift_insert_unique(index, self.hash, self.key, value);
415415
&mut self.map.entries[index].value
416416
}
417+
418+
/// Replaces the key at the given index with this entry's key, returning the
419+
/// old key and an `OccupiedEntry` for that index.
420+
///
421+
/// ***Panics*** if `index` is out of bounds.
422+
///
423+
/// Computes in **O(1)** time (average).
424+
#[track_caller]
425+
pub fn replace_index(self, index: usize) -> (K, OccupiedEntry<'a, K, V>) {
426+
self.map.replace_index_unique(index, self.hash, self.key)
427+
}
417428
}
418429

419430
impl<K: fmt::Debug, V> fmt::Debug for VacantEntry<'_, K, V> {

src/set.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,22 @@ where
550550
}
551551
}
552552

553+
/// Replaces the value at the given index. The new value does not need to be
554+
/// equivalent to the one it is replacing, but it must be unique to the rest
555+
/// of the set.
556+
///
557+
/// Returns `Ok(old_value)` if successful, or `Err((other_index, value))` if
558+
/// an equivalent value already exists at a different index. The set will be
559+
/// unchanged in the error case.
560+
///
561+
/// ***Panics*** if `index` is out of bounds.
562+
///
563+
/// Computes in **O(1)** time (average).
564+
#[track_caller]
565+
pub fn replace_index(&mut self, index: usize, value: T) -> Result<T, (usize, T)> {
566+
self.map.replace_index(index, value)
567+
}
568+
553569
/// Return an iterator over the values that are in `self` but not `other`.
554570
///
555571
/// Values are produced in the same order that they appear in `self`.

tests/quick.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,60 @@ quickcheck_limit! {
130130
true
131131
}
132132

133+
fn replace_index(insert: Vec<u8>, index: u8, new_key: u8) -> TestResult {
134+
if insert.is_empty() {
135+
return TestResult::discard();
136+
}
137+
let mut map = IndexMap::new();
138+
for &key in &insert {
139+
map.insert(key, ());
140+
}
141+
let mut index = usize::from(index);
142+
if index < map.len() {
143+
match map.replace_index(index, new_key) {
144+
Ok(old_key) => {
145+
assert!(old_key == new_key || !map.contains_key(&old_key));
146+
}
147+
Err((i, key)) => {
148+
assert_eq!(key, new_key);
149+
index = i;
150+
}
151+
}
152+
assert_eq!(map.get_index_of(&new_key), Some(index));
153+
assert_eq!(map.get_index(index), Some((&new_key, &())));
154+
TestResult::passed()
155+
} else {
156+
TestResult::must_fail(move || map.replace_index(index, new_key))
157+
}
158+
}
159+
160+
fn vacant_replace_index(insert: Vec<u8>, index: u8, new_key: u8) -> TestResult {
161+
if insert.is_empty() {
162+
return TestResult::discard();
163+
}
164+
let mut map = IndexMap::new();
165+
for &key in &insert {
166+
map.insert(key, ());
167+
}
168+
let index = usize::from(index);
169+
if let Some((&old_key, &())) = map.get_index(index) {
170+
match map.entry(new_key) {
171+
Entry::Occupied(_) => return TestResult::discard(),
172+
Entry::Vacant(entry) => {
173+
let (replaced_key, entry) = entry.replace_index(index);
174+
assert_eq!(old_key, replaced_key);
175+
assert_eq!(*entry.key(), new_key);
176+
}
177+
};
178+
assert!(!map.contains_key(&old_key));
179+
assert_eq!(map.get_index_of(&new_key), Some(index));
180+
assert_eq!(map.get_index(index), Some((&new_key, &())));
181+
TestResult::passed()
182+
} else {
183+
TestResult::must_fail(move || map.replace_index(index, new_key))
184+
}
185+
}
186+
133187
fn pop(insert: Vec<u8>) -> bool {
134188
let mut map = IndexMap::new();
135189
for &key in &insert {

0 commit comments

Comments
 (0)