Skip to content

FileHandle: Refactor file descriptor precondition checks in API calls #1877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 3, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 28 additions & 22 deletions Foundation/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,41 @@ import CoreFoundation
open class FileHandle : NSObject, NSSecureCoding {
#if os(Windows)
private var _handle: HANDLE

@available(windows, unavailable, message: "Cannot perform non-owning handle to fd conversion")
open var fileDescriptor: Int32 {
NSUnsupported()
}

private func _checkFileHandle() {
precondition(_handle != INVALID_HANDLE_VALUE, "Invalid file handle")
}

#else
private var _fd: Int32
#endif
private var _closeOnDealloc: Bool

@available(windows, unavailable,
message: "cannot perform non-owning handle to fd conversion")
open var fileDescriptor: Int32 {
#if os(Windows)
return -1
#else
return _fd
#endif
}

private func _checkFileHandle() {
precondition(_fd >= 0, "Bad file descriptor")
}
#endif

private var _closeOnDealloc: Bool


open var readabilityHandler: ((FileHandle) -> Void)? = {
(FileHandle) -> Void in NSUnimplemented()
}

open var writeabilityHandler: ((FileHandle) -> Void)? = {
(FileHandle) -> Void in NSUnimplemented()
}

open var availableData: Data {
_checkFileHandle()
do {
let readResult = try _readDataOfLength(Int.max, untilEOF: false)
return readResult.toData()
Expand All @@ -44,10 +56,12 @@ open class FileHandle : NSObject, NSSecureCoding {
}

open func readDataToEndOfFile() -> Data {
_checkFileHandle()
return readData(ofLength: Int.max)
}

open func readData(ofLength length: Int) -> Data {
_checkFileHandle()
do {
let readResult = try _readDataOfLength(length, untilEOF: true)
return readResult.toData()
Expand All @@ -58,7 +72,6 @@ open class FileHandle : NSObject, NSSecureCoding {

internal func _readDataOfLength(_ length: Int, untilEOF: Bool, options: NSData.ReadingOptions = []) throws -> NSData.NSDataReadResult {
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")

if length == 0 && !untilEOF {
// Nothing requested, return empty response
Expand Down Expand Up @@ -131,7 +144,6 @@ open class FileHandle : NSObject, NSSecureCoding {
free(buffer)
}
#else
precondition(_fd >= 0, "Bad file descriptor")
if length == 0 && !untilEOF {
// Nothing requested, return empty response
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
Expand Down Expand Up @@ -202,8 +214,8 @@ open class FileHandle : NSObject, NSSecureCoding {
}

open func write(_ data: Data) {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
data.enumerateBytes() { (bytes, range, stop) in
do {
try NSData.write(toHandle: self._handle, path: nil,
Expand All @@ -214,7 +226,6 @@ open class FileHandle : NSObject, NSSecureCoding {
}
}
#else
guard _fd >= 0 else { return }
data.enumerateBytes() { (bytes, range, stop) in
do {
try NSData.write(toFileDescriptor: self._fd, path: nil, buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count)
Expand All @@ -228,52 +239,49 @@ open class FileHandle : NSObject, NSSecureCoding {
// TODO: Error handling.

open var offsetInFile: UInt64 {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
&liPointer, DWORD(FILE_CURRENT)) == FALSE {
fatalError("SetFilePointerEx failed")
}
return UInt64(liPointer.QuadPart)
#else
precondition(_fd >= 0, "Bad file descriptor")
return UInt64(lseek(_fd, 0, SEEK_CUR))
#endif
}

@discardableResult
open func seekToEndOfFile() -> UInt64 {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
&liPointer, DWORD(FILE_END)) == FALSE {
fatalError("SetFilePointerEx failed")
}
return UInt64(liPointer.QuadPart)
#else
precondition(_fd >= 0, "Bad file descriptor")
return UInt64(lseek(_fd, 0, SEEK_END))
#endif
}

open func seek(toFileOffset offset: UInt64) {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
nil, DWORD(FILE_BEGIN)) == FALSE {
fatalError("SetFilePointerEx failed")
}
#else
precondition(_fd >= 0, "Bad file descriptor")
lseek(_fd, off_t(offset), SEEK_SET)
#endif
}

open func truncateFile(atOffset offset: UInt64) {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
nil, DWORD(FILE_BEGIN)) == FALSE {
fatalError("SetFilePointerEx failed")
Expand All @@ -282,20 +290,18 @@ open class FileHandle : NSObject, NSSecureCoding {
fatalError("SetEndOfFile failed")
}
#else
precondition(_fd >= 0, "Bad file descriptor")
if lseek(_fd, off_t(offset), SEEK_SET) < 0 { fatalError("lseek() failed.") }
if ftruncate(_fd, off_t(offset)) < 0 { fatalError("ftruncate() failed.") }
#endif
}

open func synchronizeFile() {
_checkFileHandle()
#if os(Windows)
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
if FlushFileBuffers(_handle) == FALSE {
fatalError("FlushFileBuffers failed: \(GetLastError())")
}
#else
precondition(_fd >= 0, "Bad file descriptor")
fsync(_fd)
#endif
}
Expand Down