Skip to content

Commit d47fba0

Browse files
committed
Foundation: port FileHandle to Windows
This ports the FileHandle interfaces to Windows.
1 parent a8a5e78 commit d47fba0

File tree

1 file changed

+227
-13
lines changed

1 file changed

+227
-13
lines changed

Foundation/FileHandle.swift

Lines changed: 227 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@
88
//
99

1010
import CoreFoundation
11+
#if os(Windows)
12+
import WinSDK
13+
#endif
1114

1215
open class FileHandle : NSObject, NSSecureCoding {
16+
#if os(Windows)
17+
private var _handle: HANDLE
18+
#else
1319
private var _fd: Int32
20+
#endif
1421
private var _closeOnDealloc: Bool
1522

23+
@available(windows, unavailable,
24+
message: "cannot perform non-owning handle to fd conversion")
1625
open var fileDescriptor: Int32 {
26+
#if os(Windows)
27+
return -1
28+
#else
1729
return _fd
30+
#endif
1831
}
1932

2033
open var readabilityHandler: ((FileHandle) -> Void)? = {
@@ -47,6 +60,79 @@ open class FileHandle : NSObject, NSSecureCoding {
4760
}
4861

4962
internal func _readDataOfLength(_ length: Int, untilEOF: Bool, options: NSData.ReadingOptions = []) throws -> NSData.NSDataReadResult {
63+
#if os(Windows)
64+
if _handle == INVALID_HANDLE_VALUE {
65+
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
66+
}
67+
if length == 0 && !untilEOF {
68+
// Nothing requested, return empty response
69+
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
70+
}
71+
72+
var fiFileInfo: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
73+
if GetFileInformationByHandle(_handle, &fiFileInfo) == FALSE {
74+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(GetLastError()),
75+
userInfo: nil)
76+
}
77+
78+
if fiFileInfo.dwFileAttributes & DWORD(FILE_ATTRIBUTE_NORMAL) == FILE_ATTRIBUTE_NORMAL {
79+
if options.contains(.alwaysMapped) {
80+
let hMapping: HANDLE =
81+
CreateFileMappingA(_handle, nil, DWORD(PAGE_READONLY), 0, 0, nil)
82+
if hMapping == HANDLE(bitPattern: 0) {
83+
fatalError("CrateFileMappingA failed")
84+
}
85+
86+
let szFileSize: UInt64 = (UInt64(fiFileInfo.nFileSizeHigh) << 32) | UInt64(fiFileInfo.nFileSizeLow << 0)
87+
let szMapSize: UInt64 = Swift.min(UInt64(length), szFileSize)
88+
let pData: UnsafeMutableRawPointer =
89+
MapViewOfFile(hMapping, DWORD(FILE_MAP_READ), 0, 0, szMapSize)
90+
91+
return NSData.NSDataReadResult(bytes: pData, length: Int(szMapSize)) { buffer, length in
92+
if UnmapViewOfFile(buffer) == FALSE {
93+
fatalError("UnmapViewOfFile failed")
94+
}
95+
if CloseHandle(hMapping) == FALSE {
96+
fatalError("CloseHandle failed")
97+
}
98+
}
99+
}
100+
}
101+
102+
let blockSize: Int = 8 * 1024
103+
var allocated: Int = blockSize
104+
var buffer: UnsafeMutableRawPointer = malloc(allocated)!
105+
var total: Int = 0
106+
107+
defer { free(buffer) }
108+
109+
while total < length {
110+
let remaining = length - total
111+
let BytesToRead: DWORD = DWORD(min(blockSize, remaining))
112+
113+
if (allocated - total) < BytesToRead {
114+
allocated *= 2
115+
buffer = _CFReallocf(buffer, allocated)
116+
}
117+
118+
var BytesRead: DWORD = 0
119+
if ReadFile(_handle, buffer.advanced(by: total), BytesToRead, &BytesRead, nil) == FALSE {
120+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(GetLastError()), userInfo: nil)
121+
}
122+
total += Int(BytesRead)
123+
if BytesRead == 0 || !untilEOF {
124+
break
125+
}
126+
}
127+
128+
if total == 0 {
129+
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
130+
}
131+
132+
buffer = _CFReallocf(buffer, total)
133+
let data = buffer.bindMemory(to: UInt8.self, capacity: total)
134+
return NSData.NSDataReadResult(bytes: data, length: total)
135+
#else
50136
precondition(_fd >= 0, "Bad file descriptor")
51137
if length == 0 && !untilEOF {
52138
// Nothing requested, return empty response
@@ -114,9 +200,22 @@ open class FileHandle : NSObject, NSSecureCoding {
114200
return NSData.NSDataReadResult(bytes: bytePtr, length: total) { buffer, length in
115201
free(buffer)
116202
}
203+
#endif
117204
}
118-
205+
119206
open func write(_ data: Data) {
207+
#if os(Windows)
208+
guard _handle != INVALID_HANDLE_VALUE else { return }
209+
data.enumerateBytes() { (bytes, range, stop) in
210+
do {
211+
try NSData.write(toHandle: self._handle, path: nil,
212+
buf: UnsafeRawPointer(bytes.baseAddress!),
213+
length: bytes.count)
214+
} catch {
215+
fatalError("Write failure")
216+
}
217+
}
218+
#else
120219
guard _fd >= 0 else { return }
121220
data.enumerateBytes() { (bytes, range, stop) in
122221
do {
@@ -125,44 +224,136 @@ open class FileHandle : NSObject, NSSecureCoding {
125224
fatalError("Write failure")
126225
}
127226
}
227+
#endif
128228
}
129-
229+
130230
// TODO: Error handling.
131-
231+
132232
open var offsetInFile: UInt64 {
233+
#if os(Windows)
234+
if _handle == INVALID_HANDLE_VALUE { return 0 }
235+
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
236+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
237+
&liPointer, DWORD(FILE_CURRENT)) == FALSE {
238+
fatalError("SetFilePointerEx failed")
239+
}
240+
return UInt64(liPointer.QuadPart)
241+
#else
133242
precondition(_fd >= 0, "Bad file descriptor")
134243
return UInt64(lseek(_fd, 0, SEEK_CUR))
244+
#endif
135245
}
136-
246+
137247
@discardableResult
138248
open func seekToEndOfFile() -> UInt64 {
249+
#if os(Windows)
250+
if _handle == INVALID_HANDLE_VALUE { return 0 }
251+
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
252+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
253+
&liPointer, DWORD(FILE_END)) == FALSE {
254+
fatalError("SetFilePointerEx failed")
255+
}
256+
return UInt64(liPointer.QuadPart)
257+
#else
139258
precondition(_fd >= 0, "Bad file descriptor")
140259
return UInt64(lseek(_fd, 0, SEEK_END))
260+
#endif
141261
}
142-
262+
143263
open func seek(toFileOffset offset: UInt64) {
264+
#if os(Windows)
265+
if _handle == INVALID_HANDLE_VALUE { return }
266+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
267+
nil, DWORD(FILE_BEGIN)) == FALSE {
268+
fatalError("SetFilePointerEx failed")
269+
}
270+
#else
144271
precondition(_fd >= 0, "Bad file descriptor")
145272
lseek(_fd, off_t(offset), SEEK_SET)
273+
#endif
146274
}
147275

148276
open func truncateFile(atOffset offset: UInt64) {
277+
#if os(Windows)
278+
if _handle == INVALID_HANDLE_VALUE { return }
279+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
280+
nil, DWORD(FILE_BEGIN)) == FALSE {
281+
fatalError("SetFilePointerEx failed")
282+
}
283+
if SetEndOfFile(_handle) == FALSE {
284+
fatalError("SetEndOfFile failed")
285+
}
286+
#else
149287
precondition(_fd >= 0, "Bad file descriptor")
150288
if lseek(_fd, off_t(offset), SEEK_SET) < 0 { fatalError("lseek() failed.") }
151289
if ftruncate(_fd, off_t(offset)) < 0 { fatalError("ftruncate() failed.") }
290+
#endif
152291
}
153292

154293
open func synchronizeFile() {
294+
#if os(Windows)
295+
if _handle == INVALID_HANDLE_VALUE { return }
296+
if FlushFileBuffers(_handle) == FALSE {
297+
fatalError("FlushFileBuffers failed: \(GetLastError())")
298+
}
299+
#else
155300
precondition(_fd >= 0, "Bad file descriptor")
156301
fsync(_fd)
302+
#endif
157303
}
158-
304+
159305
open func closeFile() {
306+
#if os(Windows)
307+
if _handle != INVALID_HANDLE_VALUE {
308+
if CloseHandle(_handle) == FALSE {
309+
fatalError("CloseHandle failed")
310+
}
311+
_handle = INVALID_HANDLE_VALUE
312+
}
313+
#else
160314
if _fd >= 0 {
161315
close(_fd)
162316
_fd = -1
163317
}
318+
#endif
319+
}
320+
321+
#if os(Windows)
322+
public init(handle: HANDLE, closeOnDealloc closeopt: Bool) {
323+
_handle = handle
324+
_closeOnDealloc = closeopt
164325
}
165326

327+
public init(fileDescriptor fd: Int32, closeOnDealloc closeopt: Bool) {
328+
if (closeopt) {
329+
var handle: HANDLE?
330+
if DuplicateHandle(GetCurrentProcess(),
331+
HANDLE(bitPattern: _get_osfhandle(fd))!,
332+
GetCurrentProcess(), &handle,
333+
DWORD(DUPLICATE_SAME_ACCESS), FALSE, 0) == FALSE {
334+
fatalError("DuplicateHandle() failed")
335+
}
336+
_close(fd)
337+
_handle = handle!
338+
_closeOnDealloc = true
339+
} else {
340+
_handle = HANDLE(bitPattern: _get_osfhandle(fd))!
341+
_closeOnDealloc = false
342+
}
343+
}
344+
345+
public convenience init(fileDescriptor fd: Int32) {
346+
self.init(handle: HANDLE(bitPattern: _get_osfhandle(fd))!,
347+
closeOnDealloc: false)
348+
}
349+
350+
internal convenience init?(path: String, flags: Int32, createMode: Int) {
351+
self.init(fileDescriptor: _CFOpenFileWithMode(path, flags,
352+
mode_t(createMode)),
353+
closeOnDealloc: true)
354+
if _handle == INVALID_HANDLE_VALUE { return nil }
355+
}
356+
#else
166357
public init(fileDescriptor fd: Int32, closeOnDealloc closeopt: Bool) {
167358
_fd = fd
168359
_closeOnDealloc = closeopt
@@ -180,14 +371,25 @@ open class FileHandle : NSObject, NSSecureCoding {
180371
return nil
181372
}
182373
}
183-
374+
#endif
375+
184376
deinit {
185-
if _fd >= 0 && _closeOnDealloc {
377+
guard _closeOnDealloc == true else { return }
378+
#if os(Windows)
379+
if _handle != INVALID_HANDLE_VALUE {
380+
if CloseHandle(_handle) == FALSE {
381+
fatalError("CloseHandle failed")
382+
}
383+
_handle = INVALID_HANDLE_VALUE
384+
}
385+
#else
386+
if _fd >= 0 {
186387
close(_fd)
187388
_fd = -1
188389
}
390+
#endif
189391
}
190-
392+
191393
public required init?(coder: NSCoder) {
192394
NSUnimplemented()
193395
}
@@ -324,31 +526,31 @@ public let NSFileHandleNotificationDataItem: String = "NSFileHandleNotificationD
324526
public let NSFileHandleNotificationFileHandleItem: String = "NSFileHandleNotificationFileHandleItem"
325527

326528
extension FileHandle {
327-
open func readInBackgroundAndNotify(forModes modes: [RunLoopMode]?) {
529+
open func readInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) {
328530
NSUnimplemented()
329531
}
330532

331533
open func readInBackgroundAndNotify() {
332534
NSUnimplemented()
333535
}
334536

335-
open func readToEndOfFileInBackgroundAndNotify(forModes modes: [RunLoopMode]?) {
537+
open func readToEndOfFileInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) {
336538
NSUnimplemented()
337539
}
338540

339541
open func readToEndOfFileInBackgroundAndNotify() {
340542
NSUnimplemented()
341543
}
342544

343-
open func acceptConnectionInBackgroundAndNotify(forModes modes: [RunLoopMode]?) {
545+
open func acceptConnectionInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) {
344546
NSUnimplemented()
345547
}
346548

347549
open func acceptConnectionInBackgroundAndNotify() {
348550
NSUnimplemented()
349551
}
350552

351-
open func waitForDataInBackgroundAndNotify(forModes modes: [RunLoopMode]?) {
553+
open func waitForDataInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) {
352554
NSUnimplemented()
353555
}
354556

@@ -362,6 +564,17 @@ open class Pipe: NSObject {
362564
public let fileHandleForWriting: FileHandle
363565

364566
public override init() {
567+
#if os(Windows)
568+
var hReadPipe: HANDLE?
569+
var hWritePipe: HANDLE?
570+
if CreatePipe(&hReadPipe, &hWritePipe, nil, 0) == FALSE {
571+
fatalError("CreatePipe failed")
572+
}
573+
self.fileHandleForReading = FileHandle(handle: hReadPipe!,
574+
closeOnDealloc: true)
575+
self.fileHandleForWriting = FileHandle(handle: hWritePipe!,
576+
closeOnDealloc: true)
577+
#else
365578
/// the `pipe` system call creates two `fd` in a malloc'ed area
366579
var fds = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
367580
defer {
@@ -383,6 +596,7 @@ open class Pipe: NSObject {
383596
default:
384597
fatalError("Error calling pipe(): \(errno)")
385598
}
599+
#endif
386600
super.init()
387601
}
388602
}

0 commit comments

Comments
 (0)