Skip to content

Commit 93aa217

Browse files
author
Pushkar Kulkarni
committed
Fix for a crash in String.substring(with:) [SR-2483]
1 parent da2fdd9 commit 93aa217

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

Foundation/NSString.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,23 @@ extension NSString {
346346
let start = _storage.utf16.startIndex
347347
let min = start.advanced(by: range.location)
348348
let max = start.advanced(by: range.location + range.length)
349-
return String(_storage.utf16[min..<max])!
349+
if let substr = String(_storage.utf16[min..<max]) {
350+
return substr
351+
} else {
352+
//a broken surrogate pair is replaced by OXFFFD - the Unicode Replacement Character
353+
let replacementCharacter = String(describing: UnicodeScalar(0xFFFD)!)
354+
355+
//is min breaking a surrogate pair?
356+
guard min.advanced(by: 1) < max else { return "" }
357+
if let substrSuffix = String(_storage.utf16[min.advanced(by: 1)..<max]) {
358+
return replacementCharacter + substrSuffix
359+
}
360+
361+
//max is breaking a surrogate pair
362+
guard min < max.advanced(by: -1) else { return "" }
363+
let substrPrefix = String(_storage.utf16[min..<max.advanced(by: -1)])!
364+
return substrPrefix + replacementCharacter
365+
}
350366
} else {
351367
let buff = UnsafeMutablePointer<unichar>.allocate(capacity: range.length)
352368
getCharacters(buff, range: range)

TestFoundation/TestNSString.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class TestNSString : XCTestCase {
8989
("test_PrefixSuffix", test_PrefixSuffix),
9090
("test_utf16StringRangeCount", test_StringUTF16ViewIndexStrideableRange),
9191
("test_reflection", { _ in test_reflection }),
92+
("test_substringWithRange", test_substringWithRange),
9293
]
9394
}
9495

@@ -970,6 +971,21 @@ class TestNSString : XCTestCase {
970971
let mutableString = NSMutableString(string: "Test")
971972
XCTAssertEqual(mutableString, "Test")
972973
}
974+
975+
func test_substringWithRange() {
976+
let trivial = NSString(string: "swift.org")
977+
XCTAssertEqual(trivial.substring(with: NSMakeRange(0, 5)), "swift")
978+
979+
let surrogatePairSuffix = NSString(string: "Hurray🎉")
980+
XCTAssertEqual(surrogatePairSuffix.substring(with: NSMakeRange(0, 7)), "Hurray�")
981+
982+
let surrogatePairPrefix = NSString(string: "🐱Cat")
983+
XCTAssertEqual(surrogatePairPrefix.substring(with: NSMakeRange(1, 4)), "�Cat")
984+
985+
let crlf = NSString(string: "\r\n")
986+
XCTAssertEqual(crlf.substring(with: NSMakeRange(0,1)), "")
987+
XCTAssertEqual(crlf.substring(with: NSMakeRange(1,0)), "")
988+
}
973989
}
974990

975991
struct ComparisonTest {

0 commit comments

Comments
 (0)