Skip to content

Commit d961d73

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

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

src/string/mod.rs

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

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

0 commit comments

Comments
 (0)