Skip to content

Commit 87709dc

Browse files
committed
Internal methods introduced to reduce the usage of FileHandle.readData() in order to avoid calling fstat, alloc and dealloc while reading a file
* New internal init method introduced to init with fileSystemRepresentation UnsafeMutablePointer<Int8> * New internal method _readBytes() introduced for continuous iteration of reading a file without allocating and deallocating buffer memory.
1 parent 7adc2d6 commit 87709dc

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

Foundation/FileHandle.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,14 @@ open class FileHandle : NSObject, NSSecureCoding {
342342
}
343343
#endif
344344
}
345+
346+
internal func _readBytes(into buffer: UnsafeMutablePointer<UInt8>, length: Int) throws -> Int {
347+
let amtRead = _read(_fd, buffer, length)
348+
if amtRead < 0 {
349+
throw _NSErrorWithErrno(errno, reading: true)
350+
}
351+
return amtRead
352+
}
345353

346354
internal func _writeBytes(buf: UnsafeRawPointer, length: Int) throws {
347355
#if os(Windows)
@@ -425,6 +433,15 @@ open class FileHandle : NSObject, NSSecureCoding {
425433
}
426434
}
427435
#endif
436+
437+
internal init?(fileSystemRepresentation: UnsafePointer<Int8>, flags: Int32, createMode: Int) {
438+
_fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode))
439+
_closeOnDealloc = true
440+
super.init()
441+
if _fd < 0 {
442+
return nil
443+
}
444+
}
428445

429446
deinit {
430447
// .close() tries to wait after operations in flight on the handle queue, if one exists, and then close. It does so by sending .sync { … } work to it.

Foundation/FileManager.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,25 +1720,29 @@ open class FileManager : NSObject {
17201720
#if os(Windows)
17211721
NSUnimplemented()
17221722
#else
1723-
guard let file1 = FileHandle(forReadingAtPath: String(cString: file1Rep)) else {
1724-
return false
1725-
}
1726-
1727-
defer { try? file1.close() }
1723+
guard let file1 = FileHandle(fileSystemRepresentation: file1Rep, flags: O_RDONLY, createMode: 0) else { return false }
1724+
guard let file2 = FileHandle(fileSystemRepresentation: file2Rep, flags: O_RDONLY, createMode: 0) else { return false }
17281725

1729-
guard let file2 = FileHandle(forReadingAtPath: String(cString: file2Rep)) else {
1730-
return false
1726+
var buffer1 = UnsafeMutablePointer<UInt8>.allocate(capacity: bufSize)
1727+
var buffer2 = UnsafeMutablePointer<UInt8>.allocate(capacity: bufSize)
1728+
defer {
1729+
buffer1.deallocate()
1730+
buffer2.deallocate()
17311731
}
17321732

1733-
defer { try? file2.close() }
1734-
17351733
var bytesLeft = size
17361734
while bytesLeft > 0 {
17371735
let bytesToRead = Int(min(Int64(bufSize), bytesLeft))
1738-
let file1Data = file1.readData(ofLength: bytesToRead)
1739-
let file2Data = file2.readData(ofLength: bytesToRead)
17401736

1741-
guard file1Data.count == bytesToRead, file2Data.count == bytesToRead, file1Data == file2Data else {
1737+
guard let file1BytesRead = try? file1._readBytes(into: buffer1, length: bytesToRead), file1BytesRead == bytesToRead else {
1738+
return false
1739+
}
1740+
1741+
guard let file2BytesRead = try? file2._readBytes(into: buffer2, length: bytesToRead), file2BytesRead == bytesToRead else {
1742+
return false
1743+
}
1744+
1745+
guard memcmp(buffer1, buffer2, bytesToRead) == 0 else {
17421746
return false
17431747
}
17441748

0 commit comments

Comments
 (0)