Skip to content

Commit 8412028

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. (cherry picked from commit 860956a)
1 parent f7e9cd8 commit 8412028

File tree

9 files changed

+64
-13
lines changed

9 files changed

+64
-13
lines changed

DarwinCompatibilityTests.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
B917D32620B0DE2000728EE0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B917D32520B0DE2000728EE0 /* main.swift */; };
1313
B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */; };
1414
B9C89ED21F6BF67C00087AF4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9C89ED11F6BF67C00087AF4 /* XCTest.framework */; };
15+
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 */; };
@@ -132,6 +134,8 @@
132134
B917D32520B0DE2000728EE0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = TestFoundation/xdgTestHelper/main.swift; sourceTree = "<group>"; };
133135
B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestNSNumberBridging.swift; path = TestFoundation/TestNSNumberBridging.swift; sourceTree = "<group>"; };
134136
B9C89EC11F6BF47D00087AF4 /* DarwinCompatibilityTests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = DarwinCompatibilityTests; sourceTree = BUILT_PRODUCTS_DIR; };
137+
B987C65D2093C8AF0026B50D /* TestImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestImports.swift; path = TestFoundation/TestImports.swift; sourceTree = "<group>"; };
138+
B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TestFileWithZeros.txt; path = TestFoundation/Resources/TestFileWithZeros.txt; sourceTree = "<group>"; };
135139
B9C89ED11F6BF67C00087AF4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
136140
B9C89ED71F6BF77E00087AF4 /* DarwinCompatibilityTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DarwinCompatibilityTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
137141
B9C89EDB1F6BF77E00087AF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -283,6 +287,10 @@
283287
B9C89FA91F6DCAE700087AF4 /* NSXMLDTDTestData.xml */,
284288
B9C89FB81F6DCAEB00087AF4 /* PropertyList-1.0.dtd */,
285289
B9C89FB91F6DCAEB00087AF4 /* Test.plist */,
290+
B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */,
291+
B917D32520B0DE2000728EE0 /* main.swift */,
292+
B987C65D2093C8AF0026B50D /* TestImports.swift */,
293+
B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */,
286294
B9C89F891F6D4D9D00087AF4 /* HTTPServer.swift */,
287295
B9C89F321F6BF89B00087AF4 /* TestAffineTransform.swift */,
288296
B9C89EFB1F6BF89200087AF4 /* TestBundle.swift */,
@@ -486,6 +494,7 @@
486494
B9C89FC11F6DCAEB00087AF4 /* Info.plist in Resources */,
487495
B9C89FC21F6DCAEB00087AF4 /* NSKeyedUnarchiver-ArrayTest.plist in Resources */,
488496
B9C89FC31F6DCAEB00087AF4 /* NSStringTestData.txt in Resources */,
497+
B98E33E02136AC120044EBE9 /* TestFileWithZeros.txt in Resources */,
489498
B9C89FC41F6DCAEB00087AF4 /* NSKeyedUnarchiver-OrderedSetTest.plist in Resources */,
490499
B9C89FC51F6DCAEB00087AF4 /* NSString-UTF32-BE-data.txt in Resources */,
491500
B9C89FC61F6DCAEB00087AF4 /* NSKeyedUnarchiver-URLTest.plist in Resources */,
@@ -515,6 +524,7 @@
515524
isa = PBXSourcesBuildPhase;
516525
buildActionMask = 2147483647;
517526
files = (
527+
B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */,
518528
B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */,
519529
B9C89F8B1F6D4DA900087AF4 /* HTTPServer.swift in Sources */,
520530
B9C89F8C1F6D4DA900087AF4 /* TestHTTPCookieStorage.swift in Sources */,

Foundation.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@
338338
B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */; };
339339
B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */; };
340340
B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */; };
341+
B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */; };
341342
B9974B961EDF4A22007F15B8 /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B8F1EDF4A22007F15B8 /* TransferState.swift */; };
342343
B9974B971EDF4A22007F15B8 /* MultiHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B901EDF4A22007F15B8 /* MultiHandle.swift */; };
343344
B9974B981EDF4A22007F15B8 /* libcurlHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */; };
@@ -821,6 +822,7 @@
821822
B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-BE-data.txt"; sourceTree = "<group>"; };
822823
B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-LE-data.txt"; sourceTree = "<group>"; };
823824
B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSLock.swift; sourceTree = "<group>"; };
825+
B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TestFileWithZeros.txt; sourceTree = "<group>"; };
824826
B9974B8F1EDF4A22007F15B8 /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferState.swift; sourceTree = "<group>"; };
825827
B9974B901EDF4A22007F15B8 /* MultiHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiHandle.swift; sourceTree = "<group>"; };
826828
B9974B911EDF4A22007F15B8 /* libcurlHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = libcurlHelpers.swift; sourceTree = "<group>"; };
@@ -1446,6 +1448,7 @@
14461448
EA66F6391BF1619600136161 /* Resources */ = {
14471449
isa = PBXGroup;
14481450
children = (
1451+
B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */,
14491452
D370696D1C394FBF00295652 /* NSKeyedUnarchiver-RangeTest.plist */,
14501453
D3E8D6D41C36AC0C00295652 /* NSKeyedUnarchiver-RectTest.plist */,
14511454
D3E8D6D21C36982700295652 /* NSKeyedUnarchiver-EdgeInsetsTest.plist */,
@@ -2143,6 +2146,7 @@
21432146
isa = PBXResourcesBuildPhase;
21442147
buildActionMask = 2147483647;
21452148
files = (
2149+
B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */,
21462150
B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */,
21472151
B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */,
21482152
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
@@ -1466,11 +1466,12 @@ extension TestNSData {
14661466
if let contents = contents {
14671467
XCTAssertTrue(contents.length > 0)
14681468
let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
1469-
let str = String(bytesNoCopy: ptr, length: contents.length,
1470-
encoding: .ascii, freeWhenDone: false)
1471-
XCTAssertNotNil(str)
1472-
if let str = str {
1469+
var zeroIdx = contents.range(of: Data([0]), in: NSMakeRange(0, contents.length)).location
1470+
if zeroIdx == NSNotFound { zeroIdx = contents.length }
1471+
if let str = String(bytesNoCopy: ptr, length: zeroIdx, encoding: .ascii, freeWhenDone: false) {
14731472
XCTAssertTrue(str.hasSuffix("TestFoundation"))
1473+
} else {
1474+
XCTFail("Cant create String")
14741475
}
14751476
}
14761477

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 {
@@ -303,6 +325,16 @@ class TestNSString: LoopbackServerTest {
303325
if let contents = contents {
304326
XCTAssertEqual(contents, "This file is encoded as ISO-8859-1\nÀÁÂÃÄÅÿ\n±\n")
305327
}
328+
329+
guard let zeroFileURL = testBundle().url(forResource: "TestFileWithZeros", withExtension: "txt") else {
330+
XCTFail("Cant get URL for TestFileWithZeros.txt")
331+
return
332+
}
333+
guard let zeroString = try? String(contentsOf: zeroFileURL, encoding: .utf8) else {
334+
XCTFail("Cant create string from \(zeroFileURL)")
335+
return
336+
}
337+
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")
306338
}
307339

308340
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(Stream.Status.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(Stream.Status.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(Stream.Status.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
@@ -515,6 +515,7 @@
515515
'TestFoundation/Resources/NSKeyedUnarchiver-URLTest.plist',
516516
'TestFoundation/Resources/NSKeyedUnarchiver-UUIDTest.plist',
517517
'TestFoundation/Resources/NSKeyedUnarchiver-OrderedSetTest.plist',
518+
'TestFoundation/Resources/TestFileWithZeros.txt',
518519
])
519520

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

0 commit comments

Comments
 (0)