File tree Expand file tree Collapse file tree 2 files changed +58
-9
lines changed Expand file tree Collapse file tree 2 files changed +58
-9
lines changed Original file line number Diff line number Diff line change @@ -1232,24 +1232,38 @@ impl CompactString {
1232
1232
pub fn retain ( & mut self , mut predicate : impl FnMut ( char ) -> bool ) {
1233
1233
// We iterate over the string, and copy character by character.
1234
1234
1235
- let s = self . as_mut_str ( ) ;
1236
- let mut dest_idx = 0 ;
1237
- let mut src_idx = 0 ;
1238
- while let Some ( ch) = s[ src_idx..] . chars ( ) . next ( ) {
1235
+ struct SetLenOnDrop < ' a > {
1236
+ self_ : & ' a mut CompactString ,
1237
+ src_idx : usize ,
1238
+ dst_idx : usize ,
1239
+ }
1240
+
1241
+ let mut g = SetLenOnDrop {
1242
+ self_ : self ,
1243
+ src_idx : 0 ,
1244
+ dst_idx : 0 ,
1245
+ } ;
1246
+ let s = g. self_ . as_mut_str ( ) ;
1247
+ while let Some ( ch) = s[ g. src_idx ..] . chars ( ) . next ( ) {
1239
1248
let ch_len = ch. len_utf8 ( ) ;
1240
1249
if predicate ( ch) {
1241
1250
// SAFETY: We know that both indices are valid, and that we don't split a char.
1242
1251
unsafe {
1243
1252
let p = s. as_mut_ptr ( ) ;
1244
- core:: ptr:: copy ( p. add ( src_idx) , p. add ( dest_idx ) , ch_len) ;
1253
+ core:: ptr:: copy ( p. add ( g . src_idx ) , p. add ( g . dst_idx ) , ch_len) ;
1245
1254
}
1246
- dest_idx += ch_len;
1255
+ g . dst_idx += ch_len;
1247
1256
}
1248
- src_idx += ch_len;
1257
+ g . src_idx += ch_len;
1249
1258
}
1250
1259
1251
- // SAFETY: We know that the index is a valid position to break the string.
1252
- unsafe { self . set_len ( dest_idx) } ;
1260
+ impl Drop for SetLenOnDrop < ' _ > {
1261
+ fn drop ( & mut self ) {
1262
+ // SAFETY: We know that the index is a valid position to break the string.
1263
+ unsafe { self . self_ . set_len ( self . dst_idx ) } ;
1264
+ }
1265
+ }
1266
+ drop ( g) ;
1253
1267
}
1254
1268
1255
1269
/// Decode a bytes slice as UTF-8 string, replacing any illegal codepoints
Original file line number Diff line number Diff line change @@ -1367,6 +1367,41 @@ fn test_insert(to_compact: fn(&'static str) -> CompactString) {
1367
1367
) ;
1368
1368
}
1369
1369
1370
+ #[ test]
1371
+ #[ cfg_attr( not( panic = "unwind" ) , ignore = "test requires unwinding support" ) ]
1372
+ fn test_retain ( ) {
1373
+ let mut s = CompactString :: from ( "α_β_γ" ) ;
1374
+
1375
+ s. retain ( |_| true ) ;
1376
+ assert_eq ! ( s, "α_β_γ" ) ;
1377
+
1378
+ s. retain ( |c| c != '_' ) ;
1379
+ assert_eq ! ( s, "αβγ" ) ;
1380
+
1381
+ s. retain ( |c| c != 'β' ) ;
1382
+ assert_eq ! ( s, "αγ" ) ;
1383
+
1384
+ s. retain ( |c| c == 'α' ) ;
1385
+ assert_eq ! ( s, "α" ) ;
1386
+
1387
+ s. retain ( |_| false ) ;
1388
+ assert_eq ! ( s, "" ) ;
1389
+
1390
+ let mut s = CompactString :: from ( "0è0" ) ;
1391
+ let _ = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
1392
+ let mut count = 0 ;
1393
+ s. retain ( |_| {
1394
+ count += 1 ;
1395
+ match count {
1396
+ 1 => false ,
1397
+ 2 => true ,
1398
+ _ => panic ! ( ) ,
1399
+ }
1400
+ } ) ;
1401
+ } ) ) ;
1402
+ assert ! ( std:: str :: from_utf8( s. as_bytes( ) ) . is_ok( ) ) ;
1403
+ }
1404
+
1370
1405
#[ test]
1371
1406
fn test_remove ( ) {
1372
1407
let mut control = String :: from ( "🦄🦀hello🎶world🇺🇸" ) ;
You can’t perform that action at this time.
0 commit comments