Skip to content

Commit ae9b7a1

Browse files
authored
Merge pull request #1166 from spevans/pr_sr_4993
2 parents 84ee193 + 6b2f1d0 commit ae9b7a1

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

Foundation/NSData.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
146146
public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? = nil) {
147147
self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator)
148148
}
149+
149150
public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions = []) throws {
150151
let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask)
151152
self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator)
@@ -380,7 +381,10 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
380381
}
381382

382383
let length = Int(info.st_size)
383-
384+
if length == 0 && (info.st_mode & S_IFMT == S_IFREG) {
385+
return try readZeroSizeFile(fd)
386+
}
387+
384388
if options.contains(.alwaysMapped) {
385389
let data = mmap(nil, length, PROT_READ, MAP_PRIVATE, fd, 0)
386390

@@ -414,6 +418,37 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
414418
free(buffer)
415419
}
416420
}
421+
422+
internal static func readZeroSizeFile(_ fd: Int32) throws -> NSDataReadResult {
423+
let blockSize = 1024 * 1024 // 1MB
424+
var data: UnsafeMutableRawPointer? = nil
425+
var bytesRead = 0
426+
var amt = 0
427+
428+
repeat {
429+
data = realloc(data, bytesRead + blockSize)
430+
amt = read(fd, data!.advanced(by: bytesRead), blockSize)
431+
432+
// Dont continue on EINTR or EAGAIN as the file position may not
433+
// have changed, see read(2).
434+
if amt < 0 {
435+
free(data!)
436+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil)
437+
}
438+
bytesRead += amt
439+
} while amt > 0
440+
441+
if bytesRead == 0 {
442+
free(data!)
443+
data = malloc(0)
444+
} else {
445+
data = realloc(data, bytesRead) // shrink down the allocated block.
446+
}
447+
448+
return NSDataReadResult(bytes: data!, length: bytesRead) { buffer, length in
449+
free(buffer)
450+
}
451+
}
417452

418453
internal func makeTemporaryFile(inDirectory dirPath: String) throws -> (Int32, String) {
419454
let template = dirPath._nsObject.appendingPathComponent("tmp.XXXXXX")

TestFoundation/TestNSData.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class TestNSData: XCTestCase {
3737
("test_base64Data_medium", test_base64Data_medium),
3838
("test_base64Data_small", test_base64Data_small),
3939
("test_openingNonExistentFile", test_openingNonExistentFile),
40+
("test_contentsOfFile", test_contentsOfFile),
41+
("test_contentsOfZeroFile", test_contentsOfZeroFile),
4042
("test_basicReadWrite", test_basicReadWrite),
4143
("test_bufferSizeCalculation", test_bufferSizeCalculation),
4244
// ("test_dataHash", test_dataHash), Disabled due to lack of brdiging in swift runtime -- infinite loops
@@ -908,6 +910,47 @@ extension TestNSData {
908910
XCTAssertTrue(didCatchError)
909911
}
910912

913+
func test_contentsOfFile() {
914+
let testDir = testBundle().resourcePath
915+
let filename = testDir!.appending("/NSStringTestData.txt")
916+
917+
let contents = NSData(contentsOfFile: filename)
918+
XCTAssertNotNil(contents)
919+
if let contents = contents {
920+
let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
921+
let str = String(bytesNoCopy: ptr, length: contents.length,
922+
encoding: .ascii, freeWhenDone: false)
923+
XCTAssertEqual(str, "swift-corelibs-foundation")
924+
}
925+
}
926+
927+
func test_contentsOfZeroFile() {
928+
#if os(Linux)
929+
guard FileManager.default.fileExists(atPath: "/proc/self") else {
930+
return
931+
}
932+
let contents = NSData(contentsOfFile: "/proc/self/cmdline")
933+
XCTAssertNotNil(contents)
934+
if let contents = contents {
935+
XCTAssertTrue(contents.length > 0)
936+
let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
937+
let str = String(bytesNoCopy: ptr, length: contents.length,
938+
encoding: .ascii, freeWhenDone: false)
939+
XCTAssertNotNil(str)
940+
if let str = str {
941+
XCTAssertTrue(str.hasSuffix("TestFoundation"))
942+
}
943+
}
944+
945+
do {
946+
let maps = try String(contentsOfFile: "/proc/self/maps", encoding: .utf8)
947+
XCTAssertTrue(maps.count > 0)
948+
} catch {
949+
XCTFail("Cannot read /proc/self/maps: \(String(describing: error))")
950+
}
951+
#endif
952+
}
953+
911954
func test_basicReadWrite() {
912955
let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("testfile")
913956
let count = 1 << 24

0 commit comments

Comments
 (0)