Skip to content

Commit 0ae4b0a

Browse files
authored
Merge pull request swiftlang#25866 from Catfish-Man/someone-set-up-us-the-bom
Bridge non-ASCII SmallStrings as native Swift Strings rather than by creating CFStrings
2 parents 09a3147 + 27359bd commit 0ae4b0a

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

stdlib/public/core/StringBridge.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,25 @@ extension String {
321321
@_effects(releasenone)
322322
public // SPI(Foundation)
323323
func _bridgeToObjectiveCImpl() -> AnyObject {
324-
if _guts.isSmall {
324+
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
325+
if _guts.isSmallASCII {
325326
return _guts.asSmall.withUTF8 { bufPtr in
326327
return _createCFString(
327-
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
328-
bufPtr.count,
329-
kCFStringEncodingUTF8
328+
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
329+
bufPtr.count,
330+
kCFStringEncodingUTF8
330331
)
331332
}
332333
}
334+
if _guts.isSmall {
335+
// We can't form a tagged pointer String, so grow to a non-small String,
336+
// and bridge that instead. Also avoids CF deleting any BOM that may be
337+
// present
338+
var copy = self
339+
copy._guts.grow(_SmallString.capacity + 1)
340+
_internalInvariant(!copy._guts.isSmall)
341+
return copy._bridgeToObjectiveCImpl()
342+
}
333343
if _guts._object.isImmortal {
334344
// TODO: We'd rather emit a valid ObjC object statically than create a
335345
// shared string class instance.

test/stdlib/TestNSString.swift

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,40 @@ import FoundationBridgeObjC
3030

3131
class TestNSString : TestNSStringSuper {
3232

33-
func test_equalOverflow() {
34-
let cyrillic = "чебурашка@ящик-с-апельсинами.рф"
35-
let other = getNSStringEqualTestString()
36-
print(NSStringBridgeTestEqual(cyrillic, other))
37-
}
33+
func test_equalOverflow() {
34+
let cyrillic = "чебурашка@ящик-с-апельсинами.рф"
35+
let other = getNSStringEqualTestString()
36+
print(NSStringBridgeTestEqual(cyrillic, other))
37+
}
38+
39+
func test_smallString_BOM() {
40+
let bom = "\u{FEFF}" // U+FEFF (ZERO WIDTH NO-BREAK SPACE)
41+
// expectEqual(1, NSString(string: bom).length)
42+
// expectEqual(4, NSString(string: "\(bom)abc").length)
43+
// expectEqual(5, NSString(string: "\(bom)\(bom)abc").length)
44+
// expectEqual(4, NSString(string: "a\(bom)bc").length)
45+
// expectEqual(13, NSString(string: "\(bom)234567890123").length)
46+
// expectEqual(14, NSString(string: "\(bom)2345678901234").length)
47+
48+
expectEqual(1, (bom as NSString).length)
49+
expectEqual(4, ("\(bom)abc" as NSString).length)
50+
expectEqual(5, ("\(bom)\(bom)abc" as NSString).length)
51+
expectEqual(4, ("a\(bom)bc" as NSString).length)
52+
expectEqual(13, ("\(bom)234567890123" as NSString).length)
53+
expectEqual(14, ("\(bom)2345678901234" as NSString).length)
54+
55+
let string = "\(bom)abc"
56+
let middleIndex = string.index(string.startIndex, offsetBy: 2)
57+
string.enumerateSubstrings(in: middleIndex..<string.endIndex, options: .byLines) { (_, _, _, _) in } //shouldn't crash
58+
}
3859

3960
}
4061

4162
#if !FOUNDATION_XCTEST
4263
var NSStringTests = TestSuite("TestNSString")
4364
NSStringTests.test("test_equalOverflow") { TestNSString().test_equalOverflow() }
65+
NSStringTests.test("test_smallString_BOM") {
66+
TestNSString().test_smallString_BOM()
67+
}
4468
runAllTests()
4569
#endif

0 commit comments

Comments
 (0)