Skip to content

Commit d71f676

Browse files
Adds insert and insert_str methods to String
1 parent 6b17767 commit d71f676

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed

src/string/mod.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,138 @@ 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+
core::ptr::copy(
722+
self.vec.as_ptr().add(idx),
723+
self.vec.as_mut_ptr().add(idx + ch_len),
724+
len - idx,
725+
);
726+
}
727+
728+
// SAFETY: Copy the encoded character into the vacated region if
729+
// `idx != len`, or into the uninitialized spare capacity otherwise.
730+
unsafe {
731+
// 4 bytes is the maximum length of a UTF-8 character
732+
let mut buf = [0u8; 4];
733+
let encoded = ch.encode_utf8(&mut buf);
734+
core::ptr::copy_nonoverlapping(
735+
encoded.as_ptr(),
736+
self.vec.as_mut_ptr().add(idx),
737+
ch_len,
738+
);
739+
}
740+
741+
// SAFETY: Update the length to include the newly added bytes.
742+
unsafe {
743+
self.vec.set_len(len + ch_len);
744+
}
745+
746+
Ok(())
747+
}
748+
749+
/// Inserts a string slice into this `String` at a byte position.
750+
///
751+
/// This is an *O*(*n*) operation as it requires copying every element in the
752+
/// buffer.
753+
///
754+
/// # Panics
755+
///
756+
/// Panics if `idx` is larger than the `String`'s length, or if it does not
757+
/// lie on a [`char`] boundary.
758+
///
759+
/// # Examples
760+
///
761+
/// ```
762+
/// use heapless::String;
763+
///
764+
/// let mut s: String<8> = String::try_from("bar")?;
765+
///
766+
/// s.insert_str(0, "foo")?;
767+
///
768+
/// assert_eq!("foobar", s);
769+
/// # Ok::<(), heapless::CapacityError>(())
770+
/// ```
771+
#[inline]
772+
pub fn insert_str(&mut self, idx: usize, string: &str) -> Result<(), CapacityError> {
773+
assert!(self.is_char_boundary(idx), "index must be a char boundary");
774+
775+
let len = self.len();
776+
let string_len = string.len();
777+
778+
// Check if there is enough capacity
779+
if len + string_len > self.capacity() {
780+
return Err(CapacityError);
781+
}
782+
783+
// SAFETY: Move the bytes starting from `idx` to their new location
784+
// `string_len` bytes ahead. This is safe because we checked there is
785+
// sufficient capacity, and `idx` is a char boundary.
786+
unsafe {
787+
core::ptr::copy(
788+
self.vec.as_ptr().add(idx),
789+
self.vec.as_mut_ptr().add(idx + string_len),
790+
len - idx,
791+
);
792+
}
793+
794+
// SAFETY: Copy the new string slice into the vacated region if `idx != len`,
795+
// or into the uninitialized spare capacity otherwise. The borrow checker
796+
// ensures that the source and destination do not overlap.
797+
unsafe {
798+
core::ptr::copy_nonoverlapping(
799+
string.as_ptr(),
800+
self.vec.as_mut_ptr().add(idx),
801+
string_len,
802+
);
803+
}
804+
805+
// SAFETY: Update the length to include the newly added bytes.
806+
unsafe {
807+
self.vec.set_len(len + string_len);
808+
}
809+
810+
Ok(())
811+
}
680812
}
681813

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

0 commit comments

Comments
 (0)