Skip to content

Commit 40b4ee6

Browse files
committed
SR-7455: Allow NUL in Strings to match Darwin
- String._conditionallyBridgeFromObjectiveC: Use String(decoding:as: UTF8.self) instead of String(cString:) to allow NULs in the decoded string. - Update tests which use buffers with trailing zeros to create strings, making the tests work correctly against Darwin's native Foundation.
1 parent 5b3dae9 commit 40b4ee6

File tree

9 files changed

+65
-16
lines changed

9 files changed

+65
-16
lines changed

DarwinCompatibilityTests.xcodeproj/project.pbxproj

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
B907F36F20BB188800013CBE /* NSString-ISO-8859-1-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B907F36E20BB188800013CBE /* NSString-ISO-8859-1-data.txt */; };
1111
B917D32420B0DB9700728EE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B917D32320B0DB9700728EE0 /* Foundation.framework */; };
1212
B917D32620B0DE2000728EE0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B917D32520B0DE2000728EE0 /* main.swift */; };
13+
B94897772135E7AD00FB930E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94897762135E7AC00FB930E /* Utilities.swift */; };
1314
B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */; };
1415
B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */ = {isa = PBXBuildFile; fileRef = B987C65D2093C8AF0026B50D /* TestImports.swift */; };
16+
B98E33E02136AC120044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */; };
1517
B9C89F361F6BF89C00087AF4 /* TestScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89EE61F6BF88F00087AF4 /* TestScanner.swift */; };
1618
B9C89F371F6BF89C00087AF4 /* TestNSValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89EE71F6BF88F00087AF4 /* TestNSValue.swift */; };
1719
B9C89F381F6BF89C00087AF4 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89EE81F6BF88F00087AF4 /* TestUtils.swift */; };
@@ -151,8 +153,10 @@
151153
B917D31C20B0DB8B00728EE0 /* xdgTestHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xdgTestHelper; sourceTree = BUILT_PRODUCTS_DIR; };
152154
B917D32320B0DB9700728EE0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
153155
B917D32520B0DE2000728EE0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = TestFoundation/xdgTestHelper/main.swift; sourceTree = "<group>"; };
156+
B94897762135E7AC00FB930E /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = TestFoundation/Utilities.swift; sourceTree = "<group>"; };
154157
B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestNSNumberBridging.swift; path = TestFoundation/TestNSNumberBridging.swift; sourceTree = "<group>"; };
155158
B987C65D2093C8AF0026B50D /* TestImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestImports.swift; path = TestFoundation/TestImports.swift; sourceTree = "<group>"; };
159+
B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TestFileWithZeros.txt; path = TestFoundation/Resources/TestFileWithZeros.txt; sourceTree = "<group>"; };
156160
B9C89ED11F6BF67C00087AF4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
157161
B9C89ED71F6BF77E00087AF4 /* DarwinCompatibilityTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DarwinCompatibilityTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
158162
B9C89EDB1F6BF77E00087AF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -288,9 +292,6 @@
288292
B9C89EB81F6BF47D00087AF4 = {
289293
isa = PBXGroup;
290294
children = (
291-
B917D32520B0DE2000728EE0 /* main.swift */,
292-
B987C65D2093C8AF0026B50D /* TestImports.swift */,
293-
B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */,
294295
B9C89FAC1F6DCAE700087AF4 /* Info.plist */,
295296
B9C89FAD1F6DCAE800087AF4 /* NSKeyedUnarchiver-ArrayTest.plist */,
296297
B9C89FB31F6DCAE900087AF4 /* NSKeyedUnarchiver-ComplexTest.plist */,
@@ -313,6 +314,11 @@
313314
B9C89FA91F6DCAE700087AF4 /* NSXMLDTDTestData.xml */,
314315
B9C89FB81F6DCAEB00087AF4 /* PropertyList-1.0.dtd */,
315316
B9C89FB91F6DCAEB00087AF4 /* Test.plist */,
317+
B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */,
318+
B917D32520B0DE2000728EE0 /* main.swift */,
319+
B94897762135E7AC00FB930E /* Utilities.swift */,
320+
B987C65D2093C8AF0026B50D /* TestImports.swift */,
321+
B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */,
316322
B9C89F891F6D4D9D00087AF4 /* HTTPServer.swift */,
317323
B9C89F321F6BF89B00087AF4 /* TestAffineTransform.swift */,
318324
B9C89EFB1F6BF89200087AF4 /* TestBundle.swift */,
@@ -519,6 +525,7 @@
519525
B9C89FC11F6DCAEB00087AF4 /* Info.plist in Resources */,
520526
B9C89FC21F6DCAEB00087AF4 /* NSKeyedUnarchiver-ArrayTest.plist in Resources */,
521527
B9C89FC31F6DCAEB00087AF4 /* NSStringTestData.txt in Resources */,
528+
B98E33E02136AC120044EBE9 /* TestFileWithZeros.txt in Resources */,
522529
B9C89FC41F6DCAEB00087AF4 /* NSKeyedUnarchiver-OrderedSetTest.plist in Resources */,
523530
B9C89FC51F6DCAEB00087AF4 /* NSString-UTF32-BE-data.txt in Resources */,
524531
B9C89FC61F6DCAEB00087AF4 /* NSKeyedUnarchiver-URLTest.plist in Resources */,
@@ -548,6 +555,7 @@
548555
isa = PBXSourcesBuildPhase;
549556
buildActionMask = 2147483647;
550557
files = (
558+
B94897772135E7AD00FB930E /* Utilities.swift in Sources */,
551559
B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */,
552560
B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */,
553561
B9C89F8B1F6D4DA900087AF4 /* HTTPServer.swift in Sources */,

Foundation.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@
339339
B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */; };
340340
B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */; };
341341
B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */; };
342+
B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */; };
342343
B9974B961EDF4A22007F15B8 /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B8F1EDF4A22007F15B8 /* TransferState.swift */; };
343344
B9974B971EDF4A22007F15B8 /* MultiHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B901EDF4A22007F15B8 /* MultiHandle.swift */; };
344345
B9974B981EDF4A22007F15B8 /* libcurlHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */; };
@@ -839,6 +840,7 @@
839840
B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-BE-data.txt"; sourceTree = "<group>"; };
840841
B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-LE-data.txt"; sourceTree = "<group>"; };
841842
B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSLock.swift; sourceTree = "<group>"; };
843+
B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TestFileWithZeros.txt; sourceTree = "<group>"; };
842844
B9974B8F1EDF4A22007F15B8 /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferState.swift; sourceTree = "<group>"; };
843845
B9974B901EDF4A22007F15B8 /* MultiHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiHandle.swift; sourceTree = "<group>"; };
844846
B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = libcurlHelpers.swift; sourceTree = "<group>"; };
@@ -1464,6 +1466,7 @@
14641466
EA66F6391BF1619600136161 /* Resources */ = {
14651467
isa = PBXGroup;
14661468
children = (
1469+
B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */,
14671470
D370696D1C394FBF00295652 /* NSKeyedUnarchiver-RangeTest.plist */,
14681471
D3E8D6D41C36AC0C00295652 /* NSKeyedUnarchiver-RectTest.plist */,
14691472
D3E8D6D21C36982700295652 /* NSKeyedUnarchiver-EdgeInsetsTest.plist */,
@@ -2168,6 +2171,7 @@
21682171
isa = PBXResourcesBuildPhase;
21692172
buildActionMask = 2147483647;
21702173
files = (
2174+
B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */,
21712175
B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */,
21722176
B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */,
21732177
D3A597F41C34142600295652 /* NSKeyedUnarchiver-NotificationTest.plist in Resources */,

Foundation/String.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ extension String : _ObjectiveCBridgeable {
2929
result = source._storage
3030
} else if type(of: source) == _NSCFString.self {
3131
let cf = unsafeBitCast(source, to: CFString.self)
32+
let length = CFStringGetLength(cf)
3233
if let str = CFStringGetCStringPtr(cf, CFStringEncoding(kCFStringEncodingUTF8)) {
33-
result = String(cString: str)
34+
result = str.withMemoryRebound(to: UInt8.self, capacity: length) { ptr in
35+
let buffer = UnsafeBufferPointer(start: ptr, count: length)
36+
return String(decoding: buffer, as: UTF8.self)
37+
}
3438
} else {
35-
let length = CFStringGetLength(cf)
3639
let buffer = UnsafeMutablePointer<UniChar>.allocate(capacity: length)
3740
CFStringGetCharacters(cf, CFRangeMake(0, length), buffer)
3841

45 Bytes
Binary file not shown.

TestFoundation/TestJSONSerialization.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ extension TestJSONSerialization {
13671367
let result = try JSONSerialization.writeJSONObject(dict, toStream: outputStream, options: [])
13681368
outputStream.close()
13691369
if(result > -1) {
1370-
XCTAssertEqual(NSString(bytes: buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
1370+
XCTAssertEqual(NSString(bytes: buffer, length: buffer.firstIndex(of: 0) ?? buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
13711371
}
13721372
} catch {
13731373
XCTFail("Error thrown: \(error)")
@@ -1390,7 +1390,7 @@ extension TestJSONSerialization {
13901390
let resultRead: Int = fileStream.read(&buffer, maxLength: buffer.count)
13911391
fileStream.close()
13921392
if(resultRead > -1){
1393-
XCTAssertEqual(NSString(bytes: buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
1393+
XCTAssertEqual(NSString(bytes: buffer, length: buffer.firstIndex(of: 0) ?? buffer.count, encoding: String.Encoding.utf8.rawValue), "{\"a\":{\"b\":1}}")
13941394
}
13951395
}
13961396
removeTestFile(filePath)

TestFoundation/TestNSData.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,11 +1475,12 @@ extension TestNSData {
14751475
if let contents = contents {
14761476
XCTAssertTrue(contents.length > 0)
14771477
let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
1478-
let str = String(bytesNoCopy: ptr, length: contents.length,
1479-
encoding: .ascii, freeWhenDone: false)
1480-
XCTAssertNotNil(str)
1481-
if let str = str {
1478+
var zeroIdx = contents.range(of: Data([0]), in: NSMakeRange(0, contents.length)).location
1479+
if zeroIdx == NSNotFound { zeroIdx = contents.length }
1480+
if let str = String(bytesNoCopy: ptr, length: zeroIdx, encoding: .ascii, freeWhenDone: false) {
14821481
XCTAssertTrue(str.hasSuffix("TestFoundation"))
1482+
} else {
1483+
XCTFail("Cant create String")
14831484
}
14841485
}
14851486

TestFoundation/TestNSString.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class TestNSString: LoopbackServerTest {
3131

3232
static var allTests: [(String, (TestNSString) -> () throws -> Void)] {
3333
return [
34+
("test_initData", test_initData),
3435
("test_boolValue", test_boolValue ),
3536
("test_BridgeConstruction", test_BridgeConstruction ),
3637
("test_integerValue", test_integerValue ),
@@ -95,6 +96,27 @@ class TestNSString: LoopbackServerTest {
9596
]
9697
}
9798

99+
func test_initData() {
100+
let testString = "\u{00} This is a test string"
101+
let data = testString.data(using: .utf8)!
102+
XCTAssertEqual(data.count, 23)
103+
_ = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
104+
if let text1 = NSString(bytes: bytes , length: data.count, encoding: String.Encoding.utf8.rawValue) {
105+
XCTAssertEqual(text1.length, data.count)
106+
XCTAssertEqual(text1, testString as NSString)
107+
} else {
108+
XCTFail("Cant convert Data to NSString")
109+
}
110+
}
111+
112+
if let text2 = String(data: data, encoding: .utf8) {
113+
XCTAssertEqual(text2.count, data.count)
114+
XCTAssertEqual(text2, testString)
115+
} else {
116+
XCTFail("Cant convert Data to String")
117+
}
118+
}
119+
98120
func test_boolValue() {
99121
let trueStrings: [NSString] = ["t", "true", "TRUE", "tRuE", "yes", "YES", "1", "+000009"]
100122
for string in trueStrings {
@@ -325,6 +347,16 @@ class TestNSString: LoopbackServerTest {
325347
if let contents = contents {
326348
XCTAssertEqual(contents, "This file is encoded as ISO-8859-1\nÀÁÂÃÄÅÿ\n±\n")
327349
}
350+
351+
guard let zeroFileURL = testBundle().url(forResource: "TestFileWithZeros", withExtension: "txt") else {
352+
XCTFail("Cant get URL for TestFileWithZeros.txt")
353+
return
354+
}
355+
guard let zeroString = try? String(contentsOf: zeroFileURL, encoding: .utf8) else {
356+
XCTFail("Cant create string from \(zeroFileURL)")
357+
return
358+
}
359+
XCTAssertEqual(zeroString, "Some\u{00}text\u{00}with\u{00}NUL\u{00}bytes\u{00}instead\u{00}of\u{00}spaces.\u{00}\n")
328360
}
329361

330362
func test_FromContentOfFileUsedEncodingIgnored() {

TestFoundation/TestStream.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ class TestStream : XCTestCase {
3636
let result: Int = dataStream.read(&buffer, maxLength: buffer.count)
3737
dataStream.close()
3838
XCTAssertEqual(.closed, dataStream.streamStatus)
39-
if(result > 0){
40-
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
39+
if(result > 0) {
40+
let output = NSString(bytes: &buffer, length: buffer.firstIndex(of: 0) ?? buffer.count, encoding: String.Encoding.utf8.rawValue)
4141
XCTAssertEqual(message, output!)
4242
}
4343
}
@@ -64,7 +64,7 @@ class TestStream : XCTestCase {
6464
XCTAssertEqual(.closed, urlStream.streamStatus)
6565
XCTAssertEqual(messageData.count, result)
6666
if(result > 0) {
67-
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
67+
let output = NSString(bytes: &buffer, length: buffer.firstIndex(of: 0) ?? buffer.count, encoding: String.Encoding.utf8.rawValue)
6868
XCTAssertEqual(message, output!)
6969
}
7070
}
@@ -90,8 +90,8 @@ class TestStream : XCTestCase {
9090
fileStream.close()
9191
XCTAssertEqual(.closed, fileStream.streamStatus)
9292
XCTAssertEqual(messageData.count, result)
93-
if(result > 0){
94-
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
93+
if(result > 0) {
94+
let output = NSString(bytes: &buffer, length: buffer.firstIndex(of: 0) ?? buffer.count, encoding: String.Encoding.utf8.rawValue)
9595
XCTAssertEqual(message, output!)
9696
}
9797
}

build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@
516516
'TestFoundation/Resources/NSKeyedUnarchiver-URLTest.plist',
517517
'TestFoundation/Resources/NSKeyedUnarchiver-UUIDTest.plist',
518518
'TestFoundation/Resources/NSKeyedUnarchiver-OrderedSetTest.plist',
519+
'TestFoundation/Resources/TestFileWithZeros.txt',
519520
])
520521

521522
# TODO: Probably this should be another 'product', but for now it's simply a phase

0 commit comments

Comments
 (0)