@@ -677,6 +677,129 @@ impl<LenT: LenType, S: StringStorage + ?Sized> StringInner<LenT, S> {
677
677
pub fn clear ( & mut self ) {
678
678
self . vec . clear ( ) ;
679
679
}
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
+ }
680
803
}
681
804
682
805
impl < LenT : LenType , const N : usize > Default for String < N , LenT > {
@@ -1240,4 +1363,103 @@ mod tests {
1240
1363
let formatted = format ! ( 2 ; "123" ) ;
1241
1364
assert_eq ! ( formatted, Err ( core:: fmt:: Error ) ) ;
1242
1365
}
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
+ }
1243
1465
}
0 commit comments