Skip to content

Commit 05971ca

Browse files
committed
SR-7455: Ask for ASCII when calling CFStringGetCStringPtr()
- Add tests for multibyte UTF-8/ UTF-16.
1 parent b5e250f commit 05971ca

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

Foundation/String.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ extension String : _ObjectiveCBridgeable {
3232
let length = CFStringGetLength(cf)
3333
if length == 0 {
3434
result = ""
35-
} else if let ptr = CFStringGetCStringPtr(cf, CFStringEncoding(kCFStringEncodingUTF8)) {
35+
} else if let ptr = CFStringGetCStringPtr(cf, CFStringEncoding(kCFStringEncodingASCII)) {
36+
// ASCII encoding has 1 byte per code point and CFStringGetLength() returned the length in
37+
// codepoints so length should be the length of the ASCII string in bytes. We cant ask for the UTF-8
38+
// encoding as some codepoints are multi-byte in UTF8 so the buffer length wouldn't be known.
39+
// Note: CFStringGetCStringPtr(cf, CFStringEncoding(kCFStringEncodingUTF8)) does seems to return NULL
40+
// for strings with multibyte UTF-8 but this isnt guaranteed or documented so ASCII is safer.
3641
result = ptr.withMemoryRebound(to: UInt8.self, capacity: length) {
3742
return String(decoding: UnsafeBufferPointer(start: $0, count: length), as: UTF8.self)
3843
}

TestFoundation/TestNSString.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,49 @@ class TestNSString: LoopbackServerTest {
115115
} else {
116116
XCTFail("Cant convert Data to String")
117117
}
118+
119+
// Test multibyte UTF8 and UTF16
120+
// kra ("ĸ") has codepoint value 312,
121+
// as UTF-8 bytes it is 0xC4 0xB8
122+
// as UTF-16 bytes it is 0x1, 0x38
123+
let kra = "ĸ"
124+
let utf8KraData = Data(bytes: [0xc4, 0xb8])
125+
if let utf8kra = utf8KraData.withUnsafeBytes( { (bytes: UnsafePointer<UInt8>) in
126+
return NSString(bytes: bytes, length: utf8KraData.count, encoding: String.Encoding.utf8.rawValue)
127+
}) {
128+
XCTAssertEqual(kra.count, 1)
129+
XCTAssertEqual(kra.utf8.count, 2)
130+
XCTAssertEqual(kra.utf16.count, 1)
131+
XCTAssertEqual(kra, utf8kra as String)
132+
} else {
133+
XCTFail("Cant create UTF8 kra")
134+
}
135+
136+
let utf16KraData = Data(bytes: [0x1, 0x38])
137+
if let utf16kra = utf16KraData.withUnsafeBytes( { (bytes: UnsafePointer<UInt8>) in
138+
return NSString(bytes: bytes, length: utf16KraData.count, encoding: String.Encoding.utf16.rawValue)
139+
}) {
140+
XCTAssertEqual(kra.count, 1)
141+
XCTAssertEqual(kra.utf8.count, 2)
142+
XCTAssertEqual(kra.utf16.count, 1)
143+
XCTAssertEqual(kra, utf16kra as String)
144+
} else {
145+
XCTFail("Cant create UTF16 kra")
146+
}
147+
148+
// Test a large string > 255 characters
149+
let largeString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut at tincidunt arcu. Suspendisse nec sodales erat, sit amet imperdiet ipsum. Etiam sed ornare felis. Nunc mauris turpis, bibendum non lectus quis, malesuada placerat turpis. Nam adipiscing non massa et semper. Nulla convallis semper bibendum."
150+
XCTAssertTrue(largeString.count > 255)
151+
let largeData = largeString.data(using: .utf8)!
152+
if let largeText = largeData.withUnsafeBytes( { (bytes: UnsafePointer<UInt8>) in
153+
return NSString(bytes: bytes, length: largeData.count, encoding: String.Encoding.ascii.rawValue)
154+
}) {
155+
XCTAssertEqual(largeText.length, largeString.count)
156+
XCTAssertEqual(largeText.length, largeData.count)
157+
XCTAssertEqual(largeString, largeText as String)
158+
} else {
159+
XCTFail("Cant convert large Data string to String")
160+
}
118161
}
119162

120163
func test_boolValue() {

0 commit comments

Comments
 (0)