@@ -1412,6 +1412,18 @@ extension NSMutableString {
1412
1412
}
1413
1413
return 0
1414
1414
}
1415
+
1416
+ private static func makeFindResultsRangeIterator( findResults: CFArray , count: Int , backwards: Bool ) -> AnyIterator < NSRange > {
1417
+ var index = 0
1418
+ return AnyIterator < NSRange > ( ) { ( ) -> NSRange ? in
1419
+ defer { index += 1 }
1420
+ if index < count {
1421
+ let rangePtr = CFArrayGetValueAtIndex ( findResults, backwards ? count - index - 1 : index)
1422
+ return NSRange ( rangePtr!. load ( as: CFRange . self) )
1423
+ }
1424
+ return nil
1425
+ }
1426
+ }
1415
1427
1416
1428
public func replaceOccurrences( of target: String , with replacement: String , options: CompareOptions = [ ] , range searchRange: NSRange ) -> Int {
1417
1429
let backwards = options. contains ( . backwards)
@@ -1422,19 +1434,35 @@ extension NSMutableString {
1422
1434
if options. contains ( . regularExpression) {
1423
1435
return _replaceOccurrencesOfRegularExpressionPattern ( target, withTemplate: replacement, options: options, range: searchRange)
1424
1436
}
1425
-
1426
1437
1427
- if let findResults = CFStringCreateArrayWithFindResults ( kCFAllocatorSystemDefault, _cfObject, target. _cfObject, CFRange ( searchRange) , options. _cfValue ( true ) ) {
1428
- let numOccurrences = CFArrayGetCount ( findResults)
1429
- for cnt in 0 ..< numOccurrences {
1430
- let rangePtr = CFArrayGetValueAtIndex ( findResults, backwards ? cnt : numOccurrences - cnt - 1 )
1431
- replaceCharacters ( in: NSRange ( rangePtr!. load ( as: CFRange . self) ) , with: replacement)
1438
+ guard let findResults = CFStringCreateArrayWithFindResults ( kCFAllocatorSystemDefault, _cfObject, target. _cfObject, CFRange ( searchRange) , options. _cfValue ( true ) ) else {
1439
+ return 0
1440
+ }
1441
+ let numOccurrences = CFArrayGetCount ( findResults)
1442
+
1443
+ let rangeIterator = NSMutableString . makeFindResultsRangeIterator ( findResults: findResults, count: numOccurrences, backwards: backwards)
1444
+
1445
+ guard type ( of: self ) == NSMutableString . self else {
1446
+ // If we're dealing with non NSMutableString, mutations must go through `replaceCharacters` (documented behavior)
1447
+ for range in rangeIterator {
1448
+ replaceCharacters ( in: range, with: replacement)
1432
1449
}
1450
+
1433
1451
return numOccurrences
1434
- } else {
1435
- return 0
1436
1452
}
1437
1453
1454
+ var newStorage = Substring ( )
1455
+ var sourceStringCurrentIndex = _storage. startIndex
1456
+ for range in rangeIterator {
1457
+ let matchStartIndex = String . Index ( utf16Offset: range. location, in: _storage)
1458
+ let matchEndIndex = String . Index ( utf16Offset: range. location + range. length, in: _storage)
1459
+ newStorage += _storage [ sourceStringCurrentIndex ..< matchStartIndex]
1460
+ newStorage += replacement
1461
+ sourceStringCurrentIndex = matchEndIndex
1462
+ }
1463
+ newStorage += _storage [ sourceStringCurrentIndex ..< _storage. endIndex]
1464
+ _storage = String ( newStorage)
1465
+ return numOccurrences
1438
1466
}
1439
1467
1440
1468
public func applyTransform( _ transform: String , reverse: Bool , range: NSRange , updatedRange resultingRange: NSRangePointer ? ) -> Bool {
0 commit comments