Skip to content

Commit 244ae5e

Browse files
committed
Add Vec::remove_insert()
In patterns where an insertion directly follows a removal, this serves as being more efficient, since it only shifts/copies values as needed for the combined operation. This also comes with the added bonus of not needing to check for fullness, and thus, a `Result` is not needed as a return type. Signed-off-by: Mohammad AlSaleh <[email protected]>
1 parent e79f79c commit 244ae5e

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99
- Added `from_bytes_truncating_at_nul` to `CString`
10+
- Added `remove_insert` to `Vec`
1011

1112
## [v0.9.2] 2025-11-12
1213

src/vec/mod.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,89 @@ impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> VecInner<T, LenT, S> {
960960
value
961961
}
962962

963+
/// This is the same as calling [`Vec::remove`] with `remove_index`
964+
/// followed by calling [`Vec::insert`] with `insert_index` and `element`.
965+
///
966+
/// The returned value is the removed element.
967+
///
968+
/// This is more efficient than removing then inserting since it only shifts
969+
/// `remove_index.abs_diff(insert_index)` values.
970+
///
971+
/// [`remove`]: Vec::remove
972+
/// [`insert`]: Vec::insert
973+
///
974+
/// # Panics
975+
///
976+
/// Panics if `remove_index` or `insert_index` are out of bounds.
977+
///
978+
/// # Examples
979+
///
980+
/// ```
981+
/// use heapless::Vec;
982+
///
983+
/// let mut v: Vec<_, 8> = Vec::from_slice(&[0, 1, 2, 3]).unwrap();
984+
/// assert_eq!(v.remove_insert(1, 2, 4), 1);
985+
/// // only one element (2) is shifted back
986+
/// assert_eq!(v, [0, 2, 4, 3]);
987+
/// ```
988+
pub fn remove_insert(&mut self, remove_index: usize, insert_index: usize, element: T) -> T {
989+
let length = self.len();
990+
991+
assert!(remove_index < length);
992+
assert!(insert_index < length);
993+
994+
// SAFETY: `remove_index < length` assertion guarantees the pointer is within bounds.
995+
let to_remove = unsafe { ptr::read(self.as_ptr().add(remove_index)) };
996+
997+
match remove_index.cmp(&insert_index) {
998+
Ordering::Equal => (),
999+
Ordering::Less => {
1000+
// SAFETY: `remove_index < length` assertion guarantees the pointer is within
1001+
// bounds.
1002+
let remove_at = unsafe { self.as_mut_ptr().add(remove_index) };
1003+
1004+
// SAFETY:
1005+
// Copies from
1006+
// (remove_index + 1)..(insert_index + 1)
1007+
// to
1008+
// remove_index..insert_index
1009+
//
1010+
// remove_index < insert_index (this match)
1011+
// remove_index + 1 < insert_index + 1 (can't saturate because <= length)
1012+
// remove_index + 1 <= insert_index < length
1013+
// insert_index + 1 <= length
1014+
//
1015+
// If `remove_index == 0`, and `insert_index + 1 == length`, then we copy from
1016+
// 1..length to 0..(length-1).
1017+
unsafe { ptr::copy(remove_at.add(1), remove_at, insert_index - remove_index) };
1018+
}
1019+
Ordering::Greater => {
1020+
// SAFETY: `insert_index < length` assertion guarantees the pointer is within
1021+
// bounds.
1022+
let insert_at = unsafe { self.as_mut_ptr().add(insert_index) };
1023+
1024+
// SAFETY:
1025+
// Copies from
1026+
// insert_index..remove_index
1027+
// to
1028+
// (insert_index + 1)..(remove_index + 1)
1029+
//
1030+
// insert_index < remove_index (this match)
1031+
// insert_index + 1 < remove_index + 1 (can't saturate because <= length)
1032+
// insert_index + 1 <= remove_index < length
1033+
// remove_index + 1 <= length
1034+
//
1035+
// If `insert_index == 0`, and `remove_index + 1 == length`, then we copy from
1036+
// 0..(length-1) to 1..(length).
1037+
unsafe { ptr::copy(insert_at, insert_at.add(1), remove_index - insert_index) };
1038+
}
1039+
}
1040+
1041+
// SAFETY: `insert_index < length` assertion guarantees the pointer is within bounds.
1042+
unsafe { ptr::write(self.as_mut_ptr().add(insert_index), element) };
1043+
to_remove
1044+
}
1045+
9631046
/// Returns true if the vec is full
9641047
pub fn is_full(&self) -> bool {
9651048
self.len() == self.capacity()
@@ -2119,6 +2202,92 @@ mod tests {
21192202
assert_eq!(v.len(), 0);
21202203
}
21212204

2205+
#[test]
2206+
fn remove_insert() {
2207+
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2208+
let mut v: Vec<u8, 10> = Vec::from_array(arr);
2209+
let mut v2: Vec<u8, 10> = Vec::from_array(arr);
2210+
2211+
// insert_index == remove_index
2212+
let n = v.remove_insert(2, 2, 10);
2213+
assert_eq!(n, 2);
2214+
assert_eq!(v, [0, 1, 10, 3, 4, 5, 6, 7, 8, 9]);
2215+
2216+
let n2 = v2.remove(2);
2217+
v2.insert(2, 10).unwrap();
2218+
assert_eq!(n, n2);
2219+
assert_eq!(v, v2);
2220+
2221+
// reset
2222+
v.copy_from_slice(&arr);
2223+
v2.copy_from_slice(&arr);
2224+
2225+
// insert_index > remove_index
2226+
let n = v.remove_insert(3, 5, 10);
2227+
assert_eq!(n, 3);
2228+
assert_eq!(v, [0, 1, 2, 4, 5, 10, 6, 7, 8, 9]);
2229+
2230+
let n2 = v2.remove(3);
2231+
v2.insert(5, 10).unwrap();
2232+
assert_eq!(n, n2);
2233+
assert_eq!(v, v2);
2234+
2235+
v.copy_from_slice(&arr);
2236+
v2.copy_from_slice(&arr);
2237+
2238+
// insert_index < remove_index
2239+
let n = v.remove_insert(5, 3, 10);
2240+
assert_eq!(n, 5);
2241+
assert_eq!(v, [0, 1, 2, 10, 3, 4, 6, 7, 8, 9]);
2242+
2243+
let n2 = v2.remove(5);
2244+
v2.insert(3, 10).unwrap();
2245+
2246+
assert_eq!(n, n2);
2247+
assert_eq!(v, v2);
2248+
2249+
// at boundaries
2250+
2251+
v.copy_from_slice(&arr);
2252+
v2.copy_from_slice(&arr);
2253+
2254+
let n = v.remove_insert(0, 9, 10);
2255+
assert_eq!(n, 0);
2256+
assert_eq!(v, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2257+
2258+
let n2 = v2.remove(0);
2259+
v2.insert(9, 10).unwrap();
2260+
assert_eq!(n, n2);
2261+
assert_eq!(v, v2);
2262+
2263+
v.copy_from_slice(&arr);
2264+
v2.copy_from_slice(&arr);
2265+
2266+
let n = v.remove_insert(9, 0, 10);
2267+
assert_eq!(n, 9);
2268+
assert_eq!(v, [10, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
2269+
2270+
let n2 = v2.remove(9);
2271+
v2.insert(0, 10).unwrap();
2272+
assert_eq!(n, n2);
2273+
assert_eq!(v, v2);
2274+
}
2275+
2276+
#[test]
2277+
#[should_panic]
2278+
fn remove_insert_out_of_bounds() {
2279+
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2280+
let mut v: Vec<u8, 10> = Vec::from_array(arr);
2281+
let _ = v.remove_insert(0, 10, 10);
2282+
}
2283+
2284+
#[test]
2285+
#[should_panic]
2286+
fn remove_insert_empty() {
2287+
let mut v: Vec<u8, 10> = Vec::from_array([]);
2288+
let _ = v.remove_insert(0, 0, 10);
2289+
}
2290+
21222291
#[test]
21232292
fn resize_size_limit() {
21242293
let mut v: Vec<u8, 4> = Vec::new();

0 commit comments

Comments
 (0)