Skip to content

Commit 06b9fb1

Browse files
authored
Merge pull request #1848 from compnerd/handle-with-care
Foundation: port FileHandle to Windows
2 parents 99c2281 + 1c331c1 commit 06b9fb1

File tree

2 files changed

+225
-9
lines changed

2 files changed

+225
-9
lines changed

Foundation/FileHandle.swift

Lines changed: 221 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@
1010
import CoreFoundation
1111

1212
open class FileHandle : NSObject, NSSecureCoding {
13+
#if os(Windows)
14+
private var _handle: HANDLE
15+
#else
1316
private var _fd: Int32
17+
#endif
1418
private var _closeOnDealloc: Bool
1519

20+
@available(windows, unavailable,
21+
message: "cannot perform non-owning handle to fd conversion")
1622
open var fileDescriptor: Int32 {
23+
#if os(Windows)
24+
return -1
25+
#else
1726
return _fd
27+
#endif
1828
}
1929

2030
open var readabilityHandler: ((FileHandle) -> Void)? = {
@@ -47,6 +57,80 @@ open class FileHandle : NSObject, NSSecureCoding {
4757
}
4858

4959
internal func _readDataOfLength(_ length: Int, untilEOF: Bool, options: NSData.ReadingOptions = []) throws -> NSData.NSDataReadResult {
60+
#if os(Windows)
61+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
62+
63+
if length == 0 && !untilEOF {
64+
// Nothing requested, return empty response
65+
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
66+
}
67+
68+
var fiFileInfo: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
69+
if GetFileInformationByHandle(_handle, &fiFileInfo) == FALSE {
70+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(GetLastError()),
71+
userInfo: nil)
72+
}
73+
74+
if fiFileInfo.dwFileAttributes & DWORD(FILE_ATTRIBUTE_NORMAL) == FILE_ATTRIBUTE_NORMAL {
75+
if options.contains(.alwaysMapped) {
76+
let hMapping: HANDLE =
77+
CreateFileMappingA(_handle, nil, DWORD(PAGE_READONLY), 0, 0, nil)
78+
if hMapping == HANDLE(bitPattern: 0) {
79+
fatalError("CreateFileMappingA failed")
80+
}
81+
82+
let szFileSize: UInt64 = (UInt64(fiFileInfo.nFileSizeHigh) << 32) | UInt64(fiFileInfo.nFileSizeLow << 0)
83+
let szMapSize: UInt64 = Swift.min(UInt64(length), szFileSize)
84+
let pData: UnsafeMutableRawPointer =
85+
MapViewOfFile(hMapping, DWORD(FILE_MAP_READ), 0, 0, szMapSize)
86+
87+
return NSData.NSDataReadResult(bytes: pData, length: Int(szMapSize)) { buffer, length in
88+
if UnmapViewOfFile(buffer) == FALSE {
89+
fatalError("UnmapViewOfFile failed")
90+
}
91+
if CloseHandle(hMapping) == FALSE {
92+
fatalError("CloseHandle failed")
93+
}
94+
}
95+
}
96+
}
97+
98+
let blockSize: Int = 8 * 1024
99+
var allocated: Int = blockSize
100+
var buffer: UnsafeMutableRawPointer = malloc(allocated)!
101+
var total: Int = 0
102+
103+
while total < length {
104+
let remaining = length - total
105+
let BytesToRead: DWORD = DWORD(min(blockSize, remaining))
106+
107+
if (allocated - total) < BytesToRead {
108+
allocated *= 2
109+
buffer = _CFReallocf(buffer, allocated)
110+
}
111+
112+
var BytesRead: DWORD = 0
113+
if ReadFile(_handle, buffer.advanced(by: total), BytesToRead, &BytesRead, nil) == FALSE {
114+
free(buffer)
115+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(GetLastError()), userInfo: nil)
116+
}
117+
total += Int(BytesRead)
118+
if BytesRead == 0 || !untilEOF {
119+
break
120+
}
121+
}
122+
123+
if total == 0 {
124+
free(buffer)
125+
return NSData.NSDataReadResult(bytes: nil, length: 0, deallocator: nil)
126+
}
127+
128+
buffer = _CFReallocf(buffer, total)
129+
let data = buffer.bindMemory(to: UInt8.self, capacity: total)
130+
return NSData.NSDataReadResult(bytes: data, length: total) { buffer, length in
131+
free(buffer)
132+
}
133+
#else
50134
precondition(_fd >= 0, "Bad file descriptor")
51135
if length == 0 && !untilEOF {
52136
// Nothing requested, return empty response
@@ -114,9 +198,22 @@ open class FileHandle : NSObject, NSSecureCoding {
114198
return NSData.NSDataReadResult(bytes: bytePtr, length: total) { buffer, length in
115199
free(buffer)
116200
}
201+
#endif
117202
}
118-
203+
119204
open func write(_ data: Data) {
205+
#if os(Windows)
206+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
207+
data.enumerateBytes() { (bytes, range, stop) in
208+
do {
209+
try NSData.write(toHandle: self._handle, path: nil,
210+
buf: UnsafeRawPointer(bytes.baseAddress!),
211+
length: bytes.count)
212+
} catch {
213+
fatalError("Write failure")
214+
}
215+
}
216+
#else
120217
guard _fd >= 0 else { return }
121218
data.enumerateBytes() { (bytes, range, stop) in
122219
do {
@@ -125,44 +222,136 @@ open class FileHandle : NSObject, NSSecureCoding {
125222
fatalError("Write failure")
126223
}
127224
}
225+
#endif
128226
}
129-
227+
130228
// TODO: Error handling.
131-
229+
132230
open var offsetInFile: UInt64 {
231+
#if os(Windows)
232+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
233+
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
234+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
235+
&liPointer, DWORD(FILE_CURRENT)) == FALSE {
236+
fatalError("SetFilePointerEx failed")
237+
}
238+
return UInt64(liPointer.QuadPart)
239+
#else
133240
precondition(_fd >= 0, "Bad file descriptor")
134241
return UInt64(lseek(_fd, 0, SEEK_CUR))
242+
#endif
135243
}
136-
244+
137245
@discardableResult
138246
open func seekToEndOfFile() -> UInt64 {
247+
#if os(Windows)
248+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
249+
var liPointer: LARGE_INTEGER = LARGE_INTEGER(QuadPart: 0)
250+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: 0),
251+
&liPointer, DWORD(FILE_END)) == FALSE {
252+
fatalError("SetFilePointerEx failed")
253+
}
254+
return UInt64(liPointer.QuadPart)
255+
#else
139256
precondition(_fd >= 0, "Bad file descriptor")
140257
return UInt64(lseek(_fd, 0, SEEK_END))
258+
#endif
141259
}
142-
260+
143261
open func seek(toFileOffset offset: UInt64) {
262+
#if os(Windows)
263+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
264+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
265+
nil, DWORD(FILE_BEGIN)) == FALSE {
266+
fatalError("SetFilePointerEx failed")
267+
}
268+
#else
144269
precondition(_fd >= 0, "Bad file descriptor")
145270
lseek(_fd, off_t(offset), SEEK_SET)
271+
#endif
146272
}
147273

148274
open func truncateFile(atOffset offset: UInt64) {
275+
#if os(Windows)
276+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
277+
if SetFilePointerEx(_handle, LARGE_INTEGER(QuadPart: LONGLONG(offset)),
278+
nil, DWORD(FILE_BEGIN)) == FALSE {
279+
fatalError("SetFilePointerEx failed")
280+
}
281+
if SetEndOfFile(_handle) == FALSE {
282+
fatalError("SetEndOfFile failed")
283+
}
284+
#else
149285
precondition(_fd >= 0, "Bad file descriptor")
150286
if lseek(_fd, off_t(offset), SEEK_SET) < 0 { fatalError("lseek() failed.") }
151287
if ftruncate(_fd, off_t(offset)) < 0 { fatalError("ftruncate() failed.") }
288+
#endif
152289
}
153290

154291
open func synchronizeFile() {
292+
#if os(Windows)
293+
precondition(_handle != INVALID_HANDLE_VALUE, "invalid file handle")
294+
if FlushFileBuffers(_handle) == FALSE {
295+
fatalError("FlushFileBuffers failed: \(GetLastError())")
296+
}
297+
#else
155298
precondition(_fd >= 0, "Bad file descriptor")
156299
fsync(_fd)
300+
#endif
157301
}
158-
302+
159303
open func closeFile() {
304+
#if os(Windows)
305+
if _handle != INVALID_HANDLE_VALUE {
306+
if CloseHandle(_handle) == FALSE {
307+
fatalError("CloseHandle failed")
308+
}
309+
_handle = INVALID_HANDLE_VALUE
310+
}
311+
#else
160312
if _fd >= 0 {
161313
close(_fd)
162314
_fd = -1
163315
}
316+
#endif
317+
}
318+
319+
#if os(Windows)
320+
public init(handle: HANDLE, closeOnDealloc closeopt: Bool) {
321+
_handle = handle
322+
_closeOnDealloc = closeopt
164323
}
165324

325+
public init(fileDescriptor fd: Int32, closeOnDealloc closeopt: Bool) {
326+
if (closeopt) {
327+
var handle: HANDLE?
328+
if DuplicateHandle(GetCurrentProcess(),
329+
HANDLE(bitPattern: _get_osfhandle(fd))!,
330+
GetCurrentProcess(), &handle,
331+
DWORD(DUPLICATE_SAME_ACCESS), FALSE, 0) == FALSE {
332+
fatalError("DuplicateHandle() failed")
333+
}
334+
_close(fd)
335+
_handle = handle!
336+
_closeOnDealloc = true
337+
} else {
338+
_handle = HANDLE(bitPattern: _get_osfhandle(fd))!
339+
_closeOnDealloc = false
340+
}
341+
}
342+
343+
public convenience init(fileDescriptor fd: Int32) {
344+
self.init(handle: HANDLE(bitPattern: _get_osfhandle(fd))!,
345+
closeOnDealloc: false)
346+
}
347+
348+
internal convenience init?(path: String, flags: Int32, createMode: Int) {
349+
self.init(fileDescriptor: _CFOpenFileWithMode(path, flags,
350+
mode_t(createMode)),
351+
closeOnDealloc: true)
352+
if _handle == INVALID_HANDLE_VALUE { return nil }
353+
}
354+
#else
166355
public init(fileDescriptor fd: Int32, closeOnDealloc closeopt: Bool) {
167356
_fd = fd
168357
_closeOnDealloc = closeopt
@@ -180,14 +369,25 @@ open class FileHandle : NSObject, NSSecureCoding {
180369
return nil
181370
}
182371
}
183-
372+
#endif
373+
184374
deinit {
185-
if _fd >= 0 && _closeOnDealloc {
375+
guard _closeOnDealloc == true else { return }
376+
#if os(Windows)
377+
if _handle != INVALID_HANDLE_VALUE {
378+
if CloseHandle(_handle) == FALSE {
379+
fatalError("CloseHandle failed")
380+
}
381+
_handle = INVALID_HANDLE_VALUE
382+
}
383+
#else
384+
if _fd >= 0 {
186385
close(_fd)
187386
_fd = -1
188387
}
388+
#endif
189389
}
190-
390+
191391
public required init?(coder: NSCoder) {
192392
NSUnimplemented()
193393
}
@@ -362,6 +562,17 @@ open class Pipe: NSObject {
362562
public let fileHandleForWriting: FileHandle
363563

364564
public override init() {
565+
#if os(Windows)
566+
var hReadPipe: HANDLE?
567+
var hWritePipe: HANDLE?
568+
if CreatePipe(&hReadPipe, &hWritePipe, nil, 0) == FALSE {
569+
fatalError("CreatePipe failed")
570+
}
571+
self.fileHandleForReading = FileHandle(handle: hReadPipe!,
572+
closeOnDealloc: true)
573+
self.fileHandleForWriting = FileHandle(handle: hWritePipe!,
574+
closeOnDealloc: true)
575+
#else
365576
/// the `pipe` system call creates two `fd` in a malloc'ed area
366577
var fds = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
367578
defer {
@@ -383,6 +594,7 @@ open class Pipe: NSObject {
383594
default:
384595
fatalError("Error calling pipe(): \(errno)")
385596
}
597+
#endif
386598
super.init()
387599
}
388600
}

Foundation/NSSwiftRuntime.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import CoreFoundation
2222

2323
@_exported import Dispatch
2424

25+
#if os(Windows)
26+
import WinSDK
27+
#endif
28+
2529
#if os(Android) // shim required for bzero
2630
@_transparent func bzero(_ ptr: UnsafeMutableRawPointer, _ size: size_t) {
2731
memset(ptr, 0, size)

0 commit comments

Comments
 (0)