Skip to content

Commit 692e8e0

Browse files
authored
Merge pull request #584 from riley-williams/string-insertion
String insertion
2 parents bcd71e5 + 451a98b commit 692e8e0

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `String::insert` and `String::insert_str`.
13+
1014
### Changed
1115

1216
- `bytes::BufMut` is now implemented on `VecInner`.

src/string/mod.rs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,129 @@ impl<LenT: LenType, S: StringStorage + ?Sized> StringInner<LenT, S> {
677677
pub fn clear(&mut self) {
678678
self.vec.clear();
679679
}
680+
681+
/// Inserts a character into this `String` at a byte position.
682+
///
683+
/// This is an *O*(*n*) operation as it requires copying every element in the
684+
/// buffer.
685+
///
686+
/// # Panics
687+
///
688+
/// Panics if `idx` is larger than the `String`'s length, or if it does not
689+
/// lie on a [`char`] boundary.
690+
///
691+
/// # Examples
692+
///
693+
/// ```
694+
/// use heapless::String;
695+
///
696+
/// let mut s: String<4> = String::new();
697+
///
698+
/// s.insert(0, 'f').unwrap();
699+
/// s.insert(1, 'o').unwrap();
700+
/// s.insert(2, 'o').unwrap();
701+
///
702+
/// assert_eq!("foo", s);
703+
/// # Ok::<(), heapless::CapacityError>(())
704+
/// ```
705+
#[inline]
706+
pub fn insert(&mut self, idx: usize, ch: char) -> Result<(), CapacityError> {
707+
assert!(self.is_char_boundary(idx), "index must be a char boundary");
708+
709+
let len = self.len();
710+
let ch_len = ch.len_utf8();
711+
712+
// Check if there is enough capacity
713+
if len + ch_len > self.capacity() {
714+
return Err(CapacityError);
715+
}
716+
717+
// SAFETY: Move the bytes starting from `idx` to their new location `ch_len`
718+
// bytes ahead. This is safe because we checked `len + ch_len` does not
719+
// exceed the capacity and `idx` is a char boundary.
720+
unsafe {
721+
let ptr = self.vec.as_mut_ptr();
722+
core::ptr::copy(ptr.add(idx), ptr.add(idx + ch_len), len - idx);
723+
}
724+
725+
// SAFETY: Encode the character into the vacated region if `idx != len`,
726+
// or into the uninitialized spare capacity otherwise. This is safe
727+
// because `is_char_boundary` checks that `idx <= len`, and we checked that
728+
// `(idx + ch_len)` does not exceed the capacity.
729+
unsafe {
730+
let buf = core::slice::from_raw_parts_mut(self.vec.as_mut_ptr().add(idx), ch_len);
731+
ch.encode_utf8(buf);
732+
}
733+
734+
// SAFETY: Update the length to include the newly added bytes. This is
735+
// safe because we checked that `len + ch_len` does not exceed the capacity.
736+
unsafe {
737+
self.vec.set_len(len + ch_len);
738+
}
739+
740+
Ok(())
741+
}
742+
743+
/// Inserts a string slice into this `String` at a byte position.
744+
///
745+
/// This is an *O*(*n*) operation as it requires copying every element in the
746+
/// buffer.
747+
///
748+
/// # Panics
749+
///
750+
/// Panics if `idx` is larger than the `String`'s length, or if it does not
751+
/// lie on a [`char`] boundary.
752+
///
753+
/// # Examples
754+
///
755+
/// ```
756+
/// use heapless::String;
757+
///
758+
/// let mut s: String<8> = String::try_from("bar")?;
759+
///
760+
/// s.insert_str(0, "foo")?;
761+
///
762+
/// assert_eq!("foobar", s);
763+
/// # Ok::<(), heapless::CapacityError>(())
764+
/// ```
765+
#[inline]
766+
pub fn insert_str(&mut self, idx: usize, string: &str) -> Result<(), CapacityError> {
767+
assert!(self.is_char_boundary(idx), "index must be a char boundary");
768+
769+
let len = self.len();
770+
let string_len = string.len();
771+
772+
// Check if there is enough capacity
773+
if len + string_len > self.capacity() {
774+
return Err(CapacityError);
775+
}
776+
777+
// SAFETY: Move the bytes starting from `idx` to their new location
778+
// `string_len` bytes ahead. This is safe because we checked there is
779+
// sufficient capacity, and `idx` is a char boundary.
780+
unsafe {
781+
let ptr = self.vec.as_mut_ptr();
782+
core::ptr::copy(ptr.add(idx), ptr.add(idx + string_len), len - idx);
783+
}
784+
785+
// SAFETY: Copy the new string slice into the vacated region if `idx != len`,
786+
// or into the uninitialized spare capacity otherwise. The borrow checker
787+
// ensures that the source and destination do not overlap.
788+
unsafe {
789+
core::ptr::copy_nonoverlapping(
790+
string.as_ptr(),
791+
self.vec.as_mut_ptr().add(idx),
792+
string_len,
793+
);
794+
}
795+
796+
// SAFETY: Update the length to include the newly added bytes.
797+
unsafe {
798+
self.vec.set_len(len + string_len);
799+
}
800+
801+
Ok(())
802+
}
680803
}
681804

682805
impl<LenT: LenType, const N: usize> Default for String<N, LenT> {
@@ -1240,4 +1363,103 @@ mod tests {
12401363
let formatted = format!(2; "123");
12411364
assert_eq!(formatted, Err(core::fmt::Error));
12421365
}
1366+
1367+
#[test]
1368+
fn insert() {
1369+
let mut s: String<6> = String::try_from("123").unwrap();
1370+
assert!(s.insert(0, 'a').is_ok());
1371+
assert_eq!(s, "a123");
1372+
1373+
assert!(s.insert(2, 'b').is_ok());
1374+
assert_eq!(s, "a1b23");
1375+
1376+
assert!(s.insert(s.len(), '4').is_ok());
1377+
assert_eq!(s, "a1b234");
1378+
1379+
assert_eq!(s.len(), 6);
1380+
assert!(s.insert(0, 'd').is_err());
1381+
assert_eq!(s, "a1b234");
1382+
}
1383+
1384+
#[test]
1385+
fn insert_unicode() {
1386+
let mut s: String<21> = String::try_from("ĝėēƶ").unwrap();
1387+
let idx = s.find("ė").unwrap();
1388+
1389+
assert!(s.insert(idx, '🦀').is_ok());
1390+
assert_eq!(s, "ĝ🦀ėēƶ");
1391+
1392+
s.insert(s.len(), '🦀').unwrap();
1393+
assert_eq!(s, "ĝ🦀ėēƶ🦀");
1394+
1395+
s.insert(0, '🦀').unwrap();
1396+
assert_eq!(s, "🦀ĝ🦀ėēƶ🦀");
1397+
1398+
assert_eq!(s.len(), 20);
1399+
assert_eq!('ƶ'.len_utf8(), 2);
1400+
assert!(s.insert(0, 'ƶ').is_err());
1401+
assert_eq!(s, "🦀ĝ🦀ėēƶ🦀");
1402+
}
1403+
1404+
#[test]
1405+
#[should_panic = "index must be a char boundary"]
1406+
fn insert_at_non_char_boundary_panics() {
1407+
let mut s: String<8> = String::try_from("é").unwrap();
1408+
_ = s.insert(1, 'a');
1409+
}
1410+
1411+
#[test]
1412+
#[should_panic = "index must be a char boundary"]
1413+
fn insert_beyond_length_panics() {
1414+
let mut s: String<8> = String::try_from("a").unwrap();
1415+
_ = s.insert(2, 'a');
1416+
}
1417+
1418+
#[test]
1419+
fn insert_str() {
1420+
let mut s: String<14> = String::try_from("bar").unwrap();
1421+
assert!(s.insert_str(0, "foo").is_ok());
1422+
assert_eq!(s, "foobar");
1423+
1424+
assert!(s.insert_str(3, "baz").is_ok());
1425+
assert_eq!(s, "foobazbar");
1426+
1427+
assert!(s.insert_str(s.len(), "end").is_ok());
1428+
assert_eq!(s, "foobazbarend");
1429+
1430+
assert_eq!(s.len(), 12);
1431+
assert!(s.insert_str(0, "def").is_err());
1432+
assert_eq!(s, "foobazbarend");
1433+
}
1434+
1435+
#[test]
1436+
fn insert_str_unicode() {
1437+
let mut s: String<20> = String::try_from("Héllô").unwrap();
1438+
let idx = s.find("lô").unwrap();
1439+
1440+
assert!(s.insert_str(idx, "p, í'm ").is_ok());
1441+
assert_eq!(s, "Hélp, í'm lô");
1442+
1443+
assert!(s.insert_str(s.len(), "st").is_ok());
1444+
assert_eq!(s, "Hélp, í'm lôst");
1445+
1446+
assert_eq!(s.len(), 17);
1447+
assert_eq!("🦀".len(), 4);
1448+
assert!(s.insert_str(0, "🦀").is_err());
1449+
assert_eq!(s, "Hélp, í'm lôst");
1450+
}
1451+
1452+
#[test]
1453+
#[should_panic = "index must be a char boundary"]
1454+
fn insert_str_at_non_char_boundary_panics() {
1455+
let mut s: String<8> = String::try_from("é").unwrap();
1456+
_ = s.insert_str(1, "a");
1457+
}
1458+
1459+
#[test]
1460+
#[should_panic = "index must be a char boundary"]
1461+
fn insert_str_beyond_length_panics() {
1462+
let mut s: String<8> = String::try_from("a").unwrap();
1463+
_ = s.insert_str(2, "a");
1464+
}
12431465
}

0 commit comments

Comments
 (0)