From 171c7570d7d8a41db96c80a6cf033ec518bfcf4b Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Tue, 16 Jan 2018 12:53:27 -0800 Subject: [PATCH] A few cleanups and one bug fix: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `fd_set` API is now much nicer and will compile about as close to the C implementation as we can get— especially on release builds, with compiler inlining. - Socket structure's `readBuffer` now initializes all its bytes to zero, not just the first. - More idiomatic/concise return expression in `Socket.wait(for:timeout:waitForever:)` (don't build an output array manually, we have `filter(_:)` for that). - Various non-Linux code to set the `SO_NOSIGPIPE` option has been tucked into a private function now, still containing the compilation conditional. It's a no-op on Linux, and will be inlined by the compiler (since it's private) and the calling code looks much more straightforward. - BUGFIX: calls to `gai_strerror(_:)` take the result of the `getaddrinfo()` call as a parameter, NOT `errno`. --- Sources/Socket/Socket.swift | 89 +++---- Sources/Socket/SocketUtils.swift | 423 ++++--------------------------- 2 files changed, 89 insertions(+), 423 deletions(-) diff --git a/Sources/Socket/Socket.swift b/Sources/Socket/Socket.swift index acc7eac..ad346ea 100644 --- a/Sources/Socket/Socket.swift +++ b/Sources/Socket/Socket.swift @@ -825,7 +825,7 @@ public class Socket: SocketReader, SocketWriter { readBuffer.deinitialize() readBuffer.deallocate(capacity: oldValue) readBuffer = UnsafeMutablePointer.allocate(capacity: readBufferSize) - readBuffer.initialize(to:0) + readBuffer.initialize(to: 0, count: readBufferSize) } } } @@ -1129,7 +1129,7 @@ public class Socket: SocketReader, SocketWriter { // Setup the array of readfds... var readfds = fd_set() - FD.ZERO(set: &readfds) + readfds.zero() var highSocketfd: Int32 = 0 for socket in sockets { @@ -1137,7 +1137,7 @@ public class Socket: SocketReader, SocketWriter { if socket.socketfd > highSocketfd { highSocketfd = socket.socketfd } - FD.SET(fd: socket.socketfd, set: &readfds) + readfds.set(socket.socketfd) } // Issue the select... @@ -1160,15 +1160,7 @@ public class Socket: SocketReader, SocketWriter { } // Build the array of returned sockets... - var dataSockets = [Socket]() - for socket in sockets { - - if FD.ISSET(fd: socket.socketfd, set: &readfds) { - dataSockets.append(socket) - } - } - - return dataSockets + return sockets.filter { readfds.isSet($0.socketfd) } } /// @@ -1237,7 +1229,7 @@ public class Socket: SocketReader, SocketWriter { private init(family: ProtocolFamily, type: SocketType, proto: SocketProtocol) throws { // Initialize the read buffer... - self.readBuffer.initialize(to: 0) + self.readBuffer.initialize(to: 0, count: readBufferSize) // If the family is .unix, set the protocol to .unix as well... var sockProto = proto @@ -1259,15 +1251,7 @@ public class Socket: SocketReader, SocketWriter { throw Error(code: Socket.SOCKET_ERR_UNABLE_TO_CREATE_SOCKET, reason: self.lastError()) } - #if !os(Linux) - // Set the socket to ignore SIGPIPE to avoid dying on interrupted connections... - // Note: Linux does not support the SO_NOSIGPIPE option. Instead, we use the - // MSG_NOSIGNAL flags passed to send. See the write() functions below. - var on: Int32 = 1 - if setsockopt(self.socketfd, SOL_SOCKET, SO_NOSIGPIPE, &on, socklen_t(MemoryLayout.size)) < 0 { - throw Error(code: Socket.SOCKET_ERR_SETSOCKOPT_FAILED, reason: self.lastError()) - } - #endif + try disableSIGPIPE(on: self.socketfd) // Create the signature... try self.signature = Signature( @@ -1290,7 +1274,7 @@ public class Socket: SocketReader, SocketWriter { self.isConnected = true self.isListening = false - self.readBuffer.initialize(to: 0) + self.readBuffer.initialize(to: 0, count: readBufferSize) self.socketfd = fd @@ -1299,15 +1283,9 @@ public class Socket: SocketReader, SocketWriter { let type = Int32(SOCK_STREAM.rawValue) #else let type = SOCK_STREAM - - // Set the socket to ignore SIGPIPE to avoid dying on interrupted connections... - // Note: Linux does not support the SO_NOSIGPIPE option. Instead, we use the - // MSG_NOSIGNAL flags passed to send. See the write() functions below. - var on: Int32 = 1 - if setsockopt(self.socketfd, SOL_SOCKET, SO_NOSIGPIPE, &on, socklen_t(MemoryLayout.size)) < 0 { - throw Error(code: Socket.SOCKET_ERR_SETSOCKOPT_FAILED, reason: self.lastError()) - } #endif + + try disableSIGPIPE(on: self.socketfd) if path != nil { @@ -1664,7 +1642,7 @@ public class Socket: SocketReader, SocketWriter { if status == EAI_SYSTEM { errorString = String(validatingUTF8: strerror(errno)) ?? "Unknown error code." } else { - errorString = String(validatingUTF8: gai_strerror(errno)) ?? "Unknown error code." + errorString = String(validatingUTF8: gai_strerror(status)) ?? "Unknown error code." } throw Error(code: Socket.SOCKET_ERR_GETADDRINFO_FAILED, reason: errorString) } @@ -1727,8 +1705,8 @@ public class Socket: SocketReader, SocketWriter { // Set up for the select call... var writefds = fd_set() - FD.ZERO(set: &writefds) - FD.SET(fd: socketDescriptor!, set: &writefds) + writefds.zero() + writefds.set(socketDescriptor!) var timer = timeval() @@ -1757,7 +1735,7 @@ public class Socket: SocketReader, SocketWriter { // If the socket is writable, we're probably connected, but check anyway to be sure... // Otherwise, we've timed out waiting to connect. - if FD.ISSET(fd: socketDescriptor!, set: &writefds) { + if writefds.isSet(socketDescriptor!) { // Check the socket... var result: Int = 0 @@ -1831,15 +1809,7 @@ public class Socket: SocketReader, SocketWriter { throw Error(code: Socket.SOCKET_ERR_WRONG_PROTOCOL, reason: "Unable to determine connected socket protocol family.") } - #if !os(Linux) - // Set the new socket to ignore SIGPIPE to avoid dying on interrupted connections... - // Note: Linux does not support the SO_NOSIGPIPE option. Instead, we use the - // MSG_NOSIGNAL flags passed to send. See the write() functions below. - var on: Int32 = 1 - if setsockopt(self.socketfd, SOL_SOCKET, SO_NOSIGPIPE, &on, socklen_t(MemoryLayout.size)) < 0 { - throw Error(code: Socket.SOCKET_ERR_SETSOCKOPT_FAILED, reason: self.lastError()) - } - #endif + try disableSIGPIPE(on: self.socketfd) try self.signature = Signature( protocolFamily: Int32(info!.pointee.ai_family), @@ -3175,12 +3145,12 @@ public class Socket: SocketReader, SocketWriter { // Create a read and write file descriptor set for this socket... var readfds = fd_set() - FD.ZERO(set: &readfds) - FD.SET(fd: self.socketfd, set: &readfds) + readfds.zero() + readfds.set(self.socketfd) var writefds = fd_set() - FD.ZERO(set: &writefds) - FD.SET(fd: self.socketfd, set: &writefds) + writefds.zero() + writefds.set(self.socketfd) // Do the wait... var count: Int32 = 0 @@ -3226,7 +3196,7 @@ public class Socket: SocketReader, SocketWriter { } // Return a tuple containing whether or not this socket is readable and/or writable... - return (FD.ISSET(fd: self.socketfd, set: &readfds), FD.ISSET(fd: self.socketfd, set: &writefds)) + return (readfds.isSet(self.socketfd), writefds.isSet(self.socketfd)) } /// @@ -3433,7 +3403,7 @@ public class Socket: SocketReader, SocketWriter { private func readDataIntoStorage() throws -> Int { // Clear the buffer... - self.readBuffer.initialize(to: 0x0) + self.readBuffer.initialize(to: 0x0, count: readBufferSize) var recvFlags: Int32 = 0 if self.readStorage.length > 0 { @@ -3545,7 +3515,7 @@ public class Socket: SocketReader, SocketWriter { private func readDatagramIntoStorage() throws -> (bytesRead: Int, fromAddress: Address?) { // Clear the buffer... - self.readBuffer.initialize(to: 0x0) + self.readBuffer.initialize(to: 0x0, count: readBufferSize) var recvFlags: Int32 = 0 if self.readStorage.length > 0 { recvFlags |= Int32(MSG_DONTWAIT) @@ -3641,5 +3611,22 @@ public class Socket: SocketReader, SocketWriter { return String(validatingUTF8: strerror(errno)) ?? "Error: \(errno)" } + + /// + /// Private function to set no-SIGPIPE on a socket. No-op on Linux. + /// + /// - Parameter fd: The socket file descriptor upon which to act. + /// + private func disableSIGPIPE(on fd: Int32) throws { + #if !os(Linux) + // Set the new socket to ignore SIGPIPE to avoid dying on interrupted connections... + // Note: Linux does not support the SO_NOSIGPIPE option. Instead, we use the + // MSG_NOSIGNAL flags passed to send. See the write() functions below. + var on: Int32 = 1 + if setsockopt(self.socketfd, SOL_SOCKET, SO_NOSIGPIPE, &on, socklen_t(MemoryLayout.size)) < 0 { + throw Error(code: Socket.SOCKET_ERR_SETSOCKOPT_FAILED, reason: self.lastError()) + } + #endif + } } diff --git a/Sources/Socket/SocketUtils.swift b/Sources/Socket/SocketUtils.swift index 23f9a2d..3c4a552 100644 --- a/Sources/Socket/SocketUtils.swift +++ b/Sources/Socket/SocketUtils.swift @@ -112,387 +112,66 @@ extension Socket.Address { } } -#if arch(arm) && os(Linux) +#if os(Linux) - public struct FD { - - /// Replacement for FD_ZERO macro - - public static func ZERO(set: inout fd_set) { - set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - } - - - /// Replacement for FD_SET macro - - public static func SET(fd: Int32, set: inout fd_set) { - let intOffset = Int32(fd / 32) - let bitOffset: Int32 = Int32(fd % 32) - let mask: Int32 = 1 << bitOffset - switch intOffset { - case 0: set.__fds_bits.0 = set.__fds_bits.0 | mask - case 1: set.__fds_bits.1 = set.__fds_bits.1 | mask - case 2: set.__fds_bits.2 = set.__fds_bits.2 | mask - case 3: set.__fds_bits.3 = set.__fds_bits.3 | mask - case 4: set.__fds_bits.4 = set.__fds_bits.4 | mask - case 5: set.__fds_bits.5 = set.__fds_bits.5 | mask - case 6: set.__fds_bits.6 = set.__fds_bits.6 | mask - case 7: set.__fds_bits.7 = set.__fds_bits.7 | mask - case 8: set.__fds_bits.8 = set.__fds_bits.8 | mask - case 9: set.__fds_bits.9 = set.__fds_bits.9 | mask - case 10: set.__fds_bits.10 = set.__fds_bits.10 | mask - case 11: set.__fds_bits.11 = set.__fds_bits.11 | mask - case 12: set.__fds_bits.12 = set.__fds_bits.12 | mask - case 13: set.__fds_bits.13 = set.__fds_bits.13 | mask - case 14: set.__fds_bits.14 = set.__fds_bits.14 | mask - case 15: set.__fds_bits.15 = set.__fds_bits.15 | mask - case 16: set.__fds_bits.16 = set.__fds_bits.16 | mask - case 17: set.__fds_bits.17 = set.__fds_bits.17 | mask - case 18: set.__fds_bits.18 = set.__fds_bits.18 | mask - case 19: set.__fds_bits.19 = set.__fds_bits.19 | mask - case 20: set.__fds_bits.20 = set.__fds_bits.20 | mask - case 21: set.__fds_bits.21 = set.__fds_bits.21 | mask - case 22: set.__fds_bits.22 = set.__fds_bits.22 | mask - case 23: set.__fds_bits.23 = set.__fds_bits.23 | mask - case 24: set.__fds_bits.24 = set.__fds_bits.24 | mask - case 25: set.__fds_bits.25 = set.__fds_bits.25 | mask - case 26: set.__fds_bits.26 = set.__fds_bits.26 | mask - case 27: set.__fds_bits.27 = set.__fds_bits.27 | mask - case 28: set.__fds_bits.28 = set.__fds_bits.28 | mask - case 29: set.__fds_bits.29 = set.__fds_bits.29 | mask - case 30: set.__fds_bits.20 = set.__fds_bits.30 | mask - case 31: set.__fds_bits.31 = set.__fds_bits.31 | mask - default: break - } - } - - - /// Replacement for FD_CLR macro - - public static func CLR(fd: Int32, set: inout fd_set) { - let intOffset = Int32(fd / 32) - let bitOffset: Int32 = Int32(fd % 32) - let mask: Int32 = ~(1 << bitOffset) - switch intOffset { - case 0: set.__fds_bits.0 = set.__fds_bits.0 & mask - case 1: set.__fds_bits.1 = set.__fds_bits.1 & mask - case 2: set.__fds_bits.2 = set.__fds_bits.2 & mask - case 3: set.__fds_bits.3 = set.__fds_bits.3 & mask - case 4: set.__fds_bits.4 = set.__fds_bits.4 & mask - case 5: set.__fds_bits.5 = set.__fds_bits.5 & mask - case 6: set.__fds_bits.6 = set.__fds_bits.6 & mask - case 7: set.__fds_bits.7 = set.__fds_bits.7 & mask - case 8: set.__fds_bits.8 = set.__fds_bits.8 & mask - case 9: set.__fds_bits.9 = set.__fds_bits.9 & mask - case 10: set.__fds_bits.10 = set.__fds_bits.10 & mask - case 11: set.__fds_bits.11 = set.__fds_bits.11 & mask - case 12: set.__fds_bits.12 = set.__fds_bits.12 & mask - case 13: set.__fds_bits.13 = set.__fds_bits.13 & mask - case 14: set.__fds_bits.14 = set.__fds_bits.14 & mask - case 15: set.__fds_bits.15 = set.__fds_bits.15 & mask - case 16: set.__fds_bits.16 = set.__fds_bits.16 & mask - case 17: set.__fds_bits.17 = set.__fds_bits.17 & mask - case 18: set.__fds_bits.18 = set.__fds_bits.18 & mask - case 19: set.__fds_bits.19 = set.__fds_bits.19 & mask - case 20: set.__fds_bits.20 = set.__fds_bits.20 & mask - case 21: set.__fds_bits.21 = set.__fds_bits.21 & mask - case 22: set.__fds_bits.22 = set.__fds_bits.22 & mask - case 23: set.__fds_bits.23 = set.__fds_bits.23 & mask - case 24: set.__fds_bits.24 = set.__fds_bits.24 & mask - case 25: set.__fds_bits.25 = set.__fds_bits.25 & mask - case 26: set.__fds_bits.26 = set.__fds_bits.26 & mask - case 27: set.__fds_bits.27 = set.__fds_bits.27 & mask - case 28: set.__fds_bits.28 = set.__fds_bits.28 & mask - case 29: set.__fds_bits.29 = set.__fds_bits.29 & mask - case 30: set.__fds_bits.20 = set.__fds_bits.30 & mask - case 31: set.__fds_bits.31 = set.__fds_bits.31 & mask - default: break - } - } - - - /// Replacement for FD_ISSET macro - - public static func ISSET(fd: Int32, set: inout fd_set) -> Bool { - let intOffset = Int32(fd / 32) - let bitOffset = Int32(fd % 32) - let mask: Int32 = 1 << bitOffset - switch intOffset { - case 0: return set.__fds_bits.0 & mask != 0 - case 1: return set.__fds_bits.1 & mask != 0 - case 2: return set.__fds_bits.2 & mask != 0 - case 3: return set.__fds_bits.3 & mask != 0 - case 4: return set.__fds_bits.4 & mask != 0 - case 5: return set.__fds_bits.5 & mask != 0 - case 6: return set.__fds_bits.6 & mask != 0 - case 7: return set.__fds_bits.7 & mask != 0 - case 8: return set.__fds_bits.8 & mask != 0 - case 9: return set.__fds_bits.9 & mask != 0 - case 10: return set.__fds_bits.10 & mask != 0 - case 11: return set.__fds_bits.11 & mask != 0 - case 12: return set.__fds_bits.12 & mask != 0 - case 13: return set.__fds_bits.13 & mask != 0 - case 14: return set.__fds_bits.14 & mask != 0 - case 15: return set.__fds_bits.15 & mask != 0 - case 16: return set.__fds_bits.16 & mask != 0 - case 17: return set.__fds_bits.17 & mask != 0 - case 18: return set.__fds_bits.18 & mask != 0 - case 19: return set.__fds_bits.19 & mask != 0 - case 20: return set.__fds_bits.20 & mask != 0 - case 21: return set.__fds_bits.21 & mask != 0 - case 22: return set.__fds_bits.22 & mask != 0 - case 23: return set.__fds_bits.23 & mask != 0 - case 24: return set.__fds_bits.24 & mask != 0 - case 25: return set.__fds_bits.25 & mask != 0 - case 26: return set.__fds_bits.26 & mask != 0 - case 27: return set.__fds_bits.27 & mask != 0 - case 28: return set.__fds_bits.28 & mask != 0 - case 29: return set.__fds_bits.29 & mask != 0 - case 30: return set.__fds_bits.30 & mask != 0 - case 31: return set.__fds_bits.31 & mask != 0 - default: return false - } - - } - } + #if arch(arm) + let __fd_set_count = 16 + #else + let __fd_set_count = 32 + #endif -#elseif os(Linux) - - public struct FD { - - /// Replacement for FD_ZERO macro - - public static func ZERO(set: inout fd_set) { - set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - } - - - /// Replacement for FD_SET macro - - public static func SET(fd: Int32, set: inout fd_set) { - let intOffset = Int(fd / 16) - let bitOffset: Int = Int(fd % 16) - let mask: Int = 1 << bitOffset - switch intOffset { - case 0: set.__fds_bits.0 = set.__fds_bits.0 | mask - case 1: set.__fds_bits.1 = set.__fds_bits.1 | mask - case 2: set.__fds_bits.2 = set.__fds_bits.2 | mask - case 3: set.__fds_bits.3 = set.__fds_bits.3 | mask - case 4: set.__fds_bits.4 = set.__fds_bits.4 | mask - case 5: set.__fds_bits.5 = set.__fds_bits.5 | mask - case 6: set.__fds_bits.6 = set.__fds_bits.6 | mask - case 7: set.__fds_bits.7 = set.__fds_bits.7 | mask - case 8: set.__fds_bits.8 = set.__fds_bits.8 | mask - case 9: set.__fds_bits.9 = set.__fds_bits.9 | mask - case 10: set.__fds_bits.10 = set.__fds_bits.10 | mask - case 11: set.__fds_bits.11 = set.__fds_bits.11 | mask - case 12: set.__fds_bits.12 = set.__fds_bits.12 | mask - case 13: set.__fds_bits.13 = set.__fds_bits.13 | mask - case 14: set.__fds_bits.14 = set.__fds_bits.14 | mask - case 15: set.__fds_bits.15 = set.__fds_bits.15 | mask - default: break - } - } - - - /// Replacement for FD_CLR macro - - public static func CLR(fd: Int32, set: inout fd_set) { - let intOffset = Int(fd / 16) - let bitOffset: Int = Int(fd % 16) - let mask: Int = ~(1 << bitOffset) - switch intOffset { - case 0: set.__fds_bits.0 = set.__fds_bits.0 & mask - case 1: set.__fds_bits.1 = set.__fds_bits.1 & mask - case 2: set.__fds_bits.2 = set.__fds_bits.2 & mask - case 3: set.__fds_bits.3 = set.__fds_bits.3 & mask - case 4: set.__fds_bits.4 = set.__fds_bits.4 & mask - case 5: set.__fds_bits.5 = set.__fds_bits.5 & mask - case 6: set.__fds_bits.6 = set.__fds_bits.6 & mask - case 7: set.__fds_bits.7 = set.__fds_bits.7 & mask - case 8: set.__fds_bits.8 = set.__fds_bits.8 & mask - case 9: set.__fds_bits.9 = set.__fds_bits.9 & mask - case 10: set.__fds_bits.10 = set.__fds_bits.10 & mask - case 11: set.__fds_bits.11 = set.__fds_bits.11 & mask - case 12: set.__fds_bits.12 = set.__fds_bits.12 & mask - case 13: set.__fds_bits.13 = set.__fds_bits.13 & mask - case 14: set.__fds_bits.14 = set.__fds_bits.14 & mask - case 15: set.__fds_bits.15 = set.__fds_bits.15 & mask - default: break - } - } - - - /// Replacement for FD_ISSET macro - - public static func ISSET(fd: Int32, set: inout fd_set) -> Bool { - let intOffset = Int(fd / 16) - let bitOffset = Int(fd % 16) - let mask: Int = 1 << bitOffset - switch intOffset { - case 0: return set.__fds_bits.0 & mask != 0 - case 1: return set.__fds_bits.1 & mask != 0 - case 2: return set.__fds_bits.2 & mask != 0 - case 3: return set.__fds_bits.3 & mask != 0 - case 4: return set.__fds_bits.4 & mask != 0 - case 5: return set.__fds_bits.5 & mask != 0 - case 6: return set.__fds_bits.6 & mask != 0 - case 7: return set.__fds_bits.7 & mask != 0 - case 8: return set.__fds_bits.8 & mask != 0 - case 9: return set.__fds_bits.9 & mask != 0 - case 10: return set.__fds_bits.10 & mask != 0 - case 11: return set.__fds_bits.11 & mask != 0 - case 12: return set.__fds_bits.12 & mask != 0 - case 13: return set.__fds_bits.13 & mask != 0 - case 14: return set.__fds_bits.14 & mask != 0 - case 15: return set.__fds_bits.15 & mask != 0 - default: return false + extension fd_set + { + @inline(__always) + mutating func withCArrayAccess(block: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try withUnsafeMutablePointer(to: &__fds_bits) { + try block(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) } } } -#else +#else // not Linux on ARM - public struct FD { - - /// Replacement for FD_ZERO macro - - public static func ZERO(set: inout fd_set) { - set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - } - - - /// Replacement for FD_SET macro - - public static func SET(fd: Int32, set: inout fd_set) { - let intOffset = Int32(fd / 32) - let bitOffset = fd % 32 - let mask: Int32 = 1 << bitOffset - switch intOffset { - case 0: set.fds_bits.0 = set.fds_bits.0 | mask - case 1: set.fds_bits.1 = set.fds_bits.1 | mask - case 2: set.fds_bits.2 = set.fds_bits.2 | mask - case 3: set.fds_bits.3 = set.fds_bits.3 | mask - case 4: set.fds_bits.4 = set.fds_bits.4 | mask - case 5: set.fds_bits.5 = set.fds_bits.5 | mask - case 6: set.fds_bits.6 = set.fds_bits.6 | mask - case 7: set.fds_bits.7 = set.fds_bits.7 | mask - case 8: set.fds_bits.8 = set.fds_bits.8 | mask - case 9: set.fds_bits.9 = set.fds_bits.9 | mask - case 10: set.fds_bits.10 = set.fds_bits.10 | mask - case 11: set.fds_bits.11 = set.fds_bits.11 | mask - case 12: set.fds_bits.12 = set.fds_bits.12 | mask - case 13: set.fds_bits.13 = set.fds_bits.13 | mask - case 14: set.fds_bits.14 = set.fds_bits.14 | mask - case 15: set.fds_bits.15 = set.fds_bits.15 | mask - case 16: set.fds_bits.16 = set.fds_bits.16 | mask - case 17: set.fds_bits.17 = set.fds_bits.17 | mask - case 18: set.fds_bits.18 = set.fds_bits.18 | mask - case 19: set.fds_bits.19 = set.fds_bits.19 | mask - case 20: set.fds_bits.20 = set.fds_bits.20 | mask - case 21: set.fds_bits.21 = set.fds_bits.21 | mask - case 22: set.fds_bits.22 = set.fds_bits.22 | mask - case 23: set.fds_bits.23 = set.fds_bits.23 | mask - case 24: set.fds_bits.24 = set.fds_bits.24 | mask - case 25: set.fds_bits.25 = set.fds_bits.25 | mask - case 26: set.fds_bits.26 = set.fds_bits.26 | mask - case 27: set.fds_bits.27 = set.fds_bits.27 | mask - case 28: set.fds_bits.28 = set.fds_bits.28 | mask - case 29: set.fds_bits.29 = set.fds_bits.29 | mask - case 30: set.fds_bits.30 = set.fds_bits.30 | mask - case 31: set.fds_bits.31 = set.fds_bits.31 | mask - default: break + let __fd_set_count = Int(__DARWIN_FD_SETSIZE) / MemoryLayout.stride + + extension fd_set + { + @inline(__always) + mutating func withCArrayAccess(block: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try withUnsafeMutablePointer(to: &fds_bits) { + try block(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) } } - - - /// Replacement for FD_CLR macro - - public static func CLR(fd: Int32, set: inout fd_set) { - let intOffset = Int32(fd / 32) - let bitOffset = fd % 32 - let mask: Int32 = ~(1 << bitOffset) - switch intOffset { - case 0: set.fds_bits.0 = set.fds_bits.0 & mask - case 1: set.fds_bits.1 = set.fds_bits.1 & mask - case 2: set.fds_bits.2 = set.fds_bits.2 & mask - case 3: set.fds_bits.3 = set.fds_bits.3 & mask - case 4: set.fds_bits.4 = set.fds_bits.4 & mask - case 5: set.fds_bits.5 = set.fds_bits.5 & mask - case 6: set.fds_bits.6 = set.fds_bits.6 & mask - case 7: set.fds_bits.7 = set.fds_bits.7 & mask - case 8: set.fds_bits.8 = set.fds_bits.8 & mask - case 9: set.fds_bits.9 = set.fds_bits.9 & mask - case 10: set.fds_bits.10 = set.fds_bits.10 & mask - case 11: set.fds_bits.11 = set.fds_bits.11 & mask - case 12: set.fds_bits.12 = set.fds_bits.12 & mask - case 13: set.fds_bits.13 = set.fds_bits.13 & mask - case 14: set.fds_bits.14 = set.fds_bits.14 & mask - case 15: set.fds_bits.15 = set.fds_bits.15 & mask - case 16: set.fds_bits.16 = set.fds_bits.16 & mask - case 17: set.fds_bits.17 = set.fds_bits.17 & mask - case 18: set.fds_bits.18 = set.fds_bits.18 & mask - case 19: set.fds_bits.19 = set.fds_bits.19 & mask - case 20: set.fds_bits.20 = set.fds_bits.20 & mask - case 21: set.fds_bits.21 = set.fds_bits.21 & mask - case 22: set.fds_bits.22 = set.fds_bits.22 & mask - case 23: set.fds_bits.23 = set.fds_bits.23 & mask - case 24: set.fds_bits.24 = set.fds_bits.24 & mask - case 25: set.fds_bits.25 = set.fds_bits.25 & mask - case 26: set.fds_bits.26 = set.fds_bits.26 & mask - case 27: set.fds_bits.27 = set.fds_bits.27 & mask - case 28: set.fds_bits.28 = set.fds_bits.28 & mask - case 29: set.fds_bits.29 = set.fds_bits.29 & mask - case 30: set.fds_bits.30 = set.fds_bits.30 & mask - case 31: set.fds_bits.31 = set.fds_bits.31 & mask - default: break - } - } - - - /// Replacement for FD_ISSET macro - - public static func ISSET(fd: Int32, set: inout fd_set) -> Bool { - let intOffset = Int32(fd / 32) - let bitOffset = fd % 32 - let mask: Int32 = 1 << bitOffset - switch intOffset { - case 0: return set.fds_bits.0 & mask != 0 - case 1: return set.fds_bits.1 & mask != 0 - case 2: return set.fds_bits.2 & mask != 0 - case 3: return set.fds_bits.3 & mask != 0 - case 4: return set.fds_bits.4 & mask != 0 - case 5: return set.fds_bits.5 & mask != 0 - case 6: return set.fds_bits.6 & mask != 0 - case 7: return set.fds_bits.7 & mask != 0 - case 8: return set.fds_bits.8 & mask != 0 - case 9: return set.fds_bits.9 & mask != 0 - case 10: return set.fds_bits.10 & mask != 0 - case 11: return set.fds_bits.11 & mask != 0 - case 12: return set.fds_bits.12 & mask != 0 - case 13: return set.fds_bits.13 & mask != 0 - case 14: return set.fds_bits.14 & mask != 0 - case 15: return set.fds_bits.15 & mask != 0 - case 16: return set.fds_bits.16 & mask != 0 - case 17: return set.fds_bits.17 & mask != 0 - case 18: return set.fds_bits.18 & mask != 0 - case 19: return set.fds_bits.19 & mask != 0 - case 20: return set.fds_bits.20 & mask != 0 - case 21: return set.fds_bits.21 & mask != 0 - case 22: return set.fds_bits.22 & mask != 0 - case 23: return set.fds_bits.23 & mask != 0 - case 24: return set.fds_bits.24 & mask != 0 - case 25: return set.fds_bits.25 & mask != 0 - case 26: return set.fds_bits.26 & mask != 0 - case 27: return set.fds_bits.27 & mask != 0 - case 28: return set.fds_bits.28 & mask != 0 - case 29: return set.fds_bits.29 & mask != 0 - case 30: return set.fds_bits.30 & mask != 0 - case 31: return set.fds_bits.31 & mask != 0 - default: return false - } - - } } #endif + +public extension fd_set +{ + @inline(__always) + private static func address(for fd: Int32) -> (Int, Int32) { + let intOffset = Int(fd) / __fd_set_count + let bitOffset = Int(fd) % __fd_set_count + let mask = Int32(1 << bitOffset) + return (intOffset, mask) + } + + public mutating func zero() { + withCArrayAccess { $0.initialize(to: 0, count: __fd_set_count) } + } + + public mutating func set(_ fd: Int32) { + let (index, mask) = fd_set.address(for: fd) + withCArrayAccess { $0[index] |= mask } + } + + public mutating func clear(_ fd: Int32) { + let (index, mask) = fd_set.address(for: fd) + withCArrayAccess { $0[index] &= ~mask } + } + + public mutating func isSet(_ fd: Int32) -> Bool { + let (index, mask) = fd_set.address(for: fd) + return withCArrayAccess { $0[index] & mask != 0 } + } +}